Files
orca/tests/e2e/global-setup.ts
T
ff27338299 refactor(ssh): connect fails for hosts orcad can't serve; remove the relay client path (#27214)
* wip: connect fails for hosts the managed server can't serve; remove relay session, deploy and relay UI

* wip: delete relay-only client modules left unreachable, prune their tests and gates

* wip: format

* wip: exhaustive unserved message

* wip: drop relay attestation test for the deleted ownership proof

* fix: prune stale baselines; type the test transport stub

* fix(ssh): census proves leftover relay leases; name the remedy for older terminals; translate every connect-failure surface

* test(ssh): leftover leases defer to the host census

* test(e2e): remove relay-path SSH e2e specs and their CI lanes

* test(e2e): drop the auto-convert case that built its relay era through a live relay

* test(e2e): mark relay-era browser and catalog journeys fixme until they seed a profile

* test(e2e): seed relay-era browser and catalog journeys from a saved profile

The three journeys built their old-version state through a connect without an orcad template, which now refuses the host. They now start from a seeded relay-era profile, so the first connect converts the host as an upgrading user's would.

* chore(ci): drop shard timings for the deleted relay tests

* test(e2e): carry a relay-era login cookie across the browser conversion; own every launch

* test(ci): drop the deleted SSH layout oracle from its route

* test(e2e): open the retained browser after conversion, retrying a pre-connect card

* test(e2e): bind the relay-era browser partition before seeding its cookie

A relay-era build always recorded the partition binding before a page stored
data in it. Seeding the cookie into an unbound partition made the binding store
refuse it (browser_route_partition_binding_store_invalid), so the retained tab
showed the generic routing card and Retry could never clear it.

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: m4air <m4air@Mac.localdomain>
Co-authored-by: Claude <noreply@anthropic.com>
2026-10-10 22:10:33 -07:00

167 lines
7.4 KiB
TypeScript

/**
* Playwright globalSetup: builds the Electron app and creates a test git repo.
*
* Why: _electron.launch() needs the compiled output in out/main/index.js.
* Running electron-vite build here ensures the tests are always against
* the current source, without requiring the user to remember a manual step.
*
* Why: a dedicated test repo makes the suite idempotent — tests don't
* depend on whatever the user has open. The repo path is written to a
* temp file so the worker fixture can pick it up at runtime.
*/
import { execSync } from 'node:child_process'
import { randomUUID } from 'node:crypto'
import { existsSync, mkdirSync, mkdtempSync, realpathSync, writeFileSync } from 'node:fs'
import path from 'node:path'
import os from 'node:os'
import { prepareDockerSshRelayImage } from './helpers/docker-ssh-relay-image'
export const E2E_TEST_REPO_PATH_FILE_ENV = 'ORCA_E2E_TEST_REPO_PATH_FILE'
const RUN_TEST_REPO_PATH_FILE =
process.env[E2E_TEST_REPO_PATH_FILE_ENV] ??
path.join(os.tmpdir(), `orca-e2e-test-repo-path-${randomUUID()}.txt`)
export function workerTestRepositoryPathFile(runPathFile: string, workerIndex?: string): string {
if (workerIndex === undefined) {
return runPathFile
}
if (!/^\d+$/.test(workerIndex)) {
throw new Error('Invalid Playwright worker index')
}
return `${runPathFile}.worker-${workerIndex}`
}
// Restart helpers and the app fixture must read the same worker-owned repository.
export const TEST_REPO_PATH_FILE = workerTestRepositoryPathFile(
RUN_TEST_REPO_PATH_FILE,
process.env.TEST_WORKER_INDEX
)
const ELECTRON_E2E_BUILD_TIMEOUT_MS = 300_000
const CLI_E2E_BUILD_TIMEOUT_MS = 120_000
const WEB_E2E_BUILD_TIMEOUT_MS = 300_000
export default function globalSetup(): void {
// Why: workers and teardown need this run's path, never another concurrent run's.
process.env[E2E_TEST_REPO_PATH_FILE_ENV] = TEST_REPO_PATH_FILE
const root = process.cwd()
const outMain = path.join(root, 'out', 'main', 'index.js')
const outCli = path.join(root, 'out', 'cli', 'index.js')
const outWeb = path.join(root, 'out', 'web', 'web-index.html')
const outLinuxRelay = path.join(root, 'out', 'relay', 'linux-x64', 'relay.js')
// ── 1. Build the Electron app ──────────────────────────────────────
if (process.env.SKIP_BUILD && existsSync(outMain)) {
console.error('[e2e] SKIP_BUILD set and out/main/index.js exists — skipping build')
} else {
// Why: --mode e2e is the build-time signal that exposes window.__store;
// the explicit env var keeps older local overrides working too.
console.error('[e2e] Building Electron app with electron-vite build --mode e2e...')
execSync('npx electron-vite build --mode e2e', {
env: { ...process.env, VITE_EXPOSE_STORE: 'true' },
cwd: root,
stdio: 'inherit',
// Why: Windows renderer builds can exceed 120s on local/CI hosts even
// when healthy; global setup should not fail before specs can run.
timeout: ELECTRON_E2E_BUILD_TIMEOUT_MS
})
console.error('[e2e] Build complete.')
}
if (process.env.SKIP_BUILD && existsSync(outCli)) {
console.error('[e2e] SKIP_BUILD set and out/cli/index.js exists — skipping CLI build')
// Artifact downloads lose executable bits; each runner still needs its local dev launcher.
execSync('pnpm run prepare:cli-output', {
cwd: root,
stdio: 'inherit',
timeout: CLI_E2E_BUILD_TIMEOUT_MS
})
} else {
console.error('[e2e] Building bundled CLI...')
execSync('pnpm run build:cli', {
cwd: root,
stdio: 'inherit',
timeout: CLI_E2E_BUILD_TIMEOUT_MS
})
console.error('[e2e] CLI build complete.')
}
if (process.env.ORCA_E2E_WEB_CLIENT === '1') {
if (process.env.SKIP_BUILD && existsSync(outWeb)) {
console.error('[e2e] SKIP_BUILD set and web client exists — skipping web build')
} else {
// Why: paired-browser specs need the web bundle served by the runtime; ordinary Electron E2E does not.
console.error('[e2e] Building paired runtime web client...')
execSync('pnpm run build:web', {
env: { ...process.env, VITE_EXPOSE_STORE: 'true' },
cwd: root,
stdio: 'inherit',
timeout: WEB_E2E_BUILD_TIMEOUT_MS
})
console.error('[e2e] Web client build complete.')
}
}
if (
process.env.ORCA_E2E_SSH_DOCKER === '1' ||
(process.env.ORCA_E2E_SKILL_STAGING === '1' && Boolean(process.env.ORCA_E2E_SKILL_SSH_HOST))
) {
if (process.env.SKIP_BUILD && existsSync(outLinuxRelay)) {
console.error('[e2e] SKIP_BUILD set and SSH relay bundle exists — skipping relay build')
} else {
// Why: explicit SSH specs need deployable Relay outputs in source-tree runs.
console.error('[e2e] Building SSH relay bundle for SSH E2E...')
execSync('pnpm run build:relay', {
cwd: root,
stdio: 'inherit',
timeout: 120_000
})
}
}
if (process.env.ORCA_E2E_SSH_DOCKER === '1') {
console.error('[e2e] Preparing Docker OpenSSH fixture image...')
prepareDockerSshRelayImage(root)
}
// ── 2. Create a seeded test git repo ───────────────────────────────
// Why: each test run gets its own git repo so the suite is fully
// idempotent. No test depends on whatever repos the user has open.
// Why: realpathSync so the seeded path matches the store's repo.path on
// macOS, where os.tmpdir() (/var/...) symlinks to /private/var/... and the
// app canonicalizes repo.path via `git rev-parse --show-toplevel` on add.
const testRepoDir = realpathSync(mkdtempSync(path.join(os.tmpdir(), 'orca-e2e-repo-')))
execSync('git init', { cwd: testRepoDir, stdio: 'pipe' })
execSync('git config user.email "e2e@test.local"', { cwd: testRepoDir, stdio: 'pipe' })
execSync('git config user.name "E2E Test"', { cwd: testRepoDir, stdio: 'pipe' })
// Seed test data files
writeFileSync(
path.join(testRepoDir, 'README.md'),
'# Orca E2E Test Repo\n\nThis repo was created automatically for Playwright tests.\n'
)
writeFileSync(path.join(testRepoDir, 'CLAUDE.md'), '# CLAUDE.md\n\nTest instructions for E2E.\n')
writeFileSync(
path.join(testRepoDir, 'package.json'),
`${JSON.stringify({ name: 'orca-e2e-test', version: '0.0.0', private: true }, null, 2)}\n`
)
writeFileSync(path.join(testRepoDir, '.gitignore'), 'node_modules/\n')
mkdirSync(path.join(testRepoDir, 'src'), { recursive: true })
writeFileSync(path.join(testRepoDir, 'src', 'index.ts'), 'export const hello = "world"\n')
writeFileSync(path.join(testRepoDir, 'src', 'diff-note-layout.ts'), 'export const seed = true\n')
execSync('git add -A', { cwd: testRepoDir, stdio: 'pipe' })
execSync('git commit -m "Initial commit for E2E tests"', { cwd: testRepoDir, stdio: 'pipe' })
// Why: several tests verify worktree-switching behavior (terminal content
// retention, browser tab retention). They need at least 2 worktrees.
// Creating one here makes those tests run instead of being skipped.
const worktreeDir = path.join(testRepoDir, '..', `orca-e2e-worktree-${randomUUID()}`)
execSync(`git worktree add "${worktreeDir}" -b e2e-secondary`, {
cwd: testRepoDir,
stdio: 'pipe'
})
console.error(`[e2e] Secondary worktree created at ${worktreeDir}`)
// Write the test repo path so the fixture can read it
writeFileSync(TEST_REPO_PATH_FILE, testRepoDir)
console.error(`[e2e] Test repo created at ${testRepoDir}`)
}