From bd5a991ce7cdbea5b9aaf036a8946cb92f73849e Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Mon, 27 Jul 2026 17:56:07 -0700 Subject: [PATCH] fix(e2e): launch plugins with real app identity (#11024) --- tests/e2e/helpers/electron-launch-args.ts | 8 +++- .../helpers/electron-launch-args.unit.test.ts | 13 +++++++ tests/e2e/plugin-demo.spec.ts | 5 ++- tests/e2e/plugin-marketplace-content.spec.ts | 38 ++++++++++--------- tests/e2e/plugin-startup-budget.spec.ts | 17 ++++----- 5 files changed, 50 insertions(+), 31 deletions(-) create mode 100644 tests/e2e/helpers/electron-launch-args.unit.test.ts diff --git a/tests/e2e/helpers/electron-launch-args.ts b/tests/e2e/helpers/electron-launch-args.ts index e2051076bf7..aec7d0e1686 100644 --- a/tests/e2e/helpers/electron-launch-args.ts +++ b/tests/e2e/helpers/electron-launch-args.ts @@ -1,6 +1,10 @@ +import { dirname } from 'node:path' + export function getOrcaElectronLaunchArgs(mainPath: string, headful: boolean): string[] { + // Launch through package.json so app version and resource paths match a packaged app. + const appPath = dirname(dirname(dirname(mainPath))) if (headful || process.platform !== 'linux') { - return [mainPath] + return [appPath] } // Why: Ubuntu CI cannot run Electron's setuid chrome-sandbox (not root-owned @@ -16,6 +20,6 @@ export function getOrcaElectronLaunchArgs(mainPath: string, headful: boolean): s '--disable-gpu-sandbox', '--disable-dev-shm-usage', '--in-process-gpu', - mainPath + appPath ] } diff --git a/tests/e2e/helpers/electron-launch-args.unit.test.ts b/tests/e2e/helpers/electron-launch-args.unit.test.ts new file mode 100644 index 00000000000..763dbbdafce --- /dev/null +++ b/tests/e2e/helpers/electron-launch-args.unit.test.ts @@ -0,0 +1,13 @@ +import { join } from 'node:path' +import { describe, expect, it } from 'vitest' +import { getOrcaElectronLaunchArgs } from './electron-launch-args' + +describe('getOrcaElectronLaunchArgs', () => { + it('launches the package root that owns the compiled main entry', () => { + const root = join('workspace', 'orca') + const mainPath = join(root, 'out', 'main', 'index.js') + + expect(getOrcaElectronLaunchArgs(mainPath, true)).toEqual([root]) + expect(getOrcaElectronLaunchArgs(mainPath, false).at(-1)).toBe(root) + }) +}) diff --git a/tests/e2e/plugin-demo.spec.ts b/tests/e2e/plugin-demo.spec.ts index 860e1aac1f1..bc2975e8b52 100644 --- a/tests/e2e/plugin-demo.spec.ts +++ b/tests/e2e/plugin-demo.spec.ts @@ -107,12 +107,13 @@ test('runs hello-orca panel, command, and event behind visible consent', async ( expect(installed.blocked).toBe(true) await openPluginSettings(orcaPage) + await orcaPage.getByRole('tab', { name: /^Installed/ }).click() const row = orcaPage.locator(`[data-plugin-key="${installed.pluginKey}"]`) await expect(row).toContainText('Needs review') - await row.getByRole('button', { name: 'Review permissions' }).click() + await row.getByRole('button', { name: 'Review & enable' }).click() const consent = orcaPage.getByRole('dialog', { name: 'Review permissions' }) await expect(consent).toBeVisible() - await expect(consent).toContainText(pluginRoot) + await expect(consent).toContainText('Local folder') await expect(consent).toContainText('full access to your files, network, and other processes') await expect(consent.getByRole('button', { name: 'Keep Disabled' })).toBeFocused() await consent.getByRole('button', { name: 'Enable plugin' }).click() diff --git a/tests/e2e/plugin-marketplace-content.spec.ts b/tests/e2e/plugin-marketplace-content.spec.ts index 7f59b0d43d6..a218339a617 100644 --- a/tests/e2e/plugin-marketplace-content.spec.ts +++ b/tests/e2e/plugin-marketplace-content.spec.ts @@ -76,16 +76,10 @@ async function copyLaunchPlugin( async function configureFixtureGit(home: string, repositories: string): Promise { const hooksDirectory = join(home, 'hooks') - const xdgConfigHome = join(home, 'xdg') const configPath = join(home, '.gitconfig') - await Promise.all([ - mkdir(hooksDirectory, { recursive: true }), - mkdir(xdgConfigHome, { recursive: true }) - ]) + await mkdir(hooksDirectory, { recursive: true }) const gitEnvironment: NodeJS.ProcessEnv = { - HOME: home, - USERPROFILE: home, - XDG_CONFIG_HOME: xdgConfigHome, + GIT_CONFIG_GLOBAL: configPath, GIT_CONFIG_NOSYSTEM: '1', GIT_TERMINAL_PROMPT: '0' } @@ -184,9 +178,9 @@ async function installMarketplacePluginThroughUi( ): Promise { const listing = page.locator(`[data-marketplace-plugin-key="${pluginKey}"]`) await expect(listing).toBeVisible() - await listing.getByRole('button', { name: 'Review' }).click() + await listing.getByRole('button', { name: 'Install' }).click() const preview = page.getByRole('dialog', { name: pluginName }) - await expect(preview).toContainText(pluginKey) + await expect(preview).toContainText('Official ยท stablyai') await preview.getByRole('button', { name: 'Install plugin' }).click() const consent = page.getByRole('dialog', { name: consentDialogName }) await expect(consent).toBeVisible() @@ -194,6 +188,21 @@ async function installMarketplacePluginThroughUi( await expect(consent).toBeHidden() } +async function enableInstalledPluginThroughUi( + page: Page, + pluginKey: string, + consentDialogName: string +): Promise { + await page.getByRole('tab', { name: /^Installed/ }).click() + const plugin = page.locator(`[data-plugin-key="${pluginKey}"]`) + await expect(plugin).toBeVisible() + await plugin.getByRole('button', { name: 'Review & enable' }).click() + const consent = page.getByRole('dialog', { name: consentDialogName }) + await expect(consent).toBeVisible() + await consent.getByRole('button', { name: 'Enable plugin' }).click() + await expect(consent).toBeHidden() +} + async function applyInstalledLanguage(page: Page): Promise { const languageId = 'plugin:stablyai.orca-portuguese/pt-BR' await page.evaluate(() => { @@ -251,10 +260,9 @@ async function runMarketplaceJourney(page: Page): Promise { 'Multipass VM Recipes', 'Review plugin content' ) - await installMarketplacePluginThroughUi( + await enableInstalledPluginThroughUi( page, 'stablyai.orca-navigation-shortcuts', - 'Orca Navigation Shortcuts', 'Review plugin content' ) @@ -266,11 +274,7 @@ async function runMarketplaceJourney(page: Page): Promise { test('installs and applies official Phase 1 content from a fresh profile', async ({}, testInfo) => { test.setTimeout(180_000) const fixture = await createMarketplaceFixture() - const session = createRestartSession(testInfo as TestInfo, { - extraEnv: { - ...fixture.gitEnvironment - } - }) + const session = createRestartSession(testInfo as TestInfo, fixture.gitEnvironment) let launched: Awaited> | null = null try { launched = await session.launch() diff --git a/tests/e2e/plugin-startup-budget.spec.ts b/tests/e2e/plugin-startup-budget.spec.ts index 77e565d53ac..8f03d03d3e7 100644 --- a/tests/e2e/plugin-startup-budget.spec.ts +++ b/tests/e2e/plugin-startup-budget.spec.ts @@ -6,7 +6,7 @@ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs' import { join } from 'node:path' -import { expect, test, type ElectronApplication, type TestInfo } from '@stablyai/playwright-test' +import { expect, test, type TestInfo } from '@stablyai/playwright-test' import { fingerprintPluginConsent } from '../../src/shared/plugins/plugin-consent-fingerprint' import { pluginManifestSchema } from '../../src/shared/plugins/plugin-manifest' import { createRestartSession } from './helpers/orca-restart' @@ -84,12 +84,11 @@ async function launchSample( testInfo: TestInfo ): Promise { let output = '' - const attachLogs = (app: ElectronApplication): void => { - app.process().stderr?.on('data', (chunk: Buffer) => { - output += chunk.toString('utf8') - }) - } - const launched = await session.launch(attachLogs) + const launched = await session.launch({ + onStderr: (chunk) => { + output += chunk + } + }) try { await expect .poll( @@ -127,9 +126,7 @@ function median(values: readonly number[]): number { // oxlint-disable-next-line no-empty-pattern -- Playwright passes fixtures before testInfo. test('keeps real Electron launch stable with 20 approved inert plugins', async ({}, testInfo) => { test.setTimeout(240_000) - const session = createRestartSession(testInfo, { - extraEnv: { ORCA_STARTUP_DIAGNOSTICS: '1' } - }) + const session = createRestartSession(testInfo, { ORCA_STARTUP_DIAGNOSTICS: '1' }) const baseline: StartupSample[] = [] const populated: StartupSample[] = [] let markerPaths: string[] = []