mirror of
https://github.com/stablyai/orca.git
synced 2026-09-24 08:02:33 +00:00
* Keep mobile-driven browser panes paintable and park idle hidden panes - Add useSyncExternalStore-based hooks to reactively track mobile-driven pages. - Prevent unmounting or losing paintability for panes controlled by a mobile device. - Park heavy browser pane subtrees in hidden worktrees to improve performance. * Derive workflow platforms dynamically in runtime contract test Avoid hardcoding Linux, macOS, and Windows platforms in the Electron runtime package contract test. Instead, extract the platforms directly from the golden E2E workflow matrix strategy and map them to their corresponding run steps.
289 lines
12 KiB
JavaScript
289 lines
12 KiB
JavaScript
import { readFileSync } from 'node:fs'
|
|
import { dirname, join, resolve } from 'node:path'
|
|
import { fileURLToPath } from 'node:url'
|
|
import { describe, expect, it } from 'vitest'
|
|
import { parse } from 'yaml'
|
|
|
|
const projectDir = resolve(dirname(fileURLToPath(import.meta.url)), '../..')
|
|
const packageJson = JSON.parse(readFileSync(join(projectDir, 'package.json'), 'utf8'))
|
|
|
|
describe('Electron runtime package contract', () => {
|
|
it('keeps root postinstall as the single Electron binary install owner', () => {
|
|
expect(packageJson.scripts.postinstall).toBe('node config/scripts/rebuild-native-deps.mjs')
|
|
expect(packageJson.pnpm.onlyBuiltDependencies).not.toContain('electron')
|
|
})
|
|
|
|
it('guards package scripts that launch Electron tooling', () => {
|
|
const scripts = packageJson.scripts
|
|
const guardedScripts = [
|
|
'start',
|
|
'dev',
|
|
'dev-stable-name',
|
|
'build:unpack',
|
|
'build:win',
|
|
'build:mac',
|
|
'build:mac:release',
|
|
'build:linux',
|
|
'test:e2e',
|
|
'test:e2e:terminal-rendering-golden',
|
|
'test:e2e:terminal-rendering-release-evidence',
|
|
'test:e2e:headful'
|
|
]
|
|
|
|
for (const scriptName of guardedScripts) {
|
|
expect(scripts[scriptName], scriptName).toContain('pnpm run ensure:electron-runtime &&')
|
|
}
|
|
})
|
|
|
|
it('keeps Windows and Linux package builds off the macOS native helper build', () => {
|
|
const scripts = packageJson.scripts
|
|
|
|
expect(scripts['build:desktop']).not.toContain('build:computer-macos')
|
|
expect(scripts['build:win']).toContain('pnpm run build:desktop')
|
|
expect(scripts['build:win']).not.toContain('pnpm run build ')
|
|
expect(scripts['build:win']).not.toContain('build:computer-macos')
|
|
expect(scripts['build:linux']).toContain('pnpm run build:desktop')
|
|
expect(scripts['build:linux']).not.toContain('pnpm run build ')
|
|
expect(scripts['build:linux']).not.toContain('build:computer-macos')
|
|
expect(scripts['build:mac']).toContain('pnpm run build:computer-macos')
|
|
expect(scripts['build:release']).toContain('pnpm run build:native')
|
|
expect(scripts['build:release']).not.toContain('build:computer-macos')
|
|
})
|
|
|
|
it('guards release publishing before electron-builder runs', () => {
|
|
const releaseWorkflow = readFileSync(
|
|
join(projectDir, '.github/workflows/release-cut.yml'),
|
|
'utf8'
|
|
)
|
|
const parsedWorkflow = parse(releaseWorkflow)
|
|
const releaseCommands = new Map(
|
|
parsedWorkflow.jobs.build.strategy.matrix.include.map(({ platform, release_command }) => [
|
|
platform,
|
|
release_command
|
|
])
|
|
)
|
|
|
|
expect([...releaseCommands.keys()].sort()).toEqual(['linux', 'mac', 'win'])
|
|
for (const command of releaseCommands.values()) {
|
|
expect(command).toContain('node config/scripts/ensure-native-runtime.mjs --runtime=electron')
|
|
expect(command).toContain('electron-builder')
|
|
expect(command.indexOf('ensure-native-runtime')).toBeLessThan(
|
|
command.indexOf('electron-builder')
|
|
)
|
|
}
|
|
expect(releaseCommands.get('mac')).toContain(' && ORCA_MAC_RELEASE=1 ')
|
|
expect(releaseCommands.get('linux')).toContain(' && pnpm exec electron-builder ')
|
|
expect(releaseCommands.get('win')).toContain(
|
|
'; if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }; pnpm exec electron-builder '
|
|
)
|
|
})
|
|
|
|
it('lets release-cut tag a version that is already present on main', () => {
|
|
const releaseWorkflow = readFileSync(
|
|
join(projectDir, '.github/workflows/release-cut.yml'),
|
|
'utf8'
|
|
)
|
|
const parsedWorkflow = parse(releaseWorkflow)
|
|
const bumpStep = parsedWorkflow.jobs.cut.steps.find(
|
|
(step) => step.name === 'Bump package.json and tag'
|
|
)
|
|
|
|
expect(bumpStep.run).toContain('git diff --cached --quiet')
|
|
expect(bumpStep.run).toContain('git commit --allow-empty -m "$commit_message"')
|
|
})
|
|
|
|
it('keeps release-cut RC retries monotonic across stale attempts', () => {
|
|
const releaseWorkflow = readFileSync(
|
|
join(projectDir, '.github/workflows/release-cut.yml'),
|
|
'utf8'
|
|
)
|
|
const parsedWorkflow = parse(releaseWorkflow)
|
|
const versionStep = parsedWorkflow.jobs.cut.steps.find(
|
|
(step) => step.name === 'Compute next version'
|
|
)
|
|
|
|
expect(versionStep.run).toContain('node config/scripts/release-rc-history.mjs "$1"')
|
|
expect(versionStep.run).toContain('tag_matches_current_ref')
|
|
expect(versionStep.run).toContain('cutting the next version instead of reusing stale artifacts')
|
|
expect(versionStep.run).toContain('git rev-parse "$existing_rc_tag"')
|
|
})
|
|
|
|
it('bumps separate Homebrew casks for stable and RC desktop tags', () => {
|
|
const releaseWorkflow = parse(
|
|
readFileSync(join(projectDir, '.github/workflows/release-cut.yml'), 'utf8')
|
|
)
|
|
const homebrewWorkflow = parse(
|
|
readFileSync(join(projectDir, '.github/workflows/homebrew-bump.yml'), 'utf8')
|
|
)
|
|
|
|
expect(releaseWorkflow.jobs['homebrew-bump'].if).toContain(
|
|
"startsWith(needs.cut.outputs.tag, 'v')"
|
|
)
|
|
expect(releaseWorkflow.jobs['homebrew-bump'].if).not.toContain('-rc.')
|
|
expect(releaseWorkflow.jobs['homebrew-bump-published-rc-draft'].with.tag).toBe(
|
|
'${{ needs.cut.outputs.latest_published_rc_tag }}'
|
|
)
|
|
|
|
const resolveCaskStep = homebrewWorkflow.jobs['bump-cask'].steps.find(
|
|
(step) => step.name === 'Resolve cask target'
|
|
)
|
|
const renderStep = homebrewWorkflow.jobs['bump-cask'].steps.find(
|
|
(step) => step.name === 'Render updated cask file'
|
|
)
|
|
const copyStep = homebrewWorkflow.jobs['bump-cask'].steps.find(
|
|
(step) => step.name === 'Copy cask into tap and open PR'
|
|
)
|
|
|
|
expect(resolveCaskStep.run).toContain('token="orca@rc"')
|
|
expect(resolveCaskStep.run).toContain('token="orca"')
|
|
expect(renderStep.env.CASK_PATH).toBe('${{ steps.cask.outputs.path }}')
|
|
expect(copyStep.run).toContain('cp "$CASK_PATH" "tap/$CASK_PATH"')
|
|
expect(copyStep.run).toContain('git add "$CASK_PATH"')
|
|
})
|
|
|
|
it('installs the Electron package binary in PR checks without changing native module ABI', () => {
|
|
const prWorkflow = readFileSync(join(projectDir, '.github/workflows/pr.yml'), 'utf8')
|
|
const parsedWorkflow = parse(prWorkflow)
|
|
const installStep = parsedWorkflow.jobs.verify.steps.find(
|
|
(step) => step.name === 'Install Electron package binary for tests'
|
|
)
|
|
|
|
expect(installStep.run).toBe('node config/scripts/install-electron-package-binary.mjs')
|
|
})
|
|
|
|
it('smokes the packaged CLI from outside the checkout in PR checks', () => {
|
|
const prWorkflow = readFileSync(join(projectDir, '.github/workflows/pr.yml'), 'utf8')
|
|
const parsedWorkflow = parse(prWorkflow)
|
|
const smokeStep = parsedWorkflow.jobs.verify.steps.find(
|
|
(step) => step.name === 'Smoke packaged CLI'
|
|
)
|
|
|
|
expect(smokeStep.run).toBe(
|
|
'node config/scripts/smoke-packaged-cli.mjs --app-dir=dist/linux-unpacked'
|
|
)
|
|
})
|
|
|
|
it('keeps terminal scale perf wired to the report budget gate', () => {
|
|
const packageScripts = packageJson.scripts
|
|
const terminalPerfWorkflow = parse(
|
|
readFileSync(join(projectDir, '.github/workflows/terminal-perf.yml'), 'utf8')
|
|
)
|
|
const steps = terminalPerfWorkflow.jobs['terminal-perf'].steps
|
|
const runStep = steps.find((step) => step.name === 'Run terminal scale perf report gate')
|
|
const uploadStep = steps.find((step) => step.name === 'Upload terminal perf report')
|
|
|
|
expect(packageScripts['test:e2e:terminal-perf:scale:report']).toContain(
|
|
'run-terminal-scale-perf-report-gate.mjs'
|
|
)
|
|
expect(runStep.run).toContain('pnpm run test:e2e:terminal-perf:scale:report')
|
|
expect(runStep.run).toContain('xvfb-run --auto-servernum')
|
|
const manualProfileKnobs = [
|
|
['ORCA_TERMINAL_PERF_FRAME_COUNT', 'frame_count', 'ORCA_E2E_OPENCODE_FRAME_COUNT'],
|
|
[
|
|
'ORCA_TERMINAL_PERF_FRAME_INTERVAL_MS',
|
|
'frame_interval_ms',
|
|
'ORCA_E2E_OPENCODE_FRAME_INTERVAL_MS'
|
|
],
|
|
[
|
|
'ORCA_TERMINAL_PERF_PRESSURE_OUTPUT_CHARS',
|
|
'pressure_output_chars',
|
|
'ORCA_E2E_OPENCODE_PRESSURE_OUTPUT_CHARS'
|
|
],
|
|
['ORCA_TERMINAL_PERF_SCALE_PANES', 'scale_panes', 'ORCA_E2E_OPENCODE_SCALE_PANES'],
|
|
[
|
|
'ORCA_TERMINAL_PERF_SCALE_CROSS_WORKSPACE_PANES',
|
|
'scale_cross_workspace_panes',
|
|
'ORCA_E2E_OPENCODE_SCALE_CROSS_WORKSPACE_PANES'
|
|
],
|
|
[
|
|
'ORCA_TERMINAL_PERF_SCALE_PRESSURE_PANES',
|
|
'scale_pressure_panes',
|
|
'ORCA_E2E_OPENCODE_SCALE_PRESSURE_PANES'
|
|
],
|
|
[
|
|
'ORCA_TERMINAL_PERF_SCALE_HIDDEN_PRESSURE_PANES',
|
|
'scale_hidden_pressure_panes',
|
|
'ORCA_E2E_OPENCODE_SCALE_HIDDEN_PRESSURE_PANES'
|
|
]
|
|
]
|
|
for (const [workflowEnv, inputName, runnerEnv] of manualProfileKnobs) {
|
|
expect(runStep.env[workflowEnv]).toBe(`\${{ inputs.${inputName} }}`)
|
|
expect(runStep.run).toContain(runnerEnv)
|
|
}
|
|
expect(uploadStep.uses).toBe('actions/upload-artifact@v7')
|
|
expect(uploadStep.with.path).toBe('${{ env.ORCA_E2E_TERMINAL_PERF_REPORT_PATH }}')
|
|
})
|
|
|
|
it('keeps terminal rendering regressions in the fast golden E2E gate', () => {
|
|
const packageScripts = packageJson.scripts
|
|
const goldenWorkflow = parse(
|
|
readFileSync(join(projectDir, '.github/workflows/golden-e2e-experiment.yml'), 'utf8')
|
|
)
|
|
const releaseWorkflow = parse(
|
|
readFileSync(join(projectDir, '.github/workflows/release-cut.yml'), 'utf8')
|
|
)
|
|
const steps = goldenWorkflow.jobs['golden-e2e'].steps
|
|
const goldenPlatformLabels = new Map([
|
|
['linux', 'Linux'],
|
|
['mac', 'macOS'],
|
|
['windows', 'Windows']
|
|
])
|
|
const goldenPlatforms = goldenWorkflow.jobs['golden-e2e'].strategy.matrix.include
|
|
.map(({ platform }) => platform)
|
|
.sort()
|
|
const goldenRunSteps = goldenPlatforms.map((platform) => {
|
|
const label = goldenPlatformLabels.get(platform)
|
|
|
|
expect(label, platform).toBeDefined()
|
|
|
|
return steps.find((step) => step.name === `Run golden E2E tests on ${label}`)
|
|
})
|
|
const pullRequestPaths = goldenWorkflow.on.pull_request.paths
|
|
const releaseGoldenJob = releaseWorkflow.jobs['terminal-rendering-golden']
|
|
const releaseEvidenceJob = releaseWorkflow.jobs['terminal-rendering-release-evidence']
|
|
const releaseBuildNeeds = releaseWorkflow.jobs.build.needs
|
|
const publishReleaseNeeds = releaseWorkflow.jobs['publish-release'].needs
|
|
|
|
expect(packageScripts['test:e2e:terminal-rendering-golden']).toContain(
|
|
'@terminal-rendering-golden'
|
|
)
|
|
expect(packageScripts['test:e2e:terminal-rendering-golden']).toContain(
|
|
'terminal-raw-emoji-table-scroll-restore.spec.ts'
|
|
)
|
|
expect(packageScripts['test:e2e:terminal-rendering-golden']).not.toContain(
|
|
'terminal-long-table-scroll-restore.spec.ts'
|
|
)
|
|
expect(packageScripts['test:e2e:terminal-rendering-release-evidence']).toContain(
|
|
'terminal-opencode-emoji-table-rendering.spec.ts'
|
|
)
|
|
expect(packageScripts['test:e2e:terminal-rendering-release-evidence']).toContain(
|
|
'terminal-long-table-scroll-restore.spec.ts'
|
|
)
|
|
for (const runStep of goldenRunSteps) {
|
|
expect(runStep?.run).toContain('pnpm run test:e2e:terminal-rendering-golden')
|
|
}
|
|
expect(pullRequestPaths).toContain('tests/e2e/terminal-raw-emoji-table-scroll-restore.spec.ts')
|
|
expect(pullRequestPaths).toContain('tests/e2e/fixtures/terminal-emoji-table.md')
|
|
expect(pullRequestPaths).toContain('src/renderer/src/lib/pane-manager/**')
|
|
expect(releaseBuildNeeds).not.toContain('terminal-rendering-golden')
|
|
expect(releaseBuildNeeds).not.toContain('terminal-rendering-release-evidence')
|
|
expect(publishReleaseNeeds).toContain('terminal-rendering-golden')
|
|
expect(publishReleaseNeeds).toContain('build')
|
|
expect(publishReleaseNeeds).not.toContain('terminal-rendering-release-evidence')
|
|
expect(releaseGoldenJob['continue-on-error']).toBeUndefined()
|
|
expect(releaseGoldenJob.strategy.matrix.include.map(({ platform }) => platform).sort()).toEqual(
|
|
goldenPlatforms
|
|
)
|
|
expect(releaseGoldenJob.steps.map((step) => step.run ?? '')).toContain(
|
|
'xvfb-run --auto-servernum env SKIP_BUILD=1 ORCA_E2E_FORWARD_APP_LOGS=1 pnpm run test:e2e:terminal-rendering-golden'
|
|
)
|
|
expect(releaseEvidenceJob['continue-on-error']).toBe(true)
|
|
expect(
|
|
releaseEvidenceJob.strategy.matrix.include.map(({ platform }) => platform).sort()
|
|
).toEqual(['linux', 'mac', 'windows'])
|
|
expect(releaseEvidenceJob.steps.map((step) => step.run ?? '')).toContain(
|
|
'xvfb-run --auto-servernum env SKIP_BUILD=1 ORCA_E2E_FORWARD_APP_LOGS=1 pnpm run test:e2e:terminal-rendering-release-evidence'
|
|
)
|
|
})
|
|
})
|