Files
ComfyUI_frontend/browser_tests/fixtures/utils/flashDetector.ts
Maanil Verma 0c23e8305f fix: skip templates modal when opening a template from the URL (#12835)
## Summary

On first launch, the templates modal flashed open for a split second
before a deeplinked template (`?template=`) loaded, which felt broken.

## Changes

- **What**: Gate the first-launch templates modal on template URL
intent, alongside the existing shared-workflow (`?share=`) check. When a
template is being opened directly from the URL, the template modal no
longer opens. Behavior is unchanged when no template is in the URL — the
template modal still shows for first-time users.
- Test util: Added browser_tests/fixtures/utils/flashDetector.ts —
installs a pre-navigation requestAnimationFrame sampler that flags if a
[data-testid] element ever renders, even for a single frame. This
catches a brief flash that toBeHidden() (final-state only) cannot.

## Review Focus

`hasTemplateUrlIntent()` mirrors the existing
`hasSharedWorkflowIntent()` (direct `route.query` check plus
preserved-query fallback for the `/user-select` redirect path). Two
regression tests cover both the URL-param and preserved-intent cases.

**Coverage:**

- Unit (useWorkflowPersistenceV2.test.ts): the modal is not opened when
a template param is in the URL, and when template intent is preserved
across the /user-select redirect.
- E2E (templates.spec.ts): templates dialog never flashes when
first-time user opens a template link — verified red-without-fix,
green-with-fix.

Screen Recording 



https://github.com/user-attachments/assets/636094d4-0ef0-4e42-af32-d4e6c7ec5731



closes #12836
2026-06-17 21:00:02 +00:00

46 lines
1.4 KiB
TypeScript

import type { Page } from '@playwright/test'
function flagAttributeFor(testId: string) {
const encoded = Array.from(testId, (ch) =>
ch.charCodeAt(0).toString(16)
).join('')
return `data-flashed-${encoded}`
}
/**
* Flags the first time an element matching `[data-testid="<testId>"]` is
* present and rendered, sampled every frame via `requestAnimationFrame` from
* page load. Catches a dialog that mounts and unmounts within a few frames,
* which `toBeHidden()` (final state only) cannot.
*
* Must be called before navigation (e.g. before `comfyPage.setup()`).
*/
export async function trackElementFlash(
page: Page,
testId: string
): Promise<{ hasFlashed: () => Promise<boolean> }> {
const flagAttribute = flagAttributeFor(testId)
await page.addInitScript(
({ id, attribute }: { id: string; attribute: string }) => {
const sample = () => {
const el = document.querySelector(`[data-testid="${CSS.escape(id)}"]`)
if (el instanceof HTMLElement) {
const rect = el.getBoundingClientRect()
if (rect.width > 0 && rect.height > 0) {
document.documentElement.setAttribute(attribute, 'true')
}
}
requestAnimationFrame(sample)
}
requestAnimationFrame(sample)
},
{ id: testId, attribute: flagAttribute }
)
return {
hasFlashed: async () =>
(await page.locator('html').getAttribute(flagAttribute)) === 'true'
}
}