From fbb385c58fca949406e540f08f06989bf1381502 Mon Sep 17 00:00:00 2001 From: 07akioni <07akioni2@gmail.com> Date: Mon, 7 Sep 2026 12:40:26 +0800 Subject: [PATCH] test(web): pin replay timezone and await UI settlement --- apps/web/tests/chat-scroll-contract.e2e.ts | 1 + apps/web/tests/cordis-tool-round.e2e.ts | 8 ++------ apps/web/tests/feedback-release.e2e.ts | 2 ++ apps/web/tests/live-interactions.e2e.ts | 4 +++- apps/web/tests/support.ts | 4 ++-- apps/web/tests/workspace-management.e2e.ts | 6 +++++- 6 files changed, 15 insertions(+), 10 deletions(-) diff --git a/apps/web/tests/chat-scroll-contract.e2e.ts b/apps/web/tests/chat-scroll-contract.e2e.ts index 3dae7b93ff..205dbdbbea 100644 --- a/apps/web/tests/chat-scroll-contract.e2e.ts +++ b/apps/web/tests/chat-scroll-contract.e2e.ts @@ -649,6 +649,7 @@ describe('web e2e: long Chat scroll contract', () => { const readyPath = join(world.scaffold.workspaceCwd, TOOL_READY_FILE) const releasePath = join(world.scaffold.workspaceCwd, TOOL_RELEASE_FILE) await openSeed(world.page, TOOL_FIXTURE, TOOL_FIXTURE.markers.assistant(TOOL_FIXTURE.turns)) + await expectBottom(world.page) const settled = world.scaffold.whenTurnSettled(60_000) let released = false try { diff --git a/apps/web/tests/cordis-tool-round.e2e.ts b/apps/web/tests/cordis-tool-round.e2e.ts index 36b8ce950b..2b7c75b547 100644 --- a/apps/web/tests/cordis-tool-round.e2e.ts +++ b/apps/web/tests/cordis-tool-round.e2e.ts @@ -18,7 +18,7 @@ import { captureStableAria, compareOrRefreshGolden, fixtureUserPrompts, launchWebScaffold, recordFixture, watchConsole, webSnapshotMode, type WebScaffold, } from './scaffold.ts' -import { connectFreshWorkspace, expandOwningTurnProcess, saveFailureShot } from './support.ts' +import { connectFreshWorkspace, expandOwningTurnProcess, newEnglishPage, saveFailureShot } from './support.ts' const FIXTURE = fileURLToPath(new URL('../../../snapshots/web/cordis-tool-round/session.v2.jsonl', import.meta.url)) const UI_EXPECTED = fileURLToPath(new URL('../../../snapshots/web/cordis-tool-round/ui.expected.md', import.meta.url)) @@ -83,11 +83,7 @@ describe('web e2e: Cordis tools use their owned cards', () => { if (key === 'modelSelection') modelChanges.push(`${String(seq)}:${JSON.stringify(value)}`) }) browser = await chromium.launch() - page = await browser.newPage({ - viewport: { width: 1680, height: 1000 }, - locale: 'en-US', - timezoneId: 'Asia/Shanghai', - }) + page = await newEnglishPage(browser) page.on('websocket', (socket) => { socket.on('framereceived', (frame) => { const payload = String(frame.payload) diff --git a/apps/web/tests/feedback-release.e2e.ts b/apps/web/tests/feedback-release.e2e.ts index e4659aa622..ea7bb3b01f 100644 --- a/apps/web/tests/feedback-release.e2e.ts +++ b/apps/web/tests/feedback-release.e2e.ts @@ -82,6 +82,8 @@ describe.each(MODE === 'record' ? ['deepseek-official'] : ['deepseek-official', await page.getByRole('menuitem', { name: /^Model\b/ }).click() await page.getByRole('menuitemradio', { name, exact: true }).click() await expect.poll(() => trigger.getAttribute('aria-label')).toContain(name) + // The selection projection can precede the RPC reply that closes the menu. + await expect.poll(() => trigger.getAttribute('aria-expanded')).toBe('false') } beforeAll(async () => { diff --git a/apps/web/tests/live-interactions.e2e.ts b/apps/web/tests/live-interactions.e2e.ts index 1001b51cf2..c818a93655 100644 --- a/apps/web/tests/live-interactions.e2e.ts +++ b/apps/web/tests/live-interactions.e2e.ts @@ -100,7 +100,9 @@ describe('web e2e: live-turn interactions (cancel / error / retry)', () => { scaffold = await launchWebScaffold({ replayFixture: FIXTURE, ...(overridePath === undefined ? {} : { replayOverride: overridePath }), - ...(overridePath === undefined ? {} : { compareReplaySession: false }), + // Override streams use live timings; positive chunk spacing keeps the + // recovered response's decode duration and throughput observable. + ...(overridePath === undefined ? {} : { compareReplaySession: false, paceMs: 5 }), ...(retryPolicy === undefined ? {} : { replayRetryPolicy: retryPolicy }), }) scaffold.ctx.on('session/event', (_session, event: SessionEvent) => { sessionEvents.push(event) }) diff --git a/apps/web/tests/support.ts b/apps/web/tests/support.ts index 63cf8c3ff8..197ae86a6a 100644 --- a/apps/web/tests/support.ts +++ b/apps/web/tests/support.ts @@ -18,7 +18,7 @@ export const REPO_ROOT = fileURLToPath(new URL('../../..', import.meta.url)) export const ZH_BROWSER_LOCALE = 'zh-CN' /** - * Open the standard browser-test page advertising English before client boot. + * Open the standard browser-test page with English and the recorded Session timezone. * This keeps role locators and goldens deterministic while leaving the Host * settings document free to override the provisional browser-derived locale; * scenarios asserting the Chinese surface advertise @@ -28,7 +28,7 @@ export const ZH_BROWSER_LOCALE = 'zh-CN' * @returns the initialized page. */ export async function newEnglishPage(browser: Browser, height = 1000): Promise { - return await browser.newPage({ viewport: { width: 1680, height }, locale: 'en-US' }) + return await browser.newPage({ viewport: { width: 1680, height }, locale: 'en-US', timezoneId: 'Asia/Shanghai' }) } /** diff --git a/apps/web/tests/workspace-management.e2e.ts b/apps/web/tests/workspace-management.e2e.ts index 382e549ddf..02aaa032eb 100644 --- a/apps/web/tests/workspace-management.e2e.ts +++ b/apps/web/tests/workspace-management.e2e.ts @@ -56,6 +56,7 @@ describe('web e2e: workspace management (create / rename / flat view / hover aff const pathInput = dialog.locator('input[aria-label="Edit path"]') await pathInput.fill(path) await pathInput.press('Enter') + await pathInput.waitFor({ state: 'hidden', timeout: 10_000 }) return dialog } @@ -68,7 +69,10 @@ describe('web e2e: workspace management (create / rename / flat view / hover aff await dialog.getByRole('button', { name: 'New folder' }).click() await page.getByLabel('Folder name').fill(name) await page.getByRole('button', { name: 'Create', exact: true }).click() - // Creating selects the new folder in the listing; Open adopts it. + // The create response closes the child dialog before its relist selects + // the new folder; adoption must wait for that selection. + await expect.poll(() => dialog.getByRole('list').getByRole('button', { name, exact: true }).getAttribute('aria-current'), + { timeout: 10_000 }).toBe('true') await dialog.getByRole('button', { name: 'Open', exact: true }).click() await dialog.waitFor({ state: 'hidden', timeout: 10_000 }) await expect.poll(