import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs' import { homedir, tmpdir } from 'node:os' import { join } from 'node:path' import { afterEach, describe, expect, it, vi } from 'vitest' vi.mock('electron', () => { const paths = new Map([['appData', '/tmp/app-data']]) return { app: { getPath: vi.fn((name: string) => paths.get(name) ?? ''), setPath: vi.fn((name: string, value: string) => { paths.set(name, value) }), quit: vi.fn(), exit: vi.fn(), isPackaged: false, disableHardwareAcceleration: vi.fn(), commandLine: { appendSwitch: vi.fn(), getSwitchValue: vi.fn(() => '') } } } }) afterEach(() => { vi.useRealTimers() vi.restoreAllMocks() }) describe('patchPackagedProcessPath', () => { const originalPlatform = Object.getOwnPropertyDescriptor(process, 'platform') const originalHome = process.env.HOME const originalPath = process.env.PATH function setPlatform(platform: NodeJS.Platform): void { Object.defineProperty(process, 'platform', { configurable: true, value: platform }) } afterEach(() => { if (originalPlatform) { Object.defineProperty(process, 'platform', originalPlatform) } if (originalHome === undefined) { delete process.env.HOME } else { process.env.HOME = originalHome } if (originalPath === undefined) { delete process.env.PATH } else { process.env.PATH = originalPath } }) it('prepends agent-CLI install dirs (~/.opencode/bin, ~/.vite-plus/bin) for packaged darwin runs', async () => { const { app } = await import('electron') const { patchPackagedProcessPath } = await import('./configure-process') setPlatform('darwin') Object.defineProperty(app, 'isPackaged', { configurable: true, value: true }) process.env.HOME = '/Users/tester' process.env.PATH = '/usr/bin:/bin' patchPackagedProcessPath() const segments = (process.env.PATH ?? '').split(':') // Why: issue #829 — ~/.opencode/bin and ~/.vite-plus/bin are the documented // fallback install locations for the opencode and Pi CLI install scripts. // Without them on PATH, GUI-launched Orca reports both as "Not installed" // even when `which` resolves them in the user's shell. expect(segments).toContain(join('/Users/tester', '.opencode/bin')) expect(segments).toContain(join('/Users/tester', '.vite-plus/bin')) expect(segments).toContain(join('/Users/tester', 'bin')) }) it('omits Linux-only snap/Linuxbrew dirs but keeps Nix on packaged darwin runs', async () => { const { app } = await import('electron') const { patchPackagedProcessPath } = await import('./configure-process') setPlatform('darwin') Object.defineProperty(app, 'isPackaged', { configurable: true, value: true }) process.env.HOME = '/Users/tester' process.env.PATH = '/usr/bin:/bin' patchPackagedProcessPath() const segments = (process.env.PATH ?? '').split(':') // Why: neither has a macOS installer, so both are phantom PATH entries. expect(segments).not.toContain('/snap/bin') expect(segments).not.toContain('/home/linuxbrew/.linuxbrew/bin') // Why: Nix does ship a macOS default profile, so it stays seeded. expect(segments).toContain('/nix/var/nix/profiles/default/bin') expect(segments).toContain('/opt/homebrew/bin') expect(segments).toContain('/usr/local/bin') }) it('keeps snap, Linuxbrew, and Nix dirs for packaged linux runs', async () => { const { app } = await import('electron') const { patchPackagedProcessPath } = await import('./configure-process') setPlatform('linux') Object.defineProperty(app, 'isPackaged', { configurable: true, value: true }) process.env.HOME = '/home/tester' process.env.PATH = '/usr/bin:/bin' patchPackagedProcessPath() const segments = (process.env.PATH ?? '').split(':') expect(segments).toContain('/snap/bin') expect(segments).toContain('/home/linuxbrew/.linuxbrew/bin') expect(segments).toContain('/nix/var/nix/profiles/default/bin') expect(segments.indexOf('/usr/local/sbin')).toBeLessThan(segments.indexOf('/snap/bin')) expect(segments.indexOf('/home/linuxbrew/.linuxbrew/bin')).toBeLessThan( segments.indexOf('/nix/var/nix/profiles/default/bin') ) }) it('omits snap/Linuxbrew but keeps Nix on non-Linux POSIX platforms', async () => { const { app } = await import('electron') const { patchPackagedProcessPath } = await import('./configure-process') setPlatform('freebsd') Object.defineProperty(app, 'isPackaged', { configurable: true, value: true }) process.env.HOME = '/home/tester' process.env.PATH = '/usr/bin:/bin' patchPackagedProcessPath() const segments = (process.env.PATH ?? '').split(':') expect(segments).not.toContain('/snap/bin') expect(segments).not.toContain('/home/linuxbrew/.linuxbrew/bin') expect(segments).toContain('/nix/var/nix/profiles/default/bin') expect(segments).toContain('/usr/local/bin') }) // Why derived, not a second literal: system-cli-install-dirs.ts documents its // order as matching this seed's system block, and hardcoding the order in the // fallback's own test lets a reorder here break that parity while both stay green. it('seeds the system block in the order the install-dir fallback expects', async () => { const { app } = await import('electron') const { patchPackagedProcessPath } = await import('./configure-process') const { getSystemCliInstallDirectories } = await import('../../shared/system-cli-install-dirs') setPlatform('linux') Object.defineProperty(app, 'isPackaged', { configurable: true, value: true }) process.env.HOME = '/home/tester' process.env.PATH = '/usr/bin:/bin' patchPackagedProcessPath() const segments = (process.env.PATH ?? '').split(':') const offsets = getSystemCliInstallDirectories('linux', '/home/tester').map((directory) => segments.indexOf(directory) ) expect(offsets.every((offset) => offset >= 0)).toBe(true) expect([...offsets].sort((a, b) => a - b)).toEqual(offsets) }) // Why this ordering is load-bearing (#18234): a seed exists so a GUI-launched // Electron can *find* a tool, not to re-rank tools the user already has. // `~/.local/bin` is user-writable and can hold a wrapper for any system tool. // The reporter's `~/.local/bin/gh` wrapped `mise x gh -- gh`; seeded ahead of // /usr/bin it ran instead of the real gh, and the wrapper's inner bare `gh` // resolved back to itself. Measured in a container: with the login shell's // ordering that chain exits in 22ms, with the seeded ordering it never // terminates and creates ~1,300 processes/second. it('never lets a seeded user dir overtake a system dir already on PATH', async () => { const { app } = await import('electron') const { patchPackagedProcessPath } = await import('./configure-process') setPlatform('linux') Object.defineProperty(app, 'isPackaged', { configurable: true, value: true }) process.env.HOME = '/home/tester' process.env.PATH = '/usr/local/bin:/usr/bin:/bin' patchPackagedProcessPath() const segments = (process.env.PATH ?? '').split(':') const localBin = segments.indexOf(join('/home/tester', '.local/bin')) // Still reachable — that is what the seeding is for (#829). expect(localBin).toBeGreaterThan(-1) for (const systemDir of ['/usr/bin', '/bin', '/usr/local/bin']) { expect(segments.indexOf(systemDir)).toBeLessThan(localBin) } expect(segments.indexOf(join('/home/tester', 'bin'))).toBeGreaterThan( segments.indexOf('/usr/bin') ) }) it('keeps version-manager shims ahead of the inherited PATH', async () => { const { app } = await import('electron') const { patchPackagedProcessPath } = await import('./configure-process') const { getVersionManagerBinPaths } = await import('../../shared/node-cli-command-resolution') setPlatform('linux') Object.defineProperty(app, 'isPackaged', { configurable: true, value: true }) process.env.HOME = '/home/tester' process.env.PATH = '/usr/bin:/bin' patchPackagedProcessPath() const segments = (process.env.PATH ?? '').split(':') const genericUserBinDirs = [join('/home/tester', 'bin'), join('/home/tester', '.local/bin')] const seeded = getVersionManagerBinPaths({ platform: 'linux', homePath: '/home/tester' }) const shimDirs = seeded.filter((dir) => !genericUserBinDirs.includes(dir)) expect(shimDirs).not.toHaveLength(0) // Why these keep leading: an nvm/mise/asdf user's runtime must beat a // system install, which is the reason this seeding is ordered at all. for (const dir of shimDirs) { expect(segments.indexOf(dir)).toBeLessThan(segments.indexOf('/usr/bin')) } // Why these do not: the same list carries the generic user bin dirs, which // hold whatever was last installed there rather than a managed toolchain. for (const dir of genericUserBinDirs) { expect(segments.indexOf(dir)).toBeGreaterThan(segments.indexOf('/usr/bin')) } }) it('leaves PATH untouched when the app is not packaged', async () => { const { app } = await import('electron') const { patchPackagedProcessPath } = await import('./configure-process') setPlatform('darwin') Object.defineProperty(app, 'isPackaged', { configurable: true, value: false }) process.env.HOME = '/Users/tester' process.env.PATH = '/usr/bin:/bin' patchPackagedProcessPath() expect(process.env.PATH).toBe('/usr/bin:/bin') }) it('prepends Windows user-local CLI dirs for packaged Start Menu launches', async () => { const { app } = await import('electron') const { patchPackagedProcessPath } = await import('./configure-process') setPlatform('win32') Object.defineProperty(app, 'isPackaged', { configurable: true, value: true }) const pathDelimiter = process.platform === 'win32' ? ';' : ':' process.env.PATH = `C:\\Windows\\System32${pathDelimiter}C:\\Windows` patchPackagedProcessPath() const segments = (process.env.PATH ?? '').split(pathDelimiter) const userLocalBin = join(homedir(), '.local', 'bin') expect(segments).toContain(userLocalBin) expect(segments.indexOf(userLocalBin)).toBeLessThan(segments.indexOf('C:\\Windows\\System32')) }) }) describe('configureDevUserDataPath', () => { it('forces Electron home into the disposable E2E profile', async () => { const { app } = await import('electron') const { configureDevUserDataPath } = await import('./configure-process') const originalE2EUserDataDir = process.env.ORCA_E2E_USER_DATA_DIR const originalE2EHomeDir = process.env.ORCA_E2E_HOME_DIR const originalHome = process.env.HOME const originalUserProfile = process.env.USERPROFILE const tempRoot = mkdtempSync(join(tmpdir(), 'orca-configure-e2e-home-')) const e2eRoot = join(tempRoot, 'user-data') const e2eHome = join(tempRoot, 'home') process.env.ORCA_E2E_USER_DATA_DIR = e2eRoot process.env.ORCA_E2E_HOME_DIR = e2eHome process.env.HOME = e2eHome process.env.USERPROFILE = e2eHome try { configureDevUserDataPath(true) } finally { rmSync(tempRoot, { recursive: true, force: true }) if (originalE2EUserDataDir === undefined) { delete process.env.ORCA_E2E_USER_DATA_DIR } else { process.env.ORCA_E2E_USER_DATA_DIR = originalE2EUserDataDir } if (originalE2EHomeDir === undefined) { delete process.env.ORCA_E2E_HOME_DIR } else { process.env.ORCA_E2E_HOME_DIR = originalE2EHomeDir } if (originalHome === undefined) { delete process.env.HOME } else { process.env.HOME = originalHome } if (originalUserProfile === undefined) { delete process.env.USERPROFILE } else { process.env.USERPROFILE = originalUserProfile } } expect(app.setPath).toHaveBeenCalledWith('home', e2eHome) expect(app.setPath).toHaveBeenCalledWith('userData', e2eRoot) }) it('rejects an E2E launch whose Node home escaped the disposable profile', async () => { const { configureDevUserDataPath } = await import('./configure-process') const originalE2EUserDataDir = process.env.ORCA_E2E_USER_DATA_DIR const originalE2EHomeDir = process.env.ORCA_E2E_HOME_DIR const originalHome = process.env.HOME const originalUserProfile = process.env.USERPROFILE const e2eRoot = mkdtempSync(join(tmpdir(), 'orca-configure-e2e-escape-')) process.env.ORCA_E2E_USER_DATA_DIR = e2eRoot process.env.ORCA_E2E_HOME_DIR = join(e2eRoot, 'home') process.env.HOME = join(e2eRoot, 'escaped-home') process.env.USERPROFILE = join(e2eRoot, 'escaped-home') try { expect(() => configureDevUserDataPath(true)).toThrow(/disposable home boundary/) } finally { rmSync(e2eRoot, { recursive: true, force: true }) restoreEnv('ORCA_E2E_USER_DATA_DIR', originalE2EUserDataDir) restoreEnv('ORCA_E2E_HOME_DIR', originalE2EHomeDir) restoreEnv('HOME', originalHome) restoreEnv('USERPROFILE', originalUserProfile) } }) it('uses an explicit dev userData override when provided', async () => { const { app } = await import('electron') const { configureDevUserDataPath } = await import('./configure-process') const originalOverride = process.env.ORCA_DEV_USER_DATA_PATH process.env.ORCA_DEV_USER_DATA_PATH = '/tmp/orca-dev-repro' try { configureDevUserDataPath(true) } finally { if (originalOverride === undefined) { delete process.env.ORCA_DEV_USER_DATA_PATH } else { process.env.ORCA_DEV_USER_DATA_PATH = originalOverride } } expect(app.setPath).toHaveBeenCalledWith('userData', '/tmp/orca-dev-repro') }) it('moves dev runs onto an orca-dev userData path', async () => { const { app } = await import('electron') const { configureDevUserDataPath } = await import('./configure-process') delete process.env.ORCA_DEV_USER_DATA_PATH configureDevUserDataPath(true) // Why: production code uses path.join(app.getPath('appData'), 'orca-dev') // which produces platform-specific separators. expect(app.setPath).toHaveBeenCalledWith('userData', join('/tmp/app-data', 'orca-dev')) }) it('leaves packaged runs on the default userData path', async () => { const { app } = await import('electron') const { configureDevUserDataPath } = await import('./configure-process') vi.mocked(app.setPath).mockClear() configureDevUserDataPath(false) expect(app.setPath).not.toHaveBeenCalled() }) }) function restoreEnv(key: string, value: string | undefined): void { if (value === undefined) { delete process.env[key] } else { process.env[key] = value } } describe('configureOrcaUserDataPathEnv', () => { it('overwrites stale inherited ORCA_USER_DATA_PATH with Electron userData', async () => { const { app } = await import('electron') const { configureOrcaUserDataPathEnv } = await import('./configure-process') const originalUserDataPath = process.env.ORCA_USER_DATA_PATH process.env.ORCA_USER_DATA_PATH = '/tmp/stale-orca-user-data' app.setPath('userData', '/tmp/current-orca-user-data') let configuredUserDataPath: string | undefined try { configureOrcaUserDataPathEnv() configuredUserDataPath = process.env.ORCA_USER_DATA_PATH } finally { if (originalUserDataPath === undefined) { delete process.env.ORCA_USER_DATA_PATH } else { process.env.ORCA_USER_DATA_PATH = originalUserDataPath } } expect(configuredUserDataPath).toBe('/tmp/current-orca-user-data') }) it('drops a runtime source inherited from the Orca terminal that started this Orca', async () => { const { app } = await import('electron') const { configureOrcaUserDataPathEnv } = await import('./configure-process') const saved = { ...process.env } process.env.ORCA_RUNTIME_SOURCE_ID = 'parent-source' process.env.ORCA_RUNTIME_SOURCE_INCARNATION = 'parent-runtime' app.setPath('userData', '/tmp/current-orca-user-data') let inherited: (string | undefined)[] = [] try { configureOrcaUserDataPathEnv() inherited = [process.env.ORCA_RUNTIME_SOURCE_ID, process.env.ORCA_RUNTIME_SOURCE_INCARNATION] } finally { process.env = saved } expect(inherited).toEqual([undefined, undefined]) }) }) describe('shouldInstallManagedHooks', () => { it('keeps managed hook auto-install enabled for default dev runs', async () => { const { shouldInstallManagedHooks } = await import('./configure-process') expect(shouldInstallManagedHooks(true)).toBe(true) }) it('allows managed hook auto-install for packaged runs', async () => { const { shouldInstallManagedHooks } = await import('./configure-process') expect(shouldInstallManagedHooks(false)).toBe(true) }) }) describe('configureElectronNetworkCompatibility', () => { const tempDirs: string[] = [] const originalEnvValue = process.env.ORCA_DISABLE_HTTP2 function createUserDataDir(settings: Record): string { const userDataPath = mkdtempSync(join(tmpdir(), 'orca-http1-compat-')) tempDirs.push(userDataPath) writeFileSync(join(userDataPath, 'orca-data.json'), JSON.stringify({ settings }), 'utf-8') return userDataPath } function createProfileState( userDataPath: string, profileId: string, settings: Record ): string { const profileDirectory = join(userDataPath, 'profiles', profileId) mkdirSync(profileDirectory, { recursive: true }) writeFileSync( join(userDataPath, 'orca-profile-index.json'), JSON.stringify({ activeProfileId: profileId, profiles: [{ id: profileId }] }), 'utf-8' ) writeFileSync(join(profileDirectory, 'orca-data.json'), JSON.stringify({ settings }), 'utf-8') return profileDirectory } afterEach(() => { for (const dir of tempDirs.splice(0)) { rmSync(dir, { recursive: true, force: true }) } if (originalEnvValue === undefined) { delete process.env.ORCA_DISABLE_HTTP2 } else { process.env.ORCA_DISABLE_HTTP2 = originalEnvValue } }) it('enables HTTP/1.1 compatibility when the persisted setting is on', async () => { const { shouldDisableHttp2ForElectronNetworking } = await import('./configure-process') const userDataPath = createUserDataDir({ electronHttp1CompatibilityMode: true }) expect(shouldDisableHttp2ForElectronNetworking({ env: {}, userDataPath })).toBe(true) }) it('leaves HTTP/2 enabled by default', async () => { const { shouldDisableHttp2ForElectronNetworking } = await import('./configure-process') const userDataPath = createUserDataDir({}) expect(shouldDisableHttp2ForElectronNetworking({ env: {}, userDataPath })).toBe(false) }) it('lets the environment override force compatibility on', async () => { const { shouldDisableHttp2ForElectronNetworking } = await import('./configure-process') expect( shouldDisableHttp2ForElectronNetworking({ env: { ORCA_DISABLE_HTTP2: 'true' }, userDataPath: createUserDataDir({ electronHttp1CompatibilityMode: false }) }) ).toBe(true) }) it('lets the environment override force compatibility off', async () => { const { shouldDisableHttp2ForElectronNetworking } = await import('./configure-process') expect( shouldDisableHttp2ForElectronNetworking({ env: { ORCA_DISABLE_HTTP2: '0' }, userDataPath: createUserDataDir({ electronHttp1CompatibilityMode: true }) }) ).toBe(false) }) it('answers from the marker without reading the settings file', async () => { const { shouldDisableHttp2ForElectronNetworking } = await import('./configure-process') const { writeHttp1CompatibilityMarker } = await import('./http1-compatibility-marker') const userDataPath = createUserDataDir({ electronHttp1CompatibilityMode: false }) writeHttp1CompatibilityMarker(userDataPath, true) rmSync(join(userDataPath, 'orca-data.json'), { force: true }) expect(shouldDisableHttp2ForElectronNetworking({ env: {}, userDataPath })).toBe(true) }) it('falls back to the settings file when no marker has been written yet', async () => { const { shouldDisableHttp2ForElectronNetworking } = await import('./configure-process') const userDataPath = createUserDataDir({ electronHttp1CompatibilityMode: true }) expect(existsSync(join(userDataPath, 'http1-compatibility.json'))).toBe(false) expect(shouldDisableHttp2ForElectronNetworking({ env: {}, userDataPath })).toBe(true) }) it('falls back to the settings file when the marker is corrupt', async () => { const { shouldDisableHttp2ForElectronNetworking } = await import('./configure-process') const userDataPath = createUserDataDir({ electronHttp1CompatibilityMode: true }) writeFileSync(join(userDataPath, 'http1-compatibility.json'), '{ not json', 'utf-8') expect(shouldDisableHttp2ForElectronNetworking({ env: {}, userDataPath })).toBe(true) }) it('lets the environment override the marker', async () => { const { shouldDisableHttp2ForElectronNetworking } = await import('./configure-process') const { writeHttp1CompatibilityMarker } = await import('./http1-compatibility-marker') const userDataPath = createUserDataDir({}) writeHttp1CompatibilityMarker(userDataPath, true) expect( shouldDisableHttp2ForElectronNetworking({ env: { ORCA_DISABLE_HTTP2: '0' }, userDataPath }) ).toBe(false) }) it('scopes a profile marker to the active profile before trusting it', async () => { const { shouldDisableHttp2ForElectronNetworking } = await import('./configure-process') const { writeHttp1CompatibilityMarker } = await import('./http1-compatibility-marker') const userDataPath = mkdtempSync(join(tmpdir(), 'orca-http1-profile-')) tempDirs.push(userDataPath) createProfileState(userDataPath, 'profile-b', { electronHttp1CompatibilityMode: false }) writeHttp1CompatibilityMarker(userDataPath, true, 'profile-a') expect(shouldDisableHttp2ForElectronNetworking({ env: {}, userDataPath })).toBe(false) }) it('uses a matching profile marker even when the legacy JSON is stale', async () => { const { shouldDisableHttp2ForElectronNetworking } = await import('./configure-process') const { writeHttp1CompatibilityMarker } = await import('./http1-compatibility-marker') const userDataPath = mkdtempSync(join(tmpdir(), 'orca-http1-profile-')) tempDirs.push(userDataPath) createProfileState(userDataPath, 'profile-b', { electronHttp1CompatibilityMode: false }) writeHttp1CompatibilityMarker(userDataPath, true, 'profile-b') expect(shouldDisableHttp2ForElectronNetworking({ env: {}, userDataPath })).toBe(true) }) it('fails closed when a profile database exists without a trusted marker', async () => { const { shouldDisableHttp2ForElectronNetworking } = await import('./configure-process') const userDataPath = mkdtempSync(join(tmpdir(), 'orca-http1-profile-')) tempDirs.push(userDataPath) const profileDirectory = createProfileState(userDataPath, 'profile-b', { electronHttp1CompatibilityMode: true }) writeFileSync(join(profileDirectory, 'profile-state.db'), 'sqlite-present', 'utf-8') expect(shouldDisableHttp2ForElectronNetworking({ env: {}, userDataPath })).toBe(false) }) it('appends Electron disable-http2 before sessions are created', async () => { const { app } = await import('electron') const { configureElectronNetworkCompatibility } = await import('./configure-process') const userDataPath = createUserDataDir({ electronHttp1CompatibilityMode: true }) vi.mocked(app.commandLine.appendSwitch).mockClear() configureElectronNetworkCompatibility({ env: {}, userDataPath }) expect(app.commandLine.appendSwitch).toHaveBeenCalledWith('disable-http2') }) }) const EXPECTED_DISABLED_FEATURES = 'FedCm,DirectSockets,DirectSocketsInSharedWorkers,DirectSocketsInServiceWorkers' describe('disableUnsupportedChromiumFeatures', () => { it('matches the shared list the real-Electron egress probes launch with', async () => { const { DISABLED_CHROMIUM_FEATURES } = await import('./disabled-chromium-features') expect(DISABLED_CHROMIUM_FEATURES.join(',')).toBe(EXPECTED_DISABLED_FEATURES) }) it('disables FedCM before Chromium sessions are created', async () => { const { app } = await import('electron') const { disableUnsupportedChromiumFeatures } = await import('./configure-process') vi.mocked(app.commandLine.appendSwitch).mockClear() disableUnsupportedChromiumFeatures() expect(app.commandLine.appendSwitch).toHaveBeenCalledWith( 'disable-features', EXPECTED_DISABLED_FEATURES ) }) it('disables every Direct Sockets surface so a hostile page cannot kill its renderer', async () => { const { app } = await import('electron') const { disableUnsupportedChromiumFeatures } = await import('./configure-process') vi.mocked(app.commandLine.appendSwitch).mockClear() disableUnsupportedChromiumFeatures() const disabled = vi .mocked(app.commandLine.appendSwitch) .mock.calls.find(([name]) => name === 'disable-features')?.[1] ?.split(',') expect(disabled).toEqual( expect.arrayContaining([ 'DirectSockets', 'DirectSocketsInSharedWorkers', 'DirectSocketsInServiceWorkers' ]) ) }) it('preserves existing disabled Chromium features', async () => { const { app } = await import('electron') const { disableUnsupportedChromiumFeatures } = await import('./configure-process') vi.mocked(app.commandLine.getSwitchValue).mockReturnValueOnce('ExistingFeature') vi.mocked(app.commandLine.appendSwitch).mockClear() disableUnsupportedChromiumFeatures() expect(app.commandLine.appendSwitch).toHaveBeenCalledWith( 'disable-features', `${EXPECTED_DISABLED_FEATURES},ExistingFeature` ) }) it('does not duplicate features when disable-features already includes them', async () => { const { app } = await import('electron') const { disableUnsupportedChromiumFeatures } = await import('./configure-process') vi.mocked(app.commandLine.getSwitchValue).mockReturnValueOnce( `${EXPECTED_DISABLED_FEATURES},ExistingFeature` ) vi.mocked(app.commandLine.appendSwitch).mockClear() disableUnsupportedChromiumFeatures() expect(app.commandLine.appendSwitch).toHaveBeenCalledWith( 'disable-features', `${EXPECTED_DISABLED_FEATURES},ExistingFeature` ) }) }) describe('enableMainProcessGpuFeatures', () => { const originalPlatform = Object.getOwnPropertyDescriptor(process, 'platform') const originalE2EUserDataDir = process.env.ORCA_E2E_USER_DATA_DIR function setPlatform(platform: NodeJS.Platform): void { Object.defineProperty(process, 'platform', { configurable: true, value: platform }) } afterEach(() => { if (originalPlatform) { Object.defineProperty(process, 'platform', originalPlatform) } if (originalE2EUserDataDir === undefined) { delete process.env.ORCA_E2E_USER_DATA_DIR } else { process.env.ORCA_E2E_USER_DATA_DIR = originalE2EUserDataDir } }) it('appends VS Code-style GPU channel flags without unsafe WebGPU/Vulkan opt-ins', async () => { const { app } = await import('electron') const { enableMainProcessGpuFeatures } = await import('./configure-process') delete process.env.ORCA_E2E_USER_DATA_DIR vi.mocked(app.commandLine.appendSwitch).mockClear() enableMainProcessGpuFeatures() expect(app.commandLine.appendSwitch).toHaveBeenCalledWith( 'enable-features', 'EarlyEstablishGpuChannel,EstablishGpuChannelAsync' ) expect(app.commandLine.appendSwitch).not.toHaveBeenCalledWith('enable-unsafe-webgpu') }) it('raises the WebGL context budget above the 16-context Blink default', async () => { const { app } = await import('electron') const { enableMainProcessGpuFeatures } = await import('./configure-process') delete process.env.ORCA_E2E_USER_DATA_DIR vi.mocked(app.commandLine.appendSwitch).mockClear() enableMainProcessGpuFeatures() expect(app.commandLine.appendSwitch).toHaveBeenCalledWith('max-active-webgl-contexts', '128') }) it('disables Skia Graphite only on macOS without disabling hardware acceleration', async () => { const { app } = await import('electron') const { enableMainProcessGpuFeatures } = await import('./configure-process') delete process.env.ORCA_E2E_USER_DATA_DIR vi.mocked(app.disableHardwareAcceleration).mockClear() for (const platform of ['darwin', 'linux', 'win32'] as const) { setPlatform(platform) vi.mocked(app.commandLine.appendSwitch).mockClear() enableMainProcessGpuFeatures() if (platform === 'darwin') { expect(app.commandLine.appendSwitch).toHaveBeenCalledWith('disable-skia-graphite') } else { expect(app.commandLine.appendSwitch).not.toHaveBeenCalledWith('disable-skia-graphite') } } expect(app.disableHardwareAcceleration).not.toHaveBeenCalled() }) it('disables the GPU sandbox on Linux Wayland without disabling acceleration', async () => { const { app } = await import('electron') const { enableMainProcessGpuFeatures } = await import('./configure-process') const originalWaylandDisplay = process.env.WAYLAND_DISPLAY try { setPlatform('linux') delete process.env.ORCA_E2E_USER_DATA_DIR process.env.WAYLAND_DISPLAY = 'wayland-1' vi.mocked(app.disableHardwareAcceleration).mockClear() vi.mocked(app.commandLine.appendSwitch).mockClear() enableMainProcessGpuFeatures() } finally { if (originalWaylandDisplay === undefined) { delete process.env.WAYLAND_DISPLAY } else { process.env.WAYLAND_DISPLAY = originalWaylandDisplay } } expect(app.commandLine.appendSwitch).toHaveBeenCalledWith('disable-gpu-sandbox') expect(app.disableHardwareAcceleration).not.toHaveBeenCalled() expect(app.commandLine.appendSwitch).not.toHaveBeenCalledWith( 'enable-features', expect.stringContaining('EarlyEstablishGpuChannel') ) expect(app.commandLine.appendSwitch).not.toHaveBeenCalledWith( 'enable-features', expect.stringContaining('EstablishGpuChannelAsync') ) }) it('uses Electron Ozone hints to recognize forced Linux Wayland launches', async () => { const { app } = await import('electron') const { enableMainProcessGpuFeatures } = await import('./configure-process') setPlatform('linux') delete process.env.ORCA_E2E_USER_DATA_DIR vi.mocked(app.commandLine.appendSwitch).mockClear() vi.mocked(app.commandLine.getSwitchValue).mockImplementation((switchName: string) => switchName === 'ozone-platform' ? 'wayland' : '' ) enableMainProcessGpuFeatures() expect(app.commandLine.appendSwitch).toHaveBeenCalledWith('disable-gpu-sandbox') expect(app.commandLine.appendSwitch).not.toHaveBeenCalledWith( 'enable-features', expect.stringContaining('EarlyEstablishGpuChannel') ) }) it('honors explicit Linux X11 Ozone overrides even when Wayland env vars are present', async () => { const { app } = await import('electron') const { enableMainProcessGpuFeatures } = await import('./configure-process') const originalWaylandDisplay = process.env.WAYLAND_DISPLAY const originalSessionType = process.env.XDG_SESSION_TYPE try { setPlatform('linux') delete process.env.ORCA_E2E_USER_DATA_DIR process.env.WAYLAND_DISPLAY = 'wayland-1' process.env.XDG_SESSION_TYPE = 'wayland' vi.mocked(app.commandLine.appendSwitch).mockClear() vi.mocked(app.commandLine.getSwitchValue).mockImplementation((switchName: string) => switchName === 'ozone-platform' ? 'x11' : '' ) enableMainProcessGpuFeatures() } finally { if (originalWaylandDisplay === undefined) { delete process.env.WAYLAND_DISPLAY } else { process.env.WAYLAND_DISPLAY = originalWaylandDisplay } if (originalSessionType === undefined) { delete process.env.XDG_SESSION_TYPE } else { process.env.XDG_SESSION_TYPE = originalSessionType } } expect(app.commandLine.appendSwitch).not.toHaveBeenCalledWith('disable-gpu-sandbox') expect(app.commandLine.appendSwitch).toHaveBeenCalledWith( 'enable-features', 'EarlyEstablishGpuChannel,EstablishGpuChannelAsync' ) }) it('does not disable the GPU sandbox outside Linux Wayland', async () => { const { app } = await import('electron') const { enableMainProcessGpuFeatures } = await import('./configure-process') const originalWaylandDisplay = process.env.WAYLAND_DISPLAY const originalSessionType = process.env.XDG_SESSION_TYPE const originalOzoneHint = process.env.ELECTRON_OZONE_PLATFORM_HINT try { delete process.env.ORCA_E2E_USER_DATA_DIR delete process.env.WAYLAND_DISPLAY delete process.env.XDG_SESSION_TYPE delete process.env.ELECTRON_OZONE_PLATFORM_HINT for (const platform of ['linux', 'darwin', 'win32'] as const) { setPlatform(platform) vi.mocked(app.commandLine.appendSwitch).mockClear() vi.mocked(app.commandLine.getSwitchValue).mockImplementation((switchName: string) => switchName === 'enable-features' ? '' : '' ) enableMainProcessGpuFeatures() expect(app.commandLine.appendSwitch).not.toHaveBeenCalledWith('disable-gpu-sandbox') } } finally { if (originalWaylandDisplay === undefined) { delete process.env.WAYLAND_DISPLAY } else { process.env.WAYLAND_DISPLAY = originalWaylandDisplay } if (originalSessionType === undefined) { delete process.env.XDG_SESSION_TYPE } else { process.env.XDG_SESSION_TYPE = originalSessionType } if (originalOzoneHint === undefined) { delete process.env.ELECTRON_OZONE_PLATFORM_HINT } else { process.env.ELECTRON_OZONE_PLATFORM_HINT = originalOzoneHint } } }) it('disables the GPU process for Linux E2E runs', async () => { const { app } = await import('electron') const { enableMainProcessGpuFeatures } = await import('./configure-process') setPlatform('linux') process.env.ORCA_E2E_USER_DATA_DIR = '/tmp/orca-e2e' vi.mocked(app.disableHardwareAcceleration).mockClear() vi.mocked(app.commandLine.appendSwitch).mockClear() enableMainProcessGpuFeatures() expect(app.disableHardwareAcceleration).toHaveBeenCalledTimes(1) expect(app.commandLine.appendSwitch).toHaveBeenCalledWith('disable-gpu') expect(app.commandLine.appendSwitch).not.toHaveBeenCalledWith( 'enable-features', expect.any(String) ) expect(app.commandLine.appendSwitch).not.toHaveBeenCalledWith( 'max-active-webgl-contexts', expect.any(String) ) }) it('preserves existing enable-features switches', async () => { const { app } = await import('electron') const { enableMainProcessGpuFeatures } = await import('./configure-process') delete process.env.ORCA_E2E_USER_DATA_DIR vi.mocked(app.commandLine.appendSwitch).mockClear() vi.mocked(app.commandLine.getSwitchValue).mockReturnValue('ExistingFeature') enableMainProcessGpuFeatures() expect(app.commandLine.appendSwitch).toHaveBeenCalledWith( 'enable-features', 'EarlyEstablishGpuChannel,EstablishGpuChannelAsync,ExistingFeature' ) }) it('preserves existing enable-features switches on Linux Wayland without eager GPU channel flags', async () => { const { app } = await import('electron') const { enableMainProcessGpuFeatures } = await import('./configure-process') const originalWaylandDisplay = process.env.WAYLAND_DISPLAY try { setPlatform('linux') delete process.env.ORCA_E2E_USER_DATA_DIR process.env.WAYLAND_DISPLAY = 'wayland-1' vi.mocked(app.commandLine.appendSwitch).mockClear() vi.mocked(app.commandLine.getSwitchValue).mockImplementation((switchName: string) => switchName === 'enable-features' ? 'ExistingFeature' : '' ) enableMainProcessGpuFeatures() } finally { if (originalWaylandDisplay === undefined) { delete process.env.WAYLAND_DISPLAY } else { process.env.WAYLAND_DISPLAY = originalWaylandDisplay } } expect(app.commandLine.appendSwitch).toHaveBeenCalledWith('disable-gpu-sandbox') expect(app.commandLine.appendSwitch).toHaveBeenCalledWith('enable-features', 'ExistingFeature') }) }) describe('safe graphics mode startup switches', () => { const originalE2EUserDataDir = process.env.ORCA_E2E_USER_DATA_DIR afterEach(() => { if (originalE2EUserDataDir === undefined) { delete process.env.ORCA_E2E_USER_DATA_DIR } else { process.env.ORCA_E2E_USER_DATA_DIR = originalE2EUserDataDir } }) function disabledFeaturesFrom(appendSwitch: ReturnType): string[] { return appendSwitch.mock.calls .filter(([name]) => name === 'disable-features') .flatMap(([, value]) => String(value ?? '').split(',')) .filter(Boolean) } it('opts hidden pages out of intensive wake-up throttling', async () => { const { app } = await import('electron') const { optOutOfHiddenPageWakeUpThrottling } = await import('./configure-process') vi.mocked(app.commandLine.appendSwitch).mockClear() optOutOfHiddenPageWakeUpThrottling() expect(app.commandLine.appendSwitch).toHaveBeenCalledWith( 'disable-features', 'IntensiveWakeUpThrottling' ) }) // Why: Chromium enables IntensiveWakeUpThrottling on every desktop platform, so the opt-out // must never become reachable only through the GPU-feature path again. it('does not couple the throttling opt-out to GPU feature setup', async () => { const { app } = await import('electron') const { enableMainProcessGpuFeatures } = await import('./configure-process') delete process.env.ORCA_E2E_USER_DATA_DIR vi.mocked(app.commandLine.appendSwitch).mockClear() enableMainProcessGpuFeatures() expect(disabledFeaturesFrom(vi.mocked(app.commandLine.appendSwitch))).not.toContain( 'IntensiveWakeUpThrottling' ) }) })