From 0cee6cbd863f286813ddbfdf8d9cb568e52ae059 Mon Sep 17 00:00:00 2001 From: OrcaWin Date: Mon, 26 Oct 2026 18:11:04 -0700 Subject: [PATCH] test: remove 368 low-value cases and 4 suites (#34157) Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> --- config/flaky-test-pattern-baseline.txt | 1 - config/scripts/ci-shard-timings.json | 4 - .../combined-diff-on-demand-load.test.ts | 80 ------ .../editor/editor-self-write-registry.test.ts | 34 --- .../editor/markdown-internal-links.test.ts | 94 ------- .../editor/markdown-preview-search.test.ts | 163 ------------ ...floating-terminal-trigger-position.test.ts | 237 ------------------ .../native-chat-autoscroll.test.ts | 89 +------ .../native-chat-availability.test.ts | 133 ---------- .../native-chat-typing-redirect.test.ts | 48 ---- .../ai-vault-session-resume-in-chat.test.ts | 82 +----- .../file-explorer-batch-deletion.test.ts | 76 ------ .../useFileExplorerWatch.test.ts | 67 ----- .../components/settings/PrivacyPane.test.ts | 197 --------------- .../CommentMarkdown.unknown-html.test.tsx | 83 ------ .../sidebar/worktree-card-pr-display.test.ts | 106 -------- .../task-page-new-issue-draft.test.ts | 92 ------- .../terminal-error-accumulation.test.ts | 74 ------ .../terminal-ime-composition-tracker.test.ts | 57 ----- .../terminal-layout-leaf-ids.test.ts | 134 +--------- .../src/lib/agent-picker-search.test.ts | 98 -------- .../lib/folder-workspace-path-status.test.ts | 70 ------ src/renderer/src/lib/gitlab-links.test.ts | 129 +--------- .../detect-option-as-alt.test.ts | 144 ----------- .../client-hosted-browser-row-state.test.ts | 98 -------- src/renderer/src/lib/tab-agent.test.ts | 188 -------------- .../src/lib/terminal-tab-for-pty-id.test.ts | 61 ----- .../src/lib/worktree-activity-state.test.ts | 156 +----------- .../remote-terminal-layout-resolution.test.ts | 55 +--- .../agent-status-pane-keyed-records.test.ts | 92 ------- .../slices/repo-identity-reconcile.test.ts | 99 -------- .../src/store/slices/tab-group-state.test.ts | 197 --------------- 32 files changed, 6 insertions(+), 3232 deletions(-) delete mode 100644 src/renderer/src/components/floating-terminal/floating-terminal-trigger-position.test.ts delete mode 100644 src/renderer/src/components/settings/PrivacyPane.test.ts delete mode 100644 src/renderer/src/lib/tab-agent.test.ts delete mode 100644 src/renderer/src/store/slices/tab-group-state.test.ts diff --git a/config/flaky-test-pattern-baseline.txt b/config/flaky-test-pattern-baseline.txt index 86787ff7ebd..7355625adda 100644 --- a/config/flaky-test-pattern-baseline.txt +++ b/config/flaky-test-pattern-baseline.txt @@ -1192,7 +1192,6 @@ 2 test-no-wall-clock:src/renderer/src/store/slices/runtime-environment-ssh.test.ts 1 test-no-wall-clock:src/renderer/src/store/slices/store-sleep-agent-status-retention.test.ts 2 test-no-wall-clock:src/renderer/src/store/slices/store-sleep-exact-runtime-stop.test.ts -1 test-no-wall-clock:src/renderer/src/store/slices/tab-group-state.test.ts 7 test-no-wall-clock:src/renderer/src/store/slices/tabs-unread-and-focus.test.ts 1 test-no-wall-clock:src/renderer/src/store/slices/terminal-helpers.test.ts 1 test-no-wall-clock:src/renderer/src/store/slices/ui-hydration-view-layout.test.ts diff --git a/config/scripts/ci-shard-timings.json b/config/scripts/ci-shard-timings.json index e7429043b07..002431ad248 100644 --- a/config/scripts/ci-shard-timings.json +++ b/config/scripts/ci-shard-timings.json @@ -7839,7 +7839,6 @@ "src/renderer/src/components/floating-terminal/floating-terminal-panel-bounds.test.ts": 64, "src/renderer/src/components/floating-terminal/floating-terminal-panel-inputs.test.ts": 106, "src/renderer/src/components/floating-terminal/floating-terminal-panel-view-state.test.ts": 153, - "src/renderer/src/components/floating-terminal/floating-terminal-trigger-position.test.ts": 50, "src/renderer/src/components/floating-terminal/floating-workspace-surface-model.test.ts": 172, "src/renderer/src/components/floating-terminal/floating-workspace-tab-reorder.test.ts": 2600, "src/renderer/src/components/floating-terminal/use-floating-terminal-close-actions.test.tsx": 1337, @@ -8783,7 +8782,6 @@ "src/renderer/src/components/settings/PluginMarketplaceSourceDialog.test.tsx": 871, "src/renderer/src/components/settings/PluginSettingsRow.test.tsx": 589, "src/renderer/src/components/settings/PluginsSettingsSection.lifecycle.test.tsx": 889, - "src/renderer/src/components/settings/PrivacyPane.test.ts": 1589, "src/renderer/src/components/settings/ProjectWindowsRuntimeSetting.test.tsx": 729, "src/renderer/src/components/settings/QuickCommandsList.test.tsx": 617, "src/renderer/src/components/settings/QuickCommandsPane.test.ts": 604, @@ -11087,7 +11085,6 @@ "src/renderer/src/lib/syntax-highlighting/textmate-core.test.ts": 46, "src/renderer/src/lib/tab-agent-identity-decision-table.test.ts": 462, "src/renderer/src/lib/tab-agent-status-index.test.ts": 137, - "src/renderer/src/lib/tab-agent.test.ts": 102, "src/renderer/src/lib/tab-number-shortcuts.test.ts": 1378, "src/renderer/src/lib/task-source-runtime-target.test.ts": 54, "src/renderer/src/lib/telemetry-track-result.test.ts": 32, @@ -11938,7 +11935,6 @@ "src/renderer/src/store/slices/store-worktree-removal-cascade.test.ts": 1503, "src/renderer/src/store/slices/superseded-ssh-repo-rows.test.ts": 56, "src/renderer/src/store/slices/tab-group-reference-repair.test.ts": 59, - "src/renderer/src/store/slices/tab-group-state.test.ts": 54, "src/renderer/src/store/slices/tab-view-mode.test.ts": 1440, "src/renderer/src/store/slices/tab-worktree-orphan-map-purge-leak.test.ts": 1305, "src/renderer/src/store/slices/tabs-close-dangling-group.test.ts": 1683, diff --git a/src/renderer/src/components/editor/combined-diff-on-demand-load.test.ts b/src/renderer/src/components/editor/combined-diff-on-demand-load.test.ts index c61278882e3..a23cbd36ecd 100644 --- a/src/renderer/src/components/editor/combined-diff-on-demand-load.test.ts +++ b/src/renderer/src/components/editor/combined-diff-on-demand-load.test.ts @@ -3,29 +3,10 @@ import { MAX_AUTOMATIC_DIFF_CHANGED_LINES, collectCountedCombinedDiffPasses, getCombinedDiffCountingPassKey, - isCombinedDiffSizeUnknown, shouldLoadCombinedDiffOnDemand } from './combined-diff-on-demand-load' describe('combined diff on-demand loading', () => { - it('defers diffs above the automatic changed-line limit', () => { - expect( - shouldLoadCombinedDiffOnDemand({ - added: MAX_AUTOMATIC_DIFF_CHANGED_LINES, - removed: 1 - }) - ).toBe(true) - }) - - it('automatically loads diffs at the limit', () => { - expect( - shouldLoadCombinedDiffOnDemand({ - added: MAX_AUTOMATIC_DIFF_CHANGED_LINES - 500, - removed: 500 - }) - ).toBe(false) - }) - it('defers uncounted tracked text files, whose size is unknown', () => { // Entry caps or failed numstat leave tracked rows uncounted; eager loading can freeze Monaco. expect(shouldLoadCombinedDiffOnDemand({ path: 'src/generated/schema.ts' })).toBe(true) @@ -43,26 +24,6 @@ describe('combined diff on-demand loading', () => { expect(shouldLoadCombinedDiffOnDemand({ path: 'data/dump.json', area: 'untracked' })).toBe(true) }) - it('automatically loads tracked binaries numstat left uncounted, whatever the extension', () => { - // Numstat marks binaries with dashes regardless of extension, including extensionless files. - for (const path of ['resources/build/icon.icns', 'assets/logo.tiff', 'bin/orca-helper']) { - expect( - shouldLoadCombinedDiffOnDemand({ path, area: 'unstaged', hasCountedSiblings: true }) - ).toBe(false) - } - }) - - it('automatically loads submodule rows whose only change is untracked content inside', () => { - // Submodule-only untracked changes produce porcelain status but no numstat row. - expect( - shouldLoadCombinedDiffOnDemand({ - path: 'vendor/sub', - area: 'unstaged', - submodule: { commitChanged: false, trackedChanges: false, untrackedChanges: true } - }) - ).toBe(false) - }) - it('defers uncounted rows when the whole pass skipped counting', () => { // Without counted siblings, a small binary is indistinguishable from unbounded text. expect( @@ -70,32 +31,6 @@ describe('combined diff on-demand loading', () => { ).toBe(true) }) - it('defers uncounted svgs, which render as text rather than a preview', () => { - expect(shouldLoadCombinedDiffOnDemand({ path: 'assets/map.svg' })).toBe(true) - }) - - it('automatically loads uncounted images, which render as a preview', () => { - expect(shouldLoadCombinedDiffOnDemand({ path: 'docs/Shot.PNG' })).toBe(false) - }) - - it('automatically loads uncounted non-image binaries of any size', () => { - expect(shouldLoadCombinedDiffOnDemand({ path: 'fixtures/sample.zip' })).toBe(false) - expect(shouldLoadCombinedDiffOnDemand({ path: 'fonts/Inter.woff2' })).toBe(false) - expect(shouldLoadCombinedDiffOnDemand({ path: 'bun.lockb' })).toBe(false) - }) - - it('defers diffs when only additions are reported', () => { - expect(shouldLoadCombinedDiffOnDemand({ added: MAX_AUTOMATIC_DIFF_CHANGED_LINES + 1 })).toBe( - true - ) - }) - - it('defers diffs when only removals are reported', () => { - expect(shouldLoadCombinedDiffOnDemand({ removed: MAX_AUTOMATIC_DIFF_CHANGED_LINES + 1 })).toBe( - true - ) - }) - it('credits a counted row only to its own counting pass', () => { // Staged, unstaged and compare counting calls fail independently; all mode must not pool their evidence. const countedPasses = collectCountedCombinedDiffPasses([ @@ -118,21 +53,6 @@ describe('combined diff on-demand loading', () => { ).toBe(false) }) - it('separates the two deferral reasons the prompt has to explain', () => { - const overLimit = { added: MAX_AUTOMATIC_DIFF_CHANGED_LINES + 1, path: 'src/schema.ts' } - const uncounted = { - path: 'resources/build/icon.icns', - area: 'unstaged' as const, - added: undefined, - removed: undefined - } - expect(shouldLoadCombinedDiffOnDemand(overLimit)).toBe(true) - expect(shouldLoadCombinedDiffOnDemand(uncounted)).toBe(true) - expect(isCombinedDiffSizeUnknown(overLimit)).toBe(false) - expect(isCombinedDiffSizeUnknown(uncounted)).toBe(true) - expect(isCombinedDiffSizeUnknown({ added: 0, removed: 0 })).toBe(false) - }) - it('keeps counted binary-extension rows on the line-count rule', () => { expect(shouldLoadCombinedDiffOnDemand({ added: 3, path: 'fixtures/sample.zip' })).toBe(false) expect( diff --git a/src/renderer/src/components/editor/editor-self-write-registry.test.ts b/src/renderer/src/components/editor/editor-self-write-registry.test.ts index 595b55c986f..77492ae381f 100644 --- a/src/renderer/src/components/editor/editor-self-write-registry.test.ts +++ b/src/renderer/src/components/editor/editor-self-write-registry.test.ts @@ -56,33 +56,6 @@ describe('editor self-write registry', () => { expect(hasRecentSelfWrite('/repo/a.md', 'env-1')).toBe(false) }) - it('trims runtime owner ids when matching stamps', () => { - recordSelfWrite('/repo/a.md', 'runtime save', ' env-1 ') - - expect(hasRecentSelfWrite('/repo/a.md', 'env-1')).toBe(true) - }) - - it('prunes expired stamps when recording later writes', () => { - recordSelfWrite('/repo/old.md') - - vi.advanceTimersByTime(751) - recordSelfWrite('/repo/new.md') - - expect(__getSelfWriteRegistrySizeForTests()).toBe(1) - expect(hasRecentSelfWrite('/repo/old.md')).toBe(false) - expect(hasRecentSelfWrite('/repo/new.md')).toBe(true) - }) - - it('caps retained stamps', () => { - for (let i = 0; i < 260; i++) { - recordSelfWrite(`/repo/${i}.md`) - } - - expect(__getSelfWriteRegistrySizeForTests()).toBe(256) - expect(hasRecentSelfWrite('/repo/0.md')).toBe(false) - expect(hasRecentSelfWrite('/repo/259.md')).toBe(true) - }) - it('keeps remote-TTL stamps alive past the local window', () => { // Remote watcher echoes can arrive after the local TTL expires. recordSelfWrite('/repo/remote.md', 'content', 'env-1', SELF_WRITE_REMOTE_TTL_MS) @@ -153,13 +126,6 @@ describe('editor self-write registry', () => { expect(vi.getTimerCount()).toBe(0) }) - it('clears the shared timer when the registry is reset', () => { - recordSelfWrite('/repo/reset.json', 'saved content') - __clearSelfWriteRegistryForTests() - expect(__getSelfWriteRegistrySizeForTests()).toBe(0) - expect(vi.getTimerCount()).toBe(0) - }) - it('bounds the timer delay after a large backward clock change', () => { const timeout = vi.spyOn(globalThis, 'setTimeout') recordSelfWrite('/repo/clock.json', 'saved content') diff --git a/src/renderer/src/components/editor/markdown-internal-links.test.ts b/src/renderer/src/components/editor/markdown-internal-links.test.ts index 89cb5975eef..813cc98d9f4 100644 --- a/src/renderer/src/components/editor/markdown-internal-links.test.ts +++ b/src/renderer/src/components/editor/markdown-internal-links.test.ts @@ -7,15 +7,6 @@ const SOURCE = '/repo/docs/note.md' const ROOT = '/repo' describe('resolveMarkdownLinkTarget', () => { - it('classifies relative .md inside worktree as markdown', () => { - const r = resolveMarkdownLinkTarget('./guide.md', SOURCE, ROOT) - expect(r).toEqual({ - kind: 'markdown', - absolutePath: '/repo/docs/guide.md', - relativePath: 'docs/guide.md' - }) - }) - it('classifies relative .md inside a UNC worktree as markdown', () => { const r = resolveMarkdownLinkTarget( './guide.md', @@ -29,29 +20,6 @@ describe('resolveMarkdownLinkTarget', () => { }) }) - it('classifies relative .md outside worktree as file', () => { - const r = resolveMarkdownLinkTarget('../../other/guide.md', SOURCE, ROOT) - expect(r?.kind).toBe('file') - }) - - it('classifies absolute .md inside worktree as markdown', () => { - const r = resolveMarkdownLinkTarget('/repo/docs/guide.md', SOURCE, ROOT) - expect(r?.kind).toBe('markdown') - }) - - it('classifies Windows drive-letter absolute .md links inside worktree as markdown', () => { - const r = resolveMarkdownLinkTarget( - 'C:\\repo\\docs\\guide.md', - 'C:\\repo\\docs\\note.md', - 'C:\\repo' - ) - expect(r).toEqual({ - kind: 'markdown', - absolutePath: 'C:/repo/docs/guide.md', - relativePath: 'docs/guide.md' - }) - }) - it('extracts hash line anchors from Windows drive-letter absolute .md links', () => { const r = resolveMarkdownLinkTarget( 'C:\\repo\\docs\\guide.md#L10', @@ -66,21 +34,6 @@ describe('resolveMarkdownLinkTarget', () => { }) }) - it('extracts line from #L10', () => { - const r = resolveMarkdownLinkTarget('./guide.md#L10', SOURCE, ROOT) - expect(r).toMatchObject({ kind: 'markdown', line: 10, column: undefined }) - }) - - it('extracts line+col from #L10C5', () => { - const r = resolveMarkdownLinkTarget('./guide.md#L10C5', SOURCE, ROOT) - expect(r).toMatchObject({ kind: 'markdown', line: 10, column: 5 }) - }) - - it('extracts line+col from trailing :10:5 syntax', () => { - const r = resolveMarkdownLinkTarget('./guide.md:10:5', SOURCE, ROOT) - expect(r).toMatchObject({ kind: 'markdown', line: 10, column: 5 }) - }) - it('extracts line+col from non-markdown file links', () => { const r = resolveMarkdownLinkTarget('../src/PdfViewer.tsx:142:7', SOURCE, ROOT) expect(r).toMatchObject({ @@ -92,11 +45,6 @@ describe('resolveMarkdownLinkTarget', () => { }) }) - it('ignores non-line-col hashes', () => { - const r = resolveMarkdownLinkTarget('./guide.md#intro', SOURCE, ROOT) - expect(r).toMatchObject({ kind: 'markdown', line: undefined, column: undefined }) - }) - it('does not treat :note in a filename as a line anchor', () => { const r = resolveMarkdownLinkTarget('./my:note.md', SOURCE, ROOT) expect(r).toMatchObject({ kind: 'markdown', line: undefined }) @@ -108,35 +56,6 @@ describe('resolveMarkdownLinkTarget', () => { expect(r).toMatchObject({ kind: 'markdown', line: undefined }) }) - it('classifies http(s) as external', () => { - const r = resolveMarkdownLinkTarget('https://example.com', SOURCE, ROOT) - expect(r).toEqual({ kind: 'external', url: 'https://example.com/' }) - }) - - it('classifies bare anchor as anchor', () => { - const r = resolveMarkdownLinkTarget('#heading-only', SOURCE, ROOT) - expect(r).toEqual({ kind: 'anchor' }) - }) - - it('classifies non-markdown local file as file', () => { - const r = resolveMarkdownLinkTarget('./image.png', SOURCE, ROOT) - expect(r).toMatchObject({ - kind: 'file', - absolutePath: '/repo/docs/image.png', - relativePath: 'docs/image.png' - }) - }) - - it('classifies explicit file URLs inside the worktree with a relative path', () => { - const r = resolveMarkdownLinkTarget('file:///repo/docs/image.png', SOURCE, ROOT) - expect(r).toMatchObject({ - kind: 'file', - uri: 'file:///repo/docs/image.png', - absolutePath: '/repo/docs/image.png', - relativePath: 'docs/image.png' - }) - }) - it('classifies explicit UNC file URLs without losing the server name', () => { const r = resolveMarkdownLinkTarget( 'file://server/share/repo/docs/image.png', @@ -166,20 +85,7 @@ describe('resolveMarkdownLinkTarget', () => { expect(r?.kind).toBe('file') }) - it('decodes URL-encoded spaces in the path', () => { - const r = resolveMarkdownLinkTarget('./my%20note.md', SOURCE, ROOT) - expect(r).toMatchObject({ - kind: 'markdown', - absolutePath: '/repo/docs/my note.md' - }) - }) - it('returns null for malformed percent-encoded file URL paths', () => { expect(resolveMarkdownLinkTarget('file:///repo/docs/%zz.md', SOURCE, ROOT)).toBeNull() }) - - it('returns null for empty href', () => { - expect(resolveMarkdownLinkTarget('', SOURCE, ROOT)).toBeNull() - expect(resolveMarkdownLinkTarget(undefined, SOURCE, ROOT)).toBeNull() - }) }) diff --git a/src/renderer/src/components/editor/markdown-preview-search.test.ts b/src/renderer/src/components/editor/markdown-preview-search.test.ts index a083548ea5d..95118165a06 100644 --- a/src/renderer/src/components/editor/markdown-preview-search.test.ts +++ b/src/renderer/src/components/editor/markdown-preview-search.test.ts @@ -2,143 +2,10 @@ import { describe, expect, it } from 'vitest' import { MARKDOWN_PREVIEW_SEARCH_QUERY_MAX_BYTES, findTextMatchRanges, - isMarkdownPreviewFindShortcut, - isMarkdownPreviewReplaceShortcut, isMarkdownPreviewSearchQueryTooLarge } from './markdown-preview-search' -describe('isMarkdownPreviewFindShortcut', () => { - it('uses Cmd on macOS', () => { - expect( - isMarkdownPreviewFindShortcut( - { - key: 'f', - code: 'KeyF', - metaKey: true, - ctrlKey: false, - altKey: false, - shiftKey: false - }, - 'darwin' - ) - ).toBe(true) - expect( - isMarkdownPreviewFindShortcut( - { - key: 'f', - code: 'KeyF', - metaKey: false, - ctrlKey: true, - altKey: false, - shiftKey: false - }, - 'darwin' - ) - ).toBe(false) - }) - - it('uses Ctrl on non-macOS platforms', () => { - expect( - isMarkdownPreviewFindShortcut( - { - key: 'f', - code: 'KeyF', - metaKey: false, - ctrlKey: true, - altKey: false, - shiftKey: false - }, - 'linux' - ) - ).toBe(true) - expect( - isMarkdownPreviewFindShortcut( - { - key: 'f', - code: 'KeyF', - metaKey: true, - ctrlKey: false, - altKey: false, - shiftKey: false - }, - 'linux' - ) - ).toBe(false) - }) -}) - -describe('isMarkdownPreviewReplaceShortcut', () => { - it('uses Cmd+Option+F on macOS', () => { - expect( - isMarkdownPreviewReplaceShortcut( - { - key: 'f', - code: 'KeyF', - metaKey: true, - ctrlKey: false, - altKey: true, - shiftKey: false - }, - 'darwin' - ) - ).toBe(true) - expect( - isMarkdownPreviewReplaceShortcut( - { - key: 'h', - code: 'KeyH', - metaKey: false, - ctrlKey: true, - altKey: false, - shiftKey: false - }, - 'darwin' - ) - ).toBe(false) - }) - - it('uses Ctrl+H on non-macOS platforms', () => { - expect( - isMarkdownPreviewReplaceShortcut( - { - key: 'h', - code: 'KeyH', - metaKey: false, - ctrlKey: true, - altKey: false, - shiftKey: false - }, - 'linux' - ) - ).toBe(true) - expect( - isMarkdownPreviewReplaceShortcut( - { - key: 'f', - code: 'KeyF', - metaKey: true, - ctrlKey: false, - altKey: true, - shiftKey: false - }, - 'linux' - ) - ).toBe(false) - }) -}) - describe('findTextMatchRanges', () => { - it('finds case-insensitive literal matches', () => { - expect(findTextMatchRanges('Alpha beta ALPHA', 'alpha')).toEqual([ - { start: 0, end: 5 }, - { start: 11, end: 16 } - ]) - }) - - it('skips overlapping matches so highlights remain stable per text node', () => { - expect(findTextMatchRanges('ababa', 'aba')).toEqual([{ start: 0, end: 3 }]) - }) - it('preserves code-point folding and offsets for Unicode around ASCII matches', () => { expect(findTextMatchRanges('ΟΣ οσ ος', 'οσ')).toEqual([ { start: 0, end: 2 }, @@ -166,42 +33,12 @@ describe('findTextMatchRanges', () => { expect(ranges.map((range) => 'İstanbul'.slice(range.start, range.end))).toEqual(['stan']) }) - it('returns no matches for an empty query', () => { - expect(findTextMatchRanges('Alpha beta', '')).toEqual([]) - }) - - it('matches only same-case occurrences when matchCase is set', () => { - expect(findTextMatchRanges('Alpha beta ALPHA alpha', 'alpha', { matchCase: true })).toEqual([ - { start: 17, end: 22 } - ]) - }) - - it('restricts matches to whole words when wholeWord is set', () => { - expect(findTextMatchRanges('cat category scatter cat', 'cat', { wholeWord: true })).toEqual([ - { start: 0, end: 3 }, - { start: 21, end: 24 } - ]) - }) - - it('treats underscore and digits as word characters for whole-word matching', () => { - expect(findTextMatchRanges('id id_2 (id)', 'id', { wholeWord: true })).toEqual([ - { start: 0, end: 2 }, - { start: 9, end: 11 } - ]) - }) - it('checks whole-word boundaries around astral letters by code point', () => { const text = '𐐀cat cat𐐀 cat' expect(findTextMatchRanges(text, 'cat', { wholeWord: true })).toEqual([{ start: 12, end: 15 }]) }) - it('combines matchCase and wholeWord constraints', () => { - expect( - findTextMatchRanges('Cat cat catalog', 'cat', { matchCase: true, wholeWord: true }) - ).toEqual([{ start: 4, end: 7 }]) - }) - it('rejects oversized pasted queries before indexing preview text', () => { const oversizedQuery = 'secret-preview-search'.repeat(MARKDOWN_PREVIEW_SEARCH_QUERY_MAX_BYTES) const throwingText = { diff --git a/src/renderer/src/components/floating-terminal/floating-terminal-trigger-position.test.ts b/src/renderer/src/components/floating-terminal/floating-terminal-trigger-position.test.ts deleted file mode 100644 index b7fc2ce2e48..00000000000 --- a/src/renderer/src/components/floating-terminal/floating-terminal-trigger-position.test.ts +++ /dev/null @@ -1,237 +0,0 @@ -import { afterEach, describe, expect, it, vi } from 'vitest' -import { - FLOATING_TERMINAL_TRIGGER_POSITION_STORAGE_KEY, - anchorFloatingTerminalTriggerPosition, - clampFloatingTerminalTriggerPosition, - getDefaultFloatingTerminalTriggerCommittedPosition, - getDefaultFloatingTerminalTriggerPosition, - hasUsableFloatingTerminalTriggerViewport, - parseFloatingTerminalTriggerPosition, - persistFloatingTerminalTriggerPosition, - readPersistedFloatingTerminalTriggerPosition, - resolveFloatingTerminalTriggerCommittedPosition, - resolveFloatingTerminalTriggerPosition, - shouldReconcileFloatingTerminalTriggerPosition -} from './floating-terminal-trigger-position' - -function stubViewport(width: number, height: number, localStorage?: Partial): void { - vi.stubGlobal('window', { innerWidth: width, innerHeight: height, localStorage }) -} - -describe('floating terminal trigger position', () => { - afterEach(() => { - vi.unstubAllGlobals() - }) - - it('defaults to the bottom right of the viewport', () => { - stubViewport(1200, 800) - - expect(getDefaultFloatingTerminalTriggerCommittedPosition()).toEqual({ - anchorX: 'right', - anchorY: 'bottom', - offsetX: 24, - offsetY: 72 - }) - expect(getDefaultFloatingTerminalTriggerPosition()).toEqual({ - left: 1140, - top: 692 - }) - }) - - it('converts each anchored position into viewport coordinates', () => { - stubViewport(1200, 800) - - expect( - resolveFloatingTerminalTriggerCommittedPosition({ - anchorX: 'left', - anchorY: 'top', - offsetX: 40, - offsetY: 48 - }) - ).toEqual({ left: 40, top: 48 }) - expect( - resolveFloatingTerminalTriggerCommittedPosition({ - anchorX: 'right', - anchorY: 'bottom', - offsetX: 24, - offsetY: 72 - }) - ).toEqual({ left: 1140, top: 692 }) - }) - - it('clamps parked positions into the viewport', () => { - stubViewport(640, 480) - - expect(clampFloatingTerminalTriggerPosition({ left: 900, top: -20 })).toEqual({ - left: 596, - top: 36 - }) - }) - - it('re-anchors default positions when startup viewport dimensions change', () => { - stubViewport(0, 0) - const initialPosition = getDefaultFloatingTerminalTriggerPosition() - - stubViewport(1200, 800) - - expect(resolveFloatingTerminalTriggerPosition(initialPosition, 'default')).toEqual({ - left: 1140, - top: 692 - }) - }) - - it('clamps user-placed positions instead of re-anchoring them on resize', () => { - stubViewport(1200, 800) - - expect(resolveFloatingTerminalTriggerPosition({ left: 900, top: 500 }, 'user')).toEqual({ - left: 900, - top: 500 - }) - }) - - it('preserves right-bottom anchored placements across skinny viewport clamps', () => { - const committed = { - anchorX: 'right' as const, - anchorY: 'bottom' as const, - offsetX: 48, - offsetY: 80 - } - stubViewport(260, 220) - expect(resolveFloatingTerminalTriggerPosition(committed, 'user')).toEqual({ - left: 176, - top: 104 - }) - - stubViewport(1200, 800) - expect(resolveFloatingTerminalTriggerPosition(committed, 'user')).toEqual({ - left: 1116, - top: 684 - }) - }) - - it('preserves left-top anchored placements across resize', () => { - const committed = anchorFloatingTerminalTriggerPosition({ left: 32, top: 44 }) - - expect(committed).toEqual({ - anchorX: 'left', - anchorY: 'top', - offsetX: 32, - offsetY: 44 - }) - - stubViewport(1200, 800) - expect(resolveFloatingTerminalTriggerPosition(committed!, 'user')).toEqual({ - left: 32, - top: 44 - }) - }) - - it('ignores malformed persisted positions', () => { - stubViewport(640, 480) - - expect(parseFloatingTerminalTriggerPosition('not-json')).toBeNull() - expect(parseFloatingTerminalTriggerPosition('{"left":"1","top":2}')).toBeNull() - expect( - parseFloatingTerminalTriggerPosition( - '{"anchorX":"center","anchorY":"bottom","offsetX":24,"offsetY":72}' - ) - ).toBeNull() - expect( - parseFloatingTerminalTriggerPosition( - '{"anchorX":"right","anchorY":"bottom","offsetX":24,"offsetY":null}' - ) - ).toBeNull() - }) - - it('parses anchored and legacy persisted positions', () => { - expect( - parseFloatingTerminalTriggerPosition( - '{"anchorX":"right","anchorY":"bottom","offsetX":24,"offsetY":72}' - ) - ).toEqual({ - anchorX: 'right', - anchorY: 'bottom', - offsetX: 24, - offsetY: 72 - }) - expect(parseFloatingTerminalTriggerPosition('{"left":900,"top":500}')).toEqual({ - left: 900, - top: 500 - }) - }) - - it('does not clamp parsed positions before the viewport is settled', () => { - stubViewport(0, 0) - - expect(parseFloatingTerminalTriggerPosition('{"left":900,"top":500}')).toEqual({ - left: 900, - top: 500 - }) - }) - - it('detects whether the viewport is usable for persisted user-position clamps', () => { - stubViewport(0, 0) - expect(hasUsableFloatingTerminalTriggerViewport()).toBe(false) - - stubViewport(1200, 800) - expect(hasUsableFloatingTerminalTriggerViewport()).toBe(true) - }) - - it('does not derive anchors from a viewport smaller than the trigger', () => { - stubViewport(40, 40) - - expect(anchorFloatingTerminalTriggerPosition({ left: 8, top: 36 })).toBeNull() - }) - - it('defers user-position reconciliation until the viewport is usable', () => { - stubViewport(0, 0) - - expect(shouldReconcileFloatingTerminalTriggerPosition('default')).toBe(true) - expect(shouldReconcileFloatingTerminalTriggerPosition('user')).toBe(false) - - stubViewport(1200, 800) - expect(shouldReconcileFloatingTerminalTriggerPosition('user')).toBe(true) - }) - - it('reads and writes persisted positions without throwing when storage is unavailable', () => { - const localStorage = { - getItem: vi.fn(() => { - throw new Error('blocked') - }), - setItem: vi.fn(() => { - throw new Error('blocked') - }) - } - stubViewport(1200, 800, localStorage) - - expect(readPersistedFloatingTerminalTriggerPosition()).toBeNull() - expect(() => - persistFloatingTerminalTriggerPosition({ - anchorX: 'right', - anchorY: 'bottom', - offsetX: 24, - offsetY: 72 - }) - ).not.toThrow() - }) - - it('persists through the trigger-specific storage key', () => { - const localStorage = { - getItem: vi.fn(() => null), - setItem: vi.fn() - } - stubViewport(1200, 800, localStorage) - - persistFloatingTerminalTriggerPosition({ - anchorX: 'right', - anchorY: 'bottom', - offsetX: 24, - offsetY: 72 - }) - - expect(localStorage.setItem).toHaveBeenCalledWith( - FLOATING_TERMINAL_TRIGGER_POSITION_STORAGE_KEY, - '{"anchorX":"right","anchorY":"bottom","offsetX":24,"offsetY":72}' - ) - }) -}) diff --git a/src/renderer/src/components/native-chat/native-chat-autoscroll.test.ts b/src/renderer/src/components/native-chat/native-chat-autoscroll.test.ts index a4e55f772ef..507e239901c 100644 --- a/src/renderer/src/components/native-chat/native-chat-autoscroll.test.ts +++ b/src/renderer/src/components/native-chat/native-chat-autoscroll.test.ts @@ -1,18 +1,12 @@ import { describe, it, expect } from 'vitest' import { - distanceFromBottom, isNearBottom, nextFollowingEnd, shouldShowJumpToLatest, NATIVE_CHAT_BOTTOM_THRESHOLD_PX, - NATIVE_CHAT_FOLLOW_REARM_PX, - readerGestureLeavesEnd + NATIVE_CHAT_FOLLOW_REARM_PX } from './native-chat-autoscroll' -const atBottom = { scrollTop: 952, scrollHeight: 1000, clientHeight: 48 } -const scrolledUp = { scrollTop: 0, scrollHeight: 1000, clientHeight: 48 } -const noOverflow = { scrollTop: 0, scrollHeight: 48, clientHeight: 48 } - /** A view parked exactly `distance` px above the end of the same document. */ function parkedAbove(distance: number): { scrollTop: number @@ -22,39 +16,6 @@ function parkedAbove(distance: number): { return { scrollTop: 952 - distance, scrollHeight: 1000, clientHeight: 48 } } -describe('distanceFromBottom', () => { - it('is zero at the exact bottom and never negative', () => { - expect(distanceFromBottom(atBottom)).toBe(0) - expect(distanceFromBottom({ scrollTop: 5000, scrollHeight: 1000, clientHeight: 48 })).toBe(0) - }) -}) - -describe('isNearBottom', () => { - it('sticks within the threshold and detaches beyond it', () => { - expect(isNearBottom(atBottom)).toBe(true) - expect( - isNearBottom({ - scrollTop: 952 - NATIVE_CHAT_BOTTOM_THRESHOLD_PX, - scrollHeight: 1000, - clientHeight: 48 - }) - ).toBe(true) - expect(isNearBottom(scrolledUp)).toBe(false) - }) -}) - -describe('shouldShowJumpToLatest', () => { - it('shows only when detached with content below', () => { - expect(shouldShowJumpToLatest(false, scrolledUp)).toBe(true) - }) - it('hides while stuck to bottom', () => { - expect(shouldShowJumpToLatest(true, scrolledUp)).toBe(false) - }) - it('hides when there is nothing to scroll', () => { - expect(shouldShowJumpToLatest(false, noOverflow)).toBe(false) - }) -}) - // Application writes emit ordinary browser scroll events; explicit marks distinguish delayed echoes from reader movement. describe('nextFollowingEnd', () => { const wellAway = parkedAbove(400) @@ -66,19 +27,11 @@ describe('nextFollowingEnd', () => { settling: false } - it('follows when the reader reaches the end', () => { - expect(nextFollowingEnd(following)).toBe(true) - }) - // Growth after application pinning is not reader detachment, even if the scroll event arrives late. it('keeps following when a delayed application scroll arrives after growth', () => { expect(nextFollowingEnd({ ...following, programmatic: true, geometry: wellAway })).toBe(true) }) - it('keeps following through unmarked passive layout offsets', () => { - expect(nextFollowingEnd({ ...following, geometry: wellAway })).toBe(true) - }) - it.each([0, NATIVE_CHAT_FOLLOW_REARM_PX, 400])( 'does not reattach a detached reader from an application write %i px from the end', (distance) => { @@ -103,21 +56,6 @@ describe('nextFollowingEnd', () => { expect(shouldShowJumpToLatest(false, parked)).toBe(false) }) - it('re-arms at the band and not one pixel past it', () => { - const detached = { - following: false, - programmatic: false, - previousDistanceFromEnd: 400, - settling: false - } - expect( - nextFollowingEnd({ ...detached, geometry: parkedAbove(NATIVE_CHAT_FOLLOW_REARM_PX) }) - ).toBe(true) - expect( - nextFollowingEnd({ ...detached, geometry: parkedAbove(NATIVE_CHAT_FOLLOW_REARM_PX + 1) }) - ).toBe(false) - }) - // Early smooth-scroll frames may be unmarked; movement away from the end must not rearm follow. it('does not reattach a detached reader who is moving away from the end', () => { const leaving = { @@ -145,29 +83,4 @@ describe('nextFollowingEnd', () => { expect(nextFollowingEnd({ ...clamped, settling: true })).toBe(false) expect(nextFollowingEnd({ ...clamped, settling: false })).toBe(true) }) - - it('keeps a following reader through a small move up inside the band', () => { - expect( - nextFollowingEnd({ ...following, previousDistanceFromEnd: 0, geometry: parkedAbove(2) }) - ).toBe(true) - }) - - // Sub-pixel and zoom rounding put the true end a fraction short of exact. - it('holds follow through rounding noise at the end', () => { - expect(nextFollowingEnd({ ...following, geometry: parkedAbove(1.5) })).toBe(true) - }) -}) - -it('only detaches for gestures that can move away from the tail', () => { - expect(readerGestureLeavesEnd({ kind: 'wheel', deltaY: -10, zoom: false }, atBottom)).toBe(true) - expect(readerGestureLeavesEnd({ kind: 'wheel', deltaY: 10, zoom: false }, atBottom)).toBe(false) - expect(readerGestureLeavesEnd({ kind: 'wheel', deltaY: -10, zoom: true }, atBottom)).toBe(false) - expect(readerGestureLeavesEnd({ kind: 'key', key: 'Home' }, atBottom)).toBe(true) - expect(readerGestureLeavesEnd({ kind: 'key', key: 'End' }, atBottom)).toBe(false) - expect(readerGestureLeavesEnd({ kind: 'scrollbar-press' }, atBottom)).toBe(true) - expect(readerGestureLeavesEnd({ kind: 'touch-drag' }, parkedAbove(60))).toBe(true) - expect(readerGestureLeavesEnd({ kind: 'touch-drag' }, atBottom)).toBe(false) - expect(readerGestureLeavesEnd({ kind: 'wheel', deltaY: -10, zoom: false }, noOverflow)).toBe( - false - ) }) diff --git a/src/renderer/src/components/native-chat/native-chat-availability.test.ts b/src/renderer/src/components/native-chat/native-chat-availability.test.ts index 7817fa6c74a..577b20737fc 100644 --- a/src/renderer/src/components/native-chat/native-chat-availability.test.ts +++ b/src/renderer/src/components/native-chat/native-chat-availability.test.ts @@ -3,85 +3,6 @@ import { canToggleNativeChat } from './native-chat-availability' import { isNativeChatTranscriptLocalReadable } from '@/lib/native-chat-transcript-readability' describe('canToggleNativeChat', () => { - it('allows a terminal launched with a supported coding agent', () => { - expect( - canToggleNativeChat({ - contentType: 'terminal', - launchAgent: 'claude' - }) - ).toBe(true) - }) - - it('allows a terminal with a live detected supported agent but no launchAgent', () => { - expect( - canToggleNativeChat({ - contentType: 'terminal', - launchAgent: null, - detectedAgent: 'codex' - }) - ).toBe(true) - }) - - it('allows a terminal with a resolved title/foreground supported agent before hooks arrive', () => { - expect( - canToggleNativeChat({ - contentType: 'terminal', - launchAgent: null, - resolvedAgent: 'claude' - }) - ).toBe(true) - }) - - it('allows the OpenClaude variant', () => { - expect( - canToggleNativeChat({ - contentType: 'terminal', - launchAgent: 'openclaude' - }) - ).toBe(true) - }) - - it('allows an existing chat view to toggle back after live signals are gone', () => { - expect( - canToggleNativeChat({ - contentType: 'terminal', - launchAgent: null, - isChatViewMode: true - }) - ).toBe(true) - }) - - it('accepts local Grok once native chat can parse its transcript', () => { - expect( - canToggleNativeChat({ - contentType: 'terminal', - launchAgent: 'grok', - nativeChatTranscriptIsLocalReadable: isNativeChatTranscriptLocalReadable(null) - }) - ).toBe(true) - }) - - it('accepts runtime-owned Grok because Model B reads the transcript locally', () => { - expect( - canToggleNativeChat({ - contentType: 'terminal', - launchAgent: 'grok', - nativeChatTranscriptIsLocalReadable: - isNativeChatTranscriptLocalReadable('runtime-ssh-env-1') - }) - ).toBe(true) - }) - - it('rejects Model-A SSH Grok when its transcript is remote-only', () => { - expect( - canToggleNativeChat({ - contentType: 'terminal', - launchAgent: 'grok', - nativeChatTranscriptIsLocalReadable: isNativeChatTranscriptLocalReadable('ssh-target-1') - }) - ).toBe(false) - }) - // omp exposes no hook transcript path; its session file requires readable agent storage. it('rejects Model-A SSH omp but accepts it local and runtime-owned', () => { const forConnection = (connectionId: string | null): boolean => @@ -106,27 +27,6 @@ describe('canToggleNativeChat', () => { ).toBe(true) }) - it.each(['gemini'] as const)('rejects unsupported agent %s detected live', (agent) => { - expect( - canToggleNativeChat({ - contentType: 'terminal', - launchAgent: null, - detectedAgent: agent - }) - ).toBe(false) - }) - - it('accepts Grok when resolved from the title', () => { - expect( - canToggleNativeChat({ - contentType: 'terminal', - launchAgent: null, - resolvedAgent: 'grok', - nativeChatTranscriptIsLocalReadable: true - }) - ).toBe(true) - }) - it('rejects a stale supported title when live detection found an unsupported agent', () => { expect( canToggleNativeChat({ @@ -157,37 +57,4 @@ describe('canToggleNativeChat', () => { }) ).toBe(false) }) - - it('rejects a plain shell terminal with no agent', () => { - expect( - canToggleNativeChat({ - contentType: 'terminal', - launchAgent: null, - detectedAgent: null - }) - ).toBe(false) - }) - - it('rejects a plain shell terminal with everything omitted', () => { - expect(canToggleNativeChat({ contentType: 'terminal' })).toBe(false) - }) - - it('rejects an editor tab even if a supported agent hint were somehow present', () => { - expect( - canToggleNativeChat({ - contentType: 'editor', - launchAgent: 'codex', - detectedAgent: 'codex' - }) - ).toBe(false) - }) - - it('rejects a browser tab', () => { - expect( - canToggleNativeChat({ - contentType: 'browser', - detectedAgent: 'claude' - }) - ).toBe(false) - }) }) diff --git a/src/renderer/src/components/native-chat/native-chat-typing-redirect.test.ts b/src/renderer/src/components/native-chat/native-chat-typing-redirect.test.ts index b357a40ef70..8004ec448ea 100644 --- a/src/renderer/src/components/native-chat/native-chat-typing-redirect.test.ts +++ b/src/renderer/src/components/native-chat/native-chat-typing-redirect.test.ts @@ -29,27 +29,11 @@ function interactiveTarget(): EventTarget { } describe('shouldRedirectNativeChatTyping', () => { - it('redirects printable typing from the native chat pane', () => { - expect(shouldRedirectNativeChatTyping(keyEvent({ key: 'x' }), true)).toBe(true) - }) - - it('does not redirect shortcuts or non-printable keys', () => { - expect(shouldRedirectNativeChatTyping(keyEvent({ key: 'Enter' }), true)).toBe(false) - expect(shouldRedirectNativeChatTyping(keyEvent({ key: 'a', metaKey: true }), true)).toBe(false) - expect(shouldRedirectNativeChatTyping(keyEvent({ key: 'a', ctrlKey: true }), true)).toBe(false) - }) - it('does not redirect IME composition or already-handled events', () => { expect(shouldRedirectNativeChatTyping(keyEvent({ isComposing: true }), true)).toBe(false) expect(shouldRedirectNativeChatTyping(keyEvent({ defaultPrevented: true }), true)).toBe(false) }) - it('leaves interactive targets alone', () => { - expect(shouldRedirectNativeChatTyping(keyEvent({ target: interactiveTarget() }), true)).toBe( - false - ) - }) - it('passes typed characters but not Space through a focused button or link', () => { const button = document.createElement('button') const link = document.createElement('a') @@ -97,11 +81,6 @@ describe('shouldRedirectNativeChatTyping', () => { }) describe('shouldFocusNativeChatComposerFromEditingKey', () => { - it('focuses the composer on Backspace/Delete from the pane', () => { - expect(shouldFocusNativeChatComposerFromEditingKey(keyEvent({ key: 'Backspace' }))).toBe(true) - expect(shouldFocusNativeChatComposerFromEditingKey(keyEvent({ key: 'Delete' }))).toBe(true) - }) - it('ignores other keys, shortcut/editing modifiers, IME composition, and handled events', () => { expect(shouldFocusNativeChatComposerFromEditingKey(keyEvent({ key: 'a' }))).toBe(false) expect( @@ -126,36 +105,9 @@ describe('shouldFocusNativeChatComposerFromEditingKey', () => { shouldFocusNativeChatComposerFromEditingKey(keyEvent({ key: 'Backspace', isComposing: true })) ).toBe(false) }) - - it('leaves interactive targets (the textarea itself) alone', () => { - expect( - shouldFocusNativeChatComposerFromEditingKey( - keyEvent({ key: 'Backspace', target: interactiveTarget() }) - ) - ).toBe(false) - }) -}) - -describe('shouldFocusNativeChatPaneFromPointerTarget', () => { - it('focuses the pane for non-interactive clicks', () => { - expect(shouldFocusNativeChatPaneFromPointerTarget(inertTarget())).toBe(true) - }) - - it('does not steal focus from controls', () => { - expect(shouldFocusNativeChatPaneFromPointerTarget(interactiveTarget())).toBe(false) - }) }) describe('shouldFocusNativeChatInputForPaste', () => { - it.each([ - [true, { key: 'v', metaKey: true }], - [false, { key: 'v', ctrlKey: true }], - [false, { key: 'V', ctrlKey: true, shiftKey: true }], - [false, { key: 'Insert', shiftKey: true }] - ])('takes the platform paste chord (mac=%s) outside an input', (isMac, keys) => { - expect(shouldFocusNativeChatInputForPaste(keyEvent(keys), isMac)).toBe(true) - }) - it('ignores the other platform chord, Alt chords, IME, handled events and inputs', () => { expect(shouldFocusNativeChatInputForPaste(keyEvent({ key: 'v', ctrlKey: true }), true)).toBe( false diff --git a/src/renderer/src/components/right-sidebar/ai-vault-session-resume-in-chat.test.ts b/src/renderer/src/components/right-sidebar/ai-vault-session-resume-in-chat.test.ts index 9f786039f31..b42003cf31c 100644 --- a/src/renderer/src/components/right-sidebar/ai-vault-session-resume-in-chat.test.ts +++ b/src/renderer/src/components/right-sidebar/ai-vault-session-resume-in-chat.test.ts @@ -1,9 +1,6 @@ import { describe, expect, it } from 'vitest' import type { AiVaultSession } from '../../../../shared/ai-vault-types' -import { - aiVaultSessionCwdMatchesWorkspace, - resolveAiVaultSessionResumeInChatEligibility -} from './ai-vault-session-resume-in-chat' +import { resolveAiVaultSessionResumeInChatEligibility } from './ai-vault-session-resume-in-chat' type ResumeInChatSession = Parameters< typeof resolveAiVaultSessionResumeInChatEligibility @@ -36,30 +33,6 @@ function eligibility( } describe('resolveAiVaultSessionResumeInChatEligibility', () => { - it('offers the chat for a local Claude row in its own workspace', () => { - expect(eligibility()).toEqual({ available: true, workspaceId: 'repo-1::/repo/orca' }) - }) - - it.each(['grok', 'omp', 'opencode', 'opencode2', 'pi'])( - 'offers the chat for a local %s row in its own workspace', - (agent) => { - expect(eligibility({ session: session({ agent }) })).toEqual({ - available: true, - workspaceId: 'repo-1::/repo/orca' - }) - } - ) - - it.each(['hermes', 'gemini'])( - 'refuses %s, whose chats cannot take over a CLI conversation', - (agent) => { - expect(eligibility({ session: session({ agent }) })).toEqual({ - available: false, - reason: 'agent' - }) - } - ) - it('refuses a row already adopted into a chat before any other check', () => { expect( eligibility({ @@ -101,47 +74,12 @@ describe('resolveAiVaultSessionResumeInChatEligibility', () => { ).toEqual({ available: false, reason: 'remote' }) }) - it('refuses a row whose transcript is stored inside WSL', () => { - expect( - eligibility({ - session: session({ - filePath: '//wsl.localhost/Ubuntu-22.04/home/dev/.claude/projects/p/session-1.jsonl' - }) - }) - ).toEqual({ available: false, reason: 'remote' }) - }) - - it('refuses a transcript that holds no conversation', () => { - expect(eligibility({ session: session({ messageCount: 0, previewMessages: [] }) })).toEqual({ - available: false, - reason: 'empty' - }) - }) - - it('offers a zero-count row whose preview proves the turns exist', () => { - expect( - eligibility({ - session: session({ - messageCount: 0, - previewMessages: [{ role: 'user', text: 'hello', timestamp: null }] - }) - }) - ).toMatchObject({ available: true }) - }) - it('refuses when the same pair could not take the structured route for a fresh chat', () => { expect(eligibility({ structuredRouteAvailable: false })).toEqual({ available: false, reason: 'workspace' }) }) - - it('refuses when there is no target workspace at all', () => { - expect(eligibility({ targetWorkspaceId: null })).toEqual({ - available: false, - reason: 'workspace' - }) - }) }) describe('workspace matching, which binds the agents that store conversations per folder', () => { @@ -184,21 +122,3 @@ describe('workspace matching, which binds the agents that store conversations pe ).toMatchObject({ available: true }) }) }) - -describe('aiVaultSessionCwdMatchesWorkspace', () => { - it('ignores separator, case, and a trailing slash', () => { - expect(aiVaultSessionCwdMatchesWorkspace('C:\\repo\\Orca', 'c:/repo/orca')).toBe(true) - expect(aiVaultSessionCwdMatchesWorkspace('/repo/orca/', '/repo/orca')).toBe(true) - expect(aiVaultSessionCwdMatchesWorkspace(' /repo/orca ', '/repo/orca')).toBe(true) - }) - - it('never calls a missing path a match', () => { - expect(aiVaultSessionCwdMatchesWorkspace(null, '/repo/orca')).toBe(false) - expect(aiVaultSessionCwdMatchesWorkspace('/repo/orca', null)).toBe(false) - expect(aiVaultSessionCwdMatchesWorkspace('', '')).toBe(false) - }) - - it('does not treat a sibling directory as the same workspace', () => { - expect(aiVaultSessionCwdMatchesWorkspace('/repo/orca-2', '/repo/orca')).toBe(false) - }) -}) diff --git a/src/renderer/src/components/right-sidebar/file-explorer-batch-deletion.test.ts b/src/renderer/src/components/right-sidebar/file-explorer-batch-deletion.test.ts index 2e94e0222ca..c69b6ce9187 100644 --- a/src/renderer/src/components/right-sidebar/file-explorer-batch-deletion.test.ts +++ b/src/renderer/src/components/right-sidebar/file-explorer-batch-deletion.test.ts @@ -13,20 +13,6 @@ function node(path: string, isDirectory = false): TreeNode { } describe('selectDeletionRoots', () => { - it('normalizes each selected path once instead of once per possible parent', () => { - let reads = 0 - const nodes = Array.from({ length: 1000 }, (_, i) => ({ - ...node(`/repo/directory-${i}`, true), - get path() { - reads++ - return `/repo/directory-${i}` - } - })) - const selected = selectDeletionRoots(nodes) - expect(reads).toBe(2000) - selected.forEach((entry, index) => expect(entry).toBe(nodes[index])) - }) - it('preserves WSL case-sensitive paths while accepting UNC aliases', () => { const parent = node('//wsl.localhost/Ubuntu/Repo', true) const child = node('//wsl$/ubuntu/Repo/file') @@ -34,46 +20,6 @@ describe('selectDeletionRoots', () => { expect(selectDeletionRoots([parent, child, outside])).toEqual([parent, outside]) }) - it('keeps unrelated files and directories', () => { - const nodes = [node('/repo/a.ts'), node('/repo/b.ts'), node('/repo/docs', true)] - expect(selectDeletionRoots(nodes)).toEqual(nodes) - }) - - it('drops children of a selected directory', () => { - const dir = node('/repo/docs', true) - const child = node('/repo/docs/readme.md') - const nested = node('/repo/docs/guides/intro.md') - const outside = node('/repo/a.ts') - expect(selectDeletionRoots([dir, child, nested, outside])).toEqual([dir, outside]) - }) - - it('keeps siblings whose paths merely share a prefix', () => { - const dir = node('/repo/docs', true) - const lookalike = node('/repo/docs-old/readme.md') - expect(selectDeletionRoots([dir, lookalike])).toEqual([dir, lookalike]) - }) - - it('does not treat selected files as containers', () => { - const file = node('/repo/a.ts') - const other = node('/repo/a.ts/impossible-child') - expect(selectDeletionRoots([file, other])).toEqual([file, other]) - }) - - it('reads directory membership once per selected node, including file-only selections', () => { - let directoryReads = 0 - const nodes = Array.from({ length: 1_000 }, (_, index) => ({ - ...node(`/repo/file-${index}.ts`), - get isDirectory() { - directoryReads += 1 - return false - } - })) - const selected = selectDeletionRoots(nodes) - expect(directoryReads).toBe(nodes.length) - expect(selected).not.toBe(nodes) - selected.forEach((entry, index) => expect(entry).toBe(nodes[index])) - }) - it('preserves order, node identity and host ownership when children precede parents', () => { const child = node('/repo/docs/guide.md') const first = { ...node('/repo/first.ts'), operationOwner: { kind: 'local' as const } } @@ -94,14 +40,6 @@ describe('selectDeletionRoots', () => { expect(nodes).toEqual([child, first, parent, last]) }) - it('keeps repeated references but excludes distinct directories with the same path', () => { - const dir = node('/repo/docs', true) - expect(selectDeletionRoots([dir, dir])).toEqual([dir, dir]) - expect(selectDeletionRoots([dir, { ...dir }])).toEqual([]) - const file = node('/repo/docs') - expect(selectDeletionRoots([file, dir])).toEqual([dir]) - }) - it.each([ ['/repo/docs/', '/repo/docs/guide.md', '/repo/docs-other/guide.md'], ['C:\\Repo\\Docs\\', 'c:/repo/docs/guide.md', 'C:/Repo/Docs-other/guide.md'], @@ -157,20 +95,6 @@ describe('runBatchDeletion', () => { expect(deleteNode).not.toHaveBeenCalled() }) - it('skips confirmation when none is needed', async () => { - const confirmBatch = vi.fn() - - const deleted = await runBatchDeletion({ - roots, - needsConfirmation: false, - confirmBatch, - deleteNode: vi.fn(async () => true) - }) - - expect(confirmBatch).not.toHaveBeenCalled() - expect(deleted).toEqual(roots) - }) - it('excludes failed deletes from the result but continues the batch', async () => { const deleteNode = vi.fn(async (n: TreeNode) => n.path !== '/repo/b.ts') diff --git a/src/renderer/src/components/right-sidebar/useFileExplorerWatch.test.ts b/src/renderer/src/components/right-sidebar/useFileExplorerWatch.test.ts index 3f1b6c8753c..1dd228dcd60 100644 --- a/src/renderer/src/components/right-sidebar/useFileExplorerWatch.test.ts +++ b/src/renderer/src/components/right-sidebar/useFileExplorerWatch.test.ts @@ -8,16 +8,6 @@ import { import type { AppState } from '@/store/types' describe('getExternalFileChangeRelativePath', () => { - it('returns a worktree-relative file path for external file updates', () => { - expect(getExternalFileChangeRelativePath('/repo', '/repo/config/settings.json', false)).toBe( - 'config/settings.json' - ) - }) - - it('ignores directory events so only file tabs reload', () => { - expect(getExternalFileChangeRelativePath('/repo', '/repo/config', true)).toBeNull() - }) - it('treats isDirectory=undefined as a file so delete events still notify', () => { // Deleted paths cannot report isDirectory; undefined must still invalidate editor contents. expect( @@ -25,53 +15,9 @@ describe('getExternalFileChangeRelativePath', () => { ).toBe('config/settings.json') }) - it('normalizes Windows separators before deriving the relative path', () => { - expect( - getExternalFileChangeRelativePath('C:\\repo', 'C:\\repo\\config\\settings.json', false) - ).toBe('config/settings.json') - }) - - it('matches Windows paths case-insensitively before deriving the relative path', () => { - expect( - getExternalFileChangeRelativePath('C:\\Repo', 'c:\\repo\\config\\settings.json', false) - ).toBe('config/settings.json') - }) - - it('preserves UNC roots when deriving the relative path', () => { - expect( - getExternalFileChangeRelativePath( - '//Server/Share/Repo', - '//server/share/repo/config/settings.json', - false - ) - ).toBe('config/settings.json') - }) - - it('ignores paths outside the active worktree', () => { - expect( - getExternalFileChangeRelativePath('/repo', '/other/config/settings.json', false) - ).toBeNull() - }) - it('rejects sibling worktrees whose path merely shares a prefix', () => { expect(getExternalFileChangeRelativePath('/repo', '/repo-other/file.ts', false)).toBeNull() }) - - it('returns null when the changed path is the worktree root itself', () => { - expect(getExternalFileChangeRelativePath('/repo', '/repo', false)).toBeNull() - }) - - it('tolerates a trailing slash on the worktree path', () => { - expect(getExternalFileChangeRelativePath('/repo/', '/repo/src/index.ts', false)).toBe( - 'src/index.ts' - ) - }) - - it('preserves nested segments in the returned relative path', () => { - expect(getExternalFileChangeRelativePath('/repo', '/repo/a/b/c/deep.ts', false)).toBe( - 'a/b/c/deep.ts' - ) - }) }) describe('canonicalizeFileExplorerWatchPath', () => { @@ -98,23 +44,10 @@ describe('canonicalizeFileExplorerWatchPath', () => { }) describe('resolveCachedDirPath', () => { - it('returns the exact cache key when present', () => { - const cache = { '/repo/src': { children: [] } } - expect(resolveCachedDirPath(cache, '/repo/src')).toBe('/repo/src') - }) - it('matches Windows cache keys case-insensitively (#10264)', () => { const cache = { 'C:\\Repo\\src': { children: [] } } expect(resolveCachedDirPath(cache, 'c:\\repo\\src')).toBe('C:\\Repo\\src') }) - - it('falls back to the worktree root path when the root is not yet cached', () => { - expect(resolveCachedDirPath({}, 'C:\\Repo', 'C:\\Repo')).toBe('C:\\Repo') - }) - - it('returns null when the directory is not cached and is not the worktree root', () => { - expect(resolveCachedDirPath({ '/repo': { children: [] } }, '/repo/src', '/repo')).toBeNull() - }) }) describe('getFileExplorerWatchRuntimeEnvironmentId', () => { diff --git a/src/renderer/src/components/settings/PrivacyPane.test.ts b/src/renderer/src/components/settings/PrivacyPane.test.ts deleted file mode 100644 index b491802930d..00000000000 --- a/src/renderer/src/components/settings/PrivacyPane.test.ts +++ /dev/null @@ -1,197 +0,0 @@ -// Use pure predicates for consent gates because static SSR never runs the consent-loading effect. - -import React from 'react' -import { renderToStaticMarkup } from 'react-dom/server' -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import type { GlobalSettings } from '../../../../shared/global-settings-types' -import type { TelemetryConsentState } from '../../../../shared/telemetry-consent-types' - -const { getConsentStateMock, setOptInMock } = vi.hoisted(() => ({ - getConsentStateMock: vi.fn<() => Promise>(), - setOptInMock: vi.fn<(optedIn: boolean) => Promise>() -})) -vi.mock('../../lib/telemetry', () => ({ - getConsentState: getConsentStateMock, - setOptIn: setOptInMock -})) - -import { PrivacyPane, computeBlockedReason, envVarNameForReason, isEnvBlocked } from './PrivacyPane' - -function buildSettings( - telemetry: Partial> = {} -): GlobalSettings { - return { - telemetry: { - installId: 'test-install-id', - existedBeforeTelemetryRelease: false, - optedIn: true, - ...telemetry - } - } as unknown as GlobalSettings -} - -describe('PrivacyPane — toggle markup reflects stored preference', () => { - beforeEach(() => { - getConsentStateMock.mockReset() - getConsentStateMock.mockResolvedValue({ effective: 'enabled' }) - }) - afterEach(() => { - vi.restoreAllMocks() - }) - - it('renders the toggle in its checked state when optedIn is true', () => { - const markup = renderToStaticMarkup( - React.createElement(PrivacyPane, { - settings: buildSettings({ optedIn: true }) - }) - ) - expect(markup).toContain('aria-checked="true"') - }) - - it('renders the toggle unchecked when optedIn is false', () => { - const markup = renderToStaticMarkup( - React.createElement(PrivacyPane, { - settings: buildSettings({ optedIn: false }) - }) - ) - expect(markup).toContain('aria-checked="false"') - }) - - it('renders the toggle unchecked when optedIn is null (existing user pre-banner)', () => { - // Banner-pending users transmit nothing; Settings opt-in uses its own attribution and resolves the notice. - const markup = renderToStaticMarkup( - React.createElement(PrivacyPane, { - settings: buildSettings({ - existedBeforeTelemetryRelease: true, - optedIn: null - }) - }) - ) - expect(markup).toContain('aria-checked="false"') - }) -}) - -describe('PrivacyPane — isEnvBlocked', () => { - it('returns true for DO_NOT_TRACK', () => { - expect(isEnvBlocked({ effective: 'disabled', reason: 'do_not_track' })).toBe(true) - }) - - it('returns true for ORCA_TELEMETRY_DISABLED', () => { - expect(isEnvBlocked({ effective: 'disabled', reason: 'orca_disabled' })).toBe(true) - }) - - it('returns true for CI', () => { - expect(isEnvBlocked({ effective: 'disabled', reason: 'ci' })).toBe(true) - }) - - it('returns false for user_opt_out (not env-blocked — the toggle remains actionable)', () => { - // User opt-out is reversible preference, so it must not disable the toggle like an environment constraint. - expect(isEnvBlocked({ effective: 'disabled', reason: 'user_opt_out' })).toBe(false) - }) - - it('returns false for pending_banner', () => { - expect(isEnvBlocked({ effective: 'pending_banner' })).toBe(false) - }) - - it('returns false for enabled', () => { - expect(isEnvBlocked({ effective: 'enabled' })).toBe(false) - }) - - it('returns false for null (consent not yet fetched)', () => { - expect(isEnvBlocked(null)).toBe(false) - }) -}) - -describe('PrivacyPane — envVarNameForReason', () => { - it('maps do_not_track to DO_NOT_TRACK', () => { - expect(envVarNameForReason('do_not_track')).toBe('DO_NOT_TRACK') - }) - - it('maps orca_disabled to ORCA_TELEMETRY_DISABLED', () => { - expect(envVarNameForReason('orca_disabled')).toBe('ORCA_TELEMETRY_DISABLED') - }) - - it('maps ci to CI', () => { - expect(envVarNameForReason('ci')).toBe('CI') - }) -}) - -describe('PrivacyPane — computeBlockedReason', () => { - it('returns null when consent is enabled', () => { - expect(computeBlockedReason({ effective: 'enabled' })).toBeNull() - }) - - it('returns null for a user_opt_out state (the toggle must remain actionable)', () => { - expect(computeBlockedReason({ effective: 'disabled', reason: 'user_opt_out' })).toBeNull() - }) - - it('returns null for pending_banner (banner-pending does NOT gate the toggle)', () => { - // Settings opt-in clears optedIn=null and dismisses the first-launch notice. - expect(computeBlockedReason({ effective: 'pending_banner' })).toBeNull() - }) - - it('names DO_NOT_TRACK as the env reason when set', () => { - const result = computeBlockedReason({ effective: 'disabled', reason: 'do_not_track' }) - expect(result).toEqual({ kind: 'env', reason: 'do_not_track' }) - }) - - it('names ORCA_TELEMETRY_DISABLED as the env reason when set', () => { - const result = computeBlockedReason({ effective: 'disabled', reason: 'orca_disabled' }) - expect(result).toEqual({ kind: 'env', reason: 'orca_disabled' }) - }) - - it('names CI as the env reason when set', () => { - const result = computeBlockedReason({ effective: 'disabled', reason: 'ci' }) - expect(result).toEqual({ kind: 'env', reason: 'ci' }) - }) -}) - -describe('PrivacyPane — markup respects blocked state', () => { - const switchTag = (markup: string): string => - markup.match(/]*role="switch"[^>]*>/)?.[0] ?? '' - - // Static SSR keeps consent null; predicate tests cover blocked reasons, while these cases verify actionable cohorts. - beforeEach(() => { - getConsentStateMock.mockReset() - getConsentStateMock.mockResolvedValue({ effective: 'enabled' }) - }) - afterEach(() => { - vi.restoreAllMocks() - }) - - it('leaves the toggle enabled for a new user', () => { - const markup = renderToStaticMarkup( - React.createElement(PrivacyPane, { - settings: buildSettings({ - existedBeforeTelemetryRelease: false, - optedIn: true - }) - }) - ) - expect(switchTag(markup)).not.toContain(' disabled=""') - }) - - it('leaves the toggle enabled for an existing user awaiting the banner (flip dismisses the banner)', () => { - const markup = renderToStaticMarkup( - React.createElement(PrivacyPane, { - settings: buildSettings({ - existedBeforeTelemetryRelease: true, - optedIn: null - }) - }) - ) - expect(switchTag(markup)).not.toContain(' disabled=""') - }) - - it('leaves the toggle enabled once the existing-user banner is resolved', () => { - const markup = renderToStaticMarkup( - React.createElement(PrivacyPane, { - settings: buildSettings({ - existedBeforeTelemetryRelease: true, - optedIn: true - }) - }) - ) - expect(switchTag(markup)).not.toContain(' disabled=""') - }) -}) diff --git a/src/renderer/src/components/sidebar/CommentMarkdown.unknown-html.test.tsx b/src/renderer/src/components/sidebar/CommentMarkdown.unknown-html.test.tsx index 9ea847f2428..bb16e797f5a 100644 --- a/src/renderer/src/components/sidebar/CommentMarkdown.unknown-html.test.tsx +++ b/src/renderer/src/components/sidebar/CommentMarkdown.unknown-html.test.tsx @@ -8,25 +8,6 @@ function renderChat(content: string): string { } describe('CommentMarkdown unknown HTML as text', () => { - it('keeps an unknown inline tag in native chat prose', () => { - const markup = renderChat('use\na ') - - expect(markup).toContain('a <se-tabs>') - }) - - it('keeps unknown opening and closing tags around their content', () => { - const markup = renderChat('wrap x here') - - expect(markup).toContain('wrap <se-tabs>x</se-tabs> here') - }) - - it('keeps a component-style tag that starts a block', () => { - const markup = renderChat('