mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 08:37:49 +00:00
test: remove low-value predicates, inventories and helper echoes (#33778)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
@@ -933,7 +933,6 @@
|
||||
6 test-no-wall-clock:src/main/runtime/orchestration/worker-output-archive.test.ts
|
||||
1 test-no-wall-clock:src/main/runtime/owned-session-teardown-policy.test.ts
|
||||
2 test-no-wall-clock:src/main/runtime/project-removal-structured-sessions.test.ts
|
||||
1 test-no-wall-clock:src/main/runtime/proven-absent-leaf-pty-verdicts.test.ts
|
||||
2 test-no-wall-clock:src/main/runtime/pty-shell-ownership-mirror.test.ts
|
||||
1 test-no-wall-clock:src/main/runtime/push/push-device-registration-persistence.test.ts
|
||||
1 test-no-wall-clock:src/main/runtime/push/push-registration-rpc.test.ts
|
||||
|
||||
@@ -13603,7 +13603,7 @@
|
||||
"pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/startup/ssh-startup-reconnect.test.ts src/renderer/src/lib/resolved-worktree-execution-host.test.ts src/renderer/src/components/terminal/background-terminal-worktree-mount.test.ts src/renderer/src/runtime/sync-runtime-graph-scheduling.test.ts src/renderer/src/components/terminal-pane/use-terminal-pane-lifecycle.test.ts src/renderer/src/components/terminal-pane/pty-connection-spawn-and-startup.test.ts src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-host-surface-replacement.test.ts src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-stream-reconnect.test.ts src/renderer/src/runtime/remote-runtime-session-tabs-inflight.test.ts src/renderer/src/runtime/web-session-terminal-handle-events.test.ts src/renderer/src/store/slices/terminal-pty-identity-replacement.test.ts",
|
||||
"pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/pty-transport-reattach-admission.test.ts src/renderer/src/components/terminal-pane/pty-transport-detach-attach-handoff.test.ts",
|
||||
"pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/terminal-error-accumulation.test.ts src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-attach-subscription.test.ts --reporter=dot",
|
||||
"pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-attach-subscription.test.ts src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-host-surface-replacement.test.ts src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-stream-reconnect.test.ts src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-expired-pane-recovery.test.ts src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-create-outcome-recovery.test.ts src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-create-handoff.test.ts src/renderer/src/components/terminal-pane/remote-runtime-pty-recovery-state.test.ts src/renderer/src/components/terminal-pane/TerminalRemoteRuntimeReconnectBanner.test.tsx src/renderer/src/components/terminal-pane/terminal-remote-runtime-recovery-ui-state.test.ts src/shared/remote-runtime-socket-liveness.test.ts src/shared/remote-runtime/control/remote-runtime-shared-control-connection.test.ts src/shared/remote-runtime/control/remote-runtime-shared-control-socket-generation.test.ts src/shared/remote-runtime/remote-runtime-client-error-classification.test.ts src/main/runtime/rpc/remote-runtime-server-heartbeat.test.ts src/main/runtime/rpc/methods/terminal-create-idempotency.test.ts src/main/runtime/orca-runtime-terminal-create-idempotency.test.ts",
|
||||
"pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-attach-subscription.test.ts src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-host-surface-replacement.test.ts src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-stream-reconnect.test.ts src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-expired-pane-recovery.test.ts src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-create-outcome-recovery.test.ts src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-create-handoff.test.ts src/renderer/src/components/terminal-pane/remote-runtime-pty-recovery-state.test.ts src/renderer/src/components/terminal-pane/TerminalRemoteRuntimeReconnectBanner.test.tsx src/shared/remote-runtime-socket-liveness.test.ts src/shared/remote-runtime/control/remote-runtime-shared-control-connection.test.ts src/shared/remote-runtime/control/remote-runtime-shared-control-socket-generation.test.ts src/shared/remote-runtime/remote-runtime-client-error-classification.test.ts src/main/runtime/rpc/remote-runtime-server-heartbeat.test.ts src/main/runtime/rpc/methods/terminal-create-idempotency.test.ts src/main/runtime/orca-runtime-terminal-create-idempotency.test.ts",
|
||||
"pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/hooks/ipc-events/direct-ssh-connect-reply-routing.test.ts src/renderer/src/store/slices/terminals-hydration.test.ts src/renderer/src/store/slices/repos-ssh-host-reconciliation.test.ts src/renderer/src/hooks/direct-ssh-state-routing.test.ts src/renderer/src/components/terminal-pane/pty-connection-spawn-and-startup.test.ts src/renderer/src/components/terminal-pane/terminal-pane-tab-detach.test.ts --reporter=dot",
|
||||
"pnpm exec vitest run --config config/vitest.config.ts src/main/ipc/ssh/ssh.test.ts src/main/ipc/worktrees-ssh-repo-owner-resolution.test.ts src/main/ipc/worktrees-lineage-hydration.test.ts src/main/runtime/public-ssh-state.test.ts src/main/ssh/connection/ssh-connection-manager.test.ts src/main/ssh/connection/ssh-connection.test.ts src/main/ssh/ssh-provider-authority.test.ts src/preload/ssh-authority-forwarding.test.ts src/renderer/src/runtime/runtime-client-events.test.ts src/renderer/src/runtime/runtime-environment-ssh-state.test.ts src/shared/ssh-retained-payload-admission.test.ts --reporter=dot",
|
||||
"pnpm exec electron-vite build --mode e2e",
|
||||
@@ -13631,7 +13631,6 @@
|
||||
"src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-create-handoff.test.ts",
|
||||
"src/renderer/src/components/terminal-pane/remote-runtime-pty-recovery-state.test.ts",
|
||||
"src/renderer/src/components/terminal-pane/TerminalRemoteRuntimeReconnectBanner.test.tsx",
|
||||
"src/renderer/src/components/terminal-pane/terminal-remote-runtime-recovery-ui-state.test.ts",
|
||||
"src/renderer/src/runtime/remote-runtime-session-tabs-inflight.test.ts",
|
||||
"src/renderer/src/runtime/web-session-terminal-handle-events.test.ts",
|
||||
"src/renderer/src/store/slices/terminal-pty-identity-replacement.test.ts",
|
||||
@@ -13753,12 +13752,6 @@
|
||||
"the disconnected state exposes one explicit Reconnect action"
|
||||
]
|
||||
},
|
||||
{
|
||||
"file": "src/renderer/src/components/terminal-pane/terminal-remote-runtime-recovery-ui-state.test.ts",
|
||||
"assertions": [
|
||||
"only actionable recovery phases retain UI state and pane closure removes disconnected state"
|
||||
]
|
||||
},
|
||||
{
|
||||
"file": "src/shared/remote-runtime-socket-liveness.test.ts",
|
||||
"assertions": [
|
||||
@@ -13960,6 +13953,7 @@
|
||||
"runner": "local",
|
||||
"platform": "macos",
|
||||
"command": "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-attach-subscription.test.ts src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-host-surface-replacement.test.ts src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-stream-reconnect.test.ts src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-expired-pane-recovery.test.ts src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-create-outcome-recovery.test.ts src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-create-handoff.test.ts src/renderer/src/components/terminal-pane/remote-runtime-pty-recovery-state.test.ts src/renderer/src/components/terminal-pane/TerminalRemoteRuntimeReconnectBanner.test.tsx src/renderer/src/components/terminal-pane/terminal-remote-runtime-recovery-ui-state.test.ts src/shared/remote-runtime-socket-liveness.test.ts src/shared/remote-runtime/control/remote-runtime-shared-control-connection.test.ts src/shared/remote-runtime/control/remote-runtime-shared-control-socket-generation.test.ts src/shared/remote-runtime/remote-runtime-client-error-classification.test.ts src/main/runtime/rpc/remote-runtime-server-heartbeat.test.ts src/main/runtime/rpc/methods/terminal-create-idempotency.test.ts src/main/runtime/orca-runtime-terminal-create-idempotency.test.ts",
|
||||
"currentCommand": "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-attach-subscription.test.ts src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-host-surface-replacement.test.ts src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-stream-reconnect.test.ts src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-expired-pane-recovery.test.ts src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-create-outcome-recovery.test.ts src/renderer/src/components/terminal-pane/remote-runtime-pty-transport-create-handoff.test.ts src/renderer/src/components/terminal-pane/remote-runtime-pty-recovery-state.test.ts src/renderer/src/components/terminal-pane/TerminalRemoteRuntimeReconnectBanner.test.tsx src/shared/remote-runtime-socket-liveness.test.ts src/shared/remote-runtime/control/remote-runtime-shared-control-connection.test.ts src/shared/remote-runtime/control/remote-runtime-shared-control-socket-generation.test.ts src/shared/remote-runtime/remote-runtime-client-error-classification.test.ts src/main/runtime/rpc/remote-runtime-server-heartbeat.test.ts src/main/runtime/rpc/methods/terminal-create-idempotency.test.ts src/main/runtime/orca-runtime-terminal-create-idempotency.test.ts",
|
||||
"result": "passed",
|
||||
"durationSeconds": 5,
|
||||
"summary": "Eleven fault-injection and recovery-UI files and 127 tests passed, covering suspended heartbeat clocks, stale socket, PTY, and create generations, canonical pre-ready close recovery with one replacement subscription, cold and repeated PTY reattachment, authoritative health, bounded PTY and terminal-create recovery, post-probe timeout clipping, manually retryable create cutoff, accurate capability-probe failures, same-PTY manual reconnect, pane-state cleanup, fatal error deduplication, stable create identity, cross-process PTY adoption, and fail-closed legacy inventory."
|
||||
|
||||
@@ -127,7 +127,6 @@ const NODE_RUNTIME_INCLUDE = [
|
||||
'src/main/foreign-sqlite-readers/**/*.test.ts',
|
||||
'src/main/browser/*cookie*.test.ts',
|
||||
'src/main/browser/cookies/*cookie*.test.ts',
|
||||
'src/main/browser/cookies/sources/browser-cookie-validation.test.ts',
|
||||
'src/main/browser/cookies/sources/browser-cookie-decryption.test.ts',
|
||||
'src/main/browser/cookies/sources/browser-cookie-import-undecryptable.test.ts',
|
||||
'src/main/browser/cookies/sources/browser-cookie-source-partition.test.ts',
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { databaseSameSite } from './browser-cookie-validation'
|
||||
|
||||
describe('databaseSameSite', () => {
|
||||
it.each([
|
||||
{ raw: -1, expected: 'unspecified' },
|
||||
{ raw: 0, expected: 'no_restriction' },
|
||||
{ raw: 1, expected: 'lax' },
|
||||
{ raw: 2, expected: 'strict' },
|
||||
{ raw: 3, expected: 'unspecified' },
|
||||
// Firefox stores SAMESITE_UNSET as 256.
|
||||
{ raw: 256, expected: 'unspecified' },
|
||||
{ raw: 99, expected: 'unspecified' },
|
||||
{ raw: 1.5, expected: 'unspecified' }
|
||||
] as const)('decodes $raw as $expected', ({ raw, expected }) => {
|
||||
expect(databaseSameSite(raw)).toBe(expected)
|
||||
})
|
||||
|
||||
// Nullish and nonnumeric inputs must not become SameSite=None.
|
||||
it.each([
|
||||
{ label: 'null', raw: null },
|
||||
{ label: 'undefined', raw: undefined },
|
||||
{ label: 'NaN', raw: Number.NaN }
|
||||
])('decodes $label as unspecified', ({ raw }) => {
|
||||
expect(databaseSameSite(raw as unknown as number)).toBe('unspecified')
|
||||
})
|
||||
})
|
||||
@@ -1,21 +0,0 @@
|
||||
import { afterEach, expect, it, vi } from 'vitest'
|
||||
|
||||
const reads = vi.hoisted(() => vi.fn(async () => null))
|
||||
vi.mock('../macos-keychain/generic-password', () => ({ readKeychainPassword: reads }))
|
||||
|
||||
import { readActiveClaudeKeychainCredentialsStrict } from './keychain'
|
||||
|
||||
const originalUser = process.env.USER
|
||||
afterEach(() => {
|
||||
process.env.USER = originalUser
|
||||
reads.mockClear()
|
||||
})
|
||||
|
||||
it('reads the claude-code-user item when $USER is not a valid Keychain account (#12857)', async () => {
|
||||
process.env.USER = 'first@example.com'
|
||||
await readActiveClaudeKeychainCredentialsStrict()
|
||||
expect(reads).toHaveBeenCalledWith('Claude Code-credentials', 'claude-code-user')
|
||||
process.env.USER = 'first.last'
|
||||
await readActiveClaudeKeychainCredentialsStrict()
|
||||
expect(reads).toHaveBeenLastCalledWith('Claude Code-credentials', 'first.last')
|
||||
})
|
||||
@@ -1,25 +0,0 @@
|
||||
import type * as NodeFs from 'node:fs'
|
||||
import { join } from 'node:path'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
const { existsSyncMock } = vi.hoisted(() => ({ existsSyncMock: vi.fn((_path: string) => false) }))
|
||||
|
||||
vi.mock('node:fs', async (importOriginal) => ({
|
||||
...(await importOriginal<typeof NodeFs>()),
|
||||
existsSync: existsSyncMock
|
||||
}))
|
||||
|
||||
import { resolveDesktopScriptProviderPath } from './desktop-script-provider-paths'
|
||||
|
||||
describe('resolveDesktopScriptProviderPath', () => {
|
||||
afterEach(() => {
|
||||
existsSyncMock.mockReset()
|
||||
})
|
||||
|
||||
it('finds the runtime orcad ships beside its sidecar when there is no resourcesPath', () => {
|
||||
const bundled = join(__dirname, 'computer-use-linux', 'runtime.py')
|
||||
existsSyncMock.mockImplementation((path) => path === bundled)
|
||||
|
||||
expect(resolveDesktopScriptProviderPath('linux')).toBe(bundled)
|
||||
})
|
||||
})
|
||||
@@ -1,26 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
DEFAULT_GPU_CRASH_FALLBACK_THRESHOLD,
|
||||
DEFAULT_GPU_CRASH_FALLBACK_WINDOW_MS,
|
||||
GpuCrashFallbackTracker
|
||||
} from './gpu-crash-fallback-decision'
|
||||
|
||||
function newTracker(): GpuCrashFallbackTracker {
|
||||
return new GpuCrashFallbackTracker({
|
||||
windowMs: DEFAULT_GPU_CRASH_FALLBACK_WINDOW_MS,
|
||||
threshold: DEFAULT_GPU_CRASH_FALLBACK_THRESHOLD
|
||||
})
|
||||
}
|
||||
|
||||
describe('1.4.190 win32 GPU-child crash cluster', () => {
|
||||
it('engages on the session that did reach three crashes (field launch 51b9e93c)', () => {
|
||||
// Field launch 51b9e93c emitted one crash with two coalesced repeats before engaging.
|
||||
const tracker = newTracker()
|
||||
expect(tracker.recordGpuCrash(3_600_000).shouldEngageFallback).toBe(false)
|
||||
expect(tracker.recordGpuCrash(3_601_000).shouldEngageFallback).toBe(false)
|
||||
expect(tracker.recordGpuCrash(3_601_890)).toEqual({
|
||||
shouldEngageFallback: true,
|
||||
crashesInWindow: 3
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,31 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { ghRepoExecOptions, githubRepoContext } from './github-repository-identity'
|
||||
|
||||
describe('ghRepoExecOptions ghAccount threading', () => {
|
||||
it('keeps ghAccount when cwd is present for local repos', () => {
|
||||
expect(
|
||||
ghRepoExecOptions(
|
||||
githubRepoContext('/repo', null, {
|
||||
wslDistro: 'Ubuntu',
|
||||
ghAccount: { host: 'github.com', user: 'Alice' }
|
||||
})
|
||||
)
|
||||
).toEqual({
|
||||
cwd: '/repo',
|
||||
wslDistro: 'Ubuntu',
|
||||
ghAccount: { host: 'github.com', user: 'Alice' }
|
||||
})
|
||||
})
|
||||
|
||||
it('keeps ghAccount when SSH omits cwd', () => {
|
||||
expect(
|
||||
ghRepoExecOptions(
|
||||
githubRepoContext('/remote/repo', 'ssh-1', {
|
||||
ghAccount: { host: 'github.acme.com', user: 'Bob' }
|
||||
})
|
||||
)
|
||||
).toEqual({
|
||||
ghAccount: { host: 'github.acme.com', user: 'Bob' }
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,23 +0,0 @@
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
vi.mock('../../browser/browser-route-partition-storage-runtime', () => ({
|
||||
clearBrowserRoutePartitionStorageForEnvironment: vi.fn()
|
||||
}))
|
||||
vi.mock('../../browser/browser-route-partition-storage-retirement', () => ({
|
||||
retireBrowserRoutePartitionStorageForEnvironment: vi.fn(async () => undefined)
|
||||
}))
|
||||
|
||||
const { retireRemovedRuntimeEnvironment } = await import('./runtime-environment-removal-cleanup')
|
||||
|
||||
describe('retiring a removed runtime environment', () => {
|
||||
it('forgets the server’s session partition and its layout snapshot', async () => {
|
||||
const store = {
|
||||
removeWorkspaceSessionHost: vi.fn(),
|
||||
getRemoteLayoutSnapshot: vi.fn(),
|
||||
setRemoteLayoutSnapshot: vi.fn()
|
||||
}
|
||||
await retireRemovedRuntimeEnvironment('env 1', vi.fn(), store)
|
||||
expect(store.removeWorkspaceSessionHost).toHaveBeenCalledWith('runtime:env%201')
|
||||
expect(store.setRemoteLayoutSnapshot).toHaveBeenCalledWith('env 1', null, 'now')
|
||||
})
|
||||
})
|
||||
@@ -13,12 +13,6 @@ describe('cancelUnreadResponseBody', () => {
|
||||
expect(cancelled).toBe(true)
|
||||
})
|
||||
|
||||
it('no-ops on a body-less response', async () => {
|
||||
await expect(
|
||||
cancelUnreadResponseBody(new Response(null, { status: 500 }))
|
||||
).resolves.toBeUndefined()
|
||||
})
|
||||
|
||||
it('swallows cancellation failures on a locked stream', async () => {
|
||||
const response = cancelTrackingResponse(500, () => {})
|
||||
response.body?.getReader()
|
||||
|
||||
@@ -13,10 +13,4 @@ describe('line editor ready output scanner', () => {
|
||||
expect(scanForLineEditorReadyOutput(state, sequence.slice(4))).toBe(true)
|
||||
}
|
||||
)
|
||||
|
||||
it('ignores ordinary prompt output', () => {
|
||||
expect(
|
||||
scanForLineEditorReadyOutput(createLineEditorReadyOutputScanState(), 'user@host % ')
|
||||
).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { hostTestAttachParams } from './structured-agent-session-host-test-data'
|
||||
import {
|
||||
pinnedAgentSessionLaunchArgs,
|
||||
pinnedAgentSessionLaunchEnv
|
||||
} from './structured-agent-session-launch-env'
|
||||
|
||||
describe('pinnedAgentSessionLaunchEnv', () => {
|
||||
it('layers the pinned account home over the shell environment', async () => {
|
||||
await expect(
|
||||
pinnedAgentSessionLaunchEnv(
|
||||
async () => ({ EXAMPLE_GATEWAY_TOKEN: 'shell-exported', CODEX_HOME: '/shell/home' }),
|
||||
hostTestAttachParams(null)
|
||||
)
|
||||
).resolves.toEqual({
|
||||
launchEnv: {
|
||||
EXAMPLE_GATEWAY_TOKEN: 'shell-exported',
|
||||
CODEX_HOME: '/home/dev/.codex'
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('copies the host-resolved provider arguments into reservation authority', async () => {
|
||||
await expect(
|
||||
pinnedAgentSessionLaunchArgs(async () => ['--profile', 'review'], hostTestAttachParams(null))
|
||||
).resolves.toEqual({ launchArgs: ['--profile', 'review'] })
|
||||
})
|
||||
})
|
||||
@@ -1,22 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { transcriptFallbackId } from './transcript-fallback-id'
|
||||
|
||||
describe('transcriptFallbackId', () => {
|
||||
it('sorts line positions numerically when timestamps are unavailable', () => {
|
||||
const ids = [10, 2, 100, 9].map((offset) => transcriptFallbackId('/chat.jsonl', offset))
|
||||
|
||||
expect(ids.sort()).toEqual([
|
||||
transcriptFallbackId('/chat.jsonl', 2),
|
||||
transcriptFallbackId('/chat.jsonl', 9),
|
||||
transcriptFallbackId('/chat.jsonl', 10),
|
||||
transcriptFallbackId('/chat.jsonl', 100)
|
||||
])
|
||||
})
|
||||
|
||||
it('sorts later incremental byte ranges after earlier ranges', () => {
|
||||
const earlier = transcriptFallbackId('/chat.jsonl', 99)
|
||||
const later = transcriptFallbackId('/chat.jsonl', 100)
|
||||
|
||||
expect(earlier < later).toBe(true)
|
||||
})
|
||||
})
|
||||
@@ -1,25 +0,0 @@
|
||||
import { expect, it } from 'vitest'
|
||||
import { readDesktopAwayState } from './desktop-away-state'
|
||||
|
||||
it.each([
|
||||
[179, false],
|
||||
[180, true],
|
||||
[181, true]
|
||||
])('checks the three-minute boundary at %s seconds', (idle, away) => {
|
||||
expect(
|
||||
readDesktopAwayState({ getSystemIdleState: () => 'active', getSystemIdleTime: () => idle })
|
||||
).toBe(away)
|
||||
})
|
||||
it('allows immediate delivery when locked and fails open when presence cannot be read', () => {
|
||||
expect(
|
||||
readDesktopAwayState({ getSystemIdleState: () => 'locked', getSystemIdleTime: () => 0 })
|
||||
).toBe(true)
|
||||
expect(
|
||||
readDesktopAwayState({
|
||||
getSystemIdleState: () => {
|
||||
throw new Error('unsupported')
|
||||
},
|
||||
getSystemIdleTime: () => 0
|
||||
})
|
||||
).toBeUndefined()
|
||||
})
|
||||
@@ -1,23 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { ORCAD_MANAGED_ACTIVATION_ROOT_ENV } from '../../shared/orcad-managed-launch-env'
|
||||
import { orcadWsPortOptions } from './orcad-ws-port-policy'
|
||||
|
||||
describe('orcadWsPortOptions', () => {
|
||||
it('leaves the default port and its fallback alone without --port', () => {
|
||||
expect(orcadWsPortOptions(undefined, {})).toEqual({})
|
||||
})
|
||||
|
||||
it('requires an operator --port', () => {
|
||||
expect(orcadWsPortOptions(7001, {})).toEqual({
|
||||
wsPort: 7001,
|
||||
preferPinnedWsPort: true,
|
||||
requirePinnedWsPort: true
|
||||
})
|
||||
})
|
||||
|
||||
it('lets a managed launch fall back when its port is taken', () => {
|
||||
expect(
|
||||
orcadWsPortOptions(6768, { [ORCAD_MANAGED_ACTIVATION_ROOT_ENV]: '/home/u/.orca-remote' })
|
||||
).toEqual({ wsPort: 6768, preferPinnedWsPort: true })
|
||||
})
|
||||
})
|
||||
@@ -1,31 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { removeInheritedNoColor } from './terminal-color-env'
|
||||
|
||||
describe('terminal color env', () => {
|
||||
it('removes inherited color-disable variables from spawned terminal env', () => {
|
||||
const env = {
|
||||
NO_COLOR: '1',
|
||||
FORCE_COLOR: '0',
|
||||
CLICOLOR: '0',
|
||||
TERM: 'xterm-256color'
|
||||
}
|
||||
|
||||
removeInheritedNoColor(env)
|
||||
|
||||
expect(env).toEqual({ TERM: 'xterm-256color' })
|
||||
})
|
||||
|
||||
it('preserves explicit color-enable variables', () => {
|
||||
const env = {
|
||||
FORCE_COLOR: '1',
|
||||
CLICOLOR: '1'
|
||||
}
|
||||
|
||||
removeInheritedNoColor(env)
|
||||
|
||||
expect(env).toEqual({
|
||||
FORCE_COLOR: '1',
|
||||
CLICOLOR: '1'
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,23 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
addLifecycleRejectionMarker,
|
||||
hasLifecycleRejectionMarker
|
||||
} from './lifecycle-rejection-marker'
|
||||
|
||||
describe('lifecycle-rejection-marker', () => {
|
||||
it('adds a marker onto an object payload and recognizes it', () => {
|
||||
const marked = addLifecycleRejectionMarker('{"keep":true}', 'rejected', 'not current')
|
||||
expect(JSON.parse(marked)).toMatchObject({
|
||||
keep: true,
|
||||
_orcaLifecycleRejection: { code: 'rejected', reason: 'not current' }
|
||||
})
|
||||
expect(hasLifecycleRejectionMarker(marked)).toBe(true)
|
||||
})
|
||||
|
||||
it('treats missing, non-object, and incomplete markers as absent', () => {
|
||||
expect(hasLifecycleRejectionMarker(null)).toBe(false)
|
||||
expect(hasLifecycleRejectionMarker('[]')).toBe(false)
|
||||
expect(hasLifecycleRejectionMarker('{"_orcaLifecycleRejection":{"code":1}}')).toBe(false)
|
||||
expect(hasLifecycleRejectionMarker('not-json')).toBe(false)
|
||||
})
|
||||
})
|
||||
@@ -1,26 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { pruneExpiredProvenAbsentLeafPtyVerdicts } from './proven-absent-leaf-pty-verdicts'
|
||||
|
||||
describe('pruneExpiredProvenAbsentLeafPtyVerdicts', () => {
|
||||
it('removes only entries at or past the TTL without a re-probe', () => {
|
||||
const map = new Map<string, number>([
|
||||
['live-dead', 1_000],
|
||||
['still-fresh', 1_400],
|
||||
['exact-expiry', 1_000]
|
||||
])
|
||||
pruneExpiredProvenAbsentLeafPtyVerdicts(map, 1_000 + 15_000, 15_000)
|
||||
expect([...map.keys()]).toEqual(['still-fresh'])
|
||||
})
|
||||
|
||||
it('leaves an empty map alone', () => {
|
||||
const map = new Map<string, number>()
|
||||
pruneExpiredProvenAbsentLeafPtyVerdicts(map, Date.now(), 15_000)
|
||||
expect(map.size).toBe(0)
|
||||
})
|
||||
|
||||
it('clears everything when ttl is non-positive', () => {
|
||||
const map = new Map<string, number>([['a', 1]])
|
||||
pruneExpiredProvenAbsentLeafPtyVerdicts(map, 100, 0)
|
||||
expect(map.size).toBe(0)
|
||||
})
|
||||
})
|
||||
@@ -1,25 +0,0 @@
|
||||
import { expect, it, vi } from 'vitest'
|
||||
import { PushOutcomeCounters } from './push-outcome-counters'
|
||||
it('limits failure logs while retaining category counts', () => {
|
||||
let now = 0
|
||||
const log = vi.spyOn(console, 'warn').mockImplementation(() => {})
|
||||
try {
|
||||
const counters = new PushOutcomeCounters(() => now)
|
||||
counters.record('rejected')
|
||||
counters.record('error')
|
||||
counters.record('error')
|
||||
expect(log).toHaveBeenCalledTimes(1)
|
||||
now += 60_000
|
||||
counters.record('rate_limited')
|
||||
expect(JSON.parse(String(log.mock.calls[1]![0]))).toEqual({
|
||||
event: 'orca_desktop_push_failures',
|
||||
error: 2,
|
||||
rate_limited: 1
|
||||
})
|
||||
counters.record('unreachable')
|
||||
counters.flush()
|
||||
expect(log).toHaveBeenCalledTimes(3)
|
||||
} finally {
|
||||
log.mockRestore()
|
||||
}
|
||||
})
|
||||
@@ -1,24 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import type { Repo } from '../../shared/repo-types'
|
||||
import { RuntimeRepositoryRefQueries } from './runtime-repository-ref-queries'
|
||||
|
||||
const repo: Repo = {
|
||||
id: 'remote-repo',
|
||||
path: '/repo',
|
||||
displayName: 'remote',
|
||||
badgeColor: 'blue',
|
||||
addedAt: 1,
|
||||
connectionId: 'ssh-1'
|
||||
}
|
||||
|
||||
describe('qualified refs in repository searches', () => {
|
||||
it('keeps folder workspaces outside Git searches', async () => {
|
||||
const queries = new RuntimeRepositoryRefQueries({
|
||||
resolveRepo: async () => ({ ...repo, kind: 'folder' })
|
||||
})
|
||||
expect(await queries.search('id:remote-repo', 'feature', 2, false)).toEqual({
|
||||
refs: [],
|
||||
truncated: false
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,25 +0,0 @@
|
||||
/** Report no Git only when spawn never started; other failures reject so remote create dialogs retain unknown instead of false absence. */
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
const { gitExecFileAsyncMock } = vi.hoisted(() => ({ gitExecFileAsyncMock: vi.fn() }))
|
||||
|
||||
vi.mock('../git/runner', () => ({ gitExecFileAsync: gitExecFileAsyncMock }))
|
||||
|
||||
import { RuntimeServerEnvironmentCommands } from './runtime-server-environment-commands'
|
||||
|
||||
describe('RuntimeServerEnvironmentCommands.isGitAvailable', () => {
|
||||
const commands = new RuntimeServerEnvironmentCommands()
|
||||
|
||||
beforeEach(() => {
|
||||
gitExecFileAsyncMock.mockReset()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks()
|
||||
})
|
||||
|
||||
it('answers true when git reports its version', async () => {
|
||||
gitExecFileAsyncMock.mockResolvedValue({ stdout: 'git version 2.25.1\n', stderr: '' })
|
||||
await expect(commands.isGitAvailable()).resolves.toBe(true)
|
||||
})
|
||||
})
|
||||
@@ -1,24 +0,0 @@
|
||||
// A newly registered agent must get launch parity snapshots: the oracle lists its agents by hand.
|
||||
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { expect, it } from 'vitest'
|
||||
import { STRUCTURED_AGENT_RUNTIME_REGISTRATIONS } from './structured-agent-runtime-registrations'
|
||||
|
||||
const SNAPSHOT = new URL(
|
||||
'./__snapshots__/structured-agent-launch-parity.test.ts.snap',
|
||||
import.meta.url
|
||||
)
|
||||
|
||||
it('snapshots the launch of every registered native-chat agent, and of no other', () => {
|
||||
const snapshots = readFileSync(fileURLToPath(SNAPSHOT), 'utf8')
|
||||
const snapshotted = new Set(
|
||||
[...snapshots.matchAll(/^exports\[`native-chat launch parity > (\S+) /gm)].map(
|
||||
([, agent]) => agent
|
||||
)
|
||||
)
|
||||
const registered = STRUCTURED_AGENT_RUNTIME_REGISTRATIONS.map(
|
||||
({ definition }) => definition.agent
|
||||
)
|
||||
expect([...snapshotted].sort()).toEqual([...registered].sort())
|
||||
})
|
||||
@@ -1,30 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
buildPtyTerminalWaitBlockedResult,
|
||||
buildPtyTerminalWaitResult,
|
||||
buildTerminalWaitBlockedResult,
|
||||
buildTerminalWaitResult
|
||||
} from './terminal-wait-results'
|
||||
|
||||
describe('terminal wait results', () => {
|
||||
it('preserves exit provenance for every wait result shape', () => {
|
||||
const terminal = {
|
||||
connected: false,
|
||||
lastExitCode: 0,
|
||||
lastExitCause: { kind: 'operator_close' as const }
|
||||
}
|
||||
const results = [
|
||||
buildTerminalWaitResult('terminal', 'exit', terminal),
|
||||
buildTerminalWaitBlockedResult('terminal', 'exit', terminal, 'agent-approval-prompt'),
|
||||
buildPtyTerminalWaitResult('pty', 'exit', terminal),
|
||||
buildPtyTerminalWaitBlockedResult('pty', 'exit', terminal, 'agent-approval-prompt')
|
||||
]
|
||||
|
||||
expect(results.map((result) => result.exitCause)).toEqual([
|
||||
{ kind: 'operator_close' },
|
||||
{ kind: 'operator_close' },
|
||||
{ kind: 'operator_close' },
|
||||
{ kind: 'operator_close' }
|
||||
])
|
||||
})
|
||||
})
|
||||
@@ -1,24 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import type { SkillScanRoot } from './skill-discovery-sources'
|
||||
import { rootMayContainSourceKind } from './skill-discovery-source-filter'
|
||||
|
||||
const homeRoot: SkillScanRoot = {
|
||||
id: 'home',
|
||||
owner: 'agents',
|
||||
path: '/home/alice/.agents/skills',
|
||||
label: 'Home',
|
||||
sourceKind: 'home',
|
||||
providers: ['agent-skills']
|
||||
}
|
||||
|
||||
describe('rootMayContainSourceKind', () => {
|
||||
it('treats an empty list as no filter', () => {
|
||||
expect(rootMayContainSourceKind(homeRoot, undefined)).toBe(true)
|
||||
expect(rootMayContainSourceKind(homeRoot, [])).toBe(true)
|
||||
})
|
||||
|
||||
it('keeps home roots for bundled classification', () => {
|
||||
expect(rootMayContainSourceKind(homeRoot, ['bundled'])).toBe(true)
|
||||
expect(rootMayContainSourceKind(homeRoot, ['plugin'])).toBe(false)
|
||||
})
|
||||
})
|
||||
@@ -1,27 +0,0 @@
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import {
|
||||
BOOTSTRAP_FATAL_EXIT_GUARD_KEY,
|
||||
removeBootstrapFatalExitGuard
|
||||
} from './bootstrap-fatal-exit-guard'
|
||||
|
||||
type BootstrapFatalExitGlobal = typeof globalThis & {
|
||||
[BOOTSTRAP_FATAL_EXIT_GUARD_KEY]?: () => void
|
||||
}
|
||||
|
||||
const bootstrapGlobal = globalThis as BootstrapFatalExitGlobal
|
||||
|
||||
afterEach(() => {
|
||||
delete bootstrapGlobal[BOOTSTRAP_FATAL_EXIT_GUARD_KEY]
|
||||
})
|
||||
|
||||
describe('bootstrap fatal exit guard', () => {
|
||||
it('removes the generated pre-import guard', () => {
|
||||
const removeGuard = vi.fn()
|
||||
bootstrapGlobal[BOOTSTRAP_FATAL_EXIT_GUARD_KEY] = removeGuard
|
||||
|
||||
removeBootstrapFatalExitGuard()
|
||||
|
||||
expect(removeGuard).toHaveBeenCalledOnce()
|
||||
expect(bootstrapGlobal[BOOTSTRAP_FATAL_EXIT_GUARD_KEY]).toBeUndefined()
|
||||
})
|
||||
})
|
||||
@@ -1,22 +0,0 @@
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
const flush = vi.hoisted(() => vi.fn())
|
||||
|
||||
vi.mock('electron', async () => {
|
||||
const { EventEmitter } = await import('node:events')
|
||||
return { powerMonitor: new EventEmitter() }
|
||||
})
|
||||
vi.mock('../orca-profiles/profile-cloud-session-owner', () => ({
|
||||
flushUnsavedCloudSessions: flush
|
||||
}))
|
||||
|
||||
import { powerMonitor } from 'electron'
|
||||
import { installCloudSessionShutdownFlush } from './cloud-session-shutdown-flush'
|
||||
|
||||
describe('cloud session shutdown flush', () => {
|
||||
it('writes unsaved rotations when the OS shuts down', () => {
|
||||
installCloudSessionShutdownFlush()
|
||||
powerMonitor.emit('shutdown')
|
||||
expect(flush).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
})
|
||||
@@ -1,24 +0,0 @@
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { applyGpuFallbackCommandLineSwitches } from './gpu-fallback-switches'
|
||||
|
||||
describe('GPU fallback command-line switches', () => {
|
||||
it('applies nothing outside Windows', () => {
|
||||
for (const platform of ['darwin', 'linux'] as const) {
|
||||
const appendSwitch = vi.fn()
|
||||
expect(applyGpuFallbackCommandLineSwitches({ appendSwitch }, platform)).toEqual([])
|
||||
expect(appendSwitch).not.toHaveBeenCalled()
|
||||
}
|
||||
})
|
||||
|
||||
// Windows 11/Electron 43.1: disable-gpu leaves one GPU child; in-process-gpu removes it.
|
||||
it('appends exactly the measured switch set to the Electron command line', () => {
|
||||
const appendSwitch = vi.fn()
|
||||
const appliedSwitches = applyGpuFallbackCommandLineSwitches({ appendSwitch }, 'win32')
|
||||
expect(appliedSwitches).toEqual([
|
||||
'disable-gpu',
|
||||
'disable-software-rasterizer',
|
||||
'in-process-gpu'
|
||||
])
|
||||
expect(appendSwitch.mock.calls.map(([name]) => name)).toEqual(appliedSwitches)
|
||||
})
|
||||
})
|
||||
@@ -1,25 +0,0 @@
|
||||
import { expect, it } from 'vitest'
|
||||
import { getAgentModelProbeSpec } from '../../shared/agent-model-probe-spec'
|
||||
import { finalizeModelDiscoveryOutput } from './commit-message-model-discovery-policy'
|
||||
|
||||
const output = JSON.stringify({
|
||||
models: [
|
||||
{
|
||||
provider: 'provider',
|
||||
id: 'exact-model',
|
||||
selector: 'provider/exact-model',
|
||||
name: 'Exact model'
|
||||
}
|
||||
]
|
||||
})
|
||||
it('keeps the OMP configured default out of generic terminal discovery results', () => {
|
||||
const spec = getAgentModelProbeSpec('omp')
|
||||
if (!spec) {
|
||||
throw new Error('Missing OMP probe spec')
|
||||
}
|
||||
expect(finalizeModelDiscoveryOutput(spec, output, '', 0)).toMatchObject({
|
||||
success: true,
|
||||
defaultModelId: 'provider/exact-model',
|
||||
models: [{ id: 'provider/exact-model' }]
|
||||
})
|
||||
})
|
||||
@@ -1,30 +0,0 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
const { recordDurableCrashBreadcrumbMock } = vi.hoisted(() => ({
|
||||
recordDurableCrashBreadcrumbMock: vi.fn()
|
||||
}))
|
||||
|
||||
vi.mock('./crash-reporting/durable-crash-breadcrumb', () => ({
|
||||
recordDurableCrashBreadcrumb: recordDurableCrashBreadcrumbMock
|
||||
}))
|
||||
|
||||
import { recordUpdaterLifecycle } from './updater-lifecycle-diagnostics'
|
||||
|
||||
beforeEach(() => {
|
||||
recordDurableCrashBreadcrumbMock.mockClear()
|
||||
vi.spyOn(console, 'info').mockImplementation(() => {})
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks()
|
||||
})
|
||||
|
||||
describe('recordUpdaterLifecycle', () => {
|
||||
it('records updater events in the durable lifecycle trace', () => {
|
||||
recordUpdaterLifecycle('quit_and_install', { version: '1.2.3' })
|
||||
|
||||
expect(recordDurableCrashBreadcrumbMock).toHaveBeenCalledWith('updater_quit_and_install', {
|
||||
version: '1.2.3'
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,28 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { isMainWindowVisible } from './main-window-visibility'
|
||||
|
||||
describe('main window visibility helpers', () => {
|
||||
it('treats a minimal alive window double as visible', () => {
|
||||
expect(isMainWindowVisible({ isDestroyed: () => false })).toBe(true)
|
||||
})
|
||||
|
||||
it('parks work when the real window is hidden, minimized, destroyed, or missing', () => {
|
||||
expect(
|
||||
isMainWindowVisible({
|
||||
isDestroyed: () => false,
|
||||
isVisible: () => false,
|
||||
isMinimized: () => false
|
||||
})
|
||||
).toBe(false)
|
||||
expect(
|
||||
isMainWindowVisible({
|
||||
isDestroyed: () => false,
|
||||
isVisible: () => true,
|
||||
isMinimized: () => true
|
||||
})
|
||||
).toBe(false)
|
||||
expect(isMainWindowVisible({ isDestroyed: () => true })).toBe(false)
|
||||
expect(isMainWindowVisible(null)).toBe(false)
|
||||
})
|
||||
})
|
||||
@@ -1,25 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
createRendererLaunchFailureBackoff,
|
||||
RENDERER_LAUNCH_FAILURE_RETRY_DELAYS_MS
|
||||
} from './renderer-launch-failure-backoff'
|
||||
|
||||
describe('createRendererLaunchFailureBackoff', () => {
|
||||
it('keeps the 250ms first retry and spreads the rest over about two minutes', () => {
|
||||
expect(RENDERER_LAUNCH_FAILURE_RETRY_DELAYS_MS[0]).toBe(250)
|
||||
const total = RENDERER_LAUNCH_FAILURE_RETRY_DELAYS_MS.reduce((sum, ms) => sum + ms, 0)
|
||||
expect(total).toBeGreaterThanOrEqual(90_000)
|
||||
expect(total).toBeLessThanOrEqual(125_000)
|
||||
})
|
||||
|
||||
it('walks the schedule, then reports it spent until reset', () => {
|
||||
const backoff = createRendererLaunchFailureBackoff([10, 20])
|
||||
expect(backoff.nextDelayMs()).toBe(10)
|
||||
expect(backoff.nextDelayMs()).toBe(20)
|
||||
expect(backoff.nextDelayMs()).toBeNull()
|
||||
expect(backoff.attempts()).toBe(2)
|
||||
backoff.reset()
|
||||
expect(backoff.attempts()).toBe(0)
|
||||
expect(backoff.nextDelayMs()).toBe(10)
|
||||
})
|
||||
})
|
||||
@@ -1,23 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { buildEncodedWslBashCommand } from './wsl-bash-command'
|
||||
|
||||
describe('buildEncodedWslBashCommand', () => {
|
||||
it('wraps Bash scripts without exposing local shell variables to wsl.exe', () => {
|
||||
const command = [
|
||||
'set -euo pipefail',
|
||||
"candidate='/home/alice/.local/share/orca/codex-accounts/a/home'",
|
||||
'candidate_real=$(readlink -f -- "$candidate")',
|
||||
'printf "%s\\n" "$candidate_real"'
|
||||
].join('\n')
|
||||
|
||||
const wrapped = buildEncodedWslBashCommand(command)
|
||||
const encoded = wrapped.match(
|
||||
/^set -o pipefail; printf %s '([^']+)' \| base64 -d \| bash$/
|
||||
)?.[1]
|
||||
|
||||
expect(wrapped).not.toContain('$candidate')
|
||||
expect(wrapped).not.toContain('\n')
|
||||
expect(encoded).toBeTruthy()
|
||||
expect(Buffer.from(encoded as string, 'base64').toString('utf8')).toBe(command)
|
||||
})
|
||||
})
|
||||
@@ -13,8 +13,4 @@ describeOnWindows('resolveWslExecutablePath on a real Windows host', () => {
|
||||
expect(resolved.toLowerCase()).toContain('system32')
|
||||
expect(existsSync(resolved)).toBe(true)
|
||||
})
|
||||
|
||||
it('memoises, so a spawn does not pay a stat', () => {
|
||||
expect(resolveWslExecutablePath()).toBe(resolveWslExecutablePath())
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { shouldPollChromiumErrorPage } from './chromium-error-page-polling'
|
||||
|
||||
describe('shouldPollChromiumErrorPage', () => {
|
||||
it('runs the fallback chrome-error poll only for the active loading browser pane', () => {
|
||||
expect(shouldPollChromiumErrorPage({ isActive: true, loading: true })).toBe(true)
|
||||
expect(shouldPollChromiumErrorPage({ isActive: false, loading: true })).toBe(false)
|
||||
expect(shouldPollChromiumErrorPage({ isActive: true, loading: false })).toBe(false)
|
||||
expect(shouldPollChromiumErrorPage({ isActive: false, loading: false })).toBe(false)
|
||||
})
|
||||
})
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { getRemoteBrowserFrameStyle } from './remote-browser-frame-style'
|
||||
|
||||
describe('getRemoteBrowserFrameStyle', () => {
|
||||
it('fits the frame by the CSS viewport it shows, not by the bitmap size', () => {
|
||||
expect(
|
||||
getRemoteBrowserFrameStyle({
|
||||
imageWidth: 2266,
|
||||
imageHeight: 1309,
|
||||
deviceWidth: 958,
|
||||
deviceHeight: 609
|
||||
})
|
||||
).toEqual({
|
||||
inset: 0,
|
||||
margin: 'auto',
|
||||
width: `min(100cqw, calc(100cqh * ${958 / 609}))`,
|
||||
height: `min(100cqh, calc(100cqw / ${958 / 609}))`,
|
||||
objectFit: 'fill'
|
||||
})
|
||||
})
|
||||
|
||||
it('fills the pane when the frame does not say what it shows', () => {
|
||||
expect(getRemoteBrowserFrameStyle({ imageWidth: 800, imageHeight: 600 })).toEqual({
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
objectFit: 'fill',
|
||||
objectPosition: 'top left'
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,24 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import en from '@/i18n/locales/en.json'
|
||||
import es from '@/i18n/locales/es.json'
|
||||
import ja from '@/i18n/locales/ja.json'
|
||||
import ko from '@/i18n/locales/ko.json'
|
||||
import zh from '@/i18n/locales/zh.json'
|
||||
|
||||
const englishLabels = en.auto.components.dictation.DictationIndicator as Record<string, string>
|
||||
const translatedLabels = { es, ja, ko, zh }
|
||||
|
||||
describe('DictationIndicator localization', () => {
|
||||
it('uses generated localization keys', () => {
|
||||
expect(Object.keys(englishLabels).every((key) => /^[a-f0-9]{10}$/.test(key))).toBe(true)
|
||||
})
|
||||
|
||||
it.each(Object.entries(translatedLabels))('translates every label in %s', (_locale, catalog) => {
|
||||
const labels = catalog.auto.components.dictation.DictationIndicator as Record<string, string>
|
||||
|
||||
for (const [key, englishLabel] of Object.entries(englishLabels)) {
|
||||
expect(labels[key]).toBeTruthy()
|
||||
expect(labels[key]).not.toBe(englishLabel)
|
||||
}
|
||||
})
|
||||
})
|
||||
-34
@@ -1,34 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { getInitialCombinedDiffSectionLoadIndices } from './combined-diff-initial-section-load'
|
||||
|
||||
describe('combined diff initial section load', () => {
|
||||
it('queues the initial section window', () => {
|
||||
expect(
|
||||
getInitialCombinedDiffSectionLoadIndices({
|
||||
sectionCount: 10,
|
||||
loadedIndices: new Set(),
|
||||
maxCount: 4
|
||||
})
|
||||
).toEqual([0, 1, 2, 3])
|
||||
})
|
||||
|
||||
it('skips sections that were restored from cache', () => {
|
||||
expect(
|
||||
getInitialCombinedDiffSectionLoadIndices({
|
||||
sectionCount: 6,
|
||||
loadedIndices: new Set([0, 2, 5]),
|
||||
maxCount: 6
|
||||
})
|
||||
).toEqual([1, 3, 4])
|
||||
})
|
||||
|
||||
it('handles an empty diff', () => {
|
||||
expect(
|
||||
getInitialCombinedDiffSectionLoadIndices({
|
||||
sectionCount: 0,
|
||||
loadedIndices: new Set(),
|
||||
maxCount: 6
|
||||
})
|
||||
).toEqual([])
|
||||
})
|
||||
})
|
||||
@@ -1,27 +0,0 @@
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
vi.mock('@/lib/jira-issue-key-casing', () => ({
|
||||
getJiraIssueKeyCasingSeedOptions: () => ({})
|
||||
}))
|
||||
|
||||
import { buildJiraBranchName } from './jira-issue-workspace-actions'
|
||||
|
||||
describe('buildJiraBranchName', () => {
|
||||
const issue = { key: 'PROJ-123', title: 'Android feature' }
|
||||
|
||||
it('lowercases the whole suggested branch name by default', () => {
|
||||
expect(buildJiraBranchName(issue)).toBe('proj-123-android-feature')
|
||||
expect(buildJiraBranchName(issue, { jiraIssueKeyCasing: 'lowercase' })).toBe(
|
||||
'proj-123-android-feature'
|
||||
)
|
||||
})
|
||||
|
||||
it('keeps the canonical uppercase key when the casing policy preserves it', () => {
|
||||
expect(buildJiraBranchName(issue, { jiraIssueKeyCasing: 'preserve' })).toBe(
|
||||
'PROJ-123-android-feature'
|
||||
)
|
||||
expect(
|
||||
buildJiraBranchName({ key: 'TEAM_ALPHA9-7', title: '' }, { jiraIssueKeyCasing: 'preserve' })
|
||||
).toBe('TEAM_ALPHA9-7')
|
||||
})
|
||||
})
|
||||
@@ -1,23 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { RUNTIME_PROVIDER_SEARCH_QUERY_MAX_BYTES } from '@/runtime/runtime-provider-search-bounds'
|
||||
|
||||
import { getLinearProjectSearchRequestQuery } from './linear-project-search-query'
|
||||
|
||||
describe('getLinearProjectSearchRequestQuery', () => {
|
||||
it('keeps project queries within the provider search budget', () => {
|
||||
expect(getLinearProjectSearchRequestQuery('roadmap')).toBe('roadmap')
|
||||
})
|
||||
|
||||
it('rejects oversized ASCII project queries before provider lookup', () => {
|
||||
const query = 'x'.repeat(RUNTIME_PROVIDER_SEARCH_QUERY_MAX_BYTES + 1)
|
||||
|
||||
expect(getLinearProjectSearchRequestQuery(query)).toBeNull()
|
||||
})
|
||||
|
||||
it('counts UTF-8 bytes rather than UTF-16 code units', () => {
|
||||
const query = 'é'.repeat(RUNTIME_PROVIDER_SEARCH_QUERY_MAX_BYTES / 2 + 1)
|
||||
|
||||
expect(getLinearProjectSearchRequestQuery(query)).toBeNull()
|
||||
})
|
||||
})
|
||||
@@ -1,24 +0,0 @@
|
||||
// @vitest-environment happy-dom
|
||||
|
||||
import '@testing-library/jest-dom/vitest'
|
||||
|
||||
import { cleanup, render, screen } from '@testing-library/react'
|
||||
import { afterEach, expect, it } from 'vitest'
|
||||
import { NativeChatEmptyState } from './NativeChatEmptyState'
|
||||
|
||||
afterEach(cleanup)
|
||||
|
||||
it('keeps pointing the terminal-backed chat back to its terminal when a read fails', () => {
|
||||
render(<NativeChatEmptyState kind="error" />)
|
||||
expect(
|
||||
screen.getByText(
|
||||
'The transcript could not be read. Toggle back to the terminal to keep working.'
|
||||
)
|
||||
).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('shows the host message in place of the terminal-backed default', () => {
|
||||
render(<NativeChatEmptyState kind="error" message="disk full" />)
|
||||
expect(screen.getByText('disk full')).toBeInTheDocument()
|
||||
expect(screen.queryByText(/Toggle back to the terminal/)).toBeNull()
|
||||
})
|
||||
@@ -1,38 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { shouldRenderPetOverlay } from './pet-overlay-visibility'
|
||||
|
||||
describe('shouldRenderPetOverlay', () => {
|
||||
it('does not render before persisted UI hydration even when the feature is enabled', () => {
|
||||
expect(
|
||||
shouldRenderPetOverlay({
|
||||
persistedUIReady: false,
|
||||
petEnabled: true,
|
||||
petVisible: true
|
||||
})
|
||||
).toBe(false)
|
||||
})
|
||||
|
||||
it('renders only after hydration when both pet switches allow it', () => {
|
||||
expect(
|
||||
shouldRenderPetOverlay({
|
||||
persistedUIReady: true,
|
||||
petEnabled: true,
|
||||
petVisible: true
|
||||
})
|
||||
).toBe(true)
|
||||
expect(
|
||||
shouldRenderPetOverlay({
|
||||
persistedUIReady: true,
|
||||
petEnabled: true,
|
||||
petVisible: false
|
||||
})
|
||||
).toBe(false)
|
||||
expect(
|
||||
shouldRenderPetOverlay({
|
||||
persistedUIReady: true,
|
||||
petEnabled: false,
|
||||
petVisible: true
|
||||
})
|
||||
).toBe(false)
|
||||
})
|
||||
})
|
||||
@@ -1,28 +0,0 @@
|
||||
// @vitest-environment happy-dom
|
||||
|
||||
import { renderToStaticMarkup } from 'react-dom/server'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { ChecksPanelUpdatedAtMetadata } from './checks-panel-updated-at-metadata'
|
||||
|
||||
function renderMetadataText(reviewShortLabel: string): string {
|
||||
const container = document.createElement('div')
|
||||
container.innerHTML = renderToStaticMarkup(
|
||||
<ChecksPanelUpdatedAtMetadata
|
||||
reviewShortLabel={reviewShortLabel}
|
||||
updatedAt="2026-06-24T17:30:00.000Z"
|
||||
/>
|
||||
)
|
||||
return container.textContent ?? ''
|
||||
}
|
||||
|
||||
describe('ChecksPanel updated-at metadata', () => {
|
||||
it.each(['PR', 'MR'])(
|
||||
'renders a whitespace boundary between the %s updated label and timestamp',
|
||||
(reviewShortLabel) => {
|
||||
const text = renderMetadataText(reviewShortLabel)
|
||||
|
||||
expect(text).toMatch(new RegExp(`${reviewShortLabel} updated\\s+\\S`))
|
||||
expect(text).not.toMatch(new RegExp(`${reviewShortLabel} updated\\S`))
|
||||
}
|
||||
)
|
||||
})
|
||||
@@ -1,23 +0,0 @@
|
||||
import { expect, it } from 'vitest'
|
||||
import { describeFileExplorerReadError } from './file-explorer-read-error'
|
||||
|
||||
it('drops the IPC wrapper a runtime-host read error arrives with', () => {
|
||||
expect(
|
||||
describeFileExplorerReadError(
|
||||
new Error(
|
||||
"Error invoking remote method 'runtimeEnvironments:call': Error: WSL hosts are turned off in Settings → Experimental."
|
||||
)
|
||||
)
|
||||
).toBe('WSL hosts are turned off in Settings → Experimental.')
|
||||
})
|
||||
|
||||
it('keeps an empty message empty so the read still reads as failed', () => {
|
||||
const empty = new Error('replaced below')
|
||||
empty.message = ''
|
||||
expect(describeFileExplorerReadError(empty)).toBe('')
|
||||
})
|
||||
|
||||
it('keeps a plain error and a non-Error rejection as-is', () => {
|
||||
expect(describeFileExplorerReadError(new Error('Permission denied'))).toBe('Permission denied')
|
||||
expect(describeFileExplorerReadError('EACCES')).toBe('EACCES')
|
||||
})
|
||||
@@ -1,32 +0,0 @@
|
||||
import { renderToStaticMarkup } from 'react-dom/server'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { BrowserUseSkillStep } from './BrowserUseSkillStep'
|
||||
|
||||
const capturedPanel = vi.hoisted(() => ({
|
||||
props: null as null | Record<string, unknown>
|
||||
}))
|
||||
|
||||
vi.mock('./AgentSkillSetupPanel', () => ({
|
||||
AgentSkillSetupPanel: (props: Record<string, unknown>) => {
|
||||
capturedPanel.props = props
|
||||
return <div data-testid="browser-use-skill-step" />
|
||||
}
|
||||
}))
|
||||
|
||||
describe('BrowserUseSkillStep', () => {
|
||||
it('installs the orca-cli skill, which also updates it once installed', () => {
|
||||
renderToStaticMarkup(
|
||||
<BrowserUseSkillStep
|
||||
skillDetected
|
||||
skillLoading={false}
|
||||
skillError={null}
|
||||
onRecheck={vi.fn()}
|
||||
/>
|
||||
)
|
||||
|
||||
expect(capturedPanel.props).toEqual(
|
||||
expect.objectContaining({ skillNames: ['orca-cli'], installed: true })
|
||||
)
|
||||
expect(capturedPanel.props?.installedSkillNames).toBeUndefined()
|
||||
})
|
||||
})
|
||||
@@ -1,23 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { getCodexConfigSyncWarning } from './codex-config-sync-warning'
|
||||
|
||||
const systemConfigPath = '/home/user/.codex/config.toml'
|
||||
|
||||
describe('getCodexConfigSyncWarning', () => {
|
||||
it('stays silent while syncing normally', () => {
|
||||
expect(
|
||||
getCodexConfigSyncWarning({ state: 'synced', reason: null, systemConfigPath })
|
||||
).toBeNull()
|
||||
})
|
||||
|
||||
it('stays silent before the status has loaded', () => {
|
||||
expect(getCodexConfigSyncWarning(null)).toBeNull()
|
||||
expect(getCodexConfigSyncWarning(undefined)).toBeNull()
|
||||
})
|
||||
|
||||
it('surfaces the stall reason so the component can localize it', () => {
|
||||
for (const reason of ['missing-source', 'blank-source', 'unreadable-source'] as const) {
|
||||
expect(getCodexConfigSyncWarning({ state: 'stalled', reason, systemConfigPath })).toBe(reason)
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -1,18 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { getGeneralEditorSearchEntries, getGeneralPaneSearchEntries } from './general-search'
|
||||
import { matchesSettingsSearch } from './settings-search'
|
||||
|
||||
describe('collapse unchanged settings search', () => {
|
||||
it.each(['collapse unchanged', 'collapse', 'hide unchanged', 'fold', 'diff'])(
|
||||
'keeps the setting reachable through both search gates for "%s"',
|
||||
(query) => {
|
||||
const editorEntries = getGeneralEditorSearchEntries()
|
||||
const entry = editorEntries.find((item) => item.title === 'Collapse Unchanged Regions')
|
||||
|
||||
expect(entry).toBeDefined()
|
||||
expect(matchesSettingsSearch(query, entry!)).toBe(true)
|
||||
expect(matchesSettingsSearch(query, editorEntries)).toBe(true)
|
||||
expect(matchesSettingsSearch(query, getGeneralPaneSearchEntries())).toBe(true)
|
||||
}
|
||||
)
|
||||
})
|
||||
@@ -1,21 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { getGeneralNavigationSearchEntries, getGeneralPaneSearchEntries } from './general-search'
|
||||
import { matchesSettingsSearch } from './settings-search'
|
||||
|
||||
describe('preview tab setting search', () => {
|
||||
it.each(['preview', 'reuse', 'italic', 'explorer', 'browsing', 'preview tab'])(
|
||||
'keeps the setting reachable through both search gates for "%s"',
|
||||
(query) => {
|
||||
const navigationEntries = getGeneralNavigationSearchEntries()
|
||||
const entry = navigationEntries.find(
|
||||
(item) => item.title === 'Reuse a preview tab when browsing files'
|
||||
)
|
||||
|
||||
expect(entry).toBeDefined()
|
||||
expect(matchesSettingsSearch(query, entry!)).toBe(true)
|
||||
// The outer Navigation gate must match too or it hides the matching control.
|
||||
expect(matchesSettingsSearch(query, navigationEntries)).toBe(true)
|
||||
expect(matchesSettingsSearch(query, getGeneralPaneSearchEntries())).toBe(true)
|
||||
}
|
||||
)
|
||||
})
|
||||
@@ -1,23 +0,0 @@
|
||||
// @vitest-environment happy-dom
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { isProjectGroupHeaderActionTarget } from './project-group-header-drag-contract'
|
||||
import {
|
||||
isRepoHeaderActionTarget,
|
||||
REPO_HEADER_ACTION_SELECTOR
|
||||
} from './project-header-drag-contract'
|
||||
|
||||
describe('project header action selectors', () => {
|
||||
it('includes the actions overlay for both repo and group helpers', () => {
|
||||
expect(REPO_HEADER_ACTION_SELECTOR).toContain('[data-repo-header-actions]')
|
||||
|
||||
const header = document.createElement('div')
|
||||
const actions = document.createElement('div')
|
||||
actions.setAttribute('data-repo-header-actions', '')
|
||||
header.append(actions)
|
||||
document.body.append(header)
|
||||
|
||||
expect(isRepoHeaderActionTarget(actions, header)).toBe(true)
|
||||
expect(isProjectGroupHeaderActionTarget(actions, header)).toBe(true)
|
||||
})
|
||||
})
|
||||
@@ -1,30 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { getHostHeaderDetail } from './host-header-detail'
|
||||
|
||||
const runtimeRow = { kind: 'runtime', detail: 'Orca server' } as const
|
||||
|
||||
describe('getHostHeaderDetail', () => {
|
||||
it('asks for a re-pair when the host revoked this client', () => {
|
||||
expect(
|
||||
getHostHeaderDetail({
|
||||
...runtimeRow,
|
||||
health: 'blocked',
|
||||
blockedReason: 'access-revoked'
|
||||
})
|
||||
).toEqual({ text: 'Re-pair required', isWarning: true })
|
||||
})
|
||||
|
||||
it('keeps the update label for an incompatible or unlabeled block', () => {
|
||||
expect(
|
||||
getHostHeaderDetail({
|
||||
...runtimeRow,
|
||||
health: 'blocked',
|
||||
blockedReason: 'incompatible'
|
||||
})
|
||||
).toEqual({ text: 'Update required', isWarning: true })
|
||||
expect(getHostHeaderDetail({ ...runtimeRow, health: 'blocked' })).toEqual({
|
||||
text: 'Update required',
|
||||
isWarning: true
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,22 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { getScrollTopToRevealBounds } from './worktree-sidebar-reveal'
|
||||
|
||||
describe('getScrollTopToRevealBounds', () => {
|
||||
const makeContainer = (scrollTop: number, clientHeight: number) =>
|
||||
({
|
||||
scrollTop,
|
||||
clientHeight
|
||||
}) as HTMLElement
|
||||
|
||||
it('scrolls upward to reveal a mounted current workspace card above the viewport', () => {
|
||||
expect(getScrollTopToRevealBounds(makeContainer(100, 200), { start: 60, end: 120 })).toBe(60)
|
||||
})
|
||||
|
||||
it('scrolls downward to reveal a mounted current workspace card below the viewport', () => {
|
||||
expect(getScrollTopToRevealBounds(makeContainer(100, 200), { start: 250, end: 340 })).toBe(140)
|
||||
})
|
||||
|
||||
it('does not scroll when the current workspace card is already fully visible', () => {
|
||||
expect(getScrollTopToRevealBounds(makeContainer(100, 200), { start: 125, end: 260 })).toBeNull()
|
||||
})
|
||||
})
|
||||
@@ -1,19 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { isLongSkillDescription, SKILL_DESCRIPTION_CLAMP_CHARS } from './skill-description-length'
|
||||
|
||||
describe('isLongSkillDescription', () => {
|
||||
it('leaves a description that fits the clamp alone', () => {
|
||||
expect(isLongSkillDescription('Interact with Discord servers.')).toBe(false)
|
||||
expect(isLongSkillDescription('a'.repeat(SKILL_DESCRIPTION_CLAMP_CHARS))).toBe(false)
|
||||
})
|
||||
|
||||
it('offers the toggle once a description runs past the clamp', () => {
|
||||
expect(isLongSkillDescription('a'.repeat(SKILL_DESCRIPTION_CLAMP_CHARS + 1))).toBe(true)
|
||||
})
|
||||
|
||||
it('ignores surrounding whitespace and missing values', () => {
|
||||
expect(isLongSkillDescription(` ${'a'.repeat(20)} `)).toBe(false)
|
||||
expect(isLongSkillDescription(null)).toBe(false)
|
||||
expect(isLongSkillDescription(undefined)).toBe(false)
|
||||
})
|
||||
})
|
||||
@@ -1,24 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { renderToStaticMarkup } from 'react-dom/server'
|
||||
import { MiniMaxIcon } from './icons'
|
||||
|
||||
describe('MiniMaxIcon', () => {
|
||||
it('renders the official MiniMax mark as an image', () => {
|
||||
const markup = renderToStaticMarkup(<MiniMaxIcon size={14} />)
|
||||
expect(markup.startsWith('<img')).toBe(true)
|
||||
expect(markup).toContain('aria-hidden="true"')
|
||||
expect(markup).toContain('width="14"')
|
||||
expect(markup).toContain('height="14"')
|
||||
})
|
||||
|
||||
it('honors a custom size prop', () => {
|
||||
const markup = renderToStaticMarkup(<MiniMaxIcon size={20} />)
|
||||
expect(markup).toContain('width="20"')
|
||||
expect(markup).toContain('height="20"')
|
||||
})
|
||||
|
||||
it('does not render the legacy "M" placeholder text', () => {
|
||||
const markup = renderToStaticMarkup(<MiniMaxIcon size={14} />)
|
||||
expect(markup).not.toContain('>M<')
|
||||
})
|
||||
})
|
||||
@@ -1,22 +0,0 @@
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
vi.mock('@/i18n/i18n', () => ({
|
||||
translate: (_key: string, fallback: string, values?: Record<string, string>) =>
|
||||
fallback.replace('{{value0}}', values?.value0 ?? '')
|
||||
}))
|
||||
|
||||
import { formatBareUsagePercentage, formatUsagePercentageLabel } from './usage-percentage-label'
|
||||
|
||||
describe('formatUsagePercentageLabel', () => {
|
||||
it('formats used and remaining percentages without changing source semantics', () => {
|
||||
expect(formatUsagePercentageLabel(8, 'used')).toBe('8% used')
|
||||
expect(formatUsagePercentageLabel(8, 'remaining')).toBe('92% left')
|
||||
})
|
||||
})
|
||||
|
||||
describe('formatBareUsagePercentage', () => {
|
||||
it('drops the used/left word but keeps the chosen display value', () => {
|
||||
expect(formatBareUsagePercentage(8, 'used')).toBe('8%')
|
||||
expect(formatBareUsagePercentage(8, 'remaining')).toBe('92%')
|
||||
})
|
||||
})
|
||||
@@ -1,24 +0,0 @@
|
||||
import { expect, it } from 'vitest'
|
||||
import {
|
||||
includeSelectedJiraStatuses,
|
||||
orderJiraStatusCatalog
|
||||
} from './task-page-jira-status-catalog'
|
||||
|
||||
it('orders actual workflow statuses by category and name', () => {
|
||||
expect(
|
||||
orderJiraStatusCatalog([
|
||||
{ name: 'Done', categoryKey: 'done' },
|
||||
{ name: 'Ready', categoryKey: 'indeterminate' },
|
||||
{ name: 'Open', categoryKey: 'new' },
|
||||
{ name: 'Blocked', categoryKey: 'indeterminate' }
|
||||
]).map((status) => status.name)
|
||||
).toEqual(['Open', 'Blocked', 'Ready', 'Done'])
|
||||
})
|
||||
it('keeps selected statuses visible even when a new scope has not loaded them', () => {
|
||||
expect(
|
||||
includeSelectedJiraStatuses([{ name: 'Open', categoryKey: 'new' }], ['Open', 'Developed'])
|
||||
).toEqual([
|
||||
{ name: 'Open', categoryKey: 'new' },
|
||||
{ name: 'Developed', categoryKey: 'other' }
|
||||
])
|
||||
})
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
// Use a non-English catalog to expose mixed-language message/chrome regression (#9194).
|
||||
vi.mock('@/i18n/i18n', () => ({
|
||||
translate: (key: string, fallback: string) =>
|
||||
key === 'auto.components.terminal.pane.TerminalErrorToast.remoteTerminalClosed'
|
||||
? '远程终端已关闭。'
|
||||
: fallback
|
||||
}))
|
||||
|
||||
import { humanizeTerminalError } from './TerminalErrorToast'
|
||||
|
||||
describe('remote-closed terminal banner localization', () => {
|
||||
it('translates the remote-closed line instead of pinning it to English', () => {
|
||||
expect(humanizeTerminalError('Remote terminal was closed.')).toBe('远程终端已关闭。')
|
||||
})
|
||||
|
||||
it('translates the line when it is accumulated with other errors', () => {
|
||||
expect(humanizeTerminalError('Paste failed.\nRemote terminal was closed.')).toBe(
|
||||
'Paste failed.\n远程终端已关闭。'
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -1,20 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { terminalLinkClickBehaviorFor } from './terminal-link-click-behavior'
|
||||
|
||||
describe('terminalLinkClickBehaviorFor', () => {
|
||||
it('defaults to actions and preserves legacy profiles', () => {
|
||||
expect(terminalLinkClickBehaviorFor(undefined)).toBe('actions')
|
||||
expect(terminalLinkClickBehaviorFor({ terminalLinkActionPopoverEnabled: true })).toBe('actions')
|
||||
expect(terminalLinkClickBehaviorFor({ terminalLinkActionPopoverEnabled: false })).toBe('none')
|
||||
})
|
||||
|
||||
it('prefers the explicit behavior for new profiles', () => {
|
||||
expect(
|
||||
terminalLinkClickBehaviorFor({
|
||||
terminalLinkActionPopoverEnabled: false,
|
||||
terminalLinkClickBehavior: 'open'
|
||||
})
|
||||
).toBe('open')
|
||||
expect(terminalLinkClickBehaviorFor({ terminalLinkClickBehavior: 'none' })).toBe('none')
|
||||
})
|
||||
})
|
||||
-31
@@ -1,31 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { updateTerminalRemoteRuntimeRecoveryUiState } from './terminal-remote-runtime-recovery-ui-state'
|
||||
|
||||
describe('updateTerminalRemoteRuntimeRecoveryUiState', () => {
|
||||
it('tracks only recovery phases that need pane UI', () => {
|
||||
const recovering = updateTerminalRemoteRuntimeRecoveryUiState({}, 7, {
|
||||
phase: 'recovering',
|
||||
epoch: 2,
|
||||
attempt: 1
|
||||
})
|
||||
|
||||
expect(recovering[7]?.phase).toBe('recovering')
|
||||
expect(
|
||||
updateTerminalRemoteRuntimeRecoveryUiState(recovering, 7, {
|
||||
phase: 'connected',
|
||||
epoch: 2,
|
||||
attempt: 0
|
||||
})
|
||||
).toEqual({})
|
||||
})
|
||||
|
||||
it('drops disconnected state when the owning pane closes', () => {
|
||||
const disconnected = updateTerminalRemoteRuntimeRecoveryUiState({}, 7, {
|
||||
phase: 'disconnected',
|
||||
epoch: 2,
|
||||
attempt: 4
|
||||
})
|
||||
|
||||
expect(updateTerminalRemoteRuntimeRecoveryUiState(disconnected, 7, null)).toEqual({})
|
||||
})
|
||||
})
|
||||
@@ -1,20 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { normalizeAgentStatusEvent } from './normalize-agent-status-event'
|
||||
|
||||
describe('normalizeAgentStatusEvent', () => {
|
||||
it('preserves tool-output provenance for native chat gating', () => {
|
||||
const normalized = normalizeAgentStatusEvent({
|
||||
paneKey: 'tab-1:1',
|
||||
state: 'working',
|
||||
prompt: 'inspect the failure',
|
||||
agentType: 'claude',
|
||||
lastAssistantMessage: 'Exit code 1\nraw output',
|
||||
lastAssistantMessageIsToolOutput: true,
|
||||
connectionId: null,
|
||||
receivedAt: 1,
|
||||
stateStartedAt: 1
|
||||
})
|
||||
|
||||
expect(normalized?.lastAssistantMessageIsToolOutput).toBe(true)
|
||||
})
|
||||
})
|
||||
@@ -1,23 +1,9 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import {
|
||||
isPseudoLocalizationLocale,
|
||||
pseudoLocalizeString,
|
||||
PSEUDO_LOCALIZATION_LOCALE
|
||||
} from './pseudo-localization'
|
||||
import { PSEUDO_LOCALIZATION_LOCALE } from './pseudo-localization'
|
||||
import { i18n, translate } from './i18n'
|
||||
|
||||
describe('pseudo-localization', () => {
|
||||
it('wraps strings for the pseudo locale', () => {
|
||||
expect(pseudoLocalizeString('Language')).toBe('[Language]')
|
||||
expect(pseudoLocalizeString('[Already wrapped]')).toBe('[Already wrapped]')
|
||||
})
|
||||
|
||||
it('recognizes the pseudo locale id', () => {
|
||||
expect(isPseudoLocalizationLocale(PSEUDO_LOCALIZATION_LOCALE)).toBe(true)
|
||||
expect(isPseudoLocalizationLocale('en')).toBe(false)
|
||||
})
|
||||
|
||||
it('applies pseudo wrapping through translate when active', async () => {
|
||||
const previous = i18n.language
|
||||
await i18n.changeLanguage(PSEUDO_LOCALIZATION_LOCALE)
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { registerShellMarkdownAliases } from './register-shell-markdown-aliases'
|
||||
|
||||
function createMonacoMock(aliases: string[] = ['Shell', 'sh']) {
|
||||
return {
|
||||
languages: {
|
||||
getLanguages: vi.fn(() => [{ id: 'shell', aliases }]),
|
||||
register: vi.fn()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
describe('registerShellMarkdownAliases', () => {
|
||||
it('registers bash alongside the built-in shell aliases', () => {
|
||||
const monaco = createMonacoMock()
|
||||
|
||||
registerShellMarkdownAliases(monaco)
|
||||
|
||||
expect(monaco.languages.register).toHaveBeenCalledWith({
|
||||
id: 'shell',
|
||||
aliases: ['Shell', 'sh', 'bash']
|
||||
})
|
||||
})
|
||||
|
||||
it('does not register the alias again when Monaco already exposes it', () => {
|
||||
const monaco = createMonacoMock(['Shell', 'sh', 'Bash'])
|
||||
|
||||
registerShellMarkdownAliases(monaco)
|
||||
|
||||
expect(monaco.languages.register).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
@@ -10,10 +10,4 @@ describe('isXtermInstanceDisposed', () => {
|
||||
terminal.dispose()
|
||||
expect(isXtermInstanceDisposed(terminal)).toBe(true)
|
||||
})
|
||||
|
||||
it('answers false for non-terminal shapes', () => {
|
||||
expect(isXtermInstanceDisposed(null)).toBe(false)
|
||||
expect(isXtermInstanceDisposed(undefined)).toBe(false)
|
||||
expect(isXtermInstanceDisposed({})).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { isSettingsNavigationTarget } from './settings-navigation-types'
|
||||
|
||||
describe('isSettingsNavigationTarget', () => {
|
||||
it('accepts a page-level host scope', () => {
|
||||
expect(
|
||||
isSettingsNavigationTarget({
|
||||
pane: 'accounts',
|
||||
repoId: null,
|
||||
hostScope: { kind: 'environment', environmentId: 'linux-box' }
|
||||
})
|
||||
).toBe(true)
|
||||
expect(
|
||||
isSettingsNavigationTarget({ pane: 'accounts', repoId: null, hostScope: { kind: 'local' } })
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('rejects a malformed host scope', () => {
|
||||
for (const hostScope of ['linux-box', { kind: 'environment' }, { kind: 'ssh' }]) {
|
||||
expect(isSettingsNavigationTarget({ pane: 'accounts', repoId: null, hostScope })).toBe(false)
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -1,18 +0,0 @@
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { applyNativeChatLaunchDraftResolved } from './native-chat-launch-draft-runtime-resolution'
|
||||
|
||||
describe('applyNativeChatLaunchDraftResolved', () => {
|
||||
it('routes the exact generation to the store action', () => {
|
||||
const resolveNativeChatLaunchDraft = vi.fn()
|
||||
|
||||
applyNativeChatLaunchDraftResolved(
|
||||
{ resolveNativeChatLaunchDraft },
|
||||
{ type: 'nativeChatLaunchDraftResolved', tabId: 'tab-1', text: 'seed', createdAt: 7 }
|
||||
)
|
||||
|
||||
expect(resolveNativeChatLaunchDraft).toHaveBeenCalledWith('tab-1', {
|
||||
text: 'seed',
|
||||
createdAt: 7
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,16 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { changedActiveGroups } from './active-group-publication'
|
||||
|
||||
describe('the active groups a graph publication carries', () => {
|
||||
it('names only the workspaces whose group changed', () => {
|
||||
expect(changedActiveGroups({ a: 'g1', b: 'g3' }, { a: 'g1', b: 'g2' })).toEqual({ b: 'g3' })
|
||||
})
|
||||
|
||||
it('names a workspace that no longer shows a group as none', () => {
|
||||
expect(changedActiveGroups({}, { a: 'g1' })).toEqual({ a: null })
|
||||
})
|
||||
|
||||
it('carries nothing when nothing changed', () => {
|
||||
expect(changedActiveGroups({ a: 'g1' }, { a: 'g1' })).toBeUndefined()
|
||||
})
|
||||
})
|
||||
@@ -1,28 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { resolveWorktreeCreateBaseBranch } from './worktree-create-base'
|
||||
|
||||
describe('resolveWorktreeCreateBaseBranch', () => {
|
||||
it('uses an explicit Start-from selection', async () => {
|
||||
await expect(
|
||||
resolveWorktreeCreateBaseBranch({
|
||||
explicitBaseBranch: ' origin/feature '
|
||||
})
|
||||
).resolves.toBe('origin/feature')
|
||||
})
|
||||
|
||||
it('omits repo defaults so backend create owns base selection', async () => {
|
||||
await expect(
|
||||
resolveWorktreeCreateBaseBranch({
|
||||
explicitBaseBranch: undefined
|
||||
})
|
||||
).resolves.toBeUndefined()
|
||||
})
|
||||
|
||||
it('omits blank explicit selections', async () => {
|
||||
await expect(
|
||||
resolveWorktreeCreateBaseBranch({
|
||||
explicitBaseBranch: ' '
|
||||
})
|
||||
).resolves.toBeUndefined()
|
||||
})
|
||||
})
|
||||
@@ -1,30 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { copyOnWriteRecord } from './copy-on-write-record'
|
||||
|
||||
describe('copyOnWriteRecord', () => {
|
||||
it('returns the source untouched when nothing is written', () => {
|
||||
const source = { a: 1 }
|
||||
const record = copyOnWriteRecord(source)
|
||||
record.delete('missing')
|
||||
expect(record.read()).toBe(source)
|
||||
expect(source).toEqual({ a: 1 })
|
||||
})
|
||||
|
||||
it('clones once and never mutates the source', () => {
|
||||
const source = { a: 1, b: 2 }
|
||||
const record = copyOnWriteRecord(source)
|
||||
record.set('c', 3)
|
||||
const afterFirstWrite = record.read()
|
||||
record.delete('a')
|
||||
expect(record.read()).toBe(afterFirstWrite)
|
||||
expect(record.read()).toEqual({ b: 2, c: 3 })
|
||||
expect(source).toEqual({ a: 1, b: 2 })
|
||||
})
|
||||
|
||||
it('deletes a key added after the clone', () => {
|
||||
const record = copyOnWriteRecord<number>({})
|
||||
record.set('a', 1)
|
||||
record.delete('a')
|
||||
expect(record.read()).toEqual({})
|
||||
})
|
||||
})
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
const request = {
|
||||
repoId: 'repo-1',
|
||||
name: 'feature',
|
||||
setupDecision: 'inherit',
|
||||
creationId: 'creation-1'
|
||||
} as const
|
||||
|
||||
async function runtimeParams(switchedOn: boolean): Promise<Record<string, unknown>> {
|
||||
vi.resetModules()
|
||||
vi.doMock('../../../../../../shared/sidebar-inputs-switch', () => ({
|
||||
ORCA_SIDEBAR_INPUTS_IS_MAIN: switchedOn
|
||||
}))
|
||||
const { buildRuntimeWorktreeCreateParams } = await import('./worktree-create-payload')
|
||||
return buildRuntimeWorktreeCreateParams(request, { name: request.name })
|
||||
}
|
||||
|
||||
describe('the creation id sent to a paired server', () => {
|
||||
afterEach(() => {
|
||||
vi.doUnmock('../../../../../../shared/sidebar-inputs-switch')
|
||||
})
|
||||
|
||||
it('rides the request only when main produces the sidebar inputs', async () => {
|
||||
expect(await runtimeParams(true)).toMatchObject({ creationId: 'creation-1' })
|
||||
expect(await runtimeParams(false)).not.toHaveProperty('creationId')
|
||||
})
|
||||
})
|
||||
@@ -1,26 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { omitRecordKeys } from './record-key-omission'
|
||||
|
||||
describe('omitRecordKeys', () => {
|
||||
it('returns the same record when none of the keys are present', () => {
|
||||
const record = { a: 1 }
|
||||
expect(omitRecordKeys(record, ['b', 'c'])).toBe(record)
|
||||
expect(omitRecordKeys(record, new Set<string>())).toBe(record)
|
||||
})
|
||||
|
||||
it('copies once and drops every present key', () => {
|
||||
const record = { a: 1, b: 2, c: 3 }
|
||||
const next = omitRecordKeys(record, new Set(['a', 'c', 'missing']))
|
||||
expect(next).not.toBe(record)
|
||||
expect(next).toEqual({ b: 2 })
|
||||
expect(record).toEqual({ a: 1, b: 2, c: 3 })
|
||||
})
|
||||
|
||||
it('drops a key whose value is undefined', () => {
|
||||
expect(omitRecordKeys({ a: undefined }, ['a'])).toEqual({})
|
||||
})
|
||||
|
||||
it('normalizes a missing record to an empty one, as spread-then-delete did', () => {
|
||||
expect(omitRecordKeys(undefined, ['a'])).toEqual({})
|
||||
})
|
||||
})
|
||||
@@ -1,31 +0,0 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
const callRuntimeResult = vi.hoisted(() => vi.fn())
|
||||
|
||||
vi.mock('./web-runtime-calls', () => ({ callRuntimeResult }))
|
||||
|
||||
import { createGitLabApi } from './web-gitlab-api'
|
||||
|
||||
describe('web GitLab API routing', () => {
|
||||
beforeEach(() => {
|
||||
callRuntimeResult.mockReset().mockResolvedValue(null)
|
||||
})
|
||||
|
||||
it('does not forward the desktop repo-owner guard over runtime RPC', async () => {
|
||||
await createGitLabApi().workItemDetails({
|
||||
repoPath: '/workspace/repo',
|
||||
repoId: 'repo-1',
|
||||
repoOwnerExecutionHostId: 'ssh:ssh-1',
|
||||
iid: 42,
|
||||
type: 'mr'
|
||||
})
|
||||
|
||||
expect(callRuntimeResult).toHaveBeenCalledWith('gitlab.workItemDetails', {
|
||||
repo: 'id:repo-1',
|
||||
repoId: 'repo-1',
|
||||
repoPath: '/workspace/repo',
|
||||
iid: 42,
|
||||
type: 'mr'
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,21 +0,0 @@
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
vi.mock('./preload-api/web-runtime-worktree-catalog', () => ({
|
||||
resolveRuntimeFilePath: vi.fn(async (path: string) => {
|
||||
if (path.includes('missing')) {
|
||||
throw new Error('not found')
|
||||
}
|
||||
return path
|
||||
})
|
||||
}))
|
||||
|
||||
describe('web shell path existence', () => {
|
||||
it('defines pathsExist so withFallback cannot substitute an undefined-returning proxy', async () => {
|
||||
const { createShellApi } = await import('./preload-api/web-shell-api')
|
||||
const { withFallback } = await import('./preload-api/web-fallback-api')
|
||||
const shell = withFallback(createShellApi(), ['shell'])
|
||||
expect(shell.pathsExist).toBeTypeOf('function')
|
||||
const values = await shell.pathsExist?.(['/repo/present.ts', '/repo/missing.ts'])
|
||||
expect(values).toEqual([true, false])
|
||||
})
|
||||
})
|
||||
@@ -1,35 +1,5 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { getServeFlagTypoError, getServeOptionValidationError } from './serve-option-validation'
|
||||
|
||||
const validOptions = {
|
||||
noPairing: false,
|
||||
mobilePairing: false,
|
||||
recipeJson: false,
|
||||
projectRoot: null
|
||||
}
|
||||
|
||||
describe('getServeOptionValidationError', () => {
|
||||
it('accepts compatible options', () => {
|
||||
expect(getServeOptionValidationError(validOptions)).toBeNull()
|
||||
})
|
||||
|
||||
it.each([
|
||||
[{ noPairing: true, mobilePairing: true }, /either --mobile-pairing or --no-pairing/i],
|
||||
[
|
||||
{ recipeJson: true, noPairing: true, projectRoot: '/tmp/repo' },
|
||||
/requires runtime pairing.*--no-pairing/i
|
||||
],
|
||||
[
|
||||
{ recipeJson: true, mobilePairing: true, projectRoot: '/tmp/repo' },
|
||||
/requires runtime pairing.*--mobile-pairing/i
|
||||
],
|
||||
[{ recipeJson: true }, /requires --project-root/i]
|
||||
])('rejects incompatible options', (override, expected) => {
|
||||
expect(
|
||||
getServeOptionValidationError({ ...validOptions, ...override } as typeof validOptions)
|
||||
).toMatch(expected)
|
||||
})
|
||||
})
|
||||
import { getServeFlagTypoError } from './serve-option-validation'
|
||||
|
||||
describe('getServeFlagTypoError', () => {
|
||||
it('accepts exact serve flags and arbitrary Chromium switches', () => {
|
||||
|
||||
@@ -51,12 +51,6 @@ describe('reduceSortSettle', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('settles Manual and mode switches without waiting', () => {
|
||||
for (const modes of [{ sortBy: 'manual' }, { previousSortBy: 'smart' }] as const) {
|
||||
expect(reduceSortSettle(settled, write(modes), 0)).toEqual({ state: settled, settle: true })
|
||||
}
|
||||
})
|
||||
|
||||
it('retires a pending deadline when a write lands settled', () => {
|
||||
const pending = reduceSortSettle(settled, write({}), 0).state
|
||||
const reset = reduceSortSettle(pending, write({ change: null, settled: true }), 10)
|
||||
|
||||
@@ -2,26 +2,10 @@ import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
SKILL_INSTALL_BUSY_FAILURE,
|
||||
SKILL_INSTALL_CANCELLED_FAILURE,
|
||||
SkillInstallFailureCategorySchema,
|
||||
SkillInstallFailureSchema,
|
||||
classifySkillInstallFailureCode
|
||||
SkillInstallFailureSchema
|
||||
} from './skill-install-failure'
|
||||
|
||||
describe('skill install failure contract', () => {
|
||||
it('freezes every V1 failure category', () => {
|
||||
expect(SkillInstallFailureCategorySchema.options).toEqual([
|
||||
'admission',
|
||||
'transport',
|
||||
'archive',
|
||||
'filesystem',
|
||||
'conflict',
|
||||
'recovery',
|
||||
'provider-placement',
|
||||
'compatibility',
|
||||
'cancelled'
|
||||
])
|
||||
})
|
||||
|
||||
it('keeps retryability explicit and rejects unstable codes', () => {
|
||||
expect(SkillInstallFailureSchema.parse(SKILL_INSTALL_BUSY_FAILURE).retryable).toBe(true)
|
||||
expect(SkillInstallFailureSchema.parse(SKILL_INSTALL_CANCELLED_FAILURE).category).toBe(
|
||||
@@ -35,18 +19,4 @@ describe('skill install failure contract', () => {
|
||||
})
|
||||
).toThrow()
|
||||
})
|
||||
|
||||
it.each([
|
||||
['skill-install-workspace-required', 'admission'],
|
||||
['skill-download-transport-failed', 'transport'],
|
||||
['skill-package-tar-truncated', 'archive'],
|
||||
['skill-install-busy', 'filesystem'],
|
||||
['skill-install-conflict-modified', 'conflict'],
|
||||
['skill-install-recovery-conflict', 'recovery'],
|
||||
['skill-placement-create-failed', 'provider-placement'],
|
||||
['skill-install-ssh-update-required', 'compatibility'],
|
||||
['skill-install-cancelled', 'cancelled']
|
||||
] as const)('classifies %s as %s', (code, category) => {
|
||||
expect(classifySkillInstallFailureCode(code)?.category).toBe(category)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -7,16 +7,6 @@ describe('ssh pty id routing', () => {
|
||||
const HOST_ID = toSshExecutionHostId(CONNECTION) // "ssh:ssh-1779863656395-57g1q1"
|
||||
const APP_ID = toAppSshPtyId(CONNECTION, 'pty-3') // "ssh:ssh-1779863656395-57g1q1@@pty-3"
|
||||
|
||||
it('encodes the bare target id into the app pty id', () => {
|
||||
expect(APP_ID).toBe('ssh:ssh-1779863656395-57g1q1@@pty-3')
|
||||
expect(parseAppSshPtyId(APP_ID)).toEqual({ connectionId: CONNECTION, relayPtyId: 'pty-3' })
|
||||
})
|
||||
|
||||
it('round-trips app <-> relay ids for the matching bare connection', () => {
|
||||
expect(toRelaySshPtyId(CONNECTION, APP_ID)).toBe('pty-3')
|
||||
expect(toAppSshPtyId(CONNECTION, APP_ID)).toBe(APP_ID)
|
||||
})
|
||||
|
||||
// Reconnect callers may supply workspace hostId (ssh:<targetId>); it must route identically to the bare target ID.
|
||||
it('accepts the execution-host id form as the same connection', () => {
|
||||
expect(toRelaySshPtyId(HOST_ID, APP_ID)).toBe('pty-3')
|
||||
|
||||
@@ -1,24 +1,9 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
nextSshTargetGeneration,
|
||||
resolveSshTargetGenerationHighWaterMark,
|
||||
sanitizeSshTargetGeneration
|
||||
resolveSshTargetGenerationHighWaterMark
|
||||
} from './ssh-target-generation'
|
||||
|
||||
describe('sanitizeSshTargetGeneration', () => {
|
||||
it.each([0, -1, 1.5, Number.NaN, Number.POSITIVE_INFINITY, '3', null, undefined, {}])(
|
||||
'rejects %p',
|
||||
(value) => {
|
||||
expect(sanitizeSshTargetGeneration(value)).toBeUndefined()
|
||||
}
|
||||
)
|
||||
|
||||
it('accepts issued generations', () => {
|
||||
expect(sanitizeSshTargetGeneration(1)).toBe(1)
|
||||
expect(sanitizeSshTargetGeneration(42)).toBe(42)
|
||||
})
|
||||
})
|
||||
|
||||
describe('resolveSshTargetGenerationHighWaterMark', () => {
|
||||
it('never reissues a generation an automation already captured', () => {
|
||||
// A rolled-back counter cannot reissue an automation’s captured generation.
|
||||
|
||||
@@ -1,38 +1,9 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
isStablePaneId,
|
||||
isTerminalLeafId,
|
||||
makePaneKey,
|
||||
parseLegacyNumericPaneKey,
|
||||
parsePaneKey
|
||||
} from './stable-pane-id'
|
||||
import { makePaneKey, parseLegacyNumericPaneKey, parsePaneKey } from './stable-pane-id'
|
||||
|
||||
const LEAF_ID = '11111111-1111-4111-8111-111111111111'
|
||||
|
||||
describe('stable pane ids', () => {
|
||||
it('recognizes UUID leaf ids as stable pane ids', () => {
|
||||
expect(isStablePaneId(LEAF_ID)).toBe(true)
|
||||
expect(isTerminalLeafId(LEAF_ID)).toBe(true)
|
||||
})
|
||||
|
||||
it('rejects legacy numeric pane ids and malformed UUIDs', () => {
|
||||
for (const value of ['1', 'pane:1', '11111111-1111-6111-8111-111111111111', '']) {
|
||||
expect(isStablePaneId(value)).toBe(false)
|
||||
expect(isTerminalLeafId(value)).toBe(false)
|
||||
}
|
||||
})
|
||||
|
||||
it('builds and parses pane keys using the tab id and UUID leaf id', () => {
|
||||
const paneKey = makePaneKey('tab-1', LEAF_ID)
|
||||
|
||||
expect(paneKey).toBe(`tab-1:${LEAF_ID}`)
|
||||
expect(parsePaneKey(paneKey)).toEqual({
|
||||
tabId: 'tab-1',
|
||||
leafId: LEAF_ID,
|
||||
stablePaneId: LEAF_ID
|
||||
})
|
||||
})
|
||||
|
||||
it('rejects ambiguous tab ids and non-UUID leaf ids when building keys', () => {
|
||||
expect(() => makePaneKey('', LEAF_ID)).toThrow(/tabId/)
|
||||
expect(() => makePaneKey('tab:1', LEAF_ID)).toThrow(/tabId/)
|
||||
|
||||
@@ -1,15 +1,7 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
buildStartupCommandSubmission,
|
||||
isBracketedPasteSafeShell
|
||||
} from './startup-command-submission'
|
||||
import { buildStartupCommandSubmission } from './startup-command-submission'
|
||||
|
||||
describe('buildStartupCommandSubmission', () => {
|
||||
it('submits a single-line command with CR', () => {
|
||||
expect(buildStartupCommandSubmission('claude', { bracketedPasteSafe: true })).toBe('claude\r')
|
||||
expect(buildStartupCommandSubmission('claude', { bracketedPasteSafe: false })).toBe('claude\r')
|
||||
})
|
||||
|
||||
it('replaces a caller-supplied trailing terminator with CR', () => {
|
||||
for (const command of ['claude\n', 'claude\r', 'claude\r\n']) {
|
||||
expect(buildStartupCommandSubmission(command, { bracketedPasteSafe: true })).toBe('claude\r')
|
||||
@@ -30,12 +22,6 @@ describe('buildStartupCommandSubmission', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('keeps the raw path for multiline commands when bracketed paste is unsafe', () => {
|
||||
expect(buildStartupCommandSubmission('echo one\necho two', { bracketedPasteSafe: false })).toBe(
|
||||
'echo one\recho two\r'
|
||||
)
|
||||
})
|
||||
|
||||
// Interior LF is remappable Ctrl+J too; every raw line must submit with CR (#23250).
|
||||
it('submits every line of a raw multiline command with CR', () => {
|
||||
for (const command of ['echo one\necho two', 'echo one\r\necho two', 'echo one\recho two']) {
|
||||
@@ -45,22 +31,3 @@ describe('buildStartupCommandSubmission', () => {
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('isBracketedPasteSafeShell', () => {
|
||||
it('always trusts bash and zsh', () => {
|
||||
for (const shellName of ['bash', 'zsh', 'BASH']) {
|
||||
expect(isBracketedPasteSafeShell({ shellName, waitsForShellReady: false })).toBe(true)
|
||||
}
|
||||
})
|
||||
|
||||
it('trusts fish only behind the shell-ready barrier', () => {
|
||||
expect(isBracketedPasteSafeShell({ shellName: 'fish', waitsForShellReady: true })).toBe(true)
|
||||
expect(isBracketedPasteSafeShell({ shellName: 'fish', waitsForShellReady: false })).toBe(false)
|
||||
})
|
||||
|
||||
it('rejects shells with no known bracketed-paste line editor', () => {
|
||||
for (const shellName of ['sh', 'dash', 'nu', 'powershell.exe', '']) {
|
||||
expect(isBracketedPasteSafeShell({ shellName, waitsForShellReady: true })).toBe(false)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
// Match queued drafts through queuedMessageId, never by comparing draft and submission IDs.
|
||||
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import type { AgentJournalSubmission } from './agent-session-journal-types'
|
||||
import { handedOffQueuedMessageIds } from './structured-agent-session-draft-hand-off'
|
||||
|
||||
function handOff(
|
||||
dispatchState: AgentJournalSubmission['dispatchState'],
|
||||
queuedMessageId?: string
|
||||
): AgentJournalSubmission {
|
||||
return {
|
||||
clientMessageId: 'hand-off',
|
||||
fence: 1,
|
||||
payloadFingerprint: 'fingerprint',
|
||||
dispatchState,
|
||||
providerItemId: null,
|
||||
reason: dispatchState === 'rejected' ? 'refused' : null,
|
||||
submittedAt: 10,
|
||||
resolvedAt: null,
|
||||
...(queuedMessageId !== undefined ? { queuedMessageId } : {})
|
||||
}
|
||||
}
|
||||
|
||||
describe('a queued draft handed off under a fresh submission id', () => {
|
||||
it('is handed off in every dispatch state', () => {
|
||||
for (const state of ['pending', 'accepted', 'rejected', 'unknown'] as const) {
|
||||
expect(handedOffQueuedMessageIds([handOff(state, 'draft')])).toEqual(new Set(['draft']))
|
||||
}
|
||||
})
|
||||
|
||||
it('is never matched by the submission id itself', () => {
|
||||
const ids = handedOffQueuedMessageIds([handOff('pending', 'draft'), handOff('accepted')])
|
||||
expect(ids.has('hand-off')).toBe(false)
|
||||
expect([...ids]).toEqual(['draft'])
|
||||
})
|
||||
})
|
||||
@@ -1,8 +1,5 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
structuredAgentSessionDomainFingerprint,
|
||||
structuredAgentSessionPayloadFingerprint
|
||||
} from './structured-agent-session-mutation'
|
||||
import { structuredAgentSessionPayloadFingerprint } from './structured-agent-session-mutation'
|
||||
import { computeAgentSessionPayloadFingerprint } from './agent-session-mutation-envelope'
|
||||
|
||||
describe('structured agent session client mutations', () => {
|
||||
@@ -33,22 +30,4 @@ describe('structured agent session client mutations', () => {
|
||||
computeAgentSessionPayloadFingerprint(input)
|
||||
)
|
||||
})
|
||||
|
||||
it('preserves the digest when a local fingerprint domain is not an RPC method', () => {
|
||||
const fields = { text: 'hello' }
|
||||
|
||||
expect(
|
||||
structuredAgentSessionDomainFingerprint({
|
||||
domain: 'mobile.agentSession.send.intent',
|
||||
sessionId: 'session-1',
|
||||
fields
|
||||
})
|
||||
).toBe(
|
||||
structuredAgentSessionPayloadFingerprint({
|
||||
method: 'mobile.agentSession.send.intent',
|
||||
sessionId: 'session-1',
|
||||
fields
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import type { AgentJournalSubmission } from './agent-session-journal-types'
|
||||
import { structuredAgentSessionSettledSends } from './structured-agent-session-send-settlement'
|
||||
|
||||
function submission(clientMessageId: string, queuedMessageId?: string): AgentJournalSubmission {
|
||||
return {
|
||||
clientMessageId,
|
||||
...(queuedMessageId ? { queuedMessageId } : {}),
|
||||
fence: 1,
|
||||
payloadFingerprint: clientMessageId,
|
||||
dispatchState: 'accepted',
|
||||
providerItemId: null,
|
||||
reason: null,
|
||||
submittedAt: 1,
|
||||
resolvedAt: 1
|
||||
}
|
||||
}
|
||||
|
||||
describe('structuredAgentSessionSettledSends', () => {
|
||||
it('reads a send as a card while queued or once handed off, else as recorded under its id', () => {
|
||||
const handOff = submission('hand-off', 'was-queued')
|
||||
const sent = submission('sent')
|
||||
const { cards, recorded } = structuredAgentSessionSettledSends([handOff, sent], ['on-queue'])
|
||||
expect([...cards]).toEqual(['on-queue', 'was-queued'])
|
||||
expect(recorded.get('sent')).toBe(sent)
|
||||
expect(recorded.get('hand-off')).toBe(handOff)
|
||||
expect(recorded.has('on-queue')).toBe(false)
|
||||
})
|
||||
})
|
||||
@@ -4,9 +4,7 @@ import './structured-agent-session-unknown-item.spec'
|
||||
import './structured-session-foreground-commands.spec'
|
||||
import './structured-agent-session-first-prompt.spec'
|
||||
import './agent-session-journal-submission-schema.spec'
|
||||
import './structured-agent-session-send-settlement.spec'
|
||||
import './structured-agent-session-projection-sender.spec'
|
||||
import './structured-agent-session-draft-hand-off.spec'
|
||||
import './structured-agent-session-mutation.spec'
|
||||
import './structured-agent-session-option-codec.spec'
|
||||
import './structured-agent-session-first-frame-picker.spec'
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
makeCustomTerminalThemeSelection,
|
||||
normalizeTerminalCustomThemes,
|
||||
normalizeTerminalHexColor,
|
||||
parseCustomTerminalThemeSelection
|
||||
} from './terminal-custom-themes'
|
||||
import { normalizeTerminalCustomThemes } from './terminal-custom-themes'
|
||||
|
||||
describe('terminal custom themes', () => {
|
||||
it('normalizes colors and drops invalid theme records', () => {
|
||||
@@ -80,14 +75,4 @@ describe('terminal custom themes', () => {
|
||||
])
|
||||
).toEqual([])
|
||||
})
|
||||
|
||||
it('round-trips custom selection values', () => {
|
||||
expect(makeCustomTerminalThemeSelection('warp:tokyo-night')).toBe('custom:warp:tokyo-night')
|
||||
expect(parseCustomTerminalThemeSelection('custom:warp:tokyo-night')).toBe('warp:tokyo-night')
|
||||
expect(parseCustomTerminalThemeSelection('Builtin Tango Light')).toBeNull()
|
||||
})
|
||||
|
||||
it('expands short hex colors', () => {
|
||||
expect(normalizeTerminalHexColor('abc')).toBe('#aabbcc')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
classifyTerminalEscapeIntroducer,
|
||||
type TerminalEscapeIntroducer
|
||||
} from './terminal-escape-introducer'
|
||||
|
||||
/** Derive the oracle independently from VT500 byte ranges so implementation branch errors remain visible. */
|
||||
function expected(code: number): TerminalEscapeIntroducer {
|
||||
if ('[' === String.fromCharCode(code)) {
|
||||
return 'csi'
|
||||
}
|
||||
if (']' === String.fromCharCode(code)) {
|
||||
return 'osc'
|
||||
}
|
||||
if ('PX^_'.includes(String.fromCharCode(code))) {
|
||||
return 'string'
|
||||
}
|
||||
if (String.fromCharCode(code) >= ' ' && String.fromCharCode(code) <= '/') {
|
||||
return 'intermediate'
|
||||
}
|
||||
if (code < 0x20 || code === 0x7f) {
|
||||
return 'execute'
|
||||
}
|
||||
return 'final'
|
||||
}
|
||||
|
||||
describe('terminal escape introducer', () => {
|
||||
it('classifies every single-byte introducer the way the VT500 table does', () => {
|
||||
for (let code = 0; code <= 0xff; code += 1) {
|
||||
expect([code, classifyTerminalEscapeIntroducer(code)]).toEqual([code, expected(code)])
|
||||
}
|
||||
})
|
||||
|
||||
it('opens ST-terminated strings for DCS, SOS, PM and APC only', () => {
|
||||
const strings = Array.from({ length: 0x100 }, (_, code) => code).filter(
|
||||
(code) => classifyTerminalEscapeIntroducer(code) === 'string'
|
||||
)
|
||||
expect(strings.map((code) => String.fromCharCode(code))).toEqual(['P', 'X', '^', '_'])
|
||||
})
|
||||
|
||||
it('reads a missing byte (ESC at end of input) as a completed two-byte sequence', () => {
|
||||
// `charCodeAt` past the end is NaN; callers rely on that not becoming csi/osc/string.
|
||||
expect(classifyTerminalEscapeIntroducer(''.charCodeAt(0))).toBe('final')
|
||||
})
|
||||
})
|
||||
@@ -5,13 +5,6 @@ const LEAF = '11111111-1111-4111-8111-111111111111'
|
||||
const OTHER_LEAF = '22222222-2222-4222-8222-222222222222'
|
||||
|
||||
describe('parseTerminalPanePlacement', () => {
|
||||
it('accepts each kind', () => {
|
||||
const split = { kind: 'split', parentLeafId: LEAF, direction: 'vertical' }
|
||||
expect(parseTerminalPanePlacement({ kind: 'new-tab' })).toEqual({ kind: 'new-tab' })
|
||||
expect(parseTerminalPanePlacement(split)).toEqual(split)
|
||||
expect(parseTerminalPanePlacement({ kind: 'root' })).toEqual({ kind: 'root' })
|
||||
})
|
||||
|
||||
it('keeps a new tab row and a split ratio and proposed tree', () => {
|
||||
const newTab = {
|
||||
kind: 'new-tab',
|
||||
|
||||
@@ -1,27 +1,9 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
containsStatefulRendererQuery,
|
||||
extractHiddenStartupRendererQueryData,
|
||||
findCsiFinalByteIndex
|
||||
extractHiddenStartupRendererQueryData
|
||||
} from './terminal-reply-query-extraction'
|
||||
|
||||
describe('findCsiFinalByteIndex', () => {
|
||||
it('returns the final byte of a complete CSI', () => {
|
||||
expect(findCsiFinalByteIndex('\x1b[38;2;1;2;3m', 2)).toBe(12)
|
||||
})
|
||||
|
||||
it('keeps a CSI with no final byte open', () => {
|
||||
expect(findCsiFinalByteIndex('\x1b[38;2;177;1', 2)).toBe(-1)
|
||||
})
|
||||
|
||||
it('ends a CSI where ESC, CAN or SUB aborts it, as xterm does', () => {
|
||||
// Why: without the abort, the `]` of the next OSC read as this CSI's final byte.
|
||||
expect(findCsiFinalByteIndex('\x1b[38;2;177;1\x1b]104;255\x07', 2)).toBe(11)
|
||||
expect(findCsiFinalByteIndex('\x1b[38;2\x18A', 2)).toBe(5)
|
||||
expect(findCsiFinalByteIndex('\x1b[38;2\x1aA', 2)).toBe(5)
|
||||
})
|
||||
})
|
||||
|
||||
describe('reply query extraction after an aborted CSI', () => {
|
||||
it('finds a CPR that follows a carried partial CSI', () => {
|
||||
const result = extractHiddenStartupRendererQueryData('\x1b[6n', '\x1b[38;2;177;1')
|
||||
|
||||
@@ -35,12 +35,4 @@ describe('the JSON size of a terminal payload', () => {
|
||||
Buffer.byteLength(JSON.stringify(mixed), 'utf8')
|
||||
)
|
||||
})
|
||||
|
||||
it('costs an ANSI snapshot far more than its text, which is the whole reason it exists', () => {
|
||||
// Raw text fits the desktop budget while its escaped JSON can exceed the bridge frame cap.
|
||||
const screen = '\u001b[38;5;196m#'.repeat(20_000)
|
||||
expect(terminalStreamJsonByteLength(screen)).toBeGreaterThan(
|
||||
Buffer.byteLength(screen, 'utf8') * 1.4
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { resolveConfiguredTerminalColors, selectTerminalTheme } from './terminal-theme-selection'
|
||||
import { resolveConfiguredTerminalColors } from './terminal-theme-selection'
|
||||
import { TERMINAL_THEME_CATALOG } from './terminal-themes'
|
||||
|
||||
const base = {
|
||||
@@ -43,10 +43,4 @@ describe('the terminal colours a host configures', () => {
|
||||
resolveConfiguredTerminalColors({ ...base, terminalThemeDark: 'no such theme' }, true)
|
||||
).toEqual({ foreground: '#ffffff', background: '#282c34' })
|
||||
})
|
||||
|
||||
it('keeps using the dark theme in light mode when no separate light theme is set', () => {
|
||||
expect(
|
||||
selectTerminalTheme({ ...base, theme: 'light', terminalUseSeparateLightTheme: false }, true)
|
||||
).toEqual({ mode: 'light', useLightVariant: false, themeName: 'Ghostty Default Style Dark' })
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
agentNeutralTerminalWaitBlockedReason,
|
||||
describeTerminalWaitBlockedReason
|
||||
} from './terminal-wait-blocked-reason-legacy-alias'
|
||||
import type { RuntimeTerminalWaitBlockedReason } from './runtime-terminal-contracts'
|
||||
|
||||
describe('agentNeutralTerminalWaitBlockedReason', () => {
|
||||
it.each([
|
||||
['codex-update-prompt', 'agent-update-prompt'],
|
||||
['codex-trust-workspace', 'agent-trust-workspace'],
|
||||
['codex-cwd-prompt', 'agent-cwd-prompt'],
|
||||
['codex-hooks-review-prompt', 'agent-hooks-review-prompt'],
|
||||
['codex-interactive-prompt', 'agent-interactive-prompt']
|
||||
] as const)('renames %s published by an older host to %s', (legacy, neutral) => {
|
||||
expect(agentNeutralTerminalWaitBlockedReason(legacy)).toBe(neutral)
|
||||
})
|
||||
|
||||
// This build still publishes the Codex-specific migration reason; aliasing would rename a live reason, not interpret an old host.
|
||||
it('leaves the agent-specific codex-model-migration-prompt alone', () => {
|
||||
expect(agentNeutralTerminalWaitBlockedReason('codex-model-migration-prompt')).toBeNull()
|
||||
})
|
||||
|
||||
it.each(['agent-approval-prompt', 'agent-trust-workspace', 'agent-hooks-review-prompt'] as const)(
|
||||
'reports no alias for the already-neutral %s',
|
||||
(reason) => {
|
||||
expect(agentNeutralTerminalWaitBlockedReason(reason)).toBeNull()
|
||||
}
|
||||
)
|
||||
|
||||
// Wire tokens are unvalidated; prototype keys must not resolve to Object.prototype functions.
|
||||
it.each(['constructor', 'toString', 'valueOf', '__proto__', 'hasOwnProperty'])(
|
||||
'reports no alias for the prototype key %s',
|
||||
(reason) => {
|
||||
expect(
|
||||
agentNeutralTerminalWaitBlockedReason(reason as RuntimeTerminalWaitBlockedReason)
|
||||
).toBeNull()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
// Share neutral rendering across CLI wait/show and worker/federation startup-blocked receipts.
|
||||
describe('describeTerminalWaitBlockedReason', () => {
|
||||
it('names the neutral spelling beside a legacy token', () => {
|
||||
expect(describeTerminalWaitBlockedReason('codex-trust-workspace')).toBe(
|
||||
'codex-trust-workspace (agent-trust-workspace)'
|
||||
)
|
||||
})
|
||||
|
||||
it.each(['agent-trust-workspace', 'codex-model-migration-prompt'] as const)(
|
||||
'renders %s unannotated',
|
||||
(reason) => {
|
||||
expect(describeTerminalWaitBlockedReason(reason)).toBe(reason)
|
||||
}
|
||||
)
|
||||
})
|
||||
@@ -52,10 +52,4 @@ describe('isTestCodePath', () => {
|
||||
expect(isTestCodePath('src/cli/specs/account.test.ts')).toBe(true)
|
||||
expect(isTestCodePath('spec/models/user_spec.rb')).toBe(true)
|
||||
})
|
||||
|
||||
it('handles a bare filename and an empty path without throwing', () => {
|
||||
expect(isTestCodePath('chip.test.ts')).toBe(true)
|
||||
expect(isTestCodePath('chip.ts')).toBe(false)
|
||||
expect(isTestCodePath('')).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,35 +1,9 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { agentForksConversations, getAgentForkArgv } from './agent-resume-argv'
|
||||
import { buildAgentResumeStartupPlan } from './tui-agent-startup'
|
||||
|
||||
const SESSION = { key: 'session_id', id: 's1' } as const
|
||||
|
||||
describe('agent fork startup', () => {
|
||||
it('forks with each CLI that can copy a conversation', () => {
|
||||
expect(getAgentForkArgv('grok', SESSION)).toEqual(['grok', '--resume', 's1', '--fork-session'])
|
||||
expect(getAgentForkArgv('pi', { ...SESSION, transcriptPath: '/s/a.jsonl' })).toEqual([
|
||||
'pi',
|
||||
'--fork',
|
||||
'/s/a.jsonl'
|
||||
])
|
||||
expect(getAgentForkArgv('pi', SESSION)).toBeNull()
|
||||
expect(getAgentForkArgv('opencode', SESSION)).toBeNull()
|
||||
expect(getAgentForkArgv('omp', SESSION)).toBeNull()
|
||||
expect(agentForksConversations('constructor')).toBe(false)
|
||||
})
|
||||
|
||||
it('forks Claude and Codex', () => {
|
||||
expect(getAgentForkArgv('claude', SESSION)).toEqual([
|
||||
'claude',
|
||||
'--resume',
|
||||
's1',
|
||||
'--fork-session'
|
||||
])
|
||||
expect(getAgentForkArgv('codex', SESSION)).toEqual(['codex', 'fork', 's1'])
|
||||
expect(getAgentForkArgv('gemini', SESSION)).toBeNull()
|
||||
expect(getAgentForkArgv('claude', { key: 'conversation_id', id: 's1' })).toBeNull()
|
||||
})
|
||||
|
||||
it('builds a Claude fork plan that replaces a stored resume selector', () => {
|
||||
const plan = buildAgentResumeStartupPlan({
|
||||
agent: 'claude',
|
||||
|
||||
@@ -21,18 +21,6 @@ function planFor(agent: TuiAgent) {
|
||||
|
||||
describe('the prompt-transport predicate against the plan it predicts', () => {
|
||||
// An empty agent list would make the contract vacuously pass.
|
||||
it('covers every configured agent', () => {
|
||||
expect(ALL_AGENTS.length).toBeGreaterThan(30)
|
||||
expect(ALL_AGENTS).toContain('claude')
|
||||
expect(ALL_AGENTS).toContain('aider')
|
||||
})
|
||||
|
||||
it.each(ALL_AGENTS)('agrees with the built plan for %s', (agent) => {
|
||||
const plan = planFor(agent)
|
||||
expect(plan).not.toBeNull()
|
||||
// The pre-launch predicate must agree with the built plan’s null followupPrompt.
|
||||
expect(plan!.followupPrompt === null).toBe(agentPromptRidesLaunchCommand(agent))
|
||||
})
|
||||
|
||||
it('puts the prompt in the launch command exactly when it says it does', () => {
|
||||
for (const agent of ALL_AGENTS) {
|
||||
@@ -50,13 +38,4 @@ describe('the prompt-transport predicate against the plan it predicts', () => {
|
||||
expect(carried, `${agent} claims its launch command carries the prompt`).toBe(true)
|
||||
}
|
||||
})
|
||||
|
||||
it('splits the table into both halves, so neither branch is untested', () => {
|
||||
const folded = ALL_AGENTS.filter(agentPromptRidesLaunchCommand)
|
||||
const written = ALL_AGENTS.filter((agent) => !agentPromptRidesLaunchCommand(agent))
|
||||
expect(folded.length).toBeGreaterThan(0)
|
||||
expect(written.length).toBeGreaterThan(0)
|
||||
expect(folded).toContain('claude')
|
||||
expect(written).toContain('aider')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import type { TuiAgent } from './tui-agent'
|
||||
import { isTuiAgent, TUI_AGENT_CONFIG } from './tui-agent-config'
|
||||
import { getTuiAgentRestSignal, type TuiAgentRestSignal } from './tui-agent-rest-signal'
|
||||
|
||||
// Exhaustive Record forces each new agent’s evidence choice into review.
|
||||
const EXPECTED_REST_SIGNALS: Record<TuiAgent, TuiAgentRestSignal> = {
|
||||
dsh: 'hook-done',
|
||||
codex: 'synthetic-title',
|
||||
cursor: 'synthetic-title',
|
||||
pi: 'synthetic-title',
|
||||
omp: 'synthetic-title',
|
||||
droid: 'synthetic-title',
|
||||
hermes: 'synthetic-title',
|
||||
devin: 'synthetic-title',
|
||||
zcode: 'synthetic-title',
|
||||
claude: 'title',
|
||||
'claude-agent-teams': 'title',
|
||||
openclaude: 'title',
|
||||
opencode: 'title',
|
||||
opencode2: 'title',
|
||||
'mimo-code': 'title',
|
||||
gemini: 'title',
|
||||
antigravity: 'title',
|
||||
// Jcode has a classified identity title; hook-done is trusted only for DSH’s enabled profile.
|
||||
jcode: 'title',
|
||||
aider: 'title',
|
||||
openclaw: 'title',
|
||||
copilot: 'title',
|
||||
grok: 'title',
|
||||
muse: 'ready-body',
|
||||
qoder: 'ready-body',
|
||||
'qoder-cn': 'ready-body',
|
||||
codebuddy: 'none',
|
||||
autohand: 'none',
|
||||
ante: 'none',
|
||||
trae: 'none',
|
||||
'prime-agent': 'none',
|
||||
goose: 'none',
|
||||
amp: 'none',
|
||||
kilo: 'none',
|
||||
kiro: 'none',
|
||||
crush: 'none',
|
||||
aug: 'none',
|
||||
cline: 'none',
|
||||
codebuff: 'none',
|
||||
freebuff: 'none',
|
||||
'command-code': 'none',
|
||||
continue: 'none',
|
||||
kimi: 'none',
|
||||
'mistral-vibe': 'none',
|
||||
'qwen-code': 'none',
|
||||
rovo: 'none'
|
||||
}
|
||||
|
||||
describe('getTuiAgentRestSignal', () => {
|
||||
it('declares a rest signal for every launchable agent', () => {
|
||||
const derived = Object.fromEntries(
|
||||
Object.keys(TUI_AGENT_CONFIG)
|
||||
.filter(isTuiAgent)
|
||||
.map((agent) => [agent, getTuiAgentRestSignal(agent)])
|
||||
)
|
||||
expect(derived).toEqual(EXPECTED_REST_SIGNALS)
|
||||
})
|
||||
})
|
||||
@@ -1,68 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
isWindowsSignatureCheckUnavailableFailure,
|
||||
isWindowsSignatureMismatchFailure
|
||||
} from './updater-windows-signature-check'
|
||||
|
||||
describe('isWindowsSignatureCheckUnavailableFailure', () => {
|
||||
it('matches the PowerShell command failure shape (timeout / non-zero exit)', () => {
|
||||
expect(
|
||||
isWindowsSignatureCheckUnavailableFailure(
|
||||
'Command failed: set "PSModulePath=" & chcp 65001 >NUL & powershell.exe -NoProfile ' +
|
||||
'-NonInteractive -InputFormat None -Command "Get-AuthenticodeSignature -LiteralPath ' +
|
||||
"'C:\\Users\\u\\AppData\\Local\\orca-updater\\pending\\orca-windows-setup.exe' | " +
|
||||
'ConvertTo-Json -Compress"'
|
||||
)
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('matches the stderr failure shape', () => {
|
||||
expect(
|
||||
isWindowsSignatureCheckUnavailableFailure(
|
||||
'Cannot execute Get-AuthenticodeSignature, stderr: Access is denied. ' +
|
||||
'Failing signature validation due to unknown stderr.'
|
||||
)
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('does not match a genuine signature mismatch', () => {
|
||||
expect(
|
||||
isWindowsSignatureCheckUnavailableFailure(
|
||||
'New version 1.4.144 is not signed by the application owner: ' +
|
||||
'publisherNames: SignPath Foundation, raw info: {"Status": 0}'
|
||||
)
|
||||
).toBe(false)
|
||||
})
|
||||
|
||||
it('does not match unrelated update errors', () => {
|
||||
expect(isWindowsSignatureCheckUnavailableFailure('net::ERR_HTTP2_PROTOCOL_ERROR')).toBe(false)
|
||||
expect(
|
||||
isWindowsSignatureCheckUnavailableFailure('Cannot find channel "latest.yml" (404)')
|
||||
).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('isWindowsSignatureMismatchFailure', () => {
|
||||
it('matches the wrong-publisher integrity failure', () => {
|
||||
expect(
|
||||
isWindowsSignatureMismatchFailure(
|
||||
'New version 1.4.144 is not signed by the application owner: ' +
|
||||
'publisherNames: SignPath Foundation, raw info: {"Status": 0}'
|
||||
)
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('is mutually exclusive with the check-unavailable classifier', () => {
|
||||
const mismatch = 'New version 1.4.144 is not signed by the application owner: publisherNames: X'
|
||||
expect(isWindowsSignatureMismatchFailure(mismatch)).toBe(true)
|
||||
expect(isWindowsSignatureCheckUnavailableFailure(mismatch)).toBe(false)
|
||||
|
||||
const blocked = 'Command failed: … Get-AuthenticodeSignature -LiteralPath …'
|
||||
expect(isWindowsSignatureCheckUnavailableFailure(blocked)).toBe(true)
|
||||
expect(isWindowsSignatureMismatchFailure(blocked)).toBe(false)
|
||||
})
|
||||
|
||||
it('does not match unrelated errors', () => {
|
||||
expect(isWindowsSignatureMismatchFailure('net::ERR_HTTP2_PROTOCOL_ERROR')).toBe(false)
|
||||
})
|
||||
})
|
||||
@@ -10,16 +10,6 @@ function decodePayload(command: string): string {
|
||||
}
|
||||
|
||||
describe('buildWindowsCmdRunnerDelayedLaunchCommand', () => {
|
||||
it('spells no -ExecutionPolicy switch', () => {
|
||||
const command = buildWindowsCmdRunnerDelayedLaunchCommand('C:\\work\\setup.cmd')
|
||||
const switches = command.replace(/ -EncodedCommand \S+$/, '')
|
||||
|
||||
// EncodedCommand is not policy-gated (only File is); Bypass adds an EDR signal without changing execution.
|
||||
expect(switches).not.toMatch(/-ExecutionPolicy/i)
|
||||
expect(switches).not.toMatch(/Bypass/i)
|
||||
expect(switches).toBe('powershell.exe -NoProfile -NonInteractive')
|
||||
})
|
||||
|
||||
it('keeps the base64 that shields the runner path from the pane shell', () => {
|
||||
// This command is typed into a pane shell; base64 must protect cmd metacharacters in its path.
|
||||
const command = buildWindowsCmdRunnerDelayedLaunchCommand('C:\\work (x86)\\se&tup.cmd')
|
||||
|
||||
@@ -47,14 +47,4 @@ describe('stdioForWindowsInteractiveChild', () => {
|
||||
expect(stdio).toEqual([11, process.stderr, 'inherit'])
|
||||
dispose()
|
||||
})
|
||||
|
||||
it('preserves inherited output outside JSON mode', () => {
|
||||
const { stdio, dispose } = stdioForWindowsInteractiveChild(false, {
|
||||
platform: 'win32',
|
||||
openSync: vi.fn(() => 11),
|
||||
closeSync: vi.fn()
|
||||
})
|
||||
expect(stdio).toEqual([11, 'inherit', 'inherit'])
|
||||
dispose()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
import { expect, it, vi } from 'vitest'
|
||||
import { formatWorkerTranscriptMessage } from './worker-transcript-text'
|
||||
import type { NativeChatMessage } from './native-chat-types'
|
||||
|
||||
it('does not shift the remaining twin list for each matching roster', () => {
|
||||
const blocks: NativeChatMessage['blocks'] = Array.from({ length: 1000 }, (_, index) => ({
|
||||
type: 'subagent-group',
|
||||
groupId: `g-${index}`,
|
||||
agents: [{ id: 'child', label: 'task', state: 'working' }]
|
||||
}))
|
||||
blocks.push(
|
||||
...Array.from({ length: 1000 }, () => ({
|
||||
type: 'text' as const,
|
||||
text: 'Kicked off 1 subagent'
|
||||
}))
|
||||
)
|
||||
const original = Array.prototype.splice
|
||||
let shifted = 0
|
||||
const spy = vi.spyOn(Array.prototype, 'splice').mockImplementation(function (
|
||||
this: unknown[],
|
||||
...args: [number, number, ...unknown[]]
|
||||
) {
|
||||
if (this[0] === 'Kicked off 1 subagent') {
|
||||
shifted += this.length - args[0] - args[1]
|
||||
}
|
||||
return original.apply(this, args)
|
||||
})
|
||||
let output: string
|
||||
try {
|
||||
output = formatWorkerTranscriptMessage({
|
||||
id: 'm',
|
||||
role: 'assistant',
|
||||
timestamp: 1,
|
||||
source: 'transcript',
|
||||
blocks
|
||||
})
|
||||
} finally {
|
||||
spy.mockRestore()
|
||||
}
|
||||
expect(output!).toBe(`[assistant] ${Array(1000).fill('Kicked off 1 subagent').join('\n')}`)
|
||||
expect(shifted).toBe(0)
|
||||
})
|
||||
@@ -14,28 +14,6 @@ function pr(number: number, repository?: string): WorkspaceAttachment {
|
||||
afterEach(() => vi.unstubAllGlobals())
|
||||
|
||||
describe('workspace attachment normalization', () => {
|
||||
it('scales URL parsing with references instead of comparing unrelated identities', () => {
|
||||
const NativeURL = URL
|
||||
let parses = 0
|
||||
vi.stubGlobal(
|
||||
'URL',
|
||||
class extends NativeURL {
|
||||
constructor(...args: ConstructorParameters<typeof NativeURL>) {
|
||||
super(...args)
|
||||
parses++
|
||||
}
|
||||
}
|
||||
)
|
||||
const counts = [23, 230].map((count) => {
|
||||
const items = Array.from({ length: count }, (_, index) => pr(index + 1, 'repo'))
|
||||
parses = 0
|
||||
expect(normalizeWorkspaceAttachments(items)).toEqual(items)
|
||||
return parses
|
||||
})
|
||||
expect(counts[0]).toBeGreaterThan(0)
|
||||
expect(counts[1]).toBeLessThanOrEqual(counts[0] * 11)
|
||||
})
|
||||
|
||||
it('preserves global order, enrichment chains and ambiguous sources across identity groups', () => {
|
||||
const origin = { kind: 'observed', tabId: 'terminal' } as const
|
||||
const second = pr(2, 'repo')
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import type { TabGroupLayoutNode } from '../tab-types'
|
||||
import {
|
||||
findLayoutSiblingOnSplitSide,
|
||||
isPaneColumnSplitDropNoOp
|
||||
} from './pane-column-split-drop-no-op'
|
||||
|
||||
function horizontalSplit(firstGroupId: string, secondGroupId: string): TabGroupLayoutNode {
|
||||
return {
|
||||
type: 'split',
|
||||
direction: 'horizontal',
|
||||
ratio: 0.5,
|
||||
first: { type: 'leaf', groupId: firstGroupId },
|
||||
second: { type: 'leaf', groupId: secondGroupId }
|
||||
}
|
||||
}
|
||||
|
||||
describe('findLayoutSiblingOnSplitSide', () => {
|
||||
const layout = horizontalSplit('group-left', 'group-right')
|
||||
|
||||
it('finds the right sibling when splitting the left column to the right', () => {
|
||||
expect(findLayoutSiblingOnSplitSide(layout, 'group-left', 'right')).toBe('group-right')
|
||||
})
|
||||
|
||||
it('finds the left sibling when splitting the right column to the left', () => {
|
||||
expect(findLayoutSiblingOnSplitSide(layout, 'group-right', 'left')).toBe('group-left')
|
||||
})
|
||||
|
||||
it('returns null for split directions that would not target the adjacent sibling', () => {
|
||||
expect(findLayoutSiblingOnSplitSide(layout, 'group-left', 'left')).toBeNull()
|
||||
expect(findLayoutSiblingOnSplitSide(layout, 'group-right', 'right')).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe('isPaneColumnSplitDropNoOp', () => {
|
||||
const layout = horizontalSplit('group-left', 'group-right')
|
||||
|
||||
it('treats splitting the only tab in a group onto itself as a no-op', () => {
|
||||
expect(
|
||||
isPaneColumnSplitDropNoOp({
|
||||
sourceGroupId: 'group-left',
|
||||
targetGroupId: 'group-left',
|
||||
splitDirection: 'right',
|
||||
sourceTabCount: 1,
|
||||
layout
|
||||
})
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('treats dragging the only tab onto the adjacent sibling edge as a no-op', () => {
|
||||
expect(
|
||||
isPaneColumnSplitDropNoOp({
|
||||
sourceGroupId: 'group-right',
|
||||
targetGroupId: 'group-left',
|
||||
splitDirection: 'right',
|
||||
sourceTabCount: 1,
|
||||
layout
|
||||
})
|
||||
).toBe(true)
|
||||
expect(
|
||||
isPaneColumnSplitDropNoOp({
|
||||
sourceGroupId: 'group-left',
|
||||
targetGroupId: 'group-right',
|
||||
splitDirection: 'left',
|
||||
sourceTabCount: 1,
|
||||
layout
|
||||
})
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('allows splits when the source group would still have other tabs', () => {
|
||||
expect(
|
||||
isPaneColumnSplitDropNoOp({
|
||||
sourceGroupId: 'group-right',
|
||||
targetGroupId: 'group-left',
|
||||
splitDirection: 'right',
|
||||
sourceTabCount: 2,
|
||||
layout
|
||||
})
|
||||
).toBe(false)
|
||||
})
|
||||
})
|
||||
@@ -22,24 +22,6 @@ describe('normalizeBrowserHistoryEntries', () => {
|
||||
expect(normalized[0]?.url).toBe('https://example.com/499')
|
||||
expect(normalized.at(-1)?.url).toBe('https://example.com/300')
|
||||
})
|
||||
it('stops reading URLs once enough unique recent entries have been retained', () => {
|
||||
let reads = 0
|
||||
const history = Array.from({ length: 10_000 }, (_, index) => ({
|
||||
get url() {
|
||||
reads++
|
||||
return `https://example.com/${index}`
|
||||
},
|
||||
normalizedUrl: `https://example.com/${index}`,
|
||||
title: `Example ${index}`,
|
||||
lastVisitedAt: index,
|
||||
visitCount: 1
|
||||
}))
|
||||
const normalized = normalizeBrowserHistoryEntries(history)
|
||||
expect(reads).toBeLessThanOrEqual(400)
|
||||
expect(normalized).toHaveLength(200)
|
||||
expect(normalized[0]).toBe(history[9999])
|
||||
expect(normalized[199]).toBe(history[9800])
|
||||
})
|
||||
|
||||
it('preserves the session on repeated normalization while still repairing and deduplicating history', () => {
|
||||
const entry = {
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
getWorktreeCardDetails,
|
||||
getWorktreeCardModeProperties,
|
||||
getWorktreeCardModeUpdates,
|
||||
isDefaultedCompactWorktreeCardProperties,
|
||||
normalizeWorktreeCardProperties
|
||||
} from './card-properties'
|
||||
|
||||
describe('worktree card properties', () => {
|
||||
it('normalizes fixed and legacy properties while preserving selected properties', () => {
|
||||
expect(normalizeWorktreeCardProperties(['ci', 'branch', 'pr', 'automation', 'unread'])).toEqual(
|
||||
['status', 'unread', 'ci', 'branch', 'pr', 'automation']
|
||||
)
|
||||
})
|
||||
|
||||
it('returns combined mode update payloads', () => {
|
||||
expect(getWorktreeCardModeUpdates('Compact')).toEqual({
|
||||
settings: { compactWorktreeCards: true },
|
||||
ui: {
|
||||
worktreeCardProperties: getWorktreeCardModeProperties('Compact'),
|
||||
_worktreeCardModeDefaulted: true
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('recognizes only exact defaulted Compact presets', () => {
|
||||
expect(isDefaultedCompactWorktreeCardProperties(['status'])).toBe(true)
|
||||
expect(isDefaultedCompactWorktreeCardProperties(['status', 'unread'])).toBe(true)
|
||||
expect(isDefaultedCompactWorktreeCardProperties(['status', 'automation'])).toBe(true)
|
||||
expect(isDefaultedCompactWorktreeCardProperties(['status', 'unread', 'automation'])).toBe(true)
|
||||
expect(isDefaultedCompactWorktreeCardProperties(['automation', 'status'])).toBe(false)
|
||||
expect(isDefaultedCompactWorktreeCardProperties(['status', 'automation', 'pr'])).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('getWorktreeCardDetails', () => {
|
||||
it('shows each chosen property, and compact cards leave out the host and agent rows', () => {
|
||||
const chosen = ['pr', 'host', 'inline-agents'] as const
|
||||
expect(
|
||||
getWorktreeCardDetails(chosen, { compactWorktreeCards: false, newCardStyle: false })
|
||||
).toMatchObject({
|
||||
compactCards: false,
|
||||
repoInTitle: false,
|
||||
metaRow: true,
|
||||
pr: true,
|
||||
host: true,
|
||||
inlineAgents: true,
|
||||
comment: false
|
||||
})
|
||||
expect(
|
||||
getWorktreeCardDetails(chosen, { compactWorktreeCards: true, newCardStyle: false })
|
||||
).toMatchObject({
|
||||
compactCards: true,
|
||||
repoInTitle: true,
|
||||
metaRow: false,
|
||||
pr: true,
|
||||
host: false,
|
||||
inlineAgents: false
|
||||
})
|
||||
})
|
||||
|
||||
it('lets the new card style override the Compact layout', () => {
|
||||
expect(
|
||||
getWorktreeCardDetails(['host', 'inline-agents'], {
|
||||
compactWorktreeCards: true,
|
||||
newCardStyle: true
|
||||
})
|
||||
).toMatchObject({
|
||||
compactCards: false,
|
||||
repoInTitle: true,
|
||||
metaRow: true,
|
||||
host: true,
|
||||
inlineAgents: true
|
||||
})
|
||||
})
|
||||
})
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user