diff --git a/config/scripts/ci-shard-timings.json b/config/scripts/ci-shard-timings.json index 91585adc093..02a15d77edb 100644 --- a/config/scripts/ci-shard-timings.json +++ b/config/scripts/ci-shard-timings.json @@ -10581,26 +10581,21 @@ "src/renderer/src/lib/browser-link-open-layout-owner.test.ts": 3133, "src/renderer/src/lib/browser-link-open.test.ts": 2215, "src/renderer/src/lib/browser-link-profile-isolation.test.ts": 2511, - "src/renderer/src/lib/browser-page-conversion-history.test.ts": 62, "src/renderer/src/lib/browser-page-palette-activation.test.ts": 1983, "src/renderer/src/lib/browser-palette-page-entries.test.ts": 480, "src/renderer/src/lib/browser-palette-search.test.ts": 467, - "src/renderer/src/lib/browser-workspace-source-resolution.test.ts": 52, "src/renderer/src/lib/browser-workspace-tab-activation.test.ts": 1925, "src/renderer/src/lib/bundled-skill-target.test.ts": 54, "src/renderer/src/lib/check-run-details-log-normalization-performance.test.ts": 846, "src/renderer/src/lib/check-run-details-log-normalization.test.ts": 75, "src/renderer/src/lib/claude-account-switch-sessions.test.ts": 782, "src/renderer/src/lib/claude-session-account-move.test.ts": 1816, - "src/renderer/src/lib/cli-conversation-exit-guidance.test.ts": 202, "src/renderer/src/lib/client-creation-action-policy.test.ts": 1720, - "src/renderer/src/lib/clipboard-file-path.test.ts": 241, "src/renderer/src/lib/clipboard-image-copy.test.ts": 243, "src/renderer/src/lib/cmd-j-host-qualified-candidate-ownership.test.ts": 526, "src/renderer/src/lib/cmd-j-linear-issue-intent.test.ts": 42, "src/renderer/src/lib/code-editor-theme.test.ts": 101, "src/renderer/src/lib/code-host-error-copy.test.ts": 297, - "src/renderer/src/lib/codex-account-display-label.test.ts": 47, "src/renderer/src/lib/codex-pane-selection-lane.test.ts": 543, "src/renderer/src/lib/codex-session-restart-account-recheck.test.ts": 1758, "src/renderer/src/lib/codex-session-restart-shell-flap.test.ts": 1745, @@ -10610,32 +10605,23 @@ "src/renderer/src/lib/comment-body-line-count.test.ts": 84, "src/renderer/src/lib/comment-body-submit-state.test.ts": 55, "src/renderer/src/lib/composer-create-handoff.test.ts": 227, - "src/renderer/src/lib/composer-issue-command.test.ts": 1775, "src/renderer/src/lib/composer-submit-cancellation.test.ts": 41, "src/renderer/src/lib/connection-context.test.ts": 1817, "src/renderer/src/lib/crash-diagnostics.test.ts": 176, "src/renderer/src/lib/create-untitled-markdown.test.ts": 566, - "src/renderer/src/lib/default-creation-host.test.ts": 48, "src/renderer/src/lib/default-terminal-tabs-applied-persist.test.ts": 1828, - "src/renderer/src/lib/desktop-created-server-tabs-switch-off.test.ts": 51, "src/renderer/src/lib/desktop-created-server-tabs.test.ts": 1933, "src/renderer/src/lib/desktop-sidebar-feed.test.ts": 831, - "src/renderer/src/lib/desktop-window-chrome.test.ts": 40, - "src/renderer/src/lib/diff-comment-compat.test.ts": 40, - "src/renderer/src/lib/diff-comments-format.test.ts": 61, "src/renderer/src/lib/direct-ssh-target-scope.test.ts": 48, "src/renderer/src/lib/direct-work-item-setup-callers.test.tsx": 2436, "src/renderer/src/lib/direct-work-item-setup.test.ts": 2072, "src/renderer/src/lib/doc-preview-grants.test.ts": 2094, "src/renderer/src/lib/document-theme.test.ts": 136, - "src/renderer/src/lib/editable-target.test.ts": 175, "src/renderer/src/lib/editor-file-operation-owner.test.ts": 2848, - "src/renderer/src/lib/editor-typography.test.ts": 65, "src/renderer/src/lib/empty-workspace-default-agent-chat.test.ts": 1895, "src/renderer/src/lib/emulator-error-copy.test.ts": 415, "src/renderer/src/lib/ensure-hooks-confirmed.test.ts": 799, "src/renderer/src/lib/ensure-simulator-tab-behavior.test.ts": 3601, - "src/renderer/src/lib/ensure-simulator-tab.test.ts": 44, "src/renderer/src/lib/ephemeral-vm-failed-create-cleanup.test.ts": 40, "src/renderer/src/lib/ephemeral-vm-runtime-cleanup.test.ts": 68, "src/renderer/src/lib/ephemeral-vm-workspace-target.integration.test.ts": 351, diff --git a/src/renderer/src/lib/browser-link-destination.test.ts b/src/renderer/src/lib/browser-link-destination.test.ts index bcfd2b5921a..39f004f535c 100644 --- a/src/renderer/src/lib/browser-link-destination.test.ts +++ b/src/renderer/src/lib/browser-link-destination.test.ts @@ -2,7 +2,6 @@ import { beforeEach, describe, expect, it } from 'vitest' import { buildBrowserPage, buildWorkspaceFromPage } from '@/store/slices/browser-page-records' import { browserLinkPageMatches, - browserLinkUrlIdentity, browserOpenResultForPage, selectBrowserLinkDestination } from './browser-link-destination' @@ -23,119 +22,7 @@ beforeEach(() => { store = createBrowserLinkTestStore() }) -it('normalizes only explicit HTTP URLs and keeps path, query and fragment identity', () => { - expect(browserLinkUrlIdentity('https://EXAMPLE.com:443')).toBe('https://example.com/') - for (const input of [ - 'example.com', - 'search words', - 'file:///tmp/a.html', - 'javascript:alert(1)', - 'https://user:password@example.com/', - 'https://kagi.com/search?token=secret&q=a' - ]) { - expect(browserLinkUrlIdentity(input)).toBeNull() - } - const identities = ['/a', '/a/', '/A', '/a?a=1&b=2', '/a?b=2&a=1', '/a#one', '/a#two'].map( - (path) => browserLinkUrlIdentity(`https://example.com${path}`) - ) - expect(new Set(identities).size).toBe(identities.length) - expect(browserLinkUrlIdentity('http://localhost:3000')).not.toBe( - browserLinkUrlIdentity('http://127.0.0.1:3000') - ) -}) - describe('browser destination scope', () => { - it.each([ - ['worktree', 'local', undefined, 'local'], - ['worktree', 'ssh:target', undefined, 'ssh:target'], - ['worktree', 'runtime:server', undefined, 'runtime:server'], - ['worktree', 'ssh:target', 'server', 'ssh:target'], - ['worktree', 'ssh:target', 'server', 'runtime:server'], - ['folder', 'local', undefined, 'local'], - ['folder', 'runtime:server', undefined, 'runtime:server'] - ] as const)( - 'places a hostless %s source owned by %s (%s) on %s', - (kind, hostId, runtimeOwner, destinationHostId) => { - const workspaceId = kind === 'folder' ? 'folder:legacy' : WORKSPACE - store.setState((state) => ({ - activeWorktreeId: workspaceId, - activeWorkspaceExecutionHostId: hostId, - worktreesByRepo: { - repo1: [ - makeWorktree({ - id: WORKSPACE, - repoId: 'repo1', - hostId, - runtimeOwnerEnvironmentId: runtimeOwner - }) - ] - }, - folderWorkspaces: [makeFolderWorkspace({ id: 'legacy', executionHostId: hostId })], - unifiedTabsByWorktree: { - [workspaceId]: state.unifiedTabsByWorktree[WORKSPACE]!.map((tab) => ({ - ...tab, - worktreeId: workspaceId, - executionHostId: undefined - })) - }, - groupsByWorktree: { [workspaceId]: state.groupsByWorktree[WORKSPACE]! }, - layoutByWorktree: { [workspaceId]: state.layoutByWorktree[WORKSPACE]! }, - activeGroupIdByWorktree: { [workspaceId]: 'c' } - })) - const state = store.getState() - expect( - selectBrowserLinkDestination( - state, - browserLinkTestDestination({ - worktreeId: workspaceId, - executionHostId: destinationHostId - }), - captureLinkOpenIntent(state, workspaceId, 'source') - ) - ).toMatchObject({ - status: 'ready', - groupId: 'b', - match: null - }) - } - ) - - it('places hostless sources beside their group and reuses hostless browser wrappers', () => { - const existing = store.getState().createBrowserTab(WORKSPACE, URL_TO_OPEN, { - targetGroupId: 'b', - executionHostId: 'local', - browserRuntimeEnvironmentId: null, - sessionProfileId: null, - activate: false - }) - store.setState((state) => ({ - unifiedTabsByWorktree: { - [WORKSPACE]: state.unifiedTabsByWorktree[WORKSPACE]!.map((tab) => ({ - ...tab, - executionHostId: undefined - })) - } - })) - const state = store.getState() - const intent = captureLinkOpenIntent(state, WORKSPACE, 'source') - const destination = browserLinkTestDestination() - expect(selectBrowserLinkDestination(state, destination, intent)).toMatchObject({ - status: 'ready', - groupId: 'b', - match: { browserWorkspaceId: existing.id, browserPageId: existing.activePageId } - }) - expect( - browserOpenResultForPage(state, destination, existing.id, existing.activePageId!) - ).toMatchObject({ groupId: 'b', browserWorkspaceId: existing.id }) - - store.setState({ browserTabsByWorktree: {} }) - expect(selectBrowserLinkDestination(store.getState(), destination, intent)).toMatchObject({ - status: 'ready', - groupId: 'b', - match: null - }) - }) - it.each(['listed', 'detected'] as const)( 'refuses hostless ownership for %s twins even when the active workspace names one host', (catalog) => { @@ -339,41 +226,6 @@ describe('browser destination scope', () => { ).toMatchObject({ groupId: 'b', match: null }) }) - it('uses a visible browser and reuses a neighbor when that browser becomes hidden', () => { - const existing = store.getState().createBrowserTab(WORKSPACE, 'https://unrelated.example/', { - targetGroupId: 'a', - executionHostId: 'local', - browserRuntimeEnvironmentId: null, - activate: true - }) - store.setState((state) => ({ - unifiedTabsByWorktree: { - [WORKSPACE]: state.unifiedTabsByWorktree[WORKSPACE]!.map((tab) => - tab.id === 'source' ? { ...tab, groupId: 'b' } : tab - ) - } - })) - const intent = captureLinkOpenIntent(store.getState(), WORKSPACE, 'source') - expect( - selectBrowserLinkDestination(store.getState(), browserLinkTestDestination(), intent) - ).toMatchObject({ groupId: 'a', match: null }) - store.setState((state) => ({ - groupsByWorktree: { - [WORKSPACE]: state.groupsByWorktree[WORKSPACE]!.map((group) => - group.id === 'a' ? { ...group, activeTabId: 'source' } : group - ) - } - })) - expect( - selectBrowserLinkDestination(store.getState(), browserLinkTestDestination(), intent) - ).toMatchObject({ groupId: 'c', match: null }) - expect( - store - .getState() - .browserTabsByWorktree[WORKSPACE]?.some((workspace) => workspace.id === existing.id) - ).toBe(true) - }) - it('requires doc ownership and path identity, excluding current website URLs on mixed containers', () => { const docLocation = { kind: 'workspace-doc' as const, diff --git a/src/renderer/src/lib/browser-link-open.test.ts b/src/renderer/src/lib/browser-link-open.test.ts index 316b6343e6e..b945ec0c446 100644 --- a/src/renderer/src/lib/browser-link-open.test.ts +++ b/src/renderer/src/lib/browser-link-open.test.ts @@ -65,150 +65,6 @@ function browser(groupId: string, url = URL_TO_OPEN) { } describe('browser link placement and exact page activation', () => { - it.each([ - ['a', 'b'], - ['b', 'a'] - ])('opens from %s in %s without adding a third group', async (sourceGroupId, targetGroupId) => { - store.setState((state) => ({ - groupsByWorktree: { - [WORKSPACE]: state.groupsByWorktree[WORKSPACE]!.filter((group) => group.id !== 'c') - }, - unifiedTabsByWorktree: { - [WORKSPACE]: state.unifiedTabsByWorktree[WORKSPACE]!.filter((tab) => tab.groupId !== 'c') - }, - layoutByWorktree: { - [WORKSPACE]: { - type: 'split', - direction: 'horizontal', - first: { type: 'leaf', groupId: 'a' }, - second: { type: 'leaf', groupId: 'b' } - } - }, - activeGroupIdByWorktree: { [WORKSPACE]: sourceGroupId } - })) - const before = store.getState() - const sourceTabId = sourceGroupId === 'a' ? 'source' : 'terminal-b' - const result = await open({ - linkIntent: captureLinkOpenIntent(before, WORKSPACE, sourceTabId), - isCurrentRequest: beginLinkOpenRequest() - }) - expect(result).toMatchObject({ status: 'opened', result: { groupId: targetGroupId } }) - const state = store.getState() - expect(state.groupsByWorktree[WORKSPACE]).toHaveLength(2) - expect(state.layoutByWorktree[WORKSPACE]).toEqual(before.layoutByWorktree[WORKSPACE]) - expect( - state.groupsByWorktree[WORKSPACE]?.find((g) => g.id === sourceGroupId)?.activeTabId - ).toBe(sourceTabId) - expect( - state.groupsByWorktree[WORKSPACE]?.find((g) => g.id === targetGroupId)?.tabOrder - ).toContain(targetGroupId === 'a' ? 'source' : 'terminal-b') - }) - - it('creates a second group for a new URL when the source is alone', async () => { - store.setState((state) => ({ - groupsByWorktree: { [WORKSPACE]: [state.groupsByWorktree[WORKSPACE]![0]!] }, - unifiedTabsByWorktree: { - [WORKSPACE]: state.unifiedTabsByWorktree[WORKSPACE]!.filter((tab) => tab.groupId === 'a') - }, - layoutByWorktree: { [WORKSPACE]: { type: 'leaf', groupId: 'a' } }, - activeGroupIdByWorktree: { [WORKSPACE]: 'a' } - })) - expect(await open()).toMatchObject({ status: 'opened' }) - expect(store.getState().groupsByWorktree[WORKSPACE]).toHaveLength(2) - expect( - store.getState().groupsByWorktree[WORKSPACE]?.find((g) => g.id === 'a')?.activeTabId - ).toBe('source') - }) - - it('opens beside the source despite focus in a nonadjacent group, returning the real identities', async () => { - const result = await open() - expect(result.status).toBe('opened') - if (result.status !== 'opened') { - throw new Error('Expected open') - } - const state = store.getState() - expect(result.result.groupId).toBe('b') - expect(state.groupsByWorktree[WORKSPACE]?.find((group) => group.id === 'a')?.activeTabId).toBe( - 'source' - ) - expect( - state.groupsByWorktree[WORKSPACE]?.find((group) => group.id === result.result.groupId) - ?.activeTabId - ).toBe(result.result.unifiedTabId) - expect(state.browserPagesByWorkspace[result.result.browserWorkspaceId]?.[0]?.id).toBe( - result.result.browserPageId - ) - expect(state.pendingAddressBarFocusByPageId).toEqual({}) - expect(requestBrowserFocus).toHaveBeenLastCalledWith({ - pageId: result.result.browserPageId, - target: 'webview' - }) - expect(state.groupsByWorktree[WORKSPACE]).toHaveLength(3) - expect(state.layoutByWorktree[WORKSPACE]).toMatchObject({ - first: { type: 'leaf', groupId: 'a' }, - second: { first: { type: 'leaf', groupId: 'b' } } - }) - expect( - state.groupsByWorktree[WORKSPACE]?.find((group) => group.id === 'b')?.tabOrder - ).toContain('terminal-b') - }) - - it('uses a distant visible browser and preserves the neighboring terminal', async () => { - store.getState().createBrowserTab(WORKSPACE, 'https://unrelated.example/', { - targetGroupId: 'c', - executionHostId: 'local', - browserRuntimeEnvironmentId: null, - activate: true - }) - expect(await open()).toMatchObject({ status: 'opened', result: { groupId: 'c' } }) - expect( - store.getState().groupsByWorktree[WORKSPACE]?.find((group) => group.id === 'a')?.activeTabId - ).toBe('source') - }) - - it('prefers the focused visible browser, otherwise the most recently focused one', async () => { - const left = browser('b', 'https://left.example/') - const right = browser('c', 'https://right.example/') - store.setState((state) => ({ - unifiedTabsByWorktree: { - [WORKSPACE]: state.unifiedTabsByWorktree[WORKSPACE]!.map((tab) => ({ - ...tab, - lastFocusedAt: tab.entityId === left.id ? 200 : 100 - })) - }, - groupsByWorktree: { - [WORKSPACE]: state.groupsByWorktree[WORKSPACE]!.map((group) => ({ - ...group, - activeTabId: - state.unifiedTabsByWorktree[WORKSPACE]!.find( - (tab) => - tab.entityId === (group.id === 'b' ? left.id : right.id) && tab.groupId === group.id - )?.id ?? group.activeTabId - })) - }, - activeGroupIdByWorktree: { [WORKSPACE]: 'c' } - })) - expect(await open()).toMatchObject({ status: 'opened', result: { groupId: 'c' } }) - store.getState().focusGroup(WORKSPACE, 'a') - const destination = browserLinkTestDestination({ - resource: { kind: 'website', url: 'https://another.example/' } - }) - expect( - selectBrowserLinkDestination(store.getState(), destination, context().linkIntent) - ).toMatchObject({ groupId: 'c' }) - store.setState((state) => ({ - unifiedTabsByWorktree: { - [WORKSPACE]: state.unifiedTabsByWorktree[WORKSPACE]!.map((tab) => ({ - ...tab, - lastFocusedAt: tab.entityId === left.id ? 1000 : 100 - })) - } - })) - expect( - selectBrowserLinkDestination(store.getState(), destination, context().linkIntent) - ).toMatchObject({ groupId: 'b' }) - }) - it('prefers an old inactive nonadjacent match over a nearby browser without navigation or creation', async () => { store.getState().createBrowserTab(WORKSPACE, 'https://nearby.example/', { targetGroupId: 'b', @@ -276,17 +132,6 @@ describe('browser link placement and exact page activation', () => { expect(store.getState().activeGroupIdByWorktree[WORKSPACE]).toBe('c') }) - it('reuses an exact match in the source group without creating another page', async () => { - const existing = browser('a') - const result = await open() - expect(result).toMatchObject({ status: 'opened', result: { groupId: 'a' } }) - if (result.status !== 'opened') { - throw new Error('Expected open') - } - expect(result.result.browserPageId).toBe(existing.activePageId) - expect(store.getState().browserPagesByWorkspace[existing.id]?.[0]?.url).toBe(URL_TO_OPEN) - }) - it('removes a newly created tab after an activation error while preserving existing tabs', async () => { const existing = browser('c', 'https://unrelated.example/') const before = store.getState() @@ -308,49 +153,6 @@ describe('browser link placement and exact page activation', () => { expect(state.recentlyClosedBrowserTabsByWorktree[WORKSPACE] ?? []).toEqual([]) }) - it('retains a successfully created background tab without activating it', async () => { - const outcome = await openLocalBrowserLink({ - destination: browserLinkTestDestination(), - context: context(), - url: URL_TO_OPEN, - options: { activate: false }, - ownsRoute: () => true - }) - - expect(outcome.status).toBe('opened') - if (outcome.status !== 'opened') { - throw new Error('Expected open') - } - const state = store.getState() - expect(state.browserTabsByWorktree[WORKSPACE]?.map((tab) => tab.id)).toEqual([ - outcome.result.browserWorkspaceId - ]) - expect(state.browserPagesByWorkspace[outcome.result.browserWorkspaceId]?.[0]?.url).toBe( - URL_TO_OPEN - ) - expect(state.activeGroupIdByWorktree[WORKSPACE]).toBe('c') - }) - - it('uses the selected matching wrapper, then active page, instead of recency', async () => { - browser('b') - const selected = browser('b') - const second = store.getState().createBrowserPage(selected.id, URL_TO_OPEN, { - activate: true, - browserRuntimeEnvironmentId: null - }) - const wrapper = store - .getState() - .unifiedTabsByWorktree[WORKSPACE]?.find((tab) => tab.entityId === selected.id) - if (!wrapper || !second) { - throw new Error('Missing fixture') - } - store.getState().activateTab(wrapper.id, { worktreeId: WORKSPACE }) - expect(await open()).toMatchObject({ - status: 'opened', - result: { unifiedTabId: wrapper.id, browserPageId: second.id } - }) - }) - it('follows source movement while an explicit request stays pinned', async () => { const automatic = context() store.setState({ @@ -439,43 +241,9 @@ describe('browser link placement and exact page activation', () => { .unifiedTabsByWorktree[WORKSPACE]?.some((tab) => tab.contentType === 'browser') ).toBe(false) }) - - it('reuses in one group and does not create a split', async () => { - store.setState({ - groupsByWorktree: { [WORKSPACE]: [store.getState().groupsByWorktree[WORKSPACE]![0]!] }, - layoutByWorktree: { [WORKSPACE]: { type: 'leaf', groupId: 'a' } } - }) - const existing = browser('a') - expect(await open()).toMatchObject({ - status: 'opened', - result: { browserWorkspaceId: existing.id, groupId: 'a' } - }) - expect(store.getState().groupsByWorktree[WORKSPACE]).toHaveLength(1) - }) - - it('supersedes a pending operation before creation', async () => { - const captured = context() - beginLinkOpenRequest() - expect(await open(captured)).toEqual({ status: 'superseded' }) - expect(store.getState().browserPagesByWorkspace).toEqual({}) - }) }) describe('existing open routes with link intent', () => { - it('opens local HTTP through the managed route and reports the exact destination', async () => { - expect( - await openWorkspaceBrowserTabForLink({ - workspaceId: WORKSPACE, - url: URL_TO_OPEN, - intent: { kind: 'url' }, - linkContext: context() - }) - ).toMatchObject({ - status: 'opened', - result: { worktreeId: WORKSPACE, executionHostId: 'local' } - }) - }) - it('refuses a source owner that changed while HTTP routing was pending', async () => { const captured = context() const host = 'ssh:changed-owner' diff --git a/src/renderer/src/lib/browser-page-conversion-history.test.ts b/src/renderer/src/lib/browser-page-conversion-history.test.ts deleted file mode 100644 index 16899f961b2..00000000000 --- a/src/renderer/src/lib/browser-page-conversion-history.test.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest' -import { - advanceAcrossBrowserPageConversion, - returnAcrossBrowserPageConversion -} from './browser-page-conversion-history' - -const mocks = vi.hoisted(() => ({ - convertBrowserPage: vi.fn(), - convertBrowserPageToWorkspaceDoc: vi.fn() -})) -vi.mock('@/store', () => ({ - useAppStore: { getState: () => ({ convertBrowserPage: mocks.convertBrowserPage }) } -})) -vi.mock('@/lib/file-preview', () => ({ - convertBrowserPageToWorkspaceDoc: mocks.convertBrowserPageToWorkspaceDoc -})) - -beforeEach(() => { - vi.clearAllMocks() -}) - -// Carry ownership back or Back would move remote browsing onto this desktop. -describe('returnAcrossBrowserPageConversion', () => { - it('passes the recorded runtime ownership through to the rebuilt web page', () => { - returnAcrossBrowserPageConversion('page-1', { - kind: 'url', - url: 'https://remote.example/', - browserRuntimeEnvironmentId: 'env-1' - }) - - expect(mocks.convertBrowserPage).toHaveBeenCalledWith( - 'page-1', - { kind: 'web', url: 'https://remote.example/', browserRuntimeEnvironmentId: 'env-1' }, - { leg: 'history-return' } - ) - }) - - it('says inferred explicitly when the origin recorded no ownership', () => { - returnAcrossBrowserPageConversion('page-1', { kind: 'url', url: 'https://remote.example/' }) - - const target = mocks.convertBrowserPage.mock.calls[0]?.[1] as Record - // Property present with undefined — the plan's "worktree-inferred", never client-local. - expect('browserRuntimeEnvironmentId' in target).toBe(true) - expect(target.browserRuntimeEnvironmentId).toBeUndefined() - }) - - it('routes a workspace-doc origin through the doc door as a return leg', () => { - const docLocation = { kind: 'workspace-doc' as const, worktreeId: 'wt-1', filePath: '/a.html' } - returnAcrossBrowserPageConversion('page-1', { kind: 'workspace-doc', docLocation }) - - expect(mocks.convertBrowserPageToWorkspaceDoc).toHaveBeenCalledWith('page-1', docLocation, { - leg: 'history-return' - }) - expect(mocks.convertBrowserPage).not.toHaveBeenCalled() - }) -}) - -describe('advanceAcrossBrowserPageConversion', () => { - it('re-crosses to a web target with ownership intact, as an advance leg', () => { - advanceAcrossBrowserPageConversion('page-1', { - kind: 'url', - url: 'https://remote.example/', - browserRuntimeEnvironmentId: 'env-1' - }) - - expect(mocks.convertBrowserPage).toHaveBeenCalledWith( - 'page-1', - { kind: 'web', url: 'https://remote.example/', browserRuntimeEnvironmentId: 'env-1' }, - { leg: 'history-advance' } - ) - }) - - it('re-crosses to a workspace-doc target as an advance leg', () => { - const docLocation = { kind: 'workspace-doc' as const, worktreeId: 'wt-1', filePath: '/a.html' } - advanceAcrossBrowserPageConversion('page-1', { kind: 'workspace-doc', docLocation }) - - expect(mocks.convertBrowserPageToWorkspaceDoc).toHaveBeenCalledWith('page-1', docLocation, { - leg: 'history-advance' - }) - expect(mocks.convertBrowserPage).not.toHaveBeenCalled() - }) -}) diff --git a/src/renderer/src/lib/browser-page-palette-activation.test.ts b/src/renderer/src/lib/browser-page-palette-activation.test.ts index ade7163abc1..ce00ec88c85 100644 --- a/src/renderer/src/lib/browser-page-palette-activation.test.ts +++ b/src/renderer/src/lib/browser-page-palette-activation.test.ts @@ -149,32 +149,6 @@ describe('activateBrowserPagePaletteResult', () => { seedStore() }) - it('activates the browser workspace and page', () => { - expect(activateBrowserPagePaletteResult(target)).toEqual({ - status: 'activated', - pageId: 'page-1', - focusTarget: 'webview' - }) - - const state = useAppStore.getState() - expect(mocks.activateAndRevealWorkspace).toHaveBeenCalledWith('wt-1', {}) - expect(state.activeBrowserTabId).toBe('ws-1') - expect(state.activeTabType).toBe('browser') - expect(state.browserTabsByWorktree['wt-1'][0].activePageId).toBe('page-1') - }) - - it('threads the worktree execution host into worktree activation', () => { - seedStore({ - worktreesByRepo: { 'repo-1': [makeWorktree({ hostId: 'ssh:host-1' })] } - }) - - activateBrowserPagePaletteResult(target) - - expect(mocks.activateAndRevealWorkspace).toHaveBeenCalledWith('wt-1', { - executionHostId: 'ssh:host-1' - }) - }) - it('activates an SSH worktree through its paired-runtime owner alias', () => { seedStore({ worktreesByRepo: { @@ -273,55 +247,6 @@ describe('activateBrowserPagePaletteResult', () => { focusTarget: 'address-bar' }) }) - - it('reports a missing page or workspace as a stale target', () => { - seedStore({ browserPagesByWorkspace: {} }) - expect(activateBrowserPagePaletteResult(target)).toEqual({ - status: 'failed', - reason: 'missing-page' - }) - - seedStore({ browserTabsByWorktree: {} }) - expect(activateBrowserPagePaletteResult(target)).toEqual({ - status: 'failed', - reason: 'missing-page' - }) - - expect(mocks.activateAndRevealWorkspace).not.toHaveBeenCalled() - }) - - // A live page in a dead workspace is a different story than a dead page. - it('reports an absent worktree as a missing workspace', () => { - seedStore({ worktreesByRepo: {} }) - - expect(activateBrowserPagePaletteResult(target)).toEqual({ - status: 'failed', - reason: 'missing-worktree' - }) - expect(mocks.activateAndRevealWorkspace).not.toHaveBeenCalled() - }) - - // Workspace removal also purges pages; check it first to report the actual missing owner. - it('reports a deleted worktree as a missing workspace once its pages are purged', () => { - seedStore({ - worktreesByRepo: {}, - browserTabsByWorktree: {}, - browserPagesByWorkspace: {} - }) - - expect(activateBrowserPagePaletteResult(target)).toEqual({ - status: 'failed', - reason: 'missing-worktree' - }) - expect(mocks.activateAndRevealWorkspace).not.toHaveBeenCalled() - }) - - it('reports blocked activation without claiming the workspace disappeared', () => { - mocks.activateAndRevealWorkspace.mockReturnValue(false) - - expect(activateBrowserPagePaletteResult(target)).toEqual({ status: 'blocked' }) - expect(useAppStore.getState().activeBrowserTabId).toBeNull() - }) }) // Preserve cross-column focus through setActiveBrowserTab → activateTab. @@ -367,12 +292,6 @@ describe('activateBrowserPagePaletteResult group focus', () => { expect(state.groupsByWorktree['wt-1'][1].activeTabId).toBe('unified-browser-1') }) - it('leaves the focused group unchanged when the page is already in it', () => { - expect(activateBrowserPagePaletteResult(target).status).toBe('activated') - - expect(useAppStore.getState().activeGroupIdByWorktree['wt-1']).toBe('group-1') - }) - // Without a unified tab, success would leave the previous tab visible. it('fails instead of activating when no unified tab backs the browser workspace', () => { seedStore({ unifiedTabsByWorktree: {}, groupsByWorktree: { 'wt-1': [] } }) diff --git a/src/renderer/src/lib/browser-palette-page-entries.test.ts b/src/renderer/src/lib/browser-palette-page-entries.test.ts index 96dc0b6472c..1050121cae3 100644 --- a/src/renderer/src/lib/browser-palette-page-entries.test.ts +++ b/src/renderer/src/lib/browser-palette-page-entries.test.ts @@ -130,17 +130,6 @@ function buildFixture( } describe('buildSearchableBrowserPages', () => { - it('treats an unstamped legacy worktree as local when its tab is stamped', () => { - expect( - buildFixture({ - worktrees: [worktreeA], - unifiedTabsByWorktree: { - 'wt-1': [browserUnifiedTab('tab-local', 'ws-1', 'wt-1', 'local')] - } - }).map((entry) => entry.workspace.id) - ).toEqual(['ws-1', 'ws-1', 'ws-2']) - }) - it('keeps same-id browser tabs isolated by execution host', () => { const sharedId = 'repo-shared::/workspace' const local = makeWorktree({ id: sharedId, hostId: 'local', displayName: 'Local workspace' }) @@ -276,161 +265,6 @@ describe('buildSearchableBrowserPages', () => { ).toEqual([]) }) - it('builds one entry per page across every workspace in a worktree', () => { - const entries = buildFixture() - - expect(entries.map((entry) => entry.page.id)).toEqual(['page-1', 'page-2', 'page-3', 'page-4']) - expect(entries.map((entry) => entry.workspace.id)).toEqual(['ws-1', 'ws-1', 'ws-2', 'ws-3']) - expect(entries.map((entry) => entry.repoName)).toEqual([ - 'repo/one', - 'repo/one', - 'repo/one', - 'repo/two' - ]) - expect(entries.map((entry) => entry.worktreeSortIndex)).toEqual([0, 0, 0, 1]) - }) - - it('marks isCurrentPage only for the active page of the active browser workspace', () => { - expect(buildFixture().map((entry) => entry.isCurrentPage)).toEqual([true, false, false, false]) - }) - - it('marks no page current when the active tab type is not browser', () => { - expect(buildFixture({ activeTabType: 'terminal' }).map((entry) => entry.isCurrentPage)).toEqual( - [false, false, false, false] - ) - }) - - it('marks isCurrentWorktree only for the active worktree', () => { - expect(buildFixture().map((entry) => entry.isCurrentWorktree)).toEqual([ - true, - true, - true, - false - ]) - }) - - it('returns an empty array for a worktree with no browser workspaces', () => { - expect( - buildSearchableBrowserPages({ - worktrees: [worktreeA], - repoMap, - worktreeOrder, - browserTabsByWorktree: {}, - browserPagesByWorkspace: {}, - activeBrowserTabId: null, - activeWorktreeId: 'wt-1', - activeTabType: 'terminal' - }) - ).toEqual([]) - }) - - it('falls back to MAX_SAFE_INTEGER sort index for worktrees outside the order map', () => { - const entries = buildSearchableBrowserPages({ - worktrees: [worktreeA], - repoMap, - worktreeOrder: new Map(), - browserTabsByWorktree: { 'wt-1': [makeWorkspace()] }, - browserPagesByWorkspace: { 'ws-1': [makePage()] }, - activeBrowserTabId: null, - activeWorktreeId: null, - activeTabType: 'browser' - }) - - expect(entries[0].worktreeSortIndex).toBe(Number.MAX_SAFE_INTEGER) - }) - - it('reads page recency from the owning workspace unified tab, clamped to page age', () => { - function browserTab(overrides: Partial): Tab { - return { - id: 'tab-ws-1', - entityId: 'ws-1', - groupId: 'group-1', - worktreeId: 'wt-1', - contentType: 'browser', - label: 'Example', - customLabel: null, - color: null, - sortOrder: 0, - createdAt: 0, - ...overrides - } - } - - const [unfocused] = buildSearchableBrowserPages({ - worktrees: [worktreeA], - repoMap, - worktreeOrder, - browserTabsByWorktree: { 'wt-1': [makeWorkspace()] }, - browserPagesByWorkspace: { 'ws-1': [makePage()] }, - unifiedTabsByWorktree: { 'wt-1': [browserTab({})] }, - activeBrowserTabId: null, - activeWorktreeId: null, - activeTabType: 'browser' - }) - expect(unfocused.lastActiveAt).toBeNull() - - const entries = buildSearchableBrowserPages({ - worktrees: [worktreeA], - repoMap, - worktreeOrder, - browserTabsByWorktree: { 'wt-1': [makeWorkspace({ pageIds: ['page-1', 'page-2'] })] }, - browserPagesByWorkspace: { - 'ws-1': [makePage(), makePage({ id: 'page-2', createdAt: 9000 })] - }, - unifiedTabsByWorktree: { 'wt-1': [browserTab({ lastFocusedAt: 4000 })] }, - activeBrowserTabId: null, - activeWorktreeId: null, - activeTabType: 'browser' - }) - - expect(entries.map((entry) => entry.lastActiveAt)).toEqual([4000, 9000]) - expect(entries.map((entry) => entry.lastFocusedAt)).toEqual([4000, undefined]) - }) - - it('moves the workspace-focus proxy when the active browser page changes', () => { - const browserTab: Tab = { - id: 'tab-ws-1', - entityId: 'ws-1', - groupId: 'group-1', - worktreeId: 'wt-1', - contentType: 'browser', - label: 'Example', - customLabel: null, - color: null, - sortOrder: 0, - createdAt: 0, - lastFocusedAt: 8_000 - } - const pages = [makePage({ createdAt: 1_000 }), makePage({ id: 'page-2', createdAt: 2_000 })] - const build = (activePageId: string) => - buildSearchableBrowserPages({ - worktrees: [worktreeA], - repoMap, - worktreeOrder, - browserTabsByWorktree: { - 'wt-1': [makeWorkspace({ activePageId, pageIds: ['page-1', 'page-2'] })] - }, - browserPagesByWorkspace: { 'ws-1': pages }, - unifiedTabsByWorktree: { 'wt-1': [browserTab] }, - activeBrowserTabId: null, - activeWorktreeId: null, - activeTabType: 'browser' - }) - - expect(build('page-1').map((entry) => entry.lastActiveAt)).toEqual([8_000, 2_000]) - expect(build('page-2').map((entry) => entry.lastActiveAt)).toEqual([1_000, 8_000]) - expect(build('page-1').map((entry) => entry.lastFocusedAt)).toEqual([8_000, undefined]) - expect(build('page-2').map((entry) => entry.lastFocusedAt)).toEqual([undefined, 8_000]) - }) - - it('feeds Cmd+J browser search the same ranking as the inline builder did', () => { - const results = searchBrowserPages(buildFixture(), 'docs') - - // Primary title proofs lead URL-only proofs even across worktrees. - expect(results.map((result) => result.pageId)).toEqual(['page-1', 'page-4', 'page-2', 'page-3']) - expect(results[0].isCurrentPage).toBe(true) - }) - // The index keeps the first tab per workspace ID; prevent duplicates from hiding a later owned tab. it('omits a workspace claimed by more than one unified browser tab', () => { const entries = buildFixture({ diff --git a/src/renderer/src/lib/browser-palette-search.test.ts b/src/renderer/src/lib/browser-palette-search.test.ts index cd56f23851e..e2acf5d4b1a 100644 --- a/src/renderer/src/lib/browser-palette-search.test.ts +++ b/src/renderer/src/lib/browser-palette-search.test.ts @@ -5,8 +5,6 @@ import { BROWSER_PALETTE_QUERY_MAX_BYTES, buildSearchableBrowserPageDocument, searchBrowserPages, - formatBrowserPaletteUrl, - isBlankBrowserUrl, type SearchableBrowserPage } from './browser-palette-search' @@ -74,372 +72,6 @@ function makeEntry(entry: Omit): SearchableBr } describe('browser-palette-search', () => { - it('formats browser urls without protocol for palette display', () => { - expect(formatBrowserPaletteUrl('https://example.com/docs?q=1#hash')).toBe( - 'example.com/docs?q=1#hash' - ) - }) - - it('stamps the row execution host so activation never resolves by id alone', () => { - // IDs repeat across hosts; retain the row’s owner so activation cannot open its same-ID twin. - const [result] = searchBrowserPages( - [ - makeEntry({ - page: makePage({ id: 'page-1', title: 'Docs' }), - workspace: makeWorkspace({ id: 'ws-1' }), - worktree: makeWorktree({ id: 'shared', hostId: 'ssh:box' }), - repoName: 'repo/one', - worktreeSortIndex: 0, - isCurrentPage: false, - isCurrentWorktree: false - }) - ], - '' - ) - expect(result.executionHostId).toBe('ssh:box') - }) - - it('carries the page favicon into palette results', () => { - const faviconUrl = 'https://example.com/favicon.ico' - const [result] = searchBrowserPages( - [ - makeEntry({ - page: makePage({ faviconUrl }), - workspace: makeWorkspace(), - worktree: makeWorktree(), - repoName: 'repo/one', - worktreeSortIndex: 0, - isCurrentPage: false, - isCurrentWorktree: false - }) - ], - '' - ) - - expect(result.faviconUrl).toBe(faviconUrl) - }) - - it('keeps empty-query ordering deterministic and context-first', () => { - const results = searchBrowserPages( - [ - makeEntry({ - page: makePage({ id: 'page-current', title: 'Current Page' }), - workspace: makeWorkspace({ id: 'ws-current', activePageId: 'page-current' }), - worktree: makeWorktree({ id: 'wt-current', displayName: 'Current WT' }), - repoName: 'repo/current', - worktreeSortIndex: 1, - isCurrentPage: true, - isCurrentWorktree: true - }), - makeEntry({ - page: makePage({ - id: 'page-sibling', - workspaceId: 'ws-sibling', - worktreeId: 'wt-current', - title: 'Sibling Page', - url: 'https://example.com/sibling' - }), - workspace: makeWorkspace({ - id: 'ws-sibling', - worktreeId: 'wt-current', - activePageId: 'page-sibling' - }), - worktree: makeWorktree({ id: 'wt-current', displayName: 'Current WT' }), - repoName: 'repo/current', - worktreeSortIndex: 1, - isCurrentPage: false, - isCurrentWorktree: true - }), - makeEntry({ - page: makePage({ - id: 'page-other', - workspaceId: 'ws-other', - worktreeId: 'wt-other', - title: 'Other Page', - url: 'https://example.com/other' - }), - workspace: makeWorkspace({ - id: 'ws-other', - worktreeId: 'wt-other', - activePageId: 'page-other' - }), - worktree: makeWorktree({ id: 'wt-other', displayName: 'Other WT', repoId: 'repo-2' }), - repoName: 'repo/other', - worktreeSortIndex: 2, - isCurrentPage: false, - isCurrentWorktree: false - }) - ], - '' - ) - - expect(results.map((result) => result.pageId)).toEqual([ - 'page-current', - 'page-sibling', - 'page-other' - ]) - }) - - it('searches against page titles before worktree metadata', () => { - const results = searchBrowserPages( - [ - makeEntry({ - page: makePage({ id: 'page-1', title: 'Design Spec' }), - workspace: makeWorkspace({ id: 'ws-1' }), - worktree: makeWorktree({ id: 'wt-1', displayName: 'Unrelated' }), - repoName: 'repo/one', - worktreeSortIndex: 1, - isCurrentPage: false, - isCurrentWorktree: false - }), - makeEntry({ - page: makePage({ - id: 'page-2', - workspaceId: 'ws-2', - worktreeId: 'wt-2', - title: 'Home', - url: 'https://example.com/home' - }), - workspace: makeWorkspace({ id: 'ws-2', worktreeId: 'wt-2', activePageId: 'page-2' }), - worktree: makeWorktree({ id: 'wt-2', repoId: 'repo-2', displayName: 'Design Review' }), - repoName: 'repo/two', - worktreeSortIndex: 2, - isCurrentPage: false, - isCurrentWorktree: false - }) - ], - 'design' - ) - - expect(results).toHaveLength(2) - expect(results[0].pageId).toBe('page-1') - expect(results[0].titleRanges).toEqual([{ start: 0, end: 6 }]) - expect(results[1].worktreeRanges).toEqual([{ start: 0, end: 6 }]) - }) - - it('matches a two-token query across the page title and the url host', () => { - const results = searchBrowserPages( - [ - makeEntry({ - page: makePage({ - id: 'page-1', - title: 'Design Spec', - url: 'https://staging.example.com/docs' - }), - workspace: makeWorkspace({ id: 'ws-1' }), - worktree: makeWorktree({ id: 'wt-1', displayName: 'Unrelated' }), - repoName: 'repo/one', - worktreeSortIndex: 1, - isCurrentPage: false, - isCurrentWorktree: false - }) - ], - 'design staging' - ) - - expect(results).toHaveLength(1) - expect(results[0].titleRanges).toEqual([{ start: 0, end: 6 }]) - expect(results[0].secondaryRanges).toEqual([{ start: 0, end: 7 }]) - }) - - it('drops a row when only one token of a multi-keyword query lands', () => { - const results = searchBrowserPages( - [ - makeEntry({ - page: makePage({ id: 'page-1', title: 'Design Spec' }), - workspace: makeWorkspace({ id: 'ws-1' }), - worktree: makeWorktree({ id: 'wt-1', displayName: 'Unrelated' }), - repoName: 'repo/one', - worktreeSortIndex: 1, - isCurrentPage: false, - isCurrentWorktree: false - }) - ], - 'design zzzznonexistent' - ) - - expect(results).toEqual([]) - }) - - it('matches against formatted URLs when title does not match', () => { - const results = searchBrowserPages( - [ - makeEntry({ - page: makePage({ - id: 'page-1', - title: 'Dashboard', - url: 'https://app.example.com/settings' - }), - workspace: makeWorkspace({ id: 'ws-1' }), - worktree: makeWorktree({ id: 'wt-1' }), - repoName: 'repo/one', - worktreeSortIndex: 1, - isCurrentPage: false, - isCurrentWorktree: false - }) - ], - 'settings' - ) - - expect(results).toHaveLength(1) - expect(results[0].secondaryRanges).toEqual([{ start: 16, end: 24 }]) - expect(results[0].titleRanges).toEqual([]) - }) - - it('matches against raw URL when formatted URL does not match', () => { - const results = searchBrowserPages( - [ - makeEntry({ - page: makePage({ id: 'page-1', title: 'Docs', url: 'https://docs.example.com/' }), - workspace: makeWorkspace({ id: 'ws-1' }), - worktree: makeWorktree({ id: 'wt-1' }), - repoName: 'repo/one', - worktreeSortIndex: 1, - isCurrentPage: false, - isCurrentWorktree: false - }) - ], - 'https' - ) - - expect(results).toHaveLength(1) - expect(results[0].secondaryText).toBe('https://docs.example.com/') - expect(results[0].secondaryRanges).toEqual([{ start: 0, end: 5 }]) - }) - - it('returns empty array when query matches nothing', () => { - const results = searchBrowserPages( - [ - makeEntry({ - page: makePage({ id: 'page-1', title: 'Dashboard', url: 'https://example.com' }), - workspace: makeWorkspace({ id: 'ws-1' }), - worktree: makeWorktree({ id: 'wt-1', displayName: 'Feature' }), - repoName: 'myrepo', - worktreeSortIndex: 1, - isCurrentPage: false, - isCurrentWorktree: false - }) - ], - 'zzzznonexistent' - ) - - expect(results).toHaveLength(0) - }) - - it('formats blank URLs as New Tab', () => { - expect(formatBrowserPaletteUrl('about:blank')).toBe('New Tab') - expect(formatBrowserPaletteUrl('data:text/html,')).toBe('New Tab') - }) - - it('identifies blank browser URLs', () => { - expect(isBlankBrowserUrl('about:blank')).toBe(true) - expect(isBlankBrowserUrl('data:text/html,')).toBe(true) - expect(isBlankBrowserUrl('https://example.com')).toBe(false) - }) - - it('boosts current page and current worktree in scored results', () => { - const entries = [ - makeEntry({ - page: makePage({ id: 'page-other', title: 'React Docs', url: 'https://react.dev' }), - workspace: makeWorkspace({ - id: 'ws-other', - worktreeId: 'wt-other', - activePageId: 'page-other' - }), - worktree: makeWorktree({ id: 'wt-other', displayName: 'Other' }), - repoName: 'repo', - worktreeSortIndex: 1, - isCurrentPage: false, - isCurrentWorktree: false - }), - makeEntry({ - page: makePage({ - id: 'page-current', - workspaceId: 'ws-current', - worktreeId: 'wt-current', - title: 'React Native Docs', - url: 'https://reactnative.dev' - }), - workspace: makeWorkspace({ - id: 'ws-current', - worktreeId: 'wt-current', - activePageId: 'page-current' - }), - worktree: makeWorktree({ id: 'wt-current', displayName: 'Current' }), - repoName: 'repo', - worktreeSortIndex: 1, - isCurrentPage: true, - isCurrentWorktree: true - }) - ] - - const results = searchBrowserPages(entries, 'react') - - expect(results).toHaveLength(2) - expect(results[0].pageId).toBe('page-current') - expect(results[1].pageId).toBe('page-other') - }) - - it('breaks a rank tie between equally-matching pages by recency', () => { - // Ids are ordered so an id-only tiebreak would flip this expectation. - const entries = [ - makeEntry({ - page: makePage({ id: 'page-a-older', title: 'React Docs' }), - workspace: makeWorkspace({ id: 'ws-1' }), - worktree: makeWorktree(), - repoName: 'repo', - worktreeSortIndex: 0, - isCurrentPage: false, - isCurrentWorktree: false, - lastActiveAt: 1000 - }), - makeEntry({ - page: makePage({ id: 'page-z-newer', title: 'React Docs' }), - workspace: makeWorkspace({ id: 'ws-2' }), - worktree: makeWorktree(), - repoName: 'repo', - worktreeSortIndex: 0, - isCurrentPage: false, - isCurrentWorktree: false, - lastActiveAt: 5000 - }) - ] - - const results = searchBrowserPages(entries, 'react') - - expect(results.map((result) => result.pageId)).toEqual(['page-z-newer', 'page-a-older']) - expect(results[0].lastActiveAt).toBe(5000) - }) - - it('matches the visible workspace label in browser search', () => { - const results = searchBrowserPages( - [ - makeEntry({ - page: makePage({ id: 'page-1', title: 'Docs' }), - workspace: makeWorkspace({ id: 'ws-1', label: 'Browser 7' }), - // Branch off the default: it also contains "browser" and would win the tie. - worktree: makeWorktree({ - id: 'wt-1', - displayName: 'Palette Worktree', - branch: 'refs/heads/main' - }), - repoName: 'repo/one', - worktreeSortIndex: 1, - isCurrentPage: false, - isCurrentWorktree: false - }) - ], - 'browser 7' - ) - - expect(results).toHaveLength(1) - // One range per keyword: the tokens are matched independently, not as a phrase. - expect(results[0].workspaceRanges).toEqual([ - { start: 0, end: 7 }, - { start: 8, end: 9 } - ]) - }) - it('rejects a query with more unique tokens than the matcher accepts', () => { const query = 't0 t1 t2 t3 t4 t5 t6 t7 t8 t9 t10 t11 t12 t13 t14 t15 t16' const entries = [ @@ -498,53 +130,4 @@ describe('browser-palette-search', () => { ]) expect(searchBrowserPages([entry], ' '.repeat(2049))).toEqual([]) }) - - it('falls back to the branch label when a cleared display name left it undefined', () => { - // Cmd+J shares worktree objects with searchWorktrees, including corrupt raw display names. - const cleared = makeWorktree({ - displayName: undefined as unknown as string, - branch: 'refs/heads/feature/browser-search' - }) - const entries = [ - makeEntry({ - page: makePage(), - workspace: makeWorkspace(), - worktree: cleared, - repoName: 'orca', - worktreeSortIndex: 0, - isCurrentPage: false, - isCurrentWorktree: false - }) - ] - - const results = searchBrowserPages(entries, 'browser-search') - expect(results[0]).toMatchObject({ - worktreeName: 'feature/browser-search', - worktreeRanges: [{ start: 'feature/'.length, end: 'feature/browser-search'.length }] - }) - }) - - it('lists a branch-less row on the empty query without throwing', () => { - const cleared = makeWorktree({ - displayName: undefined as unknown as string, - branch: undefined as unknown as string, - path: '/repos/design-review' - }) - const entries = [ - makeEntry({ - page: makePage(), - workspace: makeWorkspace(), - worktree: cleared, - repoName: 'orca', - worktreeSortIndex: 0, - isCurrentPage: false, - isCurrentWorktree: false - }) - ] - - expect(searchBrowserPages(entries, '')[0]).toMatchObject({ - worktreeName: 'design-review', - worktreeRanges: [] - }) - }) }) diff --git a/src/renderer/src/lib/browser-workspace-source-resolution.test.ts b/src/renderer/src/lib/browser-workspace-source-resolution.test.ts deleted file mode 100644 index 6dd57c668cf..00000000000 --- a/src/renderer/src/lib/browser-workspace-source-resolution.test.ts +++ /dev/null @@ -1,115 +0,0 @@ -import { describe, expect, it } from 'vitest' -import type { Tab, TabGroup } from '../../../shared/tab-types' -import type { BrowserPage, BrowserWorkspace } from '../../../shared/browser-workspace-types' -import { resolveBrowserSourceUnifiedTab } from './browser-workspace-source-resolution' - -const WT = 'wt-1' - -type ResolverState = Parameters[0] - -function wrapper(id: string, workspaceId: string, groupId = 'group-1'): Tab { - return { - id, - entityId: workspaceId, - groupId, - worktreeId: WT, - contentType: 'browser', - label: 'Example', - customLabel: null, - color: null, - sortOrder: 0, - createdAt: 1 - } -} - -function workspace(id: string): BrowserWorkspace { - return { - id, - worktreeId: WT, - activePageId: null, - pageIds: [], - url: 'about:blank', - title: '', - loading: false, - faviconUrl: null, - canGoBack: false, - canGoForward: false, - loadError: null, - createdAt: 1 - } -} - -function page(id: string, workspaceId: string): BrowserPage { - return { - id, - workspaceId, - worktreeId: WT, - url: 'about:blank', - title: '', - loading: false, - faviconUrl: null, - canGoBack: false, - canGoForward: false, - loadError: null, - createdAt: 1 - } -} - -function makeState(overrides: { - workspaces?: string[] - pages?: Record - wrappers?: Tab[] - groups?: TabGroup[] -}): ResolverState { - const workspaces = overrides.workspaces ?? ['ws-1'] - const pages = overrides.pages ?? { 'ws-1': ['page-1'] } - const wrappers = overrides.wrappers ?? [wrapper('tab-1', 'ws-1')] - return { - browserTabsByWorktree: { - [WT]: workspaces.map(workspace) - }, - browserPagesByWorkspace: Object.fromEntries( - Object.entries(pages).map(([workspaceId, ids]) => [ - workspaceId, - ids.map((id) => page(id, workspaceId)) - ]) - ), - unifiedTabsByWorktree: { [WT]: wrappers }, - groupsByWorktree: { - [WT]: overrides.groups ?? [ - { id: 'group-1', worktreeId: WT, activeTabId: null, tabOrder: wrappers.map((t) => t.id) } - ] - } - } -} - -describe('resolveBrowserSourceUnifiedTab', () => { - it('returns the unique live wrapper for a source page', () => { - expect(resolveBrowserSourceUnifiedTab(makeState({}), 'page-1', WT)?.id).toBe('tab-1') - }) - - it('stays inside the caller workspace', () => { - expect(resolveBrowserSourceUnifiedTab(makeState({}), 'page-1', 'wt-other')).toBeUndefined() - }) - - it('omits the anchor for ambiguous wrappers', () => { - const state = makeState({ wrappers: [wrapper('tab-1', 'ws-1'), wrapper('tab-2', 'ws-1')] }) - expect(resolveBrowserSourceUnifiedTab(state, 'page-1', WT)).toBeUndefined() - }) - - it('omits the anchor when two workspaces claim the page', () => { - const state = makeState({ - workspaces: ['ws-1', 'ws-2'], - pages: { 'ws-1': ['page-1'], 'ws-2': ['page-1'] } - }) - expect(resolveBrowserSourceUnifiedTab(state, 'page-1', WT)).toBeUndefined() - }) - - it('omits the anchor for a closed page or a wrapper outside its group order', () => { - expect(resolveBrowserSourceUnifiedTab(makeState({}), 'closed-page', WT)).toBeUndefined() - const detached = makeState({ - groups: [{ id: 'group-1', worktreeId: WT, activeTabId: null, tabOrder: [] }] - }) - expect(resolveBrowserSourceUnifiedTab(detached, 'page-1', WT)).toBeUndefined() - }) -}) diff --git a/src/renderer/src/lib/bundled-skill-target.test.ts b/src/renderer/src/lib/bundled-skill-target.test.ts index 9291ff62ebb..42a93ce9945 100644 --- a/src/renderer/src/lib/bundled-skill-target.test.ts +++ b/src/renderer/src/lib/bundled-skill-target.test.ts @@ -1,38 +1,7 @@ import { describe, expect, it } from 'vitest' -import type { ProjectExecutionRuntimeResolution } from '../../../shared/project-execution-runtime' -import { - bundledSkillTargetKey, - resolveBundledSkillTarget, - servingExecutionHostId -} from './bundled-skill-target' - -const WSL_PROJECT: ProjectExecutionRuntimeResolution = { - status: 'resolved', - runtime: { - kind: 'wsl', - hostPlatform: 'wsl', - projectId: 'p', - distro: 'Ubuntu', - reason: 'project-override', - cacheKey: 'p:wsl:Ubuntu' - } -} +import { resolveBundledSkillTarget, servingExecutionHostId } from './bundled-skill-target' describe('resolveBundledSkillTarget', () => { - it('sends a paired server or SSH host to that host, whatever the local project says', () => { - const target = resolveBundledSkillTarget( - { kind: 'environment', environmentId: 'env-1' }, - WSL_PROJECT - ) - expect(target).toEqual({ kind: 'environment', environmentId: 'env-1' }) - expect(bundledSkillTargetKey(target!)).toBe('runtime:env-1') - }) - - it('plans nothing for a Windows project that runs in WSL', () => { - const target = resolveBundledSkillTarget({ kind: 'local' }, WSL_PROJECT) - expect(target).toBeNull() - }) - it('plans nothing while a WSL project needs its distro repaired, never the Windows home', () => { expect( resolveBundledSkillTarget( @@ -85,12 +54,4 @@ describe('servingExecutionHostId', () => { environmentId: 'box-server' }) }) - - it('keeps an SSH host no server serves as SSH, which never plans this machine', () => { - const hostId = servingExecutionHostId('ssh:other', environments) - expect(hostId).toBe('ssh:other') - const target = resolveBundledSkillTarget({ kind: 'local' }, undefined, hostId) - expect(target).toEqual({ kind: 'ssh', targetId: 'other' }) - expect(bundledSkillTargetKey(target!)).toBe('ssh:other') - }) }) diff --git a/src/renderer/src/lib/check-run-details-log-normalization.test.ts b/src/renderer/src/lib/check-run-details-log-normalization.test.ts index 18c333a95ba..78c0635b41c 100644 --- a/src/renderer/src/lib/check-run-details-log-normalization.test.ts +++ b/src/renderer/src/lib/check-run-details-log-normalization.test.ts @@ -55,32 +55,6 @@ describe('check details log ingress normalization', () => { expect(normalizeCheckRunDetailsLogTails(result)).toEqual(result) }) - it.each([null, checkLogDetails([]), checkLogDetails([checkLogJob(null)])])( - 'retains null/no-string responses (%#)', - (raw) => { - const sanitize = vi.spyOn(sanitation, 'sanitizeCheckJobLogExcerpt') - expect(normalizeCheckRunDetailsLogTails(raw)).toBe(raw) - expect(sanitize).not.toHaveBeenCalled() - } - ) - - it('replaces string-bearing objects even when the owned returned text is equal or empty', () => { - const own = vi.spyOn(ownership, 'ownRetainedString') - const raw = checkLogDetails([ - checkLogJob('plain unchanged text'), - checkLogJob('', 2), - { ...checkLogJob('\x00\x01', 3), logState: 'available' } - ]) - const result = normalizeCheckRunDetailsLogTails(raw) - if (!result) { - throw new Error('Expected details') - } - expect(result.jobs.map((job) => job.logTail)).toEqual(['plain unchanged text', '', '']) - expect(result.jobs[2].logState).toBe('available') - result.jobs.forEach((job, i) => expect(job).not.toBe(raw.jobs[i])) - expect(own.mock.calls).toEqual([['plain unchanged text'], [''], ['']]) - }) - it('uses the real no-Buffer owned copier and does not split multibyte boundaries', () => { vi.stubGlobal('Buffer', undefined) ownership.resetOwnRetainedStringCopier() diff --git a/src/renderer/src/lib/claude-account-switch-sessions.test.ts b/src/renderer/src/lib/claude-account-switch-sessions.test.ts index d1d261cba1c..2d0db822aa3 100644 --- a/src/renderer/src/lib/claude-account-switch-sessions.test.ts +++ b/src/renderer/src/lib/claude-account-switch-sessions.test.ts @@ -78,11 +78,6 @@ const SWITCH = { before: { state: ROSTER, accountId: 'account-a' }, after: { state: ROSTER, accountId: 'account-b' } } -const SAME_FOLDER = { - ...SWITCH, - before: { state: ROSTER, accountId: null }, - after: { state: ROSTER, accountId: null } -} describe('handleRunningClaudeSessionsAfterSwitch', () => { beforeEach(() => { @@ -94,16 +89,6 @@ describe('handleRunningClaudeSessionsAfterSwitch', () => { ) }) - it('asks nothing when no Claude session runs on the outgoing account', async () => { - vi.mocked(findClaudeSwitchSessions).mockResolvedValue(scan([])) - - expect(await handleRunningClaudeSessionsAfterSwitch(SWITCH)).toBe('show-confirmation') - expect(getClaudeAccountSwitchPrompt()).toBeNull() - expect(findClaudeSwitchSessions).toHaveBeenCalledWith( - expect.objectContaining({ fromAccountId: 'account-a' }) - ) - }) - it('keeps earlier queued restarts except those already on the new account', async () => { vi.mocked(findClaudeSwitchSessions).mockResolvedValue(scan([])) vi.mocked(findQueuedClaudeRestartsOnAccount).mockReturnValueOnce(['pty-home']) @@ -135,20 +120,6 @@ describe('handleRunningClaudeSessionsAfterSwitch', () => { expect(findQueuedClaudeRestartsOnAccount).toHaveBeenCalledTimes(1) }) - it('restarts on the new account when the user picks Restart', async () => { - vi.mocked(findClaudeSwitchSessions).mockResolvedValue(scan([candidate(1), candidate(2)])) - expect(await handleRunningClaudeSessionsAfterSwitch(SWITCH)).toBe('handled') - expect( - getClaudeAccountSwitchPrompt()?.sessions.onAccount.map((session) => session.ptyId) - ).toEqual(['pty-1', 'pty-2']) - - answerClaudeAccountSwitchPrompt('restart') - - expect(store.queuePaneAccountRestarts).toHaveBeenLastCalledWith(['pty-1', 'pty-2'], 'claude') - expect(getClaudeAccountSwitchPrompt()).toBeNull() - expect(store.updateSettings).not.toHaveBeenCalled() - }) - it('restarts only the ticked sessions, and only ones this question listed', async () => { vi.mocked(findClaudeSwitchSessions).mockResolvedValue(scan([candidate(1), candidate(2)])) await handleRunningClaudeSessionsAfterSwitch(SWITCH) @@ -168,49 +139,6 @@ describe('handleRunningClaudeSessionsAfterSwitch', () => { expect(store.queuePaneAccountRestarts).toHaveBeenLastCalledWith(['pty-1-reattached'], 'claude') }) - it('asks nothing when launches run on the same folder before and after the switch', async () => { - vi.mocked(findClaudeSwitchSessions).mockResolvedValue(scan([candidate(1)])) - - expect(await handleRunningClaudeSessionsAfterSwitch(SAME_FOLDER)).toBe('show-confirmation') - expect(findClaudeSwitchSessions).not.toHaveBeenCalled() - }) - - it('leaves sessions alone when the user keeps them', async () => { - vi.mocked(findClaudeSwitchSessions).mockResolvedValue(scan([candidate(1)])) - await handleRunningClaudeSessionsAfterSwitch(SWITCH) - - answerClaudeAccountSwitchPrompt('keep') - - expect(store.queuePaneAccountRestarts).not.toHaveBeenCalled() - expect(getClaudeAccountSwitchPrompt()).toBeNull() - }) - - it.each(['restart', 'keep'] as const)( - 'turns on automatic restarts on %s when told not to ask again', - async (answer) => { - vi.mocked(findClaudeSwitchSessions).mockResolvedValue(scan([candidate(1)])) - await handleRunningClaudeSessionsAfterSwitch(SWITCH) - - answerClaudeAccountSwitchPrompt(answer, { dontAskAgain: true }) - - expect(store.updateSettings).toHaveBeenCalledWith({ - claudeAccountSwitchRunningSessions: 'restart' - }) - } - ) - - it('restarts without asking when the setting is Always restart', async () => { - store.settings = { claudeAccountSwitchRunningSessions: 'restart' } - vi.mocked(findClaudeSwitchSessions).mockResolvedValue(scan([candidate(1), candidate(2)])) - - expect(await handleRunningClaudeSessionsAfterSwitch(SWITCH)).toBe('handled') - expect(getClaudeAccountSwitchPrompt()).toBeNull() - expect(store.queuePaneAccountRestarts).toHaveBeenLastCalledWith(['pty-1', 'pty-2'], 'claude') - expect(toastSuccess).toHaveBeenCalledWith( - 'Restarting 2 Claude sessions on account-b@example.com' - ) - }) - it('names sessions an automatic restart left behind, and reviews only those', async () => { store.settings = { claudeAccountSwitchRunningSessions: 'restart' } vi.mocked(findClaudeSwitchSessions).mockResolvedValue( diff --git a/src/renderer/src/lib/claude-session-account-move.test.ts b/src/renderer/src/lib/claude-session-account-move.test.ts index 449f436fb38..5fe11b705be 100644 --- a/src/renderer/src/lib/claude-session-account-move.test.ts +++ b/src/renderer/src/lib/claude-session-account-move.test.ts @@ -13,12 +13,10 @@ import { inspectRuntimeTerminalProcess } from '@/runtime/runtime-terminal-inspection' import { - claudeLaunchAccountId, findClaudeSwitchSessions, - findQueuedClaudeRestartsOnAccount, - isClaudePaneMidTurn + findQueuedClaudeRestartsOnAccount } from './claude-session-account-move' -import { resolveClaudePaneFolder, type ClaudeHostFolders } from './claude-pane-account-folder' +import type { ClaudeHostFolders } from './claude-pane-account-folder' const laneByPtyId: Record = {} let projectRuntime: ProjectExecutionRuntimeResolution | undefined @@ -165,17 +163,6 @@ describe('findMovableClaudeSessions', () => { vi.mocked(confirmRuntimeTerminalForegroundProcess).mockResolvedValue('claude') }) - it('finds resumable Claude panes and skips other agents and session-less panes', async () => { - seedPanes([ - { n: 1, agentType: 'claude' }, - { n: 2, agentType: 'codex' }, - { n: 3, agentType: 'claude', sessionId: null }, - { n: 4 } - ]) - - expect(await movable()).toEqual([ptyId(1)]) - }) - it('counts only sessions running on the outgoing account', async () => { seedPanes([ { n: 1, agentType: 'claude', configDir: FOLDER_A }, @@ -216,17 +203,6 @@ describe('findMovableClaudeSessions', () => { expect(await movable()).toEqual([]) }) - it('skips a pane where Claude has exited to the shell', async () => { - seedPanes([{ n: 1, agentType: 'claude' }]) - vi.mocked(inspectRuntimeTerminalProcess).mockResolvedValue({ - foregroundProcess: 'zsh', - hasChildProcesses: false - }) - vi.mocked(confirmRuntimeTerminalForegroundProcess).mockResolvedValue('zsh') - - expect(await movable()).toEqual([]) - }) - it('leaves a node or python program the user started in a pane Claude has left', async () => { seedPanes([ { n: 1, agentType: 'claude' }, @@ -327,48 +303,6 @@ describe('findMovableClaudeSessions', () => { }) }) -describe('resolveClaudePaneFolder', () => { - const accounts = [account('acct-a', FOLDER_A)] - - it('reads the account from the transcript folder, with System default as the rest', () => { - expect(resolveClaudePaneFolder(`${FOLDER_A}/projects/-repo/s.jsonl`, accounts)).toEqual({ - kind: 'account', - accountId: 'acct-a' - }) - expect(resolveClaudePaneFolder(`${SYSTEM_FOLDER}/projects/-repo/s.jsonl`, accounts)).toEqual({ - kind: 'system-default' - }) - expect(resolveClaudePaneFolder(undefined, accounts)).toBeUndefined() - }) - - it('matches Windows paths whatever their separators', () => { - const windowsAccounts = [account('acct-w', 'C:\\Users\\dev\\claude\\acct-w')] - expect( - resolveClaudePaneFolder( - 'C:\\Users\\dev\\claude\\acct-w\\projects\\-repo\\s.jsonl', - windowsAccounts - ) - ).toEqual({ kind: 'account', accountId: 'acct-w' }) - }) -}) - -describe('isClaudePaneMidTurn', () => { - it('waits on a reply, a permission prompt or a question, not on an idle session', () => { - seedPanes([ - { n: 1, agentType: 'claude', state: 'working' }, - { n: 2, agentType: 'claude', state: 'blocked' }, - { n: 3, agentType: 'claude', state: 'waiting' }, - { n: 4, agentType: 'claude', state: 'done' } - ]) - const state = useAppStore.getState() - const midTurn = [1, 2, 3, 4].map((n) => - isClaudePaneMidTurn(state, makePaneKey(tabId(n), leafId(n))) - ) - - expect(midTurn).toEqual([true, true, true, false]) - }) -}) - describe('findQueuedClaudeRestartsOnAccount', () => { it('lists queued Claude restarts in the lane whose session already runs on the account', () => { seedPanes([ @@ -473,14 +407,3 @@ describe('a switch away from the login System default holds', () => { expect(await movable('acct-a', HOST, hostFolders())).toEqual([ptyId(1)]) }) }) - -describe('claudeLaunchAccountId', () => { - it('reads an account System default covers as System default, where its sessions run', () => { - const covered = { ...account('acct-a', FOLDER_A), runsOnSystemDefault: true as const } - const own = account('acct-b', FOLDER_B) - - expect(claudeLaunchAccountId({ accounts: [covered, own] }, 'acct-a')).toBeNull() - expect(claudeLaunchAccountId({ accounts: [covered, own] }, 'acct-b')).toBe('acct-b') - expect(claudeLaunchAccountId({ accounts: [covered, own] }, null)).toBeNull() - }) -}) diff --git a/src/renderer/src/lib/cli-conversation-exit-guidance.test.ts b/src/renderer/src/lib/cli-conversation-exit-guidance.test.ts deleted file mode 100644 index c8a5b267fc7..00000000000 --- a/src/renderer/src/lib/cli-conversation-exit-guidance.test.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { expect, it } from 'vitest' -import { cliConversationExitGuidance } from './cli-conversation-exit-guidance' - -// Once the agent quits, an older host drops the pane's row and its Continue button, so the -// refusal must point to the Session History route that still opens the conversation. -it('sends a refusal in this terminal to Session History', () => { - expect(cliConversationExitGuidance('claude', true)).toBe( - 'Claude is still running this conversation in this terminal. Quit Claude here, then open this conversation from Session History.' - ) -}) diff --git a/src/renderer/src/lib/client-creation-action-policy.test.ts b/src/renderer/src/lib/client-creation-action-policy.test.ts index fa585922e41..e3535258374 100644 --- a/src/renderer/src/lib/client-creation-action-policy.test.ts +++ b/src/renderer/src/lib/client-creation-action-policy.test.ts @@ -1,17 +1,14 @@ import { afterEach, describe, expect, it, vi } from 'vitest' import { createBrowserLinkTestStore, WORKSPACE } from './browser-link-open.test-fixture' -import { FLOATING_TERMINAL_WORKTREE_ID, getDefaultSettings } from '../../../shared/constants' +import { getDefaultSettings } from '../../../shared/constants' import type { RuntimeStatus } from '../../../shared/runtime-types' import { toRuntimeExecutionHostId, toSshExecutionHostId } from '../../../shared/execution-host' import { folderWorkspaceKey } from '../../../shared/workspace-scope' import { BROWSER_CLIENT_HOSTING_RUNTIME_CAPABILITIES } from '../../../shared/browser-client-hosting-eligibility' import type { PublicKnownRuntimeEnvironment } from '../../../shared/runtime-environments' -import { BROWSER_SCREENCAST_RUNTIME_CAPABILITY } from '../../../shared/protocol-version' import { - FLOATING_BROWSER_UNAVAILABLE_MESSAGE, LOCAL_BROWSER_UNAVAILABLE_MESSAGE, MANAGED_BROWSER_UNAVAILABLE_MESSAGE, - MOBILE_EMULATOR_UNAVAILABLE_MESSAGE, assertClientCreationActionAvailable, assertManagedBrowserMaterializationAllowed, assertRuntimeManagedBrowserCreationAvailable, @@ -37,96 +34,6 @@ function runtimeStatus(capabilities?: string[]): RuntimeStatus { afterEach(() => vi.unstubAllGlobals()) -describe('resolveClientCreationActionPolicy', () => { - it('preserves Electron creation behavior without negotiated runtime signals', () => { - expect(resolveClientCreationActionPolicy({ surface: 'electron', runtimeStatus: null })).toEqual( - { - 'managed-browser': { state: 'enabled', provider: 'local-client' }, - 'mobile-emulator': { state: 'enabled', provider: 'local-client' } - } - ) - }) - - it('selects the provider that the browser action path can actually consume', () => { - const desktopHost = runtimeStatus(['browser.screencast.v1']) - const npmHostWithoutDisplay = { ...runtimeStatus(), hostPlatform: 'linux' as const } - const npmHostWithDisplay = { - ...runtimeStatus(['browser.screencast.v1', 'browser.headless.v1']), - hostPlatform: 'linux' as const - } - - expect( - resolveClientCreationActionPolicy({ surface: 'electron', runtimeStatus: desktopHost }) - ).toEqual({ - 'managed-browser': { state: 'enabled', provider: 'paired-runtime' }, - 'mobile-emulator': { state: 'enabled', provider: 'local-client' } - }) - expect( - resolveClientCreationActionPolicy({ - surface: 'electron', - runtimeStatus: npmHostWithoutDisplay - }) - ).toEqual({ - 'managed-browser': { state: 'enabled', provider: 'local-client' }, - 'mobile-emulator': { state: 'enabled', provider: 'local-client' } - }) - expect( - resolveClientCreationActionPolicy({ surface: 'paired-web', runtimeStatus: desktopHost }) - ).toEqual({ - 'managed-browser': { state: 'enabled', provider: 'paired-runtime' }, - 'mobile-emulator': { state: 'hidden', reason: MOBILE_EMULATOR_UNAVAILABLE_MESSAGE } - }) - expect( - resolveClientCreationActionPolicy({ - surface: 'paired-web', - runtimeStatus: npmHostWithoutDisplay - }) - ).toEqual({ - 'managed-browser': { state: 'hidden', reason: MANAGED_BROWSER_UNAVAILABLE_MESSAGE }, - 'mobile-emulator': { state: 'hidden', reason: MOBILE_EMULATOR_UNAVAILABLE_MESSAGE } - }) - expect( - resolveClientCreationActionPolicy({ - surface: 'paired-web', - runtimeStatus: npmHostWithDisplay - })['managed-browser'] - ).toEqual({ state: 'enabled', provider: 'paired-runtime' }) - }) - - it('enables paired-web browser creation only with negotiated streaming support', () => { - expect( - resolveClientCreationActionPolicy({ - surface: 'paired-web', - runtimeStatus: runtimeStatus(['browser.screencast.v1']) - })['managed-browser'] - ).toEqual({ state: 'enabled', provider: 'paired-runtime' }) - - expect( - resolveClientCreationActionPolicy({ - surface: 'paired-web', - runtimeStatus: runtimeStatus() - })['managed-browser'] - ).toEqual({ state: 'hidden', reason: MANAGED_BROWSER_UNAVAILABLE_MESSAGE }) - }) - - it('hides web-client floating browsers and mobile emulators as impossible surfaces', () => { - const policy = resolveClientCreationActionPolicy({ - surface: 'paired-web', - runtimeStatus: runtimeStatus(['browser.screencast.v1', 'mobile.tasks.v1']), - floatingWorkspace: true - }) - - expect(policy['managed-browser']).toEqual({ - state: 'hidden', - reason: FLOATING_BROWSER_UNAVAILABLE_MESSAGE - }) - expect(policy['mobile-emulator']).toEqual({ - state: 'hidden', - reason: MOBILE_EMULATOR_UNAVAILABLE_MESSAGE - }) - }) -}) - describe('client creation action guards', () => { it.each([false, true])('does not read server focus for a workspace policy (web: %s)', (web) => { vi.stubGlobal('__ORCA_WEB_CLIENT__', web) @@ -168,34 +75,6 @@ describe('client creation action guards', () => { ) }) - it('permits host-confirmed remote browser materialization in paired web', () => { - vi.stubGlobal('__ORCA_WEB_CLIENT__', true) - const state = { - runtimeStatusByEnvironmentId: new Map([ - ['runtime-1', { status: runtimeStatus(['browser.screencast.v1']), checkedAt: 1 }] - ]) - } - expect(() => - assertManagedBrowserMaterializationAllowed(state as never, 'runtime-1') - ).not.toThrow() - }) - - it('treats the floating workspace as local even with an active capable runtime', () => { - vi.stubGlobal('__ORCA_WEB_CLIENT__', true) - const state = { - settings: { activeRuntimeEnvironmentId: 'runtime-1' }, - runtimeStatusByEnvironmentId: new Map([ - ['runtime-1', { status: runtimeStatus(['browser.screencast.v1']), checkedAt: 1 }] - ]) - } - - expect( - getClientCreationActionPolicy(state as never, FLOATING_TERMINAL_WORKTREE_ID)[ - 'managed-browser' - ] - ).toEqual({ state: 'hidden', reason: FLOATING_BROWSER_UNAVAILABLE_MESSAGE }) - }) - it('uses folder and SSH workspace ownership instead of the focused runtime', () => { vi.stubGlobal('__ORCA_WEB_CLIENT__', true) const capableStatus = runtimeStatus(['browser.screencast.v1']) @@ -286,25 +165,4 @@ describe('a runtime whose pages this desktop hosts', () => { expect(runtimeHostsPagesOnThisDesktop(state, 'managed-ssh')).toBe(false) expect(() => assertRuntimeManagedBrowserCreationAvailable(state, 'managed-ssh')).toThrow() }) - - it('leaves pages on a streaming server when the setting asks for server rendering', () => { - const streaming = hostingState( - { id: 'electron-server' }, - { browserClientHostedRemoteEnabled: false }, - [BROWSER_SCREENCAST_RUNTIME_CAPABILITY] - ) - expect(runtimeHostsPagesOnThisDesktop(streaming, 'electron-server')).toBe(false) - }) - - it('hosts through the server it cannot stream from even when the setting asks for server rendering', () => { - const state = hostingState({ id: 'orcad-server' }, { browserClientHostedRemoteEnabled: false }) - expect(runtimeHostsPagesOnThisDesktop(state, 'orcad-server')).toBe(true) - }) - - it('stays off in the web client', () => { - vi.stubGlobal('__ORCA_WEB_CLIENT__', true) - expect( - runtimeHostsPagesOnThisDesktop(hostingState({ id: 'orcad-server' }), 'orcad-server') - ).toBe(false) - }) }) diff --git a/src/renderer/src/lib/clipboard-file-path.test.ts b/src/renderer/src/lib/clipboard-file-path.test.ts deleted file mode 100644 index 1fff853348d..00000000000 --- a/src/renderer/src/lib/clipboard-file-path.test.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { afterEach, describe, expect, it, vi } from 'vitest' -import { formatClipboardFilePath } from './clipboard-file-path' -import { resetRendererAppPlatformCacheForTests } from './renderer-app-platform' -import type { LocalPathOpenOwner } from './local-path-open-guard' - -function stubClient(platform: NodeJS.Platform, webClient = false): void { - resetRendererAppPlatformCacheForTests() - vi.stubGlobal('window', { - api: { platform: { get: () => ({ platform }) } }, - ...(webClient ? { __ORCA_WEB_CLIENT__: true } : {}) - }) -} - -afterEach(() => { - vi.unstubAllGlobals() - resetRendererAppPlatformCacheForTests() -}) - -describe('formatClipboardFilePath', () => { - it.each([ - ['C:/repo/a space/é.txt', 'C:\\repo\\a space\\é.txt'], - ['D:\\repo/mixed\\b.txt', 'D:\\repo\\mixed\\b.txt'], - ['//server/share/b.txt', '\\\\server\\share\\b.txt'] - ])('uses backslashes for local Windows path %s', (path, expected) => { - stubClient('win32') - expect(formatClipboardFilePath(path, 'local')).toBe(expected) - }) - - it.each<[string, NodeJS.Platform, LocalPathOpenOwner, string, boolean?]>([ - ['SSH owner', 'win32', 'ssh:box', 'C:/repo/b.txt'], - ['runtime owner', 'win32', 'runtime:env-1', 'C:/repo/b.txt'], - ['unresolved owner', 'win32', 'unresolved', 'C:/repo/b.txt'], - ['macOS client', 'darwin', 'local', 'C:/repo/b.txt'], - ['Linux client', 'linux', 'local', '/repo/b.txt'], - ['URL', 'win32', 'local', 'https://example.com/C:/b.txt'], - ['relative path', 'win32', 'local', 'src/b.txt'], - ['paired web client', 'win32', 'local', 'C:/repo/b.txt', true] - ])('leaves the path unchanged for a %s', (_case, platform, owner, path, webClient) => { - stubClient(platform, webClient) - expect(formatClipboardFilePath(path, owner)).toBe(path) - }) -}) diff --git a/src/renderer/src/lib/clipboard-image-copy.test.ts b/src/renderer/src/lib/clipboard-image-copy.test.ts index 031c84e7bd3..70c2a541940 100644 --- a/src/renderer/src/lib/clipboard-image-copy.test.ts +++ b/src/renderer/src/lib/clipboard-image-copy.test.ts @@ -63,11 +63,4 @@ describe('copyImageToClipboard', () => { expect(writeClipboardImage).not.toHaveBeenCalled() }) - - it('propagates a local clipboard write failure', async () => { - const error = new Error('Clipboard unavailable') - writeClipboardImage.mockRejectedValue(error) - - await expect(copyImageToClipboard(png)).rejects.toBe(error) - }) }) diff --git a/src/renderer/src/lib/cmd-j-host-qualified-candidate-ownership.test.ts b/src/renderer/src/lib/cmd-j-host-qualified-candidate-ownership.test.ts index de9a652f4dd..66d4dc43a3d 100644 --- a/src/renderer/src/lib/cmd-j-host-qualified-candidate-ownership.test.ts +++ b/src/renderer/src/lib/cmd-j-host-qualified-candidate-ownership.test.ts @@ -283,88 +283,6 @@ describe('Cmd-J host-qualified candidate ownership', () => { ]) }) - it('omits editor rows whose bare file id cannot be activated safely', () => { - const entries = buildSearchableWorkspaceTabs({ - worktrees: pairedWorktrees(), - repoMap: new Map(), - worktreeOrder: new Map(), - unifiedTabsByWorktree: { - [SHARED_WORKTREE_ID]: [ - makeTab({ - id: 'shared-editor', - entityId: 'shared-file', - contentType: 'editor', - executionHostId: 'local' - }), - makeTab({ - id: 'shared-editor', - entityId: 'shared-file', - contentType: 'editor', - executionHostId: RUNTIME_HOST_ID - }) - ] - }, - tabsByWorktree: {}, - openFiles: [ - { - id: 'shared-file', - filePath: '/local/local-atlas.ts', - relativePath: 'local/local-atlas.ts', - worktreeId: SHARED_WORKTREE_ID, - language: 'typescript', - isDirty: false, - mode: 'edit' - }, - { - id: 'shared-file', - filePath: '/remote/remote-atlas.ts', - relativePath: 'remote/remote-atlas.ts', - worktreeId: SHARED_WORKTREE_ID, - language: 'typescript', - isDirty: false, - runtimeEnvironmentId: 'paired-host', - mode: 'edit' - } - ], - agentStatusByPaneKey: {}, - retainedAgentsByPaneKey: {}, - sleepingAgentSessionsByPaneKey: {}, - activeGroupIdByWorktree: {}, - groupsByWorktree: {}, - activeWorktreeId: null, - activeTabType: 'terminal', - activeTabId: null, - activeTabIdByWorktree: {}, - activeFileId: null, - activeFileIdByWorktree: {}, - activeTabTypeByWorktree: {}, - generatedTitlesEnabled: true - }) - - expect(searchWorkspaceTabs(entries, 'local-atlas')).toEqual([]) - expect(searchWorkspaceTabs(entries, 'remote-atlas')).toEqual([]) - }) - - it('retains one unambiguous legacy tab without guessing between sibling hosts', () => { - const legacyWorktree = makeWorktree({ hostId: undefined }) - const entries = buildSearchableSimulatorTabs({ - worktrees: [legacyWorktree], - repoMap: new Map(), - worktreeOrder: new Map(), - unifiedTabsByWorktree: { - [SHARED_WORKTREE_ID]: [ - makeTab({ id: 'legacy-simulator', contentType: 'simulator', executionHostId: undefined }) - ] - }, - activeGroupIdByWorktree: {}, - groupsByWorktree: {}, - activeWorktreeId: null, - activeTabType: 'terminal' - }) - - expect(entries.map((entry) => entry.tab.id)).toEqual(['legacy-simulator']) - }) - it('keeps hidden same-id hosts in the legacy ownership ambiguity set', () => { const ownershipWorktrees = pairedWorktrees() const browserWorkspace = makeBrowserWorkspace('legacy-browser') diff --git a/src/renderer/src/lib/cmd-j-linear-issue-intent.test.ts b/src/renderer/src/lib/cmd-j-linear-issue-intent.test.ts index 3f43590bcb7..f682969b64b 100644 --- a/src/renderer/src/lib/cmd-j-linear-issue-intent.test.ts +++ b/src/renderer/src/lib/cmd-j-linear-issue-intent.test.ts @@ -6,23 +6,6 @@ import { } from '../../../shared/linear/links' describe('Cmd+J Linear issue intent', () => { - it('accepts canonical Linear issue URLs with optional slugs', () => { - expect( - parseLinearIssueUrlIntent( - 'https://linear.app/stably/issue/STA-4052/agent-terminals-disappearing-randomly' - ) - ).toEqual({ identifier: 'STA-4052', organizationUrlKey: 'stably' }) - expect( - parseLinearIssueUrlIntent( - ' https://linear.app/stably/issue/sta-4084/restore-shell-integration ' - ) - ).toEqual({ identifier: 'STA-4084', organizationUrlKey: 'stably' }) - expect(parseLinearIssueUrlIntent('http://linear.app/acme/issue/ENG-1')).toEqual({ - identifier: 'ENG-1', - organizationUrlKey: 'acme' - }) - }) - it('does not classify names, bare issue keys, or ambiguous URLs as decisive intent', () => { expect(parseLinearIssueUrlIntent('restore-shell-integration')).toBeNull() expect(parseLinearIssueUrlIntent('STA-4084')).toBeNull() diff --git a/src/renderer/src/lib/code-editor-theme.test.ts b/src/renderer/src/lib/code-editor-theme.test.ts index e7d0b00fde9..219d155d722 100644 --- a/src/renderer/src/lib/code-editor-theme.test.ts +++ b/src/renderer/src/lib/code-editor-theme.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest' import type { PluginThemeRegistration } from '../../../shared/plugins/artifacts/plugin-theme-artifact' -import { paintCodeEditorColors, resolveCodeEditorTheme } from './code-editor-theme' +import { resolveCodeEditorTheme } from './code-editor-theme' import type { ColorProbe } from './plugin-theme-tokens' // Parses #rrggbb, which is all these tests feed it. @@ -54,14 +54,6 @@ function resolve( } describe('resolveCodeEditorTheme', () => { - it('follows the painted app theme when nothing or the app theme itself is chosen', () => { - expect(resolve(null)).toEqual({ monaco: null, override: null }) - expect(resolve('light')).toEqual({ monaco: null, override: null }) - const followed = resolve(dawn, dawn) - expect(followed.override).toBeNull() - expect(followed.monaco?.data.colors['editor.background']).toBe('#fdf6e3') - }) - it("paints a dark plugin editor in a light app from that theme's own tokens", () => { const result = resolve(dusk, dawn) expect(result.monaco?.data.base).toBe('vs-dark') @@ -97,28 +89,3 @@ describe('resolveCodeEditorTheme', () => { }) }) }) - -describe('paintCodeEditorColors', () => { - it('publishes an override on the root and clears it when the editor follows the app', () => { - const properties = new Map() - const dataset: DOMStringMap = {} - const root = { - dataset, - style: { - setProperty: (name: string, value: string | null) => { - properties.set(name, value ?? '') - }, - removeProperty: (name: string) => { - properties.delete(name) - return '' - } - } - } - paintCodeEditorColors(resolve(dusk, dawn).override, root) - expect(dataset.codeEditorTheme).toBe('dark') - expect(properties.get('--code-editor-background')).toBe('#120d22') - paintCodeEditorColors(null, root) - expect(dataset.codeEditorTheme).toBeUndefined() - expect(properties.size).toBe(0) - }) -}) diff --git a/src/renderer/src/lib/codex-account-display-label.test.ts b/src/renderer/src/lib/codex-account-display-label.test.ts deleted file mode 100644 index 64c99a48f0e..00000000000 --- a/src/renderer/src/lib/codex-account-display-label.test.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { - getCodexAccountDisplayLabel, - type CodexDisplayAccount -} from './codex-account-display-label' - -const email = 'same@example.com' -const labels = (accounts: CodexDisplayAccount[]) => - accounts.map((account) => getCodexAccountDisplayLabel(account, accounts)) - -describe('Codex account display labels', () => { - it('names personal and enterprise workspaces sharing an email', () => { - expect( - labels([ - { id: 'personal', email, workspaceLabel: 'Personal (Plus)' }, - { id: 'enterprise', email, workspaceLabel: 'Enterprise' } - ]) - ).toEqual([`${email} (Personal (Plus))`, `${email} (Enterprise)`]) - }) - - it.each([null, 'Enterprise'])( - 'disambiguates missing or duplicate workspace names: %s', - (workspaceLabel) => { - const accounts = [ - { id: '12345678-a', email, workspaceLabel }, - { id: '12345678-b', email, workspaceLabel } - ] - const result = labels(accounts) - expect(new Set(result).size).toBe(2) - expect(result[0]).toContain('12345678-a') - expect(result[1]).toContain('12345678-b') - expect(labels(accounts.toReversed())).toEqual(result.toReversed()) - } - ) - - it('handles legacy and remote summaries without workspace metadata', () => { - expect( - labels([ - { id: 'account-a', email }, - { id: 'account-b', email: email.toUpperCase() } - ]) - ).toEqual([`${email} (account-a)`, `${email.toUpperCase()} (account-b)`]) - }) - - it('keeps unambiguous accounts concise', () => { - expect(labels([{ id: 'account-a', email }])).toEqual([email]) - expect(labels([{ id: 'account-a', email, workspaceLabel: 'Acme' }])).toEqual([ - `${email} (Acme)` - ]) - }) - - it('does not collide with a workspace name that looks like an ID suffix', () => { - const result = labels([ - { id: '12345678-a', email }, - { id: '87654321-b', email }, - { id: 'abcdefgh-c', email, workspaceLabel: '12345678' } - ]) - expect(new Set(result).size).toBe(3) - }) -}) diff --git a/src/renderer/src/lib/codex-pane-selection-lane.test.ts b/src/renderer/src/lib/codex-pane-selection-lane.test.ts index 15c0c7d585b..847163ffabd 100644 --- a/src/renderer/src/lib/codex-pane-selection-lane.test.ts +++ b/src/renderer/src/lib/codex-pane-selection-lane.test.ts @@ -1,11 +1,9 @@ -import { describe, expect, it, vi } from 'vitest' +import { describe, expect, it } from 'vitest' import type { AppState } from '@/store' import { getCodexSelectionLaneKey } from '../../../shared/codex-selection-lane' import { toRemoteRuntimePtyId } from '../../../shared/remote-runtime/remote-runtime-pty-id' import { getCodexAccountSwitchLaneMatcher, - isForeignMachineCodexPtyId, - isLocalCodexSelectionLaneKey, resolveCodexPaneSelectionLane, resolveCodexPaneSelectionLaneKey } from './codex-pane-selection-lane' @@ -91,49 +89,6 @@ function withWindowsRenderer(run: () => void): void { const HOST_TAB = { worktreeId: 'wt1', shellOverride: undefined } describe('resolveCodexPaneSelectionLaneKey', () => { - it('keys an ordinary local pane to the host lane', () => { - expect( - resolveCodexPaneSelectionLaneKey({ state: laneState(), tab: HOST_TAB, ptyId: 'pty-1' }) - ).toBe('host') - }) - - it('keys a pane in a WSL UNC worktree to that distro lane', () => { - expect( - resolveCodexPaneSelectionLaneKey({ - state: laneState({ worktreePath: '\\\\wsl.localhost\\Ubuntu\\home\\dev\\orca' }), - tab: HOST_TAB, - ptyId: 'pty-1' - }) - ).toBe('wsl:Ubuntu') - }) - - it('keys a wsl.exe pane outside a UNC worktree to the default WSL lane', () => { - expect( - resolveCodexPaneSelectionLaneKey({ - state: laneState(), - tab: { worktreeId: 'wt1', shellOverride: 'wsl.exe' }, - ptyId: 'pty-1' - }) - ).toBe('wsl:__default__') - }) - - // Use the resolved distro lane; wsl:__default__ would miss that distro’s account switch. - it('keys a wsl.exe pane on a Windows-path worktree to the configured distro', () => { - withWindowsRenderer(() => { - expect( - resolveCodexPaneSelectionLaneKey({ - state: laneState({ - terminalWindowsShell: 'wsl.exe', - terminalWindowsWslDistro: 'Ubuntu', - worktreePath: 'C:\\code\\app' - }), - tab: { worktreeId: 'wt1', shellOverride: 'wsl.exe' }, - ptyId: 'pty-1' - }) - ).toBe('wsl:Ubuntu') - }) - }) - it('still keys an ordinary host pane to host when a WSL distro is configured', () => { withWindowsRenderer(() => { expect( @@ -265,29 +220,6 @@ describe('resolveCodexPaneSelectionLaneKey', () => { }) ).toBe('remote-runtime') }) - - it('keeps an owner-less remote pane off the host lane when no environment is active', () => { - const laneKey = resolveCodexPaneSelectionLaneKey({ - state: laneState(), - tab: HOST_TAB, - ptyId: 'remote:term-1' - }) - // Assert separation from host: a local switch must never mute a remote pane. - expect(laneKey).not.toBe(getCodexSelectionLaneKey({ runtime: 'host' })) - expect(isLocalCodexSelectionLaneKey(laneKey)).toBe(false) - }) - - it('keys an SSH-connection pane to a lane no account selection can name', () => { - const laneKey = resolveCodexPaneSelectionLaneKey({ - state: laneState(), - tab: HOST_TAB, - ptyId: 'ssh:my-box@@pty-7' - }) - expect(laneKey).toBe('ssh-connection') - // Only host/wsl lanes have managed selections; no switch can reach this foreign key. - expect(laneKey).not.toBe(getCodexSelectionLaneKey({ runtime: 'host' })) - expect(isLocalCodexSelectionLaneKey(laneKey)).toBe(false) - }) }) describe('resolveCodexPaneSelectionLane', () => { @@ -306,53 +238,6 @@ describe('resolveCodexPaneSelectionLane', () => { }) }) - it('reports the disagreement so a re-derivation bug stays diagnosable', () => { - const warn = vi.spyOn(console, 'warn').mockImplementation(() => {}) - try { - resolveCodexPaneSelectionLane({ - state: laneState(), - tab: HOST_TAB, - ptyId: 'pty-1', - recordedLaneKey: 'wsl:Ubuntu' - }) - expect(warn).toHaveBeenCalledWith( - expect.stringContaining('[codex-lane]'), - expect.objectContaining({ ptyId: 'pty-1', recorded: 'wsl:Ubuntu', derived: 'host' }) - ) - } finally { - warn.mockRestore() - } - }) - - it('stays quiet when the record and the derivation agree', () => { - const warn = vi.spyOn(console, 'warn').mockImplementation(() => {}) - try { - const lane = resolveCodexPaneSelectionLane({ - state: laneState(), - tab: HOST_TAB, - ptyId: 'pty-1', - recordedLaneKey: 'host' - }) - expect(lane.source).toBe('recorded') - expect(warn).not.toHaveBeenCalled() - } finally { - warn.mockRestore() - } - }) - - it('derives a local host pane that main never recorded', () => { - for (const recordedLaneKey of [undefined, null, '', ' ']) { - expect( - resolveCodexPaneSelectionLane({ - state: laneState(), - tab: HOST_TAB, - ptyId: 'pty-1', - recordedLaneKey - }) - ).toEqual({ laneKey: 'host', source: 'derived', derivedLaneKey: 'host' }) - } - }) - it('falls back to the derivation when the record names no selectable lane', () => { // Registry strings are untrusted on-disk values; invalid lanes would silently lose notices. expect( @@ -403,23 +288,6 @@ describe('resolveCodexPaneSelectionLane', () => { }) describe('getCodexAccountSwitchLaneMatcher', () => { - it('scopes a local switch to the runtime slot it wrote', () => { - const hostSwitch = getCodexAccountSwitchLaneMatcher({ - owner: { kind: 'local' }, - target: { runtime: 'host' } - }) - expect(hostSwitch('host')).toBe(true) - expect(hostSwitch('wsl:Ubuntu')).toBe(false) - - const ubuntuSwitch = getCodexAccountSwitchLaneMatcher({ - owner: { kind: 'local' }, - target: { runtime: 'wsl', wslDistro: 'Ubuntu' } - }) - expect(ubuntuSwitch('wsl:Ubuntu')).toBe(true) - expect(ubuntuSwitch('wsl:Debian')).toBe(false) - expect(ubuntuSwitch('host')).toBe(false) - }) - // Clearing a distro-less selection clears every WSL slot; matching only __default__ would miss them. it('claims every WSL distro when the change cleared them all', () => { const wslDefaultSwitch = getCodexAccountSwitchLaneMatcher({ @@ -458,47 +326,4 @@ describe('getCodexAccountSwitchLaneMatcher', () => { expect(ubuntuClear('wsl:Debian')).toBe(false) expect(ubuntuClear('wsl:__default__')).toBe(false) }) - - it('scopes a switch made against a runtime environment to that machine', () => { - const environmentSwitch = getCodexAccountSwitchLaneMatcher({ - owner: { kind: 'environment', environmentId: 'env-1' }, - target: { runtime: 'host' } - }) - expect(environmentSwitch('env:env-1')).toBe(true) - expect(environmentSwitch('host')).toBe(false) - expect(environmentSwitch('env:env-2')).toBe(false) - }) - - it('never lets a local host switch claim a remote or SSH pane', () => { - const hostSwitch = getCodexAccountSwitchLaneMatcher({ - owner: { kind: 'local' }, - target: { runtime: 'host' } - }) - const state = laneState() - for (const ptyId of ['remote:env-owner@@term-1', 'remote:term-1', 'ssh:my-box@@pty-7']) { - expect(hostSwitch(resolveCodexPaneSelectionLaneKey({ state, tab: HOST_TAB, ptyId }))).toBe( - false - ) - } - }) -}) - -describe('isForeignMachineCodexPtyId', () => { - it('separates panes whose shell runs on another machine from local ones', () => { - expect(isForeignMachineCodexPtyId('remote:env-1@@term-1')).toBe(true) - expect(isForeignMachineCodexPtyId('remote:term-1')).toBe(true) - expect(isForeignMachineCodexPtyId('ssh:my-box@@pty-7')).toBe(true) - expect(isForeignMachineCodexPtyId('pty-1')).toBe(false) - }) - - it('agrees with the lane keys, so the sweep and the scan skip the same panes', () => { - const state = laneState({ activeRuntimeEnvironmentId: 'env-1' }) - for (const ptyId of ['remote:env-1@@term-1', 'remote:term-1', 'ssh:my-box@@pty-7', 'pty-1']) { - expect( - isLocalCodexSelectionLaneKey( - resolveCodexPaneSelectionLaneKey({ state, tab: HOST_TAB, ptyId }) - ) - ).toBe(!isForeignMachineCodexPtyId(ptyId)) - } - }) }) diff --git a/src/renderer/src/lib/codex-session-restart-shell-flap.test.ts b/src/renderer/src/lib/codex-session-restart-shell-flap.test.ts index daf0f7b2328..705ebe62c73 100644 --- a/src/renderer/src/lib/codex-session-restart-shell-flap.test.ts +++ b/src/renderer/src/lib/codex-session-restart-shell-flap.test.ts @@ -8,11 +8,6 @@ import { markLiveCodexSessionsForRestart, markRestoredStaleCodexSessionsForRestart } from './codex-session-restart' -import { - createCompatibleRuntimeStatusResponseIfNeeded, - type RuntimeEnvironmentCallRequest -} from '@/runtime/runtime-compatibility-test-fixture' -import { clearRuntimeCompatibilityCacheForTests } from '@/runtime/runtime-rpc-client' const ACCOUNT_A = 'account-a@example.com' const ACCOUNT_B = 'account-b@example.com' @@ -20,8 +15,6 @@ const ACCOUNT_B = 'account-b@example.com' // Reconfirm cached shell readings: one transient flap during account switch would silently lose the only restart notice (#11064). describe('spurious shell readings on Codex-launched panes', () => { const originalWindow = (globalThis as { window?: typeof window }).window - const runtimeEnvironmentCall = vi.fn() - const runtimeEnvironmentTransportCall = vi.fn() function seedPane(args: { ptyId?: string; launchAgent?: 'codex' } = {}): void { const ptyId = args.ptyId ?? 'pty-1' @@ -49,12 +42,6 @@ describe('spurious shell readings on Codex-launched panes', () => { } beforeEach(() => { - clearRuntimeCompatibilityCacheForTests() - runtimeEnvironmentCall.mockReset() - runtimeEnvironmentTransportCall.mockReset() - runtimeEnvironmentTransportCall.mockImplementation((args: RuntimeEnvironmentCallRequest) => { - return createCompatibleRuntimeStatusResponseIfNeeded(args) ?? runtimeEnvironmentCall(args) - }) seedPane({ launchAgent: 'codex' }) ;(globalThis as { window: typeof window }).window = { ...originalWindow, @@ -77,10 +64,6 @@ describe('spurious shell readings on Codex-launched panes', () => { activeAccountId: 'account-b' }), listStalePanes: vi.fn().mockResolvedValue([]) - }, - runtimeEnvironments: { - ...originalWindow?.api?.runtimeEnvironments, - call: runtimeEnvironmentTransportCall } } } as unknown as typeof window @@ -149,27 +132,6 @@ describe('spurious shell readings on Codex-launched panes', () => { expect(useAppStore.getState().codexRestartNoticeByPtyId).toEqual({}) }) - it('never routes a remote runtime pane through the local confirm bridge', async () => { - seedPane({ ptyId: 'remote:env-1@@term-1', launchAgent: 'codex' }) - useAppStore.setState({ settings: { activeRuntimeEnvironmentId: 'env-1' } as never }) - runtimeEnvironmentCall.mockResolvedValue({ - id: 'rpc-1', - ok: true, - result: { process: { foregroundProcess: 'zsh', hasChildProcesses: false } }, - _meta: { runtimeId: 'remote-runtime' } - }) - - await markLiveCodexSessionsForRestart({ - owner: focusedOwner(), - previousAccountLabel: ACCOUNT_A, - nextAccountLabel: ACCOUNT_B - }) - - // Local fresh scans say nothing about remote PTYs; reject cross-lane confirmation. - expect(window.api.pty.confirmForegroundProcess).not.toHaveBeenCalled() - expect(useAppStore.getState().codexRestartNoticeByPtyId).toEqual({}) - }) - it('heals a spurious shell reading before the restored-pane stale lookup', async () => { vi.mocked(window.api.pty.confirmForegroundProcess).mockResolvedValue('codex') vi.mocked(window.api.codexAccounts.listStalePanes).mockResolvedValue([ diff --git a/src/renderer/src/lib/codex-session-restart.test.ts b/src/renderer/src/lib/codex-session-restart.test.ts index 0cc38d04b31..d90df5d9435 100644 --- a/src/renderer/src/lib/codex-session-restart.test.ts +++ b/src/renderer/src/lib/codex-session-restart.test.ts @@ -11,10 +11,7 @@ const markForFocusedHost = ( ...args }) import type { TuiAgent } from '../../../shared/tui-agent' -import { - markLiveCodexSessionsForRestart, - markRestoredStaleCodexSessionsForRestart -} from './codex-session-restart' +import { markLiveCodexSessionsForRestart } from './codex-session-restart' import { createCompatibleRuntimeStatusResponseIfNeeded, type RuntimeEnvironmentCallRequest @@ -106,25 +103,6 @@ describe('markLiveCodexSessionsForRestart', () => { } }) - it('marks a live Codex PTY for restart', async () => { - vi.mocked(window.api.pty.inspectProcess).mockResolvedValue({ - foregroundProcess: 'codex', - hasChildProcesses: false - }) - - await markForFocusedHost({ - previousAccountLabel: ACCOUNT_A, - nextAccountLabel: ACCOUNT_B - }) - - expect(window.api.pty.inspectProcess).toHaveBeenCalledWith('pty-1') - expect(useAppStore.getState().codexRestartNoticeByPtyId['pty-1']).toEqual({ - previousAccountLabel: ACCOUNT_A, - nextAccountLabel: ACCOUNT_B - }) - expect(window.api.codexAccounts.listStalePanes).not.toHaveBeenCalled() - }) - it('marks every live Codex split pane and ignores non-Codex panes', async () => { useAppStore.setState({ tabsByWorktree: { @@ -179,20 +157,6 @@ describe('markLiveCodexSessionsForRestart', () => { }) }) - it('does not mark non-codex foreground processes', async () => { - vi.mocked(window.api.pty.inspectProcess).mockResolvedValue({ - foregroundProcess: 'zsh', - hasChildProcesses: false - }) - - await markForFocusedHost({ - previousAccountLabel: ACCOUNT_A, - nextAccountLabel: ACCOUNT_B - }) - - expect(useAppStore.getState().codexRestartNoticeByPtyId).toEqual({}) - }) - it('marks a launcher-started Codex pane whose deepest process is a subagent', async () => { // Windows reports pwsh -> node -> codex.exe -> claude.exe as "claude". setLaunchAgentOnFirstTab('codex') @@ -266,40 +230,6 @@ describe('markLiveCodexSessionsForRestart', () => { }) }) - it('treats codex.exe as codex for Windows PTYs', async () => { - vi.mocked(window.api.pty.inspectProcess).mockResolvedValue({ - foregroundProcess: 'codex.exe', - hasChildProcesses: false - }) - - await markForFocusedHost({ - previousAccountLabel: ACCOUNT_A, - nextAccountLabel: ACCOUNT_B - }) - - expect(useAppStore.getState().codexRestartNoticeByPtyId['pty-1']).toEqual({ - previousAccountLabel: ACCOUNT_A, - nextAccountLabel: ACCOUNT_B - }) - }) - - it('treats codex-prefixed packaged binaries as codex', async () => { - vi.mocked(window.api.pty.inspectProcess).mockResolvedValue({ - foregroundProcess: 'codex-aarch64-ap', - hasChildProcesses: false - }) - - await markForFocusedHost({ - previousAccountLabel: ACCOUNT_A, - nextAccountLabel: ACCOUNT_B - }) - - expect(useAppStore.getState().codexRestartNoticeByPtyId['pty-1']).toEqual({ - previousAccountLabel: ACCOUNT_A, - nextAccountLabel: ACCOUNT_B - }) - }) - it('clears stale restart notices when the selected account switches back to the live pane account', async () => { vi.mocked(window.api.pty.inspectProcess).mockResolvedValue({ foregroundProcess: 'codex', @@ -512,19 +442,6 @@ describe('markLiveCodexSessionsForRestart lane scoping', () => { } }) - it('leaves a live remote Codex pane alone on a host switch, and never inspects it', async () => { - seedPanes([{ ptyId: 'remote:env-1@@term-1' }]) - - await markForFocusedHost({ - previousAccountLabel: ACCOUNT_A, - nextAccountLabel: ACCOUNT_B, - target: { runtime: 'host' } - }) - - expect(useAppStore.getState().codexRestartNoticeByPtyId).toEqual({}) - expect(runtimeEnvironmentCall).not.toHaveBeenCalled() - }) - it('leaves a live SSH-connection Codex pane alone on a host switch', async () => { seedPanes([{ ptyId: 'ssh:my-box@@pty-7' }]) @@ -571,19 +488,6 @@ describe('markLiveCodexSessionsForRestart lane scoping', () => { }) }) - it('still marks a local host pane when the switch names no target at all', async () => { - seedPanes([{ ptyId: 'pty-1' }]) - - await markForFocusedHost({ - previousAccountLabel: ACCOUNT_A, - nextAccountLabel: ACCOUNT_B - }) - - expect(useAppStore.getState().codexRestartNoticeByPtyId).toEqual({ - 'pty-1': { previousAccountLabel: ACCOUNT_A, nextAccountLabel: ACCOUNT_B } - }) - }) - // Test a WSL pane whose foreground is Codex so lane exclusion cannot accidentally rely on wsl.exe failing identity checks. it('leaves a WSL Codex pane alone on a host switch even when its foreground is codex', async () => { seedPanes([{ ptyId: 'pty-wsl' }], { wt1: '\\\\wsl.localhost\\Ubuntu\\home\\dev\\orca' }) @@ -693,26 +597,6 @@ describe('markLiveCodexSessionsForRestart lane scoping', () => { }) }) - it('suppresses a null-to-account reauth notice when the recorded pane is already current', async () => { - seedPanes([{ ptyId: 'pty-1' }]) - vi.mocked(window.api.codexAccounts.listRecordedPaneLanes).mockResolvedValue({ - 'pty-1': 'host' - }) - - await markForFocusedHost({ - previousAccountLabel: 'System default', - nextAccountLabel: ACCOUNT_A, - previousAccountId: null, - nextAccountId: 'account-a', - target: { runtime: 'host' } - }) - - expect(window.api.codexAccounts.listStalePanes).toHaveBeenCalledWith({ - ptyIds: ['pty-1'] - }) - expect(useAppStore.getState().codexRestartNoticeByPtyId).toEqual({}) - }) - it('classifies current, stale, and unrecorded panes independently', async () => { seedPanes([{ ptyId: 'pty-current' }, { ptyId: 'pty-stale' }, { ptyId: 'pty-unknown' }]) vi.mocked(window.api.codexAccounts.listRecordedPaneLanes).mockResolvedValue({ @@ -784,132 +668,5 @@ describe('markLiveCodexSessionsForRestart lane scoping', () => { nextAccountLabel: ACCOUNT_B }) }) - - it('asks only about panes main could have recorded', async () => { - seedPanes([ - { ptyId: 'pty-1' }, - { ptyId: 'remote:env-1@@term-1' }, - { ptyId: 'ssh:my-box@@pty-7' } - ]) - - await markForFocusedHost({ - previousAccountLabel: ACCOUNT_A, - nextAccountLabel: ACCOUNT_B, - target: { runtime: 'host' } - }) - - // Main records only daemon host spawns; skip foreign IDs and batch the remaining lookup. - expect(window.api.codexAccounts.listRecordedPaneLanes).toHaveBeenCalledTimes(1) - expect(window.api.codexAccounts.listRecordedPaneLanes).toHaveBeenCalledWith({ - ptyIds: ['pty-1'] - }) - }) - }) -}) - -describe('markRestoredStaleCodexSessionsForRestart', () => { - const originalWindow = (globalThis as { window?: typeof window }).window - - beforeEach(() => { - clearRuntimeCompatibilityCacheForTests() - useAppStore.setState({ - tabsByWorktree: { - wt1: [ - { - id: 'tab-1', - ptyId: 'pty-1', - worktreeId: 'wt1', - title: 'orca-1', - customTitle: null, - color: null, - sortOrder: 0, - createdAt: 1 - } - ] - }, - ptyIdsByTabId: { 'tab-1': ['pty-1'] }, - pendingPaneAccountRestarts: {}, - codexRestartNoticeByPtyId: {} - }) - ;(globalThis as { window: typeof window }).window = { - ...originalWindow, - api: { - ...originalWindow?.api, - pty: { - ...originalWindow?.api?.pty, - getForegroundProcess: vi.fn(), - hasChildProcesses: vi.fn().mockResolvedValue(false), - inspectProcess: vi - .fn() - .mockResolvedValue({ foregroundProcess: 'codex', hasChildProcesses: false }), - confirmForegroundProcess: vi.fn().mockResolvedValue(null) - }, - codexAccounts: { - ...originalWindow?.api?.codexAccounts, - list: vi.fn().mockResolvedValue({ - accounts: [ - { id: 'account-a', email: ACCOUNT_A }, - { id: 'account-b', email: ACCOUNT_B } - ], - activeAccountId: 'account-b' - }), - listStalePanes: vi.fn().mockResolvedValue([]) - } - } - } as unknown as typeof window - }) - - afterEach(() => { - if (originalWindow) { - ;(globalThis as { window: typeof window }).window = originalWindow - } else { - delete (globalThis as { window?: typeof window }).window - } - }) - - it('re-raises the prompt for a pane the app restart forgot', async () => { - vi.mocked(window.api.codexAccounts.listStalePanes).mockResolvedValue([ - { ptyId: 'pty-1', launchAccountId: 'account-a', activeAccountId: 'account-b' } - ]) - - await markRestoredStaleCodexSessionsForRestart() - - expect(window.api.codexAccounts.listStalePanes).toHaveBeenCalledWith({ ptyIds: ['pty-1'] }) - expect(useAppStore.getState().codexRestartNoticeByPtyId['pty-1']).toEqual({ - previousAccountLabel: ACCOUNT_A, - nextAccountLabel: ACCOUNT_B, - previousAccountId: 'account-a', - nextAccountId: 'account-b' - }) - }) - - it('labels the system default when a pane launched without a managed account', async () => { - vi.mocked(window.api.codexAccounts.listStalePanes).mockResolvedValue([ - { ptyId: 'pty-1', launchAccountId: null, activeAccountId: 'account-b' } - ]) - - await markRestoredStaleCodexSessionsForRestart() - - expect(useAppStore.getState().codexRestartNoticeByPtyId['pty-1']?.previousAccountLabel).toBe( - 'System default' - ) - }) - - it('prompts nothing when every restored pane is on the selected account', async () => { - await markRestoredStaleCodexSessionsForRestart() - - expect(useAppStore.getState().codexRestartNoticeByPtyId).toEqual({}) - }) - - it('skips the account lookup entirely when no pane is running Codex', async () => { - vi.mocked(window.api.pty.inspectProcess).mockResolvedValue({ - foregroundProcess: 'zsh', - hasChildProcesses: false - }) - - await markRestoredStaleCodexSessionsForRestart() - - expect(window.api.codexAccounts.listStalePanes).not.toHaveBeenCalled() - expect(useAppStore.getState().codexRestartNoticeByPtyId).toEqual({}) }) }) diff --git a/src/renderer/src/lib/codex-stale-pane-sweep.test.ts b/src/renderer/src/lib/codex-stale-pane-sweep.test.ts index b326825b5b9..2426c3e53b9 100644 --- a/src/renderer/src/lib/codex-stale-pane-sweep.test.ts +++ b/src/renderer/src/lib/codex-stale-pane-sweep.test.ts @@ -2,8 +2,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { useAppStore } from '@/store' import { notifyCodexPaneBoundForStaleSweep, - resetCodexStalePaneSweepForTests, - sweepRestoredCodexPanesForStaleAccounts + resetCodexStalePaneSweepForTests } from './codex-stale-pane-sweep' const ACCOUNT_A = 'account-a@example.com' @@ -80,36 +79,6 @@ describe('notifyCodexPaneBoundForStaleSweep', () => { } }) - it('raises the prompt once the pane PTY actually binds', async () => { - vi.mocked(window.api.codexAccounts.listStalePanes).mockResolvedValue([STALE_PANE]) - - notifyCodexPaneBoundForStaleSweep('pty-1') - expect(window.api.pty.inspectProcess).not.toHaveBeenCalled() - - await vi.advanceTimersByTimeAsync(300) - - expect(window.api.codexAccounts.listStalePanes).toHaveBeenCalledWith({ ptyIds: ['pty-1'] }) - expect(useAppStore.getState().codexRestartNoticeByPtyId['pty-1']).toEqual({ - previousAccountLabel: ACCOUNT_A, - nextAccountLabel: ACCOUNT_B, - previousAccountId: 'account-a', - nextAccountId: 'account-b' - }) - }) - - it('coalesces a burst of startup binds into one sweep', async () => { - useAppStore.setState({ ptyIdsByTabId: { 'tab-1': ['pty-1', 'pty-2'] } }) - vi.mocked(window.api.codexAccounts.listStalePanes).mockResolvedValue([STALE_PANE]) - - notifyCodexPaneBoundForStaleSweep('pty-1') - notifyCodexPaneBoundForStaleSweep('pty-2') - await vi.advanceTimersByTimeAsync(300) - - expect(window.api.codexAccounts.listStalePanes).toHaveBeenCalledExactlyOnceWith({ - ptyIds: ['pty-1', 'pty-2'] - }) - }) - it('still coalesces a burst whose binds land milliseconds apart', async () => { // Staggered startup binds must still batch registry reads rather than make one round trip per pane. useAppStore.setState({ ptyIdsByTabId: { 'tab-1': ['pty-1', 'pty-2'] } }) @@ -125,24 +94,6 @@ describe('notifyCodexPaneBoundForStaleSweep', () => { }) }) - it('retries a PTY whose process read is unusable until the reattach settles', async () => { - vi.mocked(window.api.codexAccounts.listStalePanes).mockResolvedValue([STALE_PANE]) - vi.mocked(window.api.pty.inspectProcess).mockRejectedValueOnce(new Error('terminal_gone')) - - notifyCodexPaneBoundForStaleSweep('pty-1') - await vi.advanceTimersByTimeAsync(300) - expect(useAppStore.getState().codexRestartNoticeByPtyId).toEqual({}) - - await vi.advanceTimersByTimeAsync(1500) - - expect(useAppStore.getState().codexRestartNoticeByPtyId['pty-1']).toEqual({ - previousAccountLabel: ACCOUNT_A, - nextAccountLabel: ACCOUNT_B, - previousAccountId: 'account-a', - nextAccountId: 'account-b' - }) - }) - it('gives up after a bounded ladder instead of polling forever', async () => { vi.mocked(window.api.pty.inspectProcess).mockRejectedValue(new Error('terminal_gone')) @@ -411,25 +362,4 @@ describe('notifyCodexPaneBoundForStaleSweep', () => { nextAccountId: 'account-b' }) }) - - it('re-raises the prompt for a restored pane whose tab never mounts', async () => { - // Restored background tabs must get stale-account checks without ever mounting a pane. - vi.mocked(window.api.codexAccounts.listStalePanes).mockResolvedValue([STALE_PANE]) - - sweepRestoredCodexPanesForStaleAccounts({ - ptyIdsByTabId: { 'tab-1': ['pty-1', 'remote:env-1@@term-1'] } - }) - await vi.advanceTimersByTimeAsync(300) - - expect(window.api.codexAccounts.listStalePanes).toHaveBeenCalledExactlyOnceWith({ - ptyIds: ['pty-1'] - }) - expect(inspectCallCountFor('remote:env-1@@term-1')).toBe(0) - expect(useAppStore.getState().codexRestartNoticeByPtyId['pty-1']).toEqual({ - previousAccountLabel: ACCOUNT_A, - nextAccountLabel: ACCOUNT_B, - previousAccountId: 'account-a', - nextAccountId: 'account-b' - }) - }) }) diff --git a/src/renderer/src/lib/comment-body-line-count.test.ts b/src/renderer/src/lib/comment-body-line-count.test.ts index cea3d4684a1..633d1c64e37 100644 --- a/src/renderer/src/lib/comment-body-line-count.test.ts +++ b/src/renderer/src/lib/comment-body-line-count.test.ts @@ -10,13 +10,6 @@ afterEach(() => { }) describe('comment body layout line count', () => { - it('matches normal newline counts used for comment layout', () => { - expect(getCommentBodyLayoutLineCount('')).toBe(1) - expect(getCommentBodyLayoutLineCount('one line')).toBe(1) - expect(getCommentBodyLayoutLineCount('one\ntwo\nthree')).toBe(3) - expect(getCommentBodyLayoutLineCount('one\r\ntwo')).toBe(2) - }) - it('caps newline-heavy pasted comments without splitting the body', () => { const split = vi.spyOn(String.prototype, 'split') const charCodeAt = vi.spyOn(String.prototype, 'charCodeAt') diff --git a/src/renderer/src/lib/comment-body-submit-state.test.ts b/src/renderer/src/lib/comment-body-submit-state.test.ts index 68e3ee992e0..19dfdf782d3 100644 --- a/src/renderer/src/lib/comment-body-submit-state.test.ts +++ b/src/renderer/src/lib/comment-body-submit-state.test.ts @@ -6,19 +6,6 @@ import { } from './comment-body-submit-state' describe('comment body submit state', () => { - it('treats blank bodies as empty without producing submit text', () => { - expect(hasBoundedCommentBodyText(' \n\t ')).toBe(false) - expect(getCommentBodySubmitState(' \n\t ')).toEqual({ status: 'empty' }) - }) - - it('returns trimmed submit text for bounded comment bodies', () => { - expect(hasBoundedCommentBodyText(' hello world ')).toBe(true) - expect(getCommentBodySubmitState(' hello world ')).toEqual({ - status: 'ready', - body: 'hello world' - }) - }) - it('counts multibyte leading whitespace toward the scan budget', () => { const oversizedLeadingWhitespace = '\u3000'.repeat( Math.floor(COMMENT_BODY_NONBLANK_SCAN_MAX_BYTES / 3) + 1 diff --git a/src/renderer/src/lib/composer-create-handoff.test.ts b/src/renderer/src/lib/composer-create-handoff.test.ts index dca3761027d..e61e880b47f 100644 --- a/src/renderer/src/lib/composer-create-handoff.test.ts +++ b/src/renderer/src/lib/composer-create-handoff.test.ts @@ -78,17 +78,6 @@ describe('scheduleComposerCreateHandoff', () => { vi.clearAllMocks() }) - it('starts directly without staging or closing when preflight is fast', () => { - const { handoff, onHandOff } = schedule() - handoff.start(finalRequest) - vi.runAllTimers() - - expect(flow.run).toHaveBeenCalledWith(finalRequest) - expect(flow.begin).not.toHaveBeenCalled() - expect(onHandOff).not.toHaveBeenCalled() - expect(handoff.fail('boom')).toBe(false) - }) - it('closes the composer onto a staged row when preflight stalls, then continues it', () => { const { handoff, onHandOff } = handOff() @@ -145,12 +134,4 @@ describe('scheduleComposerCreateHandoff', () => { expect(flow.continue).not.toHaveBeenCalled() expect(toastError).not.toHaveBeenCalled() }) - - it('does not stage or close again after the user dismissed the composer', () => { - const { onHandOff } = schedule(() => true) - vi.runAllTimers() - - expect(flow.begin).not.toHaveBeenCalled() - expect(onHandOff).not.toHaveBeenCalled() - }) }) diff --git a/src/renderer/src/lib/composer-issue-command.test.ts b/src/renderer/src/lib/composer-issue-command.test.ts deleted file mode 100644 index f9245af415a..00000000000 --- a/src/renderer/src/lib/composer-issue-command.test.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { - buildTrustedComposerIssueCommand, - shouldPrepareComposerIssueCommand -} from './composer-issue-command' - -const readyInput = { - enabled: true, - provider: 'github' as const, - issueNumber: 42, - template: 'gh issue view {{issue}} --repo {{artifact_url}}', - artifactUrl: 'https://github.com/stablyai/orca/issues/42' -} - -describe('composer issue command', () => { - it('renders a trusted GitHub issue command', () => { - expect(buildTrustedComposerIssueCommand({ ...readyInput, trustDecision: 'run' })).toEqual({ - command: 'gh issue view 42 --repo https://github.com/stablyai/orca/issues/42' - }) - }) - - it('skips untrusted, disabled, PR, and empty commands', () => { - expect( - buildTrustedComposerIssueCommand({ ...readyInput, trustDecision: 'skip' }) - ).toBeUndefined() - expect(shouldPrepareComposerIssueCommand({ ...readyInput, enabled: false })).toBe(false) - expect(shouldPrepareComposerIssueCommand({ ...readyInput, issueNumber: null })).toBe(false) - expect(shouldPrepareComposerIssueCommand({ ...readyInput, template: ' ' })).toBe(false) - }) -}) diff --git a/src/renderer/src/lib/connection-context.test.ts b/src/renderer/src/lib/connection-context.test.ts index 3e1803cdb17..bfb3d7489ad 100644 --- a/src/renderer/src/lib/connection-context.test.ts +++ b/src/renderer/src/lib/connection-context.test.ts @@ -11,7 +11,6 @@ import { getConnectionIdFromState, isWorktreeConnectionResolved } from './connection-context' -import { FLOATING_TERMINAL_WORKTREE_ID } from '../../../shared/constants' import { folderWorkspaceKey } from '../../../shared/workspace-scope' import { createConnectionIdForFileSelector } from './connection-owner-resolution' @@ -53,85 +52,6 @@ describe('getConnectionId', () => { useAppStore.setState(initialState, true) }) - it('resolves SSH targets from composite worktree IDs before worktree discovery completes', () => { - useAppStore.setState({ - repos: [ - makeRepo({ - id: 'repo-ssh', - connectionId: 'ssh-1' - }) - ], - worktreesByRepo: {} - }) - - expect(getConnectionId('repo-ssh::/home/neil/repo-feature')).toBe('ssh-1') - }) - - it('returns null for known local repos without a discovered worktree', () => { - useAppStore.setState({ - repos: [makeRepo({ id: 'repo-local' })], - worktreesByRepo: {} - }) - - expect(getConnectionId('repo-local::/Users/me/repo-feature')).toBeNull() - }) - - it('returns undefined when neither the worktree nor repo is known', () => { - useAppStore.setState({ - repos: [], - worktreesByRepo: {} - }) - - expect(getConnectionId('repo-missing::/tmp/repo-feature')).toBeUndefined() - }) - - it('resolves SSH targets for folder workspaces from repos in the folder scope', () => { - useAppStore.setState({ - folderWorkspaces: [ - { - id: 'folder-workspace-1', - projectGroupId: 'group-1', - name: 'Platform workspace', - folderPath: '/home/neil/platform', - linkedTask: null, - comment: '', - isArchived: false, - isUnread: false, - isPinned: false, - sortOrder: 1, - lastActivityAt: 0, - createdAt: 1, - updatedAt: 1 - } - ], - projectGroups: [ - { - id: 'group-1', - name: 'Platform', - parentPath: '/home/neil/platform', - parentGroupId: null, - createdFrom: 'folder-scan', - tabOrder: 0, - isCollapsed: false, - color: null, - createdAt: 1, - updatedAt: 1 - } - ], - repos: [ - makeRepo({ - id: 'repo-ssh', - path: '/home/neil/platform/api', - projectGroupId: 'group-1', - connectionId: 'ssh-1' - }) - ], - worktreesByRepo: {} - }) - - expect(getConnectionId(folderWorkspaceKey('folder-workspace-1'))).toBe('ssh-1') - }) - it('resolves SSH targets for repo-less folder workspaces from persisted scope provenance', () => { useAppStore.setState({ folderWorkspaces: [ @@ -281,46 +201,6 @@ describe('getConnectionId', () => { expect(getConnectionIdForFile(workspaceKey, '/home/neil/platform/README.md')).toBeUndefined() }) - it('resolves folder workspace combined diff sections by child repo path', () => { - const workspaceKey = folderWorkspaceKey('folder-workspace-1') - useAppStore.setState({ - folderWorkspaces: [makeFolderWorkspace()], - projectGroups: [ - { - id: 'group-1', - name: 'Platform', - parentPath: '/home/neil/platform', - parentGroupId: null, - createdFrom: 'folder-scan', - tabOrder: 0, - isCollapsed: false, - color: null, - createdAt: 1, - updatedAt: 1 - } - ], - repos: [ - makeRepo({ - id: 'repo-local', - path: '/home/neil/platform/web', - projectGroupId: 'group-1' - }), - makeRepo({ - id: 'repo-ssh', - path: '/home/neil/platform/api', - projectGroupId: 'group-1', - connectionId: 'ssh-1' - }) - ], - worktreesByRepo: {} - }) - - expect(getConnectionIdForFile(workspaceKey, '/home/neil/platform')).toBeUndefined() - expect(getConnectionIdForFile(workspaceKey, '/home/neil/platform/api/src/index.ts')).toBe( - 'ssh-1' - ) - }) - it('keeps explicit folder workspace provenance isolated from unrelated same-path SSH repos', () => { useAppStore.setState({ folderWorkspaces: [ @@ -401,21 +281,6 @@ describe('getConnectionId', () => { expect(isWorktreeConnectionResolved('repo-ssh::/home/neil/repo')).toBe(true) }) - it('treats null worktrees and folder workspaces as resolved owners', () => { - useAppStore.setState({ repos: [], worktreesByRepo: {} }) - expect(isWorktreeConnectionResolved(null)).toBe(true) - // Folder workspaces resolve per-file via getConnectionIdForFile. - expect(isWorktreeConnectionResolved(folderWorkspaceKey('folder-workspace-1'))).toBe(true) - }) - - it('treats the floating workspace as a resolved local owner (#6831)', () => { - useAppStore.setState({ repos: [], worktreesByRepo: {} }) - - expect(getConnectionId(FLOATING_TERMINAL_WORKTREE_ID)).toBeNull() - expect(getConnectionIdForFile(FLOATING_TERMINAL_WORKTREE_ID, '/tmp/orca/note.md')).toBeNull() - expect(isWorktreeConnectionResolved(FLOATING_TERMINAL_WORKTREE_ID)).toBe(true) - }) - it('keeps normalized same-path folder repo ambiguity when resolving files', () => { const workspaceKey = folderWorkspaceKey('folder-workspace-1') useAppStore.setState({ @@ -534,17 +399,6 @@ describe('getConnectionIdFromState', () => { expect(getStateSpy).not.toHaveBeenCalled() }) - it('resolves SSH repo provenance for non-folder worktrees from the passed-in state', () => { - const state: ConnectionContextState = { - folderWorkspaces: [], - projectGroups: [], - repos: [makeRepo({ id: 'repo-ssh', connectionId: 'ssh-2' })], - worktreesByRepo: {} - } - - expect(getConnectionIdFromState(state, 'repo-ssh::/home/neil/repo-feature')).toBe('ssh-2') - }) - it('refuses to resolve a connection when duplicate repo rows disagree about the owning host', () => { // Same-ID repo rows with conflicting owners must not lend a client SSH connection to a runtime (#17799). const state: ConnectionContextState = { @@ -560,20 +414,6 @@ describe('getConnectionIdFromState', () => { expect(getConnectionIdFromState(state, 'repo-dup::/home/neil/repo-feature')).toBeUndefined() }) - it('still resolves duplicate repo rows that agree about the owning host', () => { - const state: ConnectionContextState = { - folderWorkspaces: [], - projectGroups: [], - repos: [ - makeRepo({ id: 'repo-dup', connectionId: 'ssh-same' }), - makeRepo({ id: 'repo-dup', connectionId: 'ssh-same', path: '/home/neil/other' }) - ], - worktreesByRepo: {} - } - - expect(getConnectionIdFromState(state, 'repo-dup::/home/neil/repo-feature')).toBe('ssh-same') - }) - it('never hands a worktree the SSH connection of a different host', () => { // Host lookup misses must not fall back by repo ID to another SSH machine’s connection (#11163). const state: ConnectionContextState = { @@ -750,17 +590,6 @@ describe('getConnectionIdFromState', () => { expect(getConnectionIdFromState(local, key)).toBeNull() }) - it('returns null for a null worktreeId', () => { - const state: ConnectionContextState = { - folderWorkspaces: [], - projectGroups: [], - repos: [], - worktreesByRepo: {} - } - - expect(getConnectionIdFromState(state, null)).toBeNull() - }) - it('recomputes a retained file-owner selector when ownership slices hydrate', () => { const selector = createConnectionIdForFileSelector( 'repo-ssh::/home/neil/repo-feature', diff --git a/src/renderer/src/lib/crash-diagnostics.test.ts b/src/renderer/src/lib/crash-diagnostics.test.ts index 3c6cf86fd42..644dd71f1e5 100644 --- a/src/renderer/src/lib/crash-diagnostics.test.ts +++ b/src/renderer/src/lib/crash-diagnostics.test.ts @@ -59,15 +59,6 @@ describe('renderer crash diagnostics', () => { vi.unstubAllGlobals() }) - it('records renderer breadcrumbs through preload', () => { - diagnostics.recordRendererCrashBreadcrumb('renderer_bootstrap_started', { dev: true }) - - expect(recordBreadcrumbMock).toHaveBeenCalledWith({ - name: 'renderer_bootstrap_started', - data: { dev: true } - }) - }) - it('installs startup, error, rejection, and memory breadcrumbs once', () => { diagnostics.installRendererCrashDiagnostics() diagnostics.installRendererCrashDiagnostics() @@ -119,24 +110,6 @@ describe('renderer crash diagnostics', () => { }) }) - it('suppresses benign ResizeObserver loop errors instead of recording them', () => { - diagnostics.installRendererCrashDiagnostics() - recordBreadcrumbMock.mockClear() - - const preventDefault = vi.fn() - listeners.get('error')?.[0]?.({ - message: 'ResizeObserver loop completed with undelivered notifications.', - preventDefault - }) - listeners.get('error')?.[0]?.({ - message: 'ResizeObserver loop limit exceeded', - preventDefault - }) - - expect(preventDefault).toHaveBeenCalledTimes(2) - expect(recordBreadcrumbMock).not.toHaveBeenCalled() - }) - it('includes delivered and queued long tasks once in each memory sample', () => { let now = 0 let deliver = (_durations: number[]): void => {} @@ -186,33 +159,6 @@ describe('renderer crash diagnostics', () => { }) }) - it('records application errors that only mention a ResizeObserver loop', () => { - diagnostics.installRendererCrashDiagnostics() - recordBreadcrumbMock.mockClear() - - const preventDefault = vi.fn() - listeners.get('error')?.[0]?.({ - message: 'ResizeObserver loop failed while rendering the terminal', - preventDefault - }) - - expect(preventDefault).not.toHaveBeenCalled() - expect(recordBreadcrumbMock).toHaveBeenCalledWith({ - name: 'renderer_error', - data: expect.objectContaining({ - message: 'ResizeObserver loop failed while rendering the terminal' - }) - }) - }) - - it('does not throw when preload is unavailable', () => { - vi.stubGlobal('window', {}) - - expect(() => - diagnostics.recordRendererCrashBreadcrumb('renderer_bootstrap_started') - ).not.toThrow() - }) - it('emits one-shot renderer_memory_highwater breadcrumbs with profile counts', async () => { const memory = (window.performance as unknown as { memory: Record }).memory memory.usedJSHeapSize = 0.7 * memory.jsHeapSizeLimit @@ -271,19 +217,6 @@ describe('renderer crash diagnostics', () => { unregister() }) - it('skips highwater emission when heap readings are not finite', () => { - const memory = (window.performance as unknown as { memory: Record }).memory - memory.usedJSHeapSize = Number.NaN - - diagnostics.installRendererCrashDiagnostics() - - expect( - recordBreadcrumbMock.mock.calls.some( - (call) => (call[0] as { name: string }).name === 'renderer_memory_highwater' - ) - ).toBe(false) - }) - describe('exact V8 heap statistics', () => { const KB = 1024 let readHeapStatistics: ReturnType @@ -304,24 +237,6 @@ describe('renderer crash diagnostics', () => { readHeapStatistics }) - it('prefers exact statistics over performance.memory and labels the source', () => { - stubHeap(101) - - diagnostics.installRendererCrashDiagnostics() - - // Blink still reports 32MB; 101 proves exact data replaces it rather than merely accompanying it. - expect(recordBreadcrumbMock).toHaveBeenCalledWith({ - name: 'renderer_memory', - data: expect.objectContaining({ - usedHeapMB: 101, - heapLimitMB: 512, - heapSource: 'v8', - mallocedMB: 3, - blinkAllocatedMB: 7 - }) - }) - }) - it('observes growth that performance.memory quantizes away', () => { // Blink’s long-lived quantized bucket can hide heap growth; exact readings must still cross highwater thresholds. const quantized = (window.performance as unknown as { memory: Record }).memory @@ -351,36 +266,6 @@ describe('renderer crash diagnostics', () => { }) }) - it('omits the Blink field instead of emitting a junk value for it', () => { - // Missing source data must omit blinkAllocatedMB rather than emit NaN from undefined * 1024. - readHeapStatistics.mockReturnValue({ - usedHeapKB: 77 * KB, - totalHeapKB: 154 * KB, - heapLimitKB: 512 * KB, - mallocedKB: 3 * KB, - blinkAllocatedKB: undefined - }) - - diagnostics.installRendererCrashDiagnostics() - - const call = recordBreadcrumbMock.mock.calls.find( - ([entry]) => (entry as { name: string }).name === 'renderer_memory' - )?.[0] as { data: Record } - expect(call.data).toMatchObject({ usedHeapMB: 77, heapSource: 'v8', mallocedMB: 3 }) - expect(call.data).not.toHaveProperty('blinkAllocatedMB') - }) - - it('falls back to performance.memory when the bridge returns null', () => { - readHeapStatistics.mockReturnValue(null) - - diagnostics.installRendererCrashDiagnostics() - - expect(recordBreadcrumbMock).toHaveBeenCalledWith({ - name: 'renderer_memory', - data: expect.objectContaining({ usedHeapMB: 32, heapSource: 'quantized' }) - }) - }) - it('samples on an older shell whose preload lacks the bridge', () => { ;(window.api.crashReports as unknown as Record).readHeapStatistics = undefined @@ -436,22 +321,6 @@ describe('renderer crash diagnostics', () => { .filter((call) => (call[0] as { name: string }).name === 'renderer_memory_highwater') .map((call) => call[0] as { data: Record }) - it('reports the private footprint and what it holds outside the heap counters', async () => { - // Model Windows crash 36048e26: private memory far exceeds V8 heap, exposing native xterm/atlas growth that heap counters miss. - stubFootprint(618) - diagnostics.installRendererCrashDiagnostics() - await flush() - const tick = setIntervalMock.mock.calls[0][0] as () => void - tick() - - const latest = memoryCalls().at(-1)! - expect(latest.data).toMatchObject({ - usedHeapMB: 150, - privateMB: 618, - outsideHeapMB: 438 - }) - }) - it('arms the leak census on footprint even when the heap ratio never trips', async () => { // Low heap/limit ratio must not prevent the census that identifies native-memory growth. stubFootprint(618) @@ -492,17 +361,6 @@ describe('renderer crash diagnostics', () => { expect(highwaterCalls().at(-1)!.data).toMatchObject({ thresholdPrivateMB: 1000 }) }) - it('keeps sampling when the shell has no footprint bridge at all', async () => { - ;(window.api.crashReports as unknown as Record).readProcessMemory = undefined - - expect(() => diagnostics.installRendererCrashDiagnostics()).not.toThrow() - await flush() - const latest = memoryCalls().at(-1)! - expect(latest.data).toMatchObject({ usedHeapMB: 150 }) - expect(latest.data).not.toHaveProperty('privateMB') - expect(highwaterCalls()).toHaveLength(0) - }) - it('does not let a rejected footprint read break the heap sample', async () => { readProcessMemory.mockRejectedValue(new Error('bridge gone')) diff --git a/src/renderer/src/lib/create-untitled-markdown.test.ts b/src/renderer/src/lib/create-untitled-markdown.test.ts index a1d85fea6c8..8365c3696bf 100644 --- a/src/renderer/src/lib/create-untitled-markdown.test.ts +++ b/src/renderer/src/lib/create-untitled-markdown.test.ts @@ -1,9 +1,5 @@ import { afterEach, describe, expect, it, vi } from 'vitest' -import { - createUntitledMarkdownFile, - createUntitledMarkdownFileWithTemplateSelection -} from './create-untitled-markdown' -import { subscribeMarkdownTemplatePicker } from './markdown-template-picker-request' +import { createUntitledMarkdownFile } from './create-untitled-markdown' import { createCompatibleRuntimeStatusResponseIfNeeded, type RuntimeEnvironmentCallRequest @@ -108,47 +104,6 @@ describe('createUntitledMarkdownFile', () => { } ) - it('creates a plugin new-file type with its extension and verbatim template', async () => { - const pathExists = vi.fn().mockResolvedValueOnce(true).mockResolvedValue(false) - const createFile = vi.fn().mockResolvedValue(undefined) - const writeFile = vi.fn().mockResolvedValue(undefined) - vi.stubGlobal('window', { api: { fs: { pathExists, createFile, writeFile } } }) - const content = '{"type":"excalidraw","title":"{{title}}"}\n' - await expect( - createUntitledMarkdownFile('/repo', 'wt', undefined, LOCAL, { - newFile: { extension: '.excalidraw', content } - }) - ).resolves.toMatchObject({ - filePath: '/repo/untitled-2.excalidraw', - relativePath: 'untitled-2.excalidraw', - isUntitled: true, - deleteUntouchedOnClose: false - }) - // Why verbatim: placeholders are a Markdown template feature, not a plugin template one. - expect(writeFile).toHaveBeenCalledWith( - expect.objectContaining({ filePath: '/repo/untitled-2.excalidraw', content }) - ) - }) - - it('leaves an empty plugin new file unwritten, like a blank markdown file', async () => { - const writeFile = vi.fn() - vi.stubGlobal('window', { - api: { - fs: { - pathExists: vi.fn().mockResolvedValue(false), - createFile: vi.fn().mockResolvedValue(undefined), - writeFile - } - } - }) - const created = await createUntitledMarkdownFile('/repo', 'wt', undefined, LOCAL, { - newFile: { extension: '.drawio', content: '' } - }) - expect(created.filePath).toBe('/repo/untitled.drawio') - expect(created.deleteUntouchedOnClose).toBeUndefined() - expect(writeFile).not.toHaveBeenCalled() - }) - it('never overwrites or deletes a file another writer filled after the create', async () => { const createFile = vi.fn().mockResolvedValue(undefined) const writeFile = vi @@ -241,111 +196,6 @@ describe('createUntitledMarkdownFile', () => { }) }) - it('writes selected template content with placeholders after creating the untitled file', async () => { - const stat = vi.fn().mockRejectedValue(new Error('ENOENT: no such file')) - const createFile = vi.fn().mockResolvedValueOnce(undefined) - const readFile = vi.fn().mockResolvedValueOnce({ - content: '# {{ title }}\n{{date}}\n{{filename}}\n', - isBinary: false - }) - const writeFile = vi.fn().mockResolvedValueOnce(undefined) - - vi.stubGlobal('window', { - api: { - shell: { pathExists: vi.fn() }, - fs: { createFile, pathExists: vi.fn().mockResolvedValue(false), readFile, stat, writeFile } - } - }) - - await expect( - createUntitledMarkdownFile('/repo', 'wt-1', undefined, LOCAL, { - now: new Date(2026, 4, 29, 7, 5), - template: { - id: '.orca/templates/daily.md', - name: 'Daily', - filePath: '/repo/.orca/templates/daily.md', - relativePath: '.orca/templates/daily.md', - templateRelativePath: 'daily.md', - basename: 'daily.md' - } - }) - ).resolves.toMatchObject({ - filePath: '/repo/untitled.md', - relativePath: 'untitled.md', - deleteUntouchedOnClose: false - }) - - expect(readFile).toHaveBeenCalledWith( - expect.objectContaining({ filePath: '/repo/.orca/templates/daily.md' }) - ) - expect(createFile).toHaveBeenCalledWith( - expect.objectContaining({ filePath: '/repo/untitled.md' }) - ) - expect(writeFile).toHaveBeenCalledWith( - expect.objectContaining({ - filePath: '/repo/untitled.md', - content: '# Untitled\n2026-05-29\nuntitled.md\n' - }) - ) - }) - - it('discovers templates before creating a new markdown file and applies the selected template', async () => { - const readDir = vi.fn().mockResolvedValueOnce([ - { name: 'daily.md', isDirectory: false, isSymlink: false }, - { name: 'draft.txt', isDirectory: false, isSymlink: false } - ]) - const stat = vi.fn().mockRejectedValue(new Error('ENOENT: no such file')) - const createFile = vi.fn().mockResolvedValueOnce(undefined) - const readFile = vi.fn().mockResolvedValueOnce({ - content: '# {{title}}\n', - isBinary: false - }) - const writeFile = vi.fn().mockResolvedValueOnce(undefined) - const pathExists = vi.fn(async ({ filePath }: { filePath: string }) => - filePath.endsWith('/.orca/templates') - ) - const unsubscribe = subscribeMarkdownTemplatePicker((request) => { - const template = request.templates[0] - if (!template) { - throw new Error('Expected a discovered template') - } - request.resolve({ type: 'template', template }) - }) - - vi.stubGlobal('window', { - api: { - shell: { pathExists: vi.fn() }, - fs: { - createFile, - pathExists, - readDir, - readFile, - stat, - writeFile - } - } - }) - - try { - await expect( - createUntitledMarkdownFileWithTemplateSelection('/repo', 'wt-1', undefined, LOCAL) - ).resolves.toMatchObject({ - filePath: '/repo/untitled.md', - relativePath: 'untitled.md', - deleteUntouchedOnClose: false - }) - } finally { - unsubscribe() - } - - expect(readDir).toHaveBeenCalledWith( - expect.objectContaining({ dirPath: '/repo/.orca/templates' }) - ) - expect(writeFile).toHaveBeenCalledWith( - expect.objectContaining({ filePath: '/repo/untitled.md', content: '# Untitled\n' }) - ) - }) - it('creates untitled files through the selected runtime environment', async () => { clearRuntimeCompatibilityCacheForTests() const stat = vi.fn() diff --git a/src/renderer/src/lib/default-creation-host.test.ts b/src/renderer/src/lib/default-creation-host.test.ts deleted file mode 100644 index 0537648793c..00000000000 --- a/src/renderer/src/lib/default-creation-host.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { - defaultCreationHost, - defaultScopeHost, - defaultScopeSource, - rowLessSourceTarget -} from './default-creation-host' - -describe('defaultCreationHost', () => { - it('is this computer unless a server is chosen', () => { - expect(defaultCreationHost(null)).toEqual({ kind: 'local' }) - expect(defaultCreationHost({ activeRuntimeEnvironmentId: null })).toEqual({ kind: 'local' }) - expect(defaultCreationHost({ activeRuntimeEnvironmentId: ' env-a ' })).toEqual({ - kind: 'environment', - environmentId: 'env-a' - }) - }) -}) - -describe('row-less sources', () => { - it('starts on the default host until a source or host is named', () => { - const settings = { activeRuntimeEnvironmentId: 'env-a' } - expect(defaultScopeHost(settings)).toEqual({ kind: 'environment', environmentId: 'env-a' }) - expect(rowLessSourceTarget(defaultScopeSource(settings))).toEqual({ - kind: 'environment', - environmentId: 'env-a' - }) - expect(rowLessSourceTarget({ kind: 'local' })).toEqual({ kind: 'local' }) - expect( - rowLessSourceTarget({ - kind: 'task-source', - provider: 'linear', - projectId: 'p', - hostId: 'runtime:env-b' - }) - ).toEqual({ kind: 'environment', environmentId: 'env-b' }) - }) -}) diff --git a/src/renderer/src/lib/desktop-created-server-tabs-switch-off.test.ts b/src/renderer/src/lib/desktop-created-server-tabs-switch-off.test.ts deleted file mode 100644 index 296986603d2..00000000000 --- a/src/renderer/src/lib/desktop-created-server-tabs-switch-off.test.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { describe, expect, it, vi } from 'vitest' -import { - DESKTOP_CREATED_SERVER_TAB_HOLDS, - forgetDesktopCreatedServerTabsOf, - noteDesktopCreatedServerTab, - pruneDesktopCreatedServerTabs -} from './desktop-created-server-tabs' - -vi.mock('../../../shared/layout-owner-switch', () => ({ ORCA_LAYOUT_OWNER_IS_MAIN: false })) - -describe('the desktop-created server tab record in a switch-off build', () => { - it('never reads nor keeps the record', () => { - const storage = { getItem: vi.fn(() => null), setItem: vi.fn(), removeItem: vi.fn() } - vi.stubGlobal('localStorage', storage) - noteDesktopCreatedServerTab('web-env-1', 'repo::/worktree', 'simulator-1') - pruneDesktopCreatedServerTabs({ unifiedTabsByWorktree: {} }) - forgetDesktopCreatedServerTabsOf('repo::/worktree') - expect(DESKTOP_CREATED_SERVER_TAB_HOLDS.isTabHeld('repo::/worktree', 'simulator-1')).toBe(false) - expect(storage.getItem).not.toHaveBeenCalled() - expect(storage.setItem).not.toHaveBeenCalled() - expect(storage.removeItem).not.toHaveBeenCalled() - }) -}) diff --git a/src/renderer/src/lib/desktop-created-server-tabs.test.ts b/src/renderer/src/lib/desktop-created-server-tabs.test.ts index 23954fa607d..7f264e0cca9 100644 --- a/src/renderer/src/lib/desktop-created-server-tabs.test.ts +++ b/src/renderer/src/lib/desktop-created-server-tabs.test.ts @@ -37,10 +37,6 @@ import { DESKTOP_CREATED_SERVER_TAB_HOLDS, _reloadDesktopCreatedServerTabsForTests, _resetDesktopCreatedServerTabsForTests, - forgetDesktopCreatedServerTab, - forgetDesktopCreatedServerTabsOf, - forgetPublishedServerTabs, - isDesktopCreatedServerTab, noteDesktopCreatedServerTab } from './desktop-created-server-tabs' @@ -56,40 +52,6 @@ beforeEach(() => { }) describe('the desktop-created server tab record', () => { - it('records an id per workspace: one workspace’s publish or removal leaves the other’s', () => { - noteDesktopCreatedServerTab(ENV, WT1, 'tab-x') - noteDesktopCreatedServerTab(ENV, WT2, 'tab-x') - expect([held(WT1, 'tab-x'), held(WT2, 'tab-x')]).toEqual([true, true]) - - forgetPublishedServerTabs(WT1, ['tab-x']) - expect([held(WT1, 'tab-x'), held(WT2, 'tab-x')]).toEqual([false, true]) - - noteDesktopCreatedServerTab(ENV, WT1, 'tab-x') - forgetDesktopCreatedServerTabsOf(WT2) - expect([held(WT1, 'tab-x'), held(WT2, 'tab-x')]).toEqual([true, false]) - }) - - it('forgets a workspace’s every id, and a closed id in any workspace', () => { - noteDesktopCreatedServerTab(ENV, WT1, 'tab-a') - noteDesktopCreatedServerTab(ENV, WT1, 'tab-b') - noteDesktopCreatedServerTab(ENV, WT2, 'tab-c') - forgetDesktopCreatedServerTabsOf(WT1) - expect([held(WT1, 'tab-a'), held(WT1, 'tab-b'), held(WT2, 'tab-c')]).toEqual([ - false, - false, - true - ]) - forgetDesktopCreatedServerTab('tab-c') - expect(held(WT2, 'tab-c')).toBe(false) - }) - - it('names a tab recorded by its entity id by its tab-bar id too', () => { - noteDesktopCreatedServerTab(ENV, WT1, 'browser-workspace-1') - const tabs = [{ id: 'tab-bar-1', entityId: 'browser-workspace-1' }] - expect(isDesktopCreatedServerTab(tabs, WT1, 'tab-bar-1')).toBe(true) - expect(isDesktopCreatedServerTab(tabs, WT2, 'tab-bar-1')).toBe(false) - }) - it('drops the removed worktree’s ids when the worktree is removed, and only those', async () => { const store = createTestStore() seedStore(store, { diff --git a/src/renderer/src/lib/desktop-sidebar-feed.test.ts b/src/renderer/src/lib/desktop-sidebar-feed.test.ts index 33d08f61859..c1bb338d5b5 100644 --- a/src/renderer/src/lib/desktop-sidebar-feed.test.ts +++ b/src/renderer/src/lib/desktop-sidebar-feed.test.ts @@ -96,17 +96,6 @@ describe('desktop sidebar feed', () => { expect(second.result.current?.domains.workspaces.size).toBe(2) }) - it('answers a refresh with an install from that read or later', async () => { - connectMain() - const reader = await load() - const installs: number[] = [] - reader.onDesktopSidebarFeedInstalled((installed) => installs.push(installed.readId)) - renderHook(() => reader.useDesktopSidebarFeed()) - await vi.waitFor(() => expect(installs).toHaveLength(1)) - const readId = reader.refreshDesktopSidebarFeed() - await vi.waitFor(() => expect(installs.at(-1)).toBeGreaterThanOrEqual(readId)) - }) - it('retries a failed first read on its own', async () => { const main = connectMain() main.failOnce() diff --git a/src/renderer/src/lib/desktop-window-chrome.test.ts b/src/renderer/src/lib/desktop-window-chrome.test.ts deleted file mode 100644 index a4bde1495dd..00000000000 --- a/src/renderer/src/lib/desktop-window-chrome.test.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { shouldRenderDesktopWindowChrome } from './desktop-window-chrome' - -describe('shouldRenderDesktopWindowChrome', () => { - it('renders custom chrome for frameless desktop Linux and Windows windows', () => { - expect(shouldRenderDesktopWindowChrome({ platform: 'linux', isWebClient: false })).toBe(true) - expect(shouldRenderDesktopWindowChrome({ platform: 'win32', isWebClient: false })).toBe(true) - }) - - it('keeps macOS on native traffic lights', () => { - expect(shouldRenderDesktopWindowChrome({ platform: 'darwin', isWebClient: false })).toBe(false) - }) - - it('does not render desktop-only window controls in the paired web client', () => { - expect(shouldRenderDesktopWindowChrome({ platform: 'linux', isWebClient: true })).toBe(false) - expect(shouldRenderDesktopWindowChrome({ platform: 'win32', isWebClient: true })).toBe(false) - }) -}) diff --git a/src/renderer/src/lib/diff-comment-compat.test.ts b/src/renderer/src/lib/diff-comment-compat.test.ts deleted file mode 100644 index 7ab54e94b83..00000000000 --- a/src/renderer/src/lib/diff-comment-compat.test.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { describe, expect, it } from 'vitest' -import type { DiffComment } from '../../../shared/diff-comment-types' -import { - getDiffCommentLineLabel, - getDiffCommentSource, - isDiffComment, - isMarkdownComment -} from './diff-comment-compat' - -function makeComment(overrides: Partial = {}): DiffComment { - return { - id: 'c1', - worktreeId: 'wt1', - filePath: 'README.md', - lineNumber: 4, - body: 'note', - createdAt: 0, - side: 'modified', - ...overrides - } -} - -describe('diff comment compatibility helpers', () => { - it('routes legacy comments with no source as diff comments', () => { - const comment = makeComment() - expect(getDiffCommentSource(comment)).toBe('diff') - expect(isDiffComment(comment)).toBe(true) - expect(isMarkdownComment(comment)).toBe(false) - }) - - it('routes markdown comments by explicit source', () => { - const comment = makeComment({ source: 'markdown' }) - expect(getDiffCommentSource(comment)).toBe('markdown') - expect(isMarkdownComment(comment)).toBe(true) - expect(isDiffComment(comment)).toBe(false) - }) - - it('formats compact and full range labels', () => { - const comment = makeComment({ startLine: 2, lineNumber: 4 }) - expect(getDiffCommentLineLabel(comment)).toBe('Lines 2-4') - expect(getDiffCommentLineLabel(comment, true)).toBe('L2-L4') - }) -}) diff --git a/src/renderer/src/lib/diff-comments-format.test.ts b/src/renderer/src/lib/diff-comments-format.test.ts deleted file mode 100644 index eaf840f755f..00000000000 --- a/src/renderer/src/lib/diff-comments-format.test.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { describe, it, expect } from 'vitest' -import type { DiffComment } from '../../../shared/diff-comment-types' -import { formatDiffComment, formatDiffComments } from './diff-comments-format' - -function makeComment(overrides: Partial = {}): DiffComment { - return { - id: 'id-1', - worktreeId: 'wt-1', - filePath: 'src/app.ts', - lineNumber: 10, - body: 'Needs validation', - createdAt: 0, - side: 'modified', - ...overrides - } -} - -describe('formatDiffComment', () => { - it('emits the fixed three-line structure', () => { - const out = formatDiffComment(makeComment()) - expect(out).toBe( - ['File: src/app.ts', 'Line: 10', 'User comment: "Needs validation"'].join('\n') - ) - }) - - it('keeps explicit diff comments without ranges in the legacy format', () => { - const out = formatDiffComment(makeComment({ source: 'diff' })) - expect(out).toBe( - ['File: src/app.ts', 'Line: 10', 'User comment: "Needs validation"'].join('\n') - ) - }) - - it('formats persisted ranges when startLine is present', () => { - const out = formatDiffComment(makeComment({ source: 'diff', startLine: 7 })) - expect(out).toBe( - ['File: src/app.ts', 'Lines: 7-10', 'User comment: "Needs validation"'].join('\n') - ) - }) - - it('formats file-level diff notes', () => { - const out = formatDiffComment(makeComment({ source: 'diff', lineNumber: 0 })) - expect(out).toBe( - ['File: src/app.ts', 'Scope: file', 'User comment: "Needs validation"'].join('\n') - ) - }) - - it('adds markdown source metadata for markdown notes', () => { - const out = formatDiffComment(makeComment({ source: 'markdown', startLine: 8 })) - expect(out).toBe( - [ - 'File: src/app.ts', - 'Source: markdown', - 'Lines: 8-10', - 'User comment: "Needs validation"' - ].join('\n') - ) - }) - - it('escapes embedded quotes in the body', () => { - const out = formatDiffComment(makeComment({ body: 'why "this" path?' })) - expect(out).toContain('User comment: "why \\"this\\" path?"') - }) - - it('escapes backslashes before quotes so the body cannot break out of the literal', () => { - const out = formatDiffComment(makeComment({ body: 'path\\to\\"thing"' })) - expect(out).toContain('User comment: "path\\\\to\\\\\\"thing\\""') - }) - - it('escapes newlines so the body cannot break out of the fixed 3-line structure', () => { - const out = formatDiffComment(makeComment({ body: 'first\nsecond' })) - expect(out).toContain('User comment: "first\\nsecond"') - expect(out.split('\n')).toHaveLength(3) - }) -}) - -describe('formatDiffComments', () => { - it('joins multiple comments with a blank line', () => { - const out = formatDiffComments([ - makeComment({ id: 'a', lineNumber: 1, body: 'first' }), - makeComment({ id: 'b', lineNumber: 2, body: 'second' }) - ]) - expect(out).toBe( - [ - 'File: src/app.ts', - 'Line: 1', - 'User comment: "first"', - '', - 'File: src/app.ts', - 'Line: 2', - 'User comment: "second"' - ].join('\n') - ) - }) - - it('returns an empty string for an empty input', () => { - expect(formatDiffComments([])).toBe('') - }) -}) diff --git a/src/renderer/src/lib/direct-work-item-setup-callers.test.tsx b/src/renderer/src/lib/direct-work-item-setup-callers.test.tsx index c3f6713aa93..c49e9b6199c 100644 --- a/src/renderer/src/lib/direct-work-item-setup-callers.test.tsx +++ b/src/renderer/src/lib/direct-work-item-setup-callers.test.tsx @@ -12,7 +12,6 @@ import { fixBrokenPullRequestChecks, startFixChecksFromDialog } from '@/components/pull-request-page/checks/fix-launch' -import { fixBrokenGitHubChecks } from '@/components/github-item-dialog/inspect-pull-request/checks-tab-actions' import * as preflight from './launch-work-item-direct-preflight' import * as preparation from './launch-work-item-direct-route-preparation' import { createCompatibleRuntimeStatusResponse } from '@/runtime/runtime-compatibility-test-fixture' @@ -258,22 +257,6 @@ it('retains an edited Fix checks request through the PR-page Start handoff', asy ) }) -it('lets the GitHub item checks action answer Ask without its toast fallback', async () => { - const launch = fixBrokenGitHubChecks({ - item, - repoId: 'repo-1', - fixingChecks: false, - failedChecksLength: 1, - list: [], - setFixingChecks: vi.fn() - }) - ;(await pending()).choose('skip') - await launch - expect(mocks.createWorktree).toHaveBeenCalledOnce() - expect(mocks.createWorktree.mock.calls[0][3]).toBe('skip') - expect(mocks.toastError).not.toHaveBeenCalled() -}) - it.each(['Start work', 'Use item'])( 'keeps the project %s action in Orca when setup asks', async (entry) => { diff --git a/src/renderer/src/lib/document-theme.test.ts b/src/renderer/src/lib/document-theme.test.ts index c05d909c6bf..6fc8a80f761 100644 --- a/src/renderer/src/lib/document-theme.test.ts +++ b/src/renderer/src/lib/document-theme.test.ts @@ -1,9 +1,7 @@ import { describe, expect, it } from 'vitest' import { - applyCachedPluginAppTheme, applyDocumentTheme, applyPluginAppTheme, - readCachedPluginAppTheme, THEME_TRANSITION_DISABLED_CLASS } from './document-theme' @@ -78,25 +76,6 @@ function createFrameQueue(): { } describe('document theme', () => { - it('removes the transition suppression class after two animation frames', () => { - const root = createThemeRoot() - const frames = createFrameQueue() - - applyDocumentTheme('dark', { - root, - requestAnimationFrame: frames.requestAnimationFrame, - cancelAnimationFrame: frames.cancelAnimationFrame - }) - - expect(root.classList.contains(THEME_TRANSITION_DISABLED_CLASS)).toBe(true) - - frames.flushNextFrame() - expect(root.classList.contains(THEME_TRANSITION_DISABLED_CLASS)).toBe(true) - - frames.flushNextFrame() - expect(root.classList.contains(THEME_TRANSITION_DISABLED_CLASS)).toBe(false) - }) - it('cancels stale transition suppression frames on rapid theme changes', () => { const root = createThemeRoot() const frames = createFrameQueue() @@ -142,23 +121,6 @@ describe('applyPluginAppTheme', () => { } } } - function memoryStorage(): Storage { - const values = new Map() - return { - get length() { - return values.size - }, - clear: () => values.clear(), - getItem: (key) => values.get(key) ?? null, - key: (index) => [...values.keys()][index] ?? null, - removeItem: (key) => { - values.delete(key) - }, - setItem: (key, value) => { - values.set(key, value) - } - } - } // Test probe: #rrggbb or #rrggbbaa. const probe = (value: string) => { const match = /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})?$/i.exec(value) @@ -192,19 +154,6 @@ describe('applyPluginAppTheme', () => { return Object.fromEntries(root.properties) } - it('overlays tokens, drops ones a new theme stops setting, and restores built-ins on removal', () => { - const root = fakeRoot() - applyPluginAppTheme(theme({ '--background': '#000000', '--border': '#111111' }), options(root)) - expect(Object.fromEntries(root.properties)).toEqual({ - '--background': '#000000', - '--border': '#111111' - }) - applyPluginAppTheme(theme({ '--background': '#222222' }), options(root)) - expect(Object.fromEntries(root.properties)).toEqual({ '--background': '#222222' }) - applyPluginAppTheme(null, options(root)) - expect(root.properties.size).toBe(0) - }) - it('checks contrast on the effective set, so dropping one pair cannot expose another', () => { // White background passes with black text, but fails against muted text; dropping the // background leaves black text on the dark built-in background, so that drops too. @@ -217,33 +166,6 @@ describe('applyPluginAppTheme', () => { ).toEqual({}) }) - it('checks plugin colors against the built-ins they sit on', () => { - // A light plugin surface would put the dark built-in light text on near-white. - expect(painted({ '--background': '#f4f4f4' })).toEqual({}) - expect(painted({ '--background': '#151520' })).toEqual({ '--background': '#151520' }) - }) - - it('keeps text readable on the editor surface notebooks paint on', () => { - const tokens = { '--background': '#000000', '--foreground': '#ffffff' } - expect(painted({ ...tokens, '--editor-surface': '#ffffff' })).not.toHaveProperty( - '--editor-surface' - ) - expect(painted({ ...tokens, '--editor-surface': '#111111' })).toHaveProperty( - '--editor-surface', - '#111111' - ) - }) - - it('rejects invisible rings and borders in any spelling', () => { - expect( - painted({ '--ring': '#0a0a0a', '--border': '#00000000', '--input': '#ffffff03' }) - ).toEqual({}) - expect(painted({ '--ring': '#ff2bd6', '--border': '#ffffff1a' })).toEqual({ - '--ring': '#ff2bd6', - '--border': '#ffffff1a' - }) - }) - it('drops only the status or link color that fails, never the shared surface', () => { const sidebar = { '--sidebar': '#202020', '--sidebar-foreground': '#ffffff' } expect( @@ -304,24 +226,4 @@ describe('applyPluginAppTheme', () => { }) ).toEqual({ ...sidebar, '--accent': '#ffffff', '--accent-foreground': '#000000' }) }) - - it('keeps the startup cache tied to the theme id and revision until cleared', () => { - const storage = memoryStorage() - applyPluginAppTheme(theme({ '--background': '#101018' }), { ...options(), storage }) - expect(readCachedPluginAppTheme(storage)).toEqual({ - id: 'plugin:samples.midnight/midnight', - revision: 'r1', - base: 'dark', - tokens: { '--background': '#101018' } - }) - const startup = fakeRoot() - const cached = readCachedPluginAppTheme(storage) - if (cached) { - applyCachedPluginAppTheme(cached, { root: startup, probe, builtIns: darkBuiltIns }) - } - expect(startup.properties.get('--background')).toBe('#101018') - - applyPluginAppTheme(null, { ...options(), storage }) - expect(readCachedPluginAppTheme(storage)).toBeNull() - }) }) diff --git a/src/renderer/src/lib/editable-target.test.ts b/src/renderer/src/lib/editable-target.test.ts deleted file mode 100644 index c1cc71f2361..00000000000 --- a/src/renderer/src/lib/editable-target.test.ts +++ /dev/null @@ -1,45 +0,0 @@ -// @vitest-environment happy-dom - -import { afterEach, describe, expect, it } from 'vitest' -import { isSelectAllShortcut } from './editable-target' - -const originalUserAgent = navigator.userAgent - -function setPlatformUserAgent(userAgent: string): void { - Object.defineProperty(navigator, 'userAgent', { configurable: true, value: userAgent }) -} - -function keyEvent(overrides: Partial[0]> = {}) { - return { - altKey: false, - ctrlKey: false, - key: 'a', - metaKey: false, - shiftKey: false, - ...overrides - } -} - -afterEach(() => setPlatformUserAgent(originalUserAgent)) - -describe('isSelectAllShortcut', () => { - it.each([ - ['macOS Cmd+A', 'Macintosh', { metaKey: true }], - ['Linux Ctrl+A', 'Linux x86_64', { ctrlKey: true }], - ['Windows Ctrl+A', 'Windows NT 10.0', { ctrlKey: true }] - ])('recognizes %s', (_label, userAgent, modifiers) => { - setPlatformUserAgent(userAgent) - - expect(isSelectAllShortcut(keyEvent(modifiers))).toBe(true) - }) - - it.each([ - ['macOS Ctrl+A', 'Macintosh', { ctrlKey: true }], - ['Linux Cmd+A', 'Linux x86_64', { metaKey: true }], - ['Windows Cmd+A', 'Windows NT 10.0', { metaKey: true }] - ])('rejects wrong-modifier %s', (_label, userAgent, modifiers) => { - setPlatformUserAgent(userAgent) - - expect(isSelectAllShortcut(keyEvent(modifiers))).toBe(false) - }) -}) diff --git a/src/renderer/src/lib/editor-file-operation-owner.test.ts b/src/renderer/src/lib/editor-file-operation-owner.test.ts index 866d93d52ed..3e84864b987 100644 --- a/src/renderer/src/lib/editor-file-operation-owner.test.ts +++ b/src/renderer/src/lib/editor-file-operation-owner.test.ts @@ -11,7 +11,6 @@ import { captureEditorFileOperationProvenance, getEditorFileOperationContext } from './editor-file-operation-owner' -import { FLOATING_TERMINAL_WORKTREE_ID } from '../../../shared/constants' import { applyOpenFileToState } from '@/store/slices/editor/actions/open-file-apply' import { buildEditorSessionData } from './workspace-session' import { restorableEditorFile } from '@/store/slices/editor/tabs/editor-tab-layout-owner' @@ -123,51 +122,6 @@ describe('editor file operation owner', () => { queue.dispose() } ) - it('retains published hints and owner-only capture without inventing physical-local evidence', () => { - useAppStore.setState({ - worktreesByRepo: { - repo: [ - makeWorktree({ id: worktreeId, repoId: 'repo', path: '/remote/repo', hostId: undefined }) - ] - } - }) - const hinted = captureEditorFileOperationProvenance( - useAppStore.getState(), - worktreeId, - 'hub-a', - true - ) - expect(hinted.generation.authority).toBeUndefined() - expect(assertEditorFileOperationCurrent(useAppStore.getState(), worktreeId, hinted)).toEqual( - hinted.generation.route - ) - useAppStore.setState({ - worktreesByRepo: { - repo: [ - { - id: worktreeId, - repoId: 'repo', - path: '/remote/repo', - runtimeOwnerEnvironmentId: 'hub-a' - } as never - ] - } - }) - const ownerOnly = captureEditorFileOperationProvenance( - useAppStore.getState(), - worktreeId, - 'hub-a', - true - ) - expect(ownerOnly.generation.authority).toBeUndefined() - expect(assertEditorFileOperationCurrent(useAppStore.getState(), worktreeId, ownerOnly)).toEqual( - ownerOnly.generation.route - ) - expect(ownerOnly.generation.route).toEqual({ - runtimeEnvironmentId: 'hub-a', - executionHostId: null - }) - }) it('keeps catalog identity and drafts across capture, saved writers and reload while fencing physical retargets', async () => { const id = 'folder:physical-owner' useAppStore.setState({ @@ -440,30 +394,6 @@ describe('editor file operation owner', () => { 'Reopen the file' ) }) - it('round-trips the synthetic local owner for floating workspace files', () => { - const provenance = captureEditorFileOperationProvenance( - useAppStore.getState(), - FLOATING_TERMINAL_WORKTREE_ID, - null, - true - ) - - expect( - getEditorFileOperationContext( - useAppStore.getState(), - { - worktreeId: FLOATING_TERMINAL_WORKTREE_ID, - runtimeEnvironmentId: null, - operationProvenance: provenance - }, - '/tmp/orca/floating-workspace' - ) - ).toMatchObject({ - worktreeId: FLOATING_TERMINAL_WORKTREE_ID, - worktreePath: '/tmp/orca/floating-workspace', - expectedExecutionHostId: 'local' - }) - }) it('uses the explicit worktree owner instead of global runtime focus', () => { useAppStore.setState({ @@ -645,42 +575,6 @@ describe('editor file operation owner', () => { }) }) - it('round-trips capture and assert for a local folder workspace (#10251)', () => { - const provenance = captureEditorFileOperationProvenance( - useAppStore.getState(), - folderKey, - undefined, - false - ) - expect(provenance.generation.route).toEqual({ - executionHostId: 'local', - runtimeEnvironmentId: null - }) - expect( - assertEditorFileOperationCurrent(useAppStore.getState(), folderKey, provenance) - ).toEqual(provenance.generation.route) - }) - - it('uses the folder root when a legacy caller passes an empty worktree path', () => { - useAppStore.setState({ - folderWorkspaces: [ - { - id: folderWorkspaceId, - projectGroupId: 'group-1', - connectionId: null, - folderPath: '/workspace/folder' - } as never - ] - }) - const context = getEditorFileOperationContext( - useAppStore.getState(), - { worktreeId: folderKey }, - '' - ) - - expect(context.worktreePath).toBe('/workspace/folder') - }) - it('fails closed when folder ownership changes between capture and assert', () => { const provenance = captureEditorFileOperationProvenance( useAppStore.getState(), diff --git a/src/renderer/src/lib/editor-typography.test.ts b/src/renderer/src/lib/editor-typography.test.ts deleted file mode 100644 index 59776366267..00000000000 --- a/src/renderer/src/lib/editor-typography.test.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { resolveEditorTypography, type EditorTypographyInputs } from './editor-typography' - -const terminal = { terminalFontFamily: 'JetBrains Mono', terminalFontSize: 14 } - -describe('resolveEditorTypography', () => { - it('keeps pre-setting rendering when no editor key is set', () => { - expect(resolveEditorTypography(terminal, 0)).toMatchObject({ - fontFamily: 'JetBrains Mono', - fontSize: 14, - fontWeight: 'normal', - lineHeight: 0, - fontLigatures: false - }) - }) - - it('applies editor overrides independently of the terminal', () => { - const typography = resolveEditorTypography( - { - ...terminal, - editorFontSize: 16, - editorFontWeight: 550, - editorLineHeight: 1.6, - editorLigatures: 'auto' - }, - 2 - ) - expect(typography).toMatchObject({ - fontSize: 18, - fontWeight: '550', - lineHeight: 29, - fontLigatures: true - }) - }) - - it('derives diff size and line height from the editor base size', () => { - const typography = resolveEditorTypography( - { ...terminal, editorFontSize: 16, editorLineHeight: 2 }, - 0, - 'diff' - ) - expect(typography.fontSize).toBe(15.5) - expect(typography.lineHeight).toBe(31) - }) - - it('reads line heights of 8 or more as pixels so they do not clamp to 3x', () => { - const typography = resolveEditorTypography({ ...terminal, editorLineHeight: 21 }, 0) - expect(typography.lineHeight).toBe(21) - }) - - it('ignores hand-edited values that are not usable', () => { - // Settings files are untyped JSON on disk, so corrupt values arrive through JSON.parse. - const handEdited: EditorTypographyInputs = JSON.parse( - JSON.stringify({ - ...terminal, - editorFontSize: 'big', - editorFontWeight: null, - editorLineHeight: -1, - editorLigatures: 'yes' - }) - ) - const typography = resolveEditorTypography(handEdited, 0) - expect(typography).toMatchObject({ - fontSize: 14, - fontWeight: 'normal', - lineHeight: 0, - fontLigatures: false - }) - }) -}) diff --git a/src/renderer/src/lib/empty-workspace-default-agent-chat.test.ts b/src/renderer/src/lib/empty-workspace-default-agent-chat.test.ts index 1ac9d7ff63a..3daa0c2eb5d 100644 --- a/src/renderer/src/lib/empty-workspace-default-agent-chat.test.ts +++ b/src/renderer/src/lib/empty-workspace-default-agent-chat.test.ts @@ -5,7 +5,6 @@ import { getDefaultSettings } from '../../../shared/constants' import { useAppStore } from '@/store' import { setLocalRuntimeCapabilitiesForTests } from '@/runtime/local-runtime-capabilities' import { - emptyWorkspaceDefaultChatAwaitsDetection, loadEmptyWorkspaceDefaultChatDetection, openDefaultAgentChatInEmptyWorkspace } from './empty-workspace-default-agent-chat' @@ -59,24 +58,6 @@ afterEach(() => { }) describe('openDefaultAgentChatInEmptyWorkspace', () => { - it("seeds a plain shell, not the agent's terminal, when the host declines the chat", () => { - seedSettings({}) - - openDefaultAgentChatInEmptyWorkspace('wt-1', mocks.seedShell) - const onDeclined = mocks.launchAgentInNewTab.mock.calls[0]?.[0]?.onStructuredHostDeclined - - expect(mocks.seedShell).not.toHaveBeenCalled() - expect(onDeclined()).toEqual({ opened: true }) - expect(mocks.seedShell).toHaveBeenCalledOnce() - }) - - it('opens no agent when the default agent is left on Auto', () => { - seedSettings({ defaultTuiAgent: null }) - - expect(openDefaultAgentChatInEmptyWorkspace('wt-1', mocks.seedShell)).toBeNull() - expect(mocks.launchAgentInNewTab).not.toHaveBeenCalled() - }) - it('reads the agents detected on the workspace SSH host', () => { seedSettings({}) mocks.detectionTargetKey.mockReturnValue('ssh:conn-1') @@ -91,25 +72,6 @@ describe('openDefaultAgentChatInEmptyWorkspace', () => { }) describe('agent detection for the default chat', () => { - it('waits only while chat is the default and the host list has not loaded', () => { - seedSettings({}) - setLocalRuntimeCapabilitiesForTests([]) - mocks.detectionTargetKey.mockReturnValue('ssh:conn-1') - useAppStore.setState({ remoteDetectedAgentIds: {} }) - expect(emptyWorkspaceDefaultChatAwaitsDetection('wt-1')).toBe(true) - - useAppStore.setState({ remoteDetectedAgentIds: { 'conn-1': [] } }) - expect(emptyWorkspaceDefaultChatAwaitsDetection('wt-1')).toBe(false) - - seedSettings({ nativeChatProviders: {} }) - useAppStore.setState({ remoteDetectedAgentIds: {} }) - expect(emptyWorkspaceDefaultChatAwaitsDetection('wt-1')).toBe(false) - - seedSettings({ defaultTuiAgent: null }) - useAppStore.setState({ remoteDetectedAgentIds: {} }) - expect(emptyWorkspaceDefaultChatAwaitsDetection('wt-1')).toBe(false) - }) - it('probes the workspace host and gives up after a bounded wait', async () => { vi.useFakeTimers() seedSettings({}) diff --git a/src/renderer/src/lib/ensure-hooks-confirmed.test.ts b/src/renderer/src/lib/ensure-hooks-confirmed.test.ts index c78270bd16e..48345af1485 100644 --- a/src/renderer/src/lib/ensure-hooks-confirmed.test.ts +++ b/src/renderer/src/lib/ensure-hooks-confirmed.test.ts @@ -142,68 +142,6 @@ describe('ensureHooksConfirmed', () => { await expect(promise).resolves.toBe('skip') }) - it('prompts for VM recipes using the recipe lifecycle declarations', async () => { - const { state, pending } = createTestState() - hooksCheckMock.mockResolvedValue({ - hasHooks: true, - hooks: { - environmentRecipes: [ - { - id: 'cloud-sandbox', - name: 'Cloud Sandbox', - description: 'Starts a per-workspace VM.', - create: './scripts/start-vm.sh', - suspend: './scripts/suspend-vm.sh', - resume: './scripts/resume-vm.sh', - destroy: './scripts/destroy-vm.sh' - } - ] - }, - mayNeedUpdate: false - }) - - const promise = ensureHooksConfirmed(state, 'repo-1', 'vmRecipe') - - await vi.waitFor(() => expect(pending).toHaveLength(1)) - expect(pending[0].data.scriptKind).toBe('vmRecipe') - expect(pending[0].data.scriptContent).toContain('# environmentRecipes.cloud-sandbox') - expect(pending[0].data.scriptContent).toContain('create: ./scripts/start-vm.sh') - expect(pending[0].data.scriptContent).toContain('suspend: ./scripts/suspend-vm.sh') - expect(pending[0].data.scriptContent).toContain('resume: ./scripts/resume-vm.sh') - expect(pending[0].data.scriptContent).toContain('destroy: ./scripts/destroy-vm.sh') - - pending[0].resolve('run') - await expect(promise).resolves.toBe('run') - }) - - it('returns run without inspecting hooks when the repo is always trusted', async () => { - const { state, pending } = createTestState() - state.trustedOrcaHooks['repo-1'] = { - all: { approvedAt: 1 } - } - hooksCheckMock.mockRejectedValue(new Error('boom')) - - const decision = await ensureHooksConfirmed(state, 'repo-1', 'setup') - - expect(decision).toBe('run') - expect(hooksCheckMock).not.toHaveBeenCalled() - expect(pending).toHaveLength(0) - }) - - it('returns run without prompting when no script of that kind is configured', async () => { - const { state, pending } = createTestState() - hooksCheckMock.mockResolvedValue({ - hasHooks: true, - hooks: { scripts: {} }, - mayNeedUpdate: false - }) - - const decision = await ensureHooksConfirmed(state, 'repo-1', 'archive') - - expect(decision).toBe('run') - expect(pending).toHaveLength(0) - }) - it('checks SSH repo hooks through local IPC even when a runtime is focused', async () => { const { state, pending } = createTestState({ settings: { activeRuntimeEnvironmentId: 'env-1' }, @@ -302,81 +240,6 @@ describe('ensureHooksConfirmed', () => { expect(pending).toHaveLength(0) }) - it('does not prompt for orca.yaml when the repo uses local commands only', async () => { - const { state, pending } = createTestState({ - repos: [ - { - id: 'repo-1', - path: '/repo-1', - displayName: 'Repo One', - badgeColor: 'blue', - addedAt: 1, - hookSettings: { - mode: 'auto', - commandSourcePolicy: 'local-only', - scripts: { setup: 'echo local', archive: '' } - } - } - ] - } as Partial) - hooksCheckMock.mockResolvedValue({ - hasHooks: true, - hooks: { scripts: { setup: 'echo shared' } }, - mayNeedUpdate: false - }) - - const decision = await ensureHooksConfirmed(state, 'repo-1', 'setup') - - expect(decision).toBe('run') - expect(hooksCheckMock).not.toHaveBeenCalled() - expect(pending).toHaveLength(0) - }) - - it('does not prompt for orca.yaml when local commands are the implicit default', async () => { - const { state, pending } = createTestState({ - repos: [ - { - id: 'repo-1', - path: '/repo-1', - displayName: 'Repo One', - badgeColor: 'blue', - addedAt: 1, - hookSettings: { - mode: 'auto', - scripts: { setup: 'echo local', archive: '' } - } - } - ] - } as Partial) - hooksCheckMock.mockResolvedValue({ - hasHooks: true, - hooks: { scripts: { setup: 'echo shared' } }, - mayNeedUpdate: false - }) - - const decision = await ensureHooksConfirmed(state, 'repo-1', 'setup') - - expect(decision).toBe('run') - expect(hooksCheckMock).not.toHaveBeenCalled() - expect(pending).toHaveLength(0) - }) - - it('returns run without prompting when issueCommand source is local (user-owned)', async () => { - const { state, pending } = createTestState() - readIssueCommandMock.mockResolvedValue({ - source: 'local', - sharedContent: null, - localContent: 'user content', - effectiveContent: 'user content', - localFilePath: '' - }) - - const decision = await ensureHooksConfirmed(state, 'repo-1', 'issueCommand') - - expect(decision).toBe('run') - expect(pending).toHaveLength(0) - }) - it('still honors local issueCommand overrides when shared inspection reports an error', async () => { const { state, pending } = createTestState() readIssueCommandMock.mockResolvedValue({ @@ -394,31 +257,6 @@ describe('ensureHooksConfirmed', () => { expect(pending).toHaveLength(0) }) - it('forwards the explicit host to issueCommand inspection when repo ids collide', async () => { - const { state } = createTestState({ - repos: [ - { id: 'repo-1', displayName: 'Local Row' }, - { id: 'repo-1', displayName: 'SSH Row', connectionId: 'server' } - ] - } as unknown as Partial) - readIssueCommandMock.mockResolvedValue({ - source: 'local', - sharedContent: null, - localContent: 'user content', - effectiveContent: 'user content', - localFilePath: '' - }) - - await ensureHooksConfirmed(state, 'repo-1', 'issueCommand', 'ssh:server') - - // A launch is a user act, so its read may start a stopped WSL distro (E9). - expect(readIssueCommandMock).toHaveBeenCalledWith({ - repoId: 'repo-1', - hostId: 'ssh:server', - wakeWslDistro: true - }) - }) - it('approves and returns the exact issue-command bytes from one host-qualified read', async () => { const { state, pending } = createTestState({ repos: [ @@ -529,30 +367,6 @@ describe('ensureHooksConfirmed', () => { expect(pending).toHaveLength(0) }) - it('opens a modal with the computed content hash and resolves with the user decision', async () => { - const { state, pending } = createTestState() - hooksCheckMock.mockResolvedValue({ - hasHooks: true, - hooks: { scripts: { setup: 'pnpm install' } }, - mayNeedUpdate: false - }) - - const promise = ensureHooksConfirmed(state, 'repo-1', 'setup') - - await vi.waitFor(() => expect(pending).toHaveLength(1)) - expect(pending[0].data).toMatchObject({ - repoId: 'repo-1', - repoName: 'Repo One', - scriptKind: 'setup', - scriptContent: 'pnpm install', - contentHash: await hashOrcaHookScript('pnpm install'), - previouslyApproved: false - }) - - pending[0].resolve('run') - await expect(promise).resolves.toBe('run') - }) - it('serializes overlapping prompts so a second call waits for the first to resolve', async () => { const { state, pending } = createTestState() hooksCheckMock.mockResolvedValue({ diff --git a/src/renderer/src/lib/ensure-simulator-tab-behavior.test.ts b/src/renderer/src/lib/ensure-simulator-tab-behavior.test.ts index 6b53c9bd8b8..80380b9c0ae 100644 --- a/src/renderer/src/lib/ensure-simulator-tab-behavior.test.ts +++ b/src/renderer/src/lib/ensure-simulator-tab-behavior.test.ts @@ -246,17 +246,6 @@ describe('ensureSimulatorTab', () => { drawn.store = null }) - it('activates an existing simulator tab through unified tab state', async () => { - const { ensureSimulatorTab } = await import('./ensure-simulator-tab') - - expect(ensureSimulatorTab('wt-1')).toBe('sim-1') - - expect(mockStoreState.activateTab).toHaveBeenCalledWith('sim-1') - expect(mockStoreState.setActiveTab).not.toHaveBeenCalled() - expect(mockStoreState.focusGroup).toHaveBeenCalledWith('wt-1', 'group-1') - expect(mockStoreState.setActiveTabType).toHaveBeenCalledWith('simulator', 'wt-1') - }) - it('does not reuse a simulator owned by a same-id sibling host', async () => { mockStoreState.activeWorkspaceExecutionHostId = 'runtime:host-b' mockStoreState.allWorktrees.mockReturnValue([ @@ -470,47 +459,6 @@ describe('ensureSimulatorTab', () => { expect(mockStoreState.setActiveTabType).toHaveBeenCalledWith('simulator', 'wt-1') }) - it('falls back to the source group when atomic right split creation fails', async () => { - mockStoreState.unifiedTabsByWorktree = { 'wt-1': [] } - mockStoreState.createUnifiedTabInSplit.mockReturnValue(null) - stubCreatedSimulatorTab('group-1') - const { ensureSimulatorTab } = await import('./ensure-simulator-tab') - - if (ORCA_LAYOUT_OWNER_IS_MAIN) { - const { tabId, tab, sent, store } = await openThroughMain(() => - ensureSimulatorTab('wt-1', { placement: 'rightSplit' }) - ) - expect(sent).toEqual([ - expect.objectContaining({ tabId, split: { besideGroupId: 'group-1', direction: 'right' } }) - ]) - expect(tab).toMatchObject({ contentType: 'simulator', label: 'Mobile Emulator' }) - expectSurfaced(store, tab!) - return - } - expect(ensureSimulatorTab('wt-1', { placement: 'rightSplit' })).toBe(createdSimulatorTabId()) - - expect(mockStoreState.createUnifiedTabInSplit).toHaveBeenCalledWith( - 'wt-1', - 'simulator', - { - sourceGroupId: 'group-1', - splitDirection: 'right' - }, - { - label: 'Mobile Emulator', - activate: true - } - ) - expect(mockStoreState.createUnifiedTab).toHaveBeenCalledWith('wt-1', 'simulator', { - id: expect.any(String), - label: 'Mobile Emulator', - targetGroupId: 'group-1', - activate: true - }) - expect(mockStoreState.dropUnifiedTab).not.toHaveBeenCalled() - expect(mockStoreState.focusGroup).toHaveBeenCalledWith('wt-1', 'group-1') - }) - it('does not create a split for background auto-attach', async () => { mockStoreState.unifiedTabsByWorktree = { 'wt-1': [] } stubCreatedSimulatorTab('group-1') @@ -624,14 +572,4 @@ describe('ensureSimulatorTab', () => { ) expect(mockStoreState.createUnifiedTab).not.toHaveBeenCalled() }) - - it('does not create or focus a simulator tab when disabled in settings', async () => { - mockStoreState.settings = { mobileEmulatorEnabled: false } - const { ensureSimulatorTab } = await import('./ensure-simulator-tab') - - expect(ensureSimulatorTab('wt-1')).toBeNull() - - expect(mockStoreState.activateTab).not.toHaveBeenCalled() - expect(mockStoreState.createUnifiedTab).not.toHaveBeenCalled() - }) }) diff --git a/src/renderer/src/lib/ensure-simulator-tab.test.ts b/src/renderer/src/lib/ensure-simulator-tab.test.ts deleted file mode 100644 index 008965cd01d..00000000000 --- a/src/renderer/src/lib/ensure-simulator-tab.test.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { shouldShutdownSimulatorForPaneUnmountFromTabs } from './simulator-tab-shutdown' - -describe('shouldShutdownSimulatorForPaneUnmountFromTabs', () => { - it('keeps the simulator alive when the same tab still exists during a remount', () => { - expect( - shouldShutdownSimulatorForPaneUnmountFromTabs( - [{ id: 'sim-1', contentType: 'simulator' }], - 'sim-1' - ) - ).toBe(false) - }) - - it('shuts down when the last simulator tab has been removed', () => { - expect( - shouldShutdownSimulatorForPaneUnmountFromTabs( - [{ id: 'terminal-1', contentType: 'terminal' }], - 'sim-1' - ) - ).toBe(true) - }) - - it('keeps the simulator alive when another simulator tab remains', () => { - expect( - shouldShutdownSimulatorForPaneUnmountFromTabs( - [ - { id: 'terminal-1', contentType: 'terminal' }, - { id: 'sim-2', contentType: 'simulator' } - ], - 'sim-1' - ) - ).toBe(false) - }) -})