From 8f0bdab84b20bcb2dd1a2cfaa2a65ab99d65fd0f Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 19 Jul 2026 20:53:05 +0000 Subject: [PATCH] fix(ui): settle single search header before overlap checks Production UI failed when gotoHome waited on header#search during a transient dual-mount from the landing-preference router.replace. Pin mode=answer and require exactly one banner before measuring overlaps. Co-authored-by: BigSimmo --- tests/ui-overlap.spec.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/tests/ui-overlap.spec.ts b/tests/ui-overlap.spec.ts index a9fc0fd40..93f69e3e8 100644 --- a/tests/ui-overlap.spec.ts +++ b/tests/ui-overlap.spec.ts @@ -62,8 +62,19 @@ async function mockDemoDashboard(page: Page) { } async function gotoHome(page: Page) { - await page.goto("/", { waitUntil: "domcontentloaded" }); - await page.locator("header#search").waitFor({ state: "visible", timeout: 30_000 }); + // Pin mode=answer so GlobalSearchShell does not immediately router.replace() + // for a stored landing preference. That replace can briefly leave two mounted + // shells (and two header#search nodes), which trips Playwright strict mode. + await page.goto("/?mode=answer", { waitUntil: "domcontentloaded" }); + const searchHeader = page.locator("header#search"); + // Same settle pattern as expectSingleMedicationPage in ui-smoke: Suspense / + // client remount can transiently yield two banners. Wait for exactly one so + // a permanent double-render still fails toHaveCount(1). + if ((await searchHeader.count()) !== 1) { + await expect(searchHeader).toHaveCount(1, { timeout: 30_000 }); + } + await expect(searchHeader).toHaveCount(1, { timeout: 30_000 }); + await expect(searchHeader).toBeVisible({ timeout: 30_000 }); await page.getByRole("button", { name: "Open answer options" }).waitFor({ state: "visible", timeout: 30_000 }); }