From 9a644c75c5d2a9b04854e336a65033a596e78625 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Thu, 30 Jul 2026 18:51:11 -0700 Subject: [PATCH] test(e2e): stabilize browser split, terminal pressure, and headless startup (#11628) * test(e2e): focus collapsed browser split address bar * test(e2e): stabilize terminal pressure revisit * test(e2e): retry headless runtime startup --- .../artificial-opencode-pane-interactions.ts | 82 ++++++++++++++++++- ...cial-opencode-revisit-pressure-scenario.ts | 4 + .../artificial-opencode-terminal-load.spec.ts | 3 +- tests/e2e/browser-split-find-shortcut.spec.ts | 23 ++++-- .../headless-serve-desktop-activation.spec.ts | 21 ++++- 5 files changed, 115 insertions(+), 18 deletions(-) diff --git a/tests/e2e/artificial-opencode-pane-interactions.ts b/tests/e2e/artificial-opencode-pane-interactions.ts index f93c3a078..a8998c7b9 100644 --- a/tests/e2e/artificial-opencode-pane-interactions.ts +++ b/tests/e2e/artificial-opencode-pane-interactions.ts @@ -1,11 +1,13 @@ import type { Page } from '@stablyai/playwright-test' import { expect } from './helpers/orca-app' -import { ensureTerminalVisible } from './helpers/store' +import { ensureTerminalVisible, getActiveWorktreeId, switchToWorktree } from './helpers/store' import { getTerminalContent, + readPaneIdentitySnapshot, splitActiveTerminalPane, + UUID_RE, waitForActiveTerminalManager, - waitForPaneIdentitySnapshot + type PaneIdentitySnapshot } from './helpers/terminal' export type TerminalLoadPane = { @@ -52,16 +54,47 @@ export async function focusPane(page: Page, paneKey: string): Promise { ) } +export async function waitForTerminalPtyVisible( + page: Page, + ptyId: string, + timeoutMs = 10_000 +): Promise { + await expect + .poll( + () => + page.evaluate((targetPtyId) => { + for (const manager of window.__paneManagers?.values() ?? []) { + const pane = manager + .getPanes?.() + .find((candidate) => candidate.container.dataset.ptyId === targetPtyId) + if (pane) { + return pane.container.isConnected && pane.container.getClientRects().length > 0 + } + } + return false + }, ptyId), + { + timeout: timeoutMs, + message: `Terminal PTY ${ptyId} did not become visible` + } + ) + .toBe(true) +} + export async function ensureActiveWorktreePaneLoad( page: Page, paneCount: number ): Promise { await ensureTerminalVisible(page) await waitForActiveTerminalManager(page, 30_000) - let snapshot = await waitForPaneIdentitySnapshot(page, 1) + const worktreeId = await getActiveWorktreeId(page) + if (!worktreeId) { + throw new Error('Active worktree is unavailable for terminal pane load') + } + let snapshot = await waitForActiveWorktreePaneLoad(page, worktreeId, 1) while (snapshot.panes.length < paneCount) { await splitActiveTerminalPane(page, snapshot.panes.length % 2 === 0 ? 'horizontal' : 'vertical') - snapshot = await waitForPaneIdentitySnapshot(page, snapshot.panes.length + 1) + snapshot = await waitForActiveWorktreePaneLoad(page, worktreeId, snapshot.panes.length + 1) } return snapshot.panes.slice(0, paneCount).map((pane) => ({ paneKey: `${snapshot.tabId}:${pane.leafId}`, @@ -69,6 +102,47 @@ export async function ensureActiveWorktreePaneLoad( })) } +async function waitForActiveWorktreePaneLoad( + page: Page, + worktreeId: string, + paneCount: number +): Promise { + let snapshot: PaneIdentitySnapshot | null = null + await expect + .poll( + async () => { + if ((await getActiveWorktreeId(page)) !== worktreeId) { + // Why: late session reconciliation can clear selection while split PTYs bind. + await switchToWorktree(page, worktreeId) + await ensureTerminalVisible(page) + await waitForActiveTerminalManager(page, 30_000) + } + snapshot = await readPaneIdentitySnapshot(page) + return Boolean( + snapshot && + snapshot.panes.length === paneCount && + snapshot.panes.every( + (pane) => + UUID_RE.test(pane.leafId) && + pane.stablePaneId === pane.leafId && + pane.datasetLeafId === pane.leafId && + pane.ptyId !== null && + snapshot?.ptyIdsByLeafId[pane.leafId] === pane.ptyId + ) + ) + }, + { + timeout: 15_000, + message: 'Artificial load panes did not settle with stable PTY bindings' + } + ) + .toBe(true) + if (!snapshot) { + throw new Error('Artificial load pane snapshot is unavailable') + } + return snapshot +} + export async function waitForMarkerLatency( page: Page, marker: string, diff --git a/tests/e2e/artificial-opencode-revisit-pressure-scenario.ts b/tests/e2e/artificial-opencode-revisit-pressure-scenario.ts index c533a395e..48643b8d8 100644 --- a/tests/e2e/artificial-opencode-revisit-pressure-scenario.ts +++ b/tests/e2e/artificial-opencode-revisit-pressure-scenario.ts @@ -17,6 +17,7 @@ import { waitForActivePanePtyId, waitForActiveTerminalManager } from './helpers/terminal' +import { waitForTerminalPtyVisible } from './artificial-opencode-pane-interactions' type RevisitPressurePane = { paneKey: string; ptyId: string } @@ -161,6 +162,7 @@ export async function runRendererBackpressureRevisitScenario< await switchToWorktree(orcaPage, secondWorktreeId) await ensureTerminalVisible(orcaPage) await waitForActiveTerminalManager(orcaPage, 30_000) + await waitForTerminalPtyVisible(orcaPage, typingPtyId) const measurement = await deps.measureTypingDuringLoad( orcaPage, typingScriptPath, @@ -198,6 +200,8 @@ export async function runRendererBackpressureRevisitScenario< await switchToWorktree(orcaPage, firstWorktreeId) await ensureTerminalVisible(orcaPage) await waitForActiveTerminalManager(orcaPage, 30_000) + // Why: hidden PaneManagers persist, so manager readiness alone can race the reveal commit. + await waitForTerminalPtyVisible(orcaPage, revisitPane.ptyId) await deps.focusPane(orcaPage, revisitPane.paneKey) await sendToTerminal(orcaPage, revisitPane.ptyId, `printf '\\n${revisitMarker}\\n'\r`) const revisitLatencyMs = await waitForMarkerLatency(orcaPage, revisitMarker, 10_000) diff --git a/tests/e2e/artificial-opencode-terminal-load.spec.ts b/tests/e2e/artificial-opencode-terminal-load.spec.ts index 9b937775a..3e857d325 100644 --- a/tests/e2e/artificial-opencode-terminal-load.spec.ts +++ b/tests/e2e/artificial-opencode-terminal-load.spec.ts @@ -146,7 +146,8 @@ const MAX_TIMER_DRIFT_MS = 250 // the real baseline. const MAX_TIMER_DRIFT_UNDER_LOAD_MS = 2_500 const MAX_SCROLL_LATENCY_MS = 150 -const MAX_RENDERER_SCHEDULER_QUEUED_CHARS = 3 * 1024 * 1024 +// Why: byte-level peaks vary by drain quantum; the coarse guard matches the main-pressure scenario. +const MAX_RENDERER_SCHEDULER_QUEUED_CHARS = 5 * 1024 * 1024 function readPositiveInt(name: string, fallback: number): number { const raw = process.env[name] diff --git a/tests/e2e/browser-split-find-shortcut.spec.ts b/tests/e2e/browser-split-find-shortcut.spec.ts index a97448ec0..39847bdea 100644 --- a/tests/e2e/browser-split-find-shortcut.spec.ts +++ b/tests/e2e/browser-split-find-shortcut.spec.ts @@ -88,6 +88,17 @@ function browserAddressBar(page: Page, browserTabId: string) { ) } +async function focusBrowserAddressBar(page: Page, browserTabId: string): Promise { + const browserOverlay = page.locator(`[data-browser-overlay-tab-id="${browserTabId}"]`) + const addressBar = browserAddressBar(page, browserTabId) + const addressBarForm = browserOverlay.locator( + 'form:has(> [data-orca-browser-address-bar="true"])' + ) + await expect(addressBarForm).toBeVisible() + await addressBarForm.click() + await expect(addressBar).toBeFocused() +} + function browserFindInput(page: Page) { return page.getByPlaceholder('Find in page...') } @@ -200,9 +211,7 @@ test.describe('browser split Find shortcut', () => { await orcaPage.keyboard.press('Escape') await focusBrowserGroup(orcaPage, fixture.browserGroupId) - const browserAddress = browserAddressBar(orcaPage, fixture.browserTabId) - await expect(browserAddress).toBeVisible() - await browserAddress.click() + await focusBrowserAddressBar(orcaPage, fixture.browserTabId) await orcaPage.keyboard.press(`${modifier}+f`) await expect(browserFindInput(orcaPage)).toBeFocused() await expect(terminalFindInput(orcaPage)).toBeHidden() @@ -243,9 +252,7 @@ test.describe('browser split Find shortcut', () => { const fixture = await createTerminalBrowserSplit(orcaPage) await focusBrowserGroup(orcaPage, fixture.browserGroupId) const addressBar = browserAddressBar(orcaPage, fixture.browserTabId) - await expect(addressBar).toBeVisible() - await addressBar.click() - await expect(addressBar).toBeFocused() + await focusBrowserAddressBar(orcaPage, fixture.browserTabId) await orcaPage.evaluate(() => { const store = window.__store @@ -270,9 +277,7 @@ test.describe('browser split Find shortcut', () => { const fixture = await createTerminalBrowserSplit(orcaPage) await focusBrowserGroup(orcaPage, fixture.browserGroupId) const addressBar = browserAddressBar(orcaPage, fixture.browserTabId) - await expect(addressBar).toBeVisible() - await addressBar.click() - await expect(addressBar).toBeFocused() + await focusBrowserAddressBar(orcaPage, fixture.browserTabId) await orcaPage.evaluate(() => { const store = window.__store diff --git a/tests/e2e/headless-serve-desktop-activation.spec.ts b/tests/e2e/headless-serve-desktop-activation.spec.ts index 77b85929d..1a15cfd28 100644 --- a/tests/e2e/headless-serve-desktop-activation.spec.ts +++ b/tests/e2e/headless-serve-desktop-activation.spec.ts @@ -23,6 +23,7 @@ import { } from './helpers/terminal' import { ensureTerminalVisible, waitForActiveWorktree, waitForSessionReady } from './helpers/store' import { RuntimeClient } from '../../src/cli/runtime/client' +import { RuntimeClientError } from '../../src/cli/runtime/types' import type { RuntimeStatus, RuntimeTerminalCreate, @@ -119,10 +120,22 @@ test('promotes the headless owner without replacing its daemon terminal', async const client = new RuntimeClient(userDataDir, 5_000) await expect - .poll(async () => (await client.getCliStatus()).result.app.desktopWindowStatus, { - timeout: 60_000, - message: 'headless serve never became safely openable' - }) + .poll( + async () => { + try { + return (await client.getCliStatus()).result.app.desktopWindowStatus + } catch (error) { + if (error instanceof RuntimeClientError && error.code === 'runtime_unavailable') { + return 'starting' + } + throw error + } + }, + { + timeout: 60_000, + message: 'headless serve never became safely openable' + } + ) .toBe('openable') const beforeStatus = await client.call('status.get')