diff --git a/config/scripts/package-electron-runtime-contract.test.mjs b/config/scripts/package-electron-runtime-contract.test.mjs index 950d5ed258a..aa34e043268 100644 --- a/config/scripts/package-electron-runtime-contract.test.mjs +++ b/config/scripts/package-electron-runtime-contract.test.mjs @@ -579,6 +579,9 @@ describe('Electron runtime package contract', () => { expect(packageScripts['test:e2e:terminal-rendering-golden']).not.toContain( 'terminal-long-table-scroll-restore.spec.ts' ) + const goldenCommand = packageScripts['test:e2e:terminal-rendering-golden'] + expect(goldenCommand).toContain('--project electron-headless') + expect(goldenCommand).toContain('--project electron-headful') expect(packageScripts['test:e2e:windows-fresh-startup-golden']).toContain( 'golden-windows-fresh-startup.spec.ts' ) diff --git a/package.json b/package.json index 7b27dffc8c7..25152e84849 100644 --- a/package.json +++ b/package.json @@ -105,7 +105,7 @@ "test:e2e:workspace-session-golden": "pnpm run ensure:electron-runtime && npx playwright test tests/e2e/golden-quit-relaunch-session.spec.ts tests/e2e/golden-terminal-file-link.spec.ts tests/e2e/golden-worktree-create-switch.spec.ts --config tests/playwright.config.ts --project electron-headless --workers=1", "test:e2e:multi-client-navigation": "node config/scripts/run-multi-client-navigation-e2e.mjs", "test:e2e:floating-mobile-emulator": "pnpm run ensure:electron-runtime && npx playwright test tests/e2e/floating-mobile-emulator-tab.spec.ts --config tests/playwright.config.ts --project electron-headless --workers=1", - "test:e2e:terminal-rendering-golden": "pnpm run ensure:electron-runtime && npx playwright test tests/e2e/terminal-raw-emoji-table-scroll-restore.spec.ts tests/e2e/terminal-webgl-atlas-budget.spec.ts --grep @terminal-rendering-golden --config tests/playwright.config.ts --project electron-headless --workers=1", + "test:e2e:terminal-rendering-golden": "pnpm run ensure:electron-runtime && npx playwright test tests/e2e/terminal-raw-emoji-table-scroll-restore.spec.ts tests/e2e/terminal-webgl-atlas-budget.spec.ts --grep @terminal-rendering-golden --config tests/playwright.config.ts --project electron-headless --project electron-headful --workers=1", "test:e2e:source-control-golden": "pnpm run ensure:electron-runtime && npx playwright test tests/e2e/golden-file-open-edit-save.spec.ts tests/e2e/golden-source-control-commit.spec.ts tests/e2e/golden-source-control-open-diff.spec.ts --grep @golden --config tests/playwright.config.ts --project electron-headless --workers=1", "test:e2e:posix-profile-index-golden": "pnpm run ensure:electron-runtime && npx playwright test tests/e2e/golden-posix-fresh-startup.spec.ts tests/e2e/golden-posix-profile-index-fsync.spec.ts --grep @posix-profile-index-golden --config tests/playwright.config.ts --project electron-headless --workers=1", "test:e2e:windows-fresh-startup-golden": "pnpm run ensure:electron-runtime && npx playwright test tests/e2e/golden-windows-fresh-startup.spec.ts --grep @windows-fresh-startup-golden --config tests/playwright.config.ts --project electron-headless --workers=1", diff --git a/tests/e2e/helpers/electron-launch-args.ts b/tests/e2e/helpers/electron-launch-args.ts index 9128a5fb551..6868f48b083 100644 --- a/tests/e2e/helpers/electron-launch-args.ts +++ b/tests/e2e/helpers/electron-launch-args.ts @@ -11,6 +11,16 @@ export function getOrcaElectronLaunchArgs(mainPath: string, headful: boolean): s // Crash tests must not block later launches on AppKit's saved-window recovery dialog. return [...keychainArgs, appPath, '-ApplePersistenceIgnoreState', 'YES'] } + if (headful && process.platform === 'linux' && process.env.CI) { + // Hosted runners have no GPU; SwiftShader keeps WebGL assertions from silently skipping. + return [ + '--use-gl=angle', + '--use-angle=swiftshader', + '--enable-unsafe-swiftshader', + '--disable-gpu-sandbox', + appPath + ] + } if (headful || process.platform !== 'linux') { return [...keychainArgs, appPath] } diff --git a/tests/e2e/helpers/electron-launch-args.unit.test.ts b/tests/e2e/helpers/electron-launch-args.unit.test.ts index 636299fbc4e..c538d6f9a85 100644 --- a/tests/e2e/helpers/electron-launch-args.unit.test.ts +++ b/tests/e2e/helpers/electron-launch-args.unit.test.ts @@ -1,8 +1,31 @@ import { join } from 'node:path' -import { describe, expect, it } from 'vitest' +import { afterEach, describe, expect, it, vi } from 'vitest' import { getOrcaElectronLaunchArgs } from './electron-launch-args' describe('getOrcaElectronLaunchArgs', () => { + afterEach(() => vi.unstubAllGlobals()) + + it.each([ + ['linux', 'true', true, true], + ['linux', undefined, true, false], + ['linux', 'true', false, false], + ['darwin', 'true', true, false], + ['win32', 'true', true, false] + ] as const)( + 'scopes software WebGL to Linux CI headful launches: %s/%s/%s', + (platform, ci, headful, enabled) => { + vi.stubGlobal('process', { ...process, platform, env: { ...process.env, CI: ci } }) + const args = getOrcaElectronLaunchArgs(join('orca', 'out', 'main', 'index.js'), headful) + expect(args.includes('--use-gl=angle')).toBe(enabled) + expect(args.includes('--use-angle=swiftshader')).toBe(enabled) + expect(args.includes('--enable-unsafe-swiftshader')).toBe(enabled) + if (enabled) { + expect(args).toContain('--disable-gpu-sandbox') + expect(args).not.toContain('--disable-gpu') + } + } + ) + it('launches the package root that owns the compiled main entry', () => { const root = join('workspace', 'orca') const mainPath = join(root, 'out', 'main', 'index.js') diff --git a/tests/e2e/terminal-document-visibility-webgl-recovery.spec.ts b/tests/e2e/terminal-document-visibility-webgl-recovery.spec.ts index 1aa355b317e..d189df075c5 100644 --- a/tests/e2e/terminal-document-visibility-webgl-recovery.spec.ts +++ b/tests/e2e/terminal-document-visibility-webgl-recovery.spec.ts @@ -281,7 +281,7 @@ async function dispatchDocumentVisibilityCycle(page: Page): Promise { } test.describe('terminal document visibility WebGL recovery', () => { - test('preserves the WebGL atlas and keeps terminal text painted after document visibility resumes', async ({ + test('@headful preserves the WebGL atlas and keeps terminal text painted after document visibility resumes', async ({ electronApp, orcaPage }, testInfo) => { diff --git a/tests/e2e/terminal-foreground-redraw-freeze.spec.ts b/tests/e2e/terminal-foreground-redraw-freeze.spec.ts index fd6fad6f42d..05630042fae 100644 --- a/tests/e2e/terminal-foreground-redraw-freeze.spec.ts +++ b/tests/e2e/terminal-foreground-redraw-freeze.spec.ts @@ -339,7 +339,7 @@ function annotateMeasurement( } test.describe('Terminal foreground redraw freeze repro', () => { - test('Codex-style line rewrites request a visible row refresh', async ({ + test('@headful Codex-style line rewrites request a visible row refresh', async ({ orcaPage }, testInfo) => { await waitForSessionReady(orcaPage) diff --git a/tests/e2e/terminal-tab-switch-visual-restore.spec.ts b/tests/e2e/terminal-tab-switch-visual-restore.spec.ts index 938dc852179..f12e7a7a60a 100644 --- a/tests/e2e/terminal-tab-switch-visual-restore.spec.ts +++ b/tests/e2e/terminal-tab-switch-visual-restore.spec.ts @@ -794,7 +794,7 @@ test.describe('Terminal tab switch visual restore', () => { .toContain(marker) }) - test('keeps returned tab glyphs intact across tab switches', async ({ orcaPage }, testInfo) => { + test('@headful keeps returned tab glyphs intact across tab switches', async ({ orcaPage }, testInfo) => { // Why: screenshot equality catches WebGL atlas corruption on the tab being // resumed, not just stale cols/rows geometry checks. await waitForSessionReady(orcaPage) diff --git a/tests/e2e/terminal-webgl-atlas-budget.spec.ts b/tests/e2e/terminal-webgl-atlas-budget.spec.ts index 70b9a87c9d6..229140a3d35 100644 --- a/tests/e2e/terminal-webgl-atlas-budget.spec.ts +++ b/tests/e2e/terminal-webgl-atlas-budget.spec.ts @@ -431,7 +431,7 @@ async function runAtlasReplacementScenario(page: Page): Promise { test.describe.configure({ timeout: 120_000 }) - test('keeps shared glyph pages bindable through overflow and recovery @terminal-rendering-golden', async ({ + test('@headful keeps shared glyph pages bindable through overflow and recovery @terminal-rendering-golden', async ({ orcaPage }) => { await waitForActiveTerminalManager(orcaPage) @@ -451,7 +451,7 @@ test.describe('terminal WebGL atlas budget', () => { expect(result.pixelDiffAfterWipe).toBe(0) }) - test('rebuilds cached vertices after attaching a different shared atlas @terminal-rendering-golden', async ({ + test('@headful rebuilds cached vertices after attaching a different shared atlas @terminal-rendering-golden', async ({ orcaPage }) => { await waitForActiveTerminalManager(orcaPage)