/** * Invariant: `orca plugin shot --editor` opens a file in a plugin's custom editor from the hidden * test window, waits for the editor to get and ask for its document, sends `--act` input into the * editor's isolated guest on a temporary copy of the file, and captures what it drew without showing or focusing the window. A * file no workspace holds opens its folder as a folder workspace; an editor that never asks for its * document, or throws, fails the command. */ import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises' import { tmpdir } from 'node:os' import { join } from 'node:path' import type { ElectronApplication } from '@stablyai/playwright-test' import { expect, test } from './helpers/orca-app' import { runCompiledOrcaCli } from './helpers/compiled-orca-cli' import { disablePluginSystem, enableDevPlugins } from './helpers/plugin-smoke' import { ISOLATED_FRAMES_E2E } from './helpers/plugin-frame-guest' import { waitForActiveWorktree, waitForSessionReady } from './helpers/store' test.use(ISOLATED_FRAMES_E2E) // Red after a click anywhere, so the image proves the act reached the guest before the capture. const EDITOR_HTML = `
` const BROKEN_HTML = `` /** Share of pure red pixels, decoded in Electron's main process. */ function redShare(app: ElectronApplication, path: string): Promise { return app.evaluate(({ nativeImage }, file) => { const bgra = nativeImage.createFromPath(file).toBitmap() let red = 0 for (let at = 0; at + 3 < bgra.length; at += 4) { if ((bgra[at + 2] ?? 0) > 200 && (bgra[at + 1] ?? 0) < 60 && (bgra[at] ?? 0) < 60) { red += 1 } } return red / Math.max(1, bgra.length / 4) }, path) } async function editorPlugin(root: string, id: string, extension: string, html: string) { await mkdir(join(root, id, 'editor'), { recursive: true }) await writeFile( join(root, id, 'orca-plugin.json'), JSON.stringify({ manifestVersion: 2, execution: 'sandboxed-ui', id, name: id, version: '1.0.0', engines: { orca: '>=1.4.246' }, pluginApi: 1, contributes: { customEditors: [{ id, title: id, extensions: [extension], entry: 'editor/index.html' }] } }) ) await writeFile(join(root, id, 'editor', 'index.html'), html) return join(root, id) } test('plugin shot --editor opens the file, acts in the guest and fails a broken editor', async ({ electronApp, orcaPage }) => { await waitForSessionReady(orcaPage) await waitForActiveWorktree(orcaPage) const tempRoot = await mkdtemp(join(tmpdir(), 'orca-editor-shot-')) try { const good = await editorPlugin(tempRoot, 'red-click', '.redclick', EDITOR_HTML) const broken = await editorPlugin(tempRoot, 'broken-editor', '.broken', BROKEN_HTML) // Outside every workspace: the shot opens this folder as a folder workspace. const docs = join(tempRoot, 'docs') await mkdir(docs) await writeFile(join(docs, 'a.redclick'), 'x') await writeFile(join(docs, 'b.broken'), 'x') await enableDevPlugins(orcaPage, [good, broken]) const userDataDir = await electronApp.evaluate(({ app }) => app.getPath('userData')) await expect .poll( async () => { const run = await runCompiledOrcaCli(userDataDir, [ 'plugin', 'shot', 'local.red-click', '--editor', join(docs, 'a.redclick'), '--width', '320', '--height', '200', '--act', '[{"click":[20,20]}]', '--out', join(tempRoot, 'acted.png'), '--json' ]) return run.json?.ok ? run.json.result : run.stderr }, { timeout: 60_000, message: 'the editor shot never succeeded' } ) .toMatchObject({ width: 320, height: 200, subscribed: true, errors: [], acted: 1 }) expect(await redShare(electronApp, join(tempRoot, 'acted.png'))).toBeGreaterThan(0.9) // The acts ran on a copy: the file is unchanged, and no tab or workspace of the copy is left. expect(await readFile(join(docs, 'a.redclick'), 'utf8')).toBe('x') expect( await orcaPage.evaluate(() => { const state = window.__store?.getState() return { tabs: state?.openFiles.filter((file) => file.filePath.endsWith('a.redclick')).length, scratch: state?.repos.filter((repo) => repo.path.includes('orca-editor-shot-')).length } }) ).toEqual({ tabs: 0, scratch: 0 }) expect( await electronApp.evaluate(({ BrowserWindow }) => BrowserWindow.getAllWindows().some((window) => window.isVisible() || window.isFocused()) ) ).toBe(false) const failed = await runCompiledOrcaCli(userDataDir, [ 'plugin', 'shot', 'local.broken-editor', '--editor', join(docs, 'b.broken'), '--out', join(tempRoot, 'broken.png') ]) expect(failed.code).toBe(1) expect(failed.stdout).toContain('never asked for its document') expect(failed.stdout).toContain('editor exploded') // Orca's own chrome, where plugins draw status bar items, from the hidden window too. const statusBar = await runCompiledOrcaCli(userDataDir, [ 'plugin', 'shot', 'local.red-click', '--status-bar', '--out', join(tempRoot, 'status-bar.png'), '--json' ]) expect(statusBar.json?.result).toMatchObject({ region: 'status-bar', height: 24 }) expect( await electronApp.evaluate(({ BrowserWindow }) => BrowserWindow.getAllWindows().some((window) => window.isVisible() || window.isFocused()) ) ).toBe(false) } finally { await disablePluginSystem(orcaPage).catch(() => undefined) await rm(tempRoot, { recursive: true, force: true }) } })