diff --git a/packages/web-shell/client/e2e/generated-ui.retention.spec.ts b/packages/web-shell/client/e2e/generated-ui.retention.spec.ts index 899a262e058..a926324f1e2 100644 --- a/packages/web-shell/client/e2e/generated-ui.retention.spec.ts +++ b/packages/web-shell/client/e2e/generated-ui.retention.spec.ts @@ -499,10 +499,32 @@ async function reviewSnapshot(page: Page, app: FrameLocator) { text = body.prompt[0].text; await route.fulfill({ status: 409, json: { error: 'captured by test' } }); }; + // A restored card finishes its first paint asynchronously, and a click that + // lands before the host accepts continuations is swallowed with no request + // and no error: the nightly run failed on the missing dispatch, not on the + // copy below. Wait for the button the app gates continuations on, then give + // dispatch a CI-sized budget so a loaded host is not read as a broken card. + await expect(app.locator('#continue-analysis')).toBeEnabled(); await page.route('**/session/*/prompt', capture); try { await app.locator('#continue-analysis').click(); - await expect.poll(() => text).toBeDefined(); + const deadline = Date.now() + (process.env['CI'] ? 20_000 : 5_000); + while (!text && Date.now() < deadline) { + await page.waitForTimeout(100); + } + if (!text) { + const cardState = await app + .locator('#status') + .getAttribute('data-state') + .catch(() => null); + const status = await app + .locator('#continuation-status') + .textContent() + .catch(() => null); + throw new Error( + `continue never dispatched a prompt request (card=${cardState}, status=${status})`, + ); + } await expect(app.locator('#continuation-status')).toContainText( 'Not sent.', );