From 2eaba5b39e5dbdd88d87117622bf6eda799a285c Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Mon, 9 Nov 2026 06:31:53 -0800 Subject: [PATCH] test: remove low-value VM, mobile, work-item and terminal suites (#40376) Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- .../scripts/ci/sharding/ci-shard-timings.json | 5 - .../pr-comment-composer-drafts.test.tsx | 9 -- .../pr-create-sheet-drafts.test.tsx | 10 -- .../review/mobile-pr-sidebar-state.test.ts | 51 ---------- mobile/src/tasks/task-draft-rows.test.tsx | 99 ------------------- .../session/mounted-terminal-lru.test.ts | 22 ----- .../actions/native-chat-quote-focus.test.tsx | 81 --------------- .../WorkspaceVmLifecycleMenuItems.test.ts | 33 ------- .../ipc/pty-transport-user-input.test.ts | 70 ------------- .../newer-daemon-hold-banner.test.tsx | 89 ----------------- .../linear/linear-issue-url-lookup.test.ts | 62 ------------ ...ree-palette-runtime-owner-identity.test.ts | 71 ------------- .../worktree-palette-task-url-match.test.ts | 20 ---- .../mounted-terminal-tab-registry.test.ts | 75 -------------- .../ephemeral-vm-lifecycle.test.ts | 66 ------------- ...ecution-host-registry-vm-lifecycle.test.ts | 74 -------------- .../github/pr-branch-fallback-rule.test.ts | 11 --- 17 files changed, 848 deletions(-) delete mode 100644 mobile/src/source-control/review/mobile-pr-sidebar-state.test.ts delete mode 100644 mobile/src/tasks/task-draft-rows.test.tsx delete mode 100644 mobile/src/terminal/session/mounted-terminal-lru.test.ts delete mode 100644 src/renderer/src/components/native-chat/pane/actions/native-chat-quote-focus.test.tsx delete mode 100644 src/renderer/src/components/sidebar/workspace-actions/sleep/WorkspaceVmLifecycleMenuItems.test.ts delete mode 100644 src/renderer/src/components/terminal-pane/pty-transport/ipc/pty-transport-user-input.test.ts delete mode 100644 src/renderer/src/components/terminal-pane/session-restore/newer-daemon/newer-daemon-hold-banner.test.tsx delete mode 100644 src/renderer/src/lib/work-items/linear/linear-issue-url-lookup.test.ts delete mode 100644 src/renderer/src/lib/workspace/search/worktrees/worktree-palette-runtime-owner-identity.test.ts delete mode 100644 src/renderer/src/runtime/hosted-terminals/mounted-surfaces/mounted-terminal-tab-registry.test.ts diff --git a/config/scripts/ci/sharding/ci-shard-timings.json b/config/scripts/ci/sharding/ci-shard-timings.json index 83b3d91b9f5..787742478fb 100644 --- a/config/scripts/ci/sharding/ci-shard-timings.json +++ b/config/scripts/ci/sharding/ci-shard-timings.json @@ -5922,7 +5922,6 @@ "src/renderer/src/components/terminal-pane/pty-connection/startup/launch-parity-window-tab.test.ts": 17426, "src/renderer/src/components/terminal-pane/layout-serialization.test.ts": 109, "src/renderer/src/components/terminal-pane/pty-transport/legacy-relay/legacy-relay-bridge-transport.test.ts": 358, - "src/renderer/src/components/terminal-pane/session-restore/newer-daemon/newer-daemon-hold-banner.test.tsx": 477, "src/renderer/src/components/terminal-pane/pty-transport/remote-runtime/paired-reconnect-multi-pane-materialization.test.ts": 1040, "src/renderer/src/components/terminal-pane/pty-connection/agent-identity/foreground-process/pane-delayed-agent-startup.test.ts": 285, "src/renderer/src/components/terminal-pane/pty-connection/agent-identity/foreground-process/pane-foreground-agent-tracker.test.ts": 486, @@ -6009,7 +6008,6 @@ "src/renderer/src/components/terminal-pane/pty-transport/ipc/pty-transport-reattach-admission.test.ts": 1636, "src/renderer/src/components/terminal-pane/pty-transport/ipc/pty-transport-recycled-pty-incarnation.test.ts": 787, "src/renderer/src/components/terminal-pane/pty-transport/ipc/pty-transport-spawn-errors.test.ts": 1218, - "src/renderer/src/components/terminal-pane/pty-transport/ipc/pty-transport-user-input.test.ts": 953, "src/renderer/src/components/terminal-pane/input/focus/regular-terminal-focus-ownership.test.ts": 208, "src/renderer/src/components/terminal-pane/pty-connection/output-restore/remote-hidden-output-restore-outcomes.test.ts": 2794, "src/renderer/src/components/terminal-pane/pty-connection/output-restore/remote-hidden-output-restore-unavailable-banner.repro.test.ts": 2355, @@ -6509,7 +6507,6 @@ "src/renderer/src/lib/lazy-with-retry.test.ts": 282, "src/renderer/src/lib/linear-board-drag-payload.test.ts": 58, "src/renderer/src/lib/work-items/linear/linear-issue-context-snapshot.test.ts": 55, - "src/renderer/src/lib/work-items/linear/linear-issue-url-lookup.test.ts": 374, "src/renderer/src/lib/work-items/linear/linear-issue-workspace-attachment.test.ts": 99, "src/renderer/src/lib/link-source-local-open-owner.test.ts": 433, "src/renderer/src/lib/linked-work-item-context.test.ts": 64, @@ -6710,7 +6707,6 @@ "src/renderer/src/lib/workspace/ownership/worktree-operation-route.test.ts": 342, "src/renderer/src/lib/workspace/search/worktrees/worktree-palette-comment-snippet.test.ts": 53, "src/renderer/src/lib/workspace/search/worktrees/worktree-palette-titleless-review.test.ts": 198, - "src/renderer/src/lib/workspace/search/worktrees/worktree-palette-runtime-owner-identity.test.ts": 425, "src/renderer/src/lib/workspace/search/worktrees/worktree-palette-search.test.ts": 206, "src/renderer/src/lib/workspace/search/worktrees/worktree-palette-task-url-match.test.ts": 165, "src/renderer/src/lib/workspace/activation/worktree-reactivation-preserved-pane-replacement.test.ts": 2268, @@ -6735,7 +6731,6 @@ "src/renderer/src/runtime/native-chat/naming/legacy-chat-rename-migration.test.tsx": 3834, "src/renderer/src/runtime/local-structured-session-tabs-sync/local-structured-session-tabs-sync.test.ts": 2643, "src/renderer/src/runtime/web-session-tabs-sync/agent-status/mirrored-agent-status-clock-skew.test.ts": 1839, - "src/renderer/src/runtime/hosted-terminals/mounted-surfaces/mounted-terminal-tab-registry.test.ts": 634, "src/renderer/src/runtime/web-session-tabs-sync/agent-status/paired-reconnect-sidebar-agent-count.test.ts": 1905, "src/renderer/src/runtime/web-session-tabs-sync/closing/paired-terminal-tab-close-environment-removal.test.ts": 1897, "src/renderer/src/runtime/web-session-tabs-sync/closing/paired-terminal-tab-close-reload.test.ts": 1943, diff --git a/mobile/src/components/pr-sidebar/pr-comment-composer-drafts.test.tsx b/mobile/src/components/pr-sidebar/pr-comment-composer-drafts.test.tsx index fa124007eb1..2710d18b844 100644 --- a/mobile/src/components/pr-sidebar/pr-comment-composer-drafts.test.tsx +++ b/mobile/src/components/pr-sidebar/pr-comment-composer-drafts.test.tsx @@ -72,15 +72,6 @@ afterEach(() => { vi.useRealTimers() }) -it('brings an unposted comment back after the app is killed', async () => { - await open() - await type('Can we keep the retry?') - kill() - - await open() - expect(box().value).toBe('Can we keep the retry?') -}) - it('keeps text typed in a reopened composer when the earlier post lands', async () => { let land: (ok: boolean) => void = () => {} onSubmit = () => new Promise((resolve) => (land = resolve)) diff --git a/mobile/src/components/pr-sidebar/pr-create-sheet-drafts.test.tsx b/mobile/src/components/pr-sidebar/pr-create-sheet-drafts.test.tsx index 19c329130c7..3db7251d9fb 100644 --- a/mobile/src/components/pr-sidebar/pr-create-sheet-drafts.test.tsx +++ b/mobile/src/components/pr-sidebar/pr-create-sheet-drafts.test.tsx @@ -125,16 +125,6 @@ afterEach(() => { vi.useRealTimers() }) -it('brings typed fields back after the app is killed instead of the commit seed', async () => { - await open() - expect(titleField()?.props.value).toBe('Seed from commits') - await typeTitle('Fix the login race') - kill() - - await open() - expect(titleField()?.props.value).toBe('Fix the login race') -}) - it('keeps nothing for the next Create once a Create lands after its sheet closed', async () => { let land: (outcome: unknown) => void = () => {} host.create.mockReturnValue(new Promise((resolve) => (land = resolve))) diff --git a/mobile/src/source-control/review/mobile-pr-sidebar-state.test.ts b/mobile/src/source-control/review/mobile-pr-sidebar-state.test.ts deleted file mode 100644 index 66764c5e41a..00000000000 --- a/mobile/src/source-control/review/mobile-pr-sidebar-state.test.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { describe, expect, it } from 'vitest' -import type { PRInfo } from '../../../../src/shared/github/pull-request-types' -import { loadPrBranchReview, type PrSidebarLoadDeps } from './mobile-pr-sidebar-state' - -const linkedPR: PRInfo = { - number: 5, - title: 'Linked', - state: 'open', - url: 'https://github.com/o/r/pull/5', - checksStatus: 'neutral', - updatedAt: '2026-11-09T00:00:00Z', - mergeable: 'MERGEABLE' -} - -describe('loadPrBranchReview', () => { - it('stops falling back to a PR read under a link the workspace no longer has', async () => { - const sent: { fallbackPRNumber?: number | null }[] = [] - const deps: PrSidebarLoadDeps = { - fetchForBranch: async () => ({ - ok: true, - result: { - provider: 'github', - number: 12, - title: 'Branch PR', - state: 'open', - url: 'https://github.com/o/r/pull/12', - status: 'neutral', - updatedAt: '2026-11-09T00:00:00Z', - mergeable: 'MERGEABLE' - } - }), - fetchWorktreeGitHubPRLink: async () => ({ - linkedPR: null, - suppressedGitHubPR: null, - pushTarget: null - }), - clearWorktreeReviewLink: async () => {}, - fetchPRForBranch: async (_worktreeId, args) => { - sent.push(args) - return { ok: true, result: null } - } - } - await loadPrBranchReview(deps, { - worktreeId: 'repo::/w', - branch: 'feature', - headSha: 'head-1', - previous: { kind: 'found', pr: linkedPR, pushTarget: null, linkedPRNumber: 5 } - }) - expect(sent.map((args) => args.fallbackPRNumber)).toEqual([12]) - }) -}) diff --git a/mobile/src/tasks/task-draft-rows.test.tsx b/mobile/src/tasks/task-draft-rows.test.tsx deleted file mode 100644 index a8b07223c71..00000000000 --- a/mobile/src/tasks/task-draft-rows.test.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import { createElement } from 'react' -import { act, create, type ReactTestRenderer } from 'react-test-renderer' -import { afterEach, beforeEach, expect, it, vi } from 'vitest' -import { publishPageDeviceStore } from '../mobile-web-shell/bridge/page-device-store' -import { resetDurableDraftFieldsForTests } from '../drafts/durable-draft-fields' -import { publishInMemoryDeviceStore } from '../drafts/in-memory-device-store.test-support' -import { item } from './github-task-identity.test-fixture' -import type { ActionableTaskItem } from './mobile-tasks-project-workspace-types' -import { useTaskCommentDraft, useTaskCreateDraft } from './task-draft-rows' - -type Screen = { - comment: ReturnType - create: ReturnType -} - -// The shell's draft table outlives the page; nothing else does. -const table = new Map() -let renderer: ReactTestRenderer | null = null -let screen: Screen | null = null - -function Tasks(props: { item: ActionableTaskItem | null }) { - screen = { comment: useTaskCommentDraft(props.item), create: useTaskCreateDraft(true) } - return null -} - -async function open(selected: ActionableTaskItem | null): Promise { - await act(async () => { - if (renderer) { - renderer.update(createElement(Tasks, { item: selected })) - } else { - renderer = create(createElement(Tasks, { item: selected })) - } - await vi.advanceTimersByTimeAsync(0) - }) - return screen! -} - -async function settle(): Promise { - await act(async () => { - await vi.advanceTimersByTimeAsync(1000) - }) -} - -// A kill: the page and its memory go, the store stays. -function kill(): void { - act(() => renderer?.unmount()) - renderer = null - resetDurableDraftFieldsForTests() -} - -beforeEach(() => { - vi.useFakeTimers() - publishInMemoryDeviceStore(table) -}) - -afterEach(() => { - kill() - table.clear() - publishPageDeviceStore(null) - vi.useRealTimers() -}) - -it('brings a comment back to the item it was typed on after a kill', async () => { - const typed = await open(item('acme')) - act(() => typed.comment[1]('Looks good, one nit')) - await settle() - kill() - - expect((await open(item('other'))).comment[0]).toBe('') - expect((await open(item('acme'))).comment[0]).toBe('Looks good, one nit') -}) - -it('does not bring back a comment whose send landed after the user moved on', async () => { - const typed = await open(item('acme')) - act(() => typed.comment[1]('Looks good, one nit')) - await settle() - const clearSent = typed.comment[1] - - await open(item('other')) - act(() => clearSent((current) => (current.trim() === 'Looks good, one nit' ? '' : current))) - await settle() - kill() - - expect((await open(item('acme'))).comment[0]).toBe('') -}) - -it('brings the New issue title and body back after a kill', async () => { - const typed = await open(null) - act(() => { - typed.create.setCreateTitle('Crash on launch') - typed.create.setCreateBody('Steps: open the app') - }) - await settle() - kill() - - const reopened = await open(null) - expect(reopened.create.createTitle).toBe('Crash on launch') - expect(reopened.create.createBody).toBe('Steps: open the app') -}) diff --git a/mobile/src/terminal/session/mounted-terminal-lru.test.ts b/mobile/src/terminal/session/mounted-terminal-lru.test.ts deleted file mode 100644 index 5415418fd78..00000000000 --- a/mobile/src/terminal/session/mounted-terminal-lru.test.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { nextMountedTerminalHandles } from './mounted-terminal-lru' - -describe('mounted terminal LRU', () => { - it('unmounts the least recently visible terminal when a fourth one is shown', () => { - const live = ['a', 'b', 'c', 'd'] - let mounted: readonly string[] = [] - for (const handle of ['a', 'b', 'c', 'b', 'd']) { - mounted = nextMountedTerminalHandles(mounted, [handle], live) - } - expect(mounted).toEqual(['d', 'b', 'c']) - }) - - it('keeps a split pair mounted with one more and drops closed terminals', () => { - expect(nextMountedTerminalHandles(['c', 'b', 'a'], ['a', 'd'], ['a', 'b', 'c', 'd'])).toEqual([ - 'a', - 'd', - 'c' - ]) - expect(nextMountedTerminalHandles(['a', 'b', 'c'], ['a'], ['a', 'c'])).toEqual(['a', 'c']) - }) -}) diff --git a/src/renderer/src/components/native-chat/pane/actions/native-chat-quote-focus.test.tsx b/src/renderer/src/components/native-chat/pane/actions/native-chat-quote-focus.test.tsx deleted file mode 100644 index 62b8840ad1e..00000000000 --- a/src/renderer/src/components/native-chat/pane/actions/native-chat-quote-focus.test.tsx +++ /dev/null @@ -1,81 +0,0 @@ -// @vitest-environment happy-dom -import { useRef } from 'react' -import { act, cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react' -import { afterEach, expect, it, vi } from 'vitest' -import { TooltipProvider } from '@/components/ui/tooltip' -import { AgentLaunchPromptComposer } from '../../../agent/AgentLaunchPromptComposer' -import type { AgentPickerEntry } from '../../../../../../shared/agent-launch/picker/agent-launch-picker' -import type { NativeChatComposerHandle } from '../../composer/native-chat-composer-types' -import { - emptyNativeChatContextMenuActions, - useNativeChatContextMenu -} from './use-native-chat-context-menu' -import { clearNativeChatComposerDraftsForTests } from '../../drafts/native-chat-draft-reset' -import { - promptEditor, - promptValue -} from '../../composer/editor/native-chat-prompt-editor.test-support' - -const entry: AgentPickerEntry = { - agent: 'claude', - label: 'Claude', - rows: [{ agent: 'claude', kind: 'cli', executionHostId: 'local' }], - surfaces: ['chat'], - defaultSurface: 'chat', - terminal: {}, - structured: {}, - catalog: { origin: 'unknown' } -} - -function Chat() { - const rootRef = useRef(null) - const composerRef = useRef(null) - const { menu, onContextMenuCapture, onPointerDownCapture } = useNativeChatContextMenu({ - rootRef, - composerRef, - showTerminalPaneActions: false, - actions: { ...emptyNativeChatContextMenuActions, onPaste: () => {} } - }) - return ( -
-
-

Reply to quote

-
- false} /> - {menu} -
- ) -} - -afterEach(() => { - cleanup() - clearNativeChatComposerDraftsForTests() - window.getSelection()?.removeAllRanges() - vi.restoreAllMocks() -}) - -it('places the caret after the quote when Add to chat closes the context menu', async () => { - const now = vi.spyOn(Date, 'now').mockReturnValue(0) - render(, { wrapper: TooltipProvider }) - const editor = document.querySelector('[contenteditable][aria-multiline="true"]')! - await waitFor(() => expect(promptEditor(editor)).toBeDefined()) - const reply = screen.getByText('Reply to quote') - window.getSelection()!.selectAllChildren(reply) - fireEvent.contextMenu(reply) - await screen.findByRole('menu') - now.mockReturnValue(1000) - fireEvent.click(screen.getByRole('menuitem', { name: 'Add to chat' })) - - expect(promptValue(editor)).toBe('> Reply to quote\n\n') - expect(document.activeElement).toBe(editor) - await act(async () => {}) - expect(document.activeElement).toBe(editor) - expect(promptEditor(editor).state.selection.empty).toBe(true) - expect(promptEditor(editor).state.selection.to).toBe( - promptEditor(editor).state.doc.content.size - 1 - ) -}) diff --git a/src/renderer/src/components/sidebar/workspace-actions/sleep/WorkspaceVmLifecycleMenuItems.test.ts b/src/renderer/src/components/sidebar/workspace-actions/sleep/WorkspaceVmLifecycleMenuItems.test.ts deleted file mode 100644 index 4bf4561bf81..00000000000 --- a/src/renderer/src/components/sidebar/workspace-actions/sleep/WorkspaceVmLifecycleMenuItems.test.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { getVmSuspendRoute, getWorkspaceVmLifecycleAction } from './WorkspaceVmLifecycleMenuItems' - -describe('Suspend VM routing', () => { - it('sleeps active child workspaces first, since they may run on this VM', () => { - expect(getVmSuspendRoute({ hasActiveDescendants: true, hasSleepableActivity: false })).toBe( - 'sleep-subtree' - ) - expect(getVmSuspendRoute({ hasActiveDescendants: true, hasSleepableActivity: true })).toBe( - 'sleep-subtree' - ) - }) - - it('sleeps open panels before the VM, and suspends directly when nothing is open', () => { - expect(getVmSuspendRoute({ hasActiveDescendants: false, hasSleepableActivity: true })).toBe( - 'sleep' - ) - expect(getVmSuspendRoute({ hasActiveDescendants: false, hasSleepableActivity: false })).toBe( - 'suspend' - ) - }) -}) - -describe('VM lifecycle menu item', () => { - it('replaces Sleep only for a VM that can be suspended or resumed', () => { - expect(getWorkspaceVmLifecycleAction(undefined, false)).toBeNull() - expect(getWorkspaceVmLifecycleAction('running', false)).toBe('suspend') - expect(getWorkspaceVmLifecycleAction('running', false, false)).toBeNull() - expect(getWorkspaceVmLifecycleAction('suspended', false)).toBe('resume') - expect(getWorkspaceVmLifecycleAction('suspended', true)).toBe('waking') - expect(getWorkspaceVmLifecycleAction('provisioning', false)).toBeNull() - }) -}) diff --git a/src/renderer/src/components/terminal-pane/pty-transport/ipc/pty-transport-user-input.test.ts b/src/renderer/src/components/terminal-pane/pty-transport/ipc/pty-transport-user-input.test.ts deleted file mode 100644 index a7467d6de5f..00000000000 --- a/src/renderer/src/components/terminal-pane/pty-transport/ipc/pty-transport-user-input.test.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { - createDeferred, - flushAsyncTicks -} from '../../pty-connection/test-fixtures/pty-connection-test-async' -import type { PtyPreconnectInputEntry } from '../input/pty-preconnect-input-buffer' -import { installIpcPtyWindow, restorePtySpecWindow } from './pty-transport-test-harness' - -describe('createIpcPtyTransport: input kind', () => { - const originalWindow: typeof window | undefined = globalThis.window - - beforeEach(() => { - vi.resetModules() - installIpcPtyWindow(originalWindow, {}) - }) - - afterEach(() => { - restorePtySpecWindow(originalWindow) - }) - - it('never merges bytes of different kinds', async () => { - const { createIpcPtyTransport } = await import('./pty-transport') - const transport = createIpcPtyTransport({}) - await transport.connect({ url: '', callbacks: {} }) - - // The first write drains at once; what queues behind it would otherwise coalesce. - transport.sendInput('\x1b[O', 'query-reply') - transport.sendInput('\x1b[I', 'query-reply') - transport.sendInput('a', 'driving') - transport.sendInput('b', 'driving') - - await vi.waitFor(() => - expect(vi.mocked(window.api.pty.write).mock.calls).toEqual([ - ['pty-1', '\x1b[O', 'query-reply'], - ['pty-1', '\x1b[I', 'query-reply'], - ['pty-1', 'ab', 'driving'] - ]) - ) - transport.disconnect() - }) - - it('keeps the kind of input written before the spawn connects and across a remount', async () => { - const spawn = createDeferred>>() - vi.mocked(window.api.pty.spawn).mockReturnValue(spawn.promise) - const { createIpcPtyTransport } = await import('./pty-transport') - const captured: PtyPreconnectInputEntry[] = [] - const predecessor = createIpcPtyTransport({ - bufferInputUntilConnect: true, - onPreconnectInput: (entry) => captured.push(entry) - }) - void predecessor.connect({ url: '', callbacks: {} }) - predecessor.sendInput('typed-early', 'driving') - predecessor.sendInput('startup', 'launch') - predecessor.destroy?.() - - const successor = createIpcPtyTransport({ preconnectInput: captured }) - spawn.resolve({ id: 'pty-1' }) - await successor.connect({ url: '', callbacks: {} }) - await flushAsyncTicks() - - expect(captured).toEqual([ - { data: 'typed-early', kind: 'ordinary', inputKind: 'driving' }, - { data: 'startup', kind: 'ordinary', inputKind: 'launch' } - ]) - expect(vi.mocked(window.api.pty.write).mock.calls).toEqual([ - ['pty-1', 'typed-early', 'driving'], - ['pty-1', 'startup', 'launch'] - ]) - }) -}) diff --git a/src/renderer/src/components/terminal-pane/session-restore/newer-daemon/newer-daemon-hold-banner.test.tsx b/src/renderer/src/components/terminal-pane/session-restore/newer-daemon/newer-daemon-hold-banner.test.tsx deleted file mode 100644 index 3debdb2e201..00000000000 --- a/src/renderer/src/components/terminal-pane/session-restore/newer-daemon/newer-daemon-hold-banner.test.tsx +++ /dev/null @@ -1,89 +0,0 @@ -// @vitest-environment happy-dom - -import { act } from 'react' -import { createRoot, type Root } from 'react-dom/client' -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { makePaneKey } from '../../../../../../shared/workspace-layout/stable-pane-id' -import type { NewerDaemonReopenResult } from '../../../../../../shared/newer-daemon-hold' -import { NewerDaemonHoldBanner } from './NewerDaemonHoldBanner' -import { - holdPaneForNewerDaemon, - newerDaemonHoldStore, - reopenHeldTerminals -} from './newer-daemon-hold-state' - -vi.mock('sonner', () => ({ toast: { success: vi.fn() } })) - -globalThis.IS_REACT_ACT_ENVIRONMENT = true - -const TAB_ID = 'tab-1' -const LEAF_ID = '11111111-1111-4111-8111-111111111111' -const PANE_KEY = makePaneKey(TAB_ID, LEAF_ID) - -let paneElement: HTMLDivElement -let root: Root -let reopenNewerDaemonTerminals: ReturnType Promise>> - -class ResizeObserverStub { - observe(): void {} - disconnect(): void {} -} - -const OTHER_PANE_KEY = makePaneKey('tab-2', LEAF_ID) - -function hold(paneKey: string, resumable: boolean): ReturnType { - const release = vi.fn(() => Promise.resolve({ reopened: true, agentResumed: resumable })) - holdPaneForNewerDaemon(paneKey, { resumable, release }) - return release -} - -async function render(): Promise { - await act(async () => { - root.render() - }) -} - -function button(label: string): HTMLButtonElement { - const match = Array.from(paneElement.querySelectorAll('button')).find( - (candidate) => candidate.textContent?.trim() === label - ) - if (!match) { - throw new Error(`missing ${label} button`) - } - return match -} - -beforeEach(() => { - vi.stubGlobal('ResizeObserver', ResizeObserverStub) - newerDaemonHoldStore.setState({ panes: {} }) - paneElement = document.createElement('div') - document.body.appendChild(paneElement) - root = createRoot(paneElement) - reopenNewerDaemonTerminals = vi.fn(() => Promise.resolve({ reopened: true })) - Object.defineProperty(window, 'api', { - configurable: true, - value: { pty: { reopenNewerDaemonTerminals } } - }) -}) - -afterEach(() => { - act(() => root.unmount()) - paneElement.remove() - vi.unstubAllGlobals() -}) - -describe('NewerDaemonHoldBanner', () => { - it('dismisses only this pane, which stays held and still reopens with the others', async () => { - const release = hold(PANE_KEY, false) - const otherRelease = hold(OTHER_PANE_KEY, false) - await render() - await act(async () => button('Dismiss').click()) - expect(paneElement.textContent).toBe('') - expect(newerDaemonHoldStore.getState().panes[PANE_KEY]?.phase).toBe('held') - expect(newerDaemonHoldStore.getState().panes[OTHER_PANE_KEY]?.dismissed).toBe(false) - expect(release).not.toHaveBeenCalled() - await act(async () => reopenHeldTerminals(PANE_KEY)) - expect(release).toHaveBeenCalledTimes(1) - expect(otherRelease).toHaveBeenCalledTimes(1) - }) -}) diff --git a/src/renderer/src/lib/work-items/linear/linear-issue-url-lookup.test.ts b/src/renderer/src/lib/work-items/linear/linear-issue-url-lookup.test.ts deleted file mode 100644 index fa87679ceee..00000000000 --- a/src/renderer/src/lib/work-items/linear/linear-issue-url-lookup.test.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { - linearIssueReadFixture, - readLinearIssueForTest -} from '@/store/slices/linear/linear-slice-test-harness' -import { describe, expect, it, vi } from 'vitest' -import type { LinearIssue } from '../../../../../shared/linear/issue-types' - -import { lookupLinearIssueUrl } from './linear-issue-url-lookup' - -const intent = { identifier: 'STA-4084', organizationUrlKey: 'stably' } - -function issue(organizationUrlKey = 'stably'): LinearIssue { - return { - id: `${organizationUrlKey}-issue`, - workspaceId: `${organizationUrlKey}-workspace`, - workspaceName: organizationUrlKey, - identifier: 'STA-4084', - title: 'Restore shell integration', - url: `https://linear.app/${organizationUrlKey}/issue/STA-4084/restore-shell-integration`, - state: { name: 'Todo', type: 'unstarted', color: '#999999' }, - team: { id: 'team-sta', name: 'Stably', key: 'STA' }, - labels: [], - labelIds: [], - priority: 2, - updatedAt: '2026-08-12T00:00:00.000Z' - } -} - -describe('Linear issue URL lookup', () => { - it('refreshes status from the source owner when the known workspace is stale', async () => { - const fetchLinearIssue = vi.fn((_identifier: string, workspaceId?: string | null) => - linearIssueReadFixture(async () => - workspaceId === 'remote-stably-workspace' ? issue() : null - ) - ) - - await expect( - readLinearIssueForTest( - lookupLinearIssueUrl({ - intent, - knownStatus: { - viewer: null, - workspaces: [{ id: 'stale-workspace', organizationUrlKey: 'stably' } as never] - }, - sourceContext: null, - fetchLinearIssue, - readLinearStatus: async () => ({ - connected: true, - viewer: null, - workspaces: [ - { - id: 'remote-stably-workspace', - organizationUrlKey: 'stably' - } as never - ] - }) - }) - ) - ).resolves.toMatchObject({ workspaceId: 'stably-workspace' }) - expect(fetchLinearIssue).toHaveBeenCalledTimes(2) - }) -}) diff --git a/src/renderer/src/lib/workspace/search/worktrees/worktree-palette-runtime-owner-identity.test.ts b/src/renderer/src/lib/workspace/search/worktrees/worktree-palette-runtime-owner-identity.test.ts deleted file mode 100644 index e231fb73d39..00000000000 --- a/src/renderer/src/lib/workspace/search/worktrees/worktree-palette-runtime-owner-identity.test.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { expect, it } from 'vitest' -import type { Repo } from '../../../../../../shared/repo-types' -import type { Worktree } from '../../../../../../shared/worktree/types' -import { - buildPaletteWorktreeIndex, - dedupePaletteWorktrees, - resolvePaletteWorktree -} from '../palette-repo-resolution' -import { buildWorktreePaletteDocuments } from './worktree-palette-document' -import { searchWorktreeDocuments } from './worktree-palette-search' -import '../../ownership/unified-tab-host-ownership' - -function makeWorktree(runtimeOwnerEnvironmentId: string, displayName: string): Worktree { - return { - id: 'repo::/srv/same', - repoId: 'repo', - path: '/srv/same', - head: 'abc123', - branch: 'main', - isBare: false, - isMainWorktree: false, - displayName, - comment: '', - linkedIssue: null, - linkedPR: null, - linkedLinearIssue: null, - isArchived: false, - isUnread: false, - isPinned: false, - sortOrder: 0, - lastActivityAt: 0, - hostId: 'ssh:same-private-target', - runtimeOwnerEnvironmentId - } -} - -it('keeps same-target SSH worktrees from separate paired runtimes distinct', () => { - const worktrees = [ - makeWorktree('hub-a', 'AlphaOwner workspace'), - makeWorktree('hub-b', 'BetaOwner workspace') - ] - const repoMap = new Map() - const documents = buildWorktreePaletteDocuments(worktrees, { repoMap }) - const results = searchWorktreeDocuments({ worktrees, query: 'workspace', documents, repoMap }) - const alphaResults = searchWorktreeDocuments({ - worktrees, - query: 'alphaowner', - documents, - repoMap - }) - const betaResults = searchWorktreeDocuments({ - worktrees, - query: 'betaowner', - documents, - repoMap - }) - const index = buildPaletteWorktreeIndex(worktrees) - - expect(dedupePaletteWorktrees(worktrees)).toHaveLength(2) - expect(documents.size).toBe(2) - expect(results.map((result) => result.worktreeHostId)).toEqual(['runtime:hub-a', 'runtime:hub-b']) - expect(alphaResults.map((result) => result.worktreeHostId)).toEqual(['runtime:hub-a']) - expect(betaResults.map((result) => result.worktreeHostId)).toEqual(['runtime:hub-b']) - expect( - results.map( - (result) => - resolvePaletteWorktree(index, result.worktreeId, result.worktreeHostId) - ?.runtimeOwnerEnvironmentId - ) - ).toEqual(['hub-a', 'hub-b']) -}) diff --git a/src/renderer/src/lib/workspace/search/worktrees/worktree-palette-task-url-match.test.ts b/src/renderer/src/lib/workspace/search/worktrees/worktree-palette-task-url-match.test.ts index 76ed3d3af36..9efdb40a52c 100644 --- a/src/renderer/src/lib/workspace/search/worktrees/worktree-palette-task-url-match.test.ts +++ b/src/renderer/src/lib/workspace/search/worktrees/worktree-palette-task-url-match.test.ts @@ -30,26 +30,6 @@ function makeWorktree(overrides: Partial = {}): Worktree { } describe('matchWorktreePaletteTaskUrl', () => { - it('matches a Jira issue URL on its own tenant', () => { - const intent = parseCmdJTaskSourceUrl('https://acme.atlassian.net/browse/PROJ-123') - - expect( - matchWorktreePaletteTaskUrl({ - worktree: makeWorktree({ - linkedWorkItem: { - provider: 'jira', - type: 'issue', - number: 0, - title: 'Tenant scoped', - jiraIdentifier: 'PROJ-123', - url: 'https://acme.atlassian.net/browse/PROJ-123' - } - }), - intent: intent! - }) - ).toMatchObject({ supportingText: { text: 'PROJ-123' } }) - }) - // Jira issue keys repeat across tenants sharing a project key. it('rejects a Jira issue URL from a different tenant with the same issue key', () => { const intent = parseCmdJTaskSourceUrl('https://acme.atlassian.net/browse/PROJ-123') diff --git a/src/renderer/src/runtime/hosted-terminals/mounted-surfaces/mounted-terminal-tab-registry.test.ts b/src/renderer/src/runtime/hosted-terminals/mounted-surfaces/mounted-terminal-tab-registry.test.ts deleted file mode 100644 index a88886716f1..00000000000 --- a/src/renderer/src/runtime/hosted-terminals/mounted-surfaces/mounted-terminal-tab-registry.test.ts +++ /dev/null @@ -1,75 +0,0 @@ -// @vitest-environment happy-dom -import { describe, expect, it, vi } from 'vitest' -import '../../sync-runtime-graph/mobile-session-snapshots' -import { - focusRuntimeTerminalSurface, - registerRuntimeTerminalTab -} from './mounted-terminal-tab-registry' -import '../../sync-runtime-graph/testing/sync-runtime-graph-test-harness' - -const TAB_ID = 'duplicate-tab' -const WORKTREE_A = 'registry-worktree-a' -const WORKTREE_B = 'registry-worktree-b' -const LEAF_A = 'aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa' -const LEAF_B = 'bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb' - -describe('runtime terminal registration ownership', () => { - it('focuses the requested worktree when tab ids collide', () => { - const textareaA = document.createElement('textarea') - const textareaB = document.createElement('textarea') - document.body.append(textareaA, textareaB) - const focusA = vi.fn(() => textareaA.focus()) - const focusB = vi.fn(() => textareaB.focus()) - const container = { querySelector: () => null } - const paneA = { - id: 1, - leafId: LEAF_A, - container, - terminal: { focus: focusA, textarea: textareaA } - } - const paneB = { - id: 1, - leafId: LEAF_B, - container, - terminal: { focus: focusB, textarea: textareaB } - } - const managerA = { - getPanes: () => [paneA], - getActivePane: () => paneA, - getLeafId: () => LEAF_A, - getNumericIdForLeaf: () => 1 - } - const managerB = { - getPanes: () => [paneB], - getActivePane: () => paneB, - getLeafId: () => LEAF_B, - getNumericIdForLeaf: () => 1 - } - const unregisterA = registerRuntimeTerminalTab({ - tabId: TAB_ID, - worktreeId: WORKTREE_A, - getManager: () => managerA as never, - getContainer: () => null, - getPtyIdForPane: () => null, - getTabWideAgentHintLeafId: () => null - }) - const unregisterB = registerRuntimeTerminalTab({ - tabId: TAB_ID, - worktreeId: WORKTREE_B, - getManager: () => managerB as never, - getContainer: () => null, - getPtyIdForPane: () => null, - getTabWideAgentHintLeafId: () => null - }) - try { - expect(focusRuntimeTerminalSurface(TAB_ID, null, WORKTREE_B)).toBe(true) - expect(document.activeElement).toBe(textareaB) - expect(focusRuntimeTerminalSurface(TAB_ID)).toBe(false) - } finally { - textareaA.remove() - textareaB.remove() - unregisterB() - unregisterA() - } - }) -}) diff --git a/src/renderer/src/store/slices/ephemeral-vm/ephemeral-vm-lifecycle.test.ts b/src/renderer/src/store/slices/ephemeral-vm/ephemeral-vm-lifecycle.test.ts index 02f3e17343d..9c8d068b8ad 100644 --- a/src/renderer/src/store/slices/ephemeral-vm/ephemeral-vm-lifecycle.test.ts +++ b/src/renderer/src/store/slices/ephemeral-vm/ephemeral-vm-lifecycle.test.ts @@ -5,7 +5,6 @@ import type { EphemeralVmRuntimeRecord } from '../../../../../shared/ephemeral-v import { createEphemeralVmLifecycleSlice, projectEphemeralVmWorkspaceRuntimes, - selectEphemeralVmWorkspaceFailure, selectEphemeralVmWorkspaceState } from './ephemeral-vm-lifecycle' @@ -39,25 +38,6 @@ function project(records: EphemeralVmRuntimeRecord[]) { } describe('ephemeral VM workspace projection', () => { - it('shows an in-flight write-ahead create as provisioning', () => { - const state = project([record('vm-1', 'provisioning')]) - expect(selectEphemeralVmWorkspaceState(state, 'ws-1')).toBe('provisioning') - }) - - it('shows an interrupted create as setup failed with its catalogued reason', () => { - const state = project([ - record('vm-1', 'failed', { - cleanupLastError: 'Orca stopped while this VM was being created.', - cleanupLastErrorCode: 'provisionInterrupted' - }) - ]) - expect(selectEphemeralVmWorkspaceState(state, 'ws-1')).toBe('provision_failed') - expect(selectEphemeralVmWorkspaceFailure(state, 'ws-1')).toEqual({ - error: 'Orca stopped while this VM was being created.', - errorCode: 'provisionInterrupted' - }) - }) - it('prefers the provisioned VM over a newer result-less failed create', () => { const state = project([ record('vm-new', 'failed', { cleanupLastError: 'boom' }), @@ -66,52 +46,6 @@ describe('ephemeral VM workspace projection', () => { expect(state.ephemeralVmRuntimesByWorkspaceId['ws-1']?.id).toBe('vm-old') expect(selectEphemeralVmWorkspaceState(state, 'ws-1')).toBe('suspended') }) - - it('carries the persisted resume failure code for the row tooltip', () => { - const state = project([ - record('vm-1', 'resume_failed', { - recipeResult: result, - lifecycleLastError: 'Resume exited with code 3.', - lifecycleLastErrorCode: 'resumeExited', - lifecycleLastErrorParams: { exitCode: 3 } - }) - ]) - expect(selectEphemeralVmWorkspaceFailure(state, 'ws-1')).toEqual({ - error: 'Resume exited with code 3.', - errorCode: 'resumeExited', - errorParams: { exitCode: 3 } - }) - }) - - it('ignores a stale lifecycle error once the VM is running again', () => { - const state = project([ - record('vm-1', 'running', { recipeResult: result, lifecycleLastError: 'old' }) - ]) - expect(selectEphemeralVmWorkspaceState(state, 'ws-1')).toBeNull() - expect(selectEphemeralVmWorkspaceFailure(state, 'ws-1')).toBeNull() - }) - - it('hides Suspend VM only when the recipe snapshot has no suspend step', () => { - const recipe = { id: 'cloud', name: 'Cloud', create: 'create' } - expect( - project([record('vm-1', 'running', { recipeResult: result, recipe })]) - .ephemeralVmRuntimesByWorkspaceId['ws-1']?.canSuspend - ).toBe(false) - expect( - project([ - record('vm-1', 'running', { recipeResult: result, recipe: { ...recipe, suspend: 's' } }) - ]).ephemeralVmRuntimesByWorkspaceId['ws-1']?.canSuspend - ).toBe(true) - expect( - project([record('vm-1', 'running', { recipeResult: result })]) - .ephemeralVmRuntimesByWorkspaceId['ws-1']?.canSuspend - ).toBe(true) - // Main skips a VM whose repo it cannot resolve, so the menu must not offer it. - expect( - project([record('vm-1', 'running', { recipeResult: result, repoId: undefined })]) - .ephemeralVmRuntimesByWorkspaceId['ws-1']?.canSuspend - ).toBe(false) - }) }) describe('refreshEphemeralVmRuntimes', () => { diff --git a/src/shared/execution-host/execution-host-registry-vm-lifecycle.test.ts b/src/shared/execution-host/execution-host-registry-vm-lifecycle.test.ts index 2b9de1f0965..968081138ca 100644 --- a/src/shared/execution-host/execution-host-registry-vm-lifecycle.test.ts +++ b/src/shared/execution-host/execution-host-registry-vm-lifecycle.test.ts @@ -1,6 +1,5 @@ import { describe, expect, it } from 'vitest' import { buildExecutionHostRegistry } from './execution-host-registry' -import { getEphemeralVmHostState, getExecutionHostVmStateHealth } from './execution-host-health' import type { EphemeralVmRuntimeStatus } from '../ephemeral-vm-runtimes' import type { RuntimeEnvironmentStatus } from '../remote-runtime/host-status/runtime-host-status' @@ -20,70 +19,6 @@ function registryFor( } describe('VM lifecycle host health', () => { - it.each([ - ['provisioning', false, 'provisioning'], - ['suspended', false, 'suspended'], - ['suspended', true, 'waking'], - ['resume_failed', false, 'resume_failed'], - ['resume_failed', true, 'waking'], - ['suspend_failed', false, null], - ['suspend_failed', true, 'waking'], - ['running', false, null], - ['running', true, null], - ['failed', false, 'provision_failed'], - ['failed', true, 'provision_failed'], - ['provisioning', true, 'provisioning'], - ['cleanup_pending', false, null], - ['cleanup_failed', false, null], - ['cleaned', false, null] - ] as const)('maps %s (waking=%s) to %s', (status, waking, expected) => { - expect(getEphemeralVmHostState(status, waking)).toBe(expected) - }) - - it('maps resume failure to error health', () => { - expect(getExecutionHostVmStateHealth('resume_failed')).toBe('error') - }) - - it('maps a failed or interrupted create to error health', () => { - expect(getExecutionHostVmStateHealth('provision_failed')).toBe('error') - expect(registryFor('failed')).toMatchObject({ health: 'error', vmState: 'provision_failed' }) - }) - - it('reads a suspended VM host as suspended, not disconnected', () => { - expect(registryFor('running')).toMatchObject({ health: 'connecting' }) - expect(registryFor('running')?.vmState).toBeUndefined() - expect(registryFor('suspended')).toMatchObject({ health: 'suspended', vmState: 'suspended' }) - }) - - it('shows waking while a resume is in flight', () => { - expect(registryFor('suspended', { waking: true })).toMatchObject({ - health: 'waking', - vmState: 'waking' - }) - }) - - it('shows resume failure as error with its VM state', () => { - expect(registryFor('resume_failed')).toMatchObject({ - health: 'error', - vmState: 'resume_failed' - }) - }) - - it('drops a stale blocked reason when the VM explains the host', () => { - const host = registryFor('suspended', { - // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the registry reads only snapshot transport/verification/blockedReason. - runtimeStatus: { - snapshot: { - transport: 'disconnected', - verification: 'blocked', - blockedReason: 'access-revoked' - } - } as unknown as RuntimeEnvironmentStatus - }) - expect(host).toMatchObject({ health: 'suspended' }) - expect(host?.blockedReason).toBeUndefined() - }) - it('lets a server that answers outrank a stale suspended record', () => { const host = registryFor('suspended', { // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the registry reads only snapshot transport/verification. @@ -105,13 +40,4 @@ describe('VM lifecycle host health', () => { expect(host).toMatchObject({ health: 'blocked', blockedReason: 'access-revoked' }) expect(host?.vmState).toBeUndefined() }) - - it('ignores VM records whose server is not a registry host', () => { - const hosts = buildExecutionHostRegistry({ - repos: [], - settings: { activeRuntimeEnvironmentId: null }, - ephemeralVmHosts: [{ runtimeEnvironmentId: 'unknown', status: 'suspended', waking: false }] - }) - expect(hosts.map((host) => host.id)).toEqual(['local']) - }) }) diff --git a/src/shared/github/pr-branch-fallback-rule.test.ts b/src/shared/github/pr-branch-fallback-rule.test.ts index e9de7d6f1bc..f456dc77bcf 100644 --- a/src/shared/github/pr-branch-fallback-rule.test.ts +++ b/src/shared/github/pr-branch-fallback-rule.test.ts @@ -17,15 +17,4 @@ describe('resolvePRBranchFallback', () => { expect(fallback).toEqual({ fallbackPRNumber: null, fallbackPRSource: null }) expect(shouldAcceptMergedFallbackPR({ linkedPRNumber: null, ...fallback })).toBe(false) }) - - it('falls back to a cached open PR with merged fallbacks accepted', () => { - const fallback = resolvePRBranchFallback({ - linkedPRNumber: null, - cachedPR: { number: 41, state: 'open', headSha: 'old-head' }, - hintPRNumber: 7, - currentHeadOid: 'new-head' - }) - expect(fallback).toEqual({ fallbackPRNumber: 41, fallbackPRSource: 'pr-cache' }) - expect(shouldAcceptMergedFallbackPR({ linkedPRNumber: null, ...fallback })).toBe(true) - }) })