diff --git a/config/scripts/ci-shard-timings.json b/config/scripts/ci-shard-timings.json index 724f3023bca..5f0df4fb03e 100644 --- a/config/scripts/ci-shard-timings.json +++ b/config/scripts/ci-shard-timings.json @@ -2330,7 +2330,6 @@ "src/main/emulator/scrcpy-video-registry.test.ts": 57, "src/main/emulator/serve-sim-accessibility-tree.test.ts": 62, "src/main/emulator/serve-sim-ax-normalization.test.ts": 35, - "src/main/emulator/serve-sim-detached-session.test.ts": 30, "src/main/emulator/serve-sim-execution.test.ts": 64, "src/main/emulator/serve-sim-helper-processes.test.ts": 59, "src/main/emulator/serve-sim-kill-contract.test.ts": 1307, @@ -2404,7 +2403,6 @@ "src/main/git/command-runner/wsl-host-failure.test.ts": 120, "src/main/git/command-runner/wsl-service-failure-direct-git.test.ts": 186, "src/main/git/commit-object-ref.test.ts": 25, - "src/main/git/commit.test.ts": 178, "src/main/git/create-event-repo-probe.test.ts": 59, "src/main/git/exact-ref-probe.test.ts": 60, "src/main/git/fetch-error-classification.test.ts": 51, @@ -2576,7 +2574,6 @@ "src/main/gitea/client-scan-invalidation.test.ts": 485, "src/main/gitea/client.test.ts": 385, "src/main/gitea/pull-request-creation.test.ts": 293, - "src/main/gitea/pull-request-mappers.test.ts": 29, "src/main/gitea/repository-ref.test.ts": 436, "src/main/github/account-binding-required-host.test.ts": 220, "src/main/github/auth-diagnose.test.ts": 58, @@ -5020,7 +5017,6 @@ "src/main/runtime/orchestration/chat-assignee.test.ts": 2933, "src/main/runtime/orchestration/chat-worker-legacy-failure-read.test.ts": 1651, "src/main/runtime/orchestration/chat-worker-request-failures.test.ts": 1678, - "src/main/runtime/orchestration/cli-command.test.ts": 57, "src/main/runtime/orchestration/codex-reconnect-failed-dispatch.test.ts": 6144, "src/main/runtime/orchestration/configured-worker-agent-selector.test.ts": 32, "src/main/runtime/orchestration/coordinator-decision-gates.test.ts": 361, @@ -5572,7 +5568,6 @@ "src/main/runtime/rpc/web-session-pairing.test.ts": 590, "src/main/runtime/rpc/workspace-creator-context.test.ts": 32, "src/main/runtime/rpc/worktree-catalog-snapshot.test.ts": 52, - "src/main/runtime/rpc/worktree-ps-other-client-marks.test.ts": 92, "src/main/runtime/rpc/worktree-removal-marker-projection.test.ts": 348, "src/main/runtime/rpc/ws-empty-payload-frame-write.test.ts": 95, "src/main/runtime/rpc/ws-transport-accept-order.test.ts": 67, @@ -7364,7 +7359,6 @@ "src/renderer/src/components/editor/editor-model-uri-parity.test.ts": 154, "src/renderer/src/components/editor/editor-model-uri.test.ts": 43, "src/renderer/src/components/editor/editor-panel-draft-selector.test.ts": 322, - "src/renderer/src/components/editor/editor-panel-git-entry-selector.test.ts": 44, "src/renderer/src/components/editor/editor-panel-render-model.test.ts": 2054, "src/renderer/src/components/editor/editor-path-display.test.ts": 34, "src/renderer/src/components/editor/editor-path-move-inflight.test.ts": 49, @@ -7949,7 +7943,6 @@ "src/renderer/src/components/native-chat/native-chat-working-suppression.test.ts": 42, "src/renderer/src/components/native-chat/omp-model-switch-capability.test.ts": 266, "src/renderer/src/components/native-chat/serialized-screen-background-rows.test.ts": 112, - "src/renderer/src/components/native-chat/structured-agent-session-delivery-notices.kept-card.test.ts": 427, "src/renderer/src/components/native-chat/structured-agent-session-delivery-notices.test.ts": 234, "src/renderer/src/components/native-chat/structured-agent-session-legacy-outbox.test.ts": 383, "src/renderer/src/components/native-chat/structured-agent-session-message-hand-back.test.ts": 377, @@ -8212,7 +8205,6 @@ "src/renderer/src/components/right-sidebar/ai-vault-search-hit-hosts.test.ts": 45, "src/renderer/src/components/right-sidebar/ai-vault-search-scope-identity.test.tsx": 479, "src/renderer/src/components/right-sidebar/ai-vault-search-session.test.ts": 88, - "src/renderer/src/components/right-sidebar/ai-vault-session-chat-action.test.ts": 1625, "src/renderer/src/components/right-sidebar/ai-vault-session-cli-fork.test.ts": 1630, "src/renderer/src/components/right-sidebar/ai-vault-session-conditional-refresh-hook.test.tsx": 1736, "src/renderer/src/components/right-sidebar/ai-vault-session-conditional-refresh.test.ts": 133, @@ -8324,7 +8316,6 @@ "src/renderer/src/components/right-sidebar/pr-comments-ai-launch-ack.test.ts": 75, "src/renderer/src/components/right-sidebar/pr-comments-list-selection.test.tsx": 1815, "src/renderer/src/components/right-sidebar/push-target-upstream-refresh-cache.test.ts": 184, - "src/renderer/src/components/right-sidebar/review-cache-entry-selection.test.ts": 24, "src/renderer/src/components/right-sidebar/right-panel-comment-composer.ime-enter.test.tsx": 273, "src/renderer/src/components/right-sidebar/right-panel-comment-focus-timers.test.ts": 82, "src/renderer/src/components/right-sidebar/right-sidebar-effective-tab.test.ts": 34, @@ -8332,7 +8323,6 @@ "src/renderer/src/components/right-sidebar/right-sidebar-width.test.ts": 61, "src/renderer/src/components/right-sidebar/runSourceControlAgentActionStart.test.ts": 276, "src/renderer/src/components/right-sidebar/search-match-open.test.ts": 100, - "src/renderer/src/components/right-sidebar/search-rows.test.ts": 35, "src/renderer/src/components/right-sidebar/source-control-action-recipe-match.test.ts": 190, "src/renderer/src/components/right-sidebar/source-control-active-open-file-keys.test.ts": 28, "src/renderer/src/components/right-sidebar/source-control-branch-context-row.test.tsx": 248, @@ -9824,7 +9814,6 @@ "src/renderer/src/components/terminal-pane/use-terminal-scroll-visibility-memory.test.ts": 56, "src/renderer/src/components/terminal-pane/use-terminal-tab-cold-parking.test.ts": 461, "src/renderer/src/components/terminal-pane/use-terminal-window-wake-recovery.test.ts": 333, - "src/renderer/src/components/terminal-pane/use-wsl-host-unavailable-reason.test.ts": 1271, "src/renderer/src/components/terminal-pane/useTerminalFontZoom.test.ts": 197, "src/renderer/src/components/terminal-pane/wrapped-terminal-http-link-limits.test.ts": 166, "src/renderer/src/components/terminal-pane/wrapped-terminal-http-link-provider.test.ts": 396, @@ -11352,7 +11341,6 @@ "src/renderer/src/store/slices/linear.test.ts": 388, "src/renderer/src/store/slices/local-detected-agent-state.test.ts": 191, "src/renderer/src/store/slices/native-chat-launch-draft-teardown.test.ts": 1318, - "src/renderer/src/store/slices/new-issue-draft.test.ts": 34, "src/renderer/src/store/slices/new-markdown.test.ts": 1089, "src/renderer/src/store/slices/orca-profiles-auth-actions-connect-overlap.test.ts": 1268, "src/renderer/src/store/slices/orca-profiles-auth-actions-sign-in-wait.test.ts": 1130, @@ -11478,7 +11466,6 @@ "src/renderer/src/store/slices/tabs/tabs-reconciliation-batch-identity.test.ts": 1309, "src/renderer/src/store/slices/tabs/tabs-reconciliation-empty-groups.test.ts": 1204, "src/renderer/src/store/slices/tabs/tabs-root-group.test.ts": 1263, - "src/renderer/src/store/slices/task-creation-drafts.test.ts": 50, "src/renderer/src/store/slices/terminal-helpers.test.ts": 68, "src/renderer/src/store/slices/terminal-input-activity-store-write.test.ts": 1443, "src/renderer/src/store/slices/terminal-layout-pty-ownership.test.ts": 1313, @@ -11664,7 +11651,6 @@ "src/renderer/src/web/web-preload-api-runtime-calls.test.ts": 838, "src/renderer/src/web/web-preload-api-runtime-environment.test.ts": 1240, "src/renderer/src/web/web-preload-api-settings.test.ts": 2080, - "src/renderer/src/web/web-preload-api-ssh.test.ts": 682, "src/renderer/src/web/web-preload-api-ui-pairing-local-fields.test.ts": 488, "src/renderer/src/web/web-preload-api-ui.test.ts": 1703, "src/renderer/src/web/web-preload-api-workspace-catalog.test.ts": 1393, diff --git a/src/main/browser/agent-browser-log-entries.test.ts b/src/main/browser/agent-browser-log-entries.test.ts deleted file mode 100644 index d8557bfa162..00000000000 --- a/src/main/browser/agent-browser-log-entries.test.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { toBrowserConsoleResult, toBrowserNetworkLogResult } from './agent-browser-log-entries' - -// Trimmed from real agent-browser 0.27.3 and 0.41.0 `--json` data; both print the same fields. -const consoleOutput = { - messages: [ - { args: [{ type: 'string', value: 'hello log' }], text: 'hello log', type: 'log' }, - { args: [{ type: 'string', value: 'a warning' }], text: 'a warning', type: 'warning' }, - { args: [{ type: 'string', value: 'an error' }], text: 'an error', type: 'error' } - ] -} - -const networkOutput = { - requests: [ - { - headers: { 'Accept-Language': 'en-US,en;q=0.9' }, - method: 'GET', - mimeType: 'text/html', - requestId: 'BECA21D6BA0B635E9916700B48479394', - resourceType: 'Document', - responseHeaders: { 'Content-Length': '254' }, - status: 200, - timestamp: 1793283228914, - url: 'http://127.0.0.1:47811/page.html' - }, - { - headers: { 'Accept-Language': 'en-US,en;q=0.9' }, - method: 'GET', - requestId: '45376.2', - resourceType: 'Fetch', - timestamp: 1793283228914, - url: 'https://example.com/?a=1' - } - ] -} - -describe('toBrowserConsoleResult', () => { - it('maps messages to entries and keeps the newest up to the limit', () => { - expect(toBrowserConsoleResult(consoleOutput, undefined)).toEqual({ - entries: [ - { level: 'log', text: 'hello log' }, - { level: 'warning', text: 'a warning' }, - { level: 'error', text: 'an error' } - ], - truncated: false - }) - expect(toBrowserConsoleResult(consoleOutput, 2)).toEqual({ - entries: [ - { level: 'warning', text: 'a warning' }, - { level: 'error', text: 'an error' } - ], - truncated: true - }) - }) -}) - -describe('toBrowserNetworkLogResult', () => { - it('maps requests to entries, including ones without a response, up to the limit', () => { - expect(toBrowserNetworkLogResult(networkOutput, undefined)).toEqual({ - entries: [ - { - url: 'http://127.0.0.1:47811/page.html', - method: 'GET', - status: 200, - mimeType: 'text/html', - timestamp: 1793283228914 - }, - { url: 'https://example.com/?a=1', method: 'GET', timestamp: 1793283228914 } - ], - truncated: false - }) - expect(toBrowserNetworkLogResult(networkOutput, 1).entries).toEqual([ - { url: 'https://example.com/?a=1', method: 'GET', timestamp: 1793283228914 } - ]) - }) -}) diff --git a/src/main/emulator/serve-sim-detached-session.test.ts b/src/main/emulator/serve-sim-detached-session.test.ts deleted file mode 100644 index 264489e3848..00000000000 --- a/src/main/emulator/serve-sim-detached-session.test.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { - deriveAxUrlFromStreamUrl, - parseServeSimDetachedSession -} from './serve-sim-detached-session' - -describe('parseServeSimDetachedSession', () => { - it('uses serve-sim streamUrl when present', () => { - const info = parseServeSimDetachedSession( - { - device: 'device-1', - streamUrl: 'http://127.0.0.1:3100/stream.mjpeg', - wsUrl: 'ws://127.0.0.1:3100/ws' - }, - 'device-1' - ) - - expect(info).toMatchObject({ - deviceUdid: 'device-1', - streamUrl: 'http://127.0.0.1:3100/stream.mjpeg', - wsUrl: 'ws://127.0.0.1:3100/ws', - axUrl: 'http://127.0.0.1:3100/ax' - }) - }) - - it('derives the device-scoped AX endpoint and preserves an explicit one', () => { - const derived = parseServeSimDetachedSession( - { - streamUrl: 'http://127.0.0.1:3200/helper/device-1/stream.mjpeg', - wsUrl: 'ws://127.0.0.1:3200/helper/device-1/ws' - }, - 'device-1' - ) - const explicit = parseServeSimDetachedSession( - { - streamUrl: 'http://127.0.0.1:3200/stream.mjpeg', - wsUrl: 'ws://127.0.0.1:3200/ws', - axUrl: 'http://127.0.0.1:3200/custom-ax' - }, - 'device-1' - ) - - expect(derived.axUrl).toBe('http://127.0.0.1:3200/helper/device-1/ax') - expect(explicit.axUrl).toBe('http://127.0.0.1:3200/custom-ax') - }) - - it('derives the MJPEG stream endpoint from older serve-sim url output', () => { - const info = parseServeSimDetachedSession( - { - device: 'device-2', - url: 'http://127.0.0.1:3100', - wsUrl: 'ws://127.0.0.1:3100/ws' - }, - 'device-2' - ) - - expect(info.streamUrl).toBe('http://127.0.0.1:3100/stream.mjpeg') - }) -}) - -describe('deriveAxUrlFromStreamUrl', () => { - it('swaps the mjpeg stream suffix for /ax', () => { - expect(deriveAxUrlFromStreamUrl('http://127.0.0.1:3100/stream.mjpeg')).toBe( - 'http://127.0.0.1:3100/ax' - ) - expect(deriveAxUrlFromStreamUrl('http://127.0.0.1:3200/helper/device-1/stream.mjpeg')).toBe( - 'http://127.0.0.1:3200/helper/device-1/ax' - ) - }) - - it('never fabricates an /ax endpoint from a non-mjpeg or missing url', () => { - expect(deriveAxUrlFromStreamUrl('http://127.0.0.1:3100/stream.h264')).toBeUndefined() - expect(deriveAxUrlFromStreamUrl('http://127.0.0.1:3100/')).toBeUndefined() - expect(deriveAxUrlFromStreamUrl(undefined)).toBeUndefined() - }) -}) diff --git a/src/main/git/commit.test.ts b/src/main/git/commit.test.ts deleted file mode 100644 index 465488d268f..00000000000 --- a/src/main/git/commit.test.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest' - -const { gitExecFileAsyncMock } = vi.hoisted(() => ({ - gitExecFileAsyncMock: vi.fn() -})) - -vi.mock('./runner', () => ({ - gitExecFileAsync: gitExecFileAsyncMock, - gitExecFileAsyncBuffer: vi.fn() -})) - -import { commitChanges } from './status' - -describe('commitChanges', () => { - beforeEach(() => { - gitExecFileAsyncMock.mockReset() - }) - - it('returns success when git commit completes', async () => { - gitExecFileAsyncMock.mockResolvedValue({ stdout: '[main abc123] message\n', stderr: '' }) - - const result = await commitChanges('/repo', 'feat: add commit action') - - expect(gitExecFileAsyncMock).toHaveBeenCalledWith(['commit', '-m', 'feat: add commit action'], { - cwd: '/repo' - }) - expect(result).toEqual({ success: true }) - }) - - it('returns stderr when commit fails (e.g. pre-commit hook)', async () => { - gitExecFileAsyncMock.mockRejectedValue({ - stderr: 'pre-commit hook failed: lint errors\n' - }) - - const result = await commitChanges('/repo', 'feat: commit with lint errors') - - expect(result).toEqual({ - success: false, - error: 'pre-commit hook failed: lint errors\n' - }) - }) - - it('returns stdout when git writes to stdout (e.g. nothing to commit)', async () => { - gitExecFileAsyncMock.mockRejectedValue({ - stdout: 'nothing to commit, working tree clean\n', - stderr: '' - }) - - const result = await commitChanges('/repo', 'feat: empty commit') - - expect(result).toEqual({ - success: false, - error: 'nothing to commit, working tree clean\n' - }) - }) - - it('prefers stderr over stdout when both are present', async () => { - gitExecFileAsyncMock.mockRejectedValue({ - stdout: 'some stdout output\n', - stderr: 'hook rejected\n' - }) - - const result = await commitChanges('/repo', 'feat: both channels') - - expect(result).toEqual({ - success: false, - error: 'hook rejected\n' - }) - }) - - it('falls back to Error.message when stdout/stderr are empty', async () => { - gitExecFileAsyncMock.mockRejectedValue(new Error('spawn git ENOENT')) - - const result = await commitChanges('/repo', 'feat: missing git') - - expect(result).toEqual({ - success: false, - error: 'spawn git ENOENT' - }) - }) -}) diff --git a/src/main/gitea/pull-request-mappers.test.ts b/src/main/gitea/pull-request-mappers.test.ts deleted file mode 100644 index d497096f166..00000000000 --- a/src/main/gitea/pull-request-mappers.test.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { - deriveGiteaCommitStatus, - mapGiteaMergeable, - mapGiteaPullRequest, - mapGiteaPullRequestState -} from './pull-request-mappers' - -describe('Gitea pull request mappers', () => { - it('maps pull request states', () => { - expect(mapGiteaPullRequestState({ state: 'open' })).toBe('open') - expect(mapGiteaPullRequestState({ state: 'closed' })).toBe('closed') - expect(mapGiteaPullRequestState({ state: 'open', draft: true })).toBe('draft') - expect(mapGiteaPullRequestState({ state: 'closed', draft: true })).toBe('closed') - expect(mapGiteaPullRequestState({ state: 'closed', merged: true })).toBe('merged') - }) - - it('maps mergeability', () => { - expect(mapGiteaMergeable(true)).toBe('MERGEABLE') - expect(mapGiteaMergeable(false)).toBe('CONFLICTING') - expect(mapGiteaMergeable(undefined)).toBe('UNKNOWN') - }) - - it('derives commit status from the combined state', () => { - expect(deriveGiteaCommitStatus({ state: 'success' })).toBe('success') - expect(deriveGiteaCommitStatus({ state: 'failure' })).toBe('failure') - expect(deriveGiteaCommitStatus({ state: 'error' })).toBe('failure') - expect(deriveGiteaCommitStatus({ state: 'pending' })).toBe('pending') - expect(deriveGiteaCommitStatus({ state: 'skipped' })).toBe('neutral') - }) - - it('rolls up individual statuses when the combined state is neutral', () => { - expect( - deriveGiteaCommitStatus({ - state: '', - statuses: [{ status: 'success' }, { status: 'failure' }] - }) - ).toBe('failure') - expect( - deriveGiteaCommitStatus({ - statuses: [{ status: 'success' }, { status: 'pending' }] - }) - ).toBe('pending') - expect(deriveGiteaCommitStatus({ statuses: [{ status: 'success' }] })).toBe('success') - expect(deriveGiteaCommitStatus({ statuses: [] })).toBe('neutral') - }) - - it('maps a raw pull request to the hosted review shape', () => { - expect( - mapGiteaPullRequest( - { - number: 12, - title: 'Gitea branch', - state: 'open', - html_url: 'https://git.example.com/team/project/pulls/12', - updated_at: '2026-05-15T00:00:00Z', - mergeable: true, - head: { sha: 'abc123' } - }, - 'success' - ) - ).toEqual({ - number: 12, - title: 'Gitea branch', - state: 'open', - url: 'https://git.example.com/team/project/pulls/12', - status: 'success', - updatedAt: '2026-05-15T00:00:00Z', - mergeable: 'MERGEABLE', - headSha: 'abc123' - }) - }) - - it('rejects incomplete pull request payloads', () => { - expect(mapGiteaPullRequest({ number: 1, title: 'missing url' }, 'neutral')).toBeNull() - }) -}) diff --git a/src/main/runtime/orchestration/cli-command.test.ts b/src/main/runtime/orchestration/cli-command.test.ts deleted file mode 100644 index ed1c82d70bf..00000000000 --- a/src/main/runtime/orchestration/cli-command.test.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { resolveTerminalOrchestrationCliCommand } from './cli-command' - -describe('resolveTerminalOrchestrationCliCommand', () => { - it('uses orca-ide for a pane recorded as WSL', () => { - expect( - resolveTerminalOrchestrationCliCommand({ - connectionId: null, - isWsl: true, - worktreeId: 'repo::C:\\repo' - }) - ).toBe('orca-ide') - }) - - it('uses project runtime and WSL paths when restored pane metadata is unavailable', () => { - expect( - resolveTerminalOrchestrationCliCommand({ - connectionId: null, - isWsl: null, - worktreeId: 'repo::C:\\repo', - projectRuntime: { - status: 'resolved', - runtime: { - kind: 'wsl', - hostPlatform: 'wsl', - projectId: 'project', - distro: 'Ubuntu', - reason: 'project-override', - cacheKey: 'project:wsl:Ubuntu' - } - } - }) - ).toBe('orca-ide') - expect( - resolveTerminalOrchestrationCliCommand({ - connectionId: null, - isWsl: null, - worktreeId: 'repo::\\\\wsl.localhost\\Ubuntu\\home\\alice\\repo' - }) - ).toBe('orca-ide') - }) - - it('preserves native and SSH bare-orca commands', () => { - expect( - resolveTerminalOrchestrationCliCommand({ - connectionId: null, - isWsl: false, - worktreeId: 'repo::/home/alice/repo' - }) - ).toBe('orca') - expect( - resolveTerminalOrchestrationCliCommand({ - connectionId: 'ssh-1', - isWsl: null, - worktreeId: 'repo::\\\\wsl.localhost\\Ubuntu\\home\\alice\\repo' - }) - ).toBe('orca') - }) - - it('uses the runtime-provided command locally but never leaks it to SSH', () => { - expect( - resolveTerminalOrchestrationCliCommand({ - connectionId: null, - isWsl: true, - worktreeId: 'repo::C:\\repo', - runtimeCliCommand: 'orca-dev' - }) - ).toBe('orca-dev') - expect( - resolveTerminalOrchestrationCliCommand({ - connectionId: 'ssh-1', - isWsl: true, - worktreeId: 'repo::C:\\repo', - runtimeCliCommand: 'orca-dev' - }) - ).toBe('orca') - }) -}) diff --git a/src/main/runtime/rpc/worktree-ps-other-client-marks.test.ts b/src/main/runtime/rpc/worktree-ps-other-client-marks.test.ts deleted file mode 100644 index 0af8ccf8320..00000000000 --- a/src/main/runtime/rpc/worktree-ps-other-client-marks.test.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { describe, expect, it } from 'vitest' -import type { - RuntimeWorktreePsResult, - RuntimeWorktreePsSummary -} from '../../../shared/runtime-worktree-contracts' -import type { RpcContext } from './core' -import { markWorktreePsOtherClientRows } from './worktree-ps-other-client-marks' - -function row( - worktreeId: string, - creatorProvenance?: RuntimeWorktreePsSummary['creatorProvenance'] -): RuntimeWorktreePsSummary { - return { - worktreeId, - repoId: 'repo', - repo: 'repo', - path: `/repo/${worktreeId}`, - branch: worktreeId, - isArchived: false, - isMainWorktree: false, - hasHostSidebarActivity: false, - parentWorktreeId: null, - childWorktreeIds: [], - displayName: worktreeId, - workspaceStatus: 'in-progress', - sortOrder: 0, - linkedIssue: null, - linkedPR: null, - linkedLinearIssue: null, - linkedGitLabMR: null, - linkedGitLabIssue: null, - comment: '', - isPinned: false, - isActive: false, - unread: false, - liveTerminalCount: 0, - hasAttachedPty: false, - lastOutputAt: null, - preview: '', - status: 'inactive', - agents: [], - ...(creatorProvenance ? { creatorProvenance } : {}) - } -} - -const rows = [ - row('desktop', { kind: 'host' }), - row('laptop', { kind: 'paired-device', deviceId: 'laptop' }), - row('unknown') -] -const result: RuntimeWorktreePsResult = { worktrees: rows, totalCount: 4, truncated: false } - -function context( - overrides: Partial> -): Parameters[1] { - return overrides -} - -function marked(next: RuntimeWorktreePsResult): string[] { - return next.worktrees.filter((entry) => entry.desktopOtherClient).map((entry) => entry.worktreeId) -} - -describe('markWorktreePsOtherClientRows', () => { - it("marks, for this host's own phone, the rows its sidebar calls another client's", () => { - expect( - marked(markWorktreePsOtherClientRows(result, context({ clientKind: 'mobile' }))) - ).toEqual(['laptop']) - }) - - it('leaves rows untouched for any other caller', () => { - expect(markWorktreePsOtherClientRows(result, context({ clientKind: 'runtime' }))).toBe(result) - expect(markWorktreePsOtherClientRows(result, context({}))).toBe(result) - expect( - markWorktreePsOtherClientRows( - result, - context({ clientKind: 'mobile', pairedDeviceParentId: 'other-desktop' }) - ) - ).toBe(result) - }) -}) diff --git a/src/renderer/src/components/editor/editor-panel-git-entry-selector.test.ts b/src/renderer/src/components/editor/editor-panel-git-entry-selector.test.ts deleted file mode 100644 index 0dedf058495..00000000000 --- a/src/renderer/src/components/editor/editor-panel-git-entry-selector.test.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { describe, expect, it } from 'vitest' -import type { GitBranchChangeEntry } from '../../../../shared/git-diff-compare-types' -import type { GitStatusEntry } from '../../../../shared/git-status-types' -import { - selectEditorPanelGitBranchEntries, - selectEditorPanelGitStatusEntries -} from './editor-panel-git-entry-selector' - -describe('editor panel Git entry selectors', () => { - it('ignore background worktree writes across mounted editor panels', () => { - const panelCount = 200 - const worktreeId = 'worktree-active' - const statusEntries = [{ path: 'src/index.ts' }] as GitStatusEntry[] - const branchEntries = [{ path: 'src/index.ts' }] as GitBranchChangeEntry[] - let gitStatusByWorktree = { [worktreeId]: statusEntries } - let gitBranchChangesByWorktree = { [worktreeId]: branchEntries } - let wholeMapInvalidations = 0 - let scopedEntryInvalidations = 0 - - for (let write = 0; write < 200; write += 1) { - const previousStatusMap = gitStatusByWorktree - gitStatusByWorktree = { - ...gitStatusByWorktree, - [`background-${write}`]: [{ path: `generated-${write}.ts` } as GitStatusEntry] - } - const previousBranchMap = gitBranchChangesByWorktree - gitBranchChangesByWorktree = { - ...gitBranchChangesByWorktree, - [`background-${write}`]: [{ path: `generated-${write}.ts` } as GitBranchChangeEntry] - } - - for (let panel = 0; panel < panelCount; panel += 1) { - wholeMapInvalidations += Number(previousStatusMap !== gitStatusByWorktree) - wholeMapInvalidations += Number(previousBranchMap !== gitBranchChangesByWorktree) - scopedEntryInvalidations += Number( - selectEditorPanelGitStatusEntries({ gitStatusByWorktree }, worktreeId) !== statusEntries - ) - scopedEntryInvalidations += Number( - selectEditorPanelGitBranchEntries({ gitBranchChangesByWorktree }, worktreeId) !== - branchEntries - ) - } - } - - expect(wholeMapInvalidations).toBe(80_000) - expect(scopedEntryInvalidations).toBe(0) - }) - - it('publishes owning entry replacements and handles an absent worktree', () => { - const firstStatus = [{ path: 'src/old.ts' }] as GitStatusEntry[] - const nextStatus = [{ path: 'src/new.ts' }] as GitStatusEntry[] - const firstBranch = [{ path: 'src/old.ts' }] as GitBranchChangeEntry[] - const nextBranch = [{ path: 'src/new.ts' }] as GitBranchChangeEntry[] - - expect( - selectEditorPanelGitStatusEntries({ gitStatusByWorktree: { active: firstStatus } }, 'active') - ).toBe(firstStatus) - expect( - selectEditorPanelGitStatusEntries({ gitStatusByWorktree: { active: nextStatus } }, 'active') - ).toBe(nextStatus) - expect( - selectEditorPanelGitBranchEntries( - { gitBranchChangesByWorktree: { active: firstBranch } }, - 'active' - ) - ).toBe(firstBranch) - expect( - selectEditorPanelGitBranchEntries( - { gitBranchChangesByWorktree: { active: nextBranch } }, - 'active' - ) - ).toBe(nextBranch) - expect(selectEditorPanelGitStatusEntries({ gitStatusByWorktree: {} }, null)).toBeUndefined() - }) -}) diff --git a/src/renderer/src/components/native-chat/structured-agent-session-delivery-notices.kept-card.test.ts b/src/renderer/src/components/native-chat/structured-agent-session-delivery-notices.kept-card.test.ts deleted file mode 100644 index f8718b09fac..00000000000 --- a/src/renderer/src/components/native-chat/structured-agent-session-delivery-notices.kept-card.test.ts +++ /dev/null @@ -1,73 +0,0 @@ -// After host admission as a queued card, its own row must not show sending/failure regardless of local delivery phase. - -import { expect, it } from 'vitest' -import type { AgentJournalSubmission } from '../../../../shared/agent-session-journal-types' -import { agentJournalSubmissionKey } from '../../../../shared/agent-session-journal-item-key' -import { structuredAgentSessionSendBody } from '../../../../shared/structured-agent-session-send-mutation' -import { structuredAgentSessionDeliveryNotices } from './structured-agent-session-delivery-notices' -import type { StructuredAgentSessionPendingSend } from './structured-agent-session-pending-sends' - -const KEPT_ID = 'client-kept' - -function pendingSend( - patch: Partial = {} -): StructuredAgentSessionPendingSend { - return { - clientMessageId: KEPT_ID, - sessionId: 'session-1', - body: structuredAgentSessionSendBody('the kept words', []), - previewUris: [], - queuedAt: 1, - phase: 'sending', - issued: false, - ...patch - } -} - -const restartRejected: AgentJournalSubmission = { - clientMessageId: KEPT_ID, - fence: 2, - payloadFingerprint: 'fingerprint-kept', - dispatchState: 'rejected', - providerItemId: null, - reason: 'Orca restarted before this message was sent.', - rejection: { kind: 'hostRestarted' }, - submittedAt: 1, - resolvedAt: 2, - recovered: true -} - -const kept: AgentJournalSubmission = { ...restartRejected, keptAsQueuedMessageId: KEPT_ID } - -function notices( - send: StructuredAgentSessionPendingSend, - submissions: readonly AgentJournalSubmission[] -) { - return structuredAgentSessionDeliveryNotices({ - pending: [send], - submissions, - agentName: 'Claude', - startFailures: [] - }) -} - -// Still being readied, on its way, or resent under its id after a lost answer. -const SENDS = [ - pendingSend(), - pendingSend({ issued: true }), - pendingSend({ phase: 'recorded', issued: true }) -] - -it('gives a kept send no notice, so it never reads "Sending…" or not sent', () => { - for (const send of SENDS) { - expect([...notices(send, [kept])]).toEqual([]) - } -}) - -it('still says not sent, never "Sending…", for a send rejected without being kept', () => { - for (const send of SENDS) { - const notice = notices(send, [restartRejected]).get(agentJournalSubmissionKey(KEPT_ID)) - expect(notice).toMatchObject({ text: expect.any(String) }) - expect(notice).not.toHaveProperty('sending') - } -}) diff --git a/src/renderer/src/components/native-chat/structured-agent-session-sending-queued-cards.test.ts b/src/renderer/src/components/native-chat/structured-agent-session-sending-queued-cards.test.ts deleted file mode 100644 index 3cbc0c14a7d..00000000000 --- a/src/renderer/src/components/native-chat/structured-agent-session-sending-queued-cards.test.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { describe, expect, it } from 'vitest' -import type { AgentJournalSubmission } from '../../../../shared/agent-session-journal-types' -import type { StructuredAgentSessionPendingSend } from './structured-agent-session-pending-sends' -import { - pendingQueueSendsOnTheirWay, - pendingSendsOutsideQueuedCards -} from './structured-agent-session-sending-queued-cards' - -function submission(clientMessageId: string, queuedMessageId?: string): AgentJournalSubmission { - return { - clientMessageId, - fence: 1, - payloadFingerprint: 'fingerprint', - dispatchState: 'pending', - providerItemId: null, - reason: null, - submittedAt: 1, - resolvedAt: null, - ...(queuedMessageId !== undefined ? { queuedMessageId } : {}) - } -} - -describe('queue sends on their way', () => { - it('a mid-turn queue send on its way is no bubble; a plain or recorded one is, until its row', () => { - const entry = ( - clientMessageId: string, - overrides: Partial = {} - ): StructuredAgentSessionPendingSend => ({ - clientMessageId, - sessionId: 'session-1', - body: { kind: 'message', role: 'user', blocks: [{ type: 'text', text: clientMessageId }] }, - previewUris: [], - queuedAt: 1, - phase: 'sending', - issued: true, - ...overrides - }) - const ids = (entries: readonly StructuredAgentSessionPendingSend[]): string[] => - entries.map((candidate) => candidate.clientMessageId) - const sends = [ - entry('queued', { delivery: 'queue-if-active' }), - entry('plain'), - entry('recorded', { phase: 'recorded' }) - ] - // Pending queued sends use cards while working; plain/recorded sends remain bubbles until host ownership appears. - expect(ids(pendingSendsOutsideQueuedCards(sends, [], true))).toEqual(['plain', 'recorded']) - expect(ids(pendingSendsOutsideQueuedCards(sends, [], false))).toEqual([ - 'queued', - 'plain', - 'recorded' - ]) - expect(ids(pendingSendsOutsideQueuedCards(sends, ['plain'], false))).toEqual([ - 'queued', - 'recorded' - ]) - }) - it('a sending card ends once the host records the send or hands its card off', () => { - const entry: StructuredAgentSessionPendingSend = { - clientMessageId: 'a', - sessionId: 'session-1', - body: { kind: 'message', role: 'user', blocks: [{ type: 'text', text: 'a' }] }, - previewUris: [], - queuedAt: 1, - phase: 'sending', - issued: true, - delivery: 'queue-if-active' - } - const onItsWay = (submissions: AgentJournalSubmission[]) => - pendingQueueSendsOnTheirWay([entry], [], true, submissions).map( - (candidate) => candidate.clientMessageId - ) - expect(onItsWay([])).toEqual(['a']) - expect(onItsWay([submission('a')])).toEqual([]) - expect(onItsWay([submission('a-hand-off', 'a')])).toEqual([]) - expect(pendingQueueSendsOnTheirWay([entry], ['a'], true, [])).toEqual([]) - expect(pendingQueueSendsOnTheirWay([entry], [], false, [])).toEqual([]) - expect(pendingQueueSendsOnTheirWay([{ ...entry, phase: 'recorded' }], [], true, [])).toEqual([]) - }) -}) diff --git a/src/renderer/src/components/plugins/list/plugin-list-filter.test.ts b/src/renderer/src/components/plugins/list/plugin-list-filter.test.ts deleted file mode 100644 index 669520c9c44..00000000000 --- a/src/renderer/src/components/plugins/list/plugin-list-filter.test.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { describe, expect, it } from 'vitest' -import type { PluginListEntry } from './plugin-list-entries' -import { matchesPluginFilter, pluginFilterCategories } from './plugin-list-filter' - -function entry( - categories: string[], - sourceId: string | null, - catalogIds: string[] = sourceId ? [sourceId] : [] -): PluginListEntry { - return { - pluginKey: `p.${categories.join('-')}`, - name: 'P', - publisher: null, - version: null, - description: null, - official: false, - categories, - types: [], - minimumOrca: null, - updateVersion: null, - catalogIds, - updatable: false, - autoUpdate: { updatedTo: null, needsReview: false }, - installed: null, - listing: sourceId - ? { - marketplaceSourceId: sourceId, - marketplaceName: 'Catalog', - marketplaceOwner: 'owner', - marketplaceCommit: 'c', - pluginKey: 'p', - source: { kind: 'git', url: 'https://example.invalid/c.git', ref: 'main' }, - categories, - official: false, - bundled: false - } - : null, - verifiedSource: { kind: 'unverified' }, - alsoListedBy: [] - } -} - -describe('plugin list filter', () => { - it('counts categories without the trust label, and matches category and catalog together', () => { - const themes = entry(['themes', 'official'], 'official') - const icons = entry(['icons'], 'acme') - expect(pluginFilterCategories([themes, icons])).toEqual([ - ['themes', 1], - ['icons', 1] - ]) - const filter = { categories: new Set(['themes']), sourceIds: new Set(['official']) } - expect(matchesPluginFilter(themes, filter)).toBe(true) - expect(matchesPluginFilter(icons, filter)).toBe(false) - expect(matchesPluginFilter(entry(['themes'], null), filter)).toBe(false) - expect(matchesPluginFilter(icons, { categories: new Set(), sourceIds: new Set() })).toBe(true) - }) - - it('folds catalog categories into readable kinds and keeps unknown ones', () => { - const terminal = entry(['terminal-themes'], 'official') - const recipes = entry(['vm-recipes'], 'official') - const odd = entry(['music'], 'official') - expect(pluginFilterCategories([terminal, recipes, odd])).toEqual([ - ['themes', 1], - ['machines', 1], - ['music', 1] - ]) - expect( - matchesPluginFilter(terminal, { categories: new Set(['themes']), sourceIds: new Set() }) - ).toBe(true) - }) - - it('matches a plugin under any catalog that lists it', () => { - const shared = entry(['themes'], 'official', ['official', 'acme']) - expect( - matchesPluginFilter(shared, { categories: new Set(), sourceIds: new Set(['acme']) }) - ).toBe(true) - }) -}) diff --git a/src/renderer/src/components/right-sidebar/ai-vault-session-chat-action.test.ts b/src/renderer/src/components/right-sidebar/ai-vault-session-chat-action.test.ts deleted file mode 100644 index 55ef6c9a600..00000000000 --- a/src/renderer/src/components/right-sidebar/ai-vault-session-chat-action.test.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { describe, expect, it } from 'vitest' -import type { AiVaultSession } from '../../../../shared/ai-vault-types' -import type { AiVaultSearchHit } from '../../../../shared/ai-vault-search-types' -import { resolveAiVaultSessionRowChatActions } from './ai-vault-session-chat-action' - -const session: AiVaultSession = { - id: 'local:claude:s-1:/tmp/s-1.jsonl', - executionHostId: 'local', - agent: 'claude', - sessionId: 's-1', - title: 'Fix auth retry', - cwd: '/repo', - branch: null, - model: null, - filePath: '/tmp/s-1.jsonl', - codexHome: null, - createdAt: null, - updatedAt: null, - modifiedAt: '2026-09-17T00:00:00.000Z', - messageCount: 2, - totalTokens: 0, - previewMessages: [{ role: 'user', text: 'Fix it', timestamp: null }], - queuedMessageCount: 0, - subagentTranscriptCount: 0, - resumeCommand: 'claude --resume s-1', - subagent: null -} - -const hit: AiVaultSearchHit = { - agent: 'claude', - sessionId: 's-1', - title: 'Fix auth retry', - cwd: '/repo', - branch: null, - updatedAt: null, - messageCount: 2, - score: 1, - source: { presence: 'missing' }, - evidence: null -} - -function actions(overrides: Partial[0]>) { - return resolveAiVaultSessionRowChatActions({ - session, - originalPaneTarget: null, - structuredSessionOpen: false, - resumeState: { blocked: false, worktreeId: 'wt-1', usesSessionWorktree: true }, - resumeInChat: { available: true, workspaceId: 'wt-own' }, - searchHit: undefined, - ...overrides - }) -} - -describe('session row chat actions', () => { - it('resumes with ▶ only while ▶ is enabled and shown', () => { - expect(actions({})).toMatchObject({ resumeDisabled: false, resumeWorktreeId: 'wt-1' }) - expect( - actions({ resumeState: { blocked: true, worktreeId: 'wt-1', usesSessionWorktree: true } }) - ).toMatchObject({ resumeDisabled: true, resumeWorktreeId: null }) - // An open chat hides ▶; switching to it is the only offer. - expect(actions({ structuredSessionOpen: true }).resumeWorktreeId).toBeNull() - expect( - actions({ session: { ...session, messageCount: 0, previewMessages: [] } }) - ).toMatchObject({ resumeDisabled: true, resumeWorktreeId: null }) - }) - - it('offers nothing a hidden button would not', () => { - expect( - actions({ resumeInChat: { available: false, reason: 'remote' } }).resumeInNewChatWorkspaceId - ).toBeNull() - // A search hit whose transcript is gone cannot resume, whatever eligibility said. - expect(actions({ searchHit: hit })).toEqual({ - canJumpToOriginalPane: false, - resumeDisabled: true, - resumeWorktreeId: null, - resumeInNewChatWorkspaceId: null, - resumeInNewCliWorktreeId: null - }) - }) -}) diff --git a/src/renderer/src/components/right-sidebar/review-cache-entry-selection.test.ts b/src/renderer/src/components/right-sidebar/review-cache-entry-selection.test.ts deleted file mode 100644 index 251dce59a3f..00000000000 --- a/src/renderer/src/components/right-sidebar/review-cache-entry-selection.test.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { selectReviewCacheData, selectReviewCacheEntry } from './review-cache-entry-selection' - -type Review = { id: string } -type ReviewEntry = { data: Review | null; fetchedAt: number } - -const ACTIVE_KEY = 'repo-active::feature' - -function replaceUnrelatedEntry( - cache: Record, - index: number -): Record { - return { - ...cache, - [`repo-background-${index % 100}::feature`]: { - data: { id: `background-${index}` }, - fetchedAt: index - } - } -} - -describe('review cache entry selection', () => { - it('turns unrelated active-panel cache invalidations into stable selections', () => { - const activeHostedReview = { id: 'hosted-active' } - const activePullRequest = { id: 'pr-active' } - let hostedCache: Record = { - [ACTIVE_KEY]: { data: activeHostedReview, fetchedAt: 1 } - } - let pullRequestCache: Record = { - [ACTIVE_KEY]: { data: activePullRequest, fetchedAt: 1 } - } - let previousHostedMap = hostedCache - let previousPullRequestMap = pullRequestCache - let previousHostedSelection = selectReviewCacheData(hostedCache, ACTIVE_KEY) - let previousPullRequestSelection = selectReviewCacheEntry(pullRequestCache, ACTIVE_KEY) - let wholeMapInvalidations = 0 - let scopedSelectionInvalidations = 0 - - for (let index = 0; index < 200; index += 1) { - hostedCache = replaceUnrelatedEntry(hostedCache, index) - pullRequestCache = replaceUnrelatedEntry(pullRequestCache, index) - if (hostedCache !== previousHostedMap) { - wholeMapInvalidations += 1 - } - if (pullRequestCache !== previousPullRequestMap) { - wholeMapInvalidations += 1 - } - - const hostedSelection = selectReviewCacheData(hostedCache, ACTIVE_KEY) - const pullRequestSelection = selectReviewCacheEntry(pullRequestCache, ACTIVE_KEY) - if (hostedSelection !== previousHostedSelection) { - scopedSelectionInvalidations += 1 - } - if (pullRequestSelection !== previousPullRequestSelection) { - scopedSelectionInvalidations += 1 - } - - previousHostedMap = hostedCache - previousPullRequestMap = pullRequestCache - previousHostedSelection = hostedSelection - previousPullRequestSelection = pullRequestSelection - } - - expect(wholeMapInvalidations).toBe(400) - expect(scopedSelectionInvalidations).toBe(0) - }) - - it('publishes relevant entry replacements and handles absent keys', () => { - const first = { data: { id: 'first' }, fetchedAt: 1 } - const second = { data: { id: 'second' }, fetchedAt: 2 } - - expect(selectReviewCacheEntry({ [ACTIVE_KEY]: first }, ACTIVE_KEY)).toBe(first) - expect(selectReviewCacheEntry({ [ACTIVE_KEY]: second }, ACTIVE_KEY)).toBe(second) - expect(selectReviewCacheData({ [ACTIVE_KEY]: second }, ACTIVE_KEY)).toBe(second.data) - expect(selectReviewCacheEntry({}, ACTIVE_KEY)).toBeUndefined() - expect(selectReviewCacheData({}, ACTIVE_KEY)).toBeNull() - expect(selectReviewCacheEntry({ [ACTIVE_KEY]: first }, null)).toBeUndefined() - }) -}) diff --git a/src/renderer/src/components/right-sidebar/search-rows.test.ts b/src/renderer/src/components/right-sidebar/search-rows.test.ts deleted file mode 100644 index 993e8ac82d2..00000000000 --- a/src/renderer/src/components/right-sidebar/search-rows.test.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { buildSearchRows } from './search-rows' - -describe('buildSearchRows', () => { - it('includes file headers and expanded matches in row order (summary is rendered separately)', () => { - const rows = buildSearchRows( - { - totalMatches: 3, - truncated: false, - files: [ - { - filePath: '/repo/a.ts', - relativePath: 'a.ts', - matches: [ - { line: 1, column: 1, matchLength: 3, lineContent: 'foo' }, - { line: 2, column: 5, matchLength: 3, lineContent: 'bar foo' } - ] - }, - { - filePath: '/repo/b.ts', - relativePath: 'nested/b.ts', - matches: [{ line: 8, column: 2, matchLength: 3, lineContent: ' foo' }] - } - ] - }, - new Set() - ) - - expect(rows.map((row) => row.type)).toEqual(['file', 'match', 'match', 'file', 'match']) - }) - - it('omits match rows for collapsed files', () => { - const rows = buildSearchRows( - { - totalMatches: 2, - truncated: true, - files: [ - { - filePath: '/repo/a.ts', - relativePath: 'a.ts', - matches: [{ line: 1, column: 1, matchLength: 3, lineContent: 'foo' }] - }, - { - filePath: '/repo/b.ts', - relativePath: 'b.ts', - matches: [{ line: 2, column: 1, matchLength: 3, lineContent: 'foo' }] - } - ] - }, - new Set(['/repo/a.ts']) - ) - - expect(rows.map((row) => row.type)).toEqual(['file', 'file', 'match']) - }) - - it('preserves the file result object for renderer-side count normalization', () => { - const fileResult = { - filePath: '/repo/a.ts', - relativePath: 'a.ts', - matchCount: 5, - matches: [{ line: 1, column: 1, matchLength: 3, lineContent: 'foo' }] - } - - const rows = buildSearchRows( - { - totalMatches: 5, - truncated: false, - files: [fileResult] - }, - new Set() - ) - - expect(rows[0]).toMatchObject({ type: 'file', fileResult }) - expect(rows[1]).toMatchObject({ type: 'match', fileResult }) - }) -}) diff --git a/src/renderer/src/components/terminal-pane/use-wsl-host-unavailable-reason.test.ts b/src/renderer/src/components/terminal-pane/use-wsl-host-unavailable-reason.test.ts deleted file mode 100644 index bdb647990b0..00000000000 --- a/src/renderer/src/components/terminal-pane/use-wsl-host-unavailable-reason.test.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { - createTestStore, - runtimeOwnedRepoState, - seedStore -} from '../../store/slices/store-test-helpers' -import { getDefaultSettings } from '../../../../shared/constants' -import { selectWslHostUnavailableReason } from './use-wsl-host-unavailable-reason' - -const WORKTREE = 'wsl-repo::/home/me/app' - -function stateWith(options: { enabled: boolean; serverFiles?: 'present' | 'missing' }) { - const store = createTestStore() - seedStore(store, { - ...runtimeOwnedRepoState('wsl:ubuntu', 'wsl-repo', '/home/me/app'), - runtimeEnvironments: [ - { - id: 'wsl:ubuntu', - name: 'Ubuntu (WSL)', - createdAt: 0, - updatedAt: 0, - lastUsedAt: null, - runtimeId: null, - preferredEndpointId: 'ws', - endpoints: [], - orcadDeployment: { kind: 'wsl', distro: 'Ubuntu', localPort: 42000, remotePort: 6768 } - } - ], - wslHostServerFiles: options.serverFiles ?? 'present' - }) - store.setState({ - settings: { ...getDefaultSettings('/home/me'), experimentalWslHosts: options.enabled } - }) - return store.getState() -} - -describe('selectWslHostUnavailableReason', () => { - it('says a WSL host’s terminal is turned off rather than reconnecting', () => { - expect(selectWslHostUnavailableReason(stateWith({ enabled: false }), WORKTREE)).toBe( - 'turned-off' - ) - }) - - it('prefers the missing server files, and is quiet when the host can run', () => { - expect( - selectWslHostUnavailableReason(stateWith({ enabled: true, serverFiles: 'missing' }), WORKTREE) - ).toBe('server-files-missing') - expect(selectWslHostUnavailableReason(stateWith({ enabled: true }), WORKTREE)).toBeNull() - }) - - it('leaves other hosts’ workspaces alone', () => { - expect( - selectWslHostUnavailableReason(stateWith({ enabled: false }), 'repo1::/repo1') - ).toBeNull() - }) - - it('is quiet when no WSL host exists, even for a workspace on another runtime', () => { - const store = createTestStore() - seedStore(store, { - ...runtimeOwnedRepoState('paired', 'remote-repo', '/srv/app'), - runtimeEnvironments: [ - { - id: 'paired', - name: 'Paired server', - createdAt: 0, - updatedAt: 0, - lastUsedAt: null, - runtimeId: null, - preferredEndpointId: 'ws', - endpoints: [] - } - ] - }) - store.setState({ settings: { ...getDefaultSettings('/home/me'), experimentalWslHosts: false } }) - expect(selectWslHostUnavailableReason(store.getState(), 'remote-repo::/srv/app')).toBeNull() - }) -}) diff --git a/src/renderer/src/store/slices/new-issue-draft.test.ts b/src/renderer/src/store/slices/new-issue-draft.test.ts deleted file mode 100644 index c8271f1819d..00000000000 --- a/src/renderer/src/store/slices/new-issue-draft.test.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { create } from 'zustand' -import { describe, expect, it } from 'vitest' -import { createNewIssueDraftSlice } from './new-issue-draft' -import type { AppState } from '../types' -import type { GitHubAssignableUser } from '../../../../shared/github/pull-request-types' - -function makeStore() { - return create>()( - (...args) => createNewIssueDraftSlice(...(args as Parameters)) - ) -} - -const assignee: GitHubAssignableUser = { login: 'octocat', name: 'Octo', avatarUrl: '' } - -describe('createNewIssueDraftSlice', () => { - it('starts with no draft', () => { - expect(makeStore().getState().newIssueDraft).toBeNull() - }) - - it('seeds a fresh empty draft with the patch when none exists', () => { - const store = makeStore() - - store.getState().setNewIssueDraft({ title: 'Bug' }) - - expect(store.getState().newIssueDraft).toEqual({ - title: 'Bug', - body: '', - labels: [], - assignees: [], - repoId: null - }) - }) - - it('shallow-merges the patch into the current draft', () => { - const store = makeStore() - store.getState().setNewIssueDraft({ - title: 'Bug', - body: 'details', - labels: ['p1'], - assignees: [assignee], - repoId: 'repo-a' - }) - - store.getState().setNewIssueDraft({ body: 'more details' }) - - expect(store.getState().newIssueDraft).toEqual({ - title: 'Bug', - body: 'more details', - labels: ['p1'], - assignees: [assignee], - repoId: 'repo-a' - }) - }) - - it('clears the draft back to null', () => { - const store = makeStore() - store.getState().setNewIssueDraft({ title: 'Bug', repoId: 'repo-a' }) - - store.getState().clearNewIssueDraft() - - expect(store.getState().newIssueDraft).toBeNull() - }) - - it('seeds fresh label/assignee arrays per empty draft (no shared-constant aliasing)', () => { - // Partial patches must seed fresh label/assignee arrays; shared defaults let mutations corrupt all future drafts. - const a = makeStore() - const b = makeStore() - a.getState().setNewIssueDraft({ title: 'A' }) - b.getState().setNewIssueDraft({ title: 'B' }) - - expect(a.getState().newIssueDraft?.labels).not.toBe(b.getState().newIssueDraft?.labels) - expect(a.getState().newIssueDraft?.assignees).not.toBe(b.getState().newIssueDraft?.assignees) - - a.getState().newIssueDraft?.labels.push('leak') - const c = makeStore() - c.getState().setNewIssueDraft({ title: 'C' }) - expect(c.getState().newIssueDraft?.labels).toEqual([]) - }) -}) diff --git a/src/renderer/src/store/slices/task-creation-drafts.test.ts b/src/renderer/src/store/slices/task-creation-drafts.test.ts deleted file mode 100644 index 09ef945790f..00000000000 --- a/src/renderer/src/store/slices/task-creation-drafts.test.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { create } from 'zustand' -import { describe, expect, it } from 'vitest' -import { - createTaskCreationDraftsSlice, - isTaskCreationDraftContentful -} from './task-creation-drafts' -import type { AppState } from '../types' - -function makeStore() { - return create< - Pick< - AppState, - | 'newLinearIssueDraft' - | 'setNewLinearIssueDraft' - | 'clearNewLinearIssueDraft' - | 'newLinearProjectDraft' - | 'setNewLinearProjectDraft' - | 'clearNewLinearProjectDraft' - | 'newJiraIssueDraft' - | 'setNewJiraIssueDraft' - | 'clearNewJiraIssueDraft' - > - >()((...args) => - createTaskCreationDraftsSlice(...(args as Parameters)) - ) -} - -describe('createTaskCreationDraftsSlice', () => { - it('starts with no drafts', () => { - const state = makeStore().getState() - expect(state.newLinearIssueDraft).toBeNull() - expect(state.newLinearProjectDraft).toBeNull() - expect(state.newJiraIssueDraft).toBeNull() - }) - - it('stores and clears each draft independently', () => { - const store = makeStore() - - store.getState().setNewLinearIssueDraft({ title: 'Linear bug', body: 'details' }) - store.getState().setNewLinearProjectDraft({ - name: 'Roadmap', - description: 'summary', - content: 'brief' - }) - store.getState().setNewJiraIssueDraft({ title: 'Jira bug', body: 'steps' }) - - store.getState().clearNewLinearIssueDraft() - - expect(store.getState().newLinearIssueDraft).toBeNull() - expect(store.getState().newLinearProjectDraft).toEqual({ - name: 'Roadmap', - description: 'summary', - content: 'brief' - }) - expect(store.getState().newJiraIssueDraft).toEqual({ title: 'Jira bug', body: 'steps' }) - }) - - it('replaces a draft wholesale on set', () => { - const store = makeStore() - store.getState().setNewJiraIssueDraft({ title: 'first', body: 'text' }) - - store.getState().setNewJiraIssueDraft({ title: 'second', body: '' }) - - expect(store.getState().newJiraIssueDraft).toEqual({ title: 'second', body: '' }) - }) -}) - -describe('isTaskCreationDraftContentful', () => { - it('rejects an all-empty or whitespace-only form', () => { - expect(isTaskCreationDraftContentful({ title: '', body: '' })).toBe(false) - expect(isTaskCreationDraftContentful({ title: ' ', body: '\n\t' })).toBe(false) - }) - - it('accepts any field with typed text', () => { - expect(isTaskCreationDraftContentful({ title: 'Bug', body: '' })).toBe(true) - expect(isTaskCreationDraftContentful({ name: '', description: '', content: 'brief' })).toBe( - true - ) - }) -}) diff --git a/src/renderer/src/web/web-preload-api-ssh.test.ts b/src/renderer/src/web/web-preload-api-ssh.test.ts deleted file mode 100644 index 87df206c8a8..00000000000 --- a/src/renderer/src/web/web-preload-api-ssh.test.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import type { RuntimeRpcResponse } from '../../../shared/runtime-rpc-envelope' -import { - installBrowserGlobals, - writeStoredRuntimeEnvironment -} from './web-preload-api-test-harness' - -describe('web SSH preload API', () => { - beforeEach(() => { - vi.resetModules() - }) - - afterEach(() => { - vi.unstubAllGlobals() - vi.doUnmock('./web-runtime-client') - }) - - it('preserves full and partial authority states from the paired runtime', async () => { - const runtimeCalls: { method: string; params: unknown }[] = [] - vi.doMock('./web-runtime-client', () => ({ - WebRuntimeClient: class { - call(method: string, params?: unknown): Promise> { - runtimeCalls.push({ method, params }) - const state = - method === 'ssh.connect' - ? { - targetId: 'ssh-1', - status: 'connected', - error: null, - reconnectAttempt: 0, - providerEpoch: 'web-provider-epoch', - connectionGeneration: 23 - } - : { - targetId: 'ssh-1', - status: 'connected', - error: null, - reconnectAttempt: 0, - providerEpoch: 'partial-provider-epoch' - } - return Promise.resolve({ - id: `call-${runtimeCalls.length}`, - ok: true, - result: { state }, - _meta: { runtimeId: 'runtime-1' } - }) - } - - close(): void {} - } - })) - - const globals = installBrowserGlobals('Linux') - writeStoredRuntimeEnvironment(globals.storage) - const { installWebPreloadApi } = await import('./web-preload-api') - installWebPreloadApi() - - await expect(globals.window.api.ssh.connect({ targetId: 'ssh-1' })).resolves.toMatchObject({ - providerEpoch: 'web-provider-epoch', - connectionGeneration: 23 - }) - const partial = await globals.window.api.ssh.getState({ targetId: 'ssh-1' }) - - expect(partial).toMatchObject({ providerEpoch: 'partial-provider-epoch' }) - expect(partial).not.toHaveProperty('connectionGeneration') - // A background connect stays automatic on the host, so it cannot lift a cancelled sign-in. - await globals.window.api.ssh.connect({ targetId: 'ssh-1', automatic: true }) - expect(runtimeCalls).toEqual([ - { method: 'ssh.connect', params: { targetId: 'ssh-1' } }, - { method: 'ssh.getState', params: { targetId: 'ssh-1' } }, - { method: 'ssh.connect', params: { targetId: 'ssh-1', automatic: true } } - ]) - }) -})