From aaaae9f35d080fc81d1cbb0d0b70bc576c5a77c7 Mon Sep 17 00:00:00 2001 From: Daniel Moerner Date: Tue, 18 Aug 2026 13:52:56 -0400 Subject: [PATCH] feat(tests): E2E test for sign in card flash This is a currently-failing E2E test which tries to reproduce a sign in card flash after entering the OTP code. --- .changeset/signin-start-card-flash-test.md | 2 + integration/tests/sign-in-flow.test.ts | 51 ++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 .changeset/signin-start-card-flash-test.md diff --git a/.changeset/signin-start-card-flash-test.md b/.changeset/signin-start-card-flash-test.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/signin-start-card-flash-test.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/integration/tests/sign-in-flow.test.ts b/integration/tests/sign-in-flow.test.ts index da9bb1c6407..aed20e7d722 100644 --- a/integration/tests/sign-in-flow.test.ts +++ b/integration/tests/sign-in-flow.test.ts @@ -61,6 +61,57 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withEmailCodes] })('sign in f await u.po.expect.toBeSignedIn(); }); + test('does not return to the start card after the email code is accepted', async ({ page, context }) => { + const u = createTestUtils({ app, page, context }); + + // The regression this guards against is a flash: the start card renders for ~150ms while + // setActive is still running. A locator assertion cannot catch that, so record every URL the + // page passes through and sample the rendered card each frame instead. + await page.addInitScript(() => { + const flash = { recording: false, urls: [] as string[], sawStartCard: false }; + (window as any).__startCardFlash = flash; + + for (const key of ['pushState', 'replaceState'] as const) { + const original = history[key]; + history[key] = function (...args: Parameters) { + const result = original.apply(this, args); + if (flash.recording) { + flash.urls.push(location.pathname); + } + return result; + }; + } + + const sample = () => { + if (flash.recording && location.pathname === '/sign-in' && document.querySelector('input[name=identifier]')) { + flash.sawStartCard = true; + } + requestAnimationFrame(sample); + }; + requestAnimationFrame(sample); + }); + + await u.po.signIn.goTo(); + await u.po.signIn.getIdentifierInput().fill(fakeUser.email); + await u.po.signIn.continue(); + await u.po.signIn.getUseAnotherMethodLink().click(); + + const prepared = page.waitForResponse( + res => res.request().method() === 'POST' && res.url().includes('prepare_first_factor'), + ); + await u.po.signIn.getAltMethodsEmailCodeButton().click(); + await prepared; + + // From here on the flow is past the start card and must never go back to it. + await page.evaluate(() => ((window as any).__startCardFlash.recording = true)); + await u.po.signIn.enterTestOtpCode({ awaitPrepare: false }); + await u.po.expect.toBeSignedIn(); + + const flash = await page.evaluate(() => (window as any).__startCardFlash); + expect(flash.urls).not.toContain('/sign-in'); + expect(flash.sawStartCard).toBe(false); + }); + test('sign in with phone number and password', async ({ page, context }) => { const u = createTestUtils({ app, page, context }); await u.po.signIn.goTo();