From f0dcf57d804df678276f797623d1ba6a72103762 Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Thu, 2 Jul 2026 22:22:03 +0800 Subject: [PATCH] test: de-flake medication-page ui-smoke against transient Suspense duplicate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "prescribing workflow uses in-app medication routes" spec intermittently failed the Chromium ui-smoke gate with a strict-mode violation: getByTestId('acamprosate-medication-page') resolved to 2 elements. Root cause: the medication route renders inside GlobalMockupSearchShell, whose Suspense fallback renders `props.children` and whose resolved client subtree also renders `children`. During a navigation/hydration overlap (the mockup-> redirect hard navigation) both
copies briefly coexist. Rendering children in the fallback is an intentional SSR pattern (content visible while useSearchParams suspends), so the app is correct in every browser — this is purely a too-strict test locator catching a transient DOM overlap. Fix: wait for the testid to settle to exactly one instance before asserting visibility (new expectSingleMedicationPage helper). toHaveCount(1) retries through the transition yet still fails on a genuine permanent double-render, so it does not mask a real regression. Follow-up (not done here): hoisting `children` out of the Suspense boundary so it renders exactly once would remove the duplicate at the source, but that is a broader shell refactor with SSR implications. Co-Authored-By: Claude Fable 5 --- tests/ui-smoke.spec.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/tests/ui-smoke.spec.ts b/tests/ui-smoke.spec.ts index 759ec8218b..64f0a42863 100644 --- a/tests/ui-smoke.spec.ts +++ b/tests/ui-smoke.spec.ts @@ -26,6 +26,18 @@ async function gotoApp(page: Page, path: string) { await page.waitForLoadState("networkidle", { timeout: 15_000 }).catch(() => undefined); } +async function expectSingleMedicationPage(page: Page) { + // The medication route renders inside GlobalMockupSearchShell, whose Suspense + // fallback and resolved client subtree both render `children`. During a + // navigation/hydration overlap the shared data-testid can transiently resolve + // to two
elements and trip Playwright strict mode. Wait for it to settle + // to exactly one before asserting visibility — a genuine permanent double-render + // still fails toHaveCount(1), so this does not mask a real regression. + const medicationPage = page.getByTestId("acamprosate-medication-page"); + await expect(medicationPage).toHaveCount(1); + await expect(medicationPage).toBeVisible(); +} + function visibleQuestionInput(page: Page) { return page.locator('[aria-label^="Search indexed guidelines by question or keyword"]:visible').first(); } @@ -1114,11 +1126,11 @@ test.describe("Clinical KB UI smoke coverage", () => { await expect(acamprosateResult).toHaveAttribute("href", "/medications/acamprosate"); await acamprosateResult.click(); await expect(page).toHaveURL(/\/medications\/acamprosate$/, { timeout: 30_000 }); - await expect(page.getByTestId("acamprosate-medication-page")).toBeVisible(); + await expectSingleMedicationPage(page); await gotoApp(page, "/mockups/medication-prescribing"); await expect(page).toHaveURL(/\/medications\/acamprosate$/); - await expect(page.getByTestId("acamprosate-medication-page")).toBeVisible(); + await expectSingleMedicationPage(page); }); test("document search mode lists matching documents and scope actions", async ({ page }) => {