test: remove 140 low-value tests (#39644)

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
OrcaWin
2026-11-07 17:40:01 -08:00
committed by GitHub
co-authored by OrcaWin
parent 62cb2dde86
commit e833bb703c
67 changed files with 46 additions and 3580 deletions
-1
View File
@@ -717,7 +717,6 @@
1 test-no-wall-clock:src/renderer/src/components/sidebar/workspace-card/surface/WorktreeCard.single-reference-details.test.tsx
1 test-no-wall-clock:src/renderer/src/components/sidebar/worktree-list/listing/WorktreeList.lineage-agent-expansion-coupling.test.tsx
2 test-no-wall-clock:src/renderer/src/components/task-page/jira/task-page-jira-cache-selectors.test.ts
1 test-no-wall-clock:src/renderer/src/components/terminal-pane/titles/cache-timer-seeding.test.ts
2 test-no-wall-clock:src/renderer/src/components/terminal-pane/pty-connection/emulation/fish-color-scheme-child-stdin.node-pty.test.ts
2 test-no-wall-clock:src/renderer/src/components/terminal-pane/snapshots/remote-snapshot-alt-screen-host-chain.test.ts
1 test-no-wall-clock:src/renderer/src/components/terminal/initial-terminal-structured-launch.test.tsx
File diff suppressed because one or more lines are too long
@@ -3953,7 +3953,6 @@
"src/main/runtime/integrations/linear/runtime-linear-write-lifetime.test.ts": 512,
"src/main/runtime/workspaces/creation/runtime-local-worktree-create-pr-conflict.test.ts": 462,
"src/main/runtime/workspaces/creation/runtime-local-worktree-create.test.ts": 693,
"src/main/runtime/workspaces/startup/runtime-local-worktree-terminal-startup-layout-owner.test.ts": 76,
"src/main/runtime/workspaces/startup/runtime-local-worktree-terminal-startup.test.ts": 79,
"src/main/runtime/workspaces/metadata/runtime-managed-worktree-metadata-sweep.test.ts": 1812,
"src/main/runtime/workspaces/metadata/runtime-managed-worktree-metadata-wsl-move.test.ts": 1812,
@@ -4165,8 +4164,6 @@
"src/main/runtime/workspace-session-membership-scaling.test.ts": 72,
"src/main/runtime/workspaces/persisted-surfaces/live-terminal-placement-before-initial-tab.test.ts": 6111,
"src/main/runtime/workspaces/persisted-surfaces/sweep-terminal-placement.test.ts": 12352,
"src/main/runtime/workspaces/sleep/remote-workspace-terminal-sleep.test.ts": 46,
"src/main/runtime/workspaces/workspace-host-notifications.test.ts": 4332,
"src/main/runtime/workspaces/lifecycle/workspace-lifecycle-queue.test.ts": 116,
"src/main/runtime/workspaces/sleep/workspace-sleep-execution.test.ts": 110,
"src/main/runtime/workspaces/startup/worktree-agent-first-surface.test.ts": 5816,
@@ -4183,11 +4180,9 @@
"src/main/runtime/workspaces/sleep/worktree-terminal-sleep-host-twin.test.ts": 4345,
"src/main/runtime/wsl-distro-loss-exit-retention.test.ts": 5519,
"src/main/runtime/wsl-guest-drive-mountinfo.test.ts": 38,
"src/main/runtime/wsl-held-pane-start-layout-owner.test.ts": 3969,
"src/main/runtime/wsl-host-agent-launch-paths.test.ts": 4079,
"src/main/runtime/wsl-host-agent-terminal-launch.test.ts": 850,
"src/main/runtime/wsl-host-windows-drive-backstop.test.ts": 868,
"src/main/runtime/wsl-host-windows-drive-guard.test.ts": 107,
"src/main/runtime/wsl-moved-project-chat-runtime.test.ts": 5094,
"src/main/updater/serve-update-handoff.test.ts": 62,
"src/main/server/serve-readiness.test.ts": 52,
@@ -4313,7 +4308,6 @@
"src/main/speech/speech-model-download-response.test.ts": 44,
"src/main/speech/stt-offline-audio-chunker.test.ts": 233,
"src/main/speech/stt-service.test.ts": 143,
"src/main/speech/stt-worker-paths.test.ts": 47,
"src/main/speech/stt-worker.test.ts": 33,
"src/main/sqlite/node-sqlite-reader-api.test.ts": 62,
"src/main/sqlite/sqlite-read-failure.test.ts": 81,
@@ -4365,7 +4359,6 @@
"src/main/ssh/legacy-relay/legacy-relay-bridge-relay-restart.test.ts": 98,
"src/main/ssh/legacy-relay/legacy-relay-bridge-restart-stop.test.ts": 3114,
"src/main/ssh/legacy-relay/legacy-relay-bridge-runtime-exit.test.ts": 3411,
"src/main/ssh/legacy-relay/legacy-relay-bridge-teardown.test.ts": 3075,
"src/main/ssh/legacy-relay/legacy-relay-bridge-timers.test.ts": 41,
"src/main/ssh/legacy-relay/legacy-relay-cli-call-limits.test.ts": 7977,
"src/main/ssh/legacy-relay/legacy-relay-cli-legacy-records.test.ts": 6234,
@@ -4680,7 +4673,6 @@
"src/main/window/clipboard-agent-session-attachment-paste.test.ts": 585,
"src/main/window/clipboard-copied-file-paths.test.ts": 58,
"src/main/window/clipboard-dashboard-popout-access.test.ts": 1361,
"src/main/window/clipboard-image-thumbnail.test.ts": 50,
"src/main/window/clipboard-ipc-handlers.test.ts": 496,
"src/main/window/clipboard-macos-image-paste.test.ts": 1087,
"src/main/window/clipboard-publisher-executable.test.ts": 62,
@@ -6130,7 +6122,6 @@
"src/renderer/src/components/terminal-pane/titles/TerminalPaneHeaderOverlay.test.tsx": 1104,
"src/renderer/src/components/terminal-pane/pty-transport/remote-runtime/recovery/TerminalPaneRecoveryPortals.test.tsx": 670,
"src/renderer/src/components/terminal-pane/pty-transport/remote-runtime/recovery/TerminalRemoteRuntimeReconnectBanner.test.tsx": 812,
"src/renderer/src/components/terminal-pane/session-restore/spawn-deferral/TerminalSleepingPaneOverlay.test.tsx": 407,
"src/renderer/src/components/terminal-pane/pty-transport/recovery/ssh/TerminalSshReconnectOverlay.test.tsx": 2354,
"src/renderer/src/components/terminal-pane/account-restart/detached/account-restart-queue-progress.test.ts": 1988,
"src/renderer/src/components/terminal-pane/agent-completion/agent-completion-coordinator-attention-dispatch.test.ts": 301,
@@ -6147,7 +6138,6 @@
"src/renderer/src/components/terminal-pane/agent-status/input-inference/agent-interrupt-inference.test.ts": 146,
"src/renderer/src/components/terminal-pane/pty-connection/startup/agent-pre-ready-typed-draft.test.ts": 1195,
"src/renderer/src/components/terminal-pane/agent-completion/process-inspection/agent-process-inspection-queue-rejection-containment.test.ts": 56,
"src/renderer/src/components/terminal-pane/titles/cache-timer-seeding.test.ts": 108,
"src/renderer/src/components/terminal-pane/account-restart/detached/claude-detached-pane-restart.test.ts": 1938,
"src/renderer/src/components/terminal-pane/provider-notices/claude-diff-panel/claude-diff-panel-notice-banner.test.tsx": 1986,
"src/renderer/src/components/terminal-pane/notifications/claude-side-question-completion.test.ts": 2305,
@@ -6179,7 +6169,6 @@
"src/renderer/src/components/terminal-pane/pty-connection/agent-identity/foreground-process/pane-foreground-agent-tracker.test.ts": 486,
"src/renderer/src/components/terminal-pane/pty-connection/agent-identity/foreground-process/pane-foreground-inspect-observation-identity.test.ts": 226,
"src/renderer/src/components/terminal-pane/pty-connection/agent-identity/foreground-process/pane-foreground-process-exit-retire.test.ts": 329,
"src/renderer/src/components/terminal-pane/titles/pane-title-overlay-rects.test.ts": 58,
"src/renderer/src/components/sidebar/workspace-card/activity/structured-chat-activity-status-live.test.tsx": 4146,
"src/renderer/src/components/terminal-pane/parking/output-watchers/parked-terminal-byte-watcher.test.ts": 2837,
"src/renderer/src/components/terminal-pane/parking/output-watchers/parked-terminal-command-status.test.ts": 352,
@@ -6281,7 +6270,6 @@
"src/renderer/src/components/terminal-pane/pty-connection/geometry/remote-reconnect-unimaged-pane-redraw.test.ts": 3122,
"src/renderer/src/components/terminal-pane/pty-transport/remote-runtime/remote-runtime-connect-failure-recovery.test.ts": 989,
"src/renderer/src/components/terminal-pane/pty-transport/remote-runtime/remote-runtime-disconnected-input-grace-retry.test.ts": 1366,
"src/renderer/src/components/terminal-pane/pty-transport/remote-runtime/input/remote-runtime-disconnected-input-grace.test.ts": 41,
"src/renderer/src/components/terminal-pane/pty-transport/remote-runtime/remote-runtime-error-surface-dismissal.test.ts": 956,
"src/renderer/src/components/terminal-pane/pty-transport/remote-runtime/input/remote-runtime-input-journal.test.ts": 69,
"src/renderer/src/components/terminal-pane/pty-transport/remote-runtime/remote-runtime-outage-toast-flood-and-stuck-reconnect.test.ts": 1269,
@@ -6334,9 +6322,7 @@
"src/renderer/src/components/terminal-pane/snapshots/remote-snapshot-history-seam.test.ts": 2093,
"src/renderer/src/components/terminal-pane/snapshots/replay-guard.test.ts": 371,
"src/renderer/src/components/terminal-pane/layout/split/resolve-split-cwd.test.ts": 56,
"src/renderer/src/components/terminal-pane/snapshots/restored-snapshot-coverage.test.ts": 44,
"src/renderer/src/components/terminal-pane/pty-transport/ipc/scan22-ssh-new-tab-disposed-spawn-kill.repro.test.ts": 577,
"src/renderer/src/components/terminal-pane/snapshots/shutdown-buffer-captures.test.ts": 48,
"src/renderer/src/components/terminal-pane/viewport/split-right-white-screen.test.ts": 53,
"src/renderer/src/components/terminal-pane/viewport/visibility/stale-document-visibility.test.ts": 88,
"src/renderer/src/components/terminal-pane/input/paste/terminal-agent-paste-bracketing.test.ts": 329,
@@ -6404,7 +6390,6 @@
"src/renderer/src/components/terminal-pane/layout/reconciliation/terminal-pane-frame-reconciliation.test.ts": 903,
"src/renderer/src/components/terminal-pane/host-state/terminal-pane-host-state-memo.test.ts": 4534,
"src/renderer/src/components/terminal-pane/mounting/terminal-pane-mount-bootstrap.test.ts": 1723,
"src/renderer/src/components/terminal-pane/render-health/recovery/terminal-pane-recovery-host-rebind-cap.test.ts": 826,
"src/renderer/src/components/terminal-pane/render-health/recovery/terminal-pane-recovery-unsettled-fallback.test.ts": 831,
"src/renderer/src/components/terminal-pane/render-health/recovery/terminal-pane-recovery.test.ts": 949,
"src/renderer/src/components/terminal-pane/layout/split/terminal-pane-split-request-routing.test.ts": 2015,
@@ -6427,7 +6412,6 @@
"src/renderer/src/components/terminal-pane/snapshots/terminal-restore-sgr-latch.test.ts": 175,
"src/shared/terminal/viewport/terminal-restored-viewport.test.ts": 46,
"src/renderer/src/components/terminal-pane/terminal-resume-escape-gap.test.ts": 102,
"src/renderer/src/components/terminal-pane/viewport/terminal-selection-fit-guard.test.ts": 42,
"src/renderer/src/components/terminal-pane/snapshots/terminal-shutdown-layout-capture.test.ts": 197,
"src/renderer/src/components/terminal-pane/pty-transport/output/terminal-side-effect-facts-handler.test.ts": 80,
"src/renderer/src/components/terminal-pane/snapshots/terminal-snapshot-replay-paint.test.ts": 499,
@@ -6685,7 +6669,6 @@
"src/renderer/src/lib/agent-session/launch/new-tab/agent-launch-caller-permissions.test.ts": 1267,
"src/renderer/src/lib/agent-session/launch/new-tab/agent-launch-caller-prompt-transport.test.ts": 1326,
"src/renderer/src/lib/agent-session/launch/new-tab/agent-launch-caller-routing.test.ts": 1266,
"src/renderer/src/lib/agent-session/launch/pane/agent-launch-pane-start.test.ts": 55,
"src/renderer/src/lib/agent-session/launch/pane/agent-launch-pane-verdict-application.test.ts": 1869,
"src/renderer/src/lib/agent-session/launch/routing/agent-launch-route-input.test.ts": 1659,
"src/renderer/src/lib/agent-session/launch/routing/agent-launch-route-registered-agents.test.ts": 1749,
@@ -6734,7 +6717,6 @@
"src/renderer/src/lib/automation-session-reuse.test.ts": 70,
"src/renderer/src/lib/automation-terminal-ownership.test.ts": 1795,
"src/renderer/src/lib/bounded-generation-map.test.ts": 35,
"src/renderer/src/lib/browser/cookies/browser-cookie-import-error-copy.test.ts": 370,
"src/renderer/src/lib/browser/history/browser-history-match.test.ts": 328,
"src/renderer/src/lib/browser/navigation/browser-link-destination.test.ts": 1839,
"src/renderer/src/lib/browser/navigation/browser-link-open-layout-owner.test.ts": 3133,
@@ -6781,7 +6763,6 @@
"src/renderer/src/lib/feedback-image-attachments.test.ts": 557,
"src/renderer/src/lib/file-preview-link-open.test.ts": 2311,
"src/renderer/src/lib/file-search-selection.test.ts": 51,
"src/renderer/src/lib/file-type-icons.test.ts": 147,
"src/renderer/src/lib/find-query-bounds.test.ts": 45,
"src/renderer/src/lib/finished-agent-resume-resurrection.test.ts": 1872,
"src/renderer/src/lib/fix-checks-agent-launch.test.ts": 730,
@@ -6966,7 +6947,6 @@
"src/renderer/src/lib/sidebar-ended-creations.test.ts": 1889,
"src/renderer/src/lib/sidebar-pending-layer.test.ts": 338,
"src/renderer/src/lib/sidebar-shadow/workspace-activity-shadow.test.ts": 1728,
"src/renderer/src/lib/sidebar-worktree-activation.test.ts": 212,
"src/renderer/src/lib/simulator-launch-coordination.test.ts": 43,
"src/renderer/src/lib/simulator-palette-search.test.ts": 515,
"src/renderer/src/lib/simulator-pane-shutdown-scheduler.test.ts": 1679,
@@ -7055,7 +7035,6 @@
"src/renderer/src/runtime/divider-ratio-refusal.test.ts": 2212,
"src/renderer/src/runtime/editor-buffer-dirty-readers.test.ts": 1892,
"src/renderer/src/runtime/file-explorer-delete-owner-provenance.test.ts": 1878,
"src/renderer/src/runtime/hosted-terminals/mounted-surfaces/focus-runtime-terminal-surface-readiness.test.ts": 329,
"src/renderer/src/runtime/host-model-catalog-snapshots.test.ts": 2348,
"src/renderer/src/runtime/host-session-mirror-hydration-frame-ordering.test.tsx": 2469,
"src/renderer/src/runtime/host-session-mirror-settle-receipt-frames.test.tsx": 2130,
@@ -7097,7 +7076,6 @@
"src/renderer/src/runtime/jira/runtime-jira-image-client.test.ts": 214,
"src/renderer/src/runtime/jira/runtime-jira-payload-stream.test.ts": 192,
"src/renderer/src/runtime/runtime-layout-client.test.ts": 165,
"src/renderer/src/runtime/runtime-legacy-recent-freshness.test.ts": 453,
"src/renderer/src/runtime/files/reads/runtime-paged-file-read.test.ts": 1705,
"src/renderer/src/runtime/runtime-quick-open-version-compatibility.test.ts": 451,
"src/renderer/src/runtime/rpc/calls/runtime-rpc-client.test.ts": 342,
@@ -7466,7 +7444,6 @@
"src/renderer/src/store/slices/worktrees/listing/detected-worktree-meta.test.ts": 318,
"src/renderer/src/store/slices/worktrees/listing/stale-listing-purges-created-worktree.test.ts": 1828,
"src/renderer/src/store/slices/worktrees/metadata/worktree-meta-persist.test.ts": 361,
"src/renderer/src/store/slices/worktrees/session/worktree-no-op-notifications.test.ts": 1129,
"src/renderer/src/store/slices/worktrees/teardown/host-qualified-removal-refusal.test.ts": 518,
"src/renderer/src/store/slices/worktrees/teardown/remove-worktree-shared-records.test.ts": 2086,
"src/renderer/src/store/slices/worktrees/teardown/removed-workspace-unsaved-files.test.ts": 2481,
@@ -1,128 +0,0 @@
import { createElement, type ReactElement } from 'react'
import { QueryClientProvider } from '@tanstack/react-query'
import { act, create, type ReactTestRenderer } from 'react-test-renderer'
import { afterEach, describe, expect, it, vi } from 'vitest'
vi.mock('../../../src/shared/sidebar-inputs-switch', () => ({ ORCA_SIDEBAR_INPUTS_IS_MAIN: true }))
vi.mock('react-native', () => ({
AppState: { currentState: 'active', addEventListener: () => ({ remove: () => {} }) }
}))
vi.mock('expo-router', () => ({ useFocusEffect: () => {} }))
vi.mock('../storage/preferences', () => ({ savePinnedIds: async () => {} }))
import { FakeSession } from '../transport/mobile-endpoint-supervisor-test-fakes'
import type { ConnectionState, RpcResponse } from '../transport/types'
import { useHostScreenState } from './use-host-screen-state'
import { useHostViewSettings } from './use-host-view-settings'
import { useHostWorktreeCatalog } from './use-host-worktree-catalog'
import { createOrcaQueryClient } from '../query/orca-query-client'
function reply(result: unknown): RpcResponse {
return { id: 'reply', ok: true, result, _meta: { runtimeId: 'runtime' } }
}
// Lose the first attempt of each write to exercise replay against the desktop’s recent sort and expanded state.
function desktop() {
const client = new FakeSession('connected')
const lost = new Set<string>()
const writes: { method: string; params: unknown }[] = []
client.sendRequest.mockImplementation(async (method: string, params: unknown) => {
if (method === 'ui.set' || method === 'ui.setCollapsed') {
writes.push({ method, params })
if (!lost.has(method)) {
lost.add(method)
throw new Error('client_disconnected')
}
return reply(method === 'ui.set' ? { ui: {} } : { collapsed: true, applied: true })
}
if (method === 'ui.get') {
return reply({ ui: { sortBy: 'recent', collapsedGroups: [] } })
}
if (method === 'worktree.ps') {
return reply({ worktrees: [], snapshotId: 's1' })
}
return reply({})
})
return { client, writes }
}
const SIDEBAR_FEED = { refresh: () => {}, changed: () => {}, onInstalled: () => () => {} }
// Keep callbacks stable as in the screen; refresh lifecycle depends on their identities.
const NO_CAPABILITIES: string[] = []
let renderer: ReactTestRenderer | null = null
afterEach(() => {
act(() => renderer?.unmount())
renderer = null
})
async function mountScreen(client: FakeSession) {
const held: {
view?: ReturnType<typeof useHostViewSettings>
state?: ReturnType<typeof useHostScreenState>
} = {}
function Probe({ connState }: { connState: ConnectionState }): null {
const state = useHostScreenState('host-1')
held.state = state
state.clientRef.current = client
const view = useHostViewSettings({ client, connState, state })
held.view = view
useHostWorktreeCatalog({
client,
connState,
embedded: true,
hostCapabilities: NO_CAPABILITIES,
hostId: 'host-1',
sidebarFeed: SIDEBAR_FEED,
state,
syncViewSettingsFromDesktop: view.syncViewSettingsFromDesktop
})
return null
}
const queryClient = createOrcaQueryClient()
const screen = (connState: ConnectionState): ReactElement =>
createElement(QueryClientProvider, { client: queryClient }, createElement(Probe, { connState }))
await act(async () => {
renderer = create(screen('connected'))
})
return {
view: () => held.view!,
state: () => held.state!,
connect: async (connState: ConnectionState) => {
await act(async () => {
renderer!.update(screen(connState))
})
}
}
}
describe('Workspace options changed while the connection drops', () => {
it('resends a sort and a collapse the desktop never got once the phone reconnects', async () => {
const { client, writes } = desktop()
const screen = await mountScreen(client)
await act(async () => {
screen.view().handleSortChange('name')
screen.view().toggleCollapsed('repo:orca')
})
expect(screen.view().pendingViewChanges.pending).toHaveLength(2)
await screen.connect('reconnecting')
await screen.connect('connected')
expect(writes).toEqual([
{ method: 'ui.set', params: expect.objectContaining({ sortBy: 'name' }) },
{
method: 'ui.setCollapsed',
params: expect.objectContaining({ key: 'repo:orca', collapsed: true })
},
{ method: 'ui.set', params: expect.objectContaining({ sortBy: 'name' }) },
{
method: 'ui.setCollapsed',
params: expect.objectContaining({ key: 'repo:orca', collapsed: true })
}
])
expect(screen.state().view).toMatchObject({ sortBy: 'name', collapsedGroups: ['repo:orca'] })
expect(screen.view().pendingViewChanges.pending).toEqual([])
})
})
@@ -1,99 +0,0 @@
import { createElement } from 'react'
import { QueryClientProvider } from '@tanstack/react-query'
import { act, create } from 'react-test-renderer'
import { describe, expect, it, vi } from 'vitest'
// The desktop's server-list feed loads react-native; its timer never runs without a subscriber.
vi.mock('react-native', () => ({ AppState: {} }))
vi.mock('expo-router', () => ({ useFocusEffect: () => {} }))
vi.mock('../storage/preferences', () => ({ savePinnedIds: async () => {} }))
vi.mock('../worktree/host-worktree-refresh', () => ({ startHostWorktreeRefresh: () => () => {} }))
import { useHostWorktreeCatalog } from './use-host-worktree-catalog'
import { createOrcaQueryClient } from '../query/orca-query-client'
import { settleQueryNotifications } from '../test-support/rpc-recording/hook-mount'
import { FakeSession } from '../transport/mobile-endpoint-supervisor-test-fakes'
import type { RpcResponse } from '../transport/types'
import { useHostScreenState } from './use-host-screen-state'
async function fetchWith(fail: boolean) {
const client = new FakeSession('connected')
const response: RpcResponse = {
id: 'r',
ok: true,
result: { worktrees: [], snapshotId: 's1' },
_meta: { runtimeId: 'runtime' }
}
client.sendRequest.mockImplementation(async () => {
if (fail) {
throw new Error('network disconnected')
}
return response
})
let current: { state: ReturnType<typeof useHostScreenState>; fetch: () => Promise<void> } | null =
null
function Probe(): null {
const state = useHostScreenState('host-1')
state.clientRef.current = client
const catalog = useHostWorktreeCatalog({
client,
connState: 'connected',
embedded: true,
hostCapabilities: [],
hostId: 'host-1',
sidebarFeed: null,
state,
syncViewSettingsFromDesktop: async () => {}
})
current = { state, fetch: catalog.fetchWorktrees }
return null
}
let renderer: ReturnType<typeof create> | null = null
try {
await act(async () => {
renderer = create(
createElement(
QueryClientProvider,
{ client: createOrcaQueryClient() },
createElement(Probe)
)
)
})
const get = (): NonNullable<typeof current> => {
if (current === null) {
throw new Error('catalog hook did not mount')
}
return current
}
// The mount's own read settles first; the failure is from an action after it.
await settleQueryNotifications()
act(() => get().state.setActionError('Could not delete workspace.'))
await act(async () => get().fetch())
await settleQueryNotifications()
expect(client.sendRequest).toHaveBeenCalledWith(
'worktree.ps',
expect.anything(),
expect.anything()
)
return {
actionError: get().state.actionError,
catalogError: get().state.catalogError,
loaded: get().state.worktreesLoaded
}
} finally {
act(() => renderer?.unmount())
}
}
describe('a catalog the host confirmed', () => {
it('clears the action failure the list is still showing', async () => {
expect(await fetchWith(false)).toEqual({ actionError: '', catalogError: null, loaded: true })
})
it('retains the action failure when the catalog transport fails', async () => {
expect(await fetchWith(true)).toMatchObject({
actionError: 'Could not delete workspace.',
catalogError: 'network_error'
})
})
})
@@ -1,107 +0,0 @@
import { createElement } from 'react'
import { act, create } from 'react-test-renderer'
import { beforeEach, describe, expect, it, vi } from 'vitest'
type RouteDependencies = {
pathnames: string[]
hostId: string | string[]
refusals: number
}
const dependencies = vi.hoisted((): RouteDependencies => ({
pathnames: [],
hostId: 'host-1',
refusals: 0
}))
vi.mock('@react-native-async-storage/async-storage', () => ({
default: { getItem: async () => null, setItem: async () => {} }
}))
vi.mock('react-native', () => ({
StyleSheet: { create: (styles: unknown) => styles },
View: 'View'
}))
vi.mock('expo-router', () => ({
useLocalSearchParams: () => ({ hostId: dependencies.hostId })
}))
// The host route's open-target hook reads the on-disk page cache; these tests cover the route's shell handoff.
vi.mock('./use-host-document-open-target', () => ({
useHostDocumentOpenTarget: () => ({
routeParams: undefined,
onRouteParamClear: () => {},
onPageLocation: () => {}
})
}))
vi.mock('./PageRouteUnavailableScreen', () => ({
PageRouteUnavailableScreen: () => {
dependencies.refusals += 1
return null
}
}))
vi.mock('./MobileWebShellScreen', () => ({
MobileWebShellScreen: (props: { hostId: string; route: { pathname: string } }) => {
dependencies.pathnames.push(props.route.pathname)
return null
}
}))
import { BRIDGE_ROUTE_PATHNAME_PATTERN } from './bridge/bridge-caps'
import HostWorktreeRoute from '../../app/h/[hostId]/index'
async function renderRoute(): Promise<void> {
await act(async () => {
create(createElement(HostWorktreeRoute))
})
}
describe('the worktree-list route that hands off to the shell', () => {
beforeEach(() => {
dependencies.pathnames.length = 0
dependencies.refusals = 0
dependencies.hostId = 'host-1'
})
it('encodes the host id into the pathname, like the shell route already does', async () => {
for (const hostId of ['a?b', 'a#b', 'a b', 'a/b', 'a\\b']) {
dependencies.hostId = hostId
dependencies.pathnames.length = 0
await renderRoute()
const pathname = dependencies.pathnames[0]
expect(pathname, hostId).toBe(`/h/${encodeURIComponent(hostId)}`)
expect(BRIDGE_ROUTE_PATHNAME_PATTERN.test(pathname ?? ''), hostId).toBe(true)
expect(decodeURIComponent((pathname ?? '').slice('/h/'.length)), hostId).toBe(hostId)
}
})
it('refuses a dot-segment host id instead of handing over a route the page refuses', async () => {
// Neither encodeURIComponent nor escaped dots can make a dot segment acceptable to the bridge.
for (const hostId of ['..', '.']) {
dependencies.hostId = hostId
dependencies.pathnames.length = 0
dependencies.refusals = 0
await renderRoute()
expect(BRIDGE_ROUTE_PATHNAME_PATTERN.test(`/h/${hostId}`), hostId).toBe(false)
expect(dependencies.pathnames, hostId).toEqual([])
expect(dependencies.refusals, hostId).toBeGreaterThan(0)
}
})
it('opens the first of a repeated host id, never the pair joined into one', async () => {
// Use firstParam for repeated Expo keys; encoding the array as a,b otherwise invents one valid but nonexistent host segment.
dependencies.hostId = ['host-1', 'host-2']
await renderRoute()
expect(dependencies.pathnames).toEqual(['/h/host-1'])
})
it('refuses an empty repeated host id, which names no host at all', async () => {
dependencies.hostId = []
await renderRoute()
expect(dependencies.pathnames).toEqual([])
expect(dependencies.refusals).toBeGreaterThan(0)
})
})
@@ -1,217 +0,0 @@
import { createElement, type ComponentType } from 'react'
import { act, create, type ReactTestRenderer } from 'react-test-renderer'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
type SwitchDependencies = {
refusals: number
shells: string[]
params: Record<string, string | string[] | undefined>
}
const dependencies = vi.hoisted((): SwitchDependencies => ({
refusals: 0,
shells: [],
params: {}
}))
const refusalScreen = vi.hoisted(
() => async (): Promise<ComponentType<Record<string, unknown>>> => {
const React = await import('react')
return function RefusalScreen() {
React.useEffect(() => {
dependencies.refusals += 1
}, [])
return null
}
}
)
vi.mock('@react-native-async-storage/async-storage', () => ({
default: { getItem: async () => null, setItem: async () => {} }
}))
vi.mock('react-native', () => ({
StyleSheet: { create: (styles: unknown) => styles },
View: 'View'
}))
vi.mock('../transport/host-client-hooks', () => ({
useHostClient: () => ({ client: null, state: 'disconnected' })
}))
vi.mock('../transport/workspace-server', () => ({
useWorkspaceServer: () => ({ routeHost: undefined, server: null })
}))
vi.mock('expo-router', () => ({
useLocalSearchParams: () => dependencies.params,
useRouter: () => ({ setParams: () => {} })
}))
vi.mock('./MobileWebShellScreen', async () => {
const React = await import('react')
return {
MobileWebShellScreen: (props: { route: { pathname: string } }) => {
const pathname = React.useRef(props.route.pathname)
React.useEffect(() => {
dependencies.shells.push(pathname.current)
}, [])
return null
}
}
})
// The host route's open-target hook reads the on-disk page cache; these tests cover the route's shell handoff.
vi.mock('./use-host-document-open-target', () => ({
useHostDocumentOpenTarget: () => ({
routeParams: undefined,
onRouteParamClear: () => {},
onPageLocation: () => {}
})
}))
vi.mock('./PageRouteUnavailableScreen', async () => ({
PageRouteUnavailableScreen: await refusalScreen()
}))
import HostListRoute from '../../app/h/[hostId]/index'
import TasksRoute from '../../app/h/[hostId]/tasks'
import AccountsRoute from '../../app/h/[hostId]/accounts'
import AgentHistoryRoute from '../../app/h/[hostId]/agent-history/[worktreeId]'
import FilesRoute from '../../app/h/[hostId]/files/[worktreeId]'
import FilesPreviewRoute from '../../app/h/[hostId]/files/preview/[worktreeId]'
import SourceControlRoute from '../../app/h/[hostId]/source-control/[worktreeId]'
import ReviewRoute from '../../app/h/[hostId]/review/[worktreeId]'
import SessionRoute from '../../app/h/[hostId]/session/[worktreeId]'
import CatchAllRoute from './catch-all-page-route'
type SwitchCase = {
readonly name: string
readonly Route: ComponentType
readonly params: Record<string, string | string[]>
readonly unroutableParams: Record<string, string | string[]>
readonly pathname: string
}
const SWITCHES: readonly SwitchCase[] = [
{
name: 'host list',
Route: HostListRoute,
params: { hostId: 'host-1' },
unroutableParams: { hostId: '' },
pathname: '/h/host-1'
},
{
name: 'tasks',
Route: TasksRoute,
params: { hostId: 'host-1' },
unroutableParams: { hostId: '' },
pathname: '/h/host-1/tasks'
},
{
name: 'accounts',
Route: AccountsRoute,
params: { hostId: 'host-1' },
unroutableParams: { hostId: '' },
pathname: '/h/host-1/accounts'
},
{
name: 'agent history',
Route: AgentHistoryRoute,
params: { hostId: 'host-1', worktreeId: 'wt-1' },
unroutableParams: { hostId: 'host-1', worktreeId: '' },
pathname: '/h/host-1/agent-history/wt-1'
},
{
name: 'files',
Route: FilesRoute,
params: { hostId: 'host-1', worktreeId: 'wt-1' },
unroutableParams: { hostId: 'host-1', worktreeId: '..' },
pathname: '/h/host-1/files/wt-1'
},
{
name: 'file preview',
Route: FilesPreviewRoute,
params: { hostId: 'host-1', worktreeId: 'wt-1', relativePath: 'src/index.ts' },
unroutableParams: { hostId: 'host-1', worktreeId: 'wt-1' },
pathname: '/h/host-1/files/preview/wt-1'
},
{
name: 'source control',
Route: SourceControlRoute,
params: { hostId: 'host-1', worktreeId: 'wt-1' },
unroutableParams: { hostId: 'host-1', worktreeId: '' },
pathname: '/h/host-1/source-control/wt-1'
},
{
name: 'review',
Route: ReviewRoute,
params: { hostId: 'host-1', worktreeId: 'wt-1' },
unroutableParams: { hostId: 'host-1', worktreeId: '' },
pathname: '/h/host-1/review/wt-1'
},
{
name: 'session',
Route: SessionRoute,
params: { hostId: 'host-1', worktreeId: 'wt-1' },
unroutableParams: { hostId: 'host-1', worktreeId: '' },
pathname: '/h/host-1/session/wt-1'
},
{
name: 'catch-all',
Route: CatchAllRoute,
params: { hostId: 'host-1', page: ['settings'] },
unroutableParams: { hostId: 'host-1' },
pathname: '/h/host-1/settings'
}
]
const mountedTrees: ReturnType<typeof create>[] = []
function createTracked(element: Parameters<typeof create>[0]): ReturnType<typeof create> {
const tree = create(element)
mountedTrees.push(tree)
return tree
}
afterEach(() => {
act(() => {
for (const tree of mountedTrees.splice(0)) {
tree.unmount()
}
})
})
// Synchronous act commits mount effects without draining async follow-up, isolating the first visible frame.
function renderFirstFrame(Route: ComponentType): ReactTestRenderer {
const rendered: { tree: ReactTestRenderer | null } = { tree: null }
act(() => {
rendered.tree = createTracked(createElement(Route))
})
if (rendered.tree === null) {
throw new Error('the switch did not render')
}
return rendered.tree
}
describe.each(SWITCHES)('the $name switch', (entry) => {
beforeEach(() => {
dependencies.refusals = 0
dependencies.shells.length = 0
})
it('commits the shell on its first frame', async () => {
dependencies.params = { ...entry.params }
renderFirstFrame(entry.Route)
expect(dependencies.shells).toEqual([entry.pathname])
await act(async () => {})
expect(dependencies.refusals).toBe(0)
expect(dependencies.shells).toEqual([entry.pathname])
})
it('commits the refusal for params that name no screen the shell could open', async () => {
dependencies.params = { ...entry.unroutableParams }
renderFirstFrame(entry.Route)
await act(async () => {})
expect(dependencies.refusals).toBe(1)
expect(dependencies.shells).toEqual([])
})
})
@@ -56,118 +56,7 @@ function committedHostState(hostIdParam: string): HostStackNavigationState {
}
}
// Model Expo’s internal root navigator: the host stack sits one level below the layout screen.
function rootLayoutScopedState(inner: HostStackNavigationState): HostStackNavigationState {
return { key: 'internal', index: 0, routes: [{ key: '__root', name: '__root', state: inner }] }
}
describe('host stack navigation', () => {
it('matches a host committed as the encoded segment it was pushed as', () => {
const harness = navigationHarness({ index: 0, routes: [{ name: 'index' }] })
const push = vi.fn()
navigateToHostStackRoute(harness.navigation, { push, replace: vi.fn() }, 'host/one', TARGET)
expect(push).toHaveBeenCalledWith('/h/host%2Fone')
expect(harness.navigation.dispatch).not.toHaveBeenCalled()
harness.setState(committedHostState(encodeURIComponent('host/one')))
expect(harness.navigation.dispatch).toHaveBeenCalledTimes(1)
expect(harness.navigation.dispatch).toHaveBeenCalledWith({
type: 'REPLACE',
target: '/h',
source: 'host-index',
payload: TARGET
})
})
it('replaces the host stack seen from the root layout, one navigator further down', () => {
const harness = navigationHarness(
rootLayoutScopedState({ index: 0, routes: [{ name: 'index' }] })
)
navigateToHostStackRoute(
harness.navigation,
{ push: vi.fn(), replace: vi.fn() },
'host/one',
TARGET
)
harness.setState(rootLayoutScopedState(committedHostState('host/one')))
expect(harness.navigation.dispatch).toHaveBeenCalledWith({
type: 'REPLACE',
target: '/h',
source: 'host-index',
payload: TARGET
})
})
it('survives the state emitted before the root navigator has hydrated', () => {
const harness = navigationHarness(undefined)
navigateToHostStackRoute(
harness.navigation,
{ push: vi.fn(), replace: vi.fn() },
'host/one',
TARGET
)
expect(() => harness.setState(undefined)).not.toThrow()
expect(harness.navigation.dispatch).not.toHaveBeenCalled()
harness.setState(rootLayoutScopedState(committedHostState('host/one')))
expect(harness.navigation.dispatch).toHaveBeenCalledTimes(1)
})
it('replaces through Expo Router when root state omits the mounted child stack', () => {
const harness = navigationHarness({ index: 0, routes: [{ name: 'index' }] })
const replace = vi.fn()
navigateToHostStackRoute(harness.navigation, { push: vi.fn(), replace }, 'host/one', TARGET)
harness.setState({
index: 0,
routes: [{ name: 'h', params: { hostId: 'host/one' } }]
})
expect(harness.navigation.dispatch).not.toHaveBeenCalled()
expect(replace).toHaveBeenCalledWith({
pathname: '/h/[hostId]/tasks',
params: { hostId: 'host/one' }
})
expect(harness.listenerCount()).toBe(0)
})
it('abandons the transition when navigation leaves the host route it was waiting on', () => {
const harness = navigationHarness({ index: 0, routes: [{ name: 'index' }] })
navigateToHostStackRoute(
harness.navigation,
{ push: vi.fn(), replace: vi.fn() },
'host/one',
TARGET
)
harness.setState({ index: 0, routes: [{ name: 'h' }] })
harness.setState({ index: 0, routes: [{ name: 'index' }] })
harness.setState(committedHostState('host/one'))
expect(harness.navigation.dispatch).not.toHaveBeenCalled()
expect(harness.listenerCount()).toBe(0)
})
it('ignores a different host whose id merely decodes badly', () => {
const harness = navigationHarness({ index: 0, routes: [{ name: 'index' }] })
navigateToHostStackRoute(
harness.navigation,
{ push: vi.fn(), replace: vi.fn() },
'host/one',
TARGET
)
harness.setState(committedHostState('100%'))
expect(harness.navigation.dispatch).not.toHaveBeenCalled()
})
it('stops listening and never replaces once canceled', () => {
const harness = navigationHarness({ index: 0, routes: [{ name: 'index' }] })
@@ -219,76 +108,4 @@ describe('host stack navigation', () => {
expect.objectContaining({ payload: OTHER_TARGET })
)
})
describe('a session opened inside its host document', () => {
const SESSION = {
name: '[hostId]/session/[worktreeId]',
params: { hostId: 'host/one', worktreeId: 'repo::/tmp/wt', paneKey: 'tab-1:leaf 2' }
} as const
// The session's href, escaped once more because the host screen's param reader decodes it.
const OPEN =
'%2Fh%2Fhost%252Fone%2Fsession%2Frepo%253A%253A%252Ftmp%252Fwt%3FpaneKey%3Dtab-1%253Aleaf%25202'
it('pushes the host with the session to open instead of pushing a second document', () => {
const harness = navigationHarness({ index: 0, routes: [{ name: 'index' }] })
const push = vi.fn()
navigateToHostStackRoute(
harness.navigation,
{ push, replace: vi.fn() },
'host/one',
SESSION,
true
)
harness.setState(committedHostState('host/one'))
expect(push.mock.calls).toEqual([
[{ pathname: '/h/[hostId]', params: { hostId: 'host/one', open: OPEN } }]
])
expect(harness.navigation.dispatch).not.toHaveBeenCalled()
})
it('folds a second tap for the same host into the push still committing', () => {
const harness = navigationHarness({ index: 0, routes: [{ name: 'index' }] })
const push = vi.fn()
const router = { push, replace: vi.fn() }
const pending = coordinateHostStackNavigation(
null,
harness.navigation,
router,
'host/one',
SESSION,
true
)
coordinateHostStackNavigation(pending, harness.navigation, router, 'host/one', SESSION, true)
expect(push).toHaveBeenCalledTimes(1)
})
it('hands the session to the host list already showing', () => {
const harness = navigationHarness(committedHostState('host/one'))
const push = vi.fn()
navigateToHostStackRoute(
harness.navigation,
{ push, replace: vi.fn() },
'host/one',
SESSION,
true
)
expect(push).not.toHaveBeenCalled()
expect(harness.navigation.dispatch.mock.calls).toEqual([
[
{
type: 'SET_PARAMS',
target: '/h',
source: 'host-index',
payload: { params: { open: OPEN } }
}
]
])
})
})
})
@@ -91,52 +91,3 @@ it('reads again when the desktop reports a change during a read, ending on the r
await settleQueryNotifications()
expect(rows()).toBe('after')
})
it('listens again on the next connection after the desktop ends the event stream', async () => {
const host = desktop()
await mountList(host)
await act(async () => host.replies[0]('first'))
host.emit({ type: 'ready', subscriptionId: 'events-1' })
host.emit({ type: 'end' })
await act(async () => {
host.setState('disconnected')
host.setState('connected')
})
// The connected edge's own re-read.
await act(async () => host.replies.at(-1)!('second'))
await settleQueryNotifications()
host.emit({ type: 'ready', subscriptionId: 'events-2' })
const before = host.replies.length
host.emit({ type: 'worktreesChanged', repoId: 'repo-1', sortOrderOnly: false })
await settleQueryNotifications()
expect(host.replies).toHaveLength(before + 1)
})
it('survives a stream that ends while opening and opens it on the next connection', async () => {
const host = desktop()
let refuse = true
const subscribe = host.client.subscribe
host.client.subscribe = (method, params, onData, options) => {
if (refuse) {
refuse = false
onData({ type: 'error', message: 'refused' })
return () => {}
}
return subscribe(method, params, onData, options)
}
await mountList(host)
await act(async () => host.replies[0]('first'))
await act(async () => {
host.setState('disconnected')
host.setState('connected')
})
await act(async () => host.replies.at(-1)!('second'))
await settleQueryNotifications()
host.emit({ type: 'ready', subscriptionId: 'events-2' })
const before = host.replies.length
host.emit({ type: 'worktreesChanged', repoId: 'repo-1', sortOrderOnly: false })
await settleQueryNotifications()
expect(host.replies).toHaveLength(before + 1)
})
@@ -1,77 +0,0 @@
import { createElement } from 'react'
import { act, create, type ReactTestRenderer } from 'react-test-renderer'
import { describe, expect, it, vi } from 'vitest'
import type { AgentSessionModelCatalogResult } from '../../../src/shared/agent-session/agent-session-wire'
import { structuredAgentSessionSeedCatalog } from '../../../src/shared/agent-session/options/structured-agent-session-seed-catalog'
import { createStructuredAgentSessionOptionState } from '../../../src/shared/agent-session/options/structured-agent-session-options'
import type { RpcClient } from '../transport/rpc-client'
import { useMobileHostModelCatalogUpgrade } from './use-mobile-host-model-catalog-upgrade'
const LISTED: AgentSessionModelCatalogResult = {
origin: 'live-session',
models: [{ id: 'running-account-model', label: 'Running', isDefault: true, efforts: [] }],
fetchedAt: 1
}
function listingClient(): RpcClient {
let plainReads = 0
return {
sendRequest: (_method: string, params?: unknown) => {
const waits = typeof params === 'object' && params !== null && 'waitForListing' in params
if (waits) {
// The host's listing is still running for the whole test.
return new Promise(() => {})
}
plainReads += 1
const result = plainReads === 1 ? { origin: 'unknown', listingInProgress: true } : LISTED
return Promise.resolve({ id: 'r', ok: true, result, _meta: { runtimeId: 'runtime-1' } })
},
subscribe: () => () => {},
updateTerminalSubscriptionViewport: () => {},
getState: () => 'connected',
getReconnectAttempt: () => 0,
getLastConnectedAt: () => 1,
onStateChange: () => () => {},
notifyForeground: () => {},
close: () => {}
}
}
describe('useMobileHostModelCatalogUpgrade', () => {
it('does not save a read sent after the first turn, even while a pre-turn listing is waited on', async () => {
const client = listingClient()
const record = vi.fn()
const activeOptionRecordRef = {
current: createStructuredAgentSessionOptionState('opencode').record
}
function Probe(props: { hasTurn: boolean; selectedModel?: string }): null {
useMobileHostModelCatalogUpgrade({
agent: 'opencode',
client,
sessionId: 'session-1',
enabled: true,
newLaunch: true,
worktree: 'wt-1',
hasTurn: props.hasTurn,
...(props.selectedModel ? { selectedModel: props.selectedModel } : {}),
fence: 1,
optionCatalog: structuredAgentSessionSeedCatalog('opencode'),
modelCatalogCache: { seed: () => undefined, record },
receiveCatalog: () => {},
activeOptionRecordRef,
updateOptionState: () => {}
})
return null
}
let renderer: ReactTestRenderer | null = null
await act(async () => {
renderer = create(createElement(Probe, { hasTurn: false }))
})
await act(async () => {
renderer?.update(createElement(Probe, { hasTurn: true, selectedModel: 'other-model' }))
})
expect(record.mock.calls.map(([, catalog]) => catalog)).not.toContainEqual(LISTED)
await act(async () => renderer?.unmount())
})
})
@@ -1,35 +0,0 @@
import { describe, expect, it } from 'vitest'
import { nextBranchCompareState } from './mobile-branch-compare-outcome'
import type { BranchCompareOutcome } from './mobile-branch-compare-outcome'
import type { MobileGitBranchCompareReply } from './git-compare-reply-schema'
import type { MobileBranchCompareState } from './mobile-source-control-screen-state'
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the mapping passes the reply through untouched and reads no member of it.
const REPLY = { summary: { baseRef: 'origin/main' } } as MobileGitBranchCompareReply
const READY: MobileBranchCompareState = { kind: 'ready', result: REPLY }
const UNAVAILABLE: BranchCompareOutcome = { kind: 'unavailable' }
const FAILED: BranchCompareOutcome = { kind: 'failed', message: 'no base' }
describe('nextBranchCompareState', () => {
it('publishes a ready compare whatever the previous state was', () => {
const outcome: BranchCompareOutcome = { kind: 'ready', result: REPLY }
expect(nextBranchCompareState(outcome, { kind: 'idle' })).toEqual(READY)
expect(nextBranchCompareState(outcome, { kind: 'error', message: 'old' })).toEqual(READY)
})
it('keeps a prior ready compare when a re-read fails', () => {
expect(nextBranchCompareState(FAILED, READY)).toBe(READY)
expect(nextBranchCompareState(UNAVAILABLE, READY)).toBe(READY)
})
it('separates a host without git from an attempt that failed', () => {
expect(nextBranchCompareState(UNAVAILABLE, { kind: 'loading' })).toEqual({
kind: 'idle'
})
expect(nextBranchCompareState(FAILED, { kind: 'loading' })).toEqual({
kind: 'error',
message: 'no base'
})
})
})
@@ -1,75 +0,0 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
const mocks = vi.hoisted(() => ({ sidebar: vi.fn(), checks: vi.fn() }))
vi.mock('./pull-request-sidebar-query', () => ({ readPullRequestSidebarQuery: mocks.sidebar }))
vi.mock('../check/complete-pr-check-observation', () => ({
readCompletePRCheckObservation: mocks.checks
}))
import {
readSharedPRCheckObservation,
readSharedPullRequestSidebarQuery
} from './shared-pr-provider-reads'
const REPO = { owner: 'acme', repo: 'orca' }
const SCOPE = 'local-runtime'
describe('shared PR provider reads', () => {
beforeEach(() => {
mocks.sidebar.mockReset()
mocks.checks.mockReset()
})
it('sends one request per PR for every checkout of one account, and none across accounts', async () => {
mocks.sidebar.mockImplementation(async () => ({ number: 12 }))
const reads = [
readSharedPullRequestSidebarQuery(REPO, 12, { cwd: '/repo' }, SCOPE),
readSharedPullRequestSidebarQuery(
{ owner: 'ACME', repo: 'orca' },
12,
{ cwd: '/repo-wt' },
SCOPE
),
readSharedPullRequestSidebarQuery(
REPO,
12,
{ cwd: '/repo', ghAccount: { host: 'github.com', user: 'other' } },
SCOPE
),
readSharedPullRequestSidebarQuery(REPO, 12, { cwd: '/repo' }, 'wsl:Ubuntu')
]
expect(mocks.sidebar).toHaveBeenCalledTimes(3)
const [first, second] = await Promise.all(reads)
expect(first).toBe(second)
})
it('shares a check read only for the same head', async () => {
mocks.checks.mockImplementation(() => new Promise(() => {}))
void readSharedPRCheckObservation(REPO, 12, 'aaa', { cwd: '/repo' }, SCOPE)
void readSharedPRCheckObservation(REPO, 12, 'aaa', { cwd: '/repo-wt' }, SCOPE)
void readSharedPRCheckObservation(REPO, 12, 'bbb', { cwd: '/repo' }, SCOPE)
expect(mocks.checks).toHaveBeenCalledTimes(2)
})
it('lets a bounded watch read join a full read, but never serve one', async () => {
mocks.sidebar.mockImplementation(() => new Promise(() => {}))
void readSharedPullRequestSidebarQuery(REPO, 12, { cwd: '/repo' }, SCOPE, {
checkPageLimit: 10
})
void readSharedPullRequestSidebarQuery(REPO, 12, { cwd: '/repo' }, SCOPE)
void readSharedPullRequestSidebarQuery(REPO, 12, { cwd: '/repo' }, SCOPE, {
checkPageLimit: 10
})
expect(mocks.sidebar).toHaveBeenCalledTimes(2)
expect(mocks.sidebar.mock.calls.map((call) => call[3])).toEqual([10, undefined])
})
it('starts a fresh read for a user refresh instead of joining one begun before the click', async () => {
mocks.sidebar.mockImplementation(() => new Promise(() => {}))
void readSharedPullRequestSidebarQuery(REPO, 13, { cwd: '/repo' }, SCOPE)
void readSharedPullRequestSidebarQuery(REPO, 13, { cwd: '/repo' }, SCOPE, { userRefresh: true })
// Later ordinary callers share the refresh's read.
void readSharedPullRequestSidebarQuery(REPO, 13, { cwd: '/repo-wt' }, SCOPE)
expect(mocks.sidebar).toHaveBeenCalledTimes(2)
})
})
@@ -1,5 +1,5 @@
import { describe, expect, it } from 'vitest'
import { restPRCheckRollupRows } from '../client/check/rest-pr-check-pages'
import '../client/check/rest-pr-check-pages'
import { evaluatePRCheckOccurrence, type PRCheckContextState } from './pr-check-occurrence'
// Provider-shaped reads of one commit: GraphQL statusCheckRollup nodes and the REST check-runs and
@@ -19,24 +19,6 @@ const graphqlStatus = (state: string, run: number) => ({
targetUrl: `https://circleci.com/gh/acme/repo/${run}`,
createdAt: '2026-11-06T10:00:00Z'
})
const restRun = (id: number, conclusion: string | null, status = 'completed') => ({
id,
name: 'build',
head_sha: '8d5f74b3c0e1',
status,
conclusion,
html_url: `https://github.com/acme/repo/runs/${id}`,
details_url: `https://github.com/acme/repo/actions/runs/101/job/${id}`,
started_at: '2026-11-06T10:00:00Z',
completed_at: status === 'completed' ? '2026-11-06T10:04:00Z' : null,
check_suite: { id: 29001 }
})
const restStatus = (state: string, run: number) => ({
context: 'ci/circleci: test',
state,
target_url: `https://circleci.com/gh/acme/repo/${run}`,
created_at: '2026-11-06T10:00:00Z'
})
function after(
rollup: unknown[],
@@ -75,43 +57,4 @@ describe('evaluatePRCheckOccurrence', () => {
// The same final state under a new run URL is a new run.
expect(after([graphqlStatus('SUCCESS', 102)], green.next).verdict).toBe('success')
})
it('reports nothing until every check is final, and nothing for cancellation or approval', () => {
expect(after([graphqlStatus('EXPECTED', 101)]).verdict).toBe(null)
expect(after([]).verdict).toBe(null)
expect(
after([graphqlRun(31001, null, 'IN_PROGRESS'), graphqlStatus('FAILURE', 101)]).verdict
).toBe(null)
expect(after([graphqlRun(31001, 'CANCELLED')]).verdict).toBe(null)
expect(
evaluatePRCheckOccurrence([graphqlRun(31001, 'SUCCESS')], false, new Map()).verdict
).toBe(null)
const awaitingApproval = restPRCheckRollupRows(
[restRun(31001, 'success')],
[],
[{ id: 29002, status: 'completed', conclusion: 'action_required' }]
)
expect(after(awaitingApproval).verdict).toBe(null)
})
it('gives the GraphQL and REST reads of one commit the same identity and verdict', () => {
const graphql = [graphqlRun(31001, 'FAILURE'), graphqlStatus('SUCCESS', 101)]
const rest = restPRCheckRollupRows(
[restRun(31001, 'failure')],
[restStatus('success', 101)],
[]
)
const fromGraphql = after(graphql)
const fromRest = after(rest)
expect(fromRest.verdict).toBe(fromGraphql.verdict)
expect([...fromRest.next.values()]).toEqual([...fromGraphql.next.values()])
// Either read after the other sees nothing new; a REST rerun is new to a GraphQL memory.
expect(after(rest, fromGraphql.next).verdict).toBe(null)
expect(
after(
restPRCheckRollupRows([restRun(31002, 'failure')], [restStatus('success', 101)], []),
fromGraphql.next
).verdict
).toBe('failure')
})
})
@@ -1,79 +0,0 @@
import { describe, expect, it, vi } from 'vitest'
import './runtime-test-module-registration.spec'
import { OrcaRuntimeService } from '../orca-runtime'
import { store } from './orca-runtime-test-fixtures.spec'
describe('OrcaRuntimeService.dedupeWorktreeCreate', () => {
it('coalesces concurrent creates that share a clientMutationId', async () => {
const runtime = new OrcaRuntimeService(store)
let calls = 0
const factory = (): Promise<{ worktree: { id: string } }> => {
calls += 1
return Promise.resolve({ worktree: { id: 'wt' } })
}
const [a, b] = await Promise.all([
runtime.dedupeWorktreeCreate('id:r', 'key-1', factory),
runtime.dedupeWorktreeCreate('id:r', 'key-1', factory)
])
expect(calls).toBe(1)
expect(a).toBe(b)
})
it('reuses a settled success for a retry whose response was lost in a cutover', async () => {
const runtime = new OrcaRuntimeService(store)
let calls = 0
const factory = (): Promise<{ worktree: { id: string } }> => {
calls += 1
return Promise.resolve({ worktree: { id: `wt-${calls}` } })
}
const first = await runtime.dedupeWorktreeCreate('id:r', 'key-1', factory)
const retried = await runtime.dedupeWorktreeCreate('id:r', 'key-1', factory)
expect(calls).toBe(1)
expect(retried).toEqual(first)
})
it('expires settled successes after the reconnect window', async () => {
vi.useFakeTimers()
try {
const runtime = new OrcaRuntimeService(store)
let calls = 0
const factory = (): Promise<{ n: number }> => Promise.resolve({ n: (calls += 1) })
await runtime.dedupeWorktreeCreate('id:r', 'key-1', factory)
await vi.advanceTimersByTimeAsync(59_999)
await runtime.dedupeWorktreeCreate('id:r', 'key-1', factory)
expect(calls).toBe(1)
await vi.advanceTimersByTimeAsync(1)
await runtime.dedupeWorktreeCreate('id:r', 'key-1', factory)
expect(calls).toBe(2)
} finally {
vi.useRealTimers()
}
})
it('drops a failed create so a genuine retry starts fresh', async () => {
const runtime = new OrcaRuntimeService(store)
let calls = 0
const factory = (): Promise<never> => {
calls += 1
return Promise.reject(new Error(`boom-${calls}`))
}
await expect(runtime.dedupeWorktreeCreate('id:r', 'key-1', factory)).rejects.toThrow('boom-1')
await expect(runtime.dedupeWorktreeCreate('id:r', 'key-1', factory)).rejects.toThrow('boom-2')
expect(calls).toBe(2)
})
it('never dedupes across repos or when no clientMutationId is supplied', async () => {
const runtime = new OrcaRuntimeService(store)
let calls = 0
const factory = (): Promise<{ n: number }> => {
calls += 1
return Promise.resolve({ n: calls })
}
await runtime.dedupeWorktreeCreate('id:a', 'key-1', factory)
await runtime.dedupeWorktreeCreate('id:b', 'key-1', factory)
await runtime.dedupeWorktreeCreate('id:a', undefined, factory)
await runtime.dedupeWorktreeCreate('id:a', undefined, factory)
expect(calls).toBe(4)
})
})
@@ -1,61 +0,0 @@
import { describe, expect, it, vi } from 'vitest'
import './runtime-test-module-registration.spec'
import { OrcaRuntimeService } from '../orca-runtime'
import {
HEADLESS_LEAF_ID,
TEST_WORKTREE_ID,
makeRuntimeStoreWithWorkspaceSession,
makeWorkspaceSessionWithHeadlessTerminal
} from './orca-runtime-test-fixtures.spec'
// #30329: host listings must expose client-renamed panes persisted in host layout.
async function headlessRuntimeWithLivePane() {
const { runtimeStore, getSession } = makeRuntimeStoreWithWorkspaceSession(
makeWorkspaceSessionWithHeadlessTerminal()
)
const runtime = new OrcaRuntimeService(runtimeStore)
runtime.setPtyController({
spawn: vi.fn(async () => ({ id: 'pane-title-pty' })),
write: () => true,
kill: () => true,
getForegroundProcess: async () => null
})
const created = await runtime.createTerminal(`id:${TEST_WORKTREE_ID}`, {
tabId: 'host-tab',
leafId: HEADLESS_LEAF_ID,
activate: true
})
const renamePane = (titlesByLeafId: Record<string, string>) =>
runtime.updateMobileSessionPaneLayout(`id:${TEST_WORKTREE_ID}`, {
tabId: 'host-tab',
root: getSession().terminalLayoutsByTabId['host-tab']!.root,
expandedLeafId: null,
titlesByLeafId
})
const listed = async () =>
(await runtime.listTerminals(`id:${TEST_WORKTREE_ID}`)).terminals.find(
(terminal) => terminal.handle === created.handle
)
return { runtime, handle: created.handle, renamePane, listed }
}
describe('pane titles in terminal summaries', () => {
it('reports a client pane rename in terminal.list and terminal.show', async () => {
const { runtime, handle, renamePane, listed } = await headlessRuntimeWithLivePane()
expect((await listed())?.paneTitle).toBeUndefined()
await renamePane({ [HEADLESS_LEAF_ID]: 'server' })
expect(await listed()).toMatchObject({ handle, paneTitle: 'server' })
expect((await runtime.showTerminal(handle)).paneTitle).toBe('server')
})
it('drops the pane title once a client clears the last one', async () => {
const { renamePane, listed } = await headlessRuntimeWithLivePane()
await renamePane({ [HEADLESS_LEAF_ID]: 'server' })
await renamePane({})
expect((await listed())?.paneTitle).toBeUndefined()
})
})
@@ -1,33 +0,0 @@
import { describe, expect, it } from 'vitest'
import { createRuntime } from './orca-runtime-test-fixtures.spec'
import {
registerManagedServerActions,
type ManagedServerActions
} from '../managed-server-actions-registry'
describe('managed server capability', () => {
// Without desktop SSH registry, never advertise methods that can only refuse.
it('is advertised only where the desktop registered the managed-server actions', () => {
registerManagedServerActions(null)
expect(createRuntime().getStatus().capabilities).not.toContain('managedServer.v1')
const unusedAction = async (): Promise<never> => {
throw new Error('Capability discovery must not invoke managed-server actions')
}
const actions: ManagedServerActions = {
status: unusedAction,
update: unusedAction,
rollback: unusedAction,
recover: unusedAction,
stop: unusedAction,
cancelStop: unusedAction,
forget: unusedAction,
forgetRedundant: unusedAction
}
registerManagedServerActions(actions)
try {
expect(createRuntime().getStatus().capabilities).toContain('managedServer.v1')
} finally {
registerManagedServerActions(null)
}
})
})
@@ -1,70 +0,0 @@
import { describe, expect, it } from 'vitest'
import { OrcaRuntimeService } from '../orca-runtime'
import type { RuntimeSyncWindowGraph } from '../../../shared/runtime-session-contracts'
export function registerOrcaRuntimeTabIdCollisionTests(): void {
const WORKTREE_A = 'repo-1::/tmp/worktree-a'
const WORKTREE_B = 'repo-1::/tmp/worktree-b'
function tab(tabId: string, worktreeId: string) {
return {
tabId,
worktreeId,
title: `${worktreeId} tab`,
activeLeafId: null,
layout: null
}
}
function graph(tabs: RuntimeSyncWindowGraph['tabs']): RuntimeSyncWindowGraph {
return { tabs, leaves: [] }
}
describe('runtime graph tab identity', () => {
it('does not claim graph authority when the first publication is malformed', () => {
const runtime = new OrcaRuntimeService()
expect(() =>
runtime.syncWindowGraph(
1,
graph([tab('tab-duplicate', WORKTREE_A), tab('tab-duplicate', WORKTREE_B)])
)
).toThrow('duplicate_runtime_tab_id')
expect(
(runtime as unknown as { authoritativeWindowId: number | null }).authoritativeWindowId
).toBe(null)
expect(() => runtime.syncWindowGraph(1, graph([tab('tab-valid', WORKTREE_A)]))).not.toThrow()
})
it('rejects duplicate tab ids across worktrees before replacing the graph', () => {
const runtime = new OrcaRuntimeService()
runtime.attachWindow(1)
runtime.syncWindowGraph(1, graph([tab('tab-unique', WORKTREE_A)]))
expect(() =>
runtime.syncWindowGraph(
1,
graph([tab('tab-duplicate', WORKTREE_A), tab('tab-duplicate', WORKTREE_B)])
)
).toThrow('duplicate_runtime_tab_id')
expect([...(runtime as unknown as { tabs: Map<string, unknown> }).tabs.keys()]).toEqual([
'tab-unique'
])
})
it('accepts distinct tab ids from different worktrees', () => {
const runtime = new OrcaRuntimeService()
runtime.attachWindow(1)
expect(() =>
runtime.syncWindowGraph(1, graph([tab('tab-a', WORKTREE_A), tab('tab-b', WORKTREE_B)]))
).not.toThrow()
expect([...(runtime as unknown as { tabs: Map<string, unknown> }).tabs.keys()]).toEqual([
'tab-a',
'tab-b'
])
})
})
}
@@ -1,79 +0,0 @@
import { describe, expect, it, vi } from 'vitest'
import './runtime-test-module-registration.spec'
import { OrcaRuntimeService } from '../orca-runtime'
import { OrchestrationDb } from '../orchestration/db/orchestration-db'
import { createRootDispatch } from '../orchestration/db/root-dispatch-test-fixture'
import { makePaneKey } from '../../../shared/workspace-layout/stable-pane-id'
import { TEST_WORKTREE_ID, store } from './orca-runtime-test-fixtures.spec'
describe('OrcaRuntimeService', () => {
it('batches attention queries across unchanged graph publishes', () => {
const runtime = new OrcaRuntimeService(store)
const terminals = Array.from({ length: 12 }, (_, index) => ({
tabId: `tab-attention-batch-${index}`,
leafId: `10000000-0000-4000-8000-${String(index).padStart(12, '0')}`,
ptyId: `pty-attention-batch-${index}`,
paneRuntimeId: index + 1
}))
const handles = terminals.map((terminal) => runtime.preAllocateHandleForPty(terminal.ptyId))
const db = new OrchestrationDb(':memory:')
try {
const run = db.createRun({
objective: 'bounded attention query oracle',
coordinatorHandle: 'term_attention_coordinator',
coordinatorPaneKey: makePaneKey(
'tab-attention-coordinator',
'20000000-0000-4000-8000-000000000000'
)
})
for (const [index, terminal] of terminals.entries()) {
const task = db.createTask({ spec: `worker ${index}`, runId: run.id })
createRootDispatch(
db,
task.id,
handles[index],
makePaneKey(terminal.tabId, terminal.leafId)
)
}
const getWorkerAttentionFacts = vi.spyOn(db, 'getWorkerAttentionFacts')
const prepare = vi.spyOn(db.db, 'prepare')
runtime.setOrchestrationDb(db)
runtime.attachWindow(1)
const graph = {
tabs: terminals.map((terminal) => ({
tabId: terminal.tabId,
worktreeId: TEST_WORKTREE_ID,
title: terminal.tabId,
activeLeafId: terminal.leafId,
layout: null
})),
leaves: terminals.map((terminal) => ({
tabId: terminal.tabId,
worktreeId: TEST_WORKTREE_ID,
leafId: terminal.leafId,
paneRuntimeId: terminal.paneRuntimeId,
ptyId: terminal.ptyId,
paneTitle: null
}))
}
runtime.syncWindowGraph(1, graph)
prepare.mockClear()
getWorkerAttentionFacts.mockClear()
const unchanged = runtime.syncWindowGraph(1, graph)
const attentionSql = prepare.mock.calls
.map(([sql]) => sql)
.filter(
(sql) =>
(sql.includes('AS pending_input') && sql.includes('json_each(?)')) ||
(sql.includes('attempt_observation_facts') && sql.includes('json_each(?)'))
)
expect(Object.keys(unchanged.agentOrchestrationByPaneKey ?? {})).toHaveLength(12)
expect(getWorkerAttentionFacts).not.toHaveBeenCalled()
expect(attentionSql).toHaveLength(2)
} finally {
db.close()
}
})
})
@@ -1,46 +0,0 @@
import { describe, expect, it } from 'vitest'
import { createRuntime, syncSinglePty } from './orca-runtime-test-fixtures.spec'
describe('headless model grid after a reattach', () => {
it('reflows a model that live bytes created at the 80x24 default onto the PTY grid', async () => {
const runtime = createRuntime()
// Model a reattach whose real grid is learned only from the reply.
runtime.setPtyController({
write: () => true,
kill: () => true,
getForegroundProcess: async () => null,
getSize: () => null
})
syncSinglePty(runtime, 'pty-1')
runtime.onPtyData('pty-1', 'user@host % claude\r\n', 100)
await expect(
runtime.serializeMainTerminalBuffer('pty-1', { scrollbackRows: 100 })
).resolves.toMatchObject({ cols: 80, rows: 24 })
runtime.reflowHeadlessTerminalToPtyGrid('pty-1', 211, 57)
await expect(
runtime.serializeMainTerminalBuffer('pty-1', { scrollbackRows: 100 })
).resolves.toMatchObject({ cols: 211, rows: 57, source: 'headless' })
})
it('never creates a model for a PTY that has none', async () => {
const runtime = createRuntime()
runtime.setPtyController({
write: () => true,
kill: () => true,
getForegroundProcess: async () => null,
getSize: () => null
})
syncSinglePty(runtime, 'pty-1')
runtime.reflowHeadlessTerminalToPtyGrid('pty-1', 211, 57)
runtime.onPtyData('pty-1', 'hello\r\n', 100)
// Pre-creating the model would bypass the renderer-authority gate.
await expect(
runtime.serializeMainTerminalBuffer('pty-1', { scrollbackRows: 100 })
).resolves.toMatchObject({ cols: 80, rows: 24 })
})
})
@@ -1,27 +0,0 @@
import { describe, expect, it, vi } from 'vitest'
import { committingSpawn } from './bind-first-pane.test-fixture'
import './runtime-test-module-registration.spec'
import { OrcaRuntimeService } from '../orca-runtime'
import { TEST_WORKTREE_PATH, store } from './orca-runtime-test-fixtures.spec'
describe('terminal agent presence', () => {
it('finds an idle Claude by a fresh read when p_comm is its version directory', async () => {
const spawn = vi.fn().mockResolvedValue({ id: 'pty-claude' })
const runtime = new OrcaRuntimeService(store)
runtime.setPtyController({
spawn: committingSpawn(runtime, spawn),
write: () => true,
kill: () => true,
getForegroundProcess: async () => '2.1.310',
confirmForegroundProcess: async () => 'claude'
})
const { handle } = await runtime.createTerminal(`path:${TEST_WORKTREE_PATH}`, {
command: 'claude',
title: 'worker'
})
// Claude answered over OSC 7501 paints no title glyph.
runtime.onPtyData('pty-claude', '\x1b]0;Orca worker dispatch task ok\x07', 1)
await expect(runtime.isTerminalRunningAgent(handle)).resolves.toBe(true)
})
})
@@ -1,30 +0,0 @@
import { describe, expect, it, vi } from 'vitest'
import './runtime-test-module-registration.spec'
import { OrcaRuntimeService } from '../orca-runtime'
import { TEST_WORKTREE_ID, store } from './orca-runtime-test-fixtures.spec'
import { spawnedPtyEnv } from '../spawned-pty-env-fixtures'
function makeRuntime(): { runtime: OrcaRuntimeService; spawn: ReturnType<typeof vi.fn> } {
const spawn = vi.fn().mockResolvedValue({ id: 'pty-workspace-id' })
const runtime = new OrcaRuntimeService(store)
runtime.setPtyController({
spawn,
write: () => true,
kill: () => true,
getForegroundProcess: async () => null
})
return { runtime, spawn }
}
// Host-created panes need the same workspace id that renderer panes pass to agent wrappers.
describe('OrcaRuntimeService terminal ORCA_WORKSPACE_ID', () => {
it('replaces a client-sent value with the host workspace id', async () => {
const { runtime, spawn } = makeRuntime()
await runtime.createTerminal(`id:${TEST_WORKTREE_ID}`, {
env: { ORCA_WORKSPACE_ID: 'stale-client-id' }
})
expect(spawnedPtyEnv(spawn.mock.calls[0]?.[0]).ORCA_WORKSPACE_ID).toBe(TEST_WORKTREE_ID)
})
})
-7
View File
@@ -13,11 +13,9 @@ await import('./orca-runtime-tests/orca-runtime-test-lifecycle.spec')
await import('./orca-runtime-tests/orca-runtime-test-fixtures.spec')
await import('./orca-runtime-tests/orca-runtime-test-scenario-builders.spec')
await import('./orca-runtime-tests/dedupe-worktree-create.spec')
await import('./orca-runtime-tests/paired-settings.spec')
await import('./orca-runtime-tests/runtime-availability.spec')
await import('./orca-runtime-tests/browser-capabilities.spec')
await import('./orca-runtime-tests/managed-server-capability.spec')
await import('./orca-runtime-tests/window-authority.spec')
await import('./orca-runtime-tests/terminal-handles.spec')
await import('./orca-runtime-tests/terminal-handles-part-02.spec')
@@ -38,7 +36,6 @@ await import('./orca-runtime-tests/terminal-side-effect-facts-part-03.spec')
await import('./orca-runtime-tests/decorative-title-fact-throttle.spec')
await import('./orca-runtime-tests/headless-snapshots.spec')
await import('./orca-runtime-tests/headless-snapshots-part-02.spec')
await import('./orca-runtime-tests/reattach-headless-grid.spec')
await import('./orca-runtime-tests/agent-status-and-waits.spec')
await import('./orca-runtime-tests/program-status-osc.spec')
await import('./orca-runtime-tests/agent-status-and-waits-part-02.spec')
@@ -46,7 +43,6 @@ await import('./orca-runtime-tests/agent-status-and-waits-part-03.spec')
await import('./orca-runtime-tests/terminal-creation-and-readiness.spec')
await import('./orca-runtime-tests/terminal-creation-and-readiness-part-02.spec')
await import('./orca-runtime-tests/terminal-creation-and-readiness-part-03.spec')
await import('./orca-runtime-tests/terminal-workspace-id-env.spec')
await import('./orca-runtime-tests/terminal-creation-and-readiness-part-04.spec')
await import('./orca-runtime-tests/terminal-creation-and-readiness-part-05.spec')
await import('./orca-runtime-tests/terminal-creation-and-readiness-part-06.spec')
@@ -68,7 +64,6 @@ await import('./orca-runtime-tests/terminal-output-and-worker-recovery-part-06.s
await import('./orca-runtime-tests/terminal-output-and-worker-recovery-part-07.spec')
await import('./orca-runtime-tests/terminal-handles-and-agent-status.spec')
await import('./orca-runtime-tests/terminal-handles-and-agent-status-part-02.spec')
await import('./orca-runtime-tests/terminal-agent-presence.spec')
await import('./orca-runtime-tests/terminal-handles-and-agent-status-part-03.spec')
await import('./orca-runtime-tests/terminal-handles-and-agent-status-part-04.spec')
await import('./orca-runtime-tests/terminal-handles-and-agent-status-part-05.spec')
@@ -79,7 +74,6 @@ await import('./orca-runtime-tests/mobile-session-tabs-part-03.spec')
await import('./orca-runtime-tests/mobile-session-tabs-part-04.spec')
await import('./orca-runtime-tests/mobile-session-tabs-part-05.spec')
await import('./orca-runtime-tests/mobile-session-tabs-part-06.spec')
await import('./orca-runtime-tests/headless-pane-title-terminal-list.spec')
await import('./orca-runtime-tests/mobile-session-tabs-part-07.spec')
await import('./orca-runtime-tests/mobile-session-tabs-part-08.spec')
await import('./orca-runtime-tests/mobile-session-tabs-part-09.spec')
@@ -112,7 +106,6 @@ await import('./orca-runtime-tests/terminal-sleep-and-teardown-part-03.spec')
await import('./orca-runtime-tests/lineage-and-scan-cache.spec')
await import('./orca-runtime-tests/lineage-and-scan-cache-part-02.spec')
await import('./orca-runtime-tests/lineage-and-scan-cache-part-05.spec')
await import('./orca-runtime-tests/orchestration-attention-batching.spec')
await import('./orca-runtime-tests/wsl-stopped-distro-worktree-scan.spec')
await import('./orca-runtime-tests/worktree-setup-and-startup.spec')
await import('./orca-runtime-tests/worktree-setup-and-startup-part-02.spec')
@@ -1,5 +1,3 @@
import { registerTerminalMobileSubscribeTabMountTests } from '../launch/terminal-mobile-subscribe-tab-mount.spec'
import { registerOrcaRuntimeTabIdCollisionTests } from '../../orca-runtime-tests/orca-runtime-tab-id-collision.spec'
registerTerminalMobileSubscribeTabMountTests()
registerOrcaRuntimeTabIdCollisionTests()
@@ -1,44 +0,0 @@
import { describe, expect, it } from 'vitest'
import {
requestRemoteWorktreeSleep,
type RemoteWorkspaceCall
} from './remote-workspace-terminal-sleep'
const noTerminals = { terminals: [], totalCount: 0, truncated: false }
/** A paired server answering by method; a function answer runs per call. */
function server(answers: Record<string, unknown>) {
const methods: string[] = []
const call: RemoteWorkspaceCall = async (method) => {
methods.push(method)
const answer = answers[method]
return typeof answer === 'function' ? answer() : answer
}
return { call, methods }
}
const notFound = () => {
throw Object.assign(new Error('unknown method'), { code: 'method_not_found' })
}
describe('requestRemoteWorktreeSleep', () => {
it('stops an older server and polls until no terminal is live', async () => {
const { call, methods } = server({
'terminal.sleep': notFound,
'terminal.stop': {},
'terminal.list': noTerminals
})
expect(await requestRemoteWorktreeSleep(call, 'r::/w')).toEqual([])
expect(methods).toEqual(['terminal.sleep', 'terminal.stop', 'terminal.list'])
})
it('takes no legacy path for any other refusal', async () => {
const { call, methods } = server({
'terminal.sleep': () => {
throw Object.assign(new Error('denied'), { code: 'forbidden' })
}
})
await expect(requestRemoteWorktreeSleep(call, 'r::/w')).rejects.toThrow('denied')
expect(methods).toEqual(['terminal.sleep'])
})
})
@@ -1,114 +0,0 @@
import { describe, expect, it, vi } from 'vitest'
import type { Repo } from '../../../../shared/repo-types'
import type { Worktree } from '../../../../shared/worktree/types'
import { startRuntimeLocalWorktreeTerminals } from './runtime-local-worktree-terminal-startup'
vi.mock('../../../../shared/layout-owner-switch', () => ({ ORCA_LAYOUT_OWNER_IS_MAIN: true }))
const repo: Repo = {
id: 'repo-1',
path: '/repo',
displayName: 'repo',
badgeColor: 'blue',
addedAt: 1
}
const worktree: Worktree = {
id: 'worktree-1',
repoId: repo.id,
path: '/worktree',
head: 'abc',
branch: 'feature',
isBare: false,
isMainWorktree: false,
displayName: 'feature',
comment: '',
linkedIssue: null,
linkedPR: null,
linkedLinearIssue: null,
isArchived: false,
isUnread: false,
isPinned: false,
sortOrder: 0,
lastActivityAt: 1
}
type StartupArgs = Parameters<typeof startRuntimeLocalWorktreeTerminals>[0]
function createPorts() {
const createTerminal = vi.fn<StartupArgs['ports']['createTerminal']>().mockResolvedValue({
handle: 'term-1',
worktreeId: worktree.id,
title: null
})
const ports: StartupArgs['ports'] = {
canSpawn: true,
createTerminal,
pasteDraft: vi.fn(),
sendFollowup: vi.fn(),
provision: vi.fn().mockResolvedValue({ setupSpawned: false, setupTerminalHandle: null }),
activate: vi.fn()
}
return { createTerminal, ports }
}
describe('layout-owner build: a create whose agent failed to start', () => {
it('still provisions setup and default tabs in main, and hands the window nothing to retry', async () => {
const { ports, createTerminal } = createPorts()
createTerminal.mockRejectedValueOnce(new Error('pty unavailable'))
const setup = { runnerScriptPath: '/repo/setup.sh', envVars: {} }
const defaultTabs = { runCommands: true, tabs: [{ title: 'Dev', command: 'pnpm dev' }] }
await startRuntimeLocalWorktreeTerminals({
request: {
repoSelector: repo.id,
name: 'recover',
activate: true,
awaitTerminalProvisioning: true
},
repo,
worktree,
startup: { command: 'codex' },
setup,
defaultTabs,
ports
})
expect(createTerminal.mock.calls[0]![1]).toMatchObject({ keepPaneOnStartFailure: true })
// The agent's pane stays, showing the failure, so no extra shell is seeded beside it.
expect(ports.provision).toHaveBeenCalledWith(
expect.objectContaining({ setup, defaultTabs, hasStartupTerminal: true })
)
// Setup did not start here either (the fake spawns nothing), and still stays main's.
expect(ports.activate).toHaveBeenCalledWith(
repo.id,
worktree.id,
undefined,
undefined,
undefined
)
})
it('asks main to start the agent even when its launch failed or no terminal service runs', async () => {
const repair = new Error('agent runtime needs repair')
for (const [canSpawn, startupLaunchRuntime] of [
[true, repair],
[false, undefined]
] as const) {
const { ports, createTerminal } = createPorts()
ports.canSpawn = canSpawn
createTerminal.mockRejectedValueOnce(new Error('start failed'))
await startRuntimeLocalWorktreeTerminals({
request: { repoSelector: repo.id, name: 'recover', activate: true },
repo,
worktree,
startup: { command: 'codex' },
...(startupLaunchRuntime ? { startupLaunchRuntime } : {}),
ports
})
expect(createTerminal).toHaveBeenCalledTimes(1)
expect(createTerminal.mock.calls[0]![1]).toMatchObject({
keepPaneOnStartFailure: true,
...(startupLaunchRuntime ? { startFailure: repair } : {})
})
}
})
})
@@ -1,42 +0,0 @@
// #36923: a workspace this host runs on a recipe VM over its SSH target is opened through this
// host's own worktree.activate (window, CLI, paired clients), so that opening wakes the VM.
import { describe, expect, it, vi } from 'vitest'
await import('../../orca-runtime-tests/runtime-test-module-registration.spec')
const { OrcaRuntimeService } = await import('../../orca-runtime')
await import('../../orca-runtime-tests/orca-runtime-test-lifecycle.spec')
const { TEST_WINDOW_ID, TEST_WORKTREE_ID, store } =
await import('../../orca-runtime-tests/orca-runtime-test-fixtures.spec')
describe('activateManagedWorktree and a recipe VM', () => {
it('wakes the VM behind a workspace this host activates', async () => {
const resumeVm = vi.fn(async () => null)
const runtime = new OrcaRuntimeService(store, undefined, {
workspaceSleepPlatform: {
callEnvironment: vi.fn(async () => {
throw new Error('no paired servers')
}),
ownsVm: (workspaceId: string) => workspaceId === TEST_WORKTREE_ID,
acceptWake: () => ({ sshHeld: false, sshUserDisconnects: 0 }),
suspendVm: vi.fn(async () => {}),
resumeVm
}
})
runtime.attachWindow(TEST_WINDOW_ID)
runtime.markGraphReady(TEST_WINDOW_ID)
await expect(
runtime.activateManagedWorktree(`id:${TEST_WORKTREE_ID}`, {
notifyClients: false,
clientKind: 'runtime'
})
).resolves.toMatchObject({ worktreeId: TEST_WORKTREE_ID })
// The wake runs on its own, since a recipe's resume can outlast activation.
await vi.waitFor(() =>
expect(resumeVm).toHaveBeenCalledWith(TEST_WORKTREE_ID, {
sshHeld: false,
sshUserDisconnects: 0
})
)
})
})
@@ -1,31 +0,0 @@
import { describe, expect, it } from 'vitest'
import type { Repo } from '../../../shared/repo-types'
import type { RuntimeClientEvent } from '../../../shared/runtime-client-events'
import { OrcaRuntimeService } from '../orca-runtime'
import type { RuntimeStore } from '../runtime-store-contract'
import { partialRuntimeStore } from '../orca-runtime-tests/runtime-store-partial-fixture'
function catalogEventsFor(repo: Repo): RuntimeClientEvent[] {
const store: Pick<RuntimeStore, 'getRepos'> = { getRepos: () => [repo] }
const runtime = new OrcaRuntimeService(partialRuntimeStore(store))
const events: RuntimeClientEvent[] = []
runtime.onClientEvent((event) => events.push(event))
runtime.notifyWorktreeCatalogChangedForRemoteClients(repo.id)
return events.filter((event) => event.type === 'worktreesChanged')
}
const baseRepo: Repo = { id: 'r', path: '/r', displayName: 'r', badgeColor: 'blue', addedAt: 1 }
describe('notifyWorktreeCatalogChangedForRemoteClients', () => {
it('tells remote clients about a local repo', () => {
expect(catalogEventsFor(baseRepo)).toHaveLength(1)
})
it('skips an SSH repo named only by its execution host', () => {
expect(catalogEventsFor({ ...baseRepo, executionHostId: 'ssh:target-1' })).toHaveLength(0)
})
it('skips an SSH repo named by its legacy connection id', () => {
expect(catalogEventsFor({ ...baseRepo, connectionId: 'target-1' })).toHaveLength(0)
})
})
@@ -1,83 +0,0 @@
// A background start held for a stopped WSL distro publishes `failed` with the hold, and Start starts it as the user.
import { describe, expect, it, vi } from 'vitest'
import { wslDistroHoldMessage } from '../../shared/wsl-distro-stopped-hold'
import { LOCAL_EXECUTION_HOST_ID, type ExecutionHostId } from '../../shared/execution-host'
import { committingSpawn } from './orca-runtime-tests/bind-first-pane.test-fixture'
vi.mock('../../shared/layout-owner-switch', () => ({ ORCA_LAYOUT_OWNER_IS_MAIN: true }))
// Preserve side-effect order: mocks, lifecycle, fixtures.
await import('./orca-runtime-tests/runtime-test-module-registration.spec')
const { OrcaRuntimeService } = await import('./orca-runtime')
await import('./orca-runtime-tests/orca-runtime-test-lifecycle.spec')
const {
HEADLESS_LEAF_ID,
TEST_WORKTREE_ID,
makeRuntimeStoreWithWorkspaceSession,
makeWorkspaceSessionWithHeadlessTerminal
} = await import('./orca-runtime-tests/orca-runtime-test-fixtures.spec')
const PANE = { worktreeId: TEST_WORKTREE_ID, tabId: 'host-tab', leafId: HEADLESS_LEAF_ID }
const HOLD = wslDistroHoldMessage('Ubuntu', 'stopped')
type SpawnArgs = {
sessionId?: string
worktreeId?: string
tabId?: string
leafId?: string
wslUserAct?: true
}
async function heldPaneRuntime() {
const { runtimeStore } = makeRuntimeStoreWithWorkspaceSession(
makeWorkspaceSessionWithHeadlessTerminal()
)
const runtime = new OrcaRuntimeService(
Object.assign(runtimeStore, {
getWorkspaceSessionHostIds: (): ExecutionHostId[] => [LOCAL_EXECUTION_HOST_ID]
})
)
runtime.subscribeWorkspaceLayouts(() => {})
const spawn = vi.fn(committingSpawn(runtime, async (_args: SpawnArgs) => ({ id: 'pty-started' })))
spawn.mockRejectedValueOnce(new Error(HOLD))
runtime.setPtyController({
spawn,
write: () => true,
kill: () => false,
stopAndWait: async () => true,
getForegroundProcess: async () => null,
listProcesses: async () => []
})
runtime.syncWindowGraph(0, { tabs: [], leaves: [] })
await expect(runtime.ensureRestoredPaneStarted(PANE)).rejects.toThrow(HOLD)
return { runtime, spawn }
}
function publishedPane(runtime: InstanceType<typeof OrcaRuntimeService>) {
const { snapshot, unsubscribe } = runtime.subscribeWorkspaceLayouts(() => {})
unsubscribe()
return snapshot
.find((entry) => entry.key === TEST_WORKTREE_ID)
?.layout.tabs.flatMap((tab) => tab.terminal?.panes ?? [])
.find((pane) => pane.leafId === HEADLESS_LEAF_ID)
}
describe('a pane main held for a stopped WSL distro (layout-owner build)', () => {
it('publishes failed, not held, so older desktops never offer the newer-build Reopen', async () => {
const { runtime, spawn } = await heldPaneRuntime()
expect(publishedPane(runtime)).toMatchObject({ status: 'failed', statusReason: HOLD })
// Main's own start is background work: it never names the user's act.
expect(spawn.mock.calls[0]?.[0]).not.toHaveProperty('wslUserAct')
})
it("starts as the user's act on Start, and the failure clears", async () => {
const { runtime, spawn } = await heldPaneRuntime()
await expect(runtime.startWslHeldTerminalPanes([PANE])).resolves.toEqual({ started: true })
expect(spawn).toHaveBeenCalledTimes(2)
expect(spawn).toHaveBeenLastCalledWith(expect.objectContaining({ wslUserAct: true }))
expect(publishedPane(runtime)?.status).toBeUndefined()
})
})
@@ -1,76 +0,0 @@
import { describe, expect, it } from 'vitest'
import { WslHostWindowsDriveRefusalError } from '../../shared/wsl-host-windows-drive-refusal'
import {
refuseWslHostWindowsDrivePath,
type WslHostWindowsDriveGuardDeps
} from './terminal/launch/wsl-host-windows-drive-guard'
function deps(overrides: Partial<WslHostWindowsDriveGuardDeps> = {}): WslHostWindowsDriveGuardDeps {
return {
isWslHost: () => true,
readMounts: async () => [
{ mountPoint: '/mnt/c', windowsRoot: 'C:\\' },
{ mountPoint: '/win/d drive', windowsRoot: 'D:\\' },
{ mountPoint: '/usr/lib/wsl/drivers', windowsRoot: null }
],
realpath: async (path) => path,
...overrides
}
}
async function refusal(path: string, guardDeps = deps()): Promise<unknown> {
return refuseWslHostWindowsDrivePath(path, guardDeps).then(
() => null,
(error: unknown) => error
)
}
describe('refuseWslHostWindowsDrivePath', () => {
it('refuses a drive mount with the path this computer uses for it', async () => {
const error = await refusal('/mnt/c/Users/me/src/')
expect(error).toBeInstanceOf(WslHostWindowsDriveRefusalError)
expect(error).toMatchObject({ data: { windowsPath: 'C:\\Users\\me\\src' } })
await expect(refusal('/win/d drive')).resolves.toMatchObject({
data: { windowsPath: 'D:\\' }
})
})
it('refuses a Linux path that resolves onto a drive', async () => {
const error = await refusal('/home/me/src', deps({ realpath: async () => '/mnt/c/work/src' }))
expect(error).toMatchObject({ data: { windowsPath: 'C:\\work\\src' } })
})
it('still refuses a missing folder by its spelling', async () => {
const error = await refusal(
'/mnt/c/new',
deps({ realpath: () => Promise.reject(new Error('ENOENT')) })
)
expect(error).toMatchObject({ data: { windowsPath: 'C:\\new' } })
})
it('refuses without an alternative when the mount does not name its drive', async () => {
await expect(refusal('/usr/lib/wsl/drivers/x')).resolves.toMatchObject({
data: { windowsPath: null }
})
})
it('rereads mounts per check, so a drive mounted after orcad started is caught', async () => {
let mounted = false
const guardDeps = deps({
readMounts: async () => (mounted ? [{ mountPoint: '/mnt/q', windowsRoot: 'Q:\\' }] : [])
})
await expect(refusal('/mnt/q/src', guardDeps)).resolves.toBeNull()
mounted = true
await expect(refusal('/mnt/q/src', guardDeps)).resolves.toMatchObject({
data: { windowsPath: 'Q:\\src' }
})
})
it('allows Linux folders, look-alike prefixes, and every non-WSL host', async () => {
await expect(refusal('/home/me/src')).resolves.toBeNull()
await expect(refusal('/mnt/cache/src')).resolves.toBeNull()
// SSH hosts and the desktop never carry the WSL-host marker.
await expect(refusal('/mnt/c/src', deps({ isWslHost: () => false }))).resolves.toBeNull()
await expect(refusal('/mnt/c/src', deps({ readMounts: async () => [] }))).resolves.toBeNull()
})
})
@@ -1,44 +0,0 @@
import { execFileSync } from 'node:child_process'
import { mkdtemp, rm } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, expect, it, vi } from 'vitest'
import type * as GitExecFile from '../git/command-runner/git-exec-file'
import { gitExecFileAsync } from '../git/command-runner/git-exec-file'
import { getForgeProviderForRepository } from './forge-provider-detection'
vi.mock('../git/command-runner/git-exec-file', async (importOriginal) => {
const actual = await importOriginal<typeof GitExecFile>()
return { ...actual, gitExecFileAsync: vi.fn(actual.gitExecFileAsync) }
})
const roots: string[] = []
afterEach(async () => {
await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true })))
})
function remoteUrlReads(): number {
return vi
.mocked(gitExecFileAsync)
.mock.calls.filter(([args]) => args[0] === 'remote' && args[1] === 'get-url').length
}
it('reads a repo remote once per forge detection and rereads it after the remote changes', async () => {
const repoPath = await mkdtemp(join(tmpdir(), 'orca-forge-remote-reads-'))
roots.push(repoPath)
execFileSync('git', ['init', '--quiet'], { cwd: repoPath })
vi.mocked(gitExecFileAsync).mockClear()
await expect(
getForgeProviderForRepository({ repoPath, executionHostId: 'local' })
).resolves.toBeNull()
expect(remoteUrlReads()).toBe(1)
execFileSync('git', ['remote', 'add', 'origin', 'https://github.com/stablyai/orca.git'], {
cwd: repoPath
})
vi.mocked(gitExecFileAsync).mockClear()
const provider = await getForgeProviderForRepository({ repoPath, executionHostId: 'local' })
expect(provider?.id).toBe('github')
expect(remoteUrlReads()).toBe(1)
})
@@ -25,24 +25,6 @@ describe('sanitizeOpenAiTranscriptionErrorMessage', () => {
describe('OpenAiTranscriptionSession', () => {
afterEach(() => vi.unstubAllGlobals())
it('sends the dictation language only when one is chosen', async () => {
const languages: (FormDataEntryValue | null)[] = []
vi.stubGlobal('fetch', async (_url: string, init: RequestInit) => {
languages.push(init.body instanceof FormData ? init.body.get('language') : 'not multipart')
return new Response(JSON.stringify({ text: 'ok' }))
})
for (const language of ['ko', undefined]) {
const session = new OpenAiTranscriptionSession(
'openai-gpt-4o-mini-transcribe',
() => 'test-key',
language
)
session.feedAudio(new Float32Array([0.1]), 16000)
await session.finish()
}
expect(languages).toEqual(['ko', null])
})
it('preserves multipart WAV bytes across owned, resampled and empty audio chunks', async () => {
const uploads: Buffer[] = []
vi.stubGlobal('fetch', async (url: string, init: RequestInit) => {
@@ -96,23 +78,4 @@ describe('OpenAiTranscriptionSession', () => {
-32768, -32768, -16384, 0, 0, 16384, 32767, 32767, 0, 32767, -32768, 0, 24575
])
})
it('keeps the first 10 minutes and reports the cap once instead of throwing', async () => {
const uploadBytes: number[] = []
vi.stubGlobal('fetch', async (_url: string, init: RequestInit) => {
const file = init.body instanceof FormData ? init.body.get('file') : null
uploadBytes.push(file instanceof File ? file.size : -1)
return new Response(JSON.stringify({ text: 'first ten minutes' }))
})
const session = new OpenAiTranscriptionSession(
'openai-gpt-4o-mini-transcribe',
() => 'test-key'
)
const sevenSeconds = new Float32Array(7 * 16000).fill(0.1)
const capReports = Array.from({ length: 90 }, () => session.feedAudio(sevenSeconds, 16000))
expect(capReports.indexOf(true)).toBe(85)
expect(capReports.filter(Boolean)).toHaveLength(1)
expect(await session.finish()).toBe('first ten minutes')
expect(uploadBytes).toEqual([44 + 600 * 16000 * 2])
})
})
-41
View File
@@ -1,41 +0,0 @@
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, describe, expect, it } from 'vitest'
import { installFakeAppEnvironment } from '../../../config/scripts/test-runtime/environment/vitest-host-ports-setup'
import { SPEECH_RECOGNIZER_DIR_ENV } from '../../shared/speech-recognizer-package'
import { getSherpaModulePath, hasLocalSpeechRecognizer } from './stt-worker-paths'
const roots: string[] = []
afterEach(() => {
delete process.env[SPEECH_RECOGNIZER_DIR_ENV]
for (const root of roots.splice(0)) {
rmSync(root, { recursive: true, force: true })
}
})
// orcad: a packaged host with no Electron resources tree.
describe('speech worker paths on orcad (#31174)', () => {
it('has no on-device recognizer unless orca serve names one', () => {
installFakeAppEnvironment({ isPackaged: () => true })
expect(getSherpaModulePath()).toBeNull()
expect(hasLocalSpeechRecognizer()).toBe(false)
})
it("uses the installed app's recognizer that orca serve names", () => {
installFakeAppEnvironment({ isPackaged: () => true })
const dir = mkdtempSync(join(tmpdir(), 'orca-recognizer-'))
roots.push(dir)
writeFileSync(join(dir, 'sherpa-onnx.node'), '')
process.env[SPEECH_RECOGNIZER_DIR_ENV] = dir
expect(getSherpaModulePath()).toBe(dir)
})
it('ignores a named directory that holds no recognizer', () => {
installFakeAppEnvironment({ isPackaged: () => true })
const dir = mkdtempSync(join(tmpdir(), 'orca-recognizer-'))
roots.push(dir)
process.env[SPEECH_RECOGNIZER_DIR_ENV] = dir
expect(getSherpaModulePath()).toBeNull()
})
})
@@ -1,169 +0,0 @@
import { beforeEach, describe, expect, it, vi, type Mock } from 'vitest'
import type { LegacyRelayBridgedPane } from '../../../shared/legacy-relay-bridge-contract'
import { toAppSshPtyId } from '../../../shared/ssh-pty-id'
import type { SshRemotePtyLease } from '../../../shared/ssh-types'
import type { LegacyRelayTargetBridgeDeps } from './legacy-relay-target-bridge'
import type { LegacyRelayBridgeHost } from './legacy-relay-bridge-registry'
import { unusedSshConnection } from '../unused-host-transport.test-fixture'
const TARGET = 'ssh-box'
const PTY = toAppSshPtyId(TARGET, 'pty-1')
const renderer = vi.hoisted(() => ({
listeners: new Set<(panes: LegacyRelayBridgedPane[]) => void>()
}))
// The bridge's relay listing is out of scope: this one lists the leased pane live on refresh.
vi.mock('./legacy-relay-target-bridge', () => ({
LegacyRelayTargetBridge: class {
private listed: LegacyRelayBridgedPane[] = []
constructor(private readonly deps: LegacyRelayTargetBridgeDeps) {}
snapshot(): LegacyRelayBridgedPane[] {
return this.listed
}
livePaneCount(): number {
return this.listed.length
}
async refresh(): Promise<void> {
this.listed = this.deps.leases().map((row) => ({
ptyId: toAppSshPtyId(this.deps.targetId, row.ptyId),
targetId: this.deps.targetId,
tabId: row.tabId ?? '',
...(row.leafId ? { leafId: row.leafId } : {}),
state: 'live' as const
}))
this.deps.onPanesChanged()
}
async attach(_ptyId: string, atSnapshot?: (replay: string) => void) {
atSnapshot?.('SCREEN')
return { status: 'attached' as const, replay: 'SCREEN' }
}
dispose(): void {
this.listed = []
this.deps.onPanesChanged()
}
}
}))
vi.stubGlobal('window', {
api: {
legacyRelayBridge: {
listPanes: async () => [],
onPanes: (listener: (panes: LegacyRelayBridgedPane[]) => void) => {
renderer.listeners.add(listener)
return () => renderer.listeners.delete(listener)
}
}
}
})
const registry = await import('./legacy-relay-bridge-registry')
const { legacyRelayBridgePtyIdForPane } =
await import('../../../renderer/src/components/terminal-pane/pty-transport/legacy-relay/legacy-relay-bridge-panes')
let leases: SshRemotePtyLease[]
let report: Mock<LegacyRelayBridgeHost['report']>
function lease(): SshRemotePtyLease {
return {
targetId: TARGET,
ptyId: 'pty-1',
tabId: 'tab-1',
leafId: 'leaf-1',
state: 'detached',
createdAt: 1,
updatedAt: 1
}
}
beforeEach(async () => {
leases = [lease()]
report = vi.fn()
registry.installLegacyRelayBridge({
leases: () => leases,
retireLease: vi.fn(),
recordStop: vi.fn(),
connection: () => null,
// Main publishes to the window as legacy-relay-bridge-ipc.ts does.
onPanesChanged: (panes) => renderer.listeners.forEach((listener) => listener(panes)),
onData: vi.fn(),
onReplay: vi.fn(),
onExit: vi.fn(),
report
})
await registry.startLegacyRelayBridge(TARGET)
})
describe('a hung-up legacy relay bridge (loss of contact is not exit)', () => {
it('keeps the panes it served as unverifiable while their leases are held', () => {
registry.stopLegacyRelayBridge(TARGET)
expect(registry.legacyRelayBridgedPanes()).toEqual([
{ ptyId: PTY, targetId: TARGET, tabId: 'tab-1', leafId: 'leaf-1', state: 'unverifiable' }
])
})
it('empties the list once the target and its leases are removed', () => {
registry.stopLegacyRelayBridge(TARGET)
expect(registry.legacyRelayBridgedPanes()).toHaveLength(1)
leases = []
registry.forgetLegacyRelayTarget(TARGET)
expect(registry.legacyRelayBridgedPanes()).toEqual([])
})
it('publishes an empty list when the next connect finds the leases retired', async () => {
registry.stopLegacyRelayBridge(TARGET)
const published: LegacyRelayBridgedPane[][] = []
const record = (panes: LegacyRelayBridgedPane[]): void => void published.push(panes)
renderer.listeners.add(record)
leases = []
await registry.startLegacyRelayBridge(TARGET)
renderer.listeners.delete(record)
expect(published.at(-1)).toEqual([])
})
it('still sends a pane that remounts during the reconnect gap to the bridge', () => {
// Disconnect, then the pane's transport is rebuilt before the next connect starts a bridge.
registry.stopLegacyRelayBridge(TARGET)
expect(
legacyRelayBridgePtyIdForPane({
tabId: 'web-terminal-tab-1',
leafId: 'leaf-1',
boundPtyId: null
})
).toBe(PTY)
})
})
describe('attaching a bridged pane', () => {
it('sends a reattach screen in output order and only once', async () => {
const replays: string[] = []
const connection = unusedSshConnection()
registry.installLegacyRelayBridge({
leases: () => leases,
retireLease: vi.fn(),
recordStop: vi.fn(),
connection: () => connection,
onPanesChanged: vi.fn(),
onData: vi.fn(),
onReplay: (_ptyId, replay) => replays.push(replay),
onExit: vi.fn(),
report: vi.fn()
})
await expect(registry.attachLegacyRelayPane(PTY)).resolves.toEqual({
status: 'attached',
replay: 'SCREEN'
})
expect(replays).toEqual([])
await expect(registry.attachLegacyRelayPane(PTY, true)).resolves.toEqual({
status: 'attached',
replay: ''
})
expect(replays).toEqual(['SCREEN'])
})
})
@@ -1,38 +1,10 @@
import { describe, expect, it } from 'vitest'
import { WS_BIND_HOST_LOOPBACK } from '../runtime/rpc/server/runtime-rpc-pairing-types'
import { normalizeServeModeArgv } from './serve-mode-argv'
import '../runtime/rpc/server/runtime-rpc-pairing-types'
import './serve-mode-argv'
import { getServeOptions } from './serve-options'
import { resolveServePinnedBindHost } from './serve-pinned-bind-host'
import './serve-pinned-bind-host'
describe('Electron serve listener pin', () => {
it('leaves the listener unpinned when --bind is absent, as before --bind existed', () => {
const options = getServeOptions(['/AppRun', '--serve', '--serve-port', '6768'])
expect(options).not.toHaveProperty('bind')
expect(resolveServePinnedBindHost({ orcadSidecar: false, serveOptions: options })).toBe(
undefined
)
expect(resolveServePinnedBindHost({ orcadSidecar: false, serveOptions: null })).toBe(undefined)
})
it('pins an explicit bind from either argv form, normalizing localhost', () => {
const internal = getServeOptions(['/AppRun', '--serve', '--serve-bind', '100.64.1.20'])
const cliForm = getServeOptions(
normalizeServeModeArgv(['/AppRun', 'serve', '--bind', 'localhost'])
)
expect(resolveServePinnedBindHost({ orcadSidecar: false, serveOptions: internal })).toBe(
'100.64.1.20'
)
expect(resolveServePinnedBindHost({ orcadSidecar: false, serveOptions: cliForm })).toBe(
'127.0.0.1'
)
})
it('keeps the orcad browser sidecar on loopback regardless of --bind', () => {
expect(
resolveServePinnedBindHost({ orcadSidecar: true, serveOptions: { bind: '0.0.0.0' } })
).toBe(WS_BIND_HOST_LOOPBACK)
})
it('rejects hostnames, empty values and --mobile-pairing with a narrower bind', () => {
expect(() => getServeOptions(['/AppRun', '--serve', '--serve-bind'])).toThrow(
'Missing value for --serve-bind.'
@@ -1,25 +0,0 @@
import { describe, expect, it, vi } from 'vitest'
import { buildClipboardImageThumbnail } from './clipboard-image-thumbnail'
function fakeImage(overrides: Partial<Parameters<typeof buildClipboardImageThumbnail>[0]>) {
return {
isEmpty: () => false,
getSize: () => ({ height: 10, width: 10 }),
resize: vi.fn(() => ({ toDataURL: () => 'data:image/png;base64,SMALL' })),
toDataURL: () => 'data:image/png;base64,FULL',
...overrides
}
}
describe('buildClipboardImageThumbnail', () => {
it('downscales to the thumbnail budget but reports the source dimensions', () => {
const image = fakeImage({ getSize: () => ({ height: 1600, width: 3200 }) })
expect(buildClipboardImageThumbnail(image)).toEqual({
dataUrl: 'data:image/png;base64,SMALL',
height: 1600,
width: 3200
})
expect(image.resize).toHaveBeenCalledWith({ height: 160, quality: 'good', width: 320 })
})
})
@@ -1,46 +0,0 @@
import { EventEmitter } from 'node:events'
import { describe, expect, it, vi } from 'vitest'
const handlers = vi.hoisted(() => new Map<string, () => unknown>())
vi.mock('electron', () => ({
ipcMain: {
handle: (channel: string, handler: () => unknown) => handlers.set(channel, handler),
removeHandler: (channel: string) => handlers.delete(channel)
}
}))
import { installMainWindowActivityRelay, type ActivityWindow } from './main-window-activity'
function fakeWindow() {
const state = { focused: true, visible: true, minimized: false }
const sent: boolean[] = []
const emitter = new EventEmitter()
const window: ActivityWindow = {
isDestroyed: () => false,
isFocused: () => state.focused,
isVisible: () => state.visible,
isMinimized: () => state.minimized,
on: (event, listener) => emitter.on(event, listener),
webContents: { isDestroyed: () => false, send: (_channel, active) => sent.push(active) }
}
return { window, emitter, state, sent }
}
describe('installMainWindowActivityRelay', () => {
it('reports the window inactive when it blurs, minimizes or hides, once per change', () => {
const { window, emitter, state, sent } = fakeWindow()
const relay = installMainWindowActivityRelay(window)
expect(handlers.get('window:isActive')?.()).toBe(true)
emitter.emit('focus')
state.minimized = true
emitter.emit('minimize')
emitter.emit('blur')
state.minimized = false
emitter.emit('restore')
state.visible = false
emitter.emit('hide')
expect(sent).toEqual([true, false, true, false])
relay.dispose()
expect(handlers.has('window:isActive')).toBe(false)
})
})
@@ -33,23 +33,4 @@ describe('a long paste saved as a file', () => {
expect(path.dirname(path.dirname(saved))).toBe(store.rootDir)
expect(readFileSync(saved, 'utf8')).toBe('log line ✓\n')
})
it("goes to the chat's store on its paired server, under a text paste's name", async () => {
mocks.upload.mockResolvedValue('/srv/agent-session-attachments/u1/orca-paste-1-a.txt')
const target = {
sessionId: 'session-1',
expectedEnvironmentPairingRevision: 7,
expectedEnvironmentRuntimeId: 'runtime-a'
}
await expect(
savePastedTextAsFile({
text: 'diff',
runtimeEnvironmentId: 'env-1',
agentSessionAttachment: target
})
).resolves.toBe('/srv/agent-session-attachments/u1/orca-paste-1-a.txt')
const [sentTarget, environmentId, , buffer, name] = mocks.upload.mock.calls[0] ?? []
expect([sentTarget, environmentId, buffer.toString('utf8')]).toEqual([target, 'env-1', 'diff'])
expect(name).toMatch(/^orca-paste-\d+-.+\.txt$/)
})
})
@@ -1,60 +0,0 @@
import { describe, expect, it } from 'vitest'
import type { Repo } from '../../../shared/repo-types'
import type { WorkspaceLineage } from '../../../shared/worktree/lineage-types'
import type { Worktree } from '../../../shared/worktree/types'
import { projectWorktreeLineageWithHostPeers } from './worktree-lineage-host-peers'
const repo: Repo = { id: 'r', path: '/r', displayName: 'r', badgeColor: '', addedAt: 0 }
function row(id: string): Worktree {
return {
id,
instanceId: `${id}#1`,
repoId: 'r',
path: id.split('::')[1],
head: 'abc',
branch: 'refs/heads/main',
isBare: false,
isMainWorktree: false,
displayName: id,
comment: '',
linkedIssue: null,
linkedPR: null,
linkedLinearIssue: null,
isArchived: false,
isUnread: false,
isPinned: false,
sortOrder: 0,
lastActivityAt: 0
}
}
const record: WorkspaceLineage = {
childWorkspaceKey: 'worktree:r::/r/child',
childInstanceId: 'r::/r/child#1',
parentWorkspaceKey: 'folder:f-1',
parentInstanceId: 'f-1',
origin: 'manual',
capture: { source: 'manual-action', confidence: 'explicit' },
createdAt: 1
}
describe('projectWorktreeLineageWithHostPeers', () => {
it('attaches each listed row’s own folder parent, so a twin on another host keeps it', () => {
const projected = projectWorktreeLineageWithHostPeers(
{
getRepos: () => [repo],
getAllWorktreeMeta: () => ({}),
getAllWorktreeMetaForHost: () => ({}),
getAllWorktreeLineage: () => ({}),
getAllWorkspaceLineage: () => ({ 'worktree:r::/r/child': record })
},
repo,
[row('r::/r/child'), row('r::/r/other')]
)
expect(projected.map((entry) => [entry.id, entry.workspaceLineage])).toEqual([
['r::/r/child', record],
['r::/r/other', null]
])
})
})
@@ -1,46 +0,0 @@
import { describe, expect, it } from 'vitest'
import { nativeChatComposerSendState } from './native-chat-composer-send-state'
function chordSteersHead(
overrides: Partial<Parameters<typeof nativeChatComposerSendState>[0]> & {
followUp?: 'queue' | 'steer'
} = {}
): boolean {
const { followUp = 'queue', ...input } = overrides
const working = input.isWorking ?? false
const structuredTransport = {
optionSnapshot: [],
sendFollowUp: { action: followUp, offersChoice: working }
}
return nativeChatComposerSendState({
agent: 'claude',
draft: '',
messageDelivery: true,
imageAttachments: [],
pastedTextSaving: false,
disabled: false,
structuredTransport,
editSession: undefined,
isWorking: false,
canStop: true,
...input
}).chordSteersHead
}
describe('nativeChatComposerSendState chordSteersHead', () => {
it('sends the head card from an empty composer where steering means sending it now', () => {
// No turn runs (a paused queue after Stop): the card's own Send.
expect(chordSteersHead()).toBe(true)
// Mid-turn with follow-ups queued: the chord's other way is a steer.
expect(chordSteersHead({ isWorking: true })).toBe(true)
})
it.each([
['the chord queues mid-turn', { isWorking: true, followUp: 'steer' as const }],
['a queued card is being edited', { editSession: { canSave: true } }],
['the composer submits a goal, not a message', { messageDelivery: false }]
])('never does where %s', (_case, overrides) => {
expect(chordSteersHead(overrides)).toBe(false)
})
})
@@ -1,41 +0,0 @@
import { describe, expect, it } from 'vitest'
import {
REMOTE_RUNTIME_DISCONNECTED_INPUT_GRACE_MS,
createRemoteRuntimeDisconnectedInputGrace
} from './remote-runtime-disconnected-input-grace'
describe('disconnected input grace', () => {
it('expires once the latch outlives its grace', () => {
let now = 0
const grace = createRemoteRuntimeDisconnectedInputGrace(() => now)
grace.start()
now = REMOTE_RUNTIME_DISCONNECTED_INPUT_GRACE_MS - 1
expect(grace.isExpired()).toBe(false)
now += 1
expect(grace.isExpired()).toBe(true)
})
it('carries a retry begun inside the grace, then measures the next latch from the first', () => {
let now = 0
const grace = createRemoteRuntimeDisconnectedInputGrace(() => now)
grace.start()
now = REMOTE_RUNTIME_DISCONNECTED_INPUT_GRACE_MS - 10_000
grace.carryThroughRetry()
now = REMOTE_RUNTIME_DISCONNECTED_INPUT_GRACE_MS + 60_000
expect(grace.isExpired()).toBe(false)
// The retry gave up again: a fresh latch must not restart the clock.
grace.start()
expect(grace.isExpired()).toBe(true)
})
it('has nothing to carry before any latch, and forgets everything on reset', () => {
let now = 0
const grace = createRemoteRuntimeDisconnectedInputGrace(() => now)
grace.carryThroughRetry()
grace.start()
now = REMOTE_RUNTIME_DISCONNECTED_INPUT_GRACE_MS
expect(grace.isExpired()).toBe(true)
grace.reset()
expect(grace.isExpired()).toBe(false)
})
})
@@ -1,57 +0,0 @@
import { afterEach, beforeEach, expect, it, vi } from 'vitest'
import {
_resetTerminalPaneRecoveryForTests,
captureTerminalPaneRecoveryGeneration
} from './terminal-pane-recovery'
import { requestAndSettle } from './terminal-recovery-ledger-test-driver'
import {
recoveryLedgerMocks as mocks,
resetRecoveryLedgerStore,
setTerminalTabs
} from './terminal-recovery-ledger-test-store'
vi.mock('@/store/index', async () => {
const store = await import('./terminal-recovery-ledger-test-store')
return { useAppStore: { getState: () => store.recoveryLedgerStoreState() } }
})
vi.mock('@/lib/crash-breadcrumb-recorder', async () => {
const store = await import('./terminal-recovery-ledger-test-store')
return { recordRendererCrashBreadcrumb: store.recoveryLedgerMocks.recordRendererCrashBreadcrumb }
})
beforeEach(() => {
_resetTerminalPaneRecoveryForTests()
resetRecoveryLedgerStore()
setTerminalTabs([{ id: 'tab-1' }])
})
afterEach(() => {
vi.useRealTimers()
})
it('stops re-binding at the window cap when the host keeps contradicting its exit record', async () => {
vi.useFakeTimers()
const rebind = () =>
requestAndSettle(
{
tabId: 'tab-1',
ptyId: 'remote:env@@terminal-1',
reason: 'host-rebound-exited-pane',
trigger: 'host-evidence',
terminalRecoveryGeneration: captureTerminalPaneRecoveryGeneration('tab-1')
},
// Each remounted pane is settled as exited again by the same contradicting host.
'failed'
)
const outcomes: boolean[] = []
for (let attempt = 0; attempt < 5; attempt += 1) {
vi.setSystemTime(attempt * 20_000)
outcomes.push(await rebind())
}
expect(outcomes).toEqual([true, true, true, false, false])
await vi.advanceTimersByTimeAsync(10 * 60_000)
expect(mocks.remountTerminalTabForRecovery).toHaveBeenCalledTimes(3)
})
@@ -1,45 +0,0 @@
// @vitest-environment happy-dom
import '@testing-library/jest-dom/vitest'
import { cleanup, render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { afterEach, describe, expect, it, vi } from 'vitest'
import { TerminalSleepingPaneOverlay } from './TerminalSleepingPaneOverlay'
vi.mock('@/i18n/i18n', () => ({
translate: (_key: string, fallback: string) => fallback
}))
afterEach(cleanup)
describe('TerminalSleepingPaneOverlay', () => {
it('resumes once from the button and from anywhere on the pane', async () => {
const onResume = vi.fn()
const user = userEvent.setup()
render(<TerminalSleepingPaneOverlay reason="live_cap" onResume={onResume} />)
await user.click(screen.getByRole('button', { name: 'Resume' }))
expect(onResume).toHaveBeenCalledOnce()
await user.click(screen.getByText('Sleeping — click to resume'))
expect(onResume).toHaveBeenCalledTimes(2)
})
it('offers to resume automatic restores only when the host can clear its pause', async () => {
const onResume = vi.fn()
const onResumeAutomaticRestores = vi.fn()
const user = userEvent.setup()
const { rerender } = render(<TerminalSleepingPaneOverlay reason="paused" onResume={onResume} />)
expect(screen.queryByRole('button', { name: 'Resume automatic restores' })).toBeNull()
rerender(
<TerminalSleepingPaneOverlay
reason="paused"
onResume={onResume}
onResumeAutomaticRestores={onResumeAutomaticRestores}
/>
)
await user.click(screen.getByRole('button', { name: 'Resume automatic restores' }))
expect(onResumeAutomaticRestores).toHaveBeenCalledOnce()
expect(onResume).not.toHaveBeenCalled()
})
})
@@ -1,30 +0,0 @@
import { describe, expect, it } from 'vitest'
import { restoredSnapshotPaintsPrintableContent } from './restored-snapshot-coverage'
describe('restoredSnapshotPaintsPrintableContent', () => {
it('accepts a snapshot with cell content in the frame or the scrollback', () => {
expect(restoredSnapshotPaintsPrintableContent({ data: '\x1b[0m$ ls\r\n' })).toBe(true)
expect(
restoredSnapshotPaintsPrintableContent({
data: '\x1b[H\x1b[2J',
scrollbackAnsi: 'older\r\n'
})
).toBe(true)
})
it('rejects a mode-rehydration-only image, which no amount of output renders to', () => {
expect(
restoredSnapshotPaintsPrintableContent({
data: '\x1b[0m\x1b[?25h\x1b[?7h'
})
).toBe(false)
expect(restoredSnapshotPaintsPrintableContent({ data: '\x1b]0;title\x07' })).toBe(false)
expect(restoredSnapshotPaintsPrintableContent({})).toBe(false)
expect(
restoredSnapshotPaintsPrintableContent({
data: '\x1b[2J',
scrollbackAnsi: ' \r\n\t'
})
).toBe(false)
})
})
@@ -1,39 +0,0 @@
import { afterEach, describe, expect, it, vi } from 'vitest'
import {
captureTerminalShutdownBuffersBestEffort,
shutdownBufferCaptures
} from './shutdown-buffer-captures'
afterEach(() => {
shutdownBufferCaptures.clear()
})
describe('captureTerminalShutdownBuffersBestEffort', () => {
it('continues after one tab capture throws', () => {
const failed = vi.fn(() => {
throw new Error('layout capture failed')
})
const succeeded = vi.fn()
shutdownBufferCaptures.set('tab-failed', failed)
shutdownBufferCaptures.set('tab-succeeded', succeeded)
expect(() =>
captureTerminalShutdownBuffersBestEffort(['tab-failed', 'tab-succeeded'], {
includeLocalBuffers: false
})
).not.toThrow()
expect(failed).toHaveBeenCalledWith({ includeLocalBuffers: false })
expect(succeeded).toHaveBeenCalledWith({ includeLocalBuffers: false })
})
it('reports incomplete coverage when a tab throws or has no registered capture', () => {
shutdownBufferCaptures.set('registered', vi.fn())
shutdownBufferCaptures.set('throwing', () => {
throw new Error('layout capture failed')
})
expect(
captureTerminalShutdownBuffersBestEffort(['registered', 'throwing', 'unregistered'])
).toEqual({ requested: 3, captured: 1 })
})
})
@@ -1,59 +0,0 @@
import { describe, expect, it } from 'vitest'
import { shouldSeedCacheTimerOnInitialTitle } from './cache-timer-seeding'
describe('shouldSeedCacheTimerOnInitialTitle', () => {
it('does not seed for fresh Claude tabs that have not been restored', () => {
expect(
shouldSeedCacheTimerOnInitialTitle({
rawTitle: '✳ Claude Code',
allowInitialIdleSeed: false,
existingTimerStartedAt: null,
promptCacheTimerEnabled: true
})
).toBe(false)
})
it('seeds for restored Claude tabs that are already idle', () => {
expect(
shouldSeedCacheTimerOnInitialTitle({
rawTitle: '✳ Claude Code',
allowInitialIdleSeed: true,
existingTimerStartedAt: null,
promptCacheTimerEnabled: true
})
).toBe(true)
})
it('does not seed for restored Claude tabs that are still working', () => {
expect(
shouldSeedCacheTimerOnInitialTitle({
rawTitle: '⠂ Claude Code',
allowInitialIdleSeed: true,
existingTimerStartedAt: null,
promptCacheTimerEnabled: true
})
).toBe(false)
})
it('does not seed when a timer already exists for the pane', () => {
expect(
shouldSeedCacheTimerOnInitialTitle({
rawTitle: '✳ Claude Code',
allowInitialIdleSeed: true,
existingTimerStartedAt: Date.now(),
promptCacheTimerEnabled: true
})
).toBe(false)
})
it('treats null settings as provisionally enabled during startup hydration', () => {
expect(
shouldSeedCacheTimerOnInitialTitle({
rawTitle: '✳ Claude Code',
allowInitialIdleSeed: true,
existingTimerStartedAt: null,
promptCacheTimerEnabled: null
})
).toBe(true)
})
})
@@ -1,59 +0,0 @@
import { describe, expect, it } from 'vitest'
import {
arePaneTitleOverlayRectsEqual,
clearPaneTitleOverlayRects
} from './pane-title-overlay-rects'
describe('clearPaneTitleOverlayRects', () => {
// Stable identity prevents React #185 loops: a fresh empty object commits another render on every pass.
it('returns the same reference when already empty', () => {
const prev = {}
expect(clearPaneTitleOverlayRects(prev)).toBe(prev)
})
it('clears a populated map to a new empty object', () => {
const prev = { 1: { left: 10, top: 20, width: 30 } }
const next = clearPaneTitleOverlayRects(prev)
expect(next).not.toBe(prev)
expect(next).toEqual({})
})
it('is stable across repeated calls once empty', () => {
const first = clearPaneTitleOverlayRects({ 1: { left: 1, top: 2, width: 3 } })
expect(clearPaneTitleOverlayRects(first)).toBe(first)
})
})
describe('arePaneTitleOverlayRectsEqual', () => {
it('treats sub-pixel drift as equal', () => {
expect(
arePaneTitleOverlayRectsEqual(
{ 1: { left: 10, top: 20, width: 30 } },
{ 1: { left: 10.4, top: 20.4, width: 30.4 } }
)
).toBe(true)
})
it('treats a half-pixel-or-greater move as different', () => {
expect(
arePaneTitleOverlayRectsEqual(
{ 1: { left: 10, top: 20, width: 30 } },
{ 1: { left: 10.5, top: 20, width: 30 } }
)
).toBe(false)
})
it('treats differing pane counts as different', () => {
expect(
arePaneTitleOverlayRectsEqual(
{ 1: { left: 0, top: 0, width: 10 } },
{ 1: { left: 0, top: 0, width: 10 }, 2: { left: 0, top: 0, width: 10 } }
)
).toBe(false)
})
it('reports two empty maps as equal', () => {
expect(arePaneTitleOverlayRectsEqual({}, {})).toBe(true)
})
})
@@ -1,37 +0,0 @@
import { describe, expect, it } from 'vitest'
import { mapRestoredPaneTitlesByPaneId } from './restored-pane-titles'
describe('mapRestoredPaneTitlesByPaneId', () => {
it('restores persisted pane titles onto newly-created pane ids', () => {
const restoredPaneByLeafId = new Map([
['11111111-1111-4111-8111-111111111111', 7],
['22222222-2222-4222-8222-222222222222', 3]
])
expect(
mapRestoredPaneTitlesByPaneId(
{
'11111111-1111-4111-8111-111111111111': 'build logs',
'22222222-2222-4222-8222-222222222222': 'test runner'
},
restoredPaneByLeafId
)
).toEqual({
7: 'build logs',
3: 'test runner'
})
})
it('ignores stale leaf ids and empty persisted titles', () => {
expect(
mapRestoredPaneTitlesByPaneId(
{
'11111111-1111-4111-8111-111111111111': 'build logs',
'22222222-2222-4222-8222-222222222222': '',
'33333333-3333-4333-8333-333333333333': 'closed pane'
},
new Map([['11111111-1111-4111-8111-111111111111', 2]])
)
).toEqual({ 2: 'build logs' })
})
})
@@ -1,40 +0,0 @@
import { describe, expect, it } from 'vitest'
import {
getPreviousVisibleForTerminalPane,
isTerminalPaneVisibilityResume
} from './pane-visibility-identity'
describe('terminal pane visibility resume tracking', () => {
it('ignores previous visibility from a different terminal identity', () => {
expect(
getPreviousVisibleForTerminalPane({
previous: { tabId: 'tab-old', cwd: '/repo', isVisible: false },
tabId: 'tab-new',
cwd: '/repo'
})
).toBeNull()
expect(
getPreviousVisibleForTerminalPane({
previous: { tabId: 'tab-1', cwd: '/repo-old', isVisible: false },
tabId: 'tab-1',
cwd: '/repo-new'
})
).toBeNull()
expect(
getPreviousVisibleForTerminalPane({
previous: { tabId: 'tab-1', cwd: '/repo', isVisible: false },
tabId: 'tab-1',
cwd: '/repo'
})
).toBe(false)
})
it('identifies only hidden-to-visible changes as visibility resumes', () => {
expect(isTerminalPaneVisibilityResume({ previousIsVisible: null, isVisible: true })).toBe(false)
expect(isTerminalPaneVisibilityResume({ previousIsVisible: true, isVisible: true })).toBe(false)
expect(isTerminalPaneVisibilityResume({ previousIsVisible: true, isVisible: false })).toBe(
false
)
expect(isTerminalPaneVisibilityResume({ previousIsVisible: false, isVisible: true })).toBe(true)
})
})
@@ -1,46 +0,0 @@
import { describe, expect, it, vi } from 'vitest'
import { installTerminalSelectionFitGuard } from './terminal-selection-fit-guard'
function setup() {
const terminalElement = new EventTarget()
const ownerDocument = new EventTarget()
const ownerWindow = new EventTarget()
Object.assign(terminalElement, { ownerDocument })
Object.assign(ownerDocument, { defaultView: ownerWindow })
const terminal = { element: terminalElement }
const onSelectionEnd = vi.fn()
const guard = installTerminalSelectionFitGuard(terminal, onSelectionEnd)
return { terminalElement, ownerDocument, ownerWindow, guard, onSelectionEnd }
}
function mouseEvent(button: number): Event {
const event = new Event('mousedown')
Object.defineProperty(event, 'button', { value: button })
return event
}
describe('terminal selection fit guard', () => {
it('stays active from primary mousedown through mouseup and requests convergence', () => {
const { terminalElement, ownerDocument, guard, onSelectionEnd } = setup()
terminalElement.dispatchEvent(mouseEvent(0))
expect(guard.isActive()).toBe(true)
ownerDocument.dispatchEvent(new Event('mouseup'))
expect(guard.isActive()).toBe(false)
expect(onSelectionEnd).toHaveBeenCalledOnce()
guard.dispose()
})
it('ignores non-primary buttons and clears an interrupted drag', () => {
const { terminalElement, ownerDocument, ownerWindow, guard, onSelectionEnd } = setup()
terminalElement.dispatchEvent(mouseEvent(2))
expect(guard.isActive()).toBe(false)
terminalElement.dispatchEvent(mouseEvent(0))
ownerWindow.dispatchEvent(new Event('blur'))
ownerDocument.dispatchEvent(new Event('mouseup'))
expect(guard.isActive()).toBe(false)
expect(onSelectionEnd).not.toHaveBeenCalled()
guard.dispose()
})
})
@@ -1,39 +0,0 @@
// @vitest-environment happy-dom
import { act, cleanup, render, screen } from '@testing-library/react'
import { memo } from 'react'
import { afterEach, expect, it } from 'vitest'
import { i18n } from './i18n'
import { TranslationScopeProvider } from './translation-scope-provider'
import { useTranslate, type Translate } from './use-translate'
afterEach(async () => {
cleanup()
await i18n.changeLanguage('en')
})
it('gives memoized components a new translate function when the language switches', async () => {
i18n.addResourceBundle('de', 'translation', { 'test.useTranslate.greeting': 'Hallo' }, true, true)
const seen: Translate[] = []
const Greeting = memo(function Greeting() {
const t = useTranslate()
seen.push(t)
return <span>{t('test.useTranslate.greeting', 'Hello')}</span>
})
const view = render(
<TranslationScopeProvider>
<Greeting />
</TranslationScopeProvider>
)
view.rerender(
<TranslationScopeProvider>
<Greeting />
</TranslationScopeProvider>
)
expect(screen.getByText('Hello')).toBeTruthy()
const english = seen.at(-1)
await act(() => i18n.changeLanguage('de'))
expect(screen.getByText('Hallo')).toBeTruthy()
expect(seen.at(-1)).not.toBe(english)
})
@@ -1,22 +0,0 @@
import { describe, expect, it } from 'vitest'
import { awaitAgentLaunchPaneStart, settleAgentLaunchPaneStart } from './agent-launch-pane-start'
import { noteAgentLaunchPaneClosedByUser } from './agent-launch-pane-closes'
const TAB = '9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d'
const LEAF = '3f2504e0-4f89-41d3-9a0c-0305e82c3301'
describe("an AI button's pane says whether its agent started", () => {
it('once: the first answer stands, and only for that pane', async () => {
const started = awaitAgentLaunchPaneStart(TAB, LEAF)
settleAgentLaunchPaneStart(TAB, 'another-leaf', 'pty-other')
settleAgentLaunchPaneStart(TAB, LEAF, 'pty-1')
settleAgentLaunchPaneStart(TAB, LEAF, null)
await expect(started).resolves.toBe('pty-1')
})
it('no, when the user closes it before its spawn returns', async () => {
const started = awaitAgentLaunchPaneStart(TAB, LEAF)
noteAgentLaunchPaneClosedByUser(TAB, LEAF)
await expect(started).resolves.toBeNull()
})
})
@@ -1,27 +0,0 @@
import { afterEach, describe, expect, it } from 'vitest'
import { i18n } from '@/i18n/i18n'
import es from '@/i18n/locales/es.json'
import { browserCookieImportFailure } from '../../../../../shared/browser-cookie-import-failure-copy'
import { browserCookieImportFailureText } from './browser-cookie-import-error-copy'
const originalLanguage = i18n.language
afterEach(async () => {
await i18n.changeLanguage(originalLanguage)
})
describe('browserCookieImportFailureText', () => {
it('keeps diagnostic detail verbatim', async () => {
i18n.addResourceBundle('es', 'translation', es)
await i18n.changeLanguage('es')
const failed = browserCookieImportFailure(
'importFailedWithLog',
{ browser: 'Chrome', logPath: '/tmp/diag.log' },
'database is locked {{browser}}'
)
expect(failed.reason).toBe(
'Could not import cookies from Chrome: database is locked {{browser}}. Details were written to /tmp/diag.log.'
)
expect(browserCookieImportFailureText(failed)).toContain('database is locked {{browser}}')
})
})
@@ -1,28 +0,0 @@
import { describe, expect, it } from 'vitest'
import { parsePluginIconThemeArtifact } from '../../../shared/plugins/artifacts/plugin-icon-theme-artifact'
import type { ActivePluginIconTheme } from '../store/plugins/appearance/plugin-icon-themes'
import { getPluginFileTypeIconIndex } from './file-type-icons'
const iconTheme = (maps: Partial<ActivePluginIconTheme>): ActivePluginIconTheme => ({
icons: {},
fileNames: {},
fileExtensions: {},
folderNames: {},
folderNamesExpanded: {},
...maps
})
describe('parsed artifact lookup', () => {
it('finds files named __proto__ and constructor from the parsed theme', () => {
const artifact = parsePluginIconThemeArtifact(
'{"schemaVersion":1,"fileNames":{"__proto__":"a.svg","Constructor":"b.svg"}}'
)
const indexes = Object.fromEntries(
Object.keys(artifact.fileNames).map((key, index) => [key, index])
)
const theme = iconTheme({ fileNames: indexes })
expect(getPluginFileTypeIconIndex(theme, '/a/__proto__')).toBe(0)
expect(getPluginFileTypeIconIndex(theme, '/a/CONSTRUCTOR')).toBe(1)
expect(getPluginFileTypeIconIndex(theme, '/a/other')).toBeNull()
})
})
@@ -1,31 +0,0 @@
import { describe, expect, it } from 'vitest'
import { tintedSidebarHostPillOutlineMix } from './left-sidebar-appearance'
import type { ColorProbe } from './plugin-theme-tokens'
const probe: ColorProbe = (value) => {
const hex = /^#([0-9a-f]{6})$/i.exec(value.trim())?.[1]
if (!hex) {
return null
}
const channel = (offset: number): number => Number.parseInt(hex.slice(offset, offset + 2), 16)
return [channel(0), channel(2), channel(4), 1]
}
const tinted = {
leftSidebarAppearanceMode: 'tinted' as const,
leftSidebarTintColor: '#18181b',
leftSidebarTintOpacity: 0.08
}
describe('host pill outline in the tinted left sidebar', () => {
it('raises the outline to 2:1 on the selected wash when the app text is only 4.5:1', () => {
// #919191 on #2a2a2a under the default 8% tint: 40% is 1.6:1 on the wash, 53% is 2:1.
const app = { foreground: '#919191', background: '#2a2a2a', base: 'dark' as const }
expect(tintedSidebarHostPillOutlineMix(tinted, app, probe)).toBe(53)
})
it("keeps Orca's 40% outline over Orca's dark colors", () => {
const app = { foreground: '#f0f0f0', background: '#0a0a0a', base: 'dark' as const }
expect(tintedSidebarHostPillOutlineMix(tinted, app, probe)).toBe(40)
})
})
@@ -1,35 +0,0 @@
import { expect, it } from 'vitest'
import { createTestStore } from '../store/testing/app-store-test-fixture'
import { TEST_REPO } from '../store/repos/repo.test-fixture'
import { makeWorktree } from '../store/slices/worktrees/worktree.test-fixture'
import { getLocalPreflightContext } from './local-preflight-context'
it('rebuilds against a replacement snapshot', () => {
const worktree = makeWorktree({
id: 'repo1::/worktree',
repoId: TEST_REPO.id,
path: String.raw`\\wsl.localhost\Ubuntu\home\alice\worktree`
})
const state = {
...createTestStore().getState(),
activeRepoId: TEST_REPO.id,
activeWorktreeId: worktree.id,
repos: [TEST_REPO],
worktreesByRepo: { [TEST_REPO.id]: [worktree] }
}
expect(getLocalPreflightContext(state, 'darwin')).toEqual({ wslDistro: 'Ubuntu' })
expect(
getLocalPreflightContext(
{
...state,
worktreesByRepo: {
[TEST_REPO.id]: [
{ ...worktree, path: String.raw`\\wsl.localhost\Debian\home\alice\moved` }
]
}
},
'darwin'
)
).toEqual({ wslDistro: 'Debian' })
})
@@ -1,54 +0,0 @@
import { describe, expect, it } from 'vitest'
import {
buildImportedHookSettings,
getDetectedSetupPreviewContent,
getSetupScriptFirstLine
} from './setup-script-prompt'
import { SETUP_SCRIPT_IMPORT_PROVIDERS } from '../../../shared/workspace-setup/setup-script-import-providers'
import type { Repo } from '../../../shared/repo-types'
const repo: Repo = {
id: 'repo',
path: '/repo',
displayName: 'Repo',
badgeColor: '#000',
addedAt: 1
}
describe('getDetectedSetupPreviewContent', () => {
it.each(SETUP_SCRIPT_IMPORT_PROVIDERS)(
'previews exactly what saving a %s candidate writes',
(provider) => {
const candidate = {
provider,
label: provider,
files: ['config'],
setup: `${provider} setup`,
archive: `${provider} archive`
}
const preview = getDetectedSetupPreviewContent(candidate, 'edited setup')
const saved = buildImportedHookSettings(
repo,
preview.editable ? { ...candidate, setup: preview.setup } : candidate,
false
)
expect(preview.editable).toBe(provider === 'package-manager')
expect(preview.setup).toBe(saved.scripts.setup)
expect(preview.archive).toBe(saved.scripts.archive)
}
)
})
describe('getSetupScriptFirstLine', () => {
it('summarizes a multi-line script by its first command', () => {
expect(getSetupScriptFirstLine('\n pnpm install \n\npnpm build\n')).toEqual({
firstLine: 'pnpm install',
hasMore: true
})
expect(getSetupScriptFirstLine('bun install\n')).toEqual({
firstLine: 'bun install',
hasMore: false
})
})
})
@@ -1,48 +0,0 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
const mocks = vi.hoisted(() => ({
activateAndRevealFolderWorkspace: vi.fn(),
activateAndRevealWorktree: vi.fn()
}))
vi.mock('@/lib/workspace/activation/worktree-activation', () => ({
activateAndRevealFolderWorkspace: mocks.activateAndRevealFolderWorkspace,
activateAndRevealWorktree: mocks.activateAndRevealWorktree
}))
import { activateWorktreeFromSidebar } from './sidebar-worktree-activation'
describe('sidebar worktree activation', () => {
beforeEach(() => {
mocks.activateAndRevealWorktree.mockClear()
mocks.activateAndRevealFolderWorkspace.mockClear()
})
afterEach(() => {
vi.unstubAllGlobals()
})
it('switches immediately while an ephemeral runtime wake is pending', async () => {
let resolveResume: ((value: null) => void) | undefined
const resumeWorkspace = vi.fn(
() =>
new Promise<null>((resolve) => {
resolveResume = resolve
})
)
vi.stubGlobal('window', {
api: { ephemeralVm: { resumeWorkspace } }
})
const activation = activateWorktreeFromSidebar('wt-vm')
expect(mocks.activateAndRevealWorktree).toHaveBeenCalledWith('wt-vm', {
navigationIntent: 'user-open',
revealInSidebar: false
})
expect(resumeWorkspace).toHaveBeenCalledWith({ workspaceId: 'wt-vm' })
resolveResume?.(null)
await activation
})
})
@@ -1,23 +0,0 @@
import { describe, expect, it } from 'vitest'
import { toRuntimeExecutionHostId } from '../../../shared/execution-host'
import { resolveSshWorkspaceBrowserRouteEligibility } from './browser/workspace/ssh-workspace-browser-route-eligibility'
const recipeEnvironment = {
id: 'env-recipe',
orcadDeployment: { sshTargetId: 'runtime-ssh-vm-1', sshTargetGeneration: 7 }
}
describe('SSH workspace browser route eligibility', () => {
it('routes an environment recipe workspace through its runtime-owned SSH host', () => {
expect(
resolveSshWorkspaceBrowserRouteEligibility(toRuntimeExecutionHostId('env-recipe'), {}, [
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: eligibility reads only the deployment link.
recipeEnvironment as never
])
).toEqual({ targetId: 'runtime-ssh-vm-1', expectedSshTargetGeneration: 7, eligible: true })
})
it('never treats a recipe VM target as a direct SSH workspace host', () => {
expect(resolveSshWorkspaceBrowserRouteEligibility('ssh:runtime-ssh-vm-1', {})).toBeNull()
})
})
@@ -1,50 +0,0 @@
// @vitest-environment happy-dom
import { afterEach, describe, expect, it, vi } from 'vitest'
import type { PaneManager } from '@/lib/pane-manager/pane-manager'
import {
focusRuntimeTerminalSurface,
registerRuntimeTerminalTab
} from './mounted-terminal-tab-registry'
let unregister: (() => void) | undefined
afterEach(() => {
unregister?.()
document.body.replaceChildren()
})
describe('terminal focus readiness', () => {
function register(hasPane: boolean, hasTextarea = true) {
const textarea = document.createElement('textarea')
const pane = {
id: 1,
container: document.createElement('div'),
terminal: {
textarea: hasTextarea ? textarea : undefined,
focus: vi.fn(() => textarea.focus())
}
}
const manager = {
getActivePane: () => (hasPane ? pane : undefined),
getPanes: () => (hasPane ? [pane] : [])
}
unregister = registerRuntimeTerminalTab({
tabId: 'restoring-tab',
worktreeId: 'restoring-workspace',
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: Focus reads only getActivePane/getPanes and the pane's container/terminal.
getManager: () => manager as unknown as PaneManager,
getContainer: () => null,
getPtyIdForPane: () => null,
getTabWideAgentHintLeafId: () => null
})
return textarea
}
it('does not consume focus until the terminal can receive it', () => {
const textarea = register(true)
expect(focusRuntimeTerminalSurface('restoring-tab', null, 'restoring-workspace')).toBe(false)
document.body.append(textarea)
expect(focusRuntimeTerminalSurface('restoring-tab', null, 'restoring-workspace')).toBe(true)
expect(document.activeElement).toBe(textarea)
})
})
@@ -1,41 +0,0 @@
import { expect, it } from 'vitest'
import { listRuntimeFiles } from './files/search/runtime-file-search-client'
import {
installRuntimeFileClientEnvironment,
runtimeEnvironmentCall
} from './files/runtime-file-client-test-harness'
installRuntimeFileClientEnvironment()
for (const version of [1, 2]) {
it(`refreshes candidate eligibility after deletion on version ${version}`, async () => {
let files = [{ relativePath: 'src/recent.ts', basename: 'recent.ts', kind: 'text' }]
runtimeEnvironmentCall.mockImplementation(({ method }) =>
Promise.resolve({
id: 'fresh',
ok: true,
_meta: { runtimeId: 'host' },
result:
method === 'files.searchPaths'
? { files: [], truncated: false, quickOpenSearchVersion: version }
: {
worktree: 'freshness',
rootPath: '/host/repo',
files: [...files],
totalCount: files.length,
truncated: false
}
})
)
const context = {
target: { kind: 'environment' as const, environmentId: 'env-1' },
worktreeId: `freshness-${version}`,
worktreePath: '/host/repo'
}
const options = { rootPath: '/host/repo', candidatePaths: ['src/recent.ts'], maxResults: 1 }
expect(await listRuntimeFiles(context, options)).toEqual(['src/recent.ts'])
files = []
expect(await listRuntimeFiles(context, options)).toEqual([])
expect(
runtimeEnvironmentCall.mock.calls.filter(([args]) => args.method === 'files.list')
).toHaveLength(2)
})
}
@@ -1,51 +0,0 @@
import { describe, expect, it } from 'vitest'
import { RuntimeRpcCallError } from '../../../../runtime/rpc/calls/runtime-rpc-result'
import type { CrossStoreParentRef } from '../../../../../../shared/worktree/lineage-types'
import { withoutRefusedInferredParent } from './worktree-create-cross-store-parent'
const refusal = (code: string) =>
new RuntimeRpcCallError({
id: '1',
ok: false,
error: { code, message: code },
_meta: { runtimeId: 'r' }
})
const parent: CrossStoreParentRef = {
installationId: 'desktop',
workspaceKey: 'folder:f-1',
instanceId: 'f-1'
}
describe('withoutRefusedInferredParent', () => {
it('drops an inferred parent the server refused, so the create retries unnested', () => {
expect(
withoutRefusedInferredParent(
{
name: 'w',
crossStoreParent: parent,
crossStoreParentInferredFrom: 'orchestration-context'
},
refusal('LINEAGE_PARENT_CONTEXT_CONFLICT')
)
).toEqual({ name: 'w' })
})
it('never retries a picked parent or a create that failed otherwise', () => {
expect(
withoutRefusedInferredParent(
{ name: 'w', crossStoreParent: parent },
refusal('LINEAGE_PARENT_CONTEXT_CONFLICT')
)
).toBeNull()
expect(
withoutRefusedInferredParent(
{
name: 'w',
crossStoreParent: parent,
crossStoreParentInferredFrom: 'orchestration-context'
},
refusal('runtime_timeout')
)
).toBeNull()
})
})
@@ -1,25 +0,0 @@
import { describe, expect, it } from 'vitest'
import { withStoreCrossStoreLineage } from './cross-store-lineage-state'
const RECORD = { childWorkspaceKey: 'worktree:repo::/srv/child', childInstanceId: 'child' }
const OTHER = { childWorkspaceKey: 'worktree:repo::/srv/other', childInstanceId: 'other' }
describe('withStoreCrossStoreLineage', () => {
it('keeps a Remove Parent made after the list request started', () => {
const atStart = {
'env-1': { 'worktree:repo::/srv/child': RECORD, 'worktree:repo::/srv/other': OTHER }
}
const afterRemove = { 'env-1': { 'worktree:repo::/srv/other': OTHER } }
const patch = withStoreCrossStoreLineage(
{ crossStoreLineageByStoreKey: afterRemove, ownLineageInstallationId: 'mac' },
'env-1',
{
worktreeLineageById: {},
workspaceLineageByChildKey: {},
crossStoreLineageByChildKey: atStart['env-1']
},
{ crossStoreLineageByStoreKey: atStart }
)
expect(patch).toEqual({})
})
})
@@ -1,50 +0,0 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import type { AppState } from '../../../types'
import type { Worktree } from '../../../../../../shared/worktree/types'
import { WORKTREE_CROSS_STORE_LINEAGE_RUNTIME_CAPABILITY } from '../../../../../../shared/worktree-runtime-capabilities'
const callRuntimeRpc = vi.hoisted(() => vi.fn())
vi.mock('../../../../runtime/rpc/calls/runtime-rpc-call', () => ({
callRuntimeRpc
}))
import { buildCrossStoreParentRef } from './cross-store-parent-ref'
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the ref reads only id, hostId and instanceId.
const PARENT = {
id: 'srv::/work/parent',
hostId: 'runtime:env-1',
instanceId: 'parent-instance'
} as unknown as Worktree
function stateWithServerInstall(installationId: string): AppState {
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the ref reads only these slices.
return {
runtimeEnvironments: [],
crossStoreLineageByStoreKey: {},
ownLineageInstallationId: 'mac-install',
removedRuntimeEnvironmentIds: new Set(),
runtimeStatusByEnvironmentId: new Map([
['env-1', { status: { hostDescriptor: { installationId } }, checkedAt: 1 }]
])
} as unknown as AppState
}
describe('buildCrossStoreParentRef', () => {
beforeEach(() => {
callRuntimeRpc.mockReset()
callRuntimeRpc.mockResolvedValue({
capabilities: [WORKTREE_CROSS_STORE_LINEAGE_RUNTIME_CAPABILITY]
})
})
it("names the parent by its server's verified install", async () => {
await expect(
buildCrossStoreParentRef(stateWithServerInstall('srv-install'), { kind: 'local' }, PARENT)
).resolves.toEqual({
installationId: 'srv-install',
workspaceKey: 'worktree:srv::/work/parent',
instanceId: 'parent-instance'
})
})
})
@@ -1,42 +0,0 @@
import { describe, expect, it, vi } from 'vitest'
import { createTestStore } from '../worktrees-slice-test-harness'
import { makeWorktree } from '../worktree.test-fixture'
vi.mock('sonner', () => ({
toast: { warning: vi.fn(), info: vi.fn(), success: vi.fn(), error: vi.fn(), dismiss: vi.fn() }
}))
vi.mock('@/components/worktree-base-fallback-notice', () => ({
requestWorktreeBaseFallbackNotice: vi.fn()
}))
describe('worktree no-op notifications', () => {
it.each(['local', 'ssh:test'] as const)(
'keeps repeated %s deletion and visit updates silent',
(hostId) => {
const store = createTestStore()
const worktree = makeWorktree({ id: 'repo1::/path/wt', repoId: 'repo1', hostId })
store.setState({ worktreesByRepo: { repo1: [worktree] } })
const target = { id: worktree.id, hostId }
store.getState().markWorktreesQueuedForDeletion([target])
store.getState().markWorktreeVisited(worktree.id, 100, hostId)
const before = store.getState()
const listener = vi.fn()
store.subscribe(listener)
before.markWorktreesQueuedForDeletion([target])
before.markWorktreeVisited(worktree.id, 100, hostId)
before.markWorktreeVisited(worktree.id, 99, hostId)
expect(store.getState()).toBe(before)
expect(listener).not.toHaveBeenCalled()
before.markWorktreesDeleting([target])
expect(listener).toHaveBeenCalledTimes(1)
const deleting = store.getState()
deleting.markWorktreesDeleting([target])
expect(store.getState()).toBe(deleting)
expect(listener).toHaveBeenCalledTimes(1)
deleting.clearWorktreeDeleteState(worktree.id, hostId)
expect(listener).toHaveBeenCalledTimes(2)
}
)
})
@@ -1,21 +0,0 @@
import { expect, it } from 'vitest'
import { searchAgentPickerSetups } from './agent-launch-picker-search'
import type { AgentPickerEntry } from './agent-launch-picker'
it('searches the fallback models the submenu offers while the host catalog is unknown', () => {
const codex: AgentPickerEntry = {
agent: 'codex',
label: 'Codex',
rows: [{ agent: 'codex', kind: 'cli' }],
surfaces: ['terminal'],
defaultSurface: 'terminal',
terminal: {},
structured: {},
catalog: { origin: 'unknown' }
}
const setups = searchAgentPickerSetups([codex], 'gpt')
expect(setups.length).toBeGreaterThan(0)
expect(setups.every((setup) => setup.model.id.startsWith('gpt'))).toBe(true)
})
@@ -1,6 +1,5 @@
import './telemetry-orca-cli-feature-tip.spec'
import './telemetry-events-feature-education.spec'
import './telemetry-feature-wall-events.spec'
import './workspace-setup/setup-script-telemetry-events.spec'
import './profile-state-telemetry-schema.spec'
import './onboarding-tour-telemetry-events.spec'
@@ -1,117 +0,0 @@
import { describe, expect, it } from 'vitest'
import '../telemetry-events'
import { eventSchemas } from '../telemetry-event-registry'
describe('setup script prompt schemas', () => {
it('accepts a bucketed candidate prompt exposure', () => {
const parsed = eventSchemas.setup_script_prompt_shown.safeParse({
mode: 'import_available',
provider: 'codex',
file_count_bucket: '1',
unsupported_field_count_bucket: '2-3',
has_shared_hooks: false
})
expect(parsed.success).toBe(true)
})
it('accepts configure prompt actions without a provider', () => {
const parsed = eventSchemas.setup_script_prompt_action.safeParse({
action: 'configure_clicked',
mode: 'configure_needed',
file_count_bucket: '0',
unsupported_field_count_bucket: '0',
has_shared_hooks: true
})
expect(parsed.success).toBe(true)
})
it.each([
'save_detected_setup_clicked',
'save_detected_setup_completed',
'save_detected_setup_failed'
])('accepts %s detected setup save actions', (action) => {
const parsed = eventSchemas.setup_script_prompt_action.safeParse({
action,
mode: 'import_available',
provider: 'package-manager',
file_count_bucket: '2-3',
unsupported_field_count_bucket: '0',
has_shared_hooks: false,
edited_before_save: true
})
expect(parsed.success).toBe(true)
})
it('rejects detected setup save actions without edit state', () => {
const parsed = eventSchemas.setup_script_prompt_action.safeParse({
action: 'save_detected_setup_completed',
mode: 'import_available',
provider: 'package-manager',
file_count_bucket: '2-3',
unsupported_field_count_bucket: '0',
has_shared_hooks: false
})
expect(parsed.success).toBe(false)
})
it('rejects detected setup save actions for non-package-manager imports', () => {
const parsed = eventSchemas.setup_script_prompt_action.safeParse({
action: 'save_detected_setup_completed',
mode: 'import_available',
provider: 'codex',
file_count_bucket: '1',
unsupported_field_count_bucket: '0',
has_shared_hooks: false,
edited_before_save: false
})
expect(parsed.success).toBe(false)
})
it('rejects candidate prompt exposure without a provider', () => {
const parsed = eventSchemas.setup_script_prompt_shown.safeParse({
mode: 'import_available',
file_count_bucket: '1',
unsupported_field_count_bucket: '0',
has_shared_hooks: false
})
expect(parsed.success).toBe(false)
})
it('rejects configure prompt actions with a provider', () => {
const parsed = eventSchemas.setup_script_prompt_action.safeParse({
action: 'configure_clicked',
mode: 'configure_needed',
provider: 'codex',
file_count_bucket: '0',
unsupported_field_count_bucket: '0',
has_shared_hooks: false
})
expect(parsed.success).toBe(false)
})
it('rejects edit state on non-save actions', () => {
const parsed = eventSchemas.setup_script_prompt_action.safeParse({
action: 'configure_clicked',
mode: 'configure_needed',
file_count_bucket: '0',
unsupported_field_count_bucket: '0',
has_shared_hooks: false,
edited_before_save: true
})
expect(parsed.success).toBe(false)
})
it('rejects raw setup import details via .strict()', () => {
const parsed = eventSchemas.setup_script_prompt_action.safeParse({
action: 'import_completed',
mode: 'import_available',
provider: 'codex',
file_count_bucket: '1',
unsupported_field_count_bucket: '0',
has_shared_hooks: false,
files: ['.codex/environments/environment.toml'],
setup: 'npm install'
})
expect(parsed.success).toBe(false)
})
})