Files
orca/tests/e2e/helpers/golden-stub-agent.ts
Jinjing f5fd7303ab test(e2e): cover tab-bar agent launches on Windows and WSL (#16110)
* test(e2e): gate the tab-bar agent launcher on Windows shells and WSL

The `+` menu agent launcher had no golden coverage in the Windows lane, so a
Windows-only break anywhere in its chain (detection row, startup-plan build,
tab create, PTY spawn, startup-command injection) could ship unnoticed.

Adds a golden spec that launches a stub agent from the menu and asserts the
agent's own banner reached the pane — a tab that spawned a bare shell instead
is indistinguishable at the store/tab layer. Runs two agents everywhere, and
on Windows also PowerShell, cmd, Git Bash and a WSL project runtime.

* test(e2e): track WSL stub agent staging state for precise cleanup

Refactor `stageWslGoldenStubAgent` to track which artifacts it creates
during setup, then only remove those artifacts during cleanup. This
prevents the test from destructively removing pre-existing symlinks or
state from previous runs, improving test isolation and idempotency.

* test(e2e): track WSL stub agent staging state for precise cleanup

- Back up and restore pre-existing stub agents to avoid destroying them
- Simplify verbose test comments to match project style guidelines

* test(e2e): serialize WSL stub agent setup with distributed lock

- Add mkdir-based lock to prevent concurrent staging invocations
- Reclaim stale locks after 10 minutes to recover from crashes
- Track lock ownership in stage state for safe cleanup

* test(e2e): track WSL stub agent staging state for precise cleanup

Track which stubs this test helper stages by writing a marker file, then
only remove stubs during stale-lock recovery if we created them. Prevents
cleanup from removing stubs left by other processes.
2026-08-24 08:58:19 -07:00

62 lines
2.3 KiB
TypeScript

import path from 'node:path'
import type { Page } from '@stablyai/playwright-test'
import { expect } from '@stablyai/playwright-test'
import { focusActiveTerminalInput, waitForTerminalOutput } from './terminal'
import type { BuiltInWindowsTerminalShell } from '../../../src/shared/windows-terminal-shell'
export const GOLDEN_STUB_READY_MARKER = 'GOLDEN_STUB_AGENT_READY'
export const GOLDEN_STUB_EXIT_MARKER = 'GOLDEN_STUB_AGENT_EXITED'
/** Agents exposed by the fixture directory for tab-bar detection. */
export const GOLDEN_STUB_AGENTS = [
{ id: 'codex', menuItemName: /^Codex(?:\s|$)/i },
{ id: 'claude', menuItemName: /^Claude(?:\s|$)/i }
] as const
const fixtureDir = path.join(process.cwd(), 'tests', 'e2e', 'fixtures', 'golden-stub-agent')
export function getGoldenStubAgentLaunchEnv(): NodeJS.ProcessEnv {
const pathKey = Object.keys(process.env).find((key) => key.toLowerCase() === 'path') ?? 'PATH'
return {
[pathKey]: [fixtureDir, process.env[pathKey] ?? ''].filter(Boolean).join(path.delimiter)
}
}
export async function configureGoldenStubAgent(
page: Page,
options: {
agent?: (typeof GOLDEN_STUB_AGENTS)[number]['id']
/** Windows default shell the launch command must survive; ignored elsewhere. */
windowsShell?: BuiltInWindowsTerminalShell
} = {}
): Promise<void> {
const agent = options.agent ?? 'codex'
await page.evaluate(
async ({ agent, windowsShell }) => {
const store = window.__store
if (!store) {
throw new Error('Orca store is unavailable')
}
await store.getState().updateSettings({
defaultTuiAgent: agent,
agentCmdOverrides: { [agent]: 'golden-stub-agent' },
agentDefaultArgs: { [agent]: '' },
...(windowsShell ? { terminalWindowsShell: windowsShell } : {})
})
},
{ agent, windowsShell: options.windowsShell ?? null }
)
}
export async function launchGoldenStubAgentFromNewTab(
page: Page,
menuItemName: RegExp = /^Codex(?:\s|$)/i
): Promise<void> {
await page.getByRole('button', { name: 'New tab' }).click({ force: true })
const launchOption = page.getByRole('menuitem', { name: menuItemName }).first()
await expect(launchOption).toBeVisible({ timeout: 15_000 })
await launchOption.click({ force: true })
await focusActiveTerminalInput(page)
await waitForTerminalOutput(page, GOLDEN_STUB_READY_MARKER, 20_000)
}