From 9da7f7edfcf636ae79b29bf4b404f329729fd237 Mon Sep 17 00:00:00 2001 From: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com> Date: Tue, 22 Sep 2026 09:49:28 -0400 Subject: [PATCH] test(config): the session render rig waits for the reads it asserts (#22269) The session route issues worktree.show, session.tabs.list and terminal.list from effects that run after the commit painting 'Terminal', so reading __orcaRenderCheckRequests where openRoute resolves is a snapshot taken before the burst. Under a 20x CPU throttle none of the three is in it, which is the shape the loaded CI job hit twice. waitForRecordedRequests polls the double's own log under a 30s bound and names what never arrived. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb --- .../scripts/mobile-web-app-render-harness.mjs | 32 +++++++++++++++++++ .../mobile-web-app-session-render.test.mjs | 18 ++++++----- 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/config/scripts/mobile-web-app-render-harness.mjs b/config/scripts/mobile-web-app-render-harness.mjs index f056223bec1..090ae537128 100644 --- a/config/scripts/mobile-web-app-render-harness.mjs +++ b/config/scripts/mobile-web-app-render-harness.mjs @@ -439,6 +439,38 @@ export function installShellDouble({ globalThis.orcaBridge = channel } +/** How long a check waits for a mount's reads before it reports what the page did send. */ +const RECORDED_REQUEST_MS = 30_000 + +/** + * The double's request log, once every method named is in it. + * + * A route issues its first reads from effects that run after the commit painting its chrome, so a + * snapshot taken where the awaited text lands is a race a loaded machine loses. The bound names + * what never arrived and what did. + */ +export async function waitForRecordedRequests( + page, + methods, + { boundMs = RECORDED_REQUEST_MS } = {} +) { + const started = Date.now() + for (;;) { + const requests = await page.evaluate(() => globalThis.__orcaRenderCheckRequests ?? []) + const missing = methods.filter((method) => !requests.some((one) => one.method === method)) + if (missing.length === 0) { + return requests + } + if (Date.now() - started > boundMs) { + throw new Error( + `[render-harness] the page never asked for ${missing.join(', ')} in ${String(boundMs)}ms; ` + + `it asked for ${JSON.stringify(requests.map((one) => one.method))}` + ) + } + await page.waitForTimeout(25) + } +} + /** * The page server the render checks run against: the built bundle, under the shell's own policy. * diff --git a/config/scripts/mobile-web-app-session-render.test.mjs b/config/scripts/mobile-web-app-session-render.test.mjs index 57ba6457869..4be56779649 100644 --- a/config/scripts/mobile-web-app-session-render.test.mjs +++ b/config/scripts/mobile-web-app-session-render.test.mjs @@ -11,7 +11,8 @@ import { installShellDouble, readBridgeFaultGrant, readBridgeProtocolVersion, - readShellCsp + readShellCsp, + waitForRecordedRequests } from './mobile-web-app-render-harness.mjs' /** @@ -212,6 +213,9 @@ async function openRoute(route, awaitText, replies = {}, options = {}) { /** The session header renders it, so the chrome is on screen before this reads the tree. */ const BACK_LABEL = 'Back to worktrees' +/** The header's live title, the tab snapshot and the terminal inventory. */ +const SESSION_MOUNT_READS = ['worktree.show', 'session.tabs.list', 'terminal.list'] + describeRender( 'the session route in a real browser', () => { @@ -341,14 +345,12 @@ describeRender( // The precondition every assertion above needs: a screen that mounted and asked for nothing // would paint the same chrome. The three reads are the header's live title, the tab snapshot // and the terminal inventory, each carrying the workspace the route named. + // + // Waited for and not read at the paint: all three are issued from effects that run after the + // commit putting 'Terminal' on screen, which is why this case reds on CI's loaded job and + // never here. Under a 20x CPU throttle the snapshot at the paint holds none of them. const opened = await openRoute(SESSION_ROUTE, 'Terminal') - const requests = await opened.page.evaluate(() => globalThis.__orcaRenderCheckRequests ?? []) - for (const method of ['worktree.show', 'session.tabs.list', 'terminal.list']) { - expect( - requests.some((request) => request.method === method), - method - ).toBe(true) - } + const requests = await waitForRecordedRequests(opened.page, SESSION_MOUNT_READS) expect(JSON.stringify(requests)).toContain(WORKTREE) await opened.page.close() }, 120_000)