From 2c50a15ed8dd3cfbef407682bfdf1084e49421c8 Mon Sep 17 00:00:00 2001 From: Jinjing <6427696+AmethystLiang@users.noreply.github.com> Date: Sun, 23 Aug 2026 14:41:12 -0700 Subject: [PATCH] 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 --- tests/e2e/golden-tab-bar-agent-launch.spec.ts | 17 ++----- tests/e2e/helpers/wsl-golden-stub-agent.ts | 51 ++++++++++++------- 2 files changed, 39 insertions(+), 29 deletions(-) diff --git a/tests/e2e/golden-tab-bar-agent-launch.spec.ts b/tests/e2e/golden-tab-bar-agent-launch.spec.ts index 378082d112a..d8501c0a668 100644 --- a/tests/e2e/golden-tab-bar-agent-launch.spec.ts +++ b/tests/e2e/golden-tab-bar-agent-launch.spec.ts @@ -43,14 +43,12 @@ for (const { id, menuItemName } of GOLDEN_STUB_AGENTS) { const activeTab = orcaPage.locator('[data-testid="sortable-tab"][data-active="true"]') await expect(activeTab).toHaveAttribute('data-tab-title', /Golden Stub Agent|Codex|Claude/i) - // Why the banner and not just the tab: a tab that spawned a bare shell - // instead of the agent looks identical at the store/tab layer. + // The marker distinguishes an agent launch from an identical bare-shell tab. expect(await getTerminalContent(orcaPage)).toContain(GOLDEN_STUB_READY_MARKER) }) } -// Why describe-scoped and not a body-level skip: `orcaPage` builds before the -// test body, so a body-level skip still pays a full Electron launch per case. +// Suite-level skipping avoids launching Electron for each unsupported case. test.describe('Windows runtimes', () => { test.skip(process.platform !== 'win32', 'Windows agent launch matrix is Windows-only') @@ -59,9 +57,7 @@ test.describe('Windows runtimes', () => { orcaPage }) => { await openWorkspaceTerminal(orcaPage) - // Why each shell: the queued launch command is quoted for the shell family - // the tab actually spawns (posix for Git Bash, cmd, PowerShell). A mismatch - // injects a command the shell rejects, and the agent never starts. + // Each shell family requires different launch-command quoting. await configureGoldenStubAgent(orcaPage, { agent: 'codex', windowsShell: shell }) await launchGoldenStubAgentFromNewTab(orcaPage) @@ -80,15 +76,12 @@ test.describe('Windows runtimes', () => { test.skip(!stage, 'WSL distro would not accept the staged stub agent') try { - // Why the runtime switch and not terminalWindowsShell: WSL is a project - // runtime, so it must retarget agent detection into the distro as well as - // the PTY. A launch quoted for a Windows shell never starts there. + // WSL must retarget both agent detection and the PTY. await useWslRuntimeForActiveProject(orcaPage, distro!) await configureGoldenStubAgent(orcaPage, { agent: 'codex' }) await launchGoldenStubAgentFromNewTab(orcaPage) - // The stub only exists inside the distro, so its marker proves the agent - // ran in WSL rather than on the Windows host. + // The distro-only marker proves the agent ran in WSL. expect(await getTerminalContent(orcaPage)).toContain(GOLDEN_STUB_READY_MARKER) } finally { removeWslGoldenStubAgent(distro!, stage!) diff --git a/tests/e2e/helpers/wsl-golden-stub-agent.ts b/tests/e2e/helpers/wsl-golden-stub-agent.ts index 8be1eb71245..84e7c80974d 100644 --- a/tests/e2e/helpers/wsl-golden-stub-agent.ts +++ b/tests/e2e/helpers/wsl-golden-stub-agent.ts @@ -5,9 +5,17 @@ import { buildWslExecArgs } from '../../../src/shared/wsl-login-shell-command' /** A WSL-only path makes the stub marker proof that the pane ran in the distro. */ const WSL_STUB_PATH = '/usr/local/bin/golden-stub-agent' const WSL_STUB_AGENT_LINK = '/usr/local/bin/codex' +const WSL_STUB_BACKUP_PATH = '/usr/local/bin/golden-stub-agent.orca-e2e-backup' + +// Keep the cross-boundary script newline-free to avoid Windows argv-encoding surprises. +// Moving the entry avoids following and overwriting a pre-existing symlink. +const BACKUP_EXISTING_STUB_SCRIPT = + `mkdir -p /usr/local/bin && ` + + `if [ -e ${WSL_STUB_BACKUP_PATH} ] || [ -L ${WSL_STUB_BACKUP_PATH} ]; then exit 1; fi && ` + + `if [ -e ${WSL_STUB_PATH} ] || [ -L ${WSL_STUB_PATH} ]; then ` + + `mv ${WSL_STUB_PATH} ${WSL_STUB_BACKUP_PATH} && ` + + `printf backed-up; else printf none; fi` -// Why printf and not a heredoc: the whole script crosses to wsl.exe as one argv -// element, and keeping it newline-free avoids Windows argv-encoding surprises. const STAGE_SCRIPT = `mkdir -p /usr/local/bin && ` + `printf '#!/bin/sh\\necho GOLDEN_STUB_AGENT_READY\\nexec sleep 3600\\n' > ${WSL_STUB_PATH} && ` + @@ -17,11 +25,13 @@ const STAGE_CODEX_LINK_IF_MISSING_SCRIPT = `if [ -e ${WSL_STUB_AGENT_LINK} ] || [ -L ${WSL_STUB_AGENT_LINK} ]; then ` + `printf existing; else ln -s ${WSL_STUB_PATH} ${WSL_STUB_AGENT_LINK} && printf created; fi` -const REMOVE_STUB_SCRIPT = `rm -f ${WSL_STUB_PATH}` -const REMOVE_CODEX_LINK_AND_STUB_SCRIPT = `rm -f ${WSL_STUB_AGENT_LINK} ${WSL_STUB_PATH}` +function buildRestoreScript(stage: WslGoldenStubAgentStage): string { + const removed = stage.createdCodexLink ? `${WSL_STUB_AGENT_LINK} ${WSL_STUB_PATH}` : WSL_STUB_PATH + const restore = stage.backedUpStub ? ` && mv ${WSL_STUB_BACKUP_PATH} ${WSL_STUB_PATH}` : '' + return `rm -f ${removed}${restore}` +} -// Why --exec (via buildWslExecArgs): under `--`, wsl.exe expands `$name` in -// every argument and silently rewrites the script. +// --exec prevents wsl.exe from expanding shell variables in argv. function runInWslAsRoot(distro: string, script: string): string { return execFileSync( 'wsl.exe', @@ -40,35 +50,42 @@ export async function getFirstWslDistro(page: Page): Promise { export type WslGoldenStubAgentStage = { createdCodexLink: boolean + backedUpStub: boolean + ownsStubPath: boolean } /** Returns null when the distro cannot stage the stub. */ export function stageWslGoldenStubAgent(distro: string): WslGoldenStubAgentStage | null { + const stage: WslGoldenStubAgentStage = { + createdCodexLink: false, + backedUpStub: false, + ownsStubPath: false + } try { + stage.backedUpStub = runInWslAsRoot(distro, BACKUP_EXISTING_STUB_SCRIPT).trim() === 'backed-up' + stage.ownsStubPath = true runInWslAsRoot(distro, STAGE_SCRIPT) - return { - createdCodexLink: - runInWslAsRoot(distro, STAGE_CODEX_LINK_IF_MISSING_SCRIPT).trim() === 'created' - } + stage.createdCodexLink = + runInWslAsRoot(distro, STAGE_CODEX_LINK_IF_MISSING_SCRIPT).trim() === 'created' + return stage } catch { - removeWslGoldenStubAgent(distro, { createdCodexLink: false }) + removeWslGoldenStubAgent(distro, stage) return null } } export function removeWslGoldenStubAgent(distro: string, stage: WslGoldenStubAgentStage): void { + if (!stage.ownsStubPath) { + return + } try { - runInWslAsRoot( - distro, - stage.createdCodexLink ? REMOVE_CODEX_LINK_AND_STUB_SCRIPT : REMOVE_STUB_SCRIPT - ) + runInWslAsRoot(distro, buildRestoreScript(stage)) } catch { // Best-effort cleanup; a leftover stub only affects this fixture's own name. } } -/** Points the active worktree's project at the WSL runtime, so agent detection - * and terminal spawn both target the distro instead of the Windows host. */ +/** Retargets project agent detection and terminal spawning to WSL. */ export async function useWslRuntimeForActiveProject(page: Page, distro: string): Promise { await page.evaluate(async (wslDistro) => { const store = window.__store