test: remove low-value inventories, passthroughs and redundant cases (#33796)

* test: remove low-value inventories, passthroughs and redundant cases

* style: format test suites after pruning redundant cases

* test: remove obsolete workflow and flaky-test references

* test: prune deleted builder test from Windows shim allowlist

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
OrcaWin
2026-10-25 23:41:49 -07:00
committed by GitHub
co-authored by OrcaWin
parent 35af10dded
commit de93f326ea
111 changed files with 26 additions and 3517 deletions
-1
View File
@@ -95,7 +95,6 @@ jobs:
config/scripts/macos-computer-helper-owner-loss-processes.test.mjs
config/scripts/computer-use-smoke.test.mjs
src/main/computer/computer-provider-lifecycle.test.ts
src/main/computer/computer-provider-unavailable-message.test.ts
src/main/computer/sidecar-client.test.ts
src/main/computer/macos-native-provider-client.test.ts
src/main/computer/macos-native-provider-socket.test.ts
-1
View File
@@ -1333,7 +1333,6 @@
6 test-timeout-needs-reason:src/main/windows/windows-pty-job.win32.test.ts
8 test-timeout-needs-reason:src/main/worker-thread-child-process/worker-thread-child-process.test.ts
6 test-timeout-needs-reason:src/main/wsl/wsl-runner.wsl.test.ts
3 test-timeout-needs-reason:src/main/zcode/interactive-capability.live.test.ts
4 test-timeout-needs-reason:src/packages/process-host/src/run-process.test.ts
5 test-timeout-needs-reason:src/renderer/src/components/sidebar/WorktreeCard.compact-hover.test.tsx
2 test-timeout-needs-reason:src/renderer/src/components/sidebar/WorktreeCard.host-badge-toggle.test.tsx
@@ -1,92 +0,0 @@
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs'
import { join } from 'node:path'
import { parse } from 'yaml'
import { expect, it } from 'vitest'
// Runner truncates/rewrites GITHUB_EVENT_PATH at each step start; background JS payload imports/composite inner steps race foreground starts and parse empty JSON.
const workflowFiles = readdirSync('.github/workflows')
.filter((name) => /\.ya?ml$/.test(name))
.map((name) => join('.github/workflows', name))
const actionFiles = readdirSync('.github/actions')
.map((name) => join('.github/actions', name, 'action.yml'))
.filter((file) => existsSync(file))
// Single-step JavaScript actions whose bundle never reads the payload (checked by searching dist/index.js).
const payloadFreeActions = ['ruby/setup-ruby@v1']
// The runner runs 10 at once; a queued step starts (and rewrites the payload) mid-foreground-step.
const backgroundSlots = 10
function stepLists(file) {
const document = parse(readFileSync(file, 'utf8'))
return document.jobs
? Object.entries(document.jobs).map(([job, value]) => [job, value.steps ?? []])
: [['composite', document.runs?.steps ?? []]]
}
function backgroundSteps() {
const found = []
for (const file of [...workflowFiles, ...actionFiles]) {
for (const [job, steps] of stepLists(file)) {
for (const step of steps) {
if (step.background) {
found.push({ where: `${file} ${job} ${step.id ?? step.name}`, step })
}
}
}
}
return found
}
function readsEventPayload(run) {
const scripts = [...run.matchAll(/(?:config\/scripts|\.github\/actions)\/[\w./-]+/g)].map(
(match) => match[0]
)
return (
run.includes('GITHUB_EVENT_PATH') ||
scripts.some(
(file) =>
existsSync(file) &&
statSync(file).isFile() &&
readFileSync(file, 'utf8').includes('GITHUB_EVENT_PATH')
)
)
}
it('finds the background steps it guards', () => {
expect(backgroundSteps().length).toBeGreaterThan(0)
})
it('backgrounds only single run steps, never actions that start later steps or parse the payload', () => {
const actions = backgroundSteps()
.filter(({ step }) => typeof step.run !== 'string' && !payloadFreeActions.includes(step.uses))
.map(({ where }) => where)
expect(actions).toEqual([])
})
it('keeps the event payload out of background run steps', () => {
const readers = backgroundSteps()
.filter(({ step }) => typeof step.run === 'string' && readsEventPayload(step.run))
.map(({ where }) => where)
expect(readers).toEqual([])
})
it('never queues a background step behind a full slot pool', () => {
const crowded = []
for (const file of workflowFiles) {
for (const [job, steps] of stepLists(file)) {
const open = new Set()
for (const [index, step] of steps.entries()) {
if (step.background) {
open.add(step.id ?? index)
}
for (const id of [step.wait ?? []].flat()) {
open.delete(id)
}
if (open.size > backgroundSlots) {
crowded.push(`${file} ${job}`)
}
}
}
}
expect(crowded).toEqual([])
})
@@ -6,64 +6,13 @@ import {
classifyE2eJobs,
DEDICATED_E2E_SPECS,
generalE2eShards,
MANAGED_SSH_E2E_SPEC_PREFIX,
NATIVE_IME_E2E_SPEC,
NODE_NETWORK_E2E_SPEC,
selectGeneralE2eSpecs
} from './ci-e2e-job-selection.mjs'
import { selectPrE2eSpecs } from './pr-e2e-source-routing.mjs'
it.each([
'src/main/runtime/runtime-browser-commands-factory.ts',
'src/main/runtime/orca-runtime-get-status.ts',
'src/main/orcad/orcad-browser-startup.ts',
'src/main/runtime/browser/browser-client-page-opening.ts'
])('routes the managed browser capability oracle from %s', (path) => {
expect(selectPrE2eSpecs([path])).toContain('tests/e2e/ssh-orcad-browser-capabilities.spec.ts')
})
it.each(['errors', 'status', 'lifecycle', 'restart-attempt'])(
'routes the real browser service status check from %s changes',
(name) => {
expect(
selectPrE2eSpecs([
`src/renderer/src/components/browser-pane/stream-remote/remote-browser-stream-${name}.ts`
])
).toContain('tests/e2e/ssh-orcad-browser-service-status.spec.ts')
}
)
it.each([
'src/renderer/src/lib/ssh-workspace-browser-route-eligibility.ts',
'src/renderer/src/components/browser-pane/use-ssh-workspace-browser-route.ts',
'src/main/browser/local-ssh-browser-route.ts',
'src/renderer/src/store/repos/converted-ssh-browser-pages.ts',
'src/renderer/src/store/slices/browser/browser-tab-actions.ts',
'src/renderer/src/hooks/ipc-events/ssh-managed-server-state-effects.ts'
])('routes the managed browser routing oracle from %s', (path) => {
expect(selectPrE2eSpecs([path])).toContain('tests/e2e/ssh-orcad-browser-routing.spec.ts')
})
it.each([
'src/renderer/src/lib/workspace-browser-tab-open.ts',
'src/renderer/src/lib/http-link-destinations.ts',
'src/renderer/src/components/terminal-pane/terminal-http-link-source-owner.ts'
])('routes the managed terminal link oracle from %s', (path) => {
expect(selectPrE2eSpecs([path])).toContain('tests/e2e/ssh-orcad-terminal-browser-link.spec.ts')
})
it.each([
'src/shared/desktop-browser-bridge-protocol.ts',
'src/main/runtime/desktop-browser-bridge/desktop-browser-bridge-route.ts',
'src/main/browser/desktop-browser-bridge/desktop-browser-bridge-executor.ts',
'src/main/ipc/desktop-browser-bridge-wiring.ts',
'src/main/runtime/rpc/methods/desktop-browser-bridge.ts'
])('routes the managed desktop browser bridge oracle from %s', (path) => {
expect(selectPrE2eSpecs([path])).toContain('tests/e2e/ssh-orcad-desktop-browser-bridge.spec.ts')
})
const workflow = parse(readFileSync('.github/workflows/e2e.yml', 'utf8'))
const prWorkflow = parse(readFileSync('.github/workflows/pr.yml', 'utf8'))
const classify = (specs) => classifyE2eJobs(JSON.stringify(specs))
it('scales broad general selections without charging extra VMs for duplicate or dedicated specs', () => {
@@ -85,34 +34,6 @@ it('scales broad general selections without charging extra VMs for duplicate or
}
})
function expectManagedSshLane(spec) {
expect(classify([spec])).toEqual({ e2e_run_changed: false, e2e_needs_build: true })
expect(spec.startsWith(MANAGED_SSH_E2E_SPEC_PREFIX)).toBe(true)
const lane = workflow.jobs['orcad-ssh-docker']
expect(lane.if).toContain(`contains(inputs.test_files, '${MANAGED_SSH_E2E_SPEC_PREFIX}')`)
expect(
lane.steps.some(
(step) =>
step.run?.includes('selection.selectManagedSshE2eSpecs') &&
step.env?.ORCA_E2E_SSH_DOCKER === '1'
)
).toBe(true)
}
it('runs real browser file-drop ownership in the template-building Docker job', () => {
const spec = 'tests/e2e/ssh-orcad-browser-drop-owner.spec.ts'
expectManagedSshLane(spec)
for (const source of [
'src/renderer/src/lib/file-preview.ts',
'src/renderer/src/lib/workspace-file-drag.ts',
'src/renderer/src/components/browser-pane/navigate/use-browser-page-navigation-downloads.ts',
'src/renderer/src/components/right-sidebar/FileExplorerRow.tsx',
'tests/e2e/helpers/browser-split-guest-probes.ts'
]) {
expect(selectPrE2eSpecs([source]), source).toContain(spec)
}
})
it('skips the general consumer only when every requested spec has a dedicated owner', () => {
for (const spec of DEDICATED_E2E_SPECS) {
expect(classify([spec]).e2e_run_changed, spec).toBe(false)
@@ -164,28 +85,6 @@ it('preserves the requested specs across source-routed and mixed selections', ()
}
})
it('applies allocation hints only to PRs and retains other callers and full references', () => {
for (const name of ['run_changed_e2e', 'needs_build']) {
expect(workflow.on.workflow_call.inputs[name]).toMatchObject({
type: 'boolean',
required: false,
default: true
})
}
expect(workflow.jobs.build.if).toBe(
"inputs.test_files == '' || github.event_name != 'pull_request' || inputs.needs_build"
)
expect(workflow.jobs.e2e.if).toBe("inputs.test_files == ''")
const changed = workflow.jobs['changed-e2e']
expect(changed.if).toContain("github.event_name != 'pull_request' || inputs.run_changed_e2e")
const command = changed.steps.find((step) => step.name === 'Run changed E2E specs').run
expect(command).toContain('node config/scripts/ci-e2e-job-selection.mjs >')
expect(command).not.toContain('mapfile -t TEST_FILES < <(')
const fallback = [...command.matchAll(/\. != "([^"]+)"/g)].map((match) => match[1])
expect(fallback).toEqual(DEDICATED_E2E_SPECS)
expect(command).toContain(`(startswith("${MANAGED_SSH_E2E_SPEC_PREFIX}") | not)`)
})
it('publishes conservative hints for malformed evidence and refuses a malformed consumer list', async () => {
const program = 'config/scripts/ci-e2e-job-selection.mjs'
const consumer = await runProcess({
@@ -206,112 +105,3 @@ it('publishes conservative hints for malformed evidence and refuses a malformed
'e2e_run_changed=true\ne2e_needs_build=true\ne2e_ssh_shards=["1/2","2/2"]\n'
)
})
it('classifies PR consumers in the existing detector and passes conservative allocation hints', () => {
const detector = prWorkflow.jobs.code_paths
expect(detector.steps[0].with['sparse-checkout']).toContain(
'/config/scripts/ci-e2e-job-selection.mjs'
)
for (const name of ['e2e_run_changed', 'e2e_needs_build']) {
expect(detector.outputs[name]).toBe(`\${{ steps.e2e_filter.outputs.${name} }}`)
}
const command = detector.steps.find((step) => step.id === 'e2e_filter').run
expect(command).toContain('ci-e2e-job-selection.mjs --job-outputs >> "$GITHUB_OUTPUT"')
expect(prWorkflow.jobs.e2e.with.run_changed_e2e).toBe(
"${{ needs.code_paths.outputs.e2e_run_changed != 'false' }}"
)
expect(prWorkflow.jobs.e2e.with.needs_build).toBe(
"${{ needs.code_paths.outputs.e2e_needs_build != 'false' }}"
)
})
it.each([
'src/renderer/src/runtime/web-session-tabs-sync/mirrored-editor-file-identity.ts',
'src/renderer/src/runtime/web-session-tabs-sync/tab-builders.ts',
'src/renderer/src/runtime/web-session-tabs-sync/apply-preparation-browser.ts',
'src/renderer/src/runtime/web-session-existing-tab-index.ts'
])('routes %s to the template-building editor ownership lane', (file) => {
const spec = 'tests/e2e/ssh-orcad-editor-ownership.spec.ts'
expect(selectPrE2eSpecs([file])).toContain(spec)
expectManagedSshLane(spec)
})
const MARKDOWN_CONVERSION_SPEC = 'tests/e2e/ssh-orcad-markdown-conversion.spec.ts'
const MARKDOWN_LINK_REFRESH_SPEC = 'tests/e2e/ssh-orcad-markdown-link-refresh.spec.ts'
const MARKDOWN_LIVE_DOCUMENTS_SPEC = 'tests/e2e/ssh-orcad-markdown-live-documents.spec.ts'
it.each([
['src/renderer/src/components/editor/useMarkdownDocuments.ts', MARKDOWN_CONVERSION_SPEC],
[
'src/renderer/src/components/editor/restored-editor-workspace-runtime-owner.ts',
MARKDOWN_CONVERSION_SPEC
],
[
'src/renderer/src/components/editor/migrate-restored-editor-file-owner.ts',
MARKDOWN_CONVERSION_SPEC
],
['src/renderer/src/components/editor/rich-markdown-doc-link.ts', MARKDOWN_LINK_REFRESH_SPEC],
[
'src/renderer/src/components/editor/useRichMarkdownProgrammaticSync.ts',
MARKDOWN_LINK_REFRESH_SPEC
],
['src/renderer/src/components/editor/useMarkdownDocuments.ts', MARKDOWN_LIVE_DOCUMENTS_SPEC],
[
'src/renderer/src/components/editor/use-markdown-document-watch-refresh.ts',
MARKDOWN_LIVE_DOCUMENTS_SPEC
],
[
'src/renderer/src/components/editor/markdown-document-list-request.ts',
MARKDOWN_LIVE_DOCUMENTS_SPEC
]
])('routes %s to the template-building Markdown lane %s', (file, spec) => {
expect(selectPrE2eSpecs([file])).toContain(spec)
expectManagedSshLane(spec)
})
it.each([
'src/renderer/src/hooks/useEditorExternalWatch.ts',
'src/renderer/src/hooks/editor-runtime-file-watch.ts'
])('routes %s to the template-building editor watch recovery lane', (file) => {
const spec = 'tests/e2e/ssh-orcad-editor-watch-recovery.spec.ts'
expect(selectPrE2eSpecs([file])).toContain(spec)
expectManagedSshLane(spec)
})
it.each([
'src/renderer/src/components/right-sidebar/useFileExplorerWatch.ts',
'src/renderer/src/hooks/worktree-file-change-event.ts'
])('routes %s to the template-building explorer watch recovery lane', (file) => {
const spec = 'tests/e2e/ssh-orcad-explorer-watch-recovery.spec.ts'
expect(selectPrE2eSpecs([file])).toContain(spec)
expectManagedSshLane(spec)
})
it.each([
'src/renderer/src/components/right-sidebar/useFileExplorerWatch.ts',
'src/renderer/src/components/right-sidebar/file-explorer-operation-owner.ts',
'src/renderer/src/components/right-sidebar/FileExplorer.tsx',
'src/renderer/src/hooks/editor-external-watch-targets.ts',
'src/renderer/src/components/right-sidebar/useFileExplorerTree.ts',
'src/renderer/src/components/right-sidebar/use-file-explorer-tree-load-effects.ts'
])('routes %s to the template-building selected-host explorer lane', (file) => {
const spec = 'tests/e2e/ssh-orcad-explorer-selected-host.spec.ts'
expect(selectPrE2eSpecs([file])).toContain(spec)
expectManagedSshLane(spec)
})
it('runs managed terminal root ownership in the Docker conversion lane', () => {
const spec = 'tests/e2e/ssh-orcad-terminal-root-owner.spec.ts'
expectManagedSshLane(spec)
for (const source of [
'src/renderer/src/components/terminal-pane/terminal-worktree-path-link.ts',
'src/renderer/src/components/terminal-pane/terminal-file-open-routing.ts',
'src/renderer/src/components/terminal-pane/terminal-file-link-actions.ts',
'src/renderer/src/components/terminal-pane/terminal-file-link-hit-testing.ts',
'src/renderer/src/components/terminal-pane/terminal-link-handlers.ts',
'src/renderer/src/lib/worktree-owner-route.ts',
'tests/e2e/helpers/terminal-workspace-root-link.ts'
]) {
expect(selectPrE2eSpecs([source]), source).toContain(spec)
}
})
@@ -11,16 +11,8 @@ const REPO_ROOT = join(import.meta.dirname, '..', '..')
const require = createRequire(import.meta.url)
const electronBuilderConfig = require('../electron-builder.config.cjs')
const { copyFiles, FileMatcher } = require('app-builder-lib/out/fileMatcher')
const FpmTarget = require('app-builder-lib/out/targets/FpmTarget').default
const electronBuilderNativeRebuild = require('./electron-builder-native-rebuild.cjs')
describe('electron-builder config', () => {
it('keeps the packaged app identity aligned with local-build validation', () => {
expect(electronBuilderConfig.appId).toBe(
require('../../src/shared/local-build-compatibility-contract.json').appId
)
})
it('excludes repo-only source trees from app.asar', () => {
const matcher = new FileMatcher('/app', '/dest', (value) => value, electronBuilderConfig.files)
matcher.prependPattern('**/*')
@@ -215,64 +207,6 @@ describe('electron-builder config', () => {
}
})
it('keeps runtime resources available through extraResources', () => {
const bundledPluginResources = expect.objectContaining({
from: 'resources/plugins/launch',
to: 'plugins/launch'
})
for (const platform of ['mac', 'linux', 'win']) {
expect(electronBuilderConfig[platform].extraResources).toContainEqual({
from: 'resources/skills',
to: 'skills'
})
expect(electronBuilderConfig[platform].extraResources).toEqual(
expect.arrayContaining([bundledPluginResources])
)
}
expect(electronBuilderConfig.mac.extraResources).toEqual(
expect.arrayContaining([
expect.objectContaining({
from: 'native/computer-use-macos/.build/release/Orca Computer Use.app',
to: 'Orca Computer Use.app'
})
])
)
expect(electronBuilderConfig.linux.extraResources).toEqual(
expect.arrayContaining([
expect.objectContaining({
from: 'native/computer-use-linux/runtime.py',
to: 'computer-use-linux/runtime.py'
})
])
)
expect(electronBuilderConfig.win.extraResources).toEqual(
expect.arrayContaining([
expect.objectContaining({
from: 'native/computer-use-windows/runtime.ps1',
to: 'computer-use-windows/runtime.ps1'
}),
expect.objectContaining({
from: 'native/windows-cli-launcher/.build/orca.exe',
to: 'bin/orca.exe'
}),
expect.objectContaining({
from: 'native/windows-cli-launcher/.build/orca-administrator-restart.exe',
to: 'bin/orca-administrator-restart.exe'
})
])
)
})
it('ships one macOS serve-sim package through the runtime closure', () => {
const serveSimResources = electronBuilderConfig.mac.extraResources.filter((resource) =>
[join('node_modules', 'serve-sim'), 'serve-sim'].includes(resource.to)
)
expect(serveSimResources).toEqual([
expect.objectContaining({ to: join('node_modules', 'serve-sim') })
])
})
it.each(['mac', 'linux'])(
'loads Codex TOML parsing after %s runtime pruning',
async (platform) => {
@@ -305,84 +239,12 @@ describe('electron-builder config', () => {
)
// Why: serve-sim's addon is a Mach-O, and Windows signing rejects every *.node that is not PE.
it('keeps serve-sim out of the Windows and Linux runtime closures', () => {
const {
PACKAGED_RUNTIME_PACKAGE_ROOTS,
createPackagedRuntimeNodeModuleResources
} = require('../packaged-runtime-node-modules.cjs')
expect(PACKAGED_RUNTIME_PACKAGE_ROOTS).not.toContain('serve-sim')
const serveSimTarget = join('node_modules', 'serve-sim')
expect(createPackagedRuntimeNodeModuleResources('linux').map((r) => r.to)).not.toContain(
serveSimTarget
)
expect(electronBuilderConfig.linux.extraResources.map((r) => r.to)).not.toContain(
serveSimTarget
)
expect(electronBuilderConfig.win.extraResources.map((r) => r.to)).not.toContain(serveSimTarget)
})
// Source shim unpacked under resources/win32/bin lacks adjacent orca.exe (#7351); only extraResources/bin copy works.
it('keeps the Windows CLI shim source tree out of app.asar', () => {
expect(electronBuilderConfig.files).toEqual(
expect.arrayContaining(['!resources/win32{,/**/*}'])
)
// Regression guard: the working shim must still ship via extraResources.
expect(electronBuilderConfig.win.extraResources).toEqual(
expect.arrayContaining([
expect.objectContaining({
from: 'resources/win32/bin/orca.cmd',
to: 'bin/orca.cmd'
})
])
)
})
// macOS 26 UNUserNotificationCenter aborts helpers launched from Resources; ship under MacOS (#7929).
it('ships the mac notification-status helper in Contents/MacOS, not Resources', () => {
expect(electronBuilderConfig.mac.extraFiles).toEqual(
expect.arrayContaining([
expect.objectContaining({
from: 'native/notification-status-macos/.build/release/orca-notification-status',
to: 'MacOS/orca-notification-status'
})
])
)
expect(electronBuilderConfig.mac.extraResources).not.toEqual(
expect.arrayContaining([expect.objectContaining({ to: 'orca-notification-status' })])
)
})
it('ships the mac keyboard-layout helper in Contents/MacOS, not Resources', () => {
expect(electronBuilderConfig.mac.extraFiles).toEqual(
expect.arrayContaining([
expect.objectContaining({
from: 'native/keyboard-layout-macos/.build/release/orca-keyboard-layout',
to: 'MacOS/orca-keyboard-layout'
})
])
)
expect(electronBuilderConfig.mac.extraResources).not.toEqual(
expect.arrayContaining([expect.objectContaining({ to: 'orca-keyboard-layout' })])
)
})
it('unpacks the compiled CommonJS boundary with CLI runtime files', () => {
expect(electronBuilderConfig.asarUnpack).toEqual(
expect.arrayContaining(['out/package.json', 'out/cli/**', 'out/shared/**'])
)
})
// Missing unpacked entry silently restores in-process watcher and #7547 main crashes.
it('unpacks the forked parcel-watcher process entry', () => {
expect(electronBuilderConfig.asarUnpack).toEqual(
expect.arrayContaining(['out/main/parcel-watcher-process-entry.js'])
)
})
it('unpacks the replaceable WSL transcript filesystem process entry', async () => {
const entryFilename = 'wsl-transcript-fs-process-entry.js'
expect(electronBuilderConfig.asarUnpack).toContain(`out/main/${entryFilename}`)
})
// CLI Node mode cannot read asar; packed entry sends orca serve to Electron (#27307).
it('unpacks the orcad serve-selection entry the CLI runs', async () => {
@@ -425,41 +287,6 @@ describe('electron-builder config', () => {
}
})
it('keeps the worker-thread hang watchdog inside app.asar', () => {
expect(electronBuilderConfig.asarUnpack).not.toContain(
'out/main/main-thread-hang-watchdog-entry.js'
)
})
it('uses the multi-size icon source for Linux packages', () => {
expect(electronBuilderConfig.linux.icon).toBe('resources/build/icon.icns')
})
it('matches the Linux desktop entry to Electron window class', () => {
expect(electronBuilderConfig.linux.desktop.entry.StartupWMClass).toBe('orca')
})
it('uses the release artifact set as local Linux targets without changing existing names', () => {
expect(electronBuilderConfig.linux.target).toEqual(['AppImage', 'deb', 'rpm'])
expect(electronBuilderConfig.toolsets).toEqual({ appimage: '1.0.3' })
expect(electronBuilderConfig.appImage.artifactName).toBe('orca-linux.${ext}')
expect(electronBuilderConfig.deb.artifactName).toBe('orca-ide_${version}_${arch}.${ext}')
expect(electronBuilderConfig.rpm).toMatchObject({
packageName: 'orca-ide',
artifactName: 'orca-ide-${version}.${arch}.${ext}'
})
})
it('retains electron-builder runtime dependencies in deb and rpm packages', () => {
for (const target of ['deb', 'rpm']) {
const dependencies = electronBuilderConfig[target].depends
expect(dependencies).toEqual(
expect.arrayContaining(FpmTarget.prototype.getDefaultDepends(target))
)
expect(new Set(dependencies).size).toBe(dependencies.length)
}
})
it('validates each AppImage before electron-builder publishes it', async () => {
const root = await mkdtemp(join(tmpdir(), 'orca-electron-builder-appimage-'))
try {
@@ -548,11 +375,6 @@ describe('electron-builder config', () => {
require('../electron-builder.config.cjs')
}
})
it('uses Orca native rebuild hook instead of electron-builder default rebuild', () => {
expect(electronBuilderConfig.beforeBuild).toBe(electronBuilderNativeRebuild)
expect(electronBuilderConfig.npmRebuild).toBe(true)
})
})
describe('arch-aware packaging guard', () => {
@@ -1,8 +1,7 @@
import { existsSync, readFileSync, readdirSync } from 'node:fs'
import { cp, mkdir, mkdtemp, readFile, readdir, stat, writeFile } from 'node:fs/promises'
import { createRequire } from 'node:module'
import { tmpdir } from 'node:os'
import { delimiter, dirname, join, relative, resolve } from 'node:path'
import { delimiter, dirname, join, resolve } from 'node:path'
import { describe, expect, it } from 'vitest'
import { removeTree } from '../../src/shared/windows-transient-lock-removal.ts'
@@ -10,10 +9,6 @@ const require = createRequire(import.meta.url)
const projectRoot = resolve(import.meta.dirname, '..', '..')
const electronBuilderConfig = require('../electron-builder.config.cjs')
const {
createPackagedRuntimeNodeModuleResources,
findAsarEntry,
isPackagedExternalSpecifier,
packageNameFromSpecifier,
prunePackagedNodePty,
prunePackagedParcelWatcher,
prunePackagedSherpaOnnx,
@@ -22,11 +17,6 @@ const {
verifyPackagedMainRuntimeDeps
} = require('../packaged-runtime-node-modules.cjs')
// Only os:win32 process-tree presence selects Windows plan; universal registry workspace link proves no binary exists.
const windowsAddonsInstalled = existsSync(
join(projectRoot, 'node_modules', '@vscode', 'windows-process-tree', 'package.json')
)
describe('packaged runtime resources', () => {
it('verifies packaged main runtime deps from Windows-style asar entries', async () => {
const resourcesDir = await mkdtemp(join(tmpdir(), 'orca-runtime-deps-'))
@@ -176,13 +166,6 @@ describe('packaged runtime resources', () => {
}
})
it('normalizes host-specific asar entry separators', () => {
expect(findAsarEntry(['\\out\\main\\index.js'], 'out/main/index.js')).toBe(
'\\out\\main\\index.js'
)
expect(findAsarEntry(['/out/main/index.js'], 'out/main/index.js')).toBe('/out/main/index.js')
})
it('prunes non-target node-pty architecture outputs from packaged runtime resources', async () => {
const resourcesDir = await mkdtemp(join(tmpdir(), 'orca-node-pty-prune-'))
try {
@@ -251,30 +234,6 @@ describe('packaged runtime resources', () => {
}
})
it('includes external main dependencies in the packaged runtime closure', () => {
// Without packaged @parcel/watcher, serve silently stops propagating file changes (#4851).
const packaged = createPackagedRuntimeNodeModuleResources()
const packagedTargets = packaged.map((resource) => resource.to)
expect(packagedTargets).toContain(join('node_modules', '@parcel', 'watcher'))
expect(
packagedTargets.some((target) =>
target.startsWith(join('node_modules', '@parcel', 'watcher-'))
)
).toBe(true)
expect(packagedTargets).toContain(join('node_modules', 'proper-lockfile'))
})
it('includes the Claude agent SDK in every desktop package plan', () => {
for (const platform of windowsAddonsInstalled
? ['darwin', 'linux', 'win32']
: ['darwin', 'linux']) {
const packagedTargets = createPackagedRuntimeNodeModuleResources(platform).map(
(resource) => resource.to
)
expect(packagedTargets).toContain(join('node_modules', '@anthropic-ai', 'claude-agent-sdk'))
}
})
it('prunes non-target @parcel/watcher architecture subpackages', async () => {
const resourcesDir = await mkdtemp(join(tmpdir(), 'orca-parcel-watcher-prune-'))
try {
@@ -573,85 +532,7 @@ async function seedBundledRipgrep(resourcesDir) {
}
}
// Bundler renames createRequire result, hiding these from bare-require scans; source-anchor or packaging passes before runtime MODULE_NOT_FOUND.
function collectLazyRequireSpecifiers(directory, found = new Map()) {
for (const entry of readdirSync(directory, { withFileTypes: true })) {
const entryPath = join(directory, entry.name)
if (entry.isDirectory()) {
collectLazyRequireSpecifiers(entryPath, found)
continue
}
if (!entry.isFile() || !entry.name.endsWith('.ts') || entry.name.includes('.test.')) {
continue
}
const source = readFileSync(entryPath, 'utf8')
if (!source.includes('createRequire(')) {
continue
}
for (const match of source.matchAll(/\brequire[A-Za-z0-9_]*\(\s*'([^']+)'\s*\)/g)) {
if (isPackagedExternalSpecifier(match[1])) {
found.set(match[1], relative(projectRoot, entryPath).replaceAll('\\', '/'))
}
}
}
return found
}
function packagedResourceDestinations(platform) {
return new Set(
(electronBuilderConfig[platform].extraResources ?? []).map((resource) =>
String(resource.to).replaceAll('\\', '/')
)
)
}
describe('lazily required packages reach Resources/node_modules', () => {
it('excludes runtime builtins while retaining ordinary lazy dependencies', async () => {
const sourceDir = await mkdtemp(join(tmpdir(), 'orca-lazy-builtins-'))
try {
await writeFile(
join(sourceDir, 'runtime.ts'),
[
'const requireFromMain = createRequire(import.meta.url)',
"requireFromMain('node:fs')",
"requireFromMain('zod')"
].join('\n')
)
expect([...collectLazyRequireSpecifiers(sourceDir).keys()]).toEqual(['zod'])
} finally {
await removeTree(sourceDir)
}
})
it('copies every createRequire specifier main uses into the packaged resource plan', () => {
const specifiers = collectLazyRequireSpecifiers(join(projectRoot, 'src', 'main'))
expect(specifiers.size).toBeGreaterThan(0)
const destinations = {
win: packagedResourceDestinations('win'),
mac: packagedResourceDestinations('mac'),
linux: packagedResourceDestinations('linux')
}
for (const [specifier, source] of specifiers) {
const packageName = packageNameFromSpecifier(specifier)
const covered = (platform) =>
destinations[platform].has(`node_modules/${packageName}`) ||
destinations[platform].has(`node_modules/${specifier}`)
// The Windows CI lane checks the full closure with its native addons installed.
if (windowsAddonsInstalled) {
expect(
covered('win'),
`${source} lazily requires '${specifier}', but nothing copies it to Resources/node_modules`
).toBe(true)
}
if (covered('mac') && covered('linux')) {
continue
}
// Only the Windows-native loaders may be absent from the mac/linux plans.
expect(source, `'${specifier}' is packaged for Windows only`).toContain('windows')
}
})
it('resolves the copied emoji dataset the way the packaged main bundle does', async () => {
const resourcesDir = await mkdtemp(join(tmpdir(), 'orca-lazy-require-'))
try {
@@ -2,15 +2,11 @@
// Web seam removes native audio/keep-awake: their web stubs deny microphone and do nothing, rather than capturing through shell.
// Resolve package names before absence checks to catch typos; assert eight excluded vendored modules, not drifting total closure size.
import { createRequire } from 'node:module'
import { fileURLToPath } from 'node:url'
import { describe, expect, it } from 'vitest'
import { mobileWebAppRouteClosure } from '../build-mobile-web-app-bundle.mjs'
import { mobileWebAppDependenciesPresent } from './mobile-web-app-bundle-dependencies.mjs'
import { MOBILE_WEB_PAGE_ROUTES } from '../mobile-web-page-routes.mjs'
import {
PAGE_ROUTE_MODULES,
pageRouteModulesCoverTheManifest
} from './mobile-web-app-page-route-modules.mjs'
import { PAGE_ROUTE_MODULES } from './mobile-web-app-page-route-modules.mjs'
import { MobileWebBundleRouteSchema } from '../../../src/shared/mobile-web-bundle/manifest-contract.ts'
// Gate mobile transforms too: nearest tsconfig extends Expo package absent from root-only shards.
@@ -21,7 +17,6 @@ const SEAM = 'src/platform/dictation-capture.web.ts'
const NATIVE_SEAM = 'src/platform/dictation-capture.ts'
// Use census’s verb set; bridge-audio-verbs.test.ts checks it against shell table.
const DICTATION_GRANTS = ['native.audio.start', 'native.audio.read', 'native.audio.stop']
// Native modules reach JSI; browser substitutes denied microphone and no-op wake lock.
@@ -74,24 +69,6 @@ describeClosure(
expect(await grantsMissingForRoutes(MOBILE_WEB_PAGE_ROUTES)).toEqual([])
})
it('finds the seam in exactly one closure, which is the session route', async () => {
const reaching = []
for (const route of MOBILE_WEB_PAGE_ROUTES) {
const module = PAGE_ROUTE_MODULES.get(route.pathname)
if (module === undefined) {
throw new Error(`No route module for ${route.pathname}`)
}
const closure = await closureOf(module)
if (closure.local.includes(SEAM)) {
reaching.push(route.pathname)
}
}
// Require session route reaches seam; absence checks must not pass because dictation left page.
expect(reaching).toEqual([SESSION_PATHNAME])
const session = await closureOf(SESSION)
expect(session.local).toContain(SEAM)
})
it('reds the same rule when the session route is registered without them', async () => {
// Drive same rule over under-granted entry to prove missing grants are detected.
expect(
@@ -133,30 +110,3 @@ describeClosure(
},
240_000
)
describe('the census rule itself', () => {
it('names a route module for every registered pathname', () => {
const { mapped, declared } = pageRouteModulesCoverTheManifest(MOBILE_WEB_PAGE_ROUTES)
expect(mapped).toEqual(declared)
})
// Dynamic import delays load but still transforms mobile tsconfig extending absent Expo; dependency gate must enclose import on root-only shards.
it.skipIf(!bundles)(
'names only verbs the shell actually serves, read from its own table',
async () => {
// Require shell verb-table entries; route census agreeing with its own copied names proves nothing.
const { BRIDGE_NATIVE_VERB_NAMES } =
await import('../../../mobile/src/mobile-web-shell/bridge/bridge-native-verbs.ts')
expect(new Set(DICTATION_GRANTS).size).toBe(3)
for (const grant of DICTATION_GRANTS) {
expect(BRIDGE_NATIVE_VERB_NAMES, grant).toContain(grant)
expect(
MobileWebBundleRouteSchema.safeParse({ pathname: '/h', grants: [grant] }).success,
grant
).toBe(true)
}
}
)
})
export const MOBILE_DIR = fileURLToPath(new URL('../../../mobile/', import.meta.url))
@@ -1,27 +1,13 @@
import { join } from 'node:path'
import { fileURLToPath } from 'node:url'
import { beforeAll, describe, expect, it } from 'vitest'
import { mobileWebAppRouteClosure } from '../build-mobile-web-app-bundle.mjs'
import { mobileWebAppDependenciesPresent } from './mobile-web-app-bundle-dependencies.mjs'
import { mobileWebAppRouteChunkClosure } from './mobile-web-app-route-chunk-closure.mjs'
import {
editableHostFontSizeOffenders,
editableHostsIn,
unresolvedEditableHostStyles
} from './platform-seams/mobile-web-app-editable-host-font-size.mjs'
import {
textInputFontSizeOffenders,
unresolvedTextInputStyles
} from './platform-seams/mobile-web-app-text-input-font-size-seam.mjs'
// Require terminal source modules/xterm, excluding WebView engine strings and generated document/factory to avoid shipping document twice.
// Remeasure closure after generators and upstream merges; shared inputs make summed historical module deltas unreliable.
// Input metafile includes dynamic imports: only emitted static import-statement edges prove Mermaid is deferred.
// Prebundle Mermaid as one artifact; package splitting once emitted 103 scripts against shell’s 256-asset limit. Deferral saves fetch/evaluation, not OTA download.
const projectDir = fileURLToPath(new URL('../../..', import.meta.url))
const mobileDir = join(projectDir, 'mobile')
const SESSION_ROUTE = 'app/h/[hostId]/session/[worktreeId].tsx'
// WebView-only generated programs cannot execute in page.
@@ -49,9 +35,6 @@ const GAINED_OUTSIDE_THE_DOCUMENT = [
const XTERM_PACKAGES = ['@xterm/xterm', '@xterm/addon-unicode11', '@xterm/addon-webgl']
// Keep all route text inputs above focus-zoom floor, including newly imported terminal inputs.
const EXPECTED_OFFENDERS = 0
const MERMAID_PAGE_ENGINE = 'src/components/pr-sidebar/mermaid-page-engine.generated.ts'
const MERMAID_PACKAGE = 'node_modules/mermaid/'
@@ -123,25 +106,6 @@ describeClosure(
// Require route chunk in walk so empty static download cannot pass.
expect(download.staticChunks).toContain(download.routeChunk)
}, 600_000)
it('leaves the 16px seam census exactly where C7.2 left it', async () => {
// Require seam reachable and every style resolved before interpreting zero offenders.
expect(closure.local).toContain('src/platform/text-input-font-size.web.ts')
expect(unresolvedTextInputStyles(mobileDir, closure)).toEqual([])
expect(textInputFontSizeOffenders(mobileDir, closure)).toHaveLength(EXPECTED_OFFENDERS)
expect(closure.local).toContain('src/session/mobile-native-chat-input-styles.web.ts')
expect(closure.local).not.toContain('src/session/mobile-native-chat-input-styles.ts')
}, 300_000)
it('holds the editables the TextInput census cannot see to the same floor', async () => {
// Rich editor contenteditable lives in markup/CSS, invisible to JSX text-input census; check same font floor through markup.
expect(editableHostsIn(mobileDir, closure)).toContainEqual({
file: 'src/components/rich-markdown/document-markup.ts',
id: 'editor'
})
expect(unresolvedEditableHostStyles(mobileDir, closure)).toEqual([])
expect(editableHostFontSizeOffenders(mobileDir, closure)).toEqual([])
}, 300_000)
},
900_000
)
-12
View File
@@ -8,7 +8,6 @@ import {
buildOrcadEntry,
escapeLegalCommentTail,
findNonAsciiSource,
moduleAtOffset,
ORCAD_COMMONJS_MODULE_OPTIONS
} from './orcad-entry-build.mjs'
import { runProcessSync } from '@orca/process-host'
@@ -111,11 +110,6 @@ it('keeps the server bundle ASCII, so V8 stores its source one byte per characte
expect(findNonAsciiSource(readFileSync(bundle, 'utf8'))).toBeNull()
})
it('names the line of a non-ASCII character a regex literal or tagged template left', () => {
expect(findNonAsciiSource('a=1\nb=/[\\u2726]/')).toBeNull()
expect(findNonAsciiSource('a=1\nb=/[\u2726]/')).toMatchObject({ offset: 8, line: 2 })
})
it('escapes only the trailing license comment, never code', () => {
const code = 'a=/[\u2726]/;'
const escaped = escapeLegalCommentTail(
@@ -127,12 +121,6 @@ it('escapes only the trailing license comment, never code', () => {
expect(escapeLegalCommentTail(code)).toBe(code)
})
it('names the module an unminified build placed an offset in', () => {
const source = 'x\n// src/a.ts\nvar a = 1;\n// node_modules/b/index.js\nvar b = /\u2726/;\n'
expect(moduleAtOffset(source, source.indexOf('\u2726'))).toBe('node_modules/b/index.js')
expect(moduleAtOffset(source, source.indexOf('var a'))).toBe('src/a.ts')
})
it('keeps a build with every dark switch turned on ASCII', () => {
const darkBundle = join(directory, 'orcad-server-dark.js')
const built = runProcessSync({
@@ -1,155 +1,13 @@
import { existsSync, readFileSync } from 'node:fs'
import { createRequire } from 'node:module'
import { readFileSync } from 'node:fs'
import { join, resolve } from 'node:path'
import { runInNewContext } from 'node:vm'
import { describe, expect, it } from 'vitest'
import { parse } from 'yaml'
import { compileAggregateExpression } from './pr-aggregate-gate-context.mjs'
const projectDir = resolve(import.meta.dirname, '../..')
const require = createRequire(import.meta.url)
const { createPackagedRuntimeNodeModuleResources } = require('../packaged-runtime-node-modules.cjs')
const readProject = (file) => readFileSync(join(projectDir, file), 'utf8')
const packageJson = JSON.parse(readProject('package.json'))
const pnpmWorkspace = parse(readProject('pnpm-workspace.yaml'))
// Resolve the win32 plan wherever its OS-gated addon is installed; windows-registry is a workspace link on every platform.
const windowsAddonsInstalled = existsSync(
join(projectDir, 'node_modules', '@vscode', 'windows-process-tree', 'package.json')
)
describe('Electron runtime package contract', () => {
const packageTargets = {
win32: windowsAddonsInstalled ? createPackagedRuntimeNodeModuleResources('win32') : [],
darwin: createPackagedRuntimeNodeModuleResources('darwin'),
linux: createPackagedRuntimeNodeModuleResources('linux')
}
it('keeps the native Windows registry addon optional and platform-gated', () => {
expect(packageJson.optionalDependencies['@orca/windows-registry']).toBe('workspace:*')
// allowBuilds suppresses install-time node-gyp; the Windows rebuild owns the addon’s runtime ABI.
expect(pnpmWorkspace.allowBuilds['@orca/windows-registry']).toBe(false)
if (windowsAddonsInstalled) {
expect(packageTargets.win32).toEqual(
expect.arrayContaining([
expect.objectContaining({ to: join('node_modules', '@orca', 'windows-registry') }),
expect.objectContaining({ to: join('node_modules', 'node-addon-api') })
])
)
}
for (const platform of ['darwin', 'linux']) {
expect(packageTargets[platform]).not.toEqual(
expect.arrayContaining([
expect.objectContaining({ to: join('node_modules', '@orca', 'windows-registry') })
])
)
}
})
// Without it the secure-file writes report unrestricted and orcad's privacy check fails closed.
it('ships the native Windows ACL addon on Windows only, built by the Windows rebuild', () => {
expect(packageJson.optionalDependencies['@orca/windows-security']).toBe('workspace:*')
expect(pnpmWorkspace.allowBuilds['@orca/windows-security']).toBe(false)
if (windowsAddonsInstalled) {
expect(packageTargets.win32).toEqual(
expect.arrayContaining([
expect.objectContaining({ to: join('node_modules', '@orca', 'windows-security') })
])
)
}
for (const platform of ['darwin', 'linux']) {
expect(packageTargets[platform]).not.toEqual(
expect.arrayContaining([
expect.objectContaining({ to: join('node_modules', '@orca', 'windows-security') })
])
)
}
})
it('keeps the native Windows process-table addon optional and platform-gated', () => {
expect(packageJson.optionalDependencies['@vscode/windows-process-tree']).toBe('0.8.0')
// As with registry, suppress install-time node-gyp so the Windows rebuild selects the runtime ABI.
expect(pnpmWorkspace.allowBuilds['@vscode/windows-process-tree']).toBe(false)
// Patch removes unavailable Spectre-library requirement and 1024-process enumeration cap that hides descendants on busy hosts.
expect(pnpmWorkspace.patchedDependencies['@vscode/windows-process-tree@0.8.0']).toBe(
'config/patches/@vscode__windows-process-tree@0.8.0.patch'
)
if (windowsAddonsInstalled) {
expect(packageTargets.win32).toEqual(
expect.arrayContaining([
expect.objectContaining({ to: join('node_modules', '@vscode', 'windows-process-tree') })
])
)
}
for (const platform of ['darwin', 'linux']) {
expect(packageTargets[platform]).not.toEqual(
expect.arrayContaining([
expect.objectContaining({ to: join('node_modules', '@vscode', 'windows-process-tree') })
])
)
}
})
it('keeps release-cut signing provenance on GitHub-hosted runners', () => {
const releaseWorkflow = parse(
readFileSync(join(projectDir, '.github/workflows/release-cut.yml'), 'utf8')
)
const buildMatrixRunners = releaseWorkflow.jobs.build.strategy.matrix.include.map(
({ os }) => os
)
const releaseWorkflowText = readFileSync(
join(projectDir, '.github/workflows/release-cut.yml'),
'utf8'
)
const macDispatchStep = releaseWorkflow.jobs['build-mac'].steps.find(
(step) => step.name === 'Run isolated macOS release build'
)
expect(releaseWorkflowText).not.toContain('blacksmith-')
for (const caller of Object.values(releaseWorkflow.jobs)) {
if (!caller.uses?.startsWith('./.github/workflows/')) {
continue
}
const child = parse(readProject(caller.uses))
const inputs = {
...Object.fromEntries(
Object.entries(child.on.workflow_call.inputs).map(([name, input]) => [
name,
input.default ?? ''
])
),
...caller.with
}
for (const job of Object.values(child.jobs)) {
if (!job['runs-on']) {
continue
}
const runner = job['runs-on'].startsWith('${{')
? compileAggregateExpression(job['runs-on'], ['inputs'])({ inputs }).coerceString()
: job['runs-on']
expect(runner).not.toMatch(/^(blacksmith-|orca-)/)
}
}
expect(releaseWorkflow.jobs['build-mac']['runs-on']).toBe('ubuntu-latest')
expect(releaseWorkflow.jobs['build-mac'].permissions.actions).toBe('write')
expect(macDispatchStep.run).toBe(
'node config/scripts/run-release-mac-build-workflow.mjs dispatch'
)
expect(macDispatchStep.env.RELEASE_MAC_BUILD_WORKFLOW).toBe('release-mac-build.yml')
expect(macDispatchStep.env.RELEASE_MAC_BUILD_TAG).toBe('${{ needs.cut.outputs.tag }}')
expect(buildMatrixRunners).not.toContain('blacksmith-6vcpu-macos-15')
expect(releaseWorkflow.jobs['publish-release'].needs).toContain('build')
expect(releaseWorkflow.jobs['publish-release'].needs).toContain('build-mac')
expect(releaseWorkflow.jobs['publish-release'].needs).toContain('verify-mac-build')
const macVerification = releaseWorkflow.jobs['verify-mac-build']
expect(macVerification.needs).toEqual(['cut', 'build-mac', 'build'])
expect(macVerification.if).toContain('!cancelled()')
expect(macVerification.if).not.toContain('needs.build.result')
expect(macVerification.permissions).toEqual({ actions: 'read', contents: 'read' })
expect(releaseWorkflow.jobs['publish-release'].if).toContain(
"needs.verify-mac-build.result == 'success'"
)
})
it('qualifies macOS after failed sibling builds and publishes only complete successful builds', () => {
const { jobs } = parse(readProject('.github/workflows/release-cut.yml'))
const evaluate = (source, needs, cancelled) =>
@@ -1,13 +1,5 @@
import { spawnSync } from 'node:child_process'
import {
existsSync,
mkdirSync,
mkdtempSync,
readdirSync,
readFileSync,
rmSync,
writeFileSync
} from 'node:fs'
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join, resolve } from 'node:path'
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
@@ -15,7 +7,6 @@ import { afterEach, beforeEach, describe, expect, it } from 'vitest'
import {
findCachedElevatePaths,
replaceCachedElevateHelpers,
resolveElectronBuilderCacheDir,
summarizeSwap
} from './replace-cached-nsis-elevate.mjs'
@@ -116,44 +107,7 @@ describe('cached elevate.exe swap covers the real electron-builder layouts', ()
).toEqual([join(overrideDir, 'elevate.exe')])
})
it('resolves the cache root the same way app-builder-lib does', () => {
expect(
resolveElectronBuilderCacheDir({
env: { LOCALAPPDATA: 'C:\\Users\\runneradmin\\AppData\\Local' },
platform: 'win32'
})
).toBe(join('C:\\Users\\runneradmin\\AppData\\Local', 'electron-builder', 'Cache'))
expect(resolveElectronBuilderCacheDir({ env: {}, platform: 'darwin', home: '/Users/a' })).toBe(
join('/Users/a', 'Library', 'Caches', 'electron-builder')
)
expect(resolveElectronBuilderCacheDir({ env: { ELECTRON_BUILDER_CACHE: '/mnt/cache' } })).toBe(
'/mnt/cache'
)
})
// Compare real cache scan against independent unbounded walk to catch vacuous empty matches; skip only if no bundle is cached.
it('finds every elevate.exe the real electron-builder cache holds', (ctx) => {
const cacheDir = resolveElectronBuilderCacheDir()
if (!existsSync(cacheDir)) {
// Report missing real cache as skipped, never passed; fixture-only coverage cannot validate unknown layouts.
ctx.skip()
return
}
const walk = (dir) =>
readdirSync(dir, { withFileTypes: true }).flatMap((entry) => {
const path = join(dir, entry.name)
if (entry.isDirectory()) {
return walk(path)
}
return entry.name.toLowerCase() === 'elevate.exe' ? [path] : []
})
const onDisk = walk(cacheDir)
if (onDisk.length === 0) {
ctx.skip()
return
}
expect(findCachedElevatePaths(cacheDir, { env: {} }).sort()).toEqual(onDisk.sort())
})
})
describe('the probe, not the scan, decides whether the swap worked', () => {
-13
View File
@@ -10,19 +10,6 @@ const projectRoot = resolve(import.meta.dirname, '../..')
describe('7za path resolution for the Windows signing gates (#6487)', () => {
// Match upstream mac/win/linux architecture dirs; darwin/ or collapsed-arch guesses silently fail to resolve.
it.each([
['darwin', 'arm64', 'mac/arm64/7za'],
['darwin', 'x64', 'mac/x64/7za'],
['win32', 'x64', 'win/x64/7za.exe'],
['win32', 'ia32', 'win/ia32/7za.exe'],
['win32', 'arm64', 'win/arm64/7za.exe'],
['linux', 'x64', 'linux/x64/7za'],
['linux', 'arm64', 'linux/arm64/7za']
])('maps the %s/%s legacy layout to %s', (platform, arch, expected) => {
expect(legacy7zaRelativePath(platform, arch).join('/')).toBe(
`node_modules/7zip-bin/${expected}`
)
})
it('prefers an existing legacy file over the downloaded toolset', async () => {
const scratch = mkdtempSync(join(tmpdir(), 'orca 7za legacy '))
+1 -33
View File
@@ -1,11 +1,9 @@
import { readFileSync } from 'node:fs'
import { join, resolve } from 'node:path'
import { runProcessSync } from '@orca/process-host'
import { DEFAULT_MAX_OUTPUT_BYTES } from '@orca/process-host/process-spec'
import { describe, expect, it, vi } from 'vitest'
import { parseAllDocuments } from 'yaml'
import { auditToolEntry, runAuditTool } from './run-audit-tool.mjs'
import { classifyPrJobs } from './pr-code-change-scope.mjs'
const root = resolve(import.meta.dirname, '../..')
const success = { code: 0, signal: null, timedOut: false }
@@ -97,34 +95,4 @@ describe('locked audit tool execution', () => {
expect(() => runAuditTool('../arbitrary-package', [], { run })).toThrow('Unknown audit tool')
expect(run).not.toHaveBeenCalled()
})
it.each(['package.json', 'pnpm-lock.yaml', 'pnpm-workspace.yaml'])(
'selects the real-tool CI smoke for an isolated %s update',
(file) => {
expect(classifyPrJobs([`config/audit-tools/${file}`]).static_analysis).toBe(true)
}
)
it('keeps the tool workspace isolated, exact and integrity-locked', () => {
const manifest = JSON.parse(readFileSync(join(root, 'config/audit-tools/package.json'), 'utf8'))
const rootManifest = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8'))
expect(manifest.packageManager).toBe(rootManifest.packageManager)
const [policy] = parseAllDocuments(
readFileSync(join(root, 'config/audit-tools/pnpm-workspace.yaml'), 'utf8')
).map((document) => document.toJS())
expect(policy).toMatchObject({ minimumReleaseAge: 4320, ignoreScripts: true, allowBuilds: {} })
const documents = parseAllDocuments(
readFileSync(join(root, 'config/audit-tools/pnpm-lock.yaml'), 'utf8')
).map((document) => document.toJS())
const lock = documents.find((document) => document.importers?.['.']?.dependencies)
expect(Object.keys(lock.importers)).toEqual(['.'])
for (const [name, version] of Object.entries(manifest.dependencies)) {
expect(version).toMatch(/^\d+\.\d+\.\d+$/)
expect(lock.importers['.'].dependencies[name].specifier).toBe(version)
expect(rootManifest.dependencies[name] ?? rootManifest.devDependencies[name]).toBeUndefined()
}
for (const entry of Object.values(lock.packages)) {
expect(entry.resolution.integrity).toMatch(/^sha512-/)
}
})
})
@@ -1,11 +1,6 @@
import { existsSync, readFileSync } from 'node:fs'
import { readFileSync } from 'node:fs'
import { expect, it } from 'vitest'
import { importsSqliteRuntime, SQLITE_MODULES } from './vitest-sqlite-runtime-scan.mjs'
// A moved module would silently send its importers back to Bun.
it.each(SQLITE_MODULES)('scans for a SQLite module that still exists: %s', (module) => {
expect(existsSync(`${module}.ts`)).toBe(true)
})
import { importsSqliteRuntime } from './vitest-sqlite-runtime-scan.mjs'
it.each([
['import { createRestTestRig } from "./structured-agent-session-rest-test-rig"', true],
@@ -29,7 +29,6 @@ const WINDOWS_SHIM_SPAWN_ALLOWLIST = [
// Write or assert on shim files rather than spawning one.
'config/scripts/dev-cli-terminal-wrapper.mjs',
'config/scripts/dev-cli-terminal-wrapper.test.mjs',
'config/scripts/electron-builder-config.test.mjs',
'config/scripts/live-remote-freeze-rpc.mjs',
'config/scripts/pty-transcript-secret-scan.test.mjs',
'config/scripts/remote-agent-session-authority-repro.mjs',
@@ -1,6 +1,6 @@
// Exercise table-listed audio verbs through the engine seam; denied permission, full rings and post-stop reads are device-independent conditions expensive to force in simulators.
import { afterEach, describe, expect, it } from 'vitest'
import { MobileWebBundleRouteSchema } from '../../../../src/shared/mobile-web-bundle/manifest-contract'
import { BridgeNativeVerbRefusedError } from '../bridge-host-errors'
import { createNativeAudioCapture, type NativeAudioEngine } from '../../platform/native-audio'
import {
@@ -36,8 +36,6 @@ function gate() {
return { promise, release: () => release() }
}
const AUDIO_VERBS = ['native.audio.start', 'native.audio.read', 'native.audio.stop'] as const
function createTestEngine(
overrides: Partial<{
permission: NativeAudioEngine['requestPermission']
@@ -121,24 +119,6 @@ function decode(base64: string): Uint8Array {
return bytes
}
describe('the audio verbs in the table', () => {
it('accepts the audio verbs as route grants', () => {
for (const verb of AUDIO_VERBS) {
expect(
MobileWebBundleRouteSchema.safeParse({ pathname: '/h', grants: [verb] }).success,
verb
).toBe(true)
}
})
it('refuses the camel-cased spelling of the read, which is what makes the name load-bearing', () => {
expect(
MobileWebBundleRouteSchema.safeParse({ pathname: '/h', grants: ['native.audio.readChunk'] })
.success
).toBe(false)
})
})
describe('what the audio schemas refuse', () => {
it('refuses a start with no rate, a rate off the grid, and an unknown param', () => {
expect(audioStartParamsSchema.safeParse({}).success).toBe(false)
@@ -178,13 +178,6 @@ describe('MobileNativeChatSessionOptionPickers', () => {
})
})
it('lets the pill shrink so a long label never pushes the composer buttons off the row', () => {
mount([MODEL_DESCRIPTOR, EFFORT_DESCRIPTOR])
expect(findAllByHostType(renderer!.root, 'View')[0]!.props.style).toMatchObject({
flexShrink: 1
})
})
it('opens the model sheet and applies a picked model', async () => {
mount([MODEL_DESCRIPTOR, EFFORT_DESCRIPTOR])
await act(async () => pill('Model').props.onPress())
@@ -480,12 +480,6 @@ describe('ToolRun', () => {
})
})
it('lets a tap inside the run land while the keyboard is up, as the transcript list does', () => {
const tree = render([{ type: 'tool-call', name: 'Bash', input: { command: 'ls' } }])
const boxes = tree.root.findAll((node) => String(node.type) === 'ScrollView')
expect(boxes.map((box) => box.props.keyboardShouldPersistTaps)).toEqual(['handled'])
})
it('follows new work at the end of a live run, but holds still when the reader opens a row', () => {
const live = (blocks: NativeChatBlock[]) =>
createElement(ToolRun, {
@@ -530,23 +524,6 @@ describe('ToolRun', () => {
expect(scrolls.toEnd).toHaveBeenCalledTimes(1)
})
it("keeps a row's output out of inline selection where the platform forbids it", () => {
const tree = render([
{ type: 'tool-call', name: 'Bash', input: { command: 'ls' } },
{ type: 'tool-result', output: 'a.ts' }
])
act(() => {
tree.root
.findAll((node) => String(node.type) === 'Pressable')
.findLast((node) => textIn(node).includes('ls'))!
.props.onPress()
})
const output = tree.root.findAll(
(node) => String(node.type) === 'Text' && textIn(node)[0] === 'a.ts'
)[0]!
expect(output.props.selectable).toBe(false)
})
it('does not save a follow for a later row opening when new work left the height unchanged', () => {
const live = (blocks: NativeChatBlock[]) =>
createElement(ToolRun, {
@@ -28,19 +28,6 @@ function unresolved(
describe('rebaseMobileNativeChatPendingBaselines', () => {
const history = [userTurn('m1', 'run the tests', 1000), assistantTurn('m2', 'passed', 1100)]
it('returns the same array when every baseline is already resolved', () => {
const pending: MobileNativeChatPendingMessage[] = [
{
id: 'p1',
text: 'hi',
expectedOccurrence: 1,
baselineTailMessageId: 'm2',
baselineResolved: true
}
]
expect(rebaseMobileNativeChatPendingBaselines(history, pending)).toBe(pending)
})
it('gives an unresolved send the loaded tail as its glue boundary', () => {
expect(
rebaseMobileNativeChatPendingBaselines(history, [unresolved('p1', 'run the tests')])
@@ -1,34 +1,7 @@
import { describe, expect, it, vi } from 'vitest'
import {
summarizeToolInput,
toolFilePath
} from '../../../src/shared/native-chat/tools/native-chat-tool-summary'
import { summarizeToolInput } from '../../../src/shared/native-chat/tools/native-chat-tool-summary'
describe('summarizeToolInput', () => {
it('passes short strings through, collapsing whitespace', () => {
expect(summarizeToolInput('ls -la\n/tmp')).toBe('ls -la /tmp')
})
it('serializes objects to compact JSON', () => {
expect(summarizeToolInput({ path: 'a.ts', limit: 5 })).toBe('{"path":"a.ts","limit":5}')
})
it('truncates long previews with an ellipsis', () => {
const long = `prefix ${'x'.repeat(200)}`
const out = summarizeToolInput(long)
expect(out).toBe(`prefix ${'x'.repeat(72)}…`)
})
it('returns empty string for null/undefined', () => {
expect(summarizeToolInput(null)).toBe('')
expect(summarizeToolInput(undefined)).toBe('')
})
it('handles numbers and booleans', () => {
expect(summarizeToolInput(42)).toBe('42')
expect(summarizeToolInput(true)).toBe('true')
})
it('does not traverse an unbounded tool-input object for a short preview', () => {
const input: Record<string, unknown> = Object.fromEntries(
Array.from({ length: 8 }, (_unused, index) => [`key${index}`, index])
@@ -40,17 +13,3 @@ describe('summarizeToolInput', () => {
expect(lateGetter).not.toHaveBeenCalled()
})
})
describe('toolFilePath', () => {
it('extracts file_path / filePath / path / notebook_path', () => {
expect(toolFilePath({ file_path: 'src/a.ts' })).toBe('src/a.ts')
expect(toolFilePath({ filePath: 'src/b.ts' })).toBe('src/b.ts')
expect(toolFilePath({ path: 'src/c.ts' })).toBe('src/c.ts')
expect(toolFilePath({ notebook_path: 'n.ipynb' })).toBe('n.ipynb')
})
it('returns null when there is no file target', () => {
expect(toolFilePath({ command: 'ls' })).toBeNull()
expect(toolFilePath('x')).toBeNull()
expect(toolFilePath(null)).toBeNull()
})
})
@@ -48,22 +48,6 @@ const startRow: AgentJournalRenderItem = {
}
describe("the phone's line under a not-sent message", () => {
it.each([
['claude', 'Claude'],
['codex', 'Codex']
])('names %s when its terminal holds the conversation', (agent, name) => {
const notices = mobileNativeChatUnsentNotices(
{
items: [],
submissions: [submission('held', { rejection: { kind: 'conversationOpenInTerminal' } })]
},
agent
)
expect(notices.get(agentJournalSubmissionKey('held'))).toBe(
`${name} is already running this conversation in another terminal. Quit ${name} there or close that terminal to continue here.`
)
})
it("words each rejected message as the desktop does, naming the chat's agent", () => {
const notices = mobileNativeChatUnsentNotices(
{
@@ -1,90 +0,0 @@
import { describe, expect, it } from 'vitest'
import { AGENT_SESSION_COHERENT_MODEL_CATALOG_CAPABILITY } from '../../../src/shared/structured-agent-session-surface-capabilities'
import {
AGENT_SESSION_ATTACHMENTS_RUNTIME_CAPABILITY,
AGENT_SESSION_ITEM_DETAIL_RUNTIME_CAPABILITY,
AGENT_SESSION_CONVERSATION_STOP_RUNTIME_CAPABILITY,
AGENT_SESSION_PHONE_ATTACHMENTS_RUNTIME_CAPABILITY,
AGENT_SESSION_PROMPT_CANCEL_RUNTIME_CAPABILITY,
AGENT_SESSION_QUESTION_ANSWERS_RUNTIME_CAPABILITY,
AGENT_SESSION_QUEUED_COMMANDS_RUNTIME_CAPABILITY,
AGENT_SESSION_QUEUED_MESSAGE_EDIT_RUNTIME_CAPABILITY,
AGENT_SESSION_QUEUED_MESSAGE_IMAGE_EDIT_RUNTIME_CAPABILITY,
AGENT_SESSION_QUEUED_MESSAGES_RUNTIME_CAPABILITY,
AGENT_SESSION_STATUS_FEED_RUNTIME_CAPABILITY,
AGENT_SESSION_REPEATED_STOP_RUNTIME_CAPABILITY
} from '../../../src/shared/protocol-version'
import { AGENT_SESSION_SEND_OPTIONS_RUNTIME_CAPABILITY } from '../../../src/shared/agent-session-create-capabilities'
import { AGENT_SESSION_PERMISSION_MODES_RUNTIME_CAPABILITY } from '../../../src/shared/agent-session-permission-modes-capability'
import { structuredAgentSessionHostSupport } from './mobile-structured-agent-session-host-support'
describe('structuredAgentSessionHostSupport', () => {
it('reads each structured-session feature from the host capability list', () => {
const none = {
coherentModelCatalog: false,
promptCancel: false,
questionAnswers: false,
queuedMessages: false,
queuedCommands: false,
queuedMessageEdit: false,
queuedMessageImageEdit: false,
quietRepeatedStop: false,
statusFeed: false,
sendOptions: false,
conversationStop: false,
permissionModes: false,
permissionOwn: false,
itemDetail: false,
phoneAttachments: false
}
expect(structuredAgentSessionHostSupport([])).toEqual(none)
expect(
structuredAgentSessionHostSupport([AGENT_SESSION_COHERENT_MODEL_CATALOG_CAPABILITY])
).toEqual({ ...none, coherentModelCatalog: true })
expect(
structuredAgentSessionHostSupport([AGENT_SESSION_QUESTION_ANSWERS_RUNTIME_CAPABILITY])
).toEqual({ ...none, questionAnswers: true })
expect(
structuredAgentSessionHostSupport([AGENT_SESSION_PROMPT_CANCEL_RUNTIME_CAPABILITY])
).toEqual({ ...none, promptCancel: true })
expect(
structuredAgentSessionHostSupport([AGENT_SESSION_QUEUED_MESSAGES_RUNTIME_CAPABILITY])
).toEqual({ ...none, queuedMessages: true })
expect(
structuredAgentSessionHostSupport([AGENT_SESSION_QUEUED_COMMANDS_RUNTIME_CAPABILITY])
).toEqual({ ...none, queuedCommands: true })
expect(
structuredAgentSessionHostSupport([AGENT_SESSION_QUEUED_MESSAGE_EDIT_RUNTIME_CAPABILITY])
).toEqual({ ...none, queuedMessageEdit: true })
expect(
structuredAgentSessionHostSupport([
AGENT_SESSION_QUEUED_MESSAGE_IMAGE_EDIT_RUNTIME_CAPABILITY
])
).toEqual({ ...none, queuedMessageImageEdit: true })
expect(
structuredAgentSessionHostSupport([AGENT_SESSION_REPEATED_STOP_RUNTIME_CAPABILITY])
).toEqual({ ...none, quietRepeatedStop: true })
expect(
structuredAgentSessionHostSupport([AGENT_SESSION_STATUS_FEED_RUNTIME_CAPABILITY])
).toEqual({ ...none, statusFeed: true })
expect(
structuredAgentSessionHostSupport([AGENT_SESSION_SEND_OPTIONS_RUNTIME_CAPABILITY])
).toEqual({ ...none, sendOptions: true })
expect(
structuredAgentSessionHostSupport([AGENT_SESSION_CONVERSATION_STOP_RUNTIME_CAPABILITY])
).toEqual({ ...none, conversationStop: true })
expect(
structuredAgentSessionHostSupport([AGENT_SESSION_PERMISSION_MODES_RUNTIME_CAPABILITY])
).toEqual({ ...none, permissionModes: true })
expect(
structuredAgentSessionHostSupport([AGENT_SESSION_PHONE_ATTACHMENTS_RUNTIME_CAPABILITY])
).toEqual({ ...none, phoneAttachments: true })
expect(
structuredAgentSessionHostSupport([AGENT_SESSION_ITEM_DETAIL_RUNTIME_CAPABILITY])
).toEqual({ ...none, itemDetail: true })
// A host with the store but from before phones could upload into it offers them nothing.
expect(
structuredAgentSessionHostSupport([AGENT_SESSION_ATTACHMENTS_RUNTIME_CAPABILITY])
).toEqual(none)
})
})
@@ -294,18 +294,6 @@ describe('useMobileNativeChatImageAttachments', () => {
expect(hook!.attachments).toEqual([])
})
it('delegates straight to baseSend when there are no attachments', async () => {
const client = makeClient([])
const baseSend = vi.fn().mockResolvedValue('accepted')
mount(baseArgs({ client: client as unknown as RpcClient, baseSend }))
await act(async () => {
await hook!.sendNativeChat('just text')
})
expect(baseSend).toHaveBeenCalledWith(...textSend('just text'))
expect(client.calls).toHaveLength(0)
})
it('keeps the chips and does not submit when the image paste is rejected', async () => {
pick.mockResolvedValue([{ base64: 'AAAA', uri: 'file:///a.jpg' }])
const client = makeClient([
@@ -11,7 +11,6 @@ const clearDraftForSend = vi.fn()
const restoreRejectedDraft = vi.fn()
const holdUnconfirmedSend = vi.fn()
// Mutable resolution/transcript doubles let launch-draft cases reuse the send-seam harness.
const viewMode = { isTabChatView: (_tabId: string) => true }
const sessionState = { messages: [] as unknown[], status: 'ready', transcriptLoading: false }
const structuredSendWithOutcome = vi.fn()
@@ -67,7 +66,6 @@ const structuredSessionState = {
loadingEarlier: false,
loadEarlier: vi.fn()
}
const draftsArgs: Record<string, unknown>[] = []
const promptsState = {
permission: null as unknown,
question: null as unknown,
@@ -107,8 +105,7 @@ vi.mock('./use-mobile-structured-agent-session', () => ({
})
}))
vi.mock('./use-mobile-native-chat-drafts', () => ({
useMobileNativeChatDrafts: (args: Record<string, unknown>) => {
draftsArgs.push(args)
useMobileNativeChatDrafts: () => {
return {
composerText: '',
setComposerText: vi.fn(),
@@ -245,10 +242,6 @@ describe('useMobileNativeChatController handleNativeChatSend', () => {
controller = null
})
it('leaves structured prompt cancellation unavailable on the legacy bridge lane', () => {
expect(controller?.handleNativeChatCancelPrompt).toBeUndefined()
})
it('clears an orphaned image paste before a question-card answer (#10228)', async () => {
markMobileNativeChatInputStale('term-1')
clientStub.sendRequest.mockResolvedValue({
@@ -317,19 +310,6 @@ describe('useMobileNativeChatController handleNativeChatSend', () => {
expect(onSendResolved).not.toHaveBeenCalled()
})
it('threads the optimistic-echo image URIs into acceptSend on an accepted send', async () => {
sendWithOutcome.mockResolvedValue('accepted')
let accepted = false
await act(async () => {
accepted = await controller!.handleNativeChatSend('look', ['file:///a.jpg'])
})
expect(accepted).toBe(true)
expect(acceptSend).toHaveBeenCalledWith(ORIGIN, 'look', ['file:///a.jpg'])
expect(controller!.nativeChatAcceptsImages).toBe(true)
expect(clearDraftForSend).toHaveBeenCalledWith(ORIGIN, 'look')
expect(restoreRejectedDraft).not.toHaveBeenCalled()
})
it('routes structured agent-session sends away from terminal/nativeChat transports', async () => {
await act(async () => {
renderer?.update(createElement(Harness, structuredTab('codex')))
@@ -511,97 +491,6 @@ describe('useMobileNativeChatController handleNativeChatSend', () => {
})
})
describe('useMobileNativeChatController launch-draft wiring', () => {
let renderer: ReactTestRenderer | null = null
const clientStub = { sendRequest: vi.fn() }
const chatTab = {
type: 'terminal',
id: 'tab-1',
title: 'Claude',
terminal: 'term-1',
launchAgent: 'claude',
launchDraft: 'https://github.com/o/r/issues/12',
launchDraftCreatedAt: 7,
isActive: true
}
function Harness({ tab }: { tab: unknown }): null {
useMobileNativeChatController({
client: clientStub as unknown as RpcClient,
connState: 'connected',
hostId: 'h',
worktreeId: 'w',
activeSessionTab: tab as never,
activeSessionTabId: 'tab-1',
activeHandleRef: { current: 'term-1' },
deviceTokenRef: { current: null },
nativeChatTranscriptIsLocalReadable: true,
nativeChatInputLeaseReady: true,
onSendError: vi.fn(),
onSendResolved: vi.fn()
})
return null
}
function render(tab: unknown): void {
act(() => {
renderer = create(createElement(Harness, { tab }))
})
}
beforeEach(() => {
draftsArgs.length = 0
viewMode.isTabChatView = () => true
sessionState.messages = []
sessionState.status = 'ready'
sessionState.transcriptLoading = false
})
afterEach(() => {
act(() => renderer?.unmount())
renderer = null
})
it('forwards the tab launch draft and chat-active flag for a chat-resolved tab', () => {
render(chatTab)
expect(draftsArgs.at(-1)).toMatchObject({
tabId: 'tab-1',
launchDraft: 'https://github.com/o/r/issues/12',
launchDraftCreatedAt: 7,
chatActive: true,
transcriptLoading: false
})
})
it('forwards the raw draft with chatActive false when the tab shows the terminal', () => {
// Keep off-chat distinct from host retraction so toggling cannot permanently decline launch prefill.
viewMode.isTabChatView = () => false
render(chatTab)
expect(draftsArgs.at(-1)).toMatchObject({
launchDraft: 'https://github.com/o/r/issues/12',
chatActive: false
})
})
it('forwards the session hook’s transcriptLoading, not its status', () => {
// Working status masks loading; only read-phase state establishes transcript trust.
sessionState.status = 'working'
sessionState.transcriptLoading = true
render(chatTab)
expect(draftsArgs.at(-1)).toMatchObject({ transcriptLoading: true })
})
it('forwards a null draft for a tab that publishes none', () => {
render({ ...chatTab, launchDraft: undefined })
expect(draftsArgs.at(-1)).toMatchObject({ launchDraft: null, chatActive: true })
})
})
describe('useMobileNativeChatController ask dismissal across a transcript reload', () => {
let renderer: ReactTestRenderer | null = null
let controller: MobileNativeChatController | null = null
@@ -107,14 +107,6 @@ describe('useMobileNativeChatMessageSend', () => {
api = null
})
it('sizes the pre-clear to every line of a parked launch draft', async () => {
mount(() => ({ text: DRAFT, createdAt: 1 }))
await act(async () => {
await api!.send('hello')
})
expect(clearArgs().clearInput).toBe(buildAgentTuiClearInputForText(DRAFT))
})
it('issues the burst as its OWN write, before the body', async () => {
mount(() => ({ text: DRAFT, createdAt: 1 }))
await act(async () => {
@@ -204,15 +196,6 @@ describe('useMobileNativeChatMessageSend', () => {
expect(sentArgs().resolvedLaunchDraft).toBeUndefined()
})
it('creates an optimistic echo for an ordinary chat send', async () => {
mount(() => null)
await act(async () => {
await api!.send('hello')
})
expect(acceptSend).toHaveBeenCalledTimes(1)
expect(onCommandSend).not.toHaveBeenCalled()
})
// TUI commands never echo as user turns; optimistic bubbles would remain queued forever.
it('never creates an optimistic echo for a catalog command send', async () => {
mount(() => null)
@@ -57,22 +57,6 @@ describe('useMobileNativeChatSessionOptions', () => {
api = null
})
it('serves the shared catalog snapshot for the agent', () => {
mount()
expect(api!.snapshot[0]).toMatchObject({ id: 'model', category: 'model' })
expect(api!.snapshot[0]!.kind).toMatchObject({ type: 'select' })
})
it('returns an empty snapshot for agents without a catalog', () => {
mount({ agent: 'amp' })
expect(api!.snapshot).toEqual([])
})
it('does not expose catalog-backed agents outside the Claude and Codex scope', () => {
mount({ agent: 'gemini' })
expect(api!.snapshot).toEqual([])
})
it('shows an OMP session under the model its hook reported', () => {
mount({ agent: 'omp' })
expect(api!.snapshot).toEqual([])
@@ -3,7 +3,7 @@ import { act, create, type ReactTestRenderer } from 'react-test-renderer'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import type { RpcClient } from '../transport/rpc-client'
import { markRpcDeliveryUnknown } from '../transport/rpc-delivery-ambiguity'
import { MOBILE_NATIVE_CHAT_SEND_TIMEOUT_MS } from './native-chat/mobile-native-chat-send'
import { useMobileNativeChatStop } from './use-mobile-native-chat-stop'
const reportWorkerTerminalUserInput = vi.fn()
@@ -157,21 +157,6 @@ describe('useMobileNativeChatStop', () => {
expect(onSendError).not.toHaveBeenCalled()
})
it('bounds the Escape on a reconnect wait instead of parking forever', async () => {
await render(true, 'stream-1')
act(() => stop?.())
expect(sendRequest).toHaveBeenCalledWith(
'terminal.send',
expect.anything(),
expect.objectContaining({
timeoutMs: MOBILE_NATIVE_CHAT_SEND_TIMEOUT_MS,
budgetSpansConnect: true
})
)
})
it('suppresses an older Stop verdict after a newer Stop succeeds', async () => {
let rejectFirst!: (error: Error) => void
const first = new Promise((_, reject) => {
@@ -534,21 +534,6 @@ describe('mobile structured send actions', () => {
expect(new Set(sentIds()).size).toBe(2)
})
it('never reads an old phone journal, even when storage is full or unreadable', async () => {
asyncStorage.getItem.mockRejectedValue(new Error('unreadable old journal'))
asyncStorage.setItem.mockRejectedValue(new Error('disk full'))
sendRequest.mockImplementation(async (method) =>
method === 'agentSession.send' ? sendResult('accepted') : ok({ models: [], current: {} })
)
await mountSession()
await act(async () => {
expect(await hook!.sendWithOutcome('new action')).toBe('accepted')
})
expect(asyncStorage.getItem).not.toHaveBeenCalled()
expect(asyncStorage.setItem).not.toHaveBeenCalled()
expect(onSendError).not.toHaveBeenCalled()
})
it('sends nothing once the action budget expires', async () => {
await mountSession()
await act(async () => {
@@ -1,18 +1,6 @@
import { readdirSync } from 'node:fs'
import { resolve } from 'node:path'
import { describe, expect, it } from 'vitest'
import { C5_PAGE_CLOSURE } from './c5-page-closure'
import {
pageClosureDrift,
pageClosureExclusions,
pageClosureTotals,
type PageClosureObservation
} from './page-closure'
import { C1_PAGE_CLOSURE } from './c1-page-closure'
import { BRIDGED_PARITY_EXCLUSIONS } from './divergence-classes'
import { readGolden } from '../rpc-recording/golden-recording'
const GOLDENS = resolve(import.meta.dirname, '../../../rpc-foundation/goldens')
import { pageClosureDrift, type PageClosureObservation } from './page-closure'
function asPinned(): Map<string, PageClosureObservation> {
const run = new Map<string, PageClosureObservation>()
@@ -24,89 +12,7 @@ function asPinned(): Map<string, PageClosureObservation> {
return run
}
describe('the C5 page closure', () => {
it('assigns each pinned golden to one closure family', () => {
const goldens = Object.values(C5_PAGE_CLOSURE).flatMap((family) => Object.keys(family))
expect(new Set(goldens).size).toBe(goldens.length)
})
// Assert class totals beside per-id pins; a self-consistent misderived table can satisfy each pin while losing wrapped entries.
it('pins how many goldens land in each class, which a per-id walk cannot see move', () => {
expect(pageClosureTotals(C5_PAGE_CLOSURE)).toEqual({
identical: 61,
'result-absent-settlement': 49,
'params-undefined': 7,
'result-absent-stream-release': 3,
'write-ordinal': 7
})
})
it("inherits C1's families whole, with the verdicts C1 committed", () => {
// C5 inlines its pins, so compare imported C1 directly; composed spread tables need source parsing to detect overwritten inherited entries.
for (const [family, pinned] of Object.entries(C1_PAGE_CLOSURE)) {
expect(C5_PAGE_CLOSURE[family], family).toEqual(pinned)
}
expect(Object.keys(C1_PAGE_CLOSURE).length).toBe(20)
})
it('adds five families and nothing else, all of them AI Vault', () => {
const added = Object.keys(C5_PAGE_CLOSURE).filter((family) => !(family in C1_PAGE_CLOSURE))
expect(added.sort()).toEqual([
'aiVault.history',
'aiVault.history-screen',
'aiVault.resume-launch',
'aiVault.resume-preparation',
'settings.resume-metadata'
])
})
it('keeps byte-identical controls outside History and strict ordinal controls within it', () => {
const added = Object.entries(C5_PAGE_CLOSURE).filter(([family]) => !(family in C1_PAGE_CLOSURE))
const wholly = added
.filter(([, pins]) => !Object.values(pins).includes('identical'))
.map(([family]) => family)
expect(wholly.sort()).toEqual(['aiVault.history', 'aiVault.history-screen'])
for (const family of wholly) {
expect(Object.values(C5_PAGE_CLOSURE[family] ?? {})).toContain('write-ordinal')
}
})
it('pins goldens that exist, in the family the corpus records them under', () => {
for (const [family, goldens] of Object.entries(C5_PAGE_CLOSURE)) {
for (const id of Object.keys(goldens)) {
// Use the corpus decoder instead of inventing another golden-shape check.
const recorded = readGolden(GOLDENS, id)
expect({ id, family: recorded.family }).toEqual({ id, family })
}
}
})
it('claims no golden the corpus does not have', () => {
const corpus = new Set(
readdirSync(GOLDENS)
.filter((name) => name.endsWith('.json'))
.map((name) => name.replace(/\.json$/, ''))
)
const missing = Object.values(C5_PAGE_CLOSURE)
.flatMap((family) => Object.keys(family))
.filter((id) => !corpus.has(id))
expect(missing).toEqual([])
})
it('excludes a closure golden only into a class that has a reason', () => {
const exclusions = pageClosureExclusions(C5_PAGE_CLOSURE)
expect(exclusions.length).toBe(66)
expect(exclusions.filter(([, name]) => BRIDGED_PARITY_EXCLUSIONS[name] === undefined)).toEqual(
[]
)
})
})
describe('reading a run against the C5 pin', () => {
it('says nothing when the run is the pin', () => {
expect(pageClosureDrift(C5_PAGE_CLOSURE, asPinned())).toEqual([])
})
it('names a closure golden that changed verdict', () => {
const run = asPinned()
const found = [...run].find(([, seen]) => seen.verdict !== 'params-undefined')
@@ -1,14 +1,10 @@
import { readdirSync } from 'node:fs'
import { resolve } from 'node:path'
import { describe, expect, it } from 'vitest'
import {
bridgedParityMembershipDrift,
bridgedParityTallyDrift,
classifyBridgedParity,
BRIDGED_PARITY_BASELINE,
BRIDGED_PARITY_EXCLUSIONS,
BRIDGED_PARITY_MEMBERS,
BRIDGED_PARITY_NAMEABLE,
type BridgedParityClass,
type BridgedParityEvidence,
type BridgedParityTally
@@ -142,35 +138,6 @@ describe('what the pin still admits', () => {
(name): name is BridgedParityClass => name !== 'identical'
)
it('gives every class it still counts a reason, and every closed one none', () => {
const reasoned = classes.filter((name) => BRIDGED_PARITY_EXCLUSIONS[name] !== undefined)
const counted = classes.filter((name) => BRIDGED_PARITY_BASELINE[name] > 0)
expect([...reasoned].sort()).toEqual([...counted].sort())
})
it('accounts for every golden in the corpus, once each', () => {
// Pin the class sum to corpus size so a partial run cannot satisfy per-class expectations.
const goldens = readdirSync(resolve(import.meta.dirname, '../../../rpc-foundation/goldens'))
const counted = Object.values(BRIDGED_PARITY_BASELINE).reduce((sum, count) => sum + count, 0)
expect({ counted }).toEqual({
counted: goldens.filter((name) => name.endsWith('.json')).length
})
})
it('names the goldens in a class small enough to name, and as many as it counts', () => {
const nameable = classes.filter(
(name) =>
BRIDGED_PARITY_BASELINE[name] > 0 &&
BRIDGED_PARITY_BASELINE[name] <= BRIDGED_PARITY_NAMEABLE
)
expect(nameable.length).toBeGreaterThan(0)
for (const name of nameable) {
const pinned = BRIDGED_PARITY_MEMBERS[name] ?? []
expect({ [name]: pinned.length }).toEqual({ [name]: BRIDGED_PARITY_BASELINE[name] })
expect({ [name]: new Set(pinned).size }).toEqual({ [name]: pinned.length })
}
})
function asPinned(): Map<string, readonly string[]> {
const run = new Map<string, readonly string[]>()
for (const name of classes) {
@@ -216,10 +183,6 @@ describe('what the pin still admits', () => {
return { identical: BRIDGED_PARITY_BASELINE.identical, counts }
}
it('says nothing about the run the numbers were taken from', () => {
expect(bridgedParityTallyDrift(asCounted())).toEqual([])
})
it('goes red on a golden that stopped diverging, which every other check lets through', () => {
// Excluded-to-identical moves leave broad parity gates satisfied; exact tallies must detect that direction too.
const tally = asCounted()
@@ -250,9 +213,4 @@ describe('what the pin still admits', () => {
`identical: pinned ${tally.identical}, ran ${tally.identical - 1}`
])
})
it('leaves nothing for the reader to close: the `_meta` class is zero', () => {
expect(BRIDGED_PARITY_BASELINE['reply-meta-required']).toBe(0)
expect(BRIDGED_PARITY_EXCLUSIONS['reply-meta-required']).toBeUndefined()
})
})
@@ -366,10 +366,6 @@ describe('the fields that diverged', () => {
})
).toEqual(['checkpoints'])
})
it('is empty when the two agree', () => {
expect(divergingFields(recording({}), recording({}))).toEqual([])
})
})
describe('reading the scenario', () => {
@@ -1,32 +1,7 @@
import { describe, expect, it } from 'vitest'
import { parseClaudeUsageRecord } from './transcript-record-parser'
function assistantLine(overrides: Record<string, unknown> = {}): string {
return JSON.stringify({
type: 'assistant',
sessionId: 'session-1',
timestamp: '2026-01-01T00:00:00.000Z',
message: { usage: { input_tokens: 3, output_tokens: 5 } },
...overrides
})
}
describe('assistant-record prefilter', () => {
it('still parses an ordinary assistant record', () => {
expect(parseClaudeUsageRecord(assistantLine())?.inputTokens).toBe(3)
})
it('rejects a user record that never mentions assistant', () => {
const userLine = JSON.stringify({
type: 'user',
sessionId: 'session-1',
timestamp: '2026-01-01T00:00:00.000Z',
message: { content: 'x'.repeat(200) }
})
expect(parseClaudeUsageRecord(userLine)).toBeNull()
})
it('rejects a non-assistant record that happens to contain the word assistant', () => {
const userLine = JSON.stringify({
type: 'user',
@@ -1,26 +0,0 @@
import { describe, expect, it } from 'vitest'
import { computerProviderUnavailableMessage } from './computer-provider-unavailable-message'
describe('computerProviderUnavailableMessage', () => {
it('gives macOS developers the helper build and restart step', () => {
expect(computerProviderUnavailableMessage('darwin')).toContain(
'run pnpm build:computer-macos and restart Orca from this worktree'
)
expect(computerProviderUnavailableMessage('darwin')).toContain(
'Orca Computer Use.app was not found or this macOS version is unsupported'
)
})
it('names the runtime file a Linux or Windows install is missing', () => {
expect(computerProviderUnavailableMessage('linux')).toContain('computer-use-linux/runtime.py')
expect(computerProviderUnavailableMessage('win32')).toContain(
'computer-use-windows/runtime.ps1'
)
})
it('keeps unsupported platforms explicit', () => {
expect(computerProviderUnavailableMessage('freebsd')).toBe(
'computer-use has no native provider for freebsd'
)
})
})
@@ -2,10 +2,6 @@ import { describe, expect, it } from 'vitest'
import { runCoalescedDaemonRestart, settledDaemonRestart } from './daemon-restart-state'
describe('settledDaemonRestart', () => {
it('is undefined with no restart running', () => {
expect(settledDaemonRestart()).toBeUndefined()
})
it('holds until the running restart settles, whether it succeeds or fails', async () => {
for (const outcome of ['resolve', 'reject'] as const) {
let finish!: () => void
-44
View File
@@ -1,44 +0,0 @@
import { afterEach, describe, expect, it, vi } from 'vitest'
const { setBadgeMock } = vi.hoisted(() => ({
setBadgeMock: vi.fn()
}))
vi.mock('electron', () => ({
app: {
dock: {
setBadge: setBadgeMock
}
}
}))
describe('unread Dock badge', () => {
const originalPlatform = Object.getOwnPropertyDescriptor(process, 'platform')
afterEach(() => {
if (originalPlatform) {
Object.defineProperty(process, 'platform', originalPlatform)
}
setBadgeMock.mockReset()
vi.resetModules()
})
it('clears the native badge when unread count is zero', async () => {
Object.defineProperty(process, 'platform', { configurable: true, value: 'darwin' })
const { setUnreadDockBadgeCount } = await import('./unread-badge')
setUnreadDockBadgeCount(5)
expect(setBadgeMock).toHaveBeenLastCalledWith('5')
setUnreadDockBadgeCount(0)
expect(setBadgeMock).toHaveBeenLastCalledWith('')
})
it('caps unread counts', async () => {
Object.defineProperty(process, 'platform', { configurable: true, value: 'darwin' })
const { setUnreadDockBadgeCount } = await import('./unread-badge')
setUnreadDockBadgeCount(104)
expect(setBadgeMock).toHaveBeenLastCalledWith('99+')
})
})
@@ -17,10 +17,6 @@ describe('gitCommandTimeoutMs', () => {
expect(gitCommandTimeoutMs(['status'], 7)).toBe(7)
expect(gitCommandTimeoutMs(['fetch'], 7)).toBe(7)
})
it('provides a deterministic read-timeout seam', () => {
expect(gitCommandTimeoutMs(['status'], undefined, 25)).toBe(25)
})
})
describe('GitCommandTimeoutError', () => {
@@ -1,26 +0,0 @@
import { join } from 'node:path'
import { describe, expect, it, vi } from 'vitest'
import { resolveHangWatchdogWorkerPath } from './hang-watchdog-worker-path'
describe('resolveHangWatchdogWorkerPath', () => {
it('resolves packaged workers inside app.asar', () => {
const appPath = join('/apps', 'orca', 'app.asar')
expect(resolveHangWatchdogWorkerPath(appPath, true)).toBe(
join(appPath, 'out', 'main', 'main-thread-hang-watchdog-entry.js')
)
})
it('uses an adjacent entry when the dev app path is out/main', () => {
const appPath = join('/repo', 'out', 'main')
const adjacent = join(appPath, 'main-thread-hang-watchdog-entry.js')
const pathExists = vi.fn((candidate: string) => candidate === adjacent)
expect(resolveHangWatchdogWorkerPath(appPath, false, pathExists)).toBe(adjacent)
})
it('resolves through out/main from a dev project root', () => {
const appPath = join('/repo', 'orca')
expect(resolveHangWatchdogWorkerPath(appPath, false, () => false)).toBe(
join(appPath, 'out', 'main', 'main-thread-hang-watchdog-entry.js')
)
})
})
@@ -2,24 +2,6 @@ import { describe, expect, it } from 'vitest'
import { eventSchemas } from '../../shared/telemetry-events'
describe('main_thread_hang_detected telemetry schema', () => {
it('accepts an unresolved hang', () => {
expect(
eventSchemas.main_thread_hang_detected.safeParse({
unresponsive_ms: 50_000,
self_recovered: false
}).success
).toBe(true)
})
it('accepts a self-recovered stall', () => {
expect(
eventSchemas.main_thread_hang_detected.safeParse({
unresponsive_ms: 61_000,
self_recovered: true
}).success
).toBe(true)
})
// Strict schema excludes paths, PIDs and window titles from telemetry.
it('rejects unknown fields and non-integer durations', () => {
expect(
@@ -1,30 +0,0 @@
import { readdirSync, readFileSync } from 'node:fs'
import { join, relative } from 'node:path'
import { describe, expect, it } from 'vitest'
const MAIN_ROOT = join(__dirname, '..')
// Route keychain access through the run-wide guard so withheld keychains cannot block the main thread.
const ALLOWED = new Set(['host/safe-storage-availability.ts', 'host/electron-secret-store.ts'])
function listSources(dir: string): string[] {
return readdirSync(dir, { withFileTypes: true }).flatMap((entry) => {
const path = join(dir, entry.name)
if (entry.isDirectory()) {
return listSources(path)
}
return entry.name.endsWith('.ts') && !entry.name.endsWith('.test.ts') ? [path] : []
})
}
describe('electron.safeStorage importers', () => {
it('stay behind the keychain withhold', () => {
const importers = listSources(MAIN_ROOT)
.filter((path) =>
/import\s*\{[^}]*\bsafeStorage\b[^}]*\}\s*from\s*'electron'/.test(
readFileSync(path, 'utf8')
)
)
.map((path) => relative(MAIN_ROOT, path).split('\\').join('/'))
expect(importers.filter((path) => !ALLOWED.has(path))).toEqual([])
})
})
@@ -1,24 +0,0 @@
import { describe, expect, it, vi } from 'vitest'
import emojiShortcodes from 'emojibase-data/en/shortcodes/emojibase.json'
import { requireEmojiShortcodeDataset } from './deferred-emoji-shortcode-dataset'
// Keep main imports outside shared tsconfig projects; the lazy catalog test enforces that boundary.
describe('deferred emoji shortcode dataset', () => {
it('loads the main-side dataset synchronously into an identical catalog', async () => {
vi.resetModules()
const eager = await import('../../shared/emoji-shortcode-catalog.js')
eager.setEmojiShortcodeDatasetLoader(() => emojiShortcodes)
const eagerEntries = eager.getStandardEmojiShortcodeEntries()
const eagerTransform = eager.replaceKnownEmojiWithShortcodes('ship \u{1F389} \u{1F44D}')
vi.resetModules()
const deferred = await import('../../shared/emoji-shortcode-catalog.js')
deferred.setEmojiShortcodeDatasetLoader(requireEmojiShortcodeDataset)
// Immediate use must work without awaiting an async import.
expect(deferred.getStandardEmojiShortcodeEntries()).toEqual(eagerEntries)
expect(deferred.replaceKnownEmojiWithShortcodes('ship \u{1F389} \u{1F44D}')).toBe(
eagerTransform
)
})
})
-38
View File
@@ -1,38 +0,0 @@
import { describe, expect, it } from 'vitest'
import {
getTuiAgentDetectionProbeCommands,
KNOWN_TUI_AGENT_DETECTION_COMMANDS,
resolveDetectedTuiAgentIds
} from '../../shared/tui-agent-detection-commands'
describe('Freebuff detection', () => {
it.each(['darwin', 'linux', 'win32', 'wsl'] as const)(
'detects Freebuff independently of Codebuff on %s',
(runtime) => {
expect(
getTuiAgentDetectionProbeCommands(KNOWN_TUI_AGENT_DETECTION_COMMANDS, runtime)
).toContain('freebuff')
expect(
resolveDetectedTuiAgentIds(
KNOWN_TUI_AGENT_DETECTION_COMMANDS,
new Set(['freebuff']),
runtime
)
).toEqual(['freebuff'])
expect(
resolveDetectedTuiAgentIds(
KNOWN_TUI_AGENT_DETECTION_COMMANDS,
new Set(['codebuff']),
runtime
)
).toEqual(['codebuff'])
expect(
resolveDetectedTuiAgentIds(
KNOWN_TUI_AGENT_DETECTION_COMMANDS,
new Set(['freebuff', 'codebuff']),
runtime
)
).toEqual(expect.arrayContaining(['freebuff', 'codebuff']))
}
)
})
@@ -1,36 +0,0 @@
import { afterEach, expect, it } from 'vitest'
import { setActiveSink } from '../../observability/tracer'
import { sharedControlEnvironmentPolicy } from './runtime-environment-shared-control-policy'
afterEach(() => setActiveSink(null))
it('traces each control socket close for the support bundle', () => {
const records: unknown[] = []
setActiveSink({ push: (record) => records.push(record), flush() {}, close() {} })
sharedControlEnvironmentPolicy('env-1').onSocketClosed?.({
cause: 'keepalive_timeout',
errorCode: 'remote_runtime_unavailable',
closeCode: null,
state: 'ready',
lastRttMs: 320,
socketUptimeMs: 60_000,
readyUptimeMs: 59_000,
reconnectAttempt: 0,
pendingRequestCount: 1,
subscriptionCount: 4
})
expect(records).toEqual([
expect.objectContaining({
type: 'effect-span',
name: 'remote-runtime.control-socket-close',
attributes: expect.objectContaining({
environmentId: 'env-1',
cause: 'keepalive_timeout',
lastRttMs: 320,
socketUptimeMs: 60_000
})
})
])
})
@@ -1,29 +0,0 @@
import { describe, expect, it, vi } from 'vitest'
const handlers = vi.hoisted(() => new Map<string, () => unknown>())
const retrySharedControl = vi.hoisted(() => vi.fn())
const retrySsh = vi.hoisted(() => vi.fn())
vi.mock('electron', () => ({
ipcMain: {
removeHandler: (channel: string) => handlers.delete(channel),
handle: (channel: string, handler: () => unknown) => handlers.set(channel, handler)
}
}))
vi.mock('./runtime-environment-request-connections', () => ({
retryRemoteRuntimeSharedControlConnectionsNow: retrySharedControl
}))
vi.mock('../ssh/ssh-host-sleep-reconnect', () => ({
retrySshConnectionsAfterNetworkReturn: retrySsh
}))
import { registerRuntimeEnvironmentRecoveryHandler } from './runtime-environment-recovery-handler'
describe('runtimeEnvironments:retryConnectionsNow', () => {
it('retries SSH transports as well as shared control when the window reports the network back', () => {
registerRuntimeEnvironmentRecoveryHandler()
handlers.get('runtimeEnvironments:retryConnectionsNow')?.()
expect(retrySharedControl).toHaveBeenCalledTimes(1)
expect(retrySsh).toHaveBeenCalledTimes(1)
})
})
@@ -1,6 +1,6 @@
import { describe, expect, it, vi } from 'vitest'
import type { IpcMainInvokeEvent } from 'electron'
import { sendBundleInstallProgress, sendSkillInstallProgress } from './skill-install-progress-ipc'
import { sendSkillInstallProgress } from './skill-install-progress-ipc'
function event(destroyed = false): IpcMainInvokeEvent {
return {
@@ -9,23 +9,6 @@ function event(destroyed = false): IpcMainInvokeEvent {
}
describe('skill install progress IPC', () => {
it('projects destination-owned bundle progress without paths or grants', () => {
const target = event()
sendBundleInstallProgress(target, {
operationId: 'operation_1',
skillId: 'alpha',
skillName: 'alpha',
skillIndex: 2,
skillCount: 30
})
expect(target.sender.send).toHaveBeenCalledWith('skills:installProgress', {
operationId: 'operation_1',
phase: 'installing',
currentSkill: { id: 'alpha', name: 'alpha', index: 2, total: 30 }
})
})
it('does not publish after the invoking renderer is destroyed', () => {
const target = event(true)
sendSkillInstallProgress(target, { operationId: 'operation_1', phase: 'authorizing' })
@@ -1,31 +0,0 @@
import { describe, expect, it } from 'vitest'
import { getWorktreeWatcherRemoval, setWorktreeWatcherRemoval } from './worktree-watcher-removal'
/** The renderer-less default stays inert; installed ports route method calls. */
describe('WorktreeWatcherRemoval port', () => {
const METHODS = ['closeLocal', 'restoreLocal', 'forgetLocal'] as const
it('defaults to inert so a renderer-less host is honest, not broken', async () => {
setWorktreeWatcherRemoval(null)
const inert = getWorktreeWatcherRemoval()
for (const method of METHODS) {
await expect(
Promise.resolve(inert[method]('repo::/tmp/w', '/tmp/w' as never))
).resolves.not.toThrow()
}
})
it('routes every method to the installed binding', async () => {
const calls: string[] = []
setWorktreeWatcherRemoval({
closeLocal: async () => void calls.push('closeLocal'),
restoreLocal: async () => void calls.push('restoreLocal'),
forgetLocal: () => void calls.push('forgetLocal')
})
for (const method of METHODS) {
await getWorktreeWatcherRemoval()[method]('conn', '/tmp/w' as never)
}
expect(calls).toEqual([...METHODS])
setWorktreeWatcherRemoval(null)
})
})
@@ -1,11 +1,6 @@
import { expect, it } from 'vitest'
import { jsonBytes, shortenJsonStringsToFit } from './json-string-shortening'
it('returns the value itself when it already fits', () => {
const value = { text: 'short' }
expect(shortenJsonStringsToFit(value, 1_000)).toBe(value)
})
it('cuts long strings to fit and marks them, never splitting a surrogate pair', () => {
const value = { text: '😀'.repeat(2_000), other: 'x'.repeat(4_000) }
const fitted = shortenJsonStringsToFit(value, 2_000)
@@ -1,37 +0,0 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
const { refreshV1, refreshV2 } = vi.hoisted(() => ({
refreshV1: vi.fn<() => void>(),
refreshV2: vi.fn<() => void>()
}))
vi.mock('./hook-service', () => ({
openCodeHookService: { refreshInstalledPlugins: refreshV1 },
openCode2HookService: { refreshInstalledPlugins: refreshV2 }
}))
import { refreshInstalledOpenCodeStatusPlugins } from './opencode-status-plugin-startup-refresh'
describe('refreshInstalledOpenCodeStatusPlugins', () => {
beforeEach(() => {
refreshV1.mockClear()
refreshV2.mockClear()
})
it('refreshes both variants by default', () => {
refreshInstalledOpenCodeStatusPlugins(null)
expect(refreshV1).toHaveBeenCalledTimes(1)
expect(refreshV2).toHaveBeenCalledTimes(1)
})
it('touches nothing while agent status hooks are off', () => {
refreshInstalledOpenCodeStatusPlugins({ agentStatusHooksEnabled: false })
expect(refreshV1).not.toHaveBeenCalled()
expect(refreshV2).not.toHaveBeenCalled()
})
it('skips a disabled variant only', () => {
refreshInstalledOpenCodeStatusPlugins({ disabledTuiAgents: ['opencode2'] })
expect(refreshV1).toHaveBeenCalledTimes(1)
expect(refreshV2).not.toHaveBeenCalled()
})
})
@@ -15,13 +15,4 @@ describe('resolveProviderChildEnv', () => {
expect(baseEnv).toEqual({ PATH: '/bin', AGENT_HOME: '/inherited', PARENT_AGENT: 'parent' })
expect(overlay.PARENT_AGENT).toBe('overlay')
})
it('copies the inherited env when the launch carries no env', () => {
const baseEnv = { PATH: '/bin' }
const childEnv = resolveProviderChildEnv({}, baseEnv)
expect(childEnv).toEqual(baseEnv)
expect(childEnv).not.toBe(baseEnv)
})
})
@@ -1,27 +1,7 @@
import { describe, expect, it, vi } from 'vitest'
import {
installWindowsPathRegistryChangeListener,
WINDOWS_SETTING_CHANGE_MESSAGE
} from './windows-path-registry-change'
import { installWindowsPathRegistryChangeListener } from './windows-path-registry-change'
describe('installWindowsPathRegistryChangeListener', () => {
it('invalidates the persisted PATH cache on a Windows settings change', () => {
const hooks = new Map<number, (wParam: Buffer, lParam: Buffer) => void>()
const invalidate = vi.fn()
installWindowsPathRegistryChangeListener(
{
hookWindowMessage: (message, callback) => {
hooks.set(message, callback)
}
},
{ invalidate, platform: 'win32' }
)
hooks.get(WINDOWS_SETTING_CHANGE_MESSAGE)?.(Buffer.alloc(0), Buffer.alloc(0))
expect(invalidate).toHaveBeenCalledOnce()
})
it('does not hook native messages outside Windows', () => {
const hookWindowMessage = vi.fn()
@@ -1,36 +0,0 @@
import { afterEach, describe, expect, it, vi } from 'vitest'
import { spawnProcess } from '@orca/process-host'
import { stopBundledRipgrep } from './bundled-ripgrep-stop'
const { signalTree, killChild } = vi.hoisted(() => ({ signalTree: vi.fn(), killChild: vi.fn() }))
vi.mock('@orca/process-host/process-tree-termination', () => ({
signalProcessTree: signalTree
}))
vi.mock('../../shared/ripgrep-process-availability', () => ({
killSpawnedRipgrepProcess: killChild
}))
afterEach(() => {
vi.restoreAllMocks()
vi.clearAllMocks()
})
describe('bundled search process termination', () => {
it('uses the existing bounded tree terminator for Windows WSL and coalesces duplicate stops', async () => {
const child = spawnProcess({
program: process.execPath,
args: ['-e', 'setInterval(() => {}, 1000)']
})
const exited = new Promise((resolve) => child.once('close', resolve))
try {
vi.spyOn(process, 'platform', 'get').mockReturnValue('win32')
signalTree.mockResolvedValue(true)
stopBundledRipgrep(child, true)
stopBundledRipgrep(child, true)
expect(signalTree).toHaveBeenCalledExactlyOnceWith(child)
expect(killChild).not.toHaveBeenCalled()
} finally {
child.kill()
await exited
}
})
})
@@ -1,24 +0,0 @@
import { describe, expect, it } from 'vitest'
import { ALL_RPC_METHODS } from '../rpc/methods'
import { MOBILE_RPC_METHOD_ALLOWLIST } from '../runtime-rpc/runtime-rpc-mobile-method-allowlist'
import { MOBILE_RPC_METHOD_ROUTES } from '../runtime-rpc/runtime-rpc-mobile-method-routing'
import { RELAYED_STREAM_CARRIERS } from './relayed-stream-carriers'
describe('relayed stream carrier census', () => {
const relayableStreams: string[] = ALL_RPC_METHODS.filter(
(method) =>
'stream' in method &&
MOBILE_RPC_METHOD_ALLOWLIST.has(method.name) &&
MOBILE_RPC_METHOD_ROUTES.get(method.name) === 'execution-host'
).map((method) => method.name)
it('classifies how every relayable stream names itself', () => {
expect(relayableStreams.filter((name) => !RELAYED_STREAM_CARRIERS.has(name))).toEqual([])
})
it('classifies nothing that is not a relayable stream', () => {
expect(
[...RELAYED_STREAM_CARRIERS.keys()].filter((name) => !relayableStreams.includes(name))
).toEqual([])
})
})
@@ -1,5 +1,5 @@
/** terminal-stream-id renumbers result/header ids; none carries no id (one phone screencast at a time).
* Refuse phone-binary-frames because the relay cannot forward their input; census tests cover new streams. */
* Refuse phone-binary-frames because the relay cannot forward their input. */
export type RelayedStreamCarrier = 'terminal-stream-id' | 'none' | 'phone-binary-frames'
export const RELAYED_STREAM_CARRIERS: ReadonlyMap<string, RelayedStreamCarrier> = new Map([
@@ -1,4 +1,4 @@
import { describe, expect, it, vi } from 'vitest'
import { describe, expect, it } from 'vitest'
import { computeTerminalTailWaitState } from './terminal-wait-tail-state'
describe('terminal tail wait state', () => {
@@ -19,19 +19,4 @@ describe('terminal tail wait state', () => {
expect(blocked.fromTail).toBe(true)
expect(blocked.signal?.reason).toBe('agent-update-prompt')
})
it('does not rebuild or repeatedly scan an ordinary saturated tail', () => {
const lines = Array.from({ length: 2000 }, () => 'x'.repeat(126))
const lastIndexOf = vi.spyOn(String.prototype, 'lastIndexOf')
try {
const state = computeTerminalTailWaitState(lines, '', '')
expect(state.signal).toBeNull()
expect(state.waitText).toBe('')
expect(lastIndexOf).not.toHaveBeenCalled()
} finally {
lastIndexOf.mockRestore()
}
})
})
@@ -1,39 +0,0 @@
import '../unused-default-rpc-methods.test-fixture'
import { describe, expect, it, vi } from 'vitest'
import { RpcDispatcher } from '../dispatcher'
import type { RpcRequest } from '../core'
import type { OrcaRuntimeService } from '../../orca-runtime'
import { REPO_METHODS } from './repo'
function makeRequest(method: string, params?: unknown): RpcRequest {
return { id: 'req-1', authToken: 'tok', method, params }
}
describe('repo badge color RPC updates', () => {
it('normalizes repo badge colors before runtime updates', async () => {
const runtime = {
getRuntimeId: () => 'test-runtime',
updateRepo: vi.fn().mockResolvedValue({
id: 'repo-1',
path: '/srv/repo',
badgeColor: '#abcdef'
})
} as unknown as OrcaRuntimeService
const dispatcher = new RpcDispatcher({ runtime, methods: REPO_METHODS })
const response = await dispatcher.dispatch(
makeRequest('repo.update', {
repo: 'repo-1',
updates: { badgeColor: ' ABCDEF ' }
})
)
expect(runtime.updateRepo).toHaveBeenCalledWith('repo-1', {
badgeColor: '#abcdef'
})
expect(response).toMatchObject({
ok: true,
result: { repo: { id: 'repo-1', badgeColor: '#abcdef' } }
})
})
})
@@ -1,17 +1,7 @@
import { describe, expect, it } from 'vitest'
import {
EXECUTION_HOST_METHODS,
MOBILE_RPC_METHOD_ROUTES
} from './runtime-rpc-mobile-method-routing'
import { PAIRED_DESKTOP_METHODS } from './runtime-rpc-mobile-paired-desktop-methods'
import { MOBILE_RPC_METHOD_ROUTES } from './runtime-rpc-mobile-method-routing'
describe('mobile RPC method routing census', () => {
it('tags every routed method exactly once', () => {
expect(PAIRED_DESKTOP_METHODS.length + EXECUTION_HOST_METHODS.length).toBe(
MOBILE_RPC_METHOD_ROUTES.size
)
})
it.each([
'jira.status',
'jira.listIssues',
@@ -1,32 +0,0 @@
import { expect, it } from 'vitest'
import { PI_RPC_RUNTIME_REGISTRATION } from '../pi/rpc-runtime-registration'
import {
STRUCTURED_AGENT_RUNTIME_METADATA,
structuredAgentRuntimeMetadata
} from './structured-agent-runtime-metadata'
import {
STRUCTURED_AGENT_RUNTIME_REGISTRATIONS,
structuredAgentRuntimeRegistration
} from './structured-agent-runtime-registrations'
it.each(
STRUCTURED_AGENT_RUNTIME_METADATA.map(
(metadata, index) => [metadata.definition.agent, metadata, index] as const
)
)(
'keeps the metadata and synchronous factory on the same %s registration',
(agent, metadata, index) => {
const registration = STRUCTURED_AGENT_RUNTIME_REGISTRATIONS[index]
expect(registration).toBe(metadata)
expect(structuredAgentRuntimeRegistration(agent)).toBe(registration)
expect(structuredAgentRuntimeMetadata(agent)).toBe(registration)
expect(typeof registration.createAdapter).toBe('function')
expect(structuredAgentRuntimeRegistration(agent)?.createAdapter).toBe(
registration.createAdapter
)
}
)
it('keeps the standalone Pi registration in the shared roster', () => {
expect(structuredAgentRuntimeRegistration('pi')).toBe(PI_RPC_RUNTIME_REGISTRATION)
})
@@ -1,6 +1,5 @@
import { describe, expect, it } from 'vitest'
import { parseContentRange, parseRetryAfterMs } from './speech-model-download-response'
import { isRetryableDownloadError } from '../network/transient-download-error'
describe('speech model download response contracts', () => {
it('accepts only internally consistent byte ranges', () => {
@@ -15,12 +14,4 @@ describe('speech model download response contracts', () => {
expect(parseRetryAfterMs(String(Number.MAX_SAFE_INTEGER))).toBeUndefined()
expect(parseRetryAfterMs('invalid')).toBeUndefined()
})
it('retries only allowlisted HTTP and network failures', () => {
expect(
isRetryableDownloadError(Object.assign(new Error('HTTP 503'), { httpStatusCode: 503 }))
).toBe(true)
expect(isRetryableDownloadError(new Error('net::ERR_CONNECTION_RESET'))).toBe(true)
expect(isRetryableDownloadError(new Error('certificate rejected'))).toBe(false)
})
})
@@ -21,10 +21,4 @@ describe('node:sqlite reader admission', () => {
expect(hasNodeSqliteReaderApi(sqlite)).toBe(admitted)
expect(probe(sqlite)).toBe(admitted)
})
it('admits the actual runtime module', () => {
const sqlite: unknown = process.getBuiltinModule('node:sqlite')
expect(hasNodeSqliteReaderApi(sqlite)).toBe(true)
expect(probe(sqlite)).toBe(true)
})
})
@@ -1,25 +0,0 @@
import { describe, expect, it } from 'vitest'
import { ORCAD_WINDOWS_PROCESS_TREE_FILENAME } from '../../shared/orcad-artifacts'
import { relayArtifactFilenames } from './relay-install-artifacts'
describe('relay install artifacts', () => {
it('keeps the optional process-table addon out of the required set', () => {
// A complete install never had to contain it: only a Windows build machine could emit it.
expect(relayArtifactFilenames(true)).not.toContain(ORCAD_WINDOWS_PROCESS_TREE_FILENAME)
expect(relayArtifactFilenames(false)).not.toContain(ORCAD_WINDOWS_PROCESS_TREE_FILENAME)
})
it('requires the Windows-only patches only on Windows', () => {
expect(relayArtifactFilenames(true)).toContain('relay.js')
expect(relayArtifactFilenames(true)).toContain('node-pty-1.1.0-console-list-agent-patch.cjs')
expect(relayArtifactFilenames(false)).not.toContain(
'node-pty-1.1.0-console-list-agent-patch.cjs'
)
})
it('lists the pty-master cloexec patch on every platform', () => {
for (const isWindows of [true, false]) {
expect(relayArtifactFilenames(isWindows)).toContain('node-pty-1.1.0-master-cloexec-patch.cjs')
}
})
})
-28
View File
@@ -1,28 +0,0 @@
import { join } from 'node:path'
import { describe, expect, it } from 'vitest'
import { currentWorkerEntryLayout, resolveWorkerThreadEntryPath } from './worker-thread-entry-path'
describe('worker-thread entry path', () => {
const outMain = join('/repo', 'out', 'main')
it('resolves an unpackaged caller bundled into a shared chunk beside the main entry', () => {
const layout = currentWorkerEntryLayout(join(outMain, 'chunks'))
expect(resolveWorkerThreadEntryPath({ ...layout, isPackaged: false }, 'entry.js')).toBe(
join(outMain, 'entry.js')
)
})
it('keeps an unpackaged caller already in the main entry folder', () => {
const layout = currentWorkerEntryLayout(outMain)
expect(resolveWorkerThreadEntryPath({ ...layout, isPackaged: false }, 'entry.js')).toBe(
join(outMain, 'entry.js')
)
})
it('resolves packaged builds from resources, whatever the caller chunk', () => {
const layout = { isPackaged: true, resourcesPath: '/res', moduleDir: join(outMain, 'chunks') }
expect(resolveWorkerThreadEntryPath(layout, 'entry.js')).toBe(
join('/res', 'app.asar', 'out', 'main', 'entry.js')
)
})
})
@@ -1,31 +0,0 @@
import { describe, expect, it } from 'vitest'
import type { WorkspaceSpaceWorktree } from '../shared/workspace-space-types'
import { summarizeWorkspaceSpaceRows } from './workspace-space-repo-scan'
describe('summarizeWorkspaceSpaceRows', () => {
it('reads each summary field once per row', () => {
const reads = { status: 0, sizeBytes: 0, reclaimableBytes: 0 }
const makeRow = (status: WorkspaceSpaceWorktree['status']) =>
new Proxy({ status, sizeBytes: 10, reclaimableBytes: 4 } as WorkspaceSpaceWorktree, {
get(target, property, receiver) {
if (
property === 'status' ||
property === 'sizeBytes' ||
property === 'reclaimableBytes'
) {
reads[property] += 1
}
// oxlint-disable-next-line anti-slop/no-reflect-get -- Proxy get trap default forward.
return Reflect.get(target, property, receiver)
}
})
expect(summarizeWorkspaceSpaceRows([makeRow('ok'), makeRow('unavailable')])).toEqual({
scannedWorktreeCount: 1,
unavailableWorktreeCount: 1,
totalSizeBytes: 20,
reclaimableBytes: 8
})
expect(reads).toEqual({ status: 2, sizeBytes: 2, reclaimableBytes: 2 })
})
})
@@ -1,30 +0,0 @@
import { existsSync } from 'node:fs'
import { describe, expect, it, beforeEach } from 'vitest'
import {
readZCodeInteractiveCapability,
_resetZCodeInteractiveCapabilityForTests
} from './interactive-capability'
// Compare real bundled/no-TUI and CLI/TUI builds only where those local fixtures exist.
const WITHOUT_TUI = '/tmp/zcode-bin-notui/zcode'
const WITH_TUI = '/tmp/zcode-bin-tui/zcode'
const bothPresent = existsSync(WITHOUT_TUI) && existsSync(WITH_TUI)
const describeLive = bothPresent ? describe : describe.skip
beforeEach(() => _resetZCodeInteractiveCapabilityForTests())
describeLive('readZCodeInteractiveCapability against real ZCode builds', () => {
it("reports missing-tui for the desktop app's bundled runtime", async () => {
await expect(readZCodeInteractiveCapability(WITHOUT_TUI)).resolves.toBe('missing-tui')
}, 30_000)
it('reports interactive for a CLI built from source', async () => {
await expect(readZCodeInteractiveCapability(WITH_TUI)).resolves.toBe('interactive')
}, 30_000)
it('fails open when the command does not exist', async () => {
await expect(readZCodeInteractiveCapability('/tmp/definitely-not-zcode')).resolves.toBe(
'unknown'
)
}, 30_000)
})
@@ -1,37 +0,0 @@
import { describe, expect, it } from 'vitest'
import {
formatBrowserDownloadProgress,
type BrowserDownloadState
} from './browser-download-progress'
function download(overrides: Partial<BrowserDownloadState> = {}): BrowserDownloadState {
return {
downloadId: 'd1',
browserPageId: 'p1',
filename: 'file.bin',
mimeType: 'application/octet-stream',
origin: 'https://example.com',
totalBytes: 2048,
receivedBytes: 1024,
status: 'downloading',
savePath: null,
error: null,
progressState: 'progressing',
completedAt: null,
url: 'https://example.com/file.bin',
...overrides
} as BrowserDownloadState
}
describe('formatBrowserDownloadProgress', () => {
it('joins received and total when both are known', () => {
expect(formatBrowserDownloadProgress(download())).toBe('1.0 KB / 2.0 KB')
})
it('falls back to the single known side', () => {
expect(formatBrowserDownloadProgress(download({ totalBytes: null }))).toBe('1.0 KB')
expect(formatBrowserDownloadProgress(download({ receivedBytes: -1, totalBytes: 2048 }))).toBe(
'2.0 KB'
)
})
})
@@ -19,10 +19,6 @@ function keyboardEvent(
}
describe('remote browser keyboard serialization', () => {
it('serializes modified letter shortcuts', () => {
expect(getRemoteBrowserKeyboardShortcut(keyboardEvent({ ctrlKey: true }))).toBe('Control+r')
})
it('preserves Shift on modified letter shortcuts', () => {
expect(
getRemoteBrowserKeyboardShortcut(keyboardEvent({ key: 'R', ctrlKey: true, shiftKey: true }))
@@ -1,32 +0,0 @@
import { describe, expect, it } from 'vitest'
import {
getCommentReplyTargetCandidates,
resolveCommentReplyTarget
} from './comment-reply-target-state'
describe('comment reply target state', () => {
it('preserves a visible reply target', () => {
expect(resolveCommentReplyTarget(2, [{ id: 1 }, { id: 2 }])).toBe(2)
})
it('clears a hidden reply target', () => {
expect(resolveCommentReplyTarget(3, [{ id: 1 }, { id: 2 }])).toBeNull()
})
it('keeps an empty reply target empty', () => {
expect(resolveCommentReplyTarget(null, [{ id: 1 }])).toBeNull()
})
it('uses filtered comments as reply targets for PRs', () => {
expect(getCommentReplyTargetCandidates('pr', [{ id: 1 }, { id: 2 }], [{ id: 2 }])).toEqual([
{ id: 2 }
])
})
it('uses all comments as reply targets for issues', () => {
expect(getCommentReplyTargetCandidates('issue', [{ id: 1 }, { id: 2 }], [{ id: 2 }])).toEqual([
{ id: 1 },
{ id: 2 }
])
})
})
@@ -1,40 +0,0 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { toast } from 'sonner'
import { submitDiffSectionComment } from './diff-section-comment-submit'
import type { DiffSection } from './diff-section-types'
vi.mock('sonner', () => ({ toast: { error: vi.fn() } }))
const section: DiffSection = {
key: 'section-1',
path: 'src/example.ts',
status: 'modified',
originalContent: 'before',
modifiedContent: 'after',
collapsed: false,
loading: false,
dirty: false,
diffResult: null,
largeDiffRenderLimit: null
}
describe('submitDiffSectionComment', () => {
beforeEach(() => {
vi.mocked(toast.error).mockClear()
})
it('surfaces persistence failures while keeping the draft open', async () => {
const addDiffComment = vi.fn().mockResolvedValue(null)
const result = await submitDiffSectionComment({
addDiffComment,
body: 'Needs revision',
target: { lineNumber: 4 },
section,
worktreeId: 'worktree-1'
})
expect(result).toBe(false)
expect(toast.error).toHaveBeenCalledWith('Failed to save comment')
})
})
@@ -1,41 +0,0 @@
import { describe, expect, it } from 'vitest'
import { getEditorDisplayLabel } from './editor-labels'
import type { OpenFile } from '@/store/slices/editor'
function makeOpenFile(overrides: Partial<OpenFile> = {}): OpenFile {
return {
id: '/repo/docs/README.md',
filePath: '/repo/docs/README.md',
relativePath: 'docs/README.md',
worktreeId: 'wt-1',
language: 'markdown',
isDirty: false,
mode: 'edit',
...overrides
}
}
describe('getEditorDisplayLabel', () => {
it('adds a preview suffix for markdown preview tabs', () => {
expect(
getEditorDisplayLabel(
makeOpenFile({
id: 'markdown-preview::/repo/docs/README.md',
mode: 'markdown-preview'
})
)
).toBe('README.md (preview)')
})
it('uses the requested label variant for markdown preview tabs', () => {
expect(
getEditorDisplayLabel(
makeOpenFile({
id: 'markdown-preview::/repo/docs/README.md',
mode: 'markdown-preview'
}),
'relativePath'
)
).toBe('docs/README.md (preview)')
})
})
@@ -1,19 +1,11 @@
import { describe, expect, it } from 'vitest'
import {
clearGitHubLinkCopied,
createGitHubLinkCopyState,
markGitHubLinkCopied,
resolveGitHubLinkCopyState,
type GitHubLinkCopyState
resolveGitHubLinkCopyState
} from './github-link-copy-state'
describe('GitHub link copy state', () => {
it('keeps copied state for the same work item', () => {
const state = markGitHubLinkCopied('item-1')
expect(resolveGitHubLinkCopyState(state, 'item-1')).toBe(state)
})
it('resets copied state when the rendered work item changes', () => {
const state = markGitHubLinkCopied('item-1')
@@ -28,10 +20,4 @@ describe('GitHub link copy state', () => {
expect(clearGitHubLinkCopied(state, 'item-1')).toBe(state)
})
it('preserves identity when clearing an already-clear state', () => {
const state: GitHubLinkCopyState = createGitHubLinkCopyState('item-1')
expect(clearGitHubLinkCopied(state, 'item-1')).toBe(state)
})
})
@@ -1,26 +0,0 @@
// @vitest-environment happy-dom
import { cleanup, render, screen } from '@testing-library/react'
import { afterEach, describe, expect, it } from 'vitest'
import { ProjectItemsEmptyState } from './ProjectViewStates'
afterEach(cleanup)
const FILTERED_COPY = "No items match this view's filter."
const UNFILTERED_COPY = 'This view has no items yet.'
const TRANSIENCE_HINT = 'Recently added items can take a while to appear.'
describe('ProjectItemsEmptyState', () => {
it('blames the filter only when the view actually has one', () => {
render(<ProjectItemsEmptyState filter="status:Todo" />)
expect(screen.getByText(FILTERED_COPY)).toBeTruthy()
expect(screen.queryByText(UNFILTERED_COPY)).toBeNull()
})
it.each(['', ' ', '\n\t'])('reports an unfiltered view as empty for filter %j', (filter) => {
render(<ProjectItemsEmptyState filter={filter} />)
expect(screen.getByText(UNFILTERED_COPY)).toBeTruthy()
expect(screen.getByText(TRANSIENCE_HINT)).toBeTruthy()
expect(screen.queryByText(FILTERED_COPY)).toBeNull()
})
})
@@ -1,34 +0,0 @@
import { describe, expect, it } from 'vitest'
import {
CLOSED_LINEAR_API_KEY_DIALOG_STATE,
resolveLinearApiKeyDialogState,
type LinearApiKeyDialogState
} from './linear-api-key-dialog-state'
describe('resolveLinearApiKeyDialogState', () => {
it('preserves draft and error state while the dialog is open', () => {
const state: LinearApiKeyDialogState = {
apiKeyDraft: 'lin_api_key',
connectState: 'error',
connectError: 'Nope'
}
expect(resolveLinearApiKeyDialogState(state, true)).toBe(state)
})
it('preserves identity for an already-reset closed dialog', () => {
expect(resolveLinearApiKeyDialogState(CLOSED_LINEAR_API_KEY_DIALOG_STATE, false)).toBe(
CLOSED_LINEAR_API_KEY_DIALOG_STATE
)
})
it('resets draft and connection state when the dialog is closed', () => {
const state: LinearApiKeyDialogState = {
apiKeyDraft: 'lin_api_key',
connectState: 'connecting',
connectError: 'Previous error'
}
expect(resolveLinearApiKeyDialogState(state, false)).toEqual(CLOSED_LINEAR_API_KEY_DIALOG_STATE)
})
})
@@ -1,38 +0,0 @@
import { describe, expect, it } from 'vitest'
import { matchNativeChatSplitShortcut } from './native-chat-split-shortcut'
describe('matchNativeChatSplitShortcut', () => {
it('uses the existing platform split bindings', () => {
expect(
matchNativeChatSplitShortcut(
{ key: 'd', metaKey: true, ctrlKey: false, altKey: false, shiftKey: false },
'darwin',
{}
)
).toBe('right')
expect(
matchNativeChatSplitShortcut(
{ key: 'd', metaKey: false, ctrlKey: true, altKey: false, shiftKey: true },
'win32',
{}
)
).toBe('right')
expect(
matchNativeChatSplitShortcut(
{ key: 'd', metaKey: false, ctrlKey: false, altKey: true, shiftKey: true },
'linux',
{}
)
).toBe('down')
})
it('respects customized bindings', () => {
expect(
matchNativeChatSplitShortcut(
{ key: 'ArrowRight', metaKey: false, ctrlKey: true, altKey: true, shiftKey: false },
'linux',
{ 'terminal.splitRight': ['Ctrl+Alt+Right'] }
)
).toBe('right')
})
})
@@ -1,27 +0,0 @@
import { describe, expect, it } from 'vitest'
import {
appendPushFailureCustomInstruction,
buildFixPushFailurePrompt
} from '../../../../shared/source-control-push-failure'
describe('SourceControl push failure recovery prompt', () => {
it('adds one-time custom instructions before the response contract', () => {
const prompt = buildFixPushFailurePrompt({
summary: 'Pre-push hook failed.',
error: 'lint failed',
branchName: 'main',
worktreePath: null,
entries: [],
customInstruction: 'Only change TypeScript files.'
})
expect(prompt).toContain('Additional user instruction for this fix:')
expect(prompt).toContain('Only change TypeScript files.')
expect(prompt.trim().endsWith('anything left for the user.')).toBe(true)
})
it('leaves the base prompt unchanged for empty custom instructions', () => {
const prompt = 'Fix the failed push.'
expect(appendPushFailureCustomInstruction(prompt, ' ')).toBe(prompt)
})
})
@@ -1,33 +0,0 @@
import { describe, expect, it } from 'vitest'
import { aiVaultSearchHitHostLabels } from './ai-vault-search-hit-hosts'
describe('aiVaultSearchHitHostLabels', () => {
it('returns null while every hit came from one host', () => {
expect(
aiVaultSearchHitHostLabels(
[{ executionHostId: 'runtime:devbox' }, { executionHostId: 'runtime:devbox' }],
[{ id: 'runtime:devbox', label: 'devbox' }]
)
).toBeNull()
})
it('names each host the way the host menu does, with the id label as fallback', () => {
const labels = aiVaultSearchHitHostLabels(
[
{ executionHostId: 'local' },
{ executionHostId: 'runtime:env-1' },
{ executionHostId: 'runtime:env-gone' }
],
[
{ id: 'local', label: 'Local Mac' },
{ id: 'runtime:env-1', label: 'devbox' }
]
)
expect(labels && Object.fromEntries(labels)).toEqual({
local: 'Local Mac',
'runtime:env-1': 'devbox',
'runtime:env-gone': 'env-gone'
})
})
})
@@ -1,36 +0,0 @@
import { describe, expect, it } from 'vitest'
import { fileExplorerRefreshConcurrency } from './file-explorer-refresh-concurrency'
describe('fileExplorerRefreshConcurrency', () => {
it('allows the widest fan-out for local disk reads', () => {
expect(fileExplorerRefreshConcurrency({ kind: 'local' })).toBe(16)
})
it('caps SSH-owned worktrees at the remote tier', () => {
expect(fileExplorerRefreshConcurrency({ kind: 'ssh', connectionId: 'target-1' })).toBe(4)
})
it('treats an SSH-backed runtime as remote', () => {
expect(
fileExplorerRefreshConcurrency({
kind: 'runtime',
environmentId: 'env-1',
executionHostId: 'ssh:target-1'
})
).toBe(4)
})
it('uses the runtime tier for a non-SSH runtime host', () => {
expect(
fileExplorerRefreshConcurrency({
kind: 'runtime',
environmentId: 'env-1',
executionHostId: 'runtime:env-1'
})
).toBe(8)
})
it('falls back to the conservative remote tier when ownership is unresolved', () => {
expect(fileExplorerRefreshConcurrency({ kind: 'unresolved' })).toBe(4)
})
})
@@ -1,28 +0,0 @@
import { describe, expect, it } from 'vitest'
import {
RIGHT_SIDEBAR_ABSOLUTE_FALLBACK_MAX_WIDTH,
RIGHT_SIDEBAR_MIN_WIDTH,
clampRightSidebarPanelWidth,
computeMaxRightSidebarPanelWidth
} from './right-sidebar-width'
describe('right sidebar rendered width', () => {
it('leaves the reserved non-sidebar area when persisted width is too large', () => {
expect(clampRightSidebarPanelWidth(900, 900, 0)).toBe(580)
})
it('includes rendered extra width in the reservation math', () => {
expect(computeMaxRightSidebarPanelWidth(900, 40)).toBe(540)
expect(clampRightSidebarPanelWidth(900, 900, 40)).toBe(540)
})
it('preserves the minimum sidebar width on narrow windows', () => {
expect(clampRightSidebarPanelWidth(900, 400, 40)).toBe(RIGHT_SIDEBAR_MIN_WIDTH)
})
it('uses the fallback max outside DOM environments', () => {
expect(computeMaxRightSidebarPanelWidth(null, 40)).toBe(
RIGHT_SIDEBAR_ABSOLUTE_FALLBACK_MAX_WIDTH
)
})
})
@@ -1,25 +0,0 @@
import { describe, expect, it } from 'vitest'
import type { HostedReviewProvider } from '../../../../shared/hosted-review'
import { resolveCreatedHostedReviewLink } from './source-control-created-review-link'
describe('resolveCreatedHostedReviewLink', () => {
it.each([
['github', { linkedPR: 42 }, { linkedGitHubPR: 42 }],
['gitlab', { linkedGitLabMR: 42 }, { linkedGitLabMR: 42 }],
['bitbucket', { linkedBitbucketPR: 42 }, { linkedBitbucketPR: 42 }],
['azure-devops', { linkedAzureDevOpsPR: 42 }, { linkedAzureDevOpsPR: 42 }],
['gitea', { linkedGiteaPR: 42 }, { linkedGiteaPR: 42 }]
] as const)('links a created %s review by number', (provider, worktree, lookup) => {
expect(resolveCreatedHostedReviewLink(provider as HostedReviewProvider, 42)).toEqual({
worktree,
lookup
})
})
it('does not link an unsupported review provider', () => {
expect(resolveCreatedHostedReviewLink('unsupported', 42)).toEqual({
worktree: {},
lookup: {}
})
})
})
@@ -1,31 +0,0 @@
import { describe, expect, it } from 'vitest'
import { getFloatingWorkspaceDirectoryInputValue } from './FloatingWorkspacePane'
describe('getFloatingWorkspaceDirectoryInputValue', () => {
it('shows home shorthand for the default terminal directory', () => {
expect(
getFloatingWorkspaceDirectoryInputValue({
configuredFloatingWorkspacePath: '~',
resolvedFloatingWorkspacePath: '/Users/example'
})
).toBe('~')
})
it('shows home shorthand for legacy blank terminal directory settings', () => {
expect(
getFloatingWorkspaceDirectoryInputValue({
configuredFloatingWorkspacePath: '',
resolvedFloatingWorkspacePath: '/Users/example'
})
).toBe('~')
})
it('shows the main-resolved trusted custom directory', () => {
expect(
getFloatingWorkspaceDirectoryInputValue({
configuredFloatingWorkspacePath: '/Users/example/notes',
resolvedFloatingWorkspacePath: '/Users/example/notes'
})
).toBe('/Users/example/notes')
})
})
@@ -1,30 +0,0 @@
import { describe, expect, it, afterEach, vi } from 'vitest'
import { shadowedKeybindingTitles } from './PluginKeybindingConsentPreview'
describe('plugin keybinding consent preview', () => {
it('names built-in shortcuts shadowed by plugin chords and user overrides', () => {
expect(shadowedKeybindingTitles('Mod+P', 'darwin')).toContain('Go to File')
expect(
shadowedKeybindingTitles('Mod+Alt+T', 'linux', { 'view.tasks': ['Mod+Alt+T'] })
).toContain('Open Tasks')
})
})
const { rejectAppStoreAccess } = vi.hoisted(() => ({
rejectAppStoreAccess: vi.fn(() => {
throw new Error('Pure helper tests must not access the app store')
})
}))
vi.mock('@/store', () => ({
useAppStore: Object.assign(rejectAppStoreAccess, {
getState: rejectAppStoreAccess,
getInitialState: rejectAppStoreAccess,
setState: rejectAppStoreAccess,
subscribe: rejectAppStoreAccess
})
}))
afterEach(() => {
expect(rejectAppStoreAccess).not.toHaveBeenCalled()
})
@@ -1,32 +0,0 @@
import { describe, expect, it, vi } from 'vitest'
import { revealRepoInProjectFilter } from './project-filter-reveal'
function makeState(filterRepoIds: readonly string[]) {
return { filterRepoIds, setFilterRepoIds: vi.fn() }
}
describe('revealRepoInProjectFilter', () => {
it('keeps the existing selection and adds the revealed project', () => {
const state = makeState(['repo-a', 'repo-b'])
revealRepoInProjectFilter(state, 'repo-c')
expect(state.setFilterRepoIds).toHaveBeenCalledWith(['repo-a', 'repo-b', 'repo-c'])
})
it('does nothing when no project filter is active', () => {
const state = makeState([])
revealRepoInProjectFilter(state, 'repo-c')
expect(state.setFilterRepoIds).not.toHaveBeenCalled()
})
it('does nothing when the project is already selected', () => {
const state = makeState(['repo-a', 'repo-c'])
revealRepoInProjectFilter(state, 'repo-c')
expect(state.setFilterRepoIds).not.toHaveBeenCalled()
})
})
@@ -1,29 +0,0 @@
// @vitest-environment happy-dom
import { describe, expect, it } from 'vitest'
import { isEventTargetInsideCurrentTarget } from './worktree-card-dom-events'
describe('worktree card DOM events', () => {
it('recognizes DOM events that originate inside the current target', () => {
const currentTarget = document.createElement('div')
const child = document.createElement('button')
currentTarget.appendChild(child)
expect(isEventTargetInsideCurrentTarget(currentTarget, child)).toBe(true)
})
it('rejects portaled DOM events that bubble through the React tree', () => {
const currentTarget = document.createElement('div')
const portaledTarget = document.createElement('button')
expect(isEventTargetInsideCurrentTarget(currentTarget, portaledTarget)).toBe(false)
})
it('supports text-node event targets inside the current target', () => {
const currentTarget = document.createElement('div')
const textTarget = document.createTextNode('Rename')
currentTarget.appendChild(textTarget)
expect(isEventTargetInsideCurrentTarget(currentTarget, textTarget)).toBe(true)
})
})
@@ -1,30 +0,0 @@
import { afterEach, describe, expect, it, vi } from 'vitest'
import { isTerminalHttpLinkActivation } from './terminal-http-link-activation'
afterEach(() => {
vi.unstubAllGlobals()
})
describe('isTerminalHttpLinkActivation', () => {
it('leaves Alt+Cmd gestures to the child TUI on macOS', () => {
vi.stubGlobal('navigator', { userAgent: 'Macintosh' })
expect(
isTerminalHttpLinkActivation({ metaKey: true, ctrlKey: false, altKey: false } as MouseEvent)
).toBe(true)
expect(
isTerminalHttpLinkActivation({ metaKey: true, ctrlKey: false, altKey: true } as MouseEvent)
).toBe(false)
})
it('leaves Alt+Ctrl gestures to the child TUI on other platforms', () => {
vi.stubGlobal('navigator', { userAgent: 'Windows' })
expect(
isTerminalHttpLinkActivation({ metaKey: false, ctrlKey: true, altKey: false } as MouseEvent)
).toBe(true)
expect(
isTerminalHttpLinkActivation({ metaKey: false, ctrlKey: true, altKey: true } as MouseEvent)
).toBe(false)
})
})
@@ -15,17 +15,6 @@ describe('terminal process exit restart', () => {
).toBe(startup)
})
it('restarts a shell when a capacity failure had no startup request', () => {
expect(
resolveTerminalProcessExitRestartStartup({
paneId: 1,
exitCode: 1,
reason: 'git-bash-console-capacity',
startup: null
})
).toBeNull()
})
it('restarts other process failures as a shell', () => {
expect(
resolveTerminalProcessExitRestartStartup({
@@ -1,35 +0,0 @@
import { describe, expect, it, afterEach, vi } from 'vitest'
import { combineTerminalWorktreeParkIds } from './use-manual-terminal-worktree-parking'
describe('combineTerminalWorktreeParkIds', () => {
it('adds manually parked worktrees without dropping automatic parks', () => {
expect(combineTerminalWorktreeParkIds(new Set(['automatic']), new Set(['manual']))).toEqual(
new Set(['automatic', 'manual'])
)
})
it('keeps the automatic set reference when there are no manual parks', () => {
const automatic = new Set(['automatic'])
expect(combineTerminalWorktreeParkIds(automatic, new Set())).toBe(automatic)
})
})
const { rejectAppStoreAccess } = vi.hoisted(() => ({
rejectAppStoreAccess: vi.fn(() => {
throw new Error('Pure helper tests must not access the app store')
})
}))
vi.mock('@/store', () => ({
useAppStore: Object.assign(rejectAppStoreAccess, {
getState: rejectAppStoreAccess,
getInitialState: rejectAppStoreAccess,
setState: rejectAppStoreAccess,
subscribe: rejectAppStoreAccess
})
}))
afterEach(() => {
expect(rejectAppStoreAccess).not.toHaveBeenCalled()
})
@@ -1,28 +0,0 @@
import { describe, expect, it } from 'vitest'
import type { Repo } from '../../../../shared/repo-types'
import { getRepoMultiComboboxDetail } from './repo-multi-combobox'
function repo(overrides: Partial<Repo> = {}): Repo {
return {
id: 'repo-1',
path: '/Users/jinwoo/orca',
displayName: 'orca',
badgeColor: '#999999',
addedAt: 1,
...overrides
}
}
describe('getRepoMultiComboboxDetail', () => {
it('shows host context before the path when available', () => {
expect(getRepoMultiComboboxDetail(repo(), 'Local Mac')).toBe('Local Mac · /Users/jinwoo/orca')
expect(getRepoMultiComboboxDetail(repo({ path: '/home/orca/orca' }), 'openclaw 2')).toBe(
'openclaw 2 · /home/orca/orca'
)
})
it('keeps the existing path-only detail when no host label is provided', () => {
expect(getRepoMultiComboboxDetail(repo(), null)).toBe('/Users/jinwoo/orca')
expect(getRepoMultiComboboxDetail(repo(), ' ')).toBe('/Users/jinwoo/orca')
})
})
@@ -1,39 +0,0 @@
// @vitest-environment happy-dom
import { act } from 'react'
import { createRoot, type Root } from 'react-dom/client'
import { afterEach, describe, expect, it } from 'vitest'
import { ScrollArea } from './scroll-area'
globalThis.IS_REACT_ACT_ENVIRONMENT = true
const roots: Root[] = []
afterEach(() => {
roots.splice(0).forEach((root) => {
act(() => root.unmount())
})
document.body.replaceChildren()
})
describe('ScrollArea sizing footgun', () => {
// Radix Root’s inline position beats absolute classes; size ScrollArea with height instead.
it('keeps Radix inline position:relative even when given an absolute className', async () => {
const container = document.createElement('div')
document.body.appendChild(container)
const root = createRoot(container)
roots.push(root)
await act(async () => {
root.render(
<ScrollArea className="absolute inset-0">
<div />
</ScrollArea>
)
})
const el = container.querySelector<HTMLElement>('[data-slot="scroll-area"]')
expect(el).not.toBeNull()
expect(el!.style.position).toBe('relative')
})
})
+1 -18
View File
@@ -1,17 +1,8 @@
import { renderToStaticMarkup } from 'react-dom/server'
import { describe, expect, it } from 'vitest'
import { Switch, SwitchIndicator } from './switch'
import { SwitchIndicator } from './switch'
describe('Switch', () => {
it('renders the checked state with symmetric track geometry', () => {
const html = renderToStaticMarkup(<Switch checked aria-label="Example setting" />)
expect(html).toContain('role="switch"')
expect(html).toContain('aria-checked="true"')
expect(html).toContain('p-0.5')
expect(html).toContain('data-[state=checked]:translate-x-4')
})
it('renders a visual-only indicator without switch semantics', () => {
const html = renderToStaticMarkup(<SwitchIndicator checked={false} />)
@@ -19,12 +10,4 @@ describe('Switch', () => {
expect(html).toContain('data-state="unchecked"')
expect(html).not.toContain('role="switch"')
})
it('renders compact indicator geometry', () => {
const html = renderToStaticMarkup(<SwitchIndicator checked size="compact" />)
expect(html).toContain('h-3.5 w-6')
expect(html).toContain('size-2.5')
expect(html).toContain('data-[state=checked]:translate-x-2.5')
})
})
@@ -26,14 +26,4 @@ describe('useResetCountdownClock', () => {
})
expect(result.current).toBe(START + 30_000 + 1)
})
it('does not schedule a tick when there is no future reset', () => {
const { result } = renderHook(() => useResetCountdownClock([START - 1000]))
expect(result.current).toBe(START)
act(() => {
vi.advanceTimersByTime(10 * MIN)
})
expect(result.current).toBe(START)
})
})
@@ -1,39 +0,0 @@
import { describe, expect, it } from 'vitest'
import { shouldCancelVirtualizedScrollOffsetRestore } from './virtualizedScrollOffsetRestore'
describe('shouldCancelVirtualizedScrollOffsetRestore', () => {
it('keeps a pending restore when direct user scroll input is not tracked', () => {
expect(
shouldCancelVirtualizedScrollOffsetRestore({
restoring: true
})
).toBe(false)
})
it('keeps a pending restore during programmatic scroll movement', () => {
expect(
shouldCancelVirtualizedScrollOffsetRestore({
hasDirectScrollInput: () => false,
restoring: true
})
).toBe(false)
})
it('does not cancel when there is no pending restore', () => {
expect(
shouldCancelVirtualizedScrollOffsetRestore({
hasDirectScrollInput: () => true,
restoring: false
})
).toBe(false)
})
it('cancels a pending restore while direct user scroll input is active', () => {
expect(
shouldCancelVirtualizedScrollOffsetRestore({
hasDirectScrollInput: () => true,
restoring: true
})
).toBe(true)
})
})
@@ -1,29 +0,0 @@
import { renderToStaticMarkup } from 'react-dom/server'
import { describe, expect, it } from 'vitest'
import { AgentTypeIcon } from './agent-type-icon'
function render(agentType: string | null | undefined): string {
return renderToStaticMarkup(<AgentTypeIcon agentType={agentType} />)
}
describe('AgentTypeIcon', () => {
it("renders a program's first letter", () => {
expect(render('brew')).toContain('>B</text>')
expect(render('terraform')).toContain('>T</text>')
})
it('skips leading punctuation the app id grammar allows', () => {
expect(render('_9tool')).toContain('>9</text>')
expect(render('.+-')).toContain('>?</text>')
})
it('keeps "?" for unidentified agents', () => {
expect(render('unknown')).toContain('>?</text>')
expect(render(null)).toContain('>?</text>')
expect(render(undefined)).toContain('>?</text>')
})
it('still renders the Claude icon for known agents', () => {
expect(render('claude')).toContain('#D97757')
})
})
@@ -24,10 +24,4 @@ describe('createOptionKeyLocationTracker', () => {
tracker.keyUp({ key: 'Alt', location: 0 })
expect(tracker.get()).toBe(0)
})
it('ignores non-Option keys', () => {
const tracker = createOptionKeyLocationTracker()
tracker.keyDown({ key: 'Shift', location: 1 })
expect(tracker.get()).toBe(0)
})
})
@@ -1,42 +0,0 @@
import { describe, expect, it } from 'vitest'
import { getLinkedWorkItemProvider } from './linked-work-item-provider'
describe('getLinkedWorkItemProvider', () => {
it.each([
[
'explicit provider metadata',
{
type: 'issue',
provider: 'jira',
number: 0,
title: 'ORCA-123 Fix Jira',
url: 'https://example.atlassian.net/browse/ORCA-123',
jiraIdentifier: 'ORCA-123'
},
'jira'
],
[
'Jira issue URL with no numeric issue id',
{
type: 'issue',
number: 0,
title: 'ORCA-123 Fix Jira',
url: 'https://example.atlassian.net/browse/ORCA-123'
},
'jira'
],
[
'legacy Linear linked issue',
{
type: 'issue',
number: 0,
title: 'Fix Linear',
url: 'https://linear.app/team/issue/ENG-123/fix-linear',
linearIdentifier: 'ENG-123'
},
'linear'
]
] as const)('detects %s', (_label, item, provider) => {
expect(getLinkedWorkItemProvider(item)).toBe(provider)
})
})
@@ -1,29 +0,0 @@
import { afterEach, describe, expect, it } from 'vitest'
import { hashOrcaHookScript } from './orca-hook-trust'
const realCrypto = globalThis.crypto
afterEach(() => {
Object.defineProperty(globalThis, 'crypto', { value: realCrypto, configurable: true })
})
function stubCrypto(value: unknown): void {
Object.defineProperty(globalThis, 'crypto', { value, configurable: true })
}
describe('hashOrcaHookScript', () => {
it('produces a stable hex digest via crypto.subtle', async () => {
const hash = await hashOrcaHookScript('echo hi')
expect(hash).toMatch(/^[0-9a-f]+$/)
expect(await hashOrcaHookScript(' echo hi ')).toBe(hash)
})
// Plain-HTTP LAN clients lack crypto.subtle; fallback must still produce deterministic matching trust hashes.
it('falls back to a deterministic hash when crypto.subtle is unavailable', async () => {
stubCrypto(undefined)
const hash = await hashOrcaHookScript('echo hi')
expect(hash).toMatch(/^[0-9a-f]+$/)
expect(await hashOrcaHookScript('echo hi')).toBe(hash)
expect(await hashOrcaHookScript('echo bye')).not.toBe(hash)
})
})
@@ -1,37 +0,0 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { shouldFocusTerminalFromPanePointerDown } from './pane-pointer-focus'
class FakeElement {
constructor(private readonly closestResult: FakeElement | null = null) {}
closest(): FakeElement | null {
return this.closestResult
}
}
describe('shouldFocusTerminalFromPanePointerDown', () => {
beforeEach(() => {
vi.stubGlobal('Element', FakeElement)
})
afterEach(() => {
vi.unstubAllGlobals()
})
it('focuses the terminal for non-element targets', () => {
expect(shouldFocusTerminalFromPanePointerDown({} as EventTarget)).toBe(true)
})
it('focuses the terminal for ordinary pane surface clicks', () => {
const target = new FakeElement()
expect(shouldFocusTerminalFromPanePointerDown(target as unknown as Element)).toBe(true)
})
it('does not steal focus from pane-local controls', () => {
const control = new FakeElement()
const target = new FakeElement(control)
expect(shouldFocusTerminalFromPanePointerDown(target as unknown as Element)).toBe(false)
})
})
-27
View File
@@ -4,12 +4,6 @@ import { placeCreatedSessionTab } from './session-tab-placement'
const created = { id: 'new' }
describe('placeCreatedSessionTab', () => {
it('inserts directly after the anchor', () => {
expect(
placeCreatedSessionTab([{ id: 'a' }, { id: 'b' }, { id: 'c' }], created, 'a').map((t) => t.id)
).toEqual(['a', 'new', 'b', 'c'])
})
it('inserts after all leaves of a split parent', () => {
expect(
placeCreatedSessionTab(
@@ -54,27 +48,12 @@ describe('placeCreatedSessionTab', () => {
).toEqual(['split::left', 'trailing', 'split::right', 'new'])
})
it('appends when the anchor is the last tab', () => {
expect(
placeCreatedSessionTab([{ id: 'a' }, { id: 'b' }], created, 'b').map((t) => t.id)
).toEqual(['a', 'b', 'new'])
})
it('appends when the anchor is absent', () => {
expect(
placeCreatedSessionTab([{ id: 'a' }, { id: 'b' }], created, 'missing').map((t) => t.id)
).toEqual(['a', 'b', 'new'])
})
it('appends when no anchor is given', () => {
for (const anchor of [undefined, null, '']) {
expect(placeCreatedSessionTab([{ id: 'a' }], created, anchor).map((t) => t.id)).toEqual([
'a',
'new'
])
}
})
it('re-places a tab that is already in the list instead of duplicating it', () => {
expect(
placeCreatedSessionTab([{ id: 'a' }, { id: 'new' }, { id: 'b' }], created, 'b').map(
@@ -82,10 +61,4 @@ describe('placeCreatedSessionTab', () => {
)
).toEqual(['a', 'b', 'new'])
})
it('does not mutate the input list', () => {
const tabs = [{ id: 'a' }, { id: 'b' }]
placeCreatedSessionTab(tabs, created, 'a')
expect(tabs.map((t) => t.id)).toEqual(['a', 'b'])
})
})
@@ -1,9 +1,5 @@
import { describe, expect, it } from 'vitest'
import {
buildWorkspaceReviewFacts,
prGroupOfReviewFact,
readWorkspaceReviewFact
} from './workspace-review-facts'
import { buildWorkspaceReviewFacts, readWorkspaceReviewFact } from './workspace-review-facts'
const repo = { id: 'repo-1', path: '/r', connectionId: null, executionHostId: null }
const row = { id: 'repo-1::/r/a', repoId: 'repo-1', branch: 'refs/heads/a', linkedPR: null }
@@ -43,15 +39,6 @@ describe('readWorkspaceReviewFact', () => {
})
})
describe('prGroupOfReviewFact', () => {
it('keeps rows without a PR in progress and lanes the rest by state', () => {
expect(prGroupOfReviewFact(null)).toBe('in-progress')
expect(prGroupOfReviewFact({ provider: 'github', reviewState: 'draft' })).toBe('in-progress')
expect(prGroupOfReviewFact({ provider: 'github', reviewState: '' })).toBe('in-review')
expect(prGroupOfReviewFact({ provider: 'github', reviewState: 'merged' })).toBe('done')
})
})
it('lists only rows with a PR, under the key the workspaces domain lists them at', () => {
const other = { ...row, id: 'repo-1::/r/b', branch: 'b' }
expect(buildWorkspaceReviewFacts([row, other], [repo], { 'repo-1::a': pr('open') })).toEqual({
+1 -65
View File
@@ -1,10 +1,5 @@
import { describe, expect, it } from 'vitest'
import {
MAX_SKILL_DELETE_BATCH,
SkillDeletePlanSchema,
SkillDeleteRequestSchema,
SkillDeleteResultSchema
} from './skill-delete-contract'
import { MAX_SKILL_DELETE_BATCH, SkillDeleteRequestSchema } from './skill-delete-contract'
const SKILL = {
id: 'skill-id',
@@ -15,13 +10,6 @@ const SKILL = {
}
describe('SkillDeleteRequestSchema', () => {
it('accepts a request with no target, which is what the page sends', () => {
expect(SkillDeleteRequestSchema.parse({ operationId: 'op', skills: [SKILL] })).toEqual({
operationId: 'op',
skills: [SKILL]
})
})
it('rejects an unknown top-level field rather than ignoring it', () => {
// Old hosts must reject delete-affecting unknown fields instead of silently dropping them.
expect(() =>
@@ -56,55 +44,3 @@ describe('SkillDeleteRequestSchema', () => {
).toThrow()
})
})
describe('plan and result schemas', () => {
it('carries a typed block reason so the band can group without string matching', () => {
const plan = SkillDeletePlanSchema.parse({
operationId: 'op',
skills: [
{
id: 'a',
name: 'demo',
canonicalPath: '/root/demo/SKILL.md',
placements: [],
blocked: 'bundled'
}
]
})
expect(plan.skills[0].blocked).toBe('bundled')
})
it('rejects a block reason outside the shared vocabulary', () => {
expect(() =>
SkillDeletePlanSchema.parse({
operationId: 'op',
skills: [{ id: 'a', name: 'demo', canonicalPath: '/p', placements: [], blocked: 'because' }]
})
).toThrow()
})
it('has no `unsupported` status, because such a delete is never issued', () => {
expect(() =>
SkillDeleteResultSchema.parse({
operationId: 'op',
skills: [{ id: 'a', name: 'demo', status: 'unsupported', removedPaths: [] }]
})
).toThrow()
})
it('accepts a partial result carrying the staged paths', () => {
const result = SkillDeleteResultSchema.parse({
operationId: 'op',
skills: [
{
id: 'a',
name: 'demo',
status: 'partial',
removedPaths: [],
stagedPaths: ['/root/.demo.orca-skill-delete-1']
}
]
})
expect(result.skills[0].stagedPaths).toHaveLength(1)
})
})
-16
View File
@@ -1,7 +1,5 @@
import { describe, expect, it } from 'vitest'
import {
nativeSkillPathSemantics,
normalizedSkillPath,
POSIX_SKILL_PATH_SEMANTICS,
skillPathDepthBelow,
skillPathInside,
@@ -74,17 +72,3 @@ describe('skillPathDepthBelow', () => {
expect(skillPathDepthBelow('/r', '/other/SKILL.md', POSIX_SKILL_PATH_SEMANTICS)).toBeNull()
})
})
describe('nativeSkillPathSemantics', () => {
it('reads the platform it is given rather than the process it runs in', () => {
expect(nativeSkillPathSemantics('win32')).toEqual(WINDOWS_SKILL_PATH_SEMANTICS)
expect(nativeSkillPathSemantics('linux')).toEqual(POSIX_SKILL_PATH_SEMANTICS)
expect(nativeSkillPathSemantics('darwin')).toEqual(POSIX_SKILL_PATH_SEMANTICS)
})
})
describe('normalizedSkillPath', () => {
it('resolves before comparing', () => {
expect(normalizedSkillPath('/a/b/../c', POSIX_SKILL_PATH_SEMANTICS)).toBe('/a/c')
})
})
@@ -2,49 +2,11 @@ import { describe, expect, it } from 'vitest'
import type { CommitMessageDraftContext } from './commit-message-generation'
import {
formatLinkedIssueTemplateValue,
SOURCE_CONTROL_ACTION_VARIABLE_INFO,
SOURCE_CONTROL_ACTION_VARIABLES,
withLinkedIssueDraftContext
} from './source-control-ai-action-variables'
import {
renderSourceControlActionCommandTemplate,
SOURCE_CONTROL_LAUNCH_ACTION_IDS
} from './source-control-ai-actions'
describe('source-control AI variable registry', () => {
it('documents every registered variable so chip hover cards cannot crash', () => {
// Keep runtime coverage if the registry’s compile-time constraint ever loosens.
const documented = new Set<string>(Object.keys(SOURCE_CONTROL_ACTION_VARIABLE_INFO))
const undocumented = [...new Set(Object.values(SOURCE_CONTROL_ACTION_VARIABLES).flat())].filter(
(variable) => !documented.has(variable)
)
expect(undocumented).toEqual([])
})
it('offers linkedIssue on commit message and pull request only', () => {
expect(SOURCE_CONTROL_ACTION_VARIABLES.commitMessage).toContain('linkedIssue')
expect(SOURCE_CONTROL_ACTION_VARIABLES.pullRequest).toContain('linkedIssue')
expect(SOURCE_CONTROL_ACTION_VARIABLES.branchName).not.toContain('linkedIssue')
for (const actionId of SOURCE_CONTROL_LAUNCH_ACTION_IDS) {
expect(SOURCE_CONTROL_ACTION_VARIABLES[actionId]).not.toContain('linkedIssue')
}
})
it('names GitHub and the empty case in the linkedIssue description', () => {
const info = SOURCE_CONTROL_ACTION_VARIABLE_INFO.linkedIssue
expect(info.description).toContain('GitHub')
expect(info.description).toContain('Empty')
expect(info.example).toBe('123')
})
})
import { renderSourceControlActionCommandTemplate } from './source-control-ai-actions'
describe('formatLinkedIssueTemplateValue', () => {
it('renders positive integers as decimal strings', () => {
expect(formatLinkedIssueTemplateValue(123)).toBe('123')
expect(formatLinkedIssueTemplateValue(1)).toBe('1')
})
it('renders anything that is not a positive integer as an empty string', () => {
// Corrupt issue numbers must not produce references like Fixes #-7 or Fixes #1e+21.
for (const value of [
-24
View File
@@ -23,11 +23,6 @@ describe('synthetic agent titles', () => {
expect(isSyntheticAgentPermissionTitle(title)).toBe(false)
})
it('provides terminal-state titles for Codex hook completion', () => {
expect(getSyntheticAgentTerminalTitle('codex', 'done')).toBe('Codex ready')
expect(getSyntheticAgentTerminalTitle('codex', 'waiting')).toBe('Codex - action required')
})
it('does not synthesize Codex working titles over Codex native spinner titles', () => {
expect(shouldDriveSyntheticAgentTitleFromHook('codex', 'working')).toBe(false)
expect(shouldDriveSyntheticAgentTitleFromHook('codex', 'done')).toBe(true)
@@ -40,23 +35,4 @@ describe('synthetic agent titles', () => {
expect(shouldDriveSyntheticAgentTitleFromHook('opencode', 'done')).toBe(false)
expect(shouldDriveSyntheticAgentTitleFromHook('opencode', 'waiting')).toBe(false)
})
it('provides Devin titles for hook-driven status updates', () => {
expect(getSyntheticAgentTerminalTitle('devin', 'done')).toBe('Devin ready')
expect(getSyntheticAgentTerminalTitle('devin', 'waiting')).toBe('Devin - action required')
expect(shouldDriveSyntheticAgentTitleFromHook('devin', 'working')).toBe(true)
})
it('provides Pi-compatible OMP titles for hook-driven status updates', () => {
expect(getSyntheticAgentTerminalTitle('omp', 'done')).toBe('OMP ready')
expect(getSyntheticAgentTerminalTitle('omp', 'waiting')).toBe('OMP - action required')
// Why: the native π working title carries session name/cwd; synthesizing would clobber it.
expect(shouldDriveSyntheticAgentTitleFromHook('omp', 'working')).toBe(false)
})
it('provides Pi titles for hook-driven status updates', () => {
expect(getSyntheticAgentTerminalTitle('pi', 'done')).toBe('Pi ready')
expect(getSyntheticAgentTerminalTitle('pi', 'waiting')).toBe('Pi - action required')
expect(shouldDriveSyntheticAgentTitleFromHook('pi', 'working')).toBe(false)
})
})
+1 -18
View File
@@ -2,23 +2,10 @@ import { describe, expect, it } from 'vitest'
import {
filterAvailableTaskProviders,
normalizeTaskProviderSettings,
normalizeVisibleTaskProviders,
restoreAvailableDefaultTaskProvider,
resolveVisibleTaskProvider
restoreAvailableDefaultTaskProvider
} from './task-providers'
describe('task providers', () => {
it('normalizes provider lists while preserving supported order', () => {
expect(normalizeVisibleTaskProviders(['gitlab', 'unknown', 'gitlab', 'linear'])).toEqual([
'gitlab',
'linear'
])
})
it('falls back to all providers when none are visible', () => {
expect(normalizeVisibleTaskProviders([])).toEqual(['github', 'gitlab', 'linear', 'jira'])
})
it('restores a valid saved default when provider settings drifted', () => {
expect(
normalizeTaskProviderSettings({
@@ -43,10 +30,6 @@ describe('task providers', () => {
})
})
it('resolves hidden preferred providers to the first visible provider', () => {
expect(resolveVisibleTaskProvider('github', ['linear'])).toBe('linear')
})
it('filters runtime-unavailable providers without changing preference normalization', () => {
expect(
filterAvailableTaskProviders(['github', 'gitlab', 'linear'], {

Some files were not shown because too many files have changed in this diff Show More