test: remove 39 low-value suites from audit batch 849 (#36491)

* test: remove 39 low-value suites from audit batch 849

* test: remove obsolete shell-contract trigger for deleted suite

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
OrcaWin
2026-10-30 17:20:27 -07:00
committed by GitHub
co-authored by OrcaWin
parent bc57cdbf77
commit be1fceb69d
44 changed files with 0 additions and 3470 deletions
-1
View File
@@ -831,7 +831,6 @@ jobs:
src/main/daemon/shell-ready.test.ts \
src/main/daemon/node-pty-fd-leak.test.ts \
src/main/providers/local-pty-shell-ready-zsh-launch-environment.test.ts \
src/main/providers/__tests__/shell-ready-framework-example.test.ts \
src/main/pty/codex-shell-launch-preflight.test.ts \
src/main/pty/codex-shell-no-daemon.test.ts \
src/main/pty/omp-shell-wrapper-alias-safety.test.ts \
-1
View File
@@ -1118,7 +1118,6 @@
5 test-no-wall-clock:src/renderer/src/components/status-bar/inline-usage-bars.test.tsx
4 test-no-wall-clock:src/renderer/src/components/status-bar/provider-segment-balance.test.tsx
2 test-no-wall-clock:src/renderer/src/components/status-bar/status-bar-antigravity-segment.test.tsx
1 test-no-wall-clock:src/renderer/src/components/status-bar/status-bar-cursor-segment.test.tsx
3 test-no-wall-clock:src/renderer/src/components/status-bar/status-bar-provider-menu-focus.test.tsx
1 test-no-wall-clock:src/renderer/src/components/status-bar/workspace-space-worktree-row.test.tsx
1 test-no-wall-clock:src/renderer/src/components/tab-bar/TabBarCreateEntry.history.test.tsx
-31
View File
@@ -1431,7 +1431,6 @@
"src/main/claude-accounts/claude-profile-router.test.ts": 864,
"src/main/claude-accounts/claude-profile-setup-worker.test.ts": 331,
"src/main/claude-accounts/claude-profile-setup.test.ts": 243,
"src/main/claude-accounts/claude-profile-shell-composition.test.ts": 70,
"src/main/claude-accounts/claude-profile-wsl-router.test.ts": 821,
"src/main/claude-accounts/claude-sign-in-browser.test.ts": 288,
"src/main/claude-accounts/claude-structured-auth-policy.test.ts": 57,
@@ -1760,7 +1759,6 @@
"src/main/codex/codex-config-mirror-mcp-ownership.test.ts": 114,
"src/main/codex/codex-config-mirror.test.ts": 247,
"src/main/codex/codex-config-settings-removal.test.ts": 36,
"src/main/codex/codex-configured-launch-defaults.test.ts": 62,
"src/main/codex/codex-daemon-socket-path-guard.test.ts": 101,
"src/main/codex/codex-default-mode-subagents.test.ts": 574,
"src/main/codex/codex-goal-journal-rows.test.ts": 74,
@@ -2499,7 +2497,6 @@
"src/main/git/status-upstream-negative-cache.test.ts": 839,
"src/main/git/status-upstream-probe-churn.test.ts": 147,
"src/main/git/status-upstream-ref.test.ts": 42,
"src/main/git/status-wsl-pathspecs.test.ts": 161,
"src/main/git/status.test.ts": 184,
"src/main/git/upstream-deferred-fork-remote-real.test.ts": 195,
"src/main/git/upstream.test.ts": 371,
@@ -2518,7 +2515,6 @@
"src/main/git/worktree-checkout-progress-real-git.test.ts": 631,
"src/main/git/worktree-common-dir-comparison.test.ts": 178,
"src/main/git/worktree-configured-paths-concurrency.test.ts": 67,
"src/main/git/worktree-create-admission-tier.test.ts": 249,
"src/main/git/worktree-create-git-executor-real-git.test.ts": 540,
"src/main/git/worktree-create-preparation-real-git.test.ts": 2248,
"src/main/git/worktree-create-preparation-real-wsl.test.ts": 209,
@@ -2685,7 +2681,6 @@
"src/main/gitlab/client-work-items.test.ts": 539,
"src/main/gitlab/client.test.ts": 395,
"src/main/gitlab/gitlab-known-host-backoff.test.ts": 349,
"src/main/gitlab/gitlab-known-host-probe-wsl-fallback.test.ts": 457,
"src/main/gitlab/gitlab-known-host-probe.test.ts": 420,
"src/main/gitlab/gitlab-known-host-retirement.test.ts": 420,
"src/main/gitlab/gl-utils.test.ts": 437,
@@ -2945,7 +2940,6 @@
"src/main/ipc/preflight-wsl-command.test.ts": 48,
"src/main/ipc/pty-attach-view-joins-main-start.test.ts": 3883,
"src/main/ipc/pty-claude-agent-view-readiness.test.ts": 3531,
"src/main/ipc/pty-painted-cold-restore-ack-layout-owner.test.ts": 2928,
"src/main/ipc/pty-spawn-saved-resume-admission.test.ts": 6908,
"src/main/ipc/pty/daemon-late-exit.test.ts": 6475,
"src/main/ipc/pty/delivery/attached-pty-size.test.ts": 68,
@@ -3279,7 +3273,6 @@
"src/main/ipc/worktrees/listing/detected-worktree-scan-hygiene-gate.test.ts": 79,
"src/main/ipc/worktrees/listing/detected-worktree-scan-listing-distro.test.ts": 677,
"src/main/ipc/worktrees/listing/detected-worktree-scan-superseded.test.ts": 46,
"src/main/ipc/worktrees/listing/register-sparse-checkout-cache-invalidation.test.ts": 122,
"src/main/ipc/wsl-guest-windows-drive-path.test.ts": 58,
"src/main/ipc/wsl-host-gate-off.test.ts": 3774,
"src/main/ipc/wsl-move-handlers.test.ts": 399,
@@ -3775,7 +3768,6 @@
"src/main/native-chat/native-chat-visuals-delivery.test.ts": 653,
"src/main/native-chat/native-chat-visuals-sweep.test.ts": 150,
"src/main/native-chat/session-file-resolver-claude-roots.test.ts": 157,
"src/main/native-chat/session-file-resolver-codex-roots.test.ts": 164,
"src/main/native-chat/session-file-resolver-wsl-scan-gate.test.ts": 138,
"src/main/native-chat/session-file-resolver-wsl.test.ts": 107,
"src/main/native-chat/session-file-resolver.test.ts": 150,
@@ -5237,7 +5229,6 @@
"src/main/runtime/rpc/methods/ai-vault-structured-ownership.test.ts": 1351,
"src/main/runtime/rpc/methods/ai-vault.test.ts": 2715,
"src/main/runtime/rpc/methods/artifacts.test.ts": 241,
"src/main/runtime/rpc/methods/automatic-work.test.ts": 2248,
"src/main/runtime/rpc/methods/automation-scoped-list-methods.test.ts": 526,
"src/main/runtime/rpc/methods/automations.test.ts": 527,
"src/main/runtime/rpc/methods/browser-client-host-attach-adoption.test.ts": 621,
@@ -5719,7 +5710,6 @@
"src/main/runtime/runtime-worktree-default-tabs-layout-owner.test.ts": 36,
"src/main/runtime/runtime-worktree-ps-summaries.test.ts": 83,
"src/main/runtime/runtime-worktree-ps-unverifiable-terminals.test.ts": 35,
"src/main/runtime/runtime-worktree-removal-process-cleanup.test.ts": 3713,
"src/main/runtime/runtime-worktree-selection.test.ts": 36,
"src/main/runtime/runtime-worktree-startup-readiness.test.ts": 215,
"src/main/runtime/runtime-worktree-structured-agent-rows-liveness.test.ts": 762,
@@ -5760,7 +5750,6 @@
"src/main/runtime/sidebar-inputs/orca-runtime-sort-stamps.test.ts": 4086,
"src/main/runtime/sidebar-inputs/orca-runtime-terminal-use.test.ts": 2341,
"src/main/runtime/sidebar-inputs/orca-runtime-workspace-activity-facts.test.ts": 3455,
"src/main/runtime/sidebar-inputs/orca-runtime-workspace-review-facts.test.ts": 3885,
"src/main/runtime/sidebar-inputs/orca-runtime-workspace-unread.test.ts": 3997,
"src/main/runtime/sidebar-inputs/paired-server-rows-producer.test.ts": 265,
"src/main/runtime/sidebar-inputs/repo-icon-data-urls.test.ts": 191,
@@ -7283,7 +7272,6 @@
"src/renderer/src/components/editor/combined-diff-on-demand-load.test.ts": 94,
"src/renderer/src/components/editor/combined-diff/browse-files/combined-diff-file-tree-collapsed-work.test.tsx": 742,
"src/renderer/src/components/editor/combined-diff/browse-files/combined-diff-file-tree-render.test.tsx": 737,
"src/renderer/src/components/editor/combined-diff/browse-files/combined-diff-file-tree-row-drag.test.tsx": 305,
"src/renderer/src/components/editor/combined-diff/browse-files/combined-diff-file-tree-windowing.test.tsx": 577,
"src/renderer/src/components/editor/combined-diff/browse-files/combined-diff-file-tree.test.ts": 77,
"src/renderer/src/components/editor/combined-diff/browse-files/use-combined-diff-file-tree-resize.test.tsx": 248,
@@ -8151,7 +8139,6 @@
"src/renderer/src/components/right-sidebar/AiVaultSessionSubagents.test.tsx": 1926,
"src/renderer/src/components/right-sidebar/AiVaultSessionVirtualList.keyboard.test.tsx": 2036,
"src/renderer/src/components/right-sidebar/AiVaultSessionVirtualList.measurement.test.tsx": 2554,
"src/renderer/src/components/right-sidebar/AiVaultShowMoreSessionsRow.test.tsx": 369,
"src/renderer/src/components/right-sidebar/AiVaultVirtualRow.test.tsx": 2253,
"src/renderer/src/components/right-sidebar/ChecksPanel.updated-at-metadata.test.tsx": 243,
"src/renderer/src/components/right-sidebar/CommitArea.chevron-spinner.test.tsx": 1564,
@@ -8376,7 +8363,6 @@
"src/renderer/src/components/right-sidebar/source-control/review/use-review-push-target.test.tsx": 255,
"src/renderer/src/components/right-sidebar/source-control/sync/git-history-format.test.ts": 47,
"src/renderer/src/components/right-sidebar/source-control/sync/git-history-graph-svg.test.tsx": 102,
"src/renderer/src/components/right-sidebar/source-control/sync/use-status-refresh.local-ignore.test.tsx": 272,
"src/renderer/src/components/right-sidebar/use-ai-vault-search-focus-request.test.tsx": 1566,
"src/renderer/src/components/right-sidebar/use-ai-vault-search.test.tsx": 485,
"src/renderer/src/components/right-sidebar/use-checks-panel-terminal-worktree.test.ts": 1574,
@@ -8512,7 +8498,6 @@
"src/renderer/src/components/settings/SessionHistorySettingsPane.test.tsx": 903,
"src/renderer/src/components/settings/SettingsConstants.test.ts": 97,
"src/renderer/src/components/settings/SettingsFormControls.font-autocomplete.test.tsx": 789,
"src/renderer/src/components/settings/SettingsFormControls.number-field.test.tsx": 753,
"src/renderer/src/components/settings/SettingsFormControls.segmented-control.test.tsx": 548,
"src/renderer/src/components/settings/SettingsSidebar.test.tsx": 2169,
"src/renderer/src/components/settings/ShareSkillsSettingsPane.test.tsx": 601,
@@ -8557,7 +8542,6 @@
"src/renderer/src/components/settings/browser-session-host-selection.test.ts": 43,
"src/renderer/src/components/settings/bundled-skill-setup-result-copy.test.ts": 304,
"src/renderer/src/components/settings/chat-naming-persistence.test.tsx": 3654,
"src/renderer/src/components/settings/chat-settings-unsaved-guard.test.tsx": 1560,
"src/renderer/src/components/settings/cli-install-failure.test.ts": 34,
"src/renderer/src/components/settings/codex-config-sync-warning.test.ts": 34,
"src/renderer/src/components/settings/codex-session-source-home-control.test.tsx": 154,
@@ -8665,7 +8649,6 @@
"src/renderer/src/components/sidebar/NewExternalWorktreesInboxLine.test.tsx": 1735,
"src/renderer/src/components/sidebar/NonGitFolderDialog.test.tsx": 1097,
"src/renderer/src/components/sidebar/NoticeHostGlyph.test.tsx": 2211,
"src/renderer/src/components/sidebar/OrcaYamlTrustDialog.test.tsx": 54,
"src/renderer/src/components/sidebar/PendingWorktreeRow.test.tsx": 1397,
"src/renderer/src/components/sidebar/ProjectAddedDialog.test.tsx": 187,
"src/renderer/src/components/sidebar/ProjectGroupDeleteDialog.test.tsx": 2252,
@@ -9017,7 +9000,6 @@
"src/renderer/src/components/sparse/SparsePresetChooser.test.tsx": 510,
"src/renderer/src/components/sparse/sparse-directory-browse.test.ts": 45,
"src/renderer/src/components/sparse/sparse-directory-entry-input.test.ts": 107,
"src/renderer/src/components/standalone-terminal/open-standalone-terminal.test.ts": 1321,
"src/renderer/src/components/star-nag/StarNagAgentValueMomentObserver.test.tsx": 1534,
"src/renderer/src/components/star-nag/StarNagToastHost.test.tsx": 344,
"src/renderer/src/components/stats/CodexUsagePane.test.tsx": 753,
@@ -9070,7 +9052,6 @@
"src/renderer/src/components/status-bar/ssh-status-menu-rows.test.ts": 48,
"src/renderer/src/components/status-bar/status-bar-antigravity-segment.test.tsx": 968,
"src/renderer/src/components/status-bar/status-bar-copy-localization.test.tsx": 885,
"src/renderer/src/components/status-bar/status-bar-cursor-segment.test.tsx": 1503,
"src/renderer/src/components/status-bar/status-bar-density.test.ts": 57,
"src/renderer/src/components/status-bar/status-bar-pool-countdown.test.tsx": 389,
"src/renderer/src/components/status-bar/status-bar-provider-menu-focus.test.tsx": 941,
@@ -9230,11 +9211,9 @@
"src/renderer/src/components/task-page-jira-status-catalog.test.ts": 60,
"src/renderer/src/components/task-page/jira/task-page-jira-status-order-session.test.tsx": 2111,
"src/renderer/src/components/task-page/jira/task-page-jira-text-fallback-notice.test.tsx": 375,
"src/renderer/src/components/task-page/jira/task-page-jira-workspace-actions.test.tsx": 1971,
"src/renderer/src/components/task-page/task-page-list-chrome-visibility.test.ts": 33,
"src/renderer/src/components/task-page/task-page-mutation-page-allocation.test.ts": 128,
"src/renderer/src/components/task-page/task-page-new-issue-draft.test.ts": 76,
"src/renderer/src/components/task-page/task-page-repo-source-divergence.test.ts": 1043,
"src/renderer/src/components/task-page/task-page-task-source-host-availability.test.ts": 401,
"src/renderer/src/components/task-page/task-page-work-item-pagination.test.ts": 56,
"src/renderer/src/components/task-page/github/rows-twin-host-routing.test.tsx": 1521,
@@ -9700,7 +9679,6 @@
"src/renderer/src/components/terminal-pane/terminal-option-punctuation.test.ts": 65,
"src/renderer/src/components/terminal-pane/terminal-output-visibility.test.ts": 39,
"src/renderer/src/components/terminal-pane/terminal-pane-attention-subscriptions.test.ts": 42,
"src/renderer/src/components/terminal-pane/terminal-pane-bootstrap-split-in-main.test.ts": 1119,
"src/renderer/src/components/terminal-pane/terminal-pane-close-identity.test.ts": 26,
"src/renderer/src/components/terminal-pane/terminal-pane-detach-layout-owner.test.ts": 1521,
"src/renderer/src/components/terminal-pane/terminal-pane-element-file-drop.test.tsx": 1853,
@@ -9830,7 +9808,6 @@
"src/renderer/src/components/terminal-search-decoration-leak.test.ts": 1946,
"src/renderer/src/components/terminal-search-long-wrapped-line.test.ts": 3017,
"src/renderer/src/components/terminal-search-safe-find.test.ts": 48,
"src/renderer/src/components/terminal-split-workspace-surfaces-startup-hold.test.tsx": 1949,
"src/renderer/src/components/terminal-startup-hold-placeholder.test.tsx": 559,
"src/renderer/src/components/terminal-workspace-keydown.test.ts": 646,
"src/renderer/src/components/terminal-workspace-surface-ids.test.tsx": 1948,
@@ -9886,7 +9863,6 @@
"src/renderer/src/components/use-terminal-editor-close-foundation.window-close.test.tsx": 1766,
"src/renderer/src/components/use-terminal-editor-close-queue.reveal.test.tsx": 581,
"src/renderer/src/components/use-terminal-parking-pass.test.tsx": 1759,
"src/renderer/src/components/use-terminal-window-lifecycle.lazy-ref.test.tsx": 170,
"src/renderer/src/components/use-terminal-workspace-foundation.runtime-status.test.tsx": 1503,
"src/renderer/src/components/use-worktree-jump-palette-browser-ownership.test.ts": 1477,
"src/renderer/src/components/use-worktree-jump-palette-task-search.test.tsx": 1933,
@@ -9905,7 +9881,6 @@
"src/renderer/src/components/workspace-cleanup/workspace-cleanup-candidate-row-data.test.ts": 279,
"src/renderer/src/components/workspace-cleanup/workspace-cleanup-candidate-row.test.tsx": 2141,
"src/renderer/src/components/workspace-cleanup/workspace-cleanup-confirm-remove.test.tsx": 658,
"src/renderer/src/components/workspace-cleanup/workspace-cleanup-confirm-stop-agents.test.tsx": 343,
"src/renderer/src/components/workspace-cleanup/workspace-cleanup-facet-controls.test.tsx": 601,
"src/renderer/src/components/workspace-cleanup/workspace-cleanup-facet-filter.test.ts": 107,
"src/renderer/src/components/workspace-cleanup/workspace-cleanup-facet-sort.test.ts": 127,
@@ -9941,7 +9916,6 @@
"src/renderer/src/hooks/client-workspace-view-report.test.ts": 42,
"src/renderer/src/hooks/composer-branch-selection.test.ts": 50,
"src/renderer/src/hooks/composer-drop-result.test.ts": 25,
"src/renderer/src/hooks/composer-native-file-drop.test.ts": 62,
"src/renderer/src/hooks/composer-state/attachment-drop-failure.test.tsx": 547,
"src/renderer/src/hooks/composer-state/attachment-drop-lifetime.test.tsx": 741,
"src/renderer/src/hooks/composer-state/branch-start-point-actions.test.ts": 231,
@@ -10619,7 +10593,6 @@
"src/renderer/src/lib/simulator-tab-palette-activation.test.ts": 1304,
"src/renderer/src/lib/skill-freshness-display-status.test.ts": 109,
"src/renderer/src/lib/sleeping-agent-pane-ownership.test.ts": 45,
"src/renderer/src/lib/sleeping-agent-session-launch-layout-owner.test.ts": 536,
"src/renderer/src/lib/sleeping-agent-session-launch-windows-quoting.test.ts": 533,
"src/renderer/src/lib/smart-github-submit.test.ts": 75,
"src/renderer/src/lib/source-control-agent-action-launch-check.test.ts": 1325,
@@ -11162,7 +11135,6 @@
"src/renderer/src/store/plugin-panels.test.ts": 122,
"src/renderer/src/store/project-host-setup-selector.test.ts": 47,
"src/renderer/src/store/projects/project-catalog-null-field-ingest.test.ts": 143,
"src/renderer/src/store/projects/project-wsl-filesystem-boundary-advisory.test.ts": 72,
"src/renderer/src/store/react-commit-cascade-write-probe.test.ts": 169,
"src/renderer/src/store/repos/converted-ssh-browser-pages.test.ts": 489,
"src/renderer/src/store/repos/converted-ssh-host-rows.test.ts": 33,
@@ -11354,7 +11326,6 @@
"src/renderer/src/store/slices/project-removal-owner-host.test.ts": 1455,
"src/renderer/src/store/slices/project-removal-runtime-terminal-order.test.ts": 1801,
"src/renderer/src/store/slices/pull-request-generation-jira-title.test.ts": 76,
"src/renderer/src/store/slices/purge-stale-runtime-host-groups.test.ts": 1390,
"src/renderer/src/store/slices/purge-stale-runtime-host-ownership.test.ts": 1529,
"src/renderer/src/store/slices/purge-stale-runtime-host-state.test.ts": 1678,
"src/renderer/src/store/slices/readopted-ssh-worktree-rows.test.ts": 47,
@@ -11399,7 +11370,6 @@
"src/renderer/src/store/slices/runtime-status-catalog-identity.test.ts": 286,
"src/renderer/src/store/slices/runtime-status-first-publication.test.ts": 329,
"src/renderer/src/store/slices/runtime-status-reconnect-connection-generation.test.ts": 1219,
"src/renderer/src/store/slices/runtime-status-restored-browser-host-attach.test.ts": 376,
"src/renderer/src/store/slices/runtime-status-skill-cache-eviction.test.ts": 269,
"src/renderer/src/store/slices/runtime-status-snapshot.test.ts": 378,
"src/renderer/src/store/slices/runtime-status.test.ts": 576,
@@ -11459,7 +11429,6 @@
"src/renderer/src/store/slices/tabs-unread-and-focus.test.ts": 1556,
"src/renderer/src/store/slices/tabs/hydrated-workspace-reconciliation-batch.test.ts": 1774,
"src/renderer/src/store/slices/tabs/layout-owner-shared-path-gates.test.ts": 2018,
"src/renderer/src/store/slices/tabs/preview-tab-promotion.test.ts": 33,
"src/renderer/src/store/slices/tabs/structured-chat-close-owner.test.ts": 1418,
"src/renderer/src/store/slices/tabs/tab-selection-contract.test.ts": 237,
"src/renderer/src/store/slices/tabs/tabs-layout-owner-groups-order.test.ts": 1456,
-1
View File
@@ -29,7 +29,6 @@ export const UNIT_EXCLUDE = [
'src/main/daemon/shell-ready.test.ts',
'src/main/daemon/node-pty-fd-leak.test.ts',
'src/main/providers/local-pty-shell-ready-zsh-launch-environment.test.ts',
'src/main/providers/__tests__/shell-ready-framework-example.test.ts',
'src/main/pty/omp-shell-wrapper-alias-safety.test.ts',
'src/main/pty/omp-shell-wrapper.node-pty.test.ts',
'src/main/shell-startup-feature-channel.test.ts',
@@ -6,7 +6,6 @@ export const SHELL_PREFIXES = [
'src/main/daemon/daemon-shell-ready-wrapper',
'src/main/daemon/node-pty-fd-leak',
'src/main/providers/local-pty-shell-ready',
'src/main/providers/__tests__/shell-ready-framework-example',
'src/main/pty/',
'src/main/shell-templates',
'src/main/shell-startup-',
@@ -1,92 +0,0 @@
import { describe, expect, it } from 'vitest'
import {
buildBrowserExtensionBackgroundWrapper,
withBrowserExtensionApiConnectSource
} from './browser-extension-background-wrapper'
const base = { manifest_version: 3, name: 'Vault', version: '1.0' }
describe('background wrapper', () => {
it('loads the shim, then the classic worker, through importScripts', () => {
const wrapper = buildBrowserExtensionBackgroundWrapper({
...base,
background: { service_worker: 'background.js' }
})
expect(wrapper?.wrapperPath).toBe('orca-background.js')
expect(wrapper?.wrapperSource).toContain(
'importScripts("/orca-extension-api.js", "/background.js");'
)
expect(wrapper?.manifest.background).toEqual({ service_worker: 'orca-background.js' })
})
it('imports the shim, then the module worker, from beside the original', () => {
const wrapper = buildBrowserExtensionBackgroundWrapper({
...base,
background: { service_worker: './build/service worker.js', type: 'module' }
})
expect(wrapper?.wrapperPath).toBe('build/orca-background.js')
expect(wrapper?.wrapperSource).toContain(
'import "/orca-extension-api.js";\nimport "/build/service%20worker.js";'
)
expect(wrapper?.manifest.background).toEqual({
service_worker: 'build/orca-background.js',
type: 'module'
})
})
it('drops the Firefox background.scripts key that Chromium warns about in MV3', () => {
const wrapper = buildBrowserExtensionBackgroundWrapper({
...base,
background: {
service_worker: 'assets/background.js',
type: 'module',
scripts: ['assets/background.js']
}
})
expect(wrapper?.manifest.background).toEqual({
service_worker: 'assets/orca-background.js',
type: 'module'
})
})
it('leaves extensions without a background worker alone', () => {
expect(buildBrowserExtensionBackgroundWrapper(base)).toBeNull()
expect(buildBrowserExtensionBackgroundWrapper({ ...base, background: {} })).toBeNull()
})
it('rejects Manifest V2 and workers outside the folder', () => {
expect(() =>
buildBrowserExtensionBackgroundWrapper({
...base,
manifest_version: 2,
background: { scripts: ['bg.js'] }
})
).toThrow('Manifest V3')
expect(() =>
buildBrowserExtensionBackgroundWrapper({
...base,
background: { service_worker: '../outside.js' }
})
).toThrow('inside the extension folder')
})
it('lets a restrictive extension-pages policy reach the API', () => {
const wrapper = buildBrowserExtensionBackgroundWrapper({
...base,
background: { service_worker: 'bg.js' },
content_security_policy: {
extension_pages: "script-src 'self'; connect-src https://vault.example",
sandbox: 'sandbox allow-scripts'
}
})
expect(wrapper?.manifest.content_security_policy).toEqual({
extension_pages:
"script-src 'self'; connect-src https://vault.example 'self' orca-extension-api:",
sandbox: 'sandbox allow-scripts'
})
expect(withBrowserExtensionApiConnectSource("default-src 'none'; script-src 'self'")).toBe(
"default-src 'none'; script-src 'self'; connect-src 'self' orca-extension-api:"
)
expect(withBrowserExtensionApiConnectSource("script-src 'self'")).toBe("script-src 'self'")
})
})
@@ -1,83 +0,0 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import type { InstalledBrowserExtension } from '../../../shared/browser-extension-types'
const state = vi.hoisted(() => {
const contents: unknown[] = []
return { contents, enabled: new Map<string, string[]>() }
})
vi.mock('electron', () => ({
webContents: { getAllWebContents: () => state.contents }
}))
vi.mock('./browser-extension-api-host', () => ({
getBrowserExtensionProfileIdForSession: (session: { profileId: string }) => session.profileId
}))
vi.mock('../browser-session-registry', () => ({
browserSessionRegistry: {
getEnabledExtensionIds: (profileId: string) => state.enabled.get(profileId) ?? []
}
}))
const { notifyChromeWebStoreInstalled, notifyChromeWebStoreUninstalled } =
await import('./browser-extension-webstore-events')
const extension: InstalledBrowserExtension = {
id: 'a'.repeat(32),
name: 'Example',
version: '1',
source: 'webstore',
path: '/extensions/example',
iconPath: null,
permissions: [],
hostPermissions: [],
optionsPage: null
}
function storePage(profileId: string, url = 'https://chromewebstore.google.com/') {
const send = vi.fn()
const contents = {
isDestroyed: () => false,
getType: () => 'webview',
session: { profileId },
mainFrame: { url, send }
}
state.contents.push(contents)
return send
}
beforeEach(() => {
state.contents = []
state.enabled.clear()
})
describe('Chrome Web Store profile events', () => {
it('reports an installation only to profiles where the extension is enabled', () => {
const enabledPage = storePage('personal')
const disabledPage = storePage('work')
const unrelatedPage = storePage('personal', 'https://example.com/')
state.enabled.set('personal', [extension.id])
notifyChromeWebStoreInstalled(extension)
expect(enabledPage).toHaveBeenCalledWith(expect.any(String), {
event: 'onInstalled',
args: [expect.objectContaining({ id: extension.id, enabled: true })]
})
expect(disabledPage).not.toHaveBeenCalled()
expect(unrelatedPage).not.toHaveBeenCalled()
})
it('reports a global uninstall to store pages in every profile', () => {
const personal = storePage('personal')
const work = storePage('work')
notifyChromeWebStoreUninstalled(extension.id)
for (const send of [personal, work]) {
expect(send).toHaveBeenCalledWith(expect.any(String), {
event: 'onUninstalled',
args: [extension.id]
})
}
})
})
@@ -1,94 +0,0 @@
import { spawnSync } from 'node:child_process'
import { chmodSync, existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, expect, it } from 'vitest'
const FISH =
['/opt/homebrew/bin/fish', '/usr/local/bin/fish', '/usr/bin/fish'].find(existsSync) ??
'/usr/bin/fish'
const roots: string[] = []
afterEach(() => {
roots.splice(0).forEach((root) => rmSync(root, { recursive: true, force: true }))
})
function fishInit({ args }: { args: string[] | null }): string {
const init = args?.[2]
if (!init) {
throw new Error('fish launch was not wrapped')
}
return init
}
async function composed() {
const local = await import('../providers/local-pty-shell-ready')
const daemon = await import('../daemon/shell-ready')
const powershell = await import('../powershell-osc133-bootstrap')
const localBash = await import('../providers/local-pty-shell-ready-bash-rcfile')
const daemonBash = await import('../daemon/daemon-bash-shell-ready-rcfile')
const zsh = await import('../zsh-startup-wrapper-builder')
const localZsh = await import('../providers/local-pty-shell-ready-wrapper-fileset')
const daemonZsh = await import('../daemon/daemon-zsh-shell-ready-wrapper-spec')
return {
zsh: [
zsh.buildZshStartupHook(localZsh.getLocalZshWrapperSpec()),
zsh.buildZshStartupHook(daemonZsh.getDaemonZshWrapperSpec())
],
bash: [
localBash.getBashShellReadyRcfileContent(),
daemonBash.getDaemonBashShellReadyRcfileContent()
],
fish: [
fishInit(local.getShellLaunchConfig(FISH, ['ready'])),
fishInit(daemon.getShellLaunchConfig(FISH, ['ready']))
],
powershell: powershell.getPowerShellOsc133Bootstrap()
}
}
it('gives every composed shell startup and the plain fish vendor snippet the claude function', async () => {
const on = await composed()
for (const text of [...on.bash, ...on.zsh]) {
expect(text).toContain('ORCA_CLAUDE_INJECTED_CONFIG_DIR')
}
const { getFishVendorConfSnippet } = await import('../fish-xdg-data-dirs-handoff')
const { getFishClaudeShellFunction } = await import('../../shared/claude-shell-function')
expect(getFishClaudeShellFunction()).toContain('function claude')
expect(getFishVendorConfSnippet()).toContain(getFishClaudeShellFunction())
})
it('starts the PowerShell claude function on its own line after the codex fragment', async () => {
const on = await composed()
expect(on.powershell).toContain(
'Remove-Variable orcaCodexCommand -ErrorAction SilentlyContinue\n$orcaClaudeCommand'
)
})
it.skipIf(!existsSync(FISH))(
'defines both the ready hook and the claude function from the composed fish init',
async () => {
const root = mkdtempSync(join(tmpdir(), 'claude-fish-compose-'))
roots.push(root)
mkdirSync(join(root, 'bin'))
writeFileSync(join(root, 'bin', 'claude'), '#!/bin/sh\nexit 0\n')
chmodSync(join(root, 'bin', 'claude'), 0o700)
const on = await composed()
for (const init of on.fish) {
const result = spawnSync(
'/usr/bin/env',
[
'-i',
`HOME=${root}`,
`PATH=${join(root, 'bin')}:/usr/bin:/bin`,
`ORCA_CLAUDE_PROFILE_POINTER=${join(root, 'selected')}`,
FISH,
'--no-config',
'-c',
`test (command -s claude) = '${join(root, 'bin', 'claude')}'; or exit 97\n${init}\nfunctions -q claude; and echo CLAUDE-FN\nfunctions -q __orca_shell_ready_marker; and echo READY-HOOK`
],
{ encoding: 'utf8' }
)
expect(result.status).not.toBe(97)
expect(result.stderr).toBe('')
expect(result.stdout).toContain('CLAUDE-FN')
expect(result.stdout).toContain('READY-HOOK')
}
}
)
@@ -1,99 +0,0 @@
import { describe, expect, it, vi } from 'vitest'
import type { CodexAppServerConnection } from './app-server/codex-app-server-connection-types'
import { fetchCodexModelCatalogListing } from './codex-structured-model-catalog'
function modelRow(id: string, isDefault = false): Record<string, unknown> {
return {
model: id,
displayName: id.toUpperCase(),
hidden: false,
supportedReasoningEfforts: [
{ reasoningEffort: 'medium' },
{ reasoningEffort: 'high' },
{ reasoningEffort: 'xhigh' }
],
defaultReasoningEffort: 'medium',
isDefault
}
}
function connection(
configRead: { answer: unknown } | { error: Error }
): Pick<CodexAppServerConnection, 'request'> & { request: ReturnType<typeof vi.fn> } {
return {
request: vi.fn(async (method: string) => {
if (method === 'model/list') {
return { data: [modelRow('gpt-6-astra', true), modelRow('gpt-5.5')], nextCursor: null }
}
if (method === 'config/read') {
if ('error' in configRead) {
throw configRead.error
}
return configRead.answer
}
throw new Error(`unexpected ${method}`)
})
}
}
function defaults(models: { id: string; isDefault: boolean; defaultEffort?: string }[]) {
return models.map(({ id, isDefault, defaultEffort }) => ({ id, isDefault, defaultEffort }))
}
describe('Codex listing default follows the configured launch model', () => {
it('marks the configured model and effort as what a launch runs', async () => {
const listing = await fetchCodexModelCatalogListing({
connection: connection({
answer: { config: { model: 'gpt-5.5', model_reasoning_effort: 'xhigh' } }
})
})
expect(defaults(listing.models)).toEqual([
{ id: 'gpt-6-astra', isDefault: false, defaultEffort: 'medium' },
{ id: 'gpt-5.5', isDefault: true, defaultEffort: 'xhigh' }
])
})
it('applies a configured effort to the listed default when no model is configured', async () => {
const listing = await fetchCodexModelCatalogListing({
connection: connection({
answer: { config: { model: null, model_reasoning_effort: 'high' } }
})
})
expect(defaults(listing.models)).toEqual([
{ id: 'gpt-6-astra', isDefault: true, defaultEffort: 'high' },
{ id: 'gpt-5.5', isDefault: false, defaultEffort: 'medium' }
])
})
it('names no default when the configured model is one the listing does not offer', async () => {
const listing = await fetchCodexModelCatalogListing({
connection: connection({
answer: { config: { model: 'gpt-oss:20b', model_reasoning_effort: 'high' } }
})
})
expect(defaults(listing.models)).toEqual([
{ id: 'gpt-6-astra', isDefault: false, defaultEffort: 'medium' },
{ id: 'gpt-5.5', isDefault: false, defaultEffort: 'medium' }
])
})
it('ignores a configured effort the listed default cannot run', async () => {
const listing = await fetchCodexModelCatalogListing({
connection: connection({ answer: { config: { model: null, model_reasoning_effort: 'max' } } })
})
expect(defaults(listing.models)).toEqual([
{ id: 'gpt-6-astra', isDefault: true, defaultEffort: 'medium' },
{ id: 'gpt-5.5', isDefault: false, defaultEffort: 'medium' }
])
})
it('keeps the listing as Codex sent it when config/read is unavailable', async () => {
const rpc = connection({ error: new Error('Method not found: config/read') })
const listing = await fetchCodexModelCatalogListing({ connection: rpc })
expect(rpc.request.mock.calls.map(([method]) => method)).toEqual(['model/list', 'config/read'])
expect(defaults(listing.models)).toEqual([
{ id: 'gpt-6-astra', isDefault: true, defaultEffort: 'medium' },
{ id: 'gpt-5.5', isDefault: false, defaultEffort: 'medium' }
])
})
})
-91
View File
@@ -1,91 +0,0 @@
import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import path from 'node:path'
import { beforeEach, describe, expect, it, vi } from 'vitest'
const { gitExecFileAsyncMock } = vi.hoisted(() => ({
gitExecFileAsyncMock: vi.fn()
}))
vi.mock('./runner', () => ({
gitExecFileAsync: gitExecFileAsyncMock,
gitExecFileAsyncBuffer: vi.fn(),
gitOptionalLocksDisabledEnv: (env: NodeJS.ProcessEnv = process.env) => env,
gitStreamStdout: vi.fn()
}))
import {
bulkDiscardChanges,
bulkStageFiles,
bulkUnstageFiles,
discardChanges,
stageFile,
unstageFile
} from './status'
const worktreePath = path.resolve('repo')
const windowsRelativePath = 'tests\\breakgit'
const wslOptions = { wslDistro: 'Ubuntu' }
describe('WSL git pathspecs', () => {
beforeEach(() => {
gitExecFileAsyncMock.mockReset()
gitExecFileAsyncMock.mockResolvedValue({ stdout: 'tests/breakgit\0', stderr: '' })
})
it('uses POSIX separators for tracked file mutations executed inside WSL', async () => {
await stageFile(worktreePath, windowsRelativePath, wslOptions)
await unstageFile(worktreePath, windowsRelativePath, wslOptions)
await bulkStageFiles(worktreePath, [windowsRelativePath], wslOptions)
await bulkUnstageFiles(worktreePath, [windowsRelativePath], wslOptions)
await discardChanges(worktreePath, windowsRelativePath, wslOptions)
await bulkDiscardChanges(worktreePath, [windowsRelativePath], wslOptions)
const pathspecs = gitExecFileAsyncMock.mock.calls.flatMap(([args]) =>
(args as string[]).filter((arg) => arg.startsWith(':(literal)'))
)
expect(pathspecs).toEqual(Array(5).fill(':(literal)tests/breakgit'))
const inputs = gitExecFileAsyncMock.mock.calls
.map(([, options]) => options.stdin)
.filter((input) => input !== undefined)
expect(inputs).toEqual(Array(3).fill(':(literal)tests/breakgit\0'))
})
it('uses POSIX separators when discarding untracked files inside WSL', async () => {
const repo = await mkdtemp(path.join(tmpdir(), 'orca-wsl-pathspec-'))
const targetPath = path.resolve(repo, windowsRelativePath)
await mkdir(path.dirname(targetPath), { recursive: true })
await writeFile(targetPath, 'untracked')
try {
gitExecFileAsyncMock
.mockRejectedValueOnce(new Error('not tracked'))
.mockResolvedValue({ stdout: '', stderr: '' })
await discardChanges(repo, windowsRelativePath, wslOptions)
expect(gitExecFileAsyncMock.mock.calls.map(([args]) => args)).toEqual([
['ls-files', '--error-unmatch', '--', ':(literal)tests/breakgit'],
['clean', '-ffdx', '--', ':(literal)tests/breakgit']
])
gitExecFileAsyncMock.mockReset()
gitExecFileAsyncMock.mockResolvedValue({ stdout: '', stderr: '' })
await bulkDiscardChanges(repo, [windowsRelativePath], wslOptions)
expect(gitExecFileAsyncMock.mock.calls.map(([args]) => args)).toEqual([
['ls-files', '-z', '--', ':(literal)tests/breakgit'],
['clean', '-ffdx', '--', ':(literal)tests/breakgit']
])
} finally {
await rm(repo, { recursive: true, force: true })
}
})
it('preserves backslashes for host Git where they can be literal filename characters', async () => {
await stageFile(worktreePath, windowsRelativePath)
expect(gitExecFileAsyncMock).toHaveBeenCalledWith(['add', '--', ':(literal)tests\\breakgit'], {
cwd: worktreePath
})
})
})
@@ -1,83 +0,0 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import type * as WorktreePreparationLock from './worktree-preparation-lock'
type GitExec = (
args: string[],
options: Record<string, unknown>
) => Promise<{ stdout: string; stderr: string }>
const gitExecFileAsyncMock = vi.hoisted(() => vi.fn<GitExec>())
vi.mock('./runner', () => ({ gitExecFileAsync: gitExecFileAsyncMock }))
vi.mock('./worktree-preparation-lock', async (importOriginal) => ({
...(await importOriginal<typeof WorktreePreparationLock>()),
verifyWorktreePreparationLock: vi.fn(async () => '/owned-lock'),
verifyWorktreePreparationLockAtPath: vi.fn(),
unlockWorktreePreparationAtPath: vi.fn()
}))
import { addWorktree } from './worktree-add'
import { listWorktreesSharedStrict } from './worktree-scan-cache'
import { finalizePreparedWorktree } from './worktree-create-preparation'
const HEAD = 'a'.repeat(40)
function optionsForCommand(match: string): Record<string, unknown>[] {
return gitExecFileAsyncMock.mock.calls
.filter((call) => call[0].join(' ').includes(match))
.map((call) => call[1])
}
describe('worktree create admission tier', () => {
beforeEach(() => {
gitExecFileAsyncMock.mockReset().mockResolvedValue({ stdout: HEAD, stderr: '' })
})
it('runs the create add at the tier the caller asked for', async () => {
await addWorktree('/repo', '/repo-wt', 'feature', 'main', false, false, {
admissionTier: 'interactive'
})
const addOptions = optionsForCommand('worktree add')
expect(addOptions).toHaveLength(1)
expect(addOptions[0]).toMatchObject({
cwd: '/repo',
admissionTier: 'interactive'
})
})
it('runs the post-add listing at the tier the caller asked for', async () => {
await listWorktreesSharedStrict('/repo', { admissionTier: 'interactive' })
const listOptions = optionsForCommand('worktree list')
expect(listOptions.length).toBeGreaterThan(0)
for (const options of listOptions) {
expect(options).toMatchObject({ admissionTier: 'interactive' })
}
})
it('runs the prepared-checkout finalize at the tier the caller asked for', async () => {
await finalizePreparedWorktree(
'/repo',
'/prepared',
'/repo-wt',
'feature',
'main',
false,
{ admissionTier: 'interactive' },
'owner'
)
for (const match of ['worktree move', 'checkout --no-track']) {
const options = optionsForCommand(match)
expect(options, match).toHaveLength(1)
expect(options[0], match).toMatchObject({ admissionTier: 'interactive' })
}
})
it('leaves a command with no tier at the scheduler default', async () => {
await addWorktree('/repo', '/repo-wt', 'feature', 'main')
expect(optionsForCommand('worktree add')[0]).not.toHaveProperty('admissionTier')
})
})
@@ -1,86 +0,0 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import type * as BackgroundDistroGate from '../wsl-background-distro-gate'
import { fakeSpawnDispatch } from '../../shared/__fixtures__/fake-spawned-child'
import type * as WslModule from '../wsl'
const { execFileSyncMock, spawnMock, getDefaultWslDistroMock } = vi.hoisted(() => ({
execFileSyncMock: vi.fn(),
spawnMock: vi.fn(),
getDefaultWslDistroMock: vi.fn()
}))
// The default-distro fallback asks whether the distro runs before it may start it (E9).
vi.mock('../wsl-background-distro-gate', async (importOriginal) => ({
...(await importOriginal<typeof BackgroundDistroGate>()),
readWslDistroRunningState: vi.fn(async () => 'running')
}))
vi.mock('child_process', () => ({
execFile: vi.fn(),
execFileSync: execFileSyncMock,
spawn: spawnMock
}))
vi.mock('../wsl', async (importOriginal) => ({
...(await importOriginal<typeof WslModule>()),
getDefaultWslDistro: getDefaultWslDistroMock
}))
import { setDefaultWslDistroOverride } from '../git/runner'
import { _resetKnownHostsCache, getGlabKnownHosts } from './gitlab-known-host-probe'
describe('glab known-hosts probe on Windows', () => {
const originalPlatform = process.platform
const hostGlabMissingWslLoggedIn = (): void => {
spawnMock.mockImplementation(
fakeSpawnDispatch((program) =>
program === 'wsl.exe'
? { stdout: 'Logged in to gitlab.wsl.test as user' }
: { spawnError: Object.assign(new Error('spawn glab ENOENT'), { code: 'ENOENT' }) }
)
)
}
beforeEach(() => {
spawnMock.mockReset()
getDefaultWslDistroMock.mockReset()
getDefaultWslDistroMock.mockReturnValue('Ubuntu')
setDefaultWslDistroOverride(null)
_resetKnownHostsCache()
Object.defineProperty(process, 'platform', { configurable: true, value: 'win32' })
})
afterEach(() => {
setDefaultWslDistroOverride(null)
_resetKnownHostsCache()
Object.defineProperty(process, 'platform', { configurable: true, value: originalPlatform })
})
it('does not wake the default WSL distro for the native execution key', async () => {
hostGlabMissingWslLoggedIn()
await expect(getGlabKnownHosts()).resolves.toEqual(['gitlab.com'])
expect(spawnMock).toHaveBeenCalledTimes(1)
expect(spawnMock).toHaveBeenCalledWith(
'glab',
['auth', 'status'],
expect.objectContaining({ cwd: undefined })
)
})
// Connection-keyed auth and API calls both use local glab, including its default-distro fallback.
it('keeps the default-distro fallback for a connection execution key', async () => {
hostGlabMissingWslLoggedIn()
await expect(getGlabKnownHosts('conn-1')).resolves.toEqual(['gitlab.com', 'gitlab.wsl.test'])
expect(spawnMock).toHaveBeenCalledWith(
'wsl.exe',
['-d', 'Ubuntu', '--exec', 'bash', '-c', "'glab' 'auth' 'status'"],
// A concrete safe cwd avoids inheriting a deletable worktree's UNC directory during Windows spawn.
expect.objectContaining({ cwd: expect.any(String) })
)
})
})
@@ -1,86 +0,0 @@
import { describe, expect, it, vi } from 'vitest'
import { onMock } from './pty/pty-ipc-mock-registry'
import { setupPtyIpcSuite } from './pty/pty-ipc-test-harness'
import { registerPtyHandlers } from './pty/register-handlers'
import { setLocalPtyProvider } from './pty/provider/registry'
vi.mock('electron', () => import('./pty/pty-ipc-mock-registry').then((m) => m.electronModuleMock()))
vi.mock('fs', () => import('./pty/pty-ipc-mock-registry').then((m) => m.fsModuleMock()))
vi.mock('node-pty', () => import('./pty/pty-ipc-mock-registry').then((m) => m.nodePtyModuleMock()))
vi.mock('node:child_process', async (importOriginal) =>
(await import('./pty/pty-ipc-mock-registry')).childProcessModuleMock(await importOriginal())
)
vi.mock('../opencode/hook-service', () =>
import('./pty/pty-ipc-mock-registry').then((m) => m.openCodeHookServiceModuleMock())
)
vi.mock('../mimo/hook-service', () =>
import('./pty/pty-ipc-mock-registry').then((m) => m.mimoHookServiceModuleMock())
)
vi.mock('../agent-hooks/server', () =>
import('./pty/pty-ipc-mock-registry').then((m) => m.agentHookServerModuleMock())
)
vi.mock('../pi/titlebar-extension-service', () =>
import('./pty/pty-ipc-mock-registry').then((m) => m.piTitlebarExtensionModuleMock())
)
vi.mock('../pwsh', () => import('./pty/pty-ipc-mock-registry').then((m) => m.pwshModuleMock()))
vi.mock('../wsl', async (importOriginal) =>
(await import('./pty/pty-ipc-mock-registry')).wslModuleMock(await importOriginal())
)
vi.mock('../telemetry/client', () =>
import('./pty/pty-ipc-mock-registry').then((m) => m.telemetryClientModuleMock())
)
vi.mock('../telemetry/classify-error', () =>
import('./pty/pty-ipc-mock-registry').then((m) => m.classifyErrorModuleMock())
)
vi.mock('../cli/linux-terminal-orca-cli-shim', () =>
import('./pty/pty-ipc-mock-registry').then((m) => m.linuxCliShimModuleMock())
)
vi.mock('../memory/pty-registry', () =>
import('./pty/pty-ipc-mock-registry').then((m) => m.ptyRegistryModuleMock())
)
vi.mock('../agent-hooks/migration-unsupported-pty-state', () =>
import('./pty/pty-ipc-mock-registry').then((m) => m.migrationUnsupportedPtyModuleMock())
)
vi.mock('../codex/codex-pane-account-registry', () =>
import('./pty/pty-ipc-mock-registry').then((m) => m.codexPaneAccountRegistryModuleMock())
)
vi.mock('../codex/codex-state-db-backfill-recovery', () =>
import('./pty/pty-ipc-mock-registry').then((m) => m.codexBackfillRecoveryModuleMock())
)
vi.mock('../../shared/layout-owner-switch', () => ({ ORCA_LAYOUT_OWNER_IS_MAIN: true }))
const PANE = { worktreeId: 'repo::/wt', tabId: 'tab-1', leafId: 'leaf-1' }
describe('a painted cold-restore ack in the layout-owner build', () => {
const { mainWindow } = setupPtyIpcSuite()
function ackWith(boundPtyId: string, args: unknown): ReturnType<typeof vi.fn> {
const ackColdRestore = vi.fn()
const isRestoredPaneBoundTo = (_pane: unknown, ptyId: string): boolean => ptyId === boundPtyId
// Only the binding read matters here; every other runtime hook the handlers install is inert.
const runtime = new Proxy(
{},
{ get: (_target, key) => (key === 'isRestoredPaneBoundTo' ? isRestoredPaneBoundTo : vi.fn()) }
)
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the Proxy answers every runtime hook the handlers install.
registerPtyHandlers(mainWindow, runtime as never)
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the ack listener calls only ackColdRestore.
setLocalPtyProvider({ ackColdRestore } as never)
const call = onMock.mock.calls.findLast((entry: unknown[]) => entry[0] === 'pty:ackColdRestore')
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: ipcMain.on registered this channel with an (event, args) listener.
const listener = call?.[1] as (event: unknown, args: unknown) => void
listener(null, args)
return ackColdRestore
}
it('releases the terminal service copy for the pane’s current terminal', () => {
expect(ackWith('pty-1', { id: 'pty-1', pane: PANE })).toHaveBeenCalledWith('pty-1')
})
it('keeps it when a stale view acks a terminal the pane no longer binds', () => {
expect(ackWith('pty-new', { id: 'pty-old', pane: PANE })).not.toHaveBeenCalled()
})
it('keeps it for an ack that names no pane', () => {
expect(ackWith('pty-1', { id: 'pty-1' })).not.toHaveBeenCalled()
})
})
@@ -1,85 +0,0 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import type { Repo } from '../../../../shared/repo-types'
import type { Store } from '../../../persistence/loading-store/store'
import { runWorktreeChangeInvalidators } from '../../worktree-change-invalidators'
import { registerSparseCheckoutCacheInvalidation } from './register-sparse-checkout-cache-invalidation'
const {
clearSparseCheckoutStateCacheMock,
clearSparseCheckoutStateCacheForRepoMock,
onSparseCheckoutStateChangedMock,
notifyWorktreesChangedMock
} = vi.hoisted(() => ({
clearSparseCheckoutStateCacheMock: vi.fn(),
clearSparseCheckoutStateCacheForRepoMock: vi.fn(),
onSparseCheckoutStateChangedMock: vi.fn(),
notifyWorktreesChangedMock: vi.fn()
}))
vi.mock('../../../git/worktree-sparse-checkout-cache', () => ({
clearSparseCheckoutStateCache: clearSparseCheckoutStateCacheMock,
clearSparseCheckoutStateCacheForRepo: clearSparseCheckoutStateCacheForRepoMock,
onSparseCheckoutStateChanged: onSparseCheckoutStateChangedMock
}))
vi.mock('../../worktree-remote', () => ({
notifyWorktreesChanged: notifyWorktreesChangedMock
}))
function makeStore(repos: Repo[]): Store {
return {
getRepo: (id: string) => repos.find((repo) => repo.id === id),
getRepos: () => repos
} as unknown as Store
}
const mainWindow = {} as never
beforeEach(() => {
vi.clearAllMocks()
})
describe('registerSparseCheckoutCacheInvalidation', () => {
it('clears only the resolved repo`s cache when the invalidator registry fires', () => {
const store = makeStore([{ id: 'repo-1', path: '/repo-1' } as Repo])
const dispose = registerSparseCheckoutCacheInvalidation(mainWindow, store)
try {
runWorktreeChangeInvalidators('repo-1')
expect(clearSparseCheckoutStateCacheForRepoMock).toHaveBeenCalledWith('/repo-1')
expect(clearSparseCheckoutStateCacheMock).not.toHaveBeenCalled()
} finally {
dispose()
}
})
it('falls back to a full clear when the repo cannot be resolved', () => {
const store = makeStore([])
const dispose = registerSparseCheckoutCacheInvalidation(mainWindow, store)
try {
runWorktreeChangeInvalidators('unknown-repo')
expect(clearSparseCheckoutStateCacheMock).toHaveBeenCalledTimes(1)
expect(clearSparseCheckoutStateCacheForRepoMock).not.toHaveBeenCalled()
} finally {
dispose()
}
})
it('forwards a background stale-while-revalidate flip to the shared worktrees-changed notification', () => {
const store = makeStore([{ id: 'repo-1', path: '/repo-1' } as Repo])
const dispose = registerSparseCheckoutCacheInvalidation(mainWindow, store)
try {
const listener = onSparseCheckoutStateChangedMock.mock.calls.at(-1)?.[0]
listener?.('/repo-1', '/repo-1/wt-a', true)
expect(notifyWorktreesChangedMock).toHaveBeenCalledWith(mainWindow, 'repo-1')
} finally {
dispose()
}
})
it('drops the change listener on disposal so a stale window/store stops receiving flips', () => {
const store = makeStore([{ id: 'repo-1', path: '/repo-1' } as Repo])
const dispose = registerSparseCheckoutCacheInvalidation(mainWindow, store)
dispose()
expect(onSparseCheckoutStateChangedMock).toHaveBeenLastCalledWith(undefined)
})
})
@@ -1,97 +0,0 @@
import { afterEach, describe, expect, it, vi } from 'vitest'
const order = vi.hoisted(() => ({ calls: new Array<string>(), serving: true }))
vi.mock('electron', () => ({
BrowserWindow: { getAllWindows: () => [] },
ipcMain: { removeHandler: vi.fn(), handle: vi.fn() }
}))
vi.mock('../wsl/wsl-host-gate', async (importOriginal) => ({
...(await importOriginal<Record<string, unknown>>()),
wslHostsServing: () => order.serving
}))
vi.mock('../../shared/runtime-environment-wsl-host-store', async (importOriginal) => ({
...(await importOriginal<Record<string, unknown>>()),
reconcileWslHostBridgePorts: () => {
order.calls.push('reconcile')
return []
}
}))
vi.mock('../wsl/wsl-host-connector', () => ({ createWslHostConnector: vi.fn() }))
vi.mock('../wsl/wsl-host-e2e-fake-connector', () => ({
readFakeWslHostsConfig: () => ({ serverFiles: 'present' }),
createFakeWslHostConnector: () => {
order.calls.push('connector')
return {
connect: async () => {
order.calls.push('connect')
}
}
}
}))
vi.mock('../wsl/wsl-orphan-holder-release', () => ({
releaseUnwantedWslHolders: async () => {},
wslHolderKeepPolicy: () => () => true
}))
vi.mock('./wsl-host-automation-wake-wiring', () => ({ createWslHostAutomationWake: () => null }))
vi.mock('../wsl/wsl-host-service', () => ({
WslHostService: class {
connector: { connect: (distro: string) => Promise<void> } | null = null
attach(connector: { connect: (distro: string) => Promise<void> }): void {
this.connector = connector
}
handleConnectionChange(): void {}
isKnownDistro(): boolean {
return true
}
hasUserVisibleWork(): boolean {
return false
}
// Startup's first bridge work: adopting holders, then any reconnect.
async start(): Promise<void> {
order.calls.push('start')
await this.connector?.connect('Ubuntu')
}
}
}))
const instance = vi.hoisted((): { service: unknown } => ({ service: null }))
vi.mock('../wsl/wsl-host-service-instance', () => ({
getWslHostService: () => instance.service,
setWslHostService: (service: unknown) => {
instance.service = service
},
// The phone's gate is built beside the service; startup order is all this suite checks.
setWslHostPhoneGate: () => {}
}))
import { registerWslHostHandlers } from './wsl-host-handlers'
function register(): void {
registerWslHostHandlers({
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: startup reads settings only through onSettingsChanged.
store: { getSettings: () => ({}), onSettingsChanged: vi.fn() } as never,
getUserDataPath: () => '/user-data',
invalidateTransport: vi.fn()
})
}
// A crash between a pairing save's two writes hides the host unless its record is realigned first.
describe('WSL host startup: the bridge-port reconcile', () => {
afterEach(() => {
order.calls = []
order.serving = true
instance.service = null
})
it('realigns saved records before the connector exists or any bridge connects', async () => {
register()
await vi.waitFor(() => expect(order.calls).toContain('connect'))
expect(order.calls).toEqual(['reconcile', 'connector', 'start', 'connect'])
})
it('reads and writes no record while WSL hosts are not serving', () => {
order.serving = false
register()
expect(order.calls).toEqual([])
expect(instance.service).toBeNull()
})
})
@@ -1,91 +0,0 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import type * as WslModule from '../wsl'
import type * as WslRunningPathFilterModule from '../wsl-running-path-filter'
const MANAGED_HOME = '/tmp/orca-user-data/codex-runtime-home/home'
// Polling may read paths only; the launch-time getter mkdirSyncs and would block every tick.
vi.mock('../codex/codex-home-paths', () => ({
getOrcaManagedCodexHomePath: vi.fn(() => MANAGED_HOME),
resolveOrcaManagedCodexHomePath: vi.fn(() => MANAGED_HOME)
}))
// Disable WSL fallback so the host-roots test behaves identically on every platform.
vi.mock('../wsl', async (importOriginal) => ({
...(await importOriginal<typeof WslModule>()),
listRunningWslHomeDirsAsync: vi.fn(async () => [])
}))
vi.mock('../wsl-running-path-filter', async (importOriginal) => ({
...(await importOriginal<typeof WslRunningPathFilterModule>()),
filterPathsToRunningWslDistrosAsync: vi.fn(async (paths: readonly string[]) => [...paths])
}))
const scanned = vi.hoisted(() => ({ dirs: [] as string[] }))
vi.mock('../ai-vault/session-scanner-discovery', () => ({
walkSessionFiles: async (dir: string) => {
scanned.dirs.push(dir)
return []
}
}))
import { join } from 'node:path'
import { getOrcaManagedCodexHomePath } from '../codex/codex-home-paths'
import {
configureHostReadableTranscriptPathSources,
resetHostReadableTranscriptPathCacheForTests
} from './host-readable-transcript-path'
import { resolveSessionFilePath } from './session-file-resolver'
beforeEach(() => {
resetHostReadableTranscriptPathCacheForTests()
vi.mocked(getOrcaManagedCodexHomePath).mockClear()
scanned.dirs = []
})
describe('codex sessions roots', () => {
it('scans the managed home without materializing it', async () => {
await resolveSessionFilePath('codex', 'sess-1')
expect(scanned.dirs).toContain(join(MANAGED_HOME, 'sessions'))
expect(vi.mocked(getOrcaManagedCodexHomePath)).not.toHaveBeenCalled()
})
it('scans per-account homes after the default roots, leaving WSL shares to their tier', async () => {
const accountHome = '/home/u/.local/share/orca/codex-accounts/a1/home'
configureHostReadableTranscriptPathSources({
getAdditionalCodexHomePaths: () => [accountHome, '\\\\wsl.localhost\\Ubuntu\\home\\u\\.codex']
})
await resolveSessionFilePath('codex', 'sess-1')
const accountDir = join(accountHome, 'sessions')
expect(scanned.dirs).toContain(accountDir)
expect(scanned.dirs.indexOf(accountDir)).toBeGreaterThan(
scanned.dirs.indexOf(join(MANAGED_HOME, 'sessions'))
)
expect(scanned.dirs.some((dir) => dir.startsWith('\\\\'))).toBe(false)
})
it('leaves per-account homes out when the caller names the roots', async () => {
const getAdditionalCodexHomePaths = vi.fn(() => ['/home/u/acct/home'])
configureHostReadableTranscriptPathSources({ getAdditionalCodexHomePaths })
await resolveSessionFilePath('codex', 'sess-1', { codexSessionsDirs: ['/pinned/sessions'] })
expect(scanned.dirs).toEqual(['/pinned/sessions'])
expect(getAdditionalCodexHomePaths).not.toHaveBeenCalled()
})
it('still answers when the account homes cannot be listed', async () => {
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {})
configureHostReadableTranscriptPathSources({
getAdditionalCodexHomePaths: () => {
throw new Error('EACCES')
}
})
await expect(resolveSessionFilePath('codex', 'sess-1')).resolves.toBe(null)
expect(scanned.dirs).toContain(join(MANAGED_HOME, 'sessions'))
warn.mockRestore()
})
})
@@ -1,87 +0,0 @@
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { mkdtempSync, rmSync } from 'node:fs'
import { spawnSync } from 'node:child_process'
import { shellScriptTest } from './shell-ready-framework/shell-script-test'
const { getUserDataPathMock } = vi.hoisted(() => ({
getUserDataPathMock: vi.fn<() => string>()
}))
vi.mock('electron', () => ({
app: {
getPath: (name: string) => {
if (name === 'userData') {
return getUserDataPathMock()
}
throw new Error(`unexpected app.getPath(${name})`)
}
}
}))
const hasZsh = process.platform !== 'win32' && spawnSync('which', ['zsh']).status === 0
const describeIfZsh = hasZsh ? describe : describe.skip
describeIfZsh('shell-script-literal framework example', () => {
let userDataPath: string
beforeEach(() => {
userDataPath = mkdtempSync(join(tmpdir(), 'shell-test-userdata-'))
getUserDataPathMock.mockReturnValue(userDataPath)
})
afterEach(() => {
rmSync(userDataPath, { recursive: true, force: true })
})
it('discovers ZDOTDIR when .zshenv sources another file', async () => {
const { stdout } = await shellScriptTest(
`
# Setup multi-file config
mkdir -p ~/.config/zsh
cat > ~/.config/zsh/env <<'EOF'
export ZDOTDIR="$HOME/.config/zsh"
EOF
cat > ~/.zshenv <<'EOF'
source "$HOME/.config/zsh/env"
EOF
# Run: check discovered ZDOTDIR
zsh -c 'env | grep -E "^(ORCA_|ZDOTDIR|HOME)=" | sort'
`,
{ userDataPath }
)
expect(stdout).toMatchInlineSnapshot(`
"HOME=<HOME>
ZDOTDIR=<HOME>/.config/zsh
"
`)
})
it('handles conditional ZDOTDIR based on SSH_CONNECTION', async () => {
const { stdout } = await shellScriptTest(
`
mkdir -p ~/.config/zsh-local ~/.config/zsh-remote
cat > ~/.zshenv <<'EOF'
if [[ -n "$SSH_CONNECTION" ]]; then
export ZDOTDIR="$HOME/.config/zsh-remote"
else
export ZDOTDIR="$HOME/.config/zsh-local"
fi
EOF
# Run with SSH_CONNECTION set
SSH_CONNECTION='192.168.1.100 52100 192.168.1.1 22' zsh -c 'env | grep ZDOTDIR | sort'
`,
{ userDataPath }
)
expect(stdout).toMatchInlineSnapshot(`
"ZDOTDIR=<HOME>/.config/zsh-remote
"
`)
})
})
@@ -1,82 +0,0 @@
import { describe, expect, it, vi } from 'vitest'
import { OrcaRuntimeService } from '../../orca-runtime'
import { denyRpcMethodForCaller } from '../rpc-caller-scope'
import { eraseRpcMethods } from '../core'
import { AUTOMATIC_WORK_METHODS } from './automatic-work'
import type { RpcContext } from '../core'
import type { RuntimePtyController } from '../../runtime-pty-controller-contract'
function context(runtime: OrcaRuntimeService): RpcContext {
return { runtime }
}
function method(name: string) {
const found = eraseRpcMethods(AUTOMATIC_WORK_METHODS).find((candidate) => candidate.name === name)
if (!found) {
throw new Error(`Missing method ${name}`)
}
return found
}
function runtimeWithController(
controller: Pick<RuntimePtyController, 'getAutomaticWorkStatus' | 'resumeAutomaticWork'>
): OrcaRuntimeService {
const runtime = new OrcaRuntimeService(null)
runtime.setPtyController({
spawn: vi.fn(),
write: () => true,
kill: () => true,
getForegroundProcess: async () => null,
...controller
})
return runtime
}
describe('runtime.automaticWork RPC methods', () => {
it('resumes through the local terminal owner and reports what it cleared', async () => {
const cleared = { kind: 'paused' as const, reason: 'unclean_host_death' as const, since: 5 }
const resumeAutomaticWork = vi.fn(async () => ({
admission: 'enforce' as const,
mode: { kind: 'normal' as const },
cleared
}))
const runtime = runtimeWithController({ resumeAutomaticWork })
await expect(
method('runtime.automaticWork.resume').handler(undefined, context(runtime))
).resolves.toEqual({
automaticWork: { admission: 'enforce', mode: { kind: 'normal' }, cleared }
})
expect(resumeAutomaticWork).toHaveBeenCalledOnce()
})
it('answers null when the local owner has no gate', async () => {
const runtime = runtimeWithController({})
await expect(
method('runtime.automaticWork.status').handler(undefined, context(runtime))
).resolves.toEqual({ automaticWork: null })
await expect(
method('runtime.automaticWork.resume').handler(undefined, context(runtime))
).resolves.toEqual({ automaticWork: null })
})
it('says when the running terminal host predates resume instead of claiming no gate', async () => {
const runtime = runtimeWithController({
resumeAutomaticWork: async () => 'terminal-host-predates-resume' as const
})
await expect(
method('runtime.automaticWork.resume').handler(undefined, context(runtime))
).resolves.toEqual({ automaticWork: null, terminalHostPredatesResume: true })
})
it('lets a paired desktop resume but never a phone', () => {
const resume = method('runtime.automaticWork.resume')
expect(resume.permission).toBe('host-admin')
expect(
denyRpcMethodForCaller({ kind: 'runtime-paired', grants: [] }, resume.name, resume.permission)
).toBeNull()
expect(
denyRpcMethodForCaller({ kind: 'mobile' }, resume.name, resume.permission)
).not.toBeNull()
})
})
@@ -1,87 +0,0 @@
/** Runtime removals must clear the same per-id process state and persisted scan snapshots as desktop IPC removals. */
import { beforeEach, describe, expect, it, vi } from 'vitest'
const mocks = vi.hoisted(() => ({
unregisterWorktree: vi.fn(),
pruneCleanupScan: vi.fn(async () => {}),
pruneSpaceAnalysis: vi.fn(async () => {})
}))
vi.mock('../localhost-worktree-label-proxy', () => ({
localhostWorktreeLabelProxy: { unregisterWorktree: mocks.unregisterWorktree }
}))
vi.mock('../workspace-cleanup-scan-snapshot', () => ({
pruneWorkspaceCleanupScanSnapshot: mocks.pruneCleanupScan
}))
vi.mock('../workspace-space-analysis-snapshot', () => ({
pruneWorkspaceSpaceAnalysisSnapshot: mocks.pruneSpaceAnalysis
}))
import type { Repo } from '../../shared/repo-types'
import { OrcaRuntimeService } from './orca-runtime'
const WT = 'repo-1::/tmp/worktree-a'
function repo(overrides: Partial<Repo> = {}): Repo {
return {
id: 'repo-1',
path: '/tmp/repo',
displayName: 'repo',
badgeColor: 'blue',
addedAt: 1,
...overrides
}
}
function createRuntime(repos: Repo[]) {
const store = {
getRepo: () => repos[0],
getRepos: () => repos,
addRepo: () => {},
updateRepo: () => undefined,
getAllWorktreeMeta: () => ({}),
getWorktreeMeta: () => undefined,
removeWorktreeMeta: () => {},
getGitHubCache: () => ({ pr: {}, issue: {} }),
setWorktreeMeta: () => undefined,
getRetiredWorktreeNameRegistry: () => ({ exhaustedTiers: 0, names: [] }),
addRetiredWorktreeName: () => {},
mergeRetiredWorktreeNames: () => false,
getProfileStorageDirectory: () => '/tmp/profile',
getSettings: () => ({
workspaceDir: '/tmp/workspaces',
nestWorkspaces: false,
refreshLocalBaseRefOnWorktreeCreate: false,
branchPrefix: 'none',
branchPrefixCustom: ''
})
}
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: a partial store covering every method this removal path reads.
const runtimeStore = store as never
return { runtime: new OrcaRuntimeService(runtimeStore), store: runtimeStore }
}
beforeEach(() => {
vi.clearAllMocks()
})
describe('runtime worktree removal cleanup', () => {
it('drops label routes and persisted scan snapshots', () => {
const { runtime, store } = createRuntime([repo()])
runtime.removeWorktreeMetadataAndHistory(store, WT, 'local')
expect(mocks.unregisterWorktree).toHaveBeenCalledWith(WT)
expect(mocks.pruneCleanupScan).toHaveBeenCalledWith('/tmp/profile', WT, 'local')
expect(mocks.pruneSpaceAnalysis).toHaveBeenCalledWith('/tmp/profile', WT, 'local')
})
it('keeps a surviving same-id twin its process state but prunes only this host snapshot', () => {
const { runtime, store } = createRuntime([repo(), repo({ connectionId: 'box' })])
runtime.removeWorktreeMetadataAndHistory(store, WT, 'local')
expect(mocks.unregisterWorktree).not.toHaveBeenCalled()
expect(mocks.pruneCleanupScan).toHaveBeenCalledWith('/tmp/profile', WT, 'local')
})
})
@@ -1,87 +0,0 @@
// Runs on plain Node, as orcad does: facts come from the host's persisted PR cache.
import { describe, expect, it, vi } from 'vitest'
import { OrcaRuntimeService } from '../orca-runtime'
import type { PRInfo } from '../../../shared/github/pull-request-types'
import type { RuntimeStore } from '../runtime-store-contract'
import type { ResolvedWorktreeSnapshot } from '../runtime-resolved-worktree-cache'
import type { ResolvedWorktree } from '../runtime-worktree-path-identity'
vi.mock('../../../shared/sidebar-inputs-switch', () => ({ ORCA_SIDEBAR_INPUTS_IS_MAIN: true }))
function withSidebarFacts(runtime: OrcaRuntimeService) {
const facts = runtime.sidebarFacts
if (!facts) {
throw new Error('built without the sidebar facts layer')
}
return facts
}
const repo = { id: 'repo-1', path: '/r', displayName: 'r', badgeColor: 'blue', addedAt: 1 }
function worktree(id: string, branch: string): ResolvedWorktree {
return {
id,
repoId: repo.id,
path: id,
branch,
head: '',
isBare: false,
isMainWorktree: false,
displayName: id,
comment: '',
linkedIssue: null,
linkedPR: null,
linkedLinearIssue: null,
linkedGitLabMR: null,
linkedGitLabIssue: null,
isArchived: false,
isUnread: false,
isPinned: false,
sortOrder: 0,
lastActivityAt: 0,
parentWorktreeId: null,
childWorktreeIds: [],
lineage: null,
git: { path: id, head: '', branch, isBare: false, isMainWorktree: false }
}
}
describe('getWorkspaceReviewFacts', () => {
it('keys the persisted PR cache by workspace row', async () => {
const merged: PRInfo = {
number: 3,
title: 'Ship it',
state: 'merged',
url: 'https://github.com/o/r/pull/3',
checksStatus: 'success',
updatedAt: '2026-10-17T00:00:00Z',
mergeable: 'UNKNOWN'
}
const pr = { 'repo-1::a': { data: merged, fetchedAt: 1 } }
const store: Pick<RuntimeStore, 'getRepos' | 'getGitHubCache'> = {
getRepos: () => [repo],
getGitHubCache: () => ({ pr, issue: {} })
}
class Probe extends OrcaRuntimeService {
protected override async listResolvedWorktreeSnapshot(): Promise<ResolvedWorktreeSnapshot> {
return {
worktrees: [worktree('repo-1::/r/a', 'refs/heads/a'), worktree('repo-1::/r/b', 'b')],
platformByRepoId: new Map()
}
}
}
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the read touches only repos and the GitHub cache.
const runtime = withSidebarFacts(new Probe(store as RuntimeStore))
await expect(runtime.getWorkspaceReviewFacts()).resolves.toEqual({
facts: { '|repo-1::/r/a': { provider: 'github', reviewState: 'merged' } }
})
})
it('publishes nothing without a store', async () => {
await expect(
withSidebarFacts(new OrcaRuntimeService()).getWorkspaceReviewFacts()
).resolves.toEqual({
facts: {}
})
})
})
@@ -1,84 +0,0 @@
import { afterEach, describe, expect, it, vi } from 'vitest'
const { loadWindowsSecurity, setIsolatedSpawner } = vi.hoisted(() => ({
loadWindowsSecurity: vi.fn(),
setIsolatedSpawner: vi.fn()
}))
vi.mock('../../shared/windows-security-addon', () => ({ loadWindowsSecurity }))
vi.mock('@orca/process-host/isolated-detached-spawn', () => ({ setIsolatedSpawner }))
import { installWindowsIsolatedSpawner } from './windows-isolated-spawner'
function addonWith(hasNativeExport: (name: string) => boolean) {
const spawnDetachedIsolated = vi.fn()
return {
spawnDetachedIsolated,
security: { userSid: 'S-1-5-18', addon: { hasNativeExport, spawnDetachedIsolated } }
}
}
describe('installWindowsIsolatedSpawner', () => {
afterEach(() => {
vi.restoreAllMocks()
loadWindowsSecurity.mockReset()
setIsolatedSpawner.mockReset()
})
it('is a no-op off Windows', () => {
installWindowsIsolatedSpawner('darwin')
expect(loadWindowsSecurity).not.toHaveBeenCalled()
expect(setIsolatedSpawner).not.toHaveBeenCalled()
})
it('installs the native spawner when the binary exports it', () => {
const { security, spawnDetachedIsolated } = addonWith(() => true)
loadWindowsSecurity.mockReturnValue(security)
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {})
installWindowsIsolatedSpawner('win32')
expect(setIsolatedSpawner).toHaveBeenCalledWith(spawnDetachedIsolated)
expect(warn).not.toHaveBeenCalled()
})
it('still installs a pre-breakaway spawner for its handle isolation, and logs it', () => {
const { security, spawnDetachedIsolated } = addonWith(
(name) => name === 'spawnDetachedIsolated'
)
loadWindowsSecurity.mockReturnValue(security)
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {})
installWindowsIsolatedSpawner('win32')
expect(setIsolatedSpawner).toHaveBeenCalledWith(spawnDetachedIsolated)
expect(warn).toHaveBeenCalledWith(
'[startup] detached spawns stay in our jobs; the addon build predates breakaway'
)
})
it('keeps libuv and logs when the binary is stale', () => {
loadWindowsSecurity.mockReturnValue(addonWith(() => false).security)
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {})
installWindowsIsolatedSpawner('win32')
expect(setIsolatedSpawner).not.toHaveBeenCalled()
expect(warn).toHaveBeenCalledWith(
'[startup] detached spawns inherit handles; the addon build is stale'
)
})
it('keeps libuv and logs when the addon is unavailable or throws', () => {
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {})
loadWindowsSecurity.mockReturnValueOnce({ error: 'missing binary' })
loadWindowsSecurity.mockImplementationOnce(() => {
throw new Error('boom')
})
expect(() => installWindowsIsolatedSpawner('win32')).not.toThrow()
expect(() => installWindowsIsolatedSpawner('win32')).not.toThrow()
expect(setIsolatedSpawner).not.toHaveBeenCalled()
expect(warn).toHaveBeenCalledTimes(2)
})
})
@@ -1,96 +0,0 @@
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, beforeEach, expect, it, vi } from 'vitest'
import { UNVERIFIABLE_ORCAD_TERMINAL_CENSUS } from '../../shared/orcad-terminal-census'
import { encodePairingOffer } from '../../shared/pairing'
import { createFakeWslHostConnector } from './wsl-host-e2e-fake-connector'
const pairingCode = encodePairingOffer({
v: 2,
endpoint: 'ws://127.0.0.1:42000',
deviceToken: 'fake-token',
publicKeyB64: Buffer.alloc(32, 7).toString('base64')
})
const runtime = {
call: async () => ({}),
census: async () => UNVERIFIABLE_ORCAD_TERMINAL_CENSUS
}
let dir: string
let distrosFile: string
function writeState(state: 'running' | 'stopped'): void {
writeFileSync(distrosFile, JSON.stringify([{ name: 'Ubuntu', state, version: 2 }]))
}
function connector(onChange = vi.fn(), stopSilently = false) {
const fake = createFakeWslHostConnector(
{ distros: [], distrosFile, pairingCode, stopSilently },
onChange,
{ isKnownDistro: () => true, runtime }
)
return { fake, onChange }
}
beforeEach(() => {
dir = mkdtempSync(join(tmpdir(), 'orca-fake-wsl-'))
distrosFile = join(dir, 'distros.json')
writeState('running')
})
afterEach(() => {
rmSync(dir, { recursive: true, force: true })
})
it('drops the bridge of a distro the test stops and refuses a no-boot reconnect', async () => {
const { fake, onChange } = connector()
await fake.connect('Ubuntu')
expect(await fake.reachability('Ubuntu', 'wsl:ubuntu')).toBe('available')
writeState('stopped')
expect(await fake.reachability('Ubuntu', 'wsl:ubuntu')).toBe('stopped')
expect(fake.get('Ubuntu')).toBeNull()
// Matches production liveness, which reports the stop rather than a plain close.
expect(onChange).toHaveBeenLastCalledWith('Ubuntu', null, {
state: 'distro-stopped',
terminals: 'exited',
recovering: false
})
await expect(fake.connect('Ubuntu', { noBoot: true })).rejects.toThrow('Ubuntu is not running.')
})
it('leaves a silent stop for a runtime call to report, as when liveness saw only a quick reboot', async () => {
const { fake, onChange } = connector(vi.fn(), true)
await fake.connect('Ubuntu')
writeState('stopped')
expect(await fake.reachability('Ubuntu', 'wsl:ubuntu')).toBe('stopped')
expect(onChange).toHaveBeenLastCalledWith('Ubuntu', null, { state: 'closed' })
})
it('still reports a plain close for an explicit disconnect of a running distro', async () => {
const { fake, onChange } = connector()
await fake.connect('Ubuntu')
await fake.disconnect('Ubuntu')
expect(onChange).toHaveBeenLastCalledWith('Ubuntu', null, { state: 'closed' })
})
it('boots a stopped distro on a plain connect', async () => {
const { fake } = connector()
writeState('stopped')
await fake.connect('Ubuntu')
expect(JSON.parse(readFileSync(distrosFile, 'utf8'))).toEqual([
{ name: 'Ubuntu', state: 'running', version: 2 }
])
expect(await fake.reachability('Ubuntu', 'wsl:ubuntu')).toBe('available')
})
it('keeps a fixed listing always running', async () => {
const fake = createFakeWslHostConnector(
{ distros: [{ name: 'Legacy', state: 'stopped', version: 1 }], pairingCode },
vi.fn(),
{ isKnownDistro: () => true, runtime }
)
expect(await fake.reachability('Legacy', 'wsl:legacy')).toBe('unreachable')
await fake.connect('Legacy', { noBoot: true })
expect(await fake.reachability('Legacy', 'wsl:legacy')).toBe('available')
})
@@ -1,86 +0,0 @@
// @vitest-environment happy-dom
import { act } from 'react'
import { createRoot, type Root } from 'react-dom/client'
import { afterEach, describe, expect, it, vi } from 'vitest'
import type { ExecutionHostId } from '../../../../../../shared/execution-host'
const testState: { executionHostId: ExecutionHostId } = vi.hoisted(() => ({
executionHostId: 'local'
}))
vi.mock('@/store', () => ({ useAppStore: { getState: () => ({}) } }))
vi.mock('@/lib/worktree-runtime-owner', () => ({
getKnownExecutionHostIdForWorktree: () => testState.executionHostId
}))
const { CombinedDiffFileTreeRow } = await import('./combined-diff-file-tree-row')
const { readWorkspaceFileDragSource } = await import('@/lib/workspace-file-drag')
globalThis.IS_REACT_ACT_ENVIRONMENT = true
const roots: Root[] = []
afterEach(() => {
roots.splice(0).forEach((root) => act(() => root.unmount()))
document.body.replaceChildren()
testState.executionHostId = 'local'
})
function renderRow(sourceWorkspaceId?: string): HTMLDivElement {
const container = document.createElement('div')
document.body.appendChild(container)
const root = createRoot(container)
roots.push(root)
act(() => {
root.render(
<CombinedDiffFileTreeRow
node={{
type: 'directory',
key: 'dir::unstaged::src',
path: 'src',
name: 'src',
depth: 0,
area: 'unstaged',
fileCount: 1,
children: []
}}
mode="uncommitted"
worktreePath="/repo/worktree"
sourceWorkspaceId={sourceWorkspaceId}
activeSectionKey={null}
sectionIndexByKey={new Map()}
isCollapsed={false}
onToggleDirectory={() => {}}
onNavigate={() => {}}
/>
)
})
return container
}
function dragRow(container: HTMLDivElement): DataTransfer {
const transfer = new DataTransfer()
const event = new Event('dragstart', { bubbles: true, cancelable: true })
Object.defineProperty(event, 'dataTransfer', { value: transfer })
act(() => {
container.querySelector('[draggable="true"]')?.dispatchEvent(event)
})
return transfer
}
describe('combined diff rows stamp their drag source', () => {
// Entry lists are snapshots; drag paths must use the workspace’s live owner, matching source-control rows.
it('stamps the live owner of the workspace the diff belongs to', () => {
testState.executionHostId = 'runtime:env-1'
expect(readWorkspaceFileDragSource(dragRow(renderRow('wt-1')))).toEqual({
executionHostId: 'runtime:env-1',
workspaceId: 'wt-1'
})
})
it('leaves the drag unstamped when the owner or the workspace is unknown', () => {
expect(readWorkspaceFileDragSource(dragRow(renderRow(undefined)))).toBeNull()
testState.executionHostId = 'runtime:unresolved-owner'
expect(readWorkspaceFileDragSource(dragRow(renderRow('wt-1')))).toBeNull()
})
})
@@ -1,85 +0,0 @@
// @vitest-environment happy-dom
import { cleanup, render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { afterEach, expect, it, vi } from 'vitest'
import { AiVaultShowMoreSessionsRow } from './AiVaultShowMoreSessionsRow'
afterEach(cleanup)
it('steps the history depth up by one page once the scan filled it', async () => {
const onSessionLimitChange = vi.fn()
render(
<AiVaultShowMoreSessionsRow
loaded={500}
loadedSessionLimit={500}
loading={false}
sessionLimit={500}
onSessionLimitChange={onSessionLimitChange}
/>
)
await userEvent.setup().click(screen.getByRole('button', { name: 'Show more sessions' }))
expect(onSessionLimitChange).toHaveBeenCalledWith(750)
})
it('stays put in a loading state while the deeper rescan runs', () => {
render(
<AiVaultShowMoreSessionsRow
loaded={250}
loadedSessionLimit={250}
loading
sessionLimit={500}
onSessionLimitChange={vi.fn()}
/>
)
const button = screen.getByRole('button', { name: 'Loading more sessions…' })
expect(button.hasAttribute('disabled')).toBe(true)
})
// Depth-minus-page is zero at the default, falsely claiming every foreground rescan loads deeper history.
it('stays hidden during a foreground rescan the scan had room for', () => {
render(
<AiVaultShowMoreSessionsRow
loaded={40}
loadedSessionLimit={250}
loading
sessionLimit={250}
onSessionLimitChange={vi.fn()}
/>
)
expect(screen.queryByRole('button')).toBeNull()
})
it('stays hidden while the scan has room or is already unlimited', () => {
render(
<AiVaultShowMoreSessionsRow
loaded={12}
loadedSessionLimit={250}
loading={false}
sessionLimit={250}
onSessionLimitChange={vi.fn()}
/>
)
render(
<AiVaultShowMoreSessionsRow
loaded={5000}
loadedSessionLimit="unlimited"
loading={false}
sessionLimit="unlimited"
onSessionLimitChange={vi.fn()}
/>
)
expect(screen.queryByRole('button')).toBeNull()
})
it('stays hidden until the first scan reports the depth it ran at', () => {
render(
<AiVaultShowMoreSessionsRow
loaded={0}
loadedSessionLimit={null}
loading
sessionLimit={250}
onSessionLimitChange={vi.fn()}
/>
)
expect(screen.queryByRole('button')).toBeNull()
})
@@ -1,86 +0,0 @@
/** @vitest-environment happy-dom */
import { act } from 'react'
import { runtimeTargetForOwnerEnvironment } from '@/runtime/runtime-client-target'
import { createRoot, type Root } from 'react-dom/client'
import { afterEach, beforeEach, expect, it, vi } from 'vitest'
import { clearHugeRepoWarningDismissalsForTests } from '@/lib/source-control-huge-repo-warning-dismissals'
import { useSourceControlStatusRefresh } from './use-status-refresh'
globalThis.IS_REACT_ACT_ENVIRONMENT = true
const actions = vi.hoisted(() => ({
setGitStatus: vi.fn(),
updateWorktreeGitIdentity: vi.fn(),
setUpstreamStatus: vi.fn(),
fetchUpstreamStatus: vi.fn()
}))
const warning = vi.hoisted(() => vi.fn())
vi.mock('@/store', () => ({
useAppStore: (select: (state: typeof actions) => unknown) => select(actions)
}))
vi.mock('sonner', () => ({ toast: { warning } }))
vi.mock('@/lib/connection-context', () => ({ getConnectionId: () => null }))
vi.mock('../../git-status-refresh', () => ({ refreshGitStatusForWorktree: vi.fn() }))
vi.mock('@/i18n/i18n', () => ({ translate: (_key: string, fallback: string) => fallback }))
const findHugeFoldersToIgnore = vi.fn()
const appendGitignore = vi.fn()
let root: Root | null = null
let container: HTMLDivElement | null = null
beforeEach(() => {
findHugeFoldersToIgnore.mockReset().mockResolvedValue(['dist'])
appendGitignore.mockReset().mockResolvedValue(true)
warning.mockReset()
vi.stubGlobal('window', {
...window,
api: { git: { findHugeFoldersToIgnore, appendGitignore } }
})
container = document.createElement('div')
root = createRoot(container)
})
afterEach(() => {
act(() => root?.unmount())
root = null
container = null
vi.unstubAllGlobals()
clearHugeRepoWarningDismissalsForTests()
})
async function renderOwner(
runtimeEnvironmentId: string | null,
connectionId: string | null = null
) {
function Probe() {
useSourceControlStatusRefresh({
activeRepoTarget: runtimeTargetForOwnerEnvironment(runtimeEnvironmentId),
activeWorktreeId: 'repo::/same/path',
worktreePath: '/same/path',
isFolder: false,
repositoryHuge: { limit: 1000 },
activeConnectionId: connectionId
})
return null
}
await act(async () => root?.render(<Probe />))
}
it('does not probe or offer a desktop ignore action for a managed owner without a legacy SSH ID', async () => {
await renderOwner('managed-owner')
expect(findHugeFoldersToIgnore).not.toHaveBeenCalled()
expect(warning).not.toHaveBeenCalled()
expect(appendGitignore).not.toHaveBeenCalled()
})
it('keeps the existing local suggestion', async () => {
await renderOwner(null)
expect(findHugeFoldersToIgnore).toHaveBeenCalledExactlyOnceWith({ worktreePath: '/same/path' })
expect(warning).toHaveBeenCalledTimes(1)
})
it('keeps direct SSH targets out of the desktop helper', async () => {
await renderOwner(null, 'ssh-owner')
expect(findHugeFoldersToIgnore).not.toHaveBeenCalled()
expect(warning).not.toHaveBeenCalled()
})
@@ -1,98 +0,0 @@
// @vitest-environment happy-dom
import { cleanup, render, screen } from '@testing-library/react'
import { afterEach, expect, it, vi } from 'vitest'
import { getDefaultSettings } from '../../../../shared/constants'
import { AppearanceThemeSlotSelect } from './AppearanceThemeControl'
const themes = vi.hoisted((): { app: unknown[]; terminal: unknown[] } => ({
app: [],
terminal: []
}))
vi.mock('@/store/plugin-themes', () => ({
usePluginThemes: () => themes.app,
usePluginTerminalThemes: () => themes.terminal
}))
afterEach(() => {
cleanup()
themes.app = []
themes.terminal = []
})
const dusk = {
id: 'plugin:samples.dusk/dusk',
pluginKey: 'samples.dusk',
revision: 'r1',
pluginName: 'Dusk',
label: 'Dusk',
base: 'dark',
tokens: { '--background': '#1b1430', '--foreground': '#e8e0ff' }
}
it('lists only themes of its mode, grouped by plugin, and offers the plugin terminal colors', async () => {
themes.app = [
dusk,
{ ...dusk, id: 'plugin:samples.dusk/dawn', label: 'Dawn', base: 'light' },
{
...dusk,
id: 'plugin:acme.night/night',
pluginKey: 'acme.night',
pluginName: 'Acme',
label: 'Night'
}
]
themes.terminal = [
{
...dusk,
mode: 'dark',
terminal: { background: '#1b1430', foreground: '#e8e0ff', red: '#f00' }
}
]
const updateSettings = vi.fn()
render(
<AppearanceThemeSlotSelect
base="dark"
label="Dark theme"
settings={{
...getDefaultSettings('/tmp'),
pluginAppThemeDark: 'plugin:samples.dusk/dusk',
terminalColorOverrides: { red: '#123456' }
}}
updateSettings={updateSettings}
/>
)
expect(screen.getByRole('combobox', { name: 'Dark theme' }).textContent).toContain('Dusk')
screen.getByRole('button', { name: 'Use its terminal colors' }).click()
expect(updateSettings).toHaveBeenCalledWith({
terminalThemeDark: 'plugin:samples.dusk/dusk',
terminalPluginPalettes: [
{
id: 'plugin:samples.dusk/dusk',
colors: { background: '#1b1430', foreground: '#e8e0ff', red: '#f00' }
}
]
})
// Overrides would otherwise mask the palette the user just chose.
expect(updateSettings.mock.calls[0]?.[0]).toHaveProperty('terminalColorOverrides', undefined)
screen.getByRole('combobox', { name: 'Dark theme' }).click()
expect(await screen.findByText('Acme')).toBeDefined()
expect(screen.queryByRole('option', { name: 'Dawn' })).toBeNull()
screen.getByRole('option', { name: 'Night' }).click()
expect(updateSettings).toHaveBeenLastCalledWith({ pluginAppThemeDark: 'plugin:acme.night/night' })
})
it('returns a slot to the built-in colors', async () => {
themes.app = [dusk]
const updateSettings = vi.fn()
render(
<AppearanceThemeSlotSelect
base="dark"
label="Dark theme"
settings={{ ...getDefaultSettings('/tmp'), pluginAppThemeDark: 'plugin:samples.dusk/dusk' }}
updateSettings={updateSettings}
/>
)
screen.getByRole('combobox', { name: 'Dark theme' }).click()
;(await screen.findByRole('option', { name: 'Built-in' })).click()
expect(updateSettings).toHaveBeenCalledWith({ pluginAppThemeDark: null })
})
@@ -1,85 +0,0 @@
// @vitest-environment happy-dom
import { cleanup, fireEvent, render, screen } from '@testing-library/react'
import { afterEach, describe, expect, it, vi } from 'vitest'
import { NumberField } from './SettingsFormControls'
afterEach(cleanup)
// Optional settings need a clear path back to automatic behavior (#10754).
describe('NumberField clearable fields', () => {
it('renders the placeholder and commits nothing while the value is unset', () => {
render(
<NumberField
label="Minimum Contrast Ratio"
description=""
value={undefined}
min={1}
max={21}
placeholder="Auto"
onChange={vi.fn()}
onClear={vi.fn()}
/>
)
const input = screen.getByLabelText('Minimum Contrast Ratio') as HTMLInputElement
expect(input.value).toBe('')
expect(input.getAttribute('placeholder')).toBe('Auto')
})
it('clears the setting when the field is emptied', () => {
const onChange = vi.fn()
const onClear = vi.fn()
render(
<NumberField
label="Minimum Contrast Ratio"
description=""
value={1}
min={1}
max={21}
placeholder="Auto"
onChange={onChange}
onClear={onClear}
/>
)
const input = screen.getByLabelText('Minimum Contrast Ratio')
fireEvent.change(input, { target: { value: '' } })
fireEvent.blur(input)
expect(onClear).toHaveBeenCalledTimes(1)
expect(onChange).not.toHaveBeenCalled()
})
it('still snaps back to the current value when the field is not clearable', () => {
const onChange = vi.fn()
render(<NumberField label="Font Size" description="" value={14} min={6} onChange={onChange} />)
const input = screen.getByLabelText('Font Size') as HTMLInputElement
fireEvent.change(input, { target: { value: '' } })
fireEvent.blur(input)
expect(onChange).not.toHaveBeenCalled()
expect(input.value).toBe('14')
})
it('clamps a committed value into the min/max window', () => {
const onChange = vi.fn()
render(
<NumberField
label="Minimum Contrast Ratio"
description=""
value={undefined}
min={1}
max={21}
onChange={onChange}
onClear={vi.fn()}
/>
)
const input = screen.getByLabelText('Minimum Contrast Ratio')
fireEvent.change(input, { target: { value: '99' } })
fireEvent.blur(input)
expect(onChange).toHaveBeenCalledWith(21)
})
})
@@ -1,84 +0,0 @@
// @vitest-environment happy-dom
import { act, cleanup, renderHook } from '@testing-library/react'
import { afterEach, describe, expect, it, vi } from 'vitest'
import { getDefaultSettings } from '../../../../shared/constants'
import {
useSettingsInteractionController,
type SettingsInteractionModel
} from './use-settings-interaction-controller'
import { useSettingsNavigationActions } from './settings-view-model'
afterEach(cleanup)
function createModel(shouldDiscard = false, dirty = true) {
return {
closeSettingsPage: vi.fn(),
confirm: vi.fn(async () => shouldDiscard),
hasUnsavedBranchPromptChanges: false,
hasUnsavedCommitPromptChanges: false,
hasUnsavedChatPromptChanges: dirty,
highlightedSettingsTargetId: null,
setFontSuggestions: vi.fn(),
setHasUnsavedBranchPromptChanges: vi.fn(),
setHasUnsavedCommitPromptChanges: vi.fn(),
setHasUnsavedChatPromptChanges: vi.fn(),
setHighlightedSettingsTargetId: vi.fn(),
setSettingsSearchQuery: vi.fn(),
setSourceControlAiPromptDiscardSignal: vi.fn(),
settings: getDefaultSettings('/tmp'),
updateSettingsOrThrow: vi.fn(async () => {}),
activeSectionId: 'chat',
setActiveSectionId: vi.fn(),
setPendingNavRequestTick: vi.fn(),
settingsSearchQuery: ''
} satisfies SettingsInteractionModel & Parameters<typeof useSettingsNavigationActions>[0]
}
function renderController(model: ReturnType<typeof createModel>) {
return renderHook(() => {
const interactions = useSettingsInteractionController(model)
const navigation = useSettingsNavigationActions(model, interactions)
return { ...interactions, ...navigation }
})
}
describe('Chat settings unsaved guard', () => {
it('asks before closing with a dirty chat recipe and keeps the page open when cancelled', async () => {
const model = createModel()
const { result } = renderController(model)
await act(async () => result.current.closeSettingsPageWithPromptGuard())
expect(model.confirm).toHaveBeenCalledWith(
expect.objectContaining({ title: 'Discard unsaved AI settings?' })
)
expect(model.closeSettingsPage).not.toHaveBeenCalled()
expect(model.setHasUnsavedChatPromptChanges).not.toHaveBeenCalled()
expect(model.setSourceControlAiPromptDiscardSignal).not.toHaveBeenCalled()
})
it('clears the chat draft through the existing discard signal before closing', async () => {
const model = createModel(true)
const { result } = renderController(model)
await act(async () => result.current.closeSettingsPageWithPromptGuard())
expect(model.confirm).toHaveBeenCalledOnce()
expect(model.setSourceControlAiPromptDiscardSignal).toHaveBeenCalledOnce()
expect(model.setHasUnsavedChatPromptChanges).toHaveBeenCalledWith(false)
expect(model.closeSettingsPage).toHaveBeenCalledOnce()
})
it('guards settings navigation with the same dirty chat recipe', async () => {
const model = createModel()
const { result } = renderController(model)
await act(async () => result.current.scrollToSection('git'))
expect(model.confirm).toHaveBeenCalledOnce()
expect(model.setActiveSectionId).not.toHaveBeenCalled()
expect(model.setSourceControlAiPromptDiscardSignal).not.toHaveBeenCalled()
})
it('closes without asking when the recipe is clean', async () => {
const model = createModel(false, false)
const { result } = renderController(model)
await act(async () => result.current.closeSettingsPageWithPromptGuard())
expect(model.confirm).not.toHaveBeenCalled()
expect(model.closeSettingsPage).toHaveBeenCalledOnce()
})
})
@@ -1,92 +0,0 @@
import { renderToStaticMarkup } from 'react-dom/server'
import type { ButtonHTMLAttributes, ReactNode } from 'react'
import { beforeEach, describe, expect, it, vi } from 'vitest'
const mocks = vi.hoisted(() => ({
state: {
activeModal: 'confirm-orca-yaml-hooks' as string | null,
modalData: {} as Record<string, unknown>,
closeModal: vi.fn(),
markOrcaHookScriptConfirmed: vi.fn(),
markOrcaHookRepoAlwaysTrusted: vi.fn()
}
}))
vi.mock('@/store', () => ({
useAppStore: Object.assign(
(selector: (state: typeof mocks.state) => unknown) => selector(mocks.state),
{
getState: () => mocks.state
}
)
}))
vi.mock('@/components/ui/dialog', () => ({
Dialog: ({ open, children }: { open: boolean; children: ReactNode }) =>
open ? <div>{children}</div> : null,
DialogContent: ({ children }: { children: ReactNode }) => <div>{children}</div>,
DialogDescription: ({ children }: { children: ReactNode }) => <div>{children}</div>,
DialogFooter: ({ children }: { children: ReactNode }) => <div>{children}</div>,
DialogHeader: ({ children }: { children: ReactNode }) => <div>{children}</div>,
DialogTitle: ({ children }: { children: ReactNode }) => <div>{children}</div>
}))
vi.mock('@/components/ui/button', () => ({
Button: ({
children,
...props
}: ButtonHTMLAttributes<HTMLButtonElement> & { children: ReactNode }) => (
<button {...props}>{children}</button>
)
}))
function decodeHtml(html: string): string {
return html
.replace(/&#x27;/g, "'")
.replace(/&quot;/g, '"')
.replace(/&amp;/g, '&')
.replace(/&lt;/g, '<')
.replace(/&gt;/g, '>')
}
describe('OrcaYamlTrustDialog', () => {
beforeEach(() => {
vi.clearAllMocks()
mocks.state.activeModal = 'confirm-orca-yaml-hooks'
mocks.state.modalData = {
repoId: 'repo-1',
repoName: 'orca',
scriptKind: 'setup',
scriptContent: 'node config/scripts/run-internal-dev-setup.mjs\npnpm install',
contentHash: 'hash-1',
previouslyApproved: false
}
})
it('keeps spaces around orca.yaml and the repo name in the first-run copy', async () => {
const { default: OrcaYamlTrustDialog } = await import('./OrcaYamlTrustDialog')
const text = decodeHtml(renderToStaticMarkup(<OrcaYamlTrustDialog />)).replace(/<[^>]+>/g, '')
expect(text).toContain("This repository's orca.yaml runs on your machine")
expect(text).toContain('Only run if you trust orca.')
expect(text).toContain('Always trust orca.yaml in orca')
expect(text).not.toContain("repository'sorca.yaml")
expect(text).not.toContain('trustorca')
expect(text).not.toContain('trustorca.yaml')
expect(text).not.toContain('inorca')
})
it('keeps spaces around orca.yaml when the script changed since last approval', async () => {
mocks.state.modalData = {
...mocks.state.modalData,
previouslyApproved: true
}
const { default: OrcaYamlTrustDialog } = await import('./OrcaYamlTrustDialog')
const text = decodeHtml(renderToStaticMarkup(<OrcaYamlTrustDialog />)).replace(/<[^>]+>/g, '')
expect(text).toContain('orca.yaml changed since you last approved')
expect(text).toContain('Always trust orca.yaml in orca')
expect(text).not.toContain('Always trustorca.yaml')
expect(text).not.toContain('inorca')
})
})
@@ -1,92 +0,0 @@
import { renderToStaticMarkup } from 'react-dom/server'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import type { Tab } from '../../../../shared/tab-types'
import type { WslMovedProject } from '../../../../shared/wsl-move/wsl-moved-project'
type MockState = {
wslMovedProjects: WslMovedProject[]
worktreesByRepo: Record<string, { id: string }[]>
folderWorkspaces: { id: string }[]
unifiedTabsByWorktree: Record<string, Partial<Tab>[]>
activeWorktreeId: string | null
activeGroupIdByWorktree: Record<string, string>
groupsByWorktree: Record<string, { id: string; activeTabId: string | null }[]>
}
let mockState: MockState
vi.mock('@/store', () => ({
useAppStore: (selector: (state: MockState) => unknown) => selector(mockState)
}))
vi.mock('@/lib/wsl-moved-chat-activation', () => ({ activateWslMovedWorkspace: vi.fn() }))
vi.mock('@/lib/wsl-moved-project-use-here', () => ({ bringWslMovedProjectBack: vi.fn() }))
vi.mock('@/components/confirmation-dialog-context', () => ({
useConfirmationDialog: () => vi.fn()
}))
const { default: WslMovedProjectsSection } = await import('./WslMovedProjectsSection')
const { TooltipProvider } = await import('@/components/ui/tooltip')
// The app shell provides the tooltip context the sidebar renders in.
const render = (): string =>
renderToStaticMarkup(
<TooltipProvider>
<WslMovedProjectsSection />
</TooltipProvider>
)
const MOVED = 'repo1::\\\\wsl.localhost\\Ubuntu\\home\\dev\\repo1'
const chat = (id: string, label: string): Partial<Tab> => ({
id,
entityId: id,
worktreeId: MOVED,
contentType: 'agent-session',
label,
customLabel: null
})
describe('WslMovedProjectsSection', () => {
beforeEach(() => {
mockState = {
wslMovedProjects: [
{ kind: 'repo', id: 'repo1', displayName: 'Repo 1', path: MOVED, distro: 'Ubuntu' }
],
worktreesByRepo: {},
folderWorkspaces: [],
unifiedTabsByWorktree: {
[MOVED]: [chat('agent-session:a', 'Fix the parser'), chat('agent-session:b', 'Plan')]
},
activeWorktreeId: MOVED,
activeGroupIdByWorktree: { [MOVED]: 'group-1' },
groupsByWorktree: { [MOVED]: [{ id: 'group-1', activeTabId: 'agent-session:b' }] }
}
})
it('lists the moved project read-only, labeled with its host, with only its chats', () => {
const markup = render()
expect(markup).toContain('Repo 1')
expect(markup).toContain('Moved to Ubuntu host')
expect(markup).toContain('Fix the parser')
// One button per chat plus "use here again": no new terminal, new chat, rename or menu.
expect(markup.match(/<button/g)).toHaveLength(3)
expect(markup.match(/data-testid="wsl-moved-chat"/g)).toHaveLength(2)
expect(markup.match(/data-testid="wsl-moved-use-here"/g)).toHaveLength(1)
expect(markup).toContain('aria-label="Use on this computer again"')
expect(markup).toContain('data-current="true"')
})
it('still lists a moved project without chats here, so it can be used here again', () => {
mockState.unifiedTabsByWorktree = {
[MOVED]: [{ ...chat('t', 'Shell'), contentType: 'terminal' }]
}
const markup = render()
expect(markup).toContain('Moved to Ubuntu host')
expect(markup.match(/data-testid="wsl-moved-chat"/g)).toBeNull()
expect(markup.match(/data-testid="wsl-moved-use-here"/g)).toHaveLength(1)
})
it('renders nothing when no project is moved', () => {
mockState.wslMovedProjects = []
expect(render()).toBe('')
})
})
@@ -1,86 +0,0 @@
// @vitest-environment happy-dom
import { describe, expect, it, vi } from 'vitest'
import { TerminalKittyKeyboardModeTracker } from '../../../../shared/terminal-kitty-keyboard-mode-tracker'
import { openStandaloneTerminal } from './open-standalone-terminal'
let emitData: (data: string) => void = () => {}
let signalUserInput: () => void = () => {}
vi.mock('@xterm/xterm', () => ({
Terminal: class {
open(): void {}
dispose(): void {}
write(_data: string, done?: () => void): void {
done?.()
}
onData(listener: (data: string) => void): { dispose: () => void } {
emitData = listener
return { dispose: () => {} }
}
}
}))
vi.mock('@xterm/xterm/css/xterm.css', () => ({}))
vi.mock('@/components/terminal-pane/terminal-user-input-signal', () => ({
subscribeToTerminalUserInput: (_terminal: unknown, listener: () => void) => {
signalUserInput = listener
return { dispose: () => {} }
}
}))
vi.mock('@/components/terminal-pane/terminal-native-copy-gutter', () => ({
installTerminalNativeCopyGutterTrim: () => ({ dispose: () => {} })
}))
vi.mock('../dashboard-popout/preview-terminal-compatibility', () => ({
installPreviewTerminalCompatibility: () => () => {}
}))
vi.mock('../dashboard-popout/preview-terminal-ime-bridge', () => ({
installPreviewImeBridge: () => ({ claimKeyEvent: () => false, dispose: () => {} })
}))
vi.mock('../dashboard-popout/preview-terminal-key-handler', () => ({
installPreviewTerminalKeyHandler: () => () => {}
}))
vi.mock('../dashboard-popout/preview-terminal-app-menu-clipboard', () => ({
installPreviewTerminalAppMenuClipboard: () => () => {}
}))
vi.mock('../dashboard-popout/preview-terminal-right-click-paste', () => ({
installPreviewTerminalRightClickPaste: () => () => {}
}))
function open(answersQueries: boolean): ReturnType<typeof vi.fn> {
const sendInput = vi.fn(() => true)
openStandaloneTerminal({
container: document.createElement('div'),
options: {},
kittyKeyboardModes: new TerminalKittyKeyboardModeTracker(),
sessionKey: 's',
sendInput,
answersQueries,
getSettings: () => null,
getMacOptionAsAlt: () => 'false',
getTerminalInput: () => null
})
return sendInput
}
const CURSOR_POSITION_REPLY = '\u001b[5;1R'
describe('standalone terminal input', () => {
it('forwards the user’s typing either way', () => {
const sendInput = open(false)
signalUserInput()
emitData('ls\r')
expect(sendInput).toHaveBeenCalledWith('ls\r')
})
it('leaves query replies to the real pane when it only mirrors one', () => {
const sendInput = open(false)
emitData(CURSOR_POSITION_REPLY)
expect(sendInput).not.toHaveBeenCalled()
})
it('answers queries when it is the session’s only emulator', () => {
const sendInput = open(true)
emitData(CURSOR_POSITION_REPLY)
expect(sendInput).toHaveBeenCalledWith(CURSOR_POSITION_REPLY)
})
})
@@ -1,92 +0,0 @@
import { renderToStaticMarkup } from 'react-dom/server'
import { describe, expect, it, vi } from 'vitest'
import {
CURSOR_MODELS_BUCKET_NAME,
CURSOR_OTHER_MODELS_BUCKET_NAME
} from '../../../../shared/cursor-usage-buckets'
import type { ProviderRateLimits, RateLimitWindow } from '../../../../shared/rate-limit-types'
vi.mock('@/i18n/i18n', () => ({
i18n: { language: 'en' },
translate: (_key: string, fallback: string, values?: Record<string, string>) => {
let result = fallback
for (const [key, value] of Object.entries(values ?? {})) {
result = result.replace(`{{${key}}}`, value)
}
return result
}
}))
vi.mock('@/lib/agent-catalog', () => ({
AgentIcon: () => null
}))
vi.mock('../../store', () => ({
useAppStore: (selector: (state: { usagePercentageDisplay: 'used' | 'remaining' }) => unknown) =>
selector({ usagePercentageDisplay: 'used' })
}))
function windowOf(usedPercent: number): RateLimitWindow {
return { usedPercent, windowMinutes: 43_200, resetsAt: null, resetDescription: null }
}
function cursorLimits(overrides: Partial<ProviderRateLimits> = {}): ProviderRateLimits {
return {
provider: 'cursor',
session: null,
weekly: null,
monthly: windowOf(92),
buckets: [
{ name: CURSOR_MODELS_BUCKET_NAME, ...windowOf(45) },
{ name: CURSOR_OTHER_MODELS_BUCKET_NAME, ...windowOf(50) }
],
updatedAt: Date.now(),
error: null,
status: 'ok',
...overrides
}
}
describe('Cursor status-bar segment', () => {
it('renders both plan pools instead of filtering them out as unknown buckets', async () => {
// Cursor’s whole-meter pools must bypass Gemini’s experimental-model allowlist.
const { ProviderSegment } = await import('./StatusBar')
const markup = renderToStaticMarkup(
<ProviderSegment p={cursorLimits()} compact={false} display="used" mode="verbose" />
)
expect(markup).toContain(CURSOR_MODELS_BUCKET_NAME)
expect(markup).toContain(CURSOR_OTHER_MODELS_BUCKET_NAME)
expect(markup).toContain('45%')
expect(markup).toContain('50%')
})
it('falls back to the plan total when no bucket is renderable', async () => {
const { ProviderSegment } = await import('./StatusBar')
const markup = renderToStaticMarkup(
<ProviderSegment
p={cursorLimits({ buckets: [{ name: 'Unknown pool', ...windowOf(70) }] })}
compact={false}
display="used"
mode="verbose"
/>
)
expect(markup).not.toContain('Unknown pool')
expect(markup).toContain('92%')
})
})
describe('getWindowSections for Cursor', () => {
it('keeps the plan total beside the pools', async () => {
// Cursor’s monthly plan total can be nearer the cap than individual buckets.
const { getWindowSections } = await import('./tooltip')
const sections = getWindowSections(cursorLimits())
expect(sections.find((section) => section.label === 'Plan')?.window?.usedPercent).toBe(92)
expect(sections.map((section) => section.label)).toContain(CURSOR_MODELS_BUCKET_NAME)
})
it('leaves other providers unchanged when they report no monthly window', async () => {
const { getWindowSections } = await import('./tooltip')
const sections = getWindowSections(cursorLimits({ monthly: null, provider: 'gemini' }))
expect(sections.some((section) => section.label === 'Plan')).toBe(false)
})
})
@@ -1,85 +0,0 @@
// @vitest-environment happy-dom
import '@testing-library/jest-dom/vitest'
import { afterEach, describe, expect, it, vi } from 'vitest'
import { cleanup, render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { TooltipProvider } from '@/components/ui/tooltip'
import { TaskPageJiraIssueList } from '../../task-page-jira-issue-list'
import { defaultJiraListColumnIds, visibleJiraListColumns } from '../../jira-list-columns'
import type { JiraIssue } from '../../../../../shared/jira/jira-types'
import { makeWorktree } from '@/store/slices/worktrees-slice-test-fixtures'
const issue: JiraIssue = {
id: '1',
key: 'APP-1',
siteId: 'site-a',
title: 'Fix login',
url: 'https://acme.atlassian.net/browse/APP-1',
project: { id: '1', key: 'APP', name: 'App' },
issueType: { id: '1', name: 'Bug' },
status: {
id: '1',
name: 'In Progress',
categoryKey: 'indeterminate',
categoryName: 'In Progress'
},
labels: [],
createdAt: '2026-10-01',
updatedAt: '2026-10-01'
}
const first = makeWorktree({ id: 'first', repoId: 'api', displayName: 'API fix' })
const second = makeWorktree({ id: 'second', repoId: 'web', displayName: 'UI fix' })
// The row receives a scoped lookup so the UI cannot accidentally match bare keys.
const onOpenWorkspace = vi.fn()
const onOpenIssue = vi.fn()
const onStartWorkspace = vi.fn()
function renderList(workspaces = [first, second]) {
const props = {
columns: visibleJiraListColumns(defaultJiraListColumnIds()),
issues: [issue],
selectedIssue: null,
showSiteContext: false,
statusOrder: null,
formatUpdatedAt: () => 'Today',
getStatusTone: () => '',
onOpenIssue,
onStartWorkspace,
getIssueWorkspaces: () => workspaces,
onOpenWorkspace
}
return render(
<TooltipProvider>
<TaskPageJiraIssueList {...props} />
</TooltipProvider>
)
}
afterEach(() => {
cleanup()
vi.clearAllMocks()
})
describe('Jira linked workspace actions', () => {
it('shows all linked workspaces and opens the selected one', async () => {
renderList()
const user = userEvent.setup()
await user.click(screen.getByRole('button', { name: '2 workspaces linked to APP-1' }))
await user.click(await screen.findByRole('menuitem', { name: /UI fix/ }))
expect(onOpenWorkspace).toHaveBeenCalledWith(second)
})
it('offers existing and new workspaces when exactly one is linked', async () => {
renderList([first])
const user = userEvent.setup()
await user.click(screen.getByRole('button', { name: '1 workspace linked to APP-1' }))
expect(onOpenWorkspace).not.toHaveBeenCalled()
expect(screen.getByRole('menuitem', { name: /API fix/ })).toBeInTheDocument()
await user.click(screen.getByRole('menuitem', { name: 'Start new workspace' }))
expect(onStartWorkspace).toHaveBeenCalledWith(issue)
expect(onOpenIssue).not.toHaveBeenCalled()
await user.click(screen.getByRole('button', { name: '1 workspace linked to APP-1' }))
await user.click(screen.getByRole('menuitem', { name: /API fix/ }))
expect(onOpenWorkspace).toHaveBeenCalledWith(first)
})
it('keeps the new-workspace action for an unlinked issue', () => {
renderList([])
expect(screen.getByRole('button', { name: 'Start workspace from APP-1' })).toBeInTheDocument()
})
})
@@ -1,92 +0,0 @@
import { describe, expect, it } from 'vitest'
import type { TaskPageRepoSourceState } from '@/components/task-page/task-page-cache-selectors'
import { hasDivergentSources, hasUpstreamCandidateDivergence } from './task-page-draft-storage'
function state(
sources: Partial<NonNullable<TaskPageRepoSourceState['sources']>>
): TaskPageRepoSourceState {
return {
repoId: 'repo-1',
repoPath: '/tmp/repo',
hostId: 'local',
sourceKey: 'repo-1',
sources: {
issues: null,
prs: null,
originCandidate: null,
upstreamCandidate: null,
...sources
},
error: null
}
}
describe('hasDivergentSources', () => {
it('is true when issues and PRs point at different owner/repos', () => {
expect(
hasDivergentSources(
state({
issues: { owner: 'acme', repo: 'fork', host: 'github.com' },
prs: { owner: 'acme', repo: 'orca', host: 'github.com' }
})
)
).toBe(true)
})
it('is false when the PR source is missing', () => {
expect(
hasDivergentSources(
state({
issues: { owner: 'acme', repo: 'fork', host: 'github.com' },
prs: null
})
)
).toBe(false)
})
it('is false when issues and PRs share an owner/repo', () => {
expect(
hasDivergentSources(
state({
issues: { owner: 'acme', repo: 'orca', host: 'github.com' },
prs: { owner: 'acme', repo: 'orca', host: 'github.com' }
})
)
).toBe(false)
})
})
describe('hasUpstreamCandidateDivergence', () => {
it('is true when origin and upstream candidates differ', () => {
expect(
hasUpstreamCandidateDivergence(
state({
originCandidate: { owner: 'me', repo: 'fork', host: 'github.com' },
upstreamCandidate: { owner: 'acme', repo: 'orca', host: 'github.com' }
})
)
).toBe(true)
})
it('is false when the upstream candidate is missing', () => {
expect(
hasUpstreamCandidateDivergence(
state({
originCandidate: { owner: 'me', repo: 'fork', host: 'github.com' },
upstreamCandidate: null
})
)
).toBe(false)
})
it('is false when origin and upstream candidates match', () => {
expect(
hasUpstreamCandidateDivergence(
state({
originCandidate: { owner: 'acme', repo: 'orca', host: 'github.com' },
upstreamCandidate: { owner: 'acme', repo: 'orca', host: 'github.com' }
})
)
).toBe(false)
})
})
@@ -1,86 +0,0 @@
import { beforeEach, expect, it, vi } from 'vitest'
import type { PaneManager } from '@/lib/pane-manager/pane-manager'
import type { PtyConnectionDeps } from './pty-connection-types'
import type { splitPaneInMain } from './terminal-pane-split-in-main'
import { runBootstrapSplitInMain } from './terminal-pane-bootstrap-split-in-main'
const mocks = vi.hoisted(() => ({
splitPaneInMain: vi.fn<typeof splitPaneInMain>(),
runTerminalPaneBootstrapSplits: vi.fn()
}))
vi.mock('./terminal-pane-split-in-main', async (importOriginal) => ({
...(await importOriginal<object>()),
splitPaneInMain: mocks.splitPaneInMain
}))
vi.mock('./terminal-pane-mount-bootstrap', () => ({
runTerminalPaneBootstrapSplits: mocks.runTerminalPaneBootstrapSplits
}))
const SOURCE = '11111111-1111-4111-8111-111111111111'
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the startup split reads only the target pane.
const manager = {
getActivePane: () => ({ id: 1, leafId: SOURCE, terminal: { cols: 120, rows: 30 } }),
getPanes: () => [{ id: 1, leafId: SOURCE, terminal: { cols: 120, rows: 30 } }]
} as unknown as PaneManager
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: passed through to today's split only.
const ptyDeps = {} as PtyConnectionDeps
const issue = { command: 'issue runner', env: { ISSUE: '42' } }
beforeEach(() => {
mocks.splitPaneInMain.mockReset()
mocks.runTerminalPaneBootstrapSplits.mockReset()
})
function run(): void {
runBootstrapSplitInMain({
manager,
worktreeId: 'wt-1',
tabId: 'tab-1',
ptyDeps,
issueCommandSplit: issue,
isActive: true
})
}
it('asks main to split the tab and start its startup command at half the source pane, keeping focus there', () => {
mocks.splitPaneInMain.mockReturnValue(true)
run()
expect(mocks.splitPaneInMain).toHaveBeenCalledWith(
{
worktreeId: 'wt-1',
tabId: 'tab-1',
sourceLeafId: SOURCE,
direction: 'vertical',
initialSize: { cols: 60, rows: 30 },
keepSourceFocused: true,
command: 'issue runner',
env: { ISSUE: '42' }
},
expect.any(Function)
)
expect(mocks.runTerminalPaneBootstrapSplits).not.toHaveBeenCalled()
})
it('splits as today where main does not own the tab', () => {
mocks.splitPaneInMain.mockReturnValue(false)
run()
expect(mocks.runTerminalPaneBootstrapSplits).toHaveBeenCalledWith({
manager,
ptyDeps,
issueCommandSplit: issue,
isActive: true
})
})
it('splits as today when main answers that a paired server holds the tab', () => {
mocks.splitPaneInMain.mockReturnValue(true)
run()
mocks.splitPaneInMain.mock.calls[0]![1]()
expect(mocks.runTerminalPaneBootstrapSplits).toHaveBeenCalledTimes(1)
})
@@ -1,86 +0,0 @@
// @vitest-environment happy-dom
import { act } from 'react'
import { createRoot, type Root } from 'react-dom/client'
import { afterEach, describe, expect, it, vi } from 'vitest'
import { TerminalSplitWorkspaceSurfaces } from './TerminalSplitWorkspaceSurfaces'
import type { StartupTerminalTabHold } from './terminal/startup-terminal-tab-hold'
import type { TerminalController } from './use-terminal-controller'
const mocks = vi.hoisted(() => ({
surfaceProps: new Map<string, ReadonlySet<string> | null>()
}))
vi.mock('./TerminalWorktreeSplitSurface', () => ({
WorktreeSplitSurface: (props: {
worktreeId: string
activationDeferredMountTabIds: ReadonlySet<string> | null
}) => {
mocks.surfaceProps.set(props.worktreeId, props.activationDeferredMountTabIds)
return null
}
}))
vi.mock('./browser-pane/host-guest/browser-guest-paint-retention', () => ({
useAnyBrowserGuestNeedsPaint: () => false
}))
Object.assign(globalThis, { IS_REACT_ACT_ENVIRONMENT: true })
const HELD_WORKTREE_ID = 'repo::/held'
const DEFERRED_WORKTREE_ID = 'repo::/deferred'
const OTHER_WORKTREE_ID = 'repo::/other'
const WORKTREE_IDS = [HELD_WORKTREE_ID, DEFERRED_WORKTREE_ID, OTHER_WORKTREE_ID]
let root: Root | undefined
afterEach(async () => {
await act(async () => root?.unmount())
root = undefined
mocks.surfaceProps.clear()
})
async function renderSurfaces(
hold: StartupTerminalTabHold | null,
deferred: Map<string, ReadonlySet<string>>
): Promise<void> {
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: TerminalSplitWorkspaceSurfaces reads only the fields listed here; the rest of the controller is machinery this render never touches.
const controller = {
activationDeferredMountTabIdsByWorktreeRef: { current: deferred },
activeGroupIdByWorktree: {},
activeView: 'terminal',
activityTerminalPortals: [],
anyMountedWorktreeHasLayout: true,
backgroundMountTabIdsByWorktreeRef: { current: new Map() },
effectiveActiveLayout: { type: 'leaf', groupId: 'group-1' },
effectiveParkedTerminalWorktreeIds: new Set(),
forceParkedTerminalWorktreeIds: new Set(),
getEffectiveLayoutForWorktree: () => ({ type: 'leaf', groupId: 'group-1' }),
measurableBackgroundWorktreeIdsRef: { current: new Set() },
mountedWorktreeIdsRef: { current: new Set(WORKTREE_IDS) },
renderedActiveWorktreeId: HELD_WORKTREE_ID,
startupTerminalTabHold: hold,
workspaceSurfaces: WORKTREE_IDS.map((id) => ({ id, path: `/${id}` }))
} as unknown as TerminalController
const container = document.createElement('div')
root = createRoot(container)
await act(async () => root?.render(<TerminalSplitWorkspaceSurfaces controller={controller} />))
}
describe('split workspace surfaces under a startup terminal hold', () => {
it('hands each surface its parked-equivalent tabs: deferral set, else held tabs, else none', async () => {
const hold: StartupTerminalTabHold = {
worktreeId: HELD_WORKTREE_ID,
heldTabIds: new Set(['held-tab'])
}
const deferredTabIds = new Set(['deferred-tab'])
await renderSurfaces(hold, new Map([[DEFERRED_WORKTREE_ID, deferredTabIds]]))
// Held tabs render no pane, so the surface must hand them to parked watchers.
expect(mocks.surfaceProps.get(HELD_WORKTREE_ID)).toBe(hold.heldTabIds)
expect(mocks.surfaceProps.get(DEFERRED_WORKTREE_ID)).toBe(deferredTabIds)
expect(mocks.surfaceProps.get(OTHER_WORKTREE_ID)).toBeNull()
})
it('passes no held tabs once the hold is released', async () => {
await renderSurfaces(null, new Map())
expect(mocks.surfaceProps.get(HELD_WORKTREE_ID)).toBeNull()
})
})
@@ -1,82 +0,0 @@
// @vitest-environment happy-dom
/** Collect browser webview IDs once per mount; eager useRef arguments previously walked every page/tab on every render. */
import { useState } from 'react'
import { act, cleanup, render } from '@testing-library/react'
import { afterEach, describe, expect, it, vi } from 'vitest'
const collectBrowserWebviewIdsCalls = vi.hoisted(() => ({ count: 0 }))
vi.mock('../store', () => {
const state = {
browserTabsByWorktree: {},
browserPagesByWorkspace: {},
openFiles: []
}
const useAppStore = Object.assign(() => undefined, {
getState: () => state,
subscribe: () => () => {}
})
return { useAppStore }
})
vi.mock('../store/slices/browser-webview-cleanup', () => ({
collectBrowserWebviewIds: (...args: unknown[]) => {
collectBrowserWebviewIdsCalls.count += 1
void args
return new Set<string>()
},
destroyWebviewsRemovedFromStore: vi.fn()
}))
vi.mock('@/lib/updater-beforeunload', () => ({
isIntentionalAppRestartInProgress: () => false
}))
vi.mock('@/lib/shutdown-checkpoint-guard', () => ({
preventUnloadAndScheduleShutdownCheckpointReset: vi.fn()
}))
vi.mock('./window-close-request-coordinator', () => ({
setWindowCloseRequestHandler: vi.fn()
}))
const { useTerminalWindowLifecycle } = await import('./use-terminal-window-lifecycle')
const controller = {
activeBrowserTabId: null,
activeTabType: 'terminal',
activeWorktreeBrowserTabIdsKey: '',
proceedToNativeWindowClose: () => {},
queueEditorCloseRequests: () => {},
renderedActiveWorktreeId: null,
setActiveBrowserTab: () => {},
setActiveTabType: () => {},
windowCloseAfterDirtyRef: { current: false }
} as unknown as Parameters<typeof useTerminalWindowLifecycle>[0]
let bumpRender: (() => void) | null = null
function Host(): null {
const [, setTick] = useState(0)
bumpRender = () => setTick((tick) => tick + 1)
useTerminalWindowLifecycle(controller)
return null
}
afterEach(() => {
cleanup()
collectBrowserWebviewIdsCalls.count = 0
bumpRender = null
})
describe('useTerminalWindowLifecycle browser-webview id seed', () => {
it('collects the id set once per mount, not once per render', () => {
render(<Host />)
expect(collectBrowserWebviewIdsCalls.count).toBe(1)
for (let i = 0; i < 20; i += 1) {
act(() => bumpRender?.())
}
expect(collectBrowserWebviewIdsCalls.count).toBe(1)
})
})
@@ -1,91 +0,0 @@
// @vitest-environment happy-dom
import { act, type ReactNode } from 'react'
import { createRoot, type Root } from 'react-dom/client'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { makeFacetCandidate } from './workspace-cleanup-facet.test.fixture'
import { WorkspaceCleanupConfirmStopAgents } from './workspace-cleanup-confirm-stop-agents'
import { getWorkspaceCleanupCandidateAccessibleName } from './workspace-cleanup-host-label'
vi.mock('@/components/ui/dialog', () => ({
DialogDescription: ({ children }: { children: ReactNode }) => <div>{children}</div>,
DialogFooter: ({ children }: { children: ReactNode }) => <div>{children}</div>,
DialogHeader: ({ children }: { children: ReactNode }) => <div>{children}</div>,
DialogTitle: ({ children }: { children: ReactNode }) => <div>{children}</div>
}))
vi.mock('@/components/ui/scroll-area', () => ({
ScrollArea: ({ children }: { children: ReactNode }) => <div>{children}</div>
}))
vi.mock('@/components/ui/tooltip', () => ({
Tooltip: ({ children }: { children: ReactNode }) => <>{children}</>,
TooltipContent: ({ children }: { children: ReactNode }) => <>{children}</>,
TooltipTrigger: ({ children }: { children: ReactNode }) => <>{children}</>
}))
let container: HTMLDivElement
let root: Root
describe('WorkspaceCleanupConfirmStopAgents', () => {
beforeEach(() => {
container = document.createElement('div')
document.body.appendChild(container)
root = createRoot(container)
})
afterEach(() => {
act(() => root.unmount())
document.body.replaceChildren()
})
it('names each workspace whose agent will be stopped and wires both buttons', () => {
const candidates = [
makeFacetCandidate({ worktreeId: 'repo-1::/b', displayName: 'b' }),
makeFacetCandidate({ worktreeId: 'repo-1::/c', displayName: 'c' })
]
const onBack = vi.fn()
const onConfirm = vi.fn()
act(() =>
root.render(
<WorkspaceCleanupConfirmStopAgents
candidates={candidates}
onBack={onBack}
onConfirm={onConfirm}
/>
)
)
expect(container.textContent).toContain('Stop these agents?')
expect(container.textContent).toContain("Deleting will stop these agents' current work.")
for (const candidate of candidates) {
expect(
container.querySelector(
`[aria-label="${getWorkspaceCleanupCandidateAccessibleName(candidate)}"]`
)
).not.toBeNull()
}
const buttons = [...container.querySelectorAll('button')]
act(() => buttons.find((button) => button.textContent === 'Back')?.click())
act(() => buttons.find((button) => button.textContent === 'Stop agents and delete')?.click())
expect(onBack).toHaveBeenCalledTimes(1)
expect(onConfirm).toHaveBeenCalledTimes(1)
})
it('reuses the close-terminal wording for a single agent', () => {
act(() =>
root.render(
<WorkspaceCleanupConfirmStopAgents
candidates={[makeFacetCandidate({ worktreeId: 'repo-1::/b', displayName: 'b' })]}
onBack={vi.fn()}
onConfirm={vi.fn()}
/>
)
)
expect(container.textContent).toContain('Stop this agent?')
expect(container.textContent).toContain("Deleting will stop the agent's current work.")
expect([...container.querySelectorAll('button')].map((button) => button.textContent)).toContain(
'Stop agent and delete'
)
})
})
@@ -1,82 +0,0 @@
import { describe, expect, it, vi } from 'vitest'
import { applyComposerNativeFileDrop } from './composer-native-file-drop'
function createDropArgs() {
return {
paths: ['/local/a.txt'],
isCurrentOwner: vi.fn(() => true),
uploadPaths: vi.fn(),
applyLocalPaths: vi.fn().mockResolvedValue(undefined),
addAttachments: vi.fn(),
insertFolderPaths: vi.fn(),
onError: vi.fn()
}
}
describe('applyComposerNativeFileDrop', () => {
it('reports upload owner failures without falling through to client-local paths', async () => {
const args = createDropArgs()
const error = new Error('Attachment upload host changed; retry the upload.')
args.uploadPaths.mockRejectedValue(error)
await applyComposerNativeFileDrop(args)
expect(args.onError).toHaveBeenCalledWith(error)
expect(args.applyLocalPaths).not.toHaveBeenCalled()
expect(args.addAttachments).not.toHaveBeenCalled()
})
it('does not surface a failure after the composer loses drop ownership', async () => {
const args = createDropArgs()
args.uploadPaths.mockRejectedValue(new Error('stale owner'))
args.isCurrentOwner.mockReturnValue(false)
await applyComposerNativeFileDrop(args)
expect(args.onError).not.toHaveBeenCalled()
expect(args.applyLocalPaths).not.toHaveBeenCalled()
})
it('applies local paths when no remote upload route is needed', async () => {
const args = createDropArgs()
args.uploadPaths.mockResolvedValue(null)
await applyComposerNativeFileDrop(args)
expect(args.applyLocalPaths).toHaveBeenCalledWith(args.paths, args.isCurrentOwner)
expect(args.addAttachments).not.toHaveBeenCalled()
expect(args.insertFolderPaths).not.toHaveBeenCalled()
expect(args.onError).not.toHaveBeenCalled()
})
it('applies uploaded files and folders only while the composer still owns the drop', async () => {
const args = createDropArgs()
args.uploadPaths.mockResolvedValue({
filePaths: ['/remote/a.txt'],
folderPaths: ['/remote/folder']
})
await applyComposerNativeFileDrop(args)
expect(args.addAttachments).toHaveBeenCalledWith(['/remote/a.txt'])
expect(args.insertFolderPaths).toHaveBeenCalledWith(['/remote/folder'])
expect(args.applyLocalPaths).not.toHaveBeenCalled()
expect(args.onError).not.toHaveBeenCalled()
})
it('discards uploaded paths after the composer loses drop ownership', async () => {
const args = createDropArgs()
args.uploadPaths.mockResolvedValue({
filePaths: ['/remote/a.txt'],
folderPaths: ['/remote/folder']
})
args.isCurrentOwner.mockReturnValue(false)
await applyComposerNativeFileDrop(args)
expect(args.addAttachments).not.toHaveBeenCalled()
expect(args.insertFolderPaths).not.toHaveBeenCalled()
expect(args.applyLocalPaths).not.toHaveBeenCalled()
expect(args.onError).not.toHaveBeenCalled()
})
})
@@ -1,86 +0,0 @@
// Layout-owner build: the sidebar's resume of a sleeping agent opens a new tab whose start main's
// create cannot carry yet (L252), so the window spawns it and its pane seeds the "session
// restored" banner from the tab's startup, as with the switch off.
import { beforeEach, describe, expect, it, vi } from 'vitest'
import type { SleepingAgentSessionRecord } from '../../../shared/agent-session-resume'
import { startupNeedsWindowSpawn } from '@/store/terminals/window-tab-start-in-main'
import { seedStartupSessionRestoredBanner } from '@/components/terminal-pane/session-restored-banner-pane-state'
import type { AppState } from '@/store/types'
type TabStartup = AppState['pendingStartupByTabId'][string]
const mockCreateTab = vi.fn((..._args: unknown[]) => ({ id: 'tab-1' }))
vi.mock('../../../shared/layout-owner-switch', () => ({ ORCA_LAYOUT_OWNER_IS_MAIN: true }))
vi.mock('@/store', () => ({
useAppStore: {
getState: () => ({
settings: { agentCmdOverrides: {}, agentDefaultArgs: {}, agentDefaultEnv: {} },
repos: [{ id: 'repo-1', connectionId: null, path: '/repo' }],
getKnownWorktreeById: () => ({ id: 'wt-1', repoId: 'repo-1', path: '/repo/wt' }),
sshConnectionStates: new Map(),
sshStateByEnvironment: new Map(),
runtimeStatusByEnvironmentId: new Map(),
agentStatusByPaneKey: {},
tabsByWorktree: { 'wt-1': [{ id: 'tab-1' }] },
openFiles: [],
browserTabsByWorktree: {},
tabBarOrderByWorktree: {},
createTab: mockCreateTab,
claimAutomaticAgentResume: vi.fn(),
clearSleepingAgentSession: vi.fn(),
setActiveTabType: vi.fn(),
setTabBarOrder: vi.fn()
})
}
}))
vi.mock('@/lib/new-workspace', () => ({ CLIENT_PLATFORM: 'darwin' }))
vi.mock('sonner', () => ({ toast: { message: vi.fn(), error: vi.fn() } }))
vi.mock('@/lib/telemetry', () => ({
track: vi.fn(),
tuiAgentToAgentKind: (agent: string) => agent
}))
vi.mock('@/components/tab-bar/reconcile-order', () => ({
reconcileTabOrder: vi.fn((_stored, termIds: string[]) => [...termIds])
}))
const record: SleepingAgentSessionRecord = {
paneKey: 'tab-0:leaf-0',
tabId: 'tab-0',
worktreeId: 'wt-1',
agent: 'claude',
providerSession: { key: 'session_id', id: 'claude-session-1' },
prompt: 'finish the task',
state: 'done',
origin: 'worktree-sleep',
capturedAt: 1,
updatedAt: 1
}
async function sidebarResumeStartup(): Promise<TabStartup | undefined> {
const { launchSleepingAgentSession } = await import('./sleeping-agent-session-launch')
expect(launchSleepingAgentSession(record)).toBe(true)
const options: unknown = mockCreateTab.mock.calls.at(-1)?.[3]
return typeof options === 'object' && options !== null && 'pendingStartup' in options
? // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: createTab's fourth argument carries the tab's pending startup.
(options.pendingStartup as TabStartup)
: undefined
}
describe("the sidebar's resume of a sleeping agent (layout-owner build)", () => {
beforeEach(() => {
mockCreateTab.mockClear()
})
it('keeps the window spawn, whose pane shows the session restored banner', async () => {
const startup = await sidebarResumeStartup()
expect(startup).toMatchObject({ resumeProviderSession: record.providerSession })
// Main's create cannot carry this start yet, so the window spawns it (L252).
expect(startupNeedsWindowSpawn(startup)).toBe(true)
const onShow = vi.fn()
seedStartupSessionRestoredBanner(startup, 7, onShow)
expect(onShow).toHaveBeenCalledExactlyOnceWith(7)
})
})
@@ -1,95 +0,0 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import type { GlobalSettings } from '../../../../shared/global-settings-types'
import type { Project } from '../../../../shared/project-types'
import type { Repo } from '../../../../shared/repo-types'
const { warningMock } = vi.hoisted(() => ({ warningMock: vi.fn() }))
vi.mock('sonner', () => ({ toast: { warning: warningMock } }))
vi.mock('@/i18n/i18n', () => ({
translate: (_key: string, fallback: string, vars?: Record<string, string>) =>
fallback.replace(/\{\{(\w+)\}\}/g, (_match, name: string) => vars?.[name] ?? '')
}))
import { warnIfProjectCrossesWslFilesystemBoundary } from './project-wsl-filesystem-boundary-advisory'
const WSL_DEFAULT = {
localWindowsRuntimeDefault: { kind: 'wsl', distro: 'Ubuntu-24.04' }
} as unknown as GlobalSettings
function makeRepo(overrides: Partial<Repo> = {}): Repo {
return { id: 'repo-1', path: 'C:\\Users\\alice\\orca', ...overrides } as Repo
}
function makeProject(overrides: Partial<Project> = {}): Project {
return { id: 'project-1', sourceRepoIds: ['repo-1'], ...overrides } as Project
}
describe('warnIfProjectCrossesWslFilesystemBoundary', () => {
beforeEach(() => {
warningMock.mockReset()
})
it('warns and names the distro when a Windows-drive project runs WSL git', () => {
warnIfProjectCrossesWslFilesystemBoundary(makeRepo(), [makeProject()], WSL_DEFAULT)
expect(warningMock).toHaveBeenCalledTimes(1)
expect(warningMock.mock.calls[0]?.[1]?.description).toContain('Ubuntu-24.04')
})
// The project override is what actually routes git, so it must beat the global default here too.
it('respects a project override that pins the project back to the Windows host', () => {
warnIfProjectCrossesWslFilesystemBoundary(
makeRepo(),
[makeProject({ localWindowsRuntimePreference: { kind: 'windows-host' } })],
WSL_DEFAULT
)
expect(warningMock).not.toHaveBeenCalled()
})
it('warns from a project override even when the global default is the Windows host', () => {
warnIfProjectCrossesWslFilesystemBoundary(
makeRepo(),
[makeProject({ localWindowsRuntimePreference: { kind: 'wsl', distro: 'Debian' } })],
{ localWindowsRuntimeDefault: { kind: 'windows-host' } } as unknown as GlobalSettings
)
expect(warningMock.mock.calls[0]?.[1]?.description).toContain('Debian')
})
// Windows runtime preference is local-only; Windows-shaped SSH paths still belong to another filesystem.
it('stays silent for a repo on a remote execution host', () => {
warnIfProjectCrossesWslFilesystemBoundary(
makeRepo({ connectionId: 'ssh-1' }),
[makeProject()],
WSL_DEFAULT
)
expect(warningMock).not.toHaveBeenCalled()
})
it('stays silent when the WSL default has no distro to repair to', () => {
warnIfProjectCrossesWslFilesystemBoundary(makeRepo(), [makeProject()], {
localWindowsRuntimeDefault: { kind: 'wsl', distro: null }
} as unknown as GlobalSettings)
expect(warningMock).not.toHaveBeenCalled()
})
it('never throws when the advisory itself fails', () => {
const repo = makeRepo()
Object.defineProperty(repo, 'path', {
get: () => {
throw new Error('unreadable path')
}
})
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
expect(() =>
warnIfProjectCrossesWslFilesystemBoundary(repo, [makeProject()], WSL_DEFAULT)
).not.toThrow()
expect(warningMock).not.toHaveBeenCalled()
warnSpy.mockRestore()
})
})
@@ -1,82 +0,0 @@
import { describe, expect, it, vi } from 'vitest'
import { toRuntimeExecutionHostId, type ExecutionHostId } from '../../../../shared/execution-host'
import type { FolderWorkspace } from '../../../../shared/folder-workspace-types'
import type { ProjectGroup } from '../../../../shared/project-group-types'
vi.mock('sonner', () => ({
toast: { info: vi.fn(), success: vi.fn(), error: vi.fn(), warning: vi.fn() }
}))
vi.mock('@/components/terminal-pane/pty-dispatcher', () => ({
restorePtyDataHandlersAfterFailedShutdown: vi.fn(),
unregisterPtyDataHandlers: vi.fn()
}))
// @ts-expect-error -- minimal window.api stub for the store under test
globalThis.window = { api: {} }
const { createTestStore } = await import('./store-test-helpers')
function group(id: string, executionHostId: string): ProjectGroup {
return {
id,
name: 'root/repo',
parentPath: '/srv',
executionHostId,
parentGroupId: null,
createdFrom: 'manual',
tabOrder: 0,
isCollapsed: false,
color: null,
createdAt: 1,
updatedAt: 1
}
}
function folder(id: string, executionHostId: ExecutionHostId): FolderWorkspace {
return {
id,
projectGroupId: 'group',
name: 'folder',
folderPath: '/srv/folder',
executionHostId,
linkedTask: null,
comment: '',
isArchived: false,
isUnread: false,
isPinned: false,
sortOrder: 0,
lastActivityAt: 1,
createdAt: 1,
updatedAt: 1
}
}
describe('purging a removed runtime’s project groups', () => {
it('drops the removed server’s groups and folder rows, and keeps every other host’s', () => {
const store = createTestStore()
const kept = [group('local-group', 'local'), group('other', toRuntimeExecutionHostId('env-b'))]
store.setState({
projectGroups: [...kept, group('stopped', toRuntimeExecutionHostId('env-a'))],
folderWorkspaces: [
folder('local-folder', 'local'),
folder('stopped-folder', toRuntimeExecutionHostId('env-a'))
]
})
store.getState().purgeStaleRuntimeHostState(['env-a'])
expect(store.getState().projectGroups.map((entry) => entry.id)).toEqual([
'local-group',
'other'
])
expect(store.getState().folderWorkspaces.map((entry) => entry.id)).toEqual(['local-folder'])
})
it('leaves the state untouched when no group belongs to the removed server', () => {
const store = createTestStore()
const projectGroups = [group('local-group', 'local')]
store.setState({ projectGroups, folderWorkspaces: [] })
store.getState().purgeStaleRuntimeHostState(['env-a'])
expect(store.getState().projectGroups).toBe(projectGroups)
})
})
@@ -1,86 +0,0 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { create } from 'zustand'
import { createCompatibleRuntimeStatusResponse } from '../../runtime/runtime-compatibility-test-fixture'
import { resetRestoredBrowserClientHostAttachForTests } from '@/runtime/restored-client-hosted-browser-host-attach'
import { replayClientHostedBrowserCloseIntents } from '@/runtime/client-hosted-browser-close-intent-replay'
import {
clearRuntimeEnvironmentConnectionGenerationsForTests,
createRuntimeStatusSlice,
type RuntimeStatusSlice
} from './runtime-status'
import { stubRuntimeEnvironmentBridge } from '../../runtime/runtime-environment-bridge-fixture'
vi.mock('sonner', () => ({
toast: { warning: vi.fn(), dismiss: vi.fn() }
}))
vi.mock('@/runtime/client-hosted-browser-close-intent-replay', () => ({
replayClientHostedBrowserCloseIntents: vi.fn(async () => {})
}))
const prepareBrowserClientHostPlacement = vi.fn(async (_args: { selector: string }) => ({
kind: 'server' as const
}))
function stubApi(getStatus: ReturnType<typeof vi.fn>): void {
stubRuntimeEnvironmentBridge({
getStatus,
list: vi.fn(),
prepareBrowserClientHostPlacement
})
}
/** A status slice whose state also carries the browser handles the attach step reads. */
function storeWithRestoredHandles(clientHosted: boolean) {
return create<RuntimeStatusSlice & { remoteBrowserPageHandlesByPageId: unknown }>()((...a) => ({
...createRuntimeStatusSlice(...(a as unknown as Parameters<typeof createRuntimeStatusSlice>)),
remoteBrowserPageHandlesByPageId: {
'page-1': {
environmentId: 'env-a',
remotePageId: 'page-1',
restoredFromSession: true,
...(clientHosted ? { restoredClientHosted: true } : {})
}
}
}))
}
describe('restored client-hosted browser host attach on reachability', () => {
beforeEach(() => {
clearRuntimeEnvironmentConnectionGenerationsForTests()
resetRestoredBrowserClientHostAttachForTests()
prepareBrowserClientHostPlacement.mockClear()
vi.mocked(replayClientHostedBrowserCloseIntents).mockClear()
})
afterEach(() => {
vi.unstubAllGlobals()
})
it('runs both recovery follow-ups after a successful refresh', async () => {
stubApi(vi.fn().mockResolvedValue(createCompatibleRuntimeStatusResponse('runtime-a')))
await storeWithRestoredHandles(true).getState().refreshRuntimeEnvironmentStatus('env-a')
expect(prepareBrowserClientHostPlacement).toHaveBeenCalledWith({
selector: 'env-a',
preference: 'auto'
})
expect(replayClientHostedBrowserCloseIntents).toHaveBeenCalledWith('env-a', expect.anything())
})
it('runs no recovery follow-ups when the environment is unreachable', async () => {
stubApi(vi.fn().mockRejectedValue(new Error('unreachable')))
await storeWithRestoredHandles(true).getState().refreshRuntimeEnvironmentStatus('env-a')
expect(prepareBrowserClientHostPlacement).not.toHaveBeenCalled()
expect(replayClientHostedBrowserCloseIntents).not.toHaveBeenCalled()
})
it('starts no browser client host for restored pages the server hosts', async () => {
stubApi(vi.fn().mockResolvedValue(createCompatibleRuntimeStatusResponse('runtime-a')))
await storeWithRestoredHandles(false).getState().refreshRuntimeEnvironmentStatus('env-a')
expect(prepareBrowserClientHostPlacement).not.toHaveBeenCalled()
})
})
@@ -1,86 +0,0 @@
// host_not_owned routes twin-workspace promotion to the window copy, matching window-owned preview behavior.
import { beforeEach, describe, expect, it, vi } from 'vitest'
import type { Tab } from '../../../../../shared/tab-types'
import { ORCA_LAYOUT_OWNER_IS_MAIN } from '../../../../../shared/layout-owner-switch'
import type { AppState } from '../../types'
import type { LayoutCommandResult } from '../../../../../shared/workspace-layout/workspace-layout-command-types'
type Answer = 'asToday' | 'apply' | 'refused'
const answer = vi.hoisted((): { value: Answer } => ({ value: 'asToday' }))
vi.mock('@/lib/resolve-owner', () => ({ runtimeEnvironmentIdUnlessTwin: () => null }))
vi.mock('@/runtime/layout-apply-send', () => ({
sendLayoutApply: (
_state: unknown,
_worktreeId: string,
_command: unknown,
handlers: {
apply: (result: LayoutCommandResult) => void
asToday: () => void
refused?: () => void
}
) => {
if (answer.value === 'asToday') {
handlers.asToday()
} else if (answer.value === 'apply') {
handlers.apply({})
} else {
handlers.refused?.()
}
return true
}
}))
const { promotePreviewTabsThroughLayoutOwner } = await import('./preview-tab-promotion')
const WT = 'repo::/twin'
function previewTab(id: string): Tab {
return {
id,
entityId: `/twin/${id}.ts`,
groupId: 'g1',
worktreeId: WT,
contentType: 'editor',
label: `${id}.ts`,
customLabel: null,
color: null,
sortOrder: 0,
createdAt: 1,
isPreview: true
}
}
function promote(id: string): Tab {
let tabs: AppState['unifiedTabsByWorktree'] = { [WT]: [previewTab(id)] }
const set = (
update: (
state: Pick<AppState, 'unifiedTabsByWorktree'>
) => Partial<Pick<AppState, 'unifiedTabsByWorktree'>>
): void => {
tabs = update({ unifiedTabsByWorktree: tabs }).unifiedTabsByWorktree ?? tabs
}
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the mocked sender and owner lookup read nothing from the state.
promotePreviewTabsThroughLayoutOwner({} as AppState, set, [{ id, worktreeId: WT }])
return tabs[WT]![0]!
}
describe.runIf(ORCA_LAYOUT_OWNER_IS_MAIN)(
'preview promotion of a twin tab, layout-owner build',
() => {
beforeEach(() => {
answer.value = 'asToday'
})
it('ends the preview here when main routes the tab to a paired server', () => {
expect(promote('routed').isPreview).toBeUndefined()
})
it("leaves the flag to main's frame when main promotes it, and keeps it on a refusal", () => {
answer.value = 'apply'
expect(promote('applied').isPreview).toBe(true)
answer.value = 'refused'
expect(promote('refused').isPreview).toBe(true)
})
}
)