Files
orca/tests/e2e/helpers/alt-screen-frame.ts
T
JinjingandOrca 468f5b77b5 test(e2e): fix two stale E2E specs failing on main (runtime host seeding, alt-screen snapshot) (#10614)
* test(e2e): register a real runtime host and publish the alt-screen frame as its snapshot

Two long-running scheduled-E2E failures on main were stale test setup, not
product defects.

`onboarding.spec.ts:420` seeded the Active Server by faking a runtime
environment in the renderer store and writing `activeRuntimeEnvironmentId`
through the generic `settings:set` IPC. Since #10011 that setter strips the
key, and the dedicated `settings:set-active-runtime-environment-preference`
handler resolves the id against the main-process environment store — CI
logged `RuntimeEnvironmentStoreError: Unknown environment: env-e2e` from
`runtimeEnvironments:subscribe`/`:call` alongside the assertion failure.
Register the host for real via `runtimeEnvironments:addFromPairingCode`
(offline; no live server) and write the preference through its own channel.

`terminal-tab-switch-visual-restore.spec.ts:604` wrote alt-screen frames
straight into the renderer's xterm, so those bytes never transited the PTY
and main's model could not contain them. On cycle 0 the freshly spawned
shell still has queued startup output, so hiding the pane makes main's
hidden-delivery gate drop bytes and latch a reveal restore, which repaints
main's snapshot over the fabricated frame; later cycles run against an idle
shell and survive. Arm the existing `setHiddenSnapshotOverride` seam (already
used by sibling tests in this file) with the same frame so the live-write and
restore paths render identically, and keep the `markerPresent` assertion.

Co-authored-by: Orca <help@stably.ai>

* test(e2e): keep the alt-screen restore path observable

Numbering the snapshot frame one higher than the live-written frame keeps
the marker assertion path-agnostic while leaving the frame number on
screen as the signal for which path painted. An unrecognised frame now
fails, and the per-cycle path is recorded rather than asserted because
which cycles latch a restore is load-dependent.

Frame authoring and readback move to a helper module; the additions
crossed the spec's max-lines cap.

Co-authored-by: Orca <help@stably.ai>

* Escape regex metacharacters in alt-screen marker pattern

Marker is treated as a literal string, so escape regex metacharacters
to prevent them from being interpreted as regex syntax.

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-25 15:58:40 -07:00

77 lines
3.0 KiB
TypeScript

import type { Page } from '@stablyai/playwright-test'
// Boxed alt-screen TUI frame: enters the alternate buffer, clears it, and paints
// a marker line carrying a zero-padded frame number.
export function buildAltScreenFrame(marker: string, frame: number): string {
const progress = `${'█'.repeat((frame % 8) + 1)}${'░'.repeat(8 - ((frame % 8) + 1))}`
return [
'\x1b[?2026h',
'\x1b[?1049h',
'\x1b[2J\x1b[H',
'\x1b[?25l',
`╭────────────────────────────────────────────────────────────────────╮`,
`│ ${marker} frame ${String(frame).padStart(3, '0')} ${progress} │`,
`│ Dimension │ Rating │`,
`╰────────────────────────────────────────────────────────────────────╯`,
'\x1b[?2026l'
].join('\r\n')
}
// Why: the live-write and the reveal restore paint the same layout, so the frame
// number is the only thing on screen that says which of the two landed last.
export async function readRenderedAltScreenFrame(
page: Page,
tabId: string,
marker: string
): Promise<number | null> {
return page.evaluate(
({ tabId, marker }) => {
const manager = window.__paneManagers?.get(tabId)
const pane = manager?.getActivePane?.() ?? manager?.getPanes?.()[0]
if (!pane) {
throw new Error(`No terminal pane for tab ${tabId}`)
}
// marker is a literal, so escape it rather than letting `[`/`.`/`+` act as regex syntax.
const pattern = new RegExp(`${marker.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')} frame (\\d{3})`)
const buffer = pane.terminal.buffer.active
for (let row = 0; row < pane.terminal.rows; row += 1) {
const line = buffer.getLine(buffer.viewportY + row)?.translateToString(true) ?? ''
const match = pattern.exec(line)
if (match) {
return Number(match[1])
}
}
return null
},
{ tabId, marker }
)
}
export function describeAltScreenRenderPath(
renderedFrame: number | null,
liveFrame: number,
restoreFrame: number
): string {
if (renderedFrame === restoreFrame) {
return 'reveal restore'
}
if (renderedFrame === liveFrame) {
return 'live write (no restore)'
}
return renderedFrame === null ? 'no marker' : `unexpected frame ${renderedFrame}`
}
export async function writeToPaneTerminal(page: Page, tabId: string, data: string): Promise<void> {
await page.evaluate(
({ tabId, data }) => {
const manager = window.__paneManagers?.get(tabId)
const pane = manager?.getActivePane?.() ?? manager?.getPanes?.()[0]
if (!pane) {
throw new Error(`No terminal pane for tab ${tabId}`)
}
return new Promise<void>((resolve) => pane.terminal.write(data, resolve))
},
{ tabId, data }
)
}