diff --git a/config/scripts/ci-shard-timings.json b/config/scripts/ci-shard-timings.json index db45e37bb80..962ccdaea85 100644 --- a/config/scripts/ci-shard-timings.json +++ b/config/scripts/ci-shard-timings.json @@ -7379,7 +7379,6 @@ "src/renderer/src/components/dashboard/AgentDashboardSettingsMenu.test.tsx": 479, "src/renderer/src/components/dashboard/DashboardAgentChildDisclosure.test.tsx": 1607, "src/renderer/src/components/dashboard/DashboardAgentRow.test.tsx": 1601, - "src/renderer/src/components/dashboard/DashboardAgentRowToolStep.test.tsx": 60, "src/renderer/src/components/dashboard/agent-dashboard-open-mode.test.ts": 43, "src/renderer/src/components/dashboard/agent-finished-timestamp.test.ts": 115, "src/renderer/src/components/dashboard/agent-row-lineage-model.test.ts": 99, @@ -7909,7 +7908,6 @@ "src/renderer/src/components/landing-preflight-runtime-boundary.test.ts": 1576, "src/renderer/src/components/landing-preflight-unverifiable-host.test.tsx": 1436, "src/renderer/src/components/linear-api-key-dialog-state.test.ts": 53, - "src/renderer/src/components/linear-issue-attribute-filter-coverage-agreement.test.ts": 590, "src/renderer/src/components/linear-issue-attribute-filter-dropdowns.test.tsx": 1226, "src/renderer/src/components/linear-issue-attribute-filter-primary-team.test.ts": 53, "src/renderer/src/components/linear-issue-attribute-filter-team-ids.test.ts": 48, @@ -7919,7 +7917,6 @@ "src/renderer/src/components/linear-issue-view-storage.test.ts": 168, "src/renderer/src/components/linear-issue-workspace-detail-state.test.tsx": 205, "src/renderer/src/components/linear-issue-workspace-text.test.ts": 128, - "src/renderer/src/components/linear-project-presentation.test.ts": 32, "src/renderer/src/components/linear-project-search-query.test.ts": 66, "src/renderer/src/components/linear-project-view-surfaces.test.tsx": 440, "src/renderer/src/components/linear-scope-selector.test.ts": 155, @@ -8443,7 +8440,6 @@ "src/renderer/src/components/right-sidebar/PortsPanel.test.tsx": 1370, "src/renderer/src/components/right-sidebar/PullRequestComposer.generate-tooltip.test.tsx": 804, "src/renderer/src/components/right-sidebar/SearchResultItems.test.tsx": 158, - "src/renderer/src/components/right-sidebar/SessionRowTrailingActions.test.tsx": 212, "src/renderer/src/components/right-sidebar/SourceControl.branch-line-total.test.tsx": 1173, "src/renderer/src/components/right-sidebar/SourceControl.commit-drafts.test.ts": 146, "src/renderer/src/components/right-sidebar/SourceControl.commit-failure-recovery.test.ts": 68, @@ -8573,7 +8569,6 @@ "src/renderer/src/components/right-sidebar/file-explorer-watch-selected-host.test.ts": 1446, "src/renderer/src/components/right-sidebar/file-search-include-pattern.test.ts": 36, "src/renderer/src/components/right-sidebar/folder-workspace-attached-worktrees.test.ts": 57, - "src/renderer/src/components/right-sidebar/fork-push-target-label.test.ts": 35, "src/renderer/src/components/right-sidebar/git-status-push-signal-refresh.test.ts": 161, "src/renderer/src/components/right-sidebar/git-status-refresh-branch-line-total.test.ts": 442, "src/renderer/src/components/right-sidebar/git-status-refresh-scheduler.test.ts": 64, @@ -8611,7 +8606,6 @@ "src/renderer/src/components/right-sidebar/source-control-active-open-file-keys.test.ts": 28, "src/renderer/src/components/right-sidebar/source-control-branch-context-row.test.tsx": 248, "src/renderer/src/components/right-sidebar/source-control-branch-context-stats.test.ts": 102, - "src/renderer/src/components/right-sidebar/source-control-branch-section-heading.test.tsx": 1344, "src/renderer/src/components/right-sidebar/source-control-commit-eligibility.test.ts": 57, "src/renderer/src/components/right-sidebar/source-control-commit-message-rows.test.ts": 70, "src/renderer/src/components/right-sidebar/source-control-create-pr-intent-flow.test.ts": 57, @@ -8860,7 +8854,6 @@ "src/renderer/src/components/settings/TerminalSettingsPreview.lifecycle.test.tsx": 318, "src/renderer/src/components/settings/TerminalTccAttributionNotice.test.tsx": 282, "src/renderer/src/components/settings/TerminalThemeSections.lifecycle.test.ts": 1337, - "src/renderer/src/components/settings/UnsealedCredentialNotice.test.tsx": 304, "src/renderer/src/components/settings/VoiceDictationSettingsSection.test.tsx": 491, "src/renderer/src/components/settings/VoiceMicrophoneSetting.test.tsx": 577, "src/renderer/src/components/settings/VoicePane.test.tsx": 770, @@ -8908,7 +8901,6 @@ "src/renderer/src/components/settings/plugin-install-source.test.ts": 110, "src/renderer/src/components/settings/provider-account-scope.test.ts": 109, "src/renderer/src/components/settings/provider-account-visibility.test.ts": 49, - "src/renderer/src/components/settings/provider-rate-limit-scope-panels.test.tsx": 475, "src/renderer/src/components/settings/repository-source-control-ai-global-ux.test.ts": 585, "src/renderer/src/components/settings/repository/RepositoryJiraProjectSection.test.tsx": 292, "src/renderer/src/components/settings/repository/RepositoryPaneDraftInput.test.tsx": 428, @@ -8931,7 +8923,6 @@ "src/renderer/src/components/settings/runtime-server-row-presentation.test.ts": 357, "src/renderer/src/components/settings/runtime-server-row-unverifiable-host.test.tsx": 2162, "src/renderer/src/components/settings/runtime-share-computer-button.test.tsx": 1304, - "src/renderer/src/components/settings/session-history-status-copy.test.ts": 225, "src/renderer/src/components/settings/session-search-computer-rollup.test.ts": 42, "src/renderer/src/components/settings/settings-deep-link-target-watcher.test.ts": 149, "src/renderer/src/components/settings/settings-form-option-filter.test.ts": 41, @@ -8941,7 +8932,6 @@ "src/renderer/src/components/settings/settings-project-regrouping.test.ts": 467, "src/renderer/src/components/settings/settings-search-keywords.test.ts": 123, "src/renderer/src/components/settings/settings-search.test.ts": 49, - "src/renderer/src/components/settings/settings-setup-guide-progress-hook.test.tsx": 85, "src/renderer/src/components/settings/settings-setup-guide-progress.test.ts": 408, "src/renderer/src/components/settings/shortcut-binding-list-mutations.test.ts": 46, "src/renderer/src/components/settings/shortcut-definition-catalog.test.ts": 222, @@ -8990,7 +8980,6 @@ "src/renderer/src/components/sidebar/AddWslHostFormPanel.test.tsx": 220, "src/renderer/src/components/sidebar/AgentDashboardSidebarHost.test.tsx": 1548, "src/renderer/src/components/sidebar/AutoRenameFailedDialog.test.tsx": 1246, - "src/renderer/src/components/sidebar/CacheTimer.test.tsx": 79, "src/renderer/src/components/sidebar/CommentMarkdown.code-block-renderer.test.tsx": 2013, "src/renderer/src/components/sidebar/CommentMarkdown.file-link-cases.test.tsx": 1688, "src/renderer/src/components/sidebar/CommentMarkdown.github-attachment-image.test.tsx": 1597, @@ -9025,7 +9014,6 @@ "src/renderer/src/components/sidebar/RemoveFolderDialog.test.tsx": 754, "src/renderer/src/components/sidebar/SetupGuideSidebarEntry.test.tsx": 433, "src/renderer/src/components/sidebar/SetupScriptPromptCard.test.ts": 73, - "src/renderer/src/components/sidebar/SetupScriptPromptCardShell.test.tsx": 375, "src/renderer/src/components/sidebar/SeveredTerminalDaemonBanner.test.tsx": 393, "src/renderer/src/components/sidebar/Sidebar.test.tsx": 597, "src/renderer/src/components/sidebar/SidebarAgentsList.test.tsx": 2110, @@ -9072,14 +9060,12 @@ "src/renderer/src/components/sidebar/WorktreeCardLinkedItemsSection.test.tsx": 3296, "src/renderer/src/components/sidebar/WorktreeCardMeta.interaction.test.tsx": 1658, "src/renderer/src/components/sidebar/WorktreeCardMeta.test.tsx": 2091, - "src/renderer/src/components/sidebar/WorktreeCardPorts.test.tsx": 970, "src/renderer/src/components/sidebar/WorktreeCardSshHostControl.test.tsx": 3049, "src/renderer/src/components/sidebar/WorktreeCardStatusSlot.test.tsx": 312, "src/renderer/src/components/sidebar/WorktreeContextMenu.copy-worktree-name.test.tsx": 2107, "src/renderer/src/components/sidebar/WorktreeContextMenu.delete-shortcut.test.tsx": 1570, "src/renderer/src/components/sidebar/WorktreeContextMenu.react185-bystander.test.tsx": 1475, "src/renderer/src/components/sidebar/WorktreeContextMenu.test.ts": 164, - "src/renderer/src/components/sidebar/WorktreeDeveloperMenu.test.tsx": 63, "src/renderer/src/components/sidebar/WorktreeDeveloperMenuReveal.test.tsx": 702, "src/renderer/src/components/sidebar/WorktreeList.card-memo-stability.test.tsx": 819, "src/renderer/src/components/sidebar/WorktreeList.empty-project-rows.test.ts": 3395, @@ -9096,7 +9082,6 @@ "src/renderer/src/components/sidebar/WorktreeReferenceStack.test.tsx": 354, "src/renderer/src/components/sidebar/WorktreeTitleInlineRename.begin-editing.test.tsx": 121, "src/renderer/src/components/sidebar/WorktreeTitleInlineRename.editor-lifecycle.test.tsx": 475, - "src/renderer/src/components/sidebar/WorktreeTitleInlineRename.test.tsx": 184, "src/renderer/src/components/sidebar/WorktreeVisibilityDialog.test.tsx": 2043, "src/renderer/src/components/sidebar/WorktreeVisibilityHelpPopover.test.tsx": 431, "src/renderer/src/components/sidebar/WorktreeVisibilitySourceList.test.tsx": 408, @@ -9138,7 +9123,6 @@ "src/renderer/src/components/sidebar/host-section-collapse.test.ts": 230, "src/renderer/src/components/sidebar/host-section-context-labels.test.ts": 75, "src/renderer/src/components/sidebar/host-section-folder-workspace-counts.test.ts": 37, - "src/renderer/src/components/sidebar/host-section-local-label.test.ts": 58, "src/renderer/src/components/sidebar/host-section-order.test.ts": 57, "src/renderer/src/components/sidebar/host-section-reveal.test.tsx": 1750, "src/renderer/src/components/sidebar/host-section-rows.test.ts": 66, @@ -9395,7 +9379,6 @@ "src/renderer/src/components/skills/skill-source-inventory.test.ts": 73, "src/renderer/src/components/skills/skill-update-run-store.test.ts": 137, "src/renderer/src/components/skills/skills-filter.test.ts": 130, - "src/renderer/src/components/source-control/SourceControlActionVariableChips.test.tsx": 191, "src/renderer/src/components/sparse/SparseCheckoutPresetDraftForm.test.tsx": 466, "src/renderer/src/components/sparse/SparsePresetChooser.test.tsx": 510, "src/renderer/src/components/sparse/sparse-directory-browse.test.ts": 45, @@ -10289,7 +10272,6 @@ "src/renderer/src/components/ui/popover-wheel-scroll.test.tsx": 409, "src/renderer/src/components/ui/repo-multi-combobox.test.ts": 187, "src/renderer/src/components/ui/scroll-area.test.tsx": 219, - "src/renderer/src/components/ui/security-surface.test.tsx": 357, "src/renderer/src/components/ui/slider.test.tsx": 282, "src/renderer/src/components/ui/switch.test.tsx": 68, "src/renderer/src/components/unexpected-signout/unexpected-signout-card.test.tsx": 1890, diff --git a/src/renderer/src/components/dashboard/DashboardAgentRowToolStep.test.tsx b/src/renderer/src/components/dashboard/DashboardAgentRowToolStep.test.tsx deleted file mode 100644 index 34ae171b62d..00000000000 --- a/src/renderer/src/components/dashboard/DashboardAgentRowToolStep.test.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { renderToStaticMarkup } from 'react-dom/server' -import type { ComponentProps } from 'react' -import { describe, expect, it } from 'vitest' -import { DashboardAgentRowToolStep } from './DashboardAgentRowToolStep' - -function renderStep(props: Partial>): string { - return renderToStaticMarkup( - - ) -} - -describe('DashboardAgentRowToolStep', () => { - it('shows a program message alone when no tool is named', () => { - const markup = renderStep({ toolInput: 'Pouring wget' }) - - expect(markup).toContain('data-agent-row-tool-input="true"') - expect(markup).toContain('Pouring wget') - expect(markup).not.toContain('lucide-wrench') - expect(markup).not.toContain('data-agent-row-tool-header') - expect(markup).not.toContain('data-agent-row-tool-placeholder') - }) - - it('keeps the placeholder when neither a tool nor input is known yet', () => { - const markup = renderStep({ reservesHeight: true }) - - expect(markup).toContain('data-agent-row-tool-placeholder="true"') - expect(markup).not.toContain('data-agent-row-tool-input') - }) - - it('renders nothing with no tool, no input and no reserved height', () => { - expect(renderStep({})).toBe('') - }) - - it('keeps the wrench header when a tool is named', () => { - const markup = renderStep({ toolName: 'bash', toolInput: 'ls' }) - - expect(markup).toContain('lucide-wrench') - expect(markup).not.toContain('data-agent-row-tool-input') - }) -}) diff --git a/src/renderer/src/components/linear-issue-attribute-filter-coverage-agreement.test.ts b/src/renderer/src/components/linear-issue-attribute-filter-coverage-agreement.test.ts deleted file mode 100644 index 9cd6acf9e58..00000000000 --- a/src/renderer/src/components/linear-issue-attribute-filter-coverage-agreement.test.ts +++ /dev/null @@ -1,39 +0,0 @@ -// The pill and inline coverage notice must use one predicate so their claims cannot diverge. -import { describe, expect, it } from 'vitest' -import { LinearFacetCoverageNotice } from './linear-issue-attribute-filter-coverage-notice' -import { isLinearMetadataGroupSelectionPartial } from './linear-issue-attribute-filter-team-ids' - -function randomGroups(rng: () => number): { key: string; ids: string[] }[] { - const count = 1 + Math.floor(rng() * 8) - let next = 0 - return Array.from({ length: count }, (_unused, group) => ({ - key: `g${group}`, - ids: Array.from({ length: 1 + Math.floor(rng() * 4) }, () => `id-${next++}`) - })) -} - -describe('coverage pill and notice agree', () => { - it('shows the notice exactly when the pill reads partial', () => { - let seed = 1337 - const rng = (): number => { - seed = (seed * 1103515245 + 12345) % 2147483648 - return seed / 2147483648 - } - - for (let run = 0; run < 20000; run += 1) { - const groups = randomGroups(rng) - const every = groups.flatMap((group) => group.ids) - const selected = every.filter(() => rng() < 0.5) - const truncated = rng() < 0.3 - const partial = isLinearMetadataGroupSelectionPartial(groups, selected, truncated) - const notice = LinearFacetCoverageNotice({ - facet: 'status', - options: groups, - selectedIds: selected, - max: 100, - truncated - }) - expect(notice === null).toBe(!partial) - } - }) -}) diff --git a/src/renderer/src/components/linear-project-presentation.test.ts b/src/renderer/src/components/linear-project-presentation.test.ts deleted file mode 100644 index 4dc6e3e3927..00000000000 --- a/src/renderer/src/components/linear-project-presentation.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { describe, expect, it } from 'vitest' - -import type { LinearProjectSummary } from '../../../shared/linear/project-types' -import { - linearProjectDateLabel, - linearProjectMetadataLabels, - linearProjectPriorityLabel, - linearProjectProgressPercent, - linearProjectUnknownText, - linearProjectWorkspaceLabel -} from './linear-project-presentation' - -function project(progress?: number | null): LinearProjectSummary { - return { id: 'project-1', name: 'Compiler', progress } -} - -describe('Linear project presentation', () => { - it('keeps optional mixed-version metadata permissive', () => { - expect(linearProjectUnknownText({ displayName: 'Ada' })).toBe('Ada') - expect(linearProjectUnknownText({ body: ' Shipped ' })).toBe('Shipped') - expect(linearProjectUnknownText({ unknown: 'value' })).toBeNull() - expect(linearProjectUnknownText(undefined)).toBeNull() - expect(linearProjectMetadataLabels([{ name: 'A' }, null, { title: 'B' }], 1)).toEqual(['A']) - }) - - it('preserves date, priority, progress, and workspace fallbacks', () => { - expect(linearProjectDateLabel(null)).toBe('None') - expect(linearProjectDateLabel('not-a-date')).toBe('not-a-date') - expect(linearProjectPriorityLabel(2, 'Urgent')).toBe('Urgent') - expect(linearProjectPriorityLabel(0, null)).toBe('None') - expect(linearProjectPriorityLabel(3, null)).toBe('P3') - expect(linearProjectProgressPercent(project(0.42))).toBe(42) - expect(linearProjectProgressPercent(project(42.4))).toBe(42) - expect(linearProjectProgressPercent(project(Number.NaN))).toBeNull() - expect(linearProjectWorkspaceLabel('all', 'Acme')).toBe('Acme') - expect(linearProjectWorkspaceLabel('workspace-1', 'Acme')).toBeNull() - }) -}) diff --git a/src/renderer/src/components/right-sidebar/SessionRowTrailingActions.test.tsx b/src/renderer/src/components/right-sidebar/SessionRowTrailingActions.test.tsx deleted file mode 100644 index bdef9eae1e7..00000000000 --- a/src/renderer/src/components/right-sidebar/SessionRowTrailingActions.test.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { renderToStaticMarkup } from 'react-dom/server' -import { describe, expect, it, vi } from 'vitest' -import { TooltipProvider } from '@/components/ui/tooltip' -import type { AiVaultSession } from '../../../../shared/ai-vault-types' -import { SessionRowTrailingActions } from './SessionRowTrailingActions' - -const session = { agent: 'claude' } as AiVaultSession - -function renderActions(onContinueInNewSession?: () => void): string { - return renderToStaticMarkup( - - - - ) -} - -describe('SessionRowTrailingActions', () => { - it('renders the new-session action as a hover-revealed control when available', () => { - const markup = renderActions(vi.fn()) - - expect(markup).toContain('data-testid="ai-vault-session-continue-in-new-session"') - expect(markup).toContain('aria-label="Hand Off to Another Agent"') - // Hidden hover actions must reject clicks as well as disappear. - const buttonMarkup = markup.split('data-testid="ai-vault-session-continue-in-new-session"')[0] - expect(buttonMarkup.slice(-600)).toContain('can-hover:pointer-events-none') - }) - - it('omits the new-session action when the session has no valid target', () => { - expect(renderActions()).not.toContain('data-testid="ai-vault-session-continue-in-new-session"') - }) -}) diff --git a/src/renderer/src/components/right-sidebar/fork-push-target-label.test.ts b/src/renderer/src/components/right-sidebar/fork-push-target-label.test.ts deleted file mode 100644 index acecb7a7edd..00000000000 --- a/src/renderer/src/components/right-sidebar/fork-push-target-label.test.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { describe, expect, it } from 'vitest' -import type { GitPushTarget } from '../../../../shared/worktree/types' -import { describeForkPushTarget } from './source-control/panel/fork-push-target-label' - -function target(overrides: Partial): GitPushTarget { - return { - remoteName: 'pr-contributor-orca', - branchName: 'contributor/fix', - ...overrides - } -} - -describe('describeForkPushTarget', () => { - it('derives owner:branch from an SSH fork URL', () => { - expect( - describeForkPushTarget(target({ remoteUrl: 'git@github.com:contributor/orca.git' })) - ).toBe('contributor:contributor/fix') - }) - - it('derives owner:branch from an HTTPS fork URL', () => { - expect( - describeForkPushTarget(target({ remoteUrl: 'https://github.com/contributor/orca.git' })) - ).toBe('contributor:contributor/fix') - }) - - it('handles a URL without a .git suffix', () => { - expect( - describeForkPushTarget(target({ remoteUrl: 'https://github.com/contributor/orca' })) - ).toBe('contributor:contributor/fix') - }) - - it('falls back to remoteName/branch when there is no remote URL', () => { - expect(describeForkPushTarget(target({ remoteUrl: undefined }))).toBe( - 'pr-contributor-orca/contributor/fix' - ) - }) - - it('works for non-GitHub hosts via the generic owner segment', () => { - expect( - describeForkPushTarget(target({ remoteUrl: 'git@gitlab.com:contributor/orca.git' })) - ).toBe('contributor:contributor/fix') - }) -}) diff --git a/src/renderer/src/components/right-sidebar/source-control-branch-section-heading.test.tsx b/src/renderer/src/components/right-sidebar/source-control-branch-section-heading.test.tsx deleted file mode 100644 index 76d879466fb..00000000000 --- a/src/renderer/src/components/right-sidebar/source-control-branch-section-heading.test.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import { tmpdir } from 'node:os' -import { join } from 'node:path' -import { renderToStaticMarkup } from 'react-dom/server' -import { describe, expect, it, vi } from 'vitest' -import { SourceControlBranchSection } from './source-control/listing/branch-section' -import type { GitBranchCompareSummary } from '../../../../shared/git-diff-compare-types' - -const readySummary: GitBranchCompareSummary = { - baseRef: 'refs/remotes/origin/main', - baseOid: 'base', - compareRef: 'feature', - headOid: 'head', - mergeBase: 'base', - changedFiles: 4, - commitsAhead: 5, - status: 'ready' -} - -function render( - summary: GitBranchCompareSummary, - fileCount = 4, - totalBranchEntryCount = fileCount -): string { - return renderToStaticMarkup( - ({ - path: `src/file-${index}.ts`, - status: 'modified' - }))} - totalBranchEntryCount={totalBranchEntryCount} - // Collapsed: the heading is the subject here, not the virtualized list. - collapsedSections={new Set(['branch'])} - toggleSection={vi.fn()} - sourceControlViewMode="list" - visibleBranchTreeRows={[]} - fileListScrollElement={null} - collapsedTreeDirs={new Set()} - toggleTreeDir={vi.fn()} - currentWorktreeId="wt-1" - worktreePath={join(tmpdir(), 'wt-1')} - revealInExplorer={vi.fn()} - activeConnectionId={null} - openCommittedDiff={vi.fn()} - openBranchAllDiffs={vi.fn()} - diffCommentCountByPath={new Map()} - /> - ) -} - -describe('SourceControlBranchSection heading', () => { - // Count current differences from the base, not every historically touched file on a rebased branch. - it('names the compare base on the count, leaving the heading text alone', () => { - const markup = render(readySummary) - - expect(markup).toContain('Committed on Branch') - expect(markup).toContain('title="4 files changed vs origin/main"') - // An aria-label inside the toggle would replace its accessible name; use the count title only. - expect(markup).not.toContain('aria-label="4 files changed vs origin/main"') - }) - - it('uses the singular count label for one file', () => { - expect(render(readySummary, 1)).toContain('title="1 file changed vs origin/main"') - }) - - // Filtered counts describe matches, not all files changed against the base. - it('leaves the count unlabelled while a filter narrows the list', () => { - expect(render(readySummary, 2, 4)).not.toContain('files changed vs') - }) - - it('leaves the count unlabelled when the summary has no base ref', () => { - const markup = render({ ...readySummary, baseRef: ' ' }) - - expect(markup).toContain('Committed on Branch') - expect(markup).not.toContain('files changed vs') - }) -}) diff --git a/src/renderer/src/components/right-sidebar/source-control-commit-message-rows.test.ts b/src/renderer/src/components/right-sidebar/source-control-commit-message-rows.test.ts index ee5f7816b7b..24acb3183ea 100644 --- a/src/renderer/src/components/right-sidebar/source-control-commit-message-rows.test.ts +++ b/src/renderer/src/components/right-sidebar/source-control-commit-message-rows.test.ts @@ -9,13 +9,6 @@ afterEach(() => { }) describe('source-control commit message row sizing', () => { - it('preserves the existing row clamp for ordinary commit messages', () => { - expect(getCommitMessageTextareaRows('')).toBe(2) - expect(getCommitMessageTextareaRows('feat: keep rows stable')).toBe(2) - expect(getCommitMessageTextareaRows('subject\n\nbody')).toBe(3) - expect(getCommitMessageTextareaRows('\n'.repeat(20))).toBe(12) - }) - it('clamps newline-heavy pasted commit messages without splitting the payload', () => { const split = vi.spyOn(String.prototype, 'split') const charCodeAt = vi.spyOn(String.prototype, 'charCodeAt') diff --git a/src/renderer/src/components/settings/RuntimeHostAccessForm.test.tsx b/src/renderer/src/components/settings/RuntimeHostAccessForm.test.tsx index 446ec56ade1..581e5c80287 100644 --- a/src/renderer/src/components/settings/RuntimeHostAccessForm.test.tsx +++ b/src/renderer/src/components/settings/RuntimeHostAccessForm.test.tsx @@ -60,25 +60,4 @@ describe('RuntimeHostAccessForm', () => { expect(markup).toContain('Enter an Orca access link or bare pairing code.') expect(markup).toContain('aria-invalid="true"') }) - - it('shows the persistence error after host verification succeeds', () => { - const markup = renderToStaticMarkup( - - ) - - expect(markup).toContain('Could not save the host') - expect(markup).toContain('A server named "Linux workstation" already exists.') - }) }) diff --git a/src/renderer/src/components/settings/RuntimePairingGeneratorForm.test.tsx b/src/renderer/src/components/settings/RuntimePairingGeneratorForm.test.tsx index 7515b204883..7265d614a34 100644 --- a/src/renderer/src/components/settings/RuntimePairingGeneratorForm.test.tsx +++ b/src/renderer/src/components/settings/RuntimePairingGeneratorForm.test.tsx @@ -39,12 +39,6 @@ function renderForm( } describe('RuntimePairingGeneratorForm', () => { - it('uses detected interfaces for another-device intent', () => { - const markup = renderForm('another', '100.76.32.125') - expect(markup).toContain('role="combobox"') - expect(markup).not.toContain('id="runtime-pairing-custom-address"') - }) - it('requires a dedicated value for custom-address intent', () => { const emptyMarkup = renderForm('custom', '') expect(emptyMarkup).toContain('id="runtime-pairing-custom-address"') diff --git a/src/renderer/src/components/settings/SparsePresetSettingsSection.test.tsx b/src/renderer/src/components/settings/SparsePresetSettingsSection.test.tsx index 8c4ba2e7c20..a9d51ea1920 100644 --- a/src/renderer/src/components/settings/SparsePresetSettingsSection.test.tsx +++ b/src/renderer/src/components/settings/SparsePresetSettingsSection.test.tsx @@ -1,6 +1,5 @@ // @vitest-environment happy-dom import { cleanup, fireEvent, render, screen } from '@testing-library/react' -import { renderToStaticMarkup } from 'react-dom/server' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import type { Repo } from '../../../../shared/repo-types' import type { SparsePreset } from '../../../../shared/worktree/create-types' @@ -35,17 +34,6 @@ describe('SparsePresetSettingsSection', () => { storeMock.state.removeSparsePreset.mockReset() }) - it('surfaces sparse preset load failures inline instead of showing an endless loader', () => { - storeMock.state.sparsePresetsLoadStatusByRepo = { 'repo-1': 'error' } - storeMock.state.sparsePresetsErrorByRepo = { 'repo-1': 'disk failed' } - - const markup = renderToStaticMarkup() - - expect(markup).toContain('role="alert"') - expect(markup).toContain('disk failed') - expect(markup).toContain('Sparse presets could not be loaded.') - }) - it('offers a load retry and prevents creating against an unknown preset list', () => { storeMock.state.sparsePresetsLoadStatusByRepo = { 'repo-1': 'error' } storeMock.state.sparsePresetsErrorByRepo = { 'repo-1': 'disk failed' } diff --git a/src/renderer/src/components/settings/UnsealedCredentialNotice.test.tsx b/src/renderer/src/components/settings/UnsealedCredentialNotice.test.tsx deleted file mode 100644 index 05ac894896a..00000000000 --- a/src/renderer/src/components/settings/UnsealedCredentialNotice.test.tsx +++ /dev/null @@ -1,41 +0,0 @@ -// @vitest-environment happy-dom - -import { cleanup, render, screen } from '@testing-library/react' -import { afterEach, describe, expect, it, vi } from 'vitest' - -vi.mock('@/i18n/i18n', () => ({ - translate: (_key: string, fallback: string, params?: Record) => - fallback.replace(/\{\{(\w+)\}\}/g, (_match, name: string) => params?.[name] ?? '') -})) - -const { UnsealedCredentialNotice } = await import('./UnsealedCredentialNotice') - -describe('UnsealedCredentialNotice', () => { - afterEach(cleanup) - - it('warns when the stored credential is plaintext', () => { - render() - const text = screen.getByRole('alert').textContent ?? '' - expect(text).toContain('MiniMax API key is stored unencrypted') - // The remedy has to be in the message; the console warning it replaces had none. - expect(text).toMatch(/gnome-keyring|kwallet/) - }) - - it('renders nothing when the credential is sealed', () => { - render() - expect(screen.queryByRole('alert')).toBeNull() - }) - - // Null means absent/unreadable storage, not evidence of plaintext exposure. - it('renders nothing when protection is unknown', () => { - render() - expect(screen.queryByRole('alert')).toBeNull() - }) - - it('names the credential so a pane with several is unambiguous', () => { - render( - - ) - expect(screen.getByRole('alert').textContent).toContain('MiniMax Session Cookie') - }) -}) diff --git a/src/renderer/src/components/settings/provider-rate-limit-scope-panels.test.tsx b/src/renderer/src/components/settings/provider-rate-limit-scope-panels.test.tsx deleted file mode 100644 index 06dde826e0e..00000000000 --- a/src/renderer/src/components/settings/provider-rate-limit-scope-panels.test.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { renderToStaticMarkup } from 'react-dom/server' -import { describe, expect, it, vi } from 'vitest' -import { getExecutionHostLabel } from '../../../../shared/execution-host' -import { GitHubRateLimitPanel } from '@/components/github/github-rate-limit-display' -import { GitLabRateLimitPanel } from '@/components/gitlab/gitlab-rate-limit-display' - -const LOCAL_HOST_LABEL = getExecutionHostLabel('local') - -type StoreState = { - settings: { activeRuntimeEnvironmentId: string | null } - openSettingsPage: () => void - openSettingsTarget: (target: { pane: string; repoId: string | null }) => void -} - -const mocks = vi.hoisted(() => ({ - store: { - current: { - settings: { activeRuntimeEnvironmentId: null }, - openSettingsPage: vi.fn(), - openSettingsTarget: vi.fn() - } as StoreState - } -})) - -vi.mock('@/store', () => ({ - useAppStore: (selector: (state: StoreState) => unknown) => selector(mocks.store.current) -})) - -describe('provider rate-limit panels account scope', () => { - it('shows the Settings host, not the focused server, for GitHub API budget', () => { - mocks.store.current = { - settings: { activeRuntimeEnvironmentId: 'runtime-focused' }, - openSettingsPage: vi.fn(), - openSettingsTarget: vi.fn() - } - - const markup = renderToStaticMarkup() - - expect(markup).toContain(`Budget scope: ${LOCAL_HOST_LABEL}`) - expect(markup).toContain( - 'GitHub API budget is fetched from the CLI on this desktop client. Choose a server in the Host menu at the top of Settings to see its budget.' - ) - expect(markup).toContain('Open Remote Servers') - }) - - it('shows the remote server scope for GitLab API budget', () => { - mocks.store.current = { - settings: { activeRuntimeEnvironmentId: null }, - openSettingsPage: vi.fn(), - openSettingsTarget: vi.fn() - } - - const markup = renderToStaticMarkup( - - ) - - expect(markup).toContain('Budget scope: Remote server: runtime-1') - expect(markup).toContain( - 'GitLab API budget is fetched from the CLI on this remote server. Choose another host in the Host menu at the top of Settings to see its budget.' - ) - }) -}) diff --git a/src/renderer/src/components/settings/session-history-status-copy.test.ts b/src/renderer/src/components/settings/session-history-status-copy.test.ts deleted file mode 100644 index e750f3eeaf3..00000000000 --- a/src/renderer/src/components/settings/session-history-status-copy.test.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { describe, expect, it } from 'vitest' -import type { AiVaultSearchStatus } from '../../../../shared/ai-vault-search-types' -import { unavailableSessionSearchStatus } from '../../../../shared/ai-vault-search-client' -import { isSessionSearchIndexReady } from './session-history-status-copy' - -function status(overrides: Partial): AiVaultSearchStatus { - return { ...unavailableSessionSearchStatus(), enabled: true, ...overrides } -} - -describe('isSessionSearchIndexReady', () => { - it('is ready once a whole sweep finished with nothing due', () => { - expect(isSessionSearchIndexReady(status({ phase: 'current', lastSweepCompletedAt: 1 }))).toBe( - true - ) - }) - - it('counts a finished sweep with unreadable files as ready', () => { - expect( - isSessionSearchIndexReady( - status({ phase: 'degraded', filesFailed: 2, filesDue: 0, lastSweepCompletedAt: 1 }) - ) - ).toBe(true) - }) - - it('is not ready before the first sweep finishes', () => { - expect(isSessionSearchIndexReady(null)).toBe(false) - expect(isSessionSearchIndexReady(status({ phase: 'idle' }))).toBe(false) - expect(isSessionSearchIndexReady(status({ phase: 'indexing', filesDue: 40 }))).toBe(false) - // A degraded pass that has not completed a sweep yet is still building. - expect(isSessionSearchIndexReady(status({ phase: 'degraded', filesFailed: 1 }))).toBe(false) - expect( - isSessionSearchIndexReady(status({ phase: 'degraded', filesDue: 3, lastSweepCompletedAt: 1 })) - ).toBe(false) - }) - - it('is not ready while search is off', () => { - expect( - isSessionSearchIndexReady( - status({ enabled: false, phase: 'current', lastSweepCompletedAt: 1 }) - ) - ).toBe(false) - }) -}) diff --git a/src/renderer/src/components/settings/settings-setup-guide-progress-hook.test.tsx b/src/renderer/src/components/settings/settings-setup-guide-progress-hook.test.tsx deleted file mode 100644 index 12060d19b35..00000000000 --- a/src/renderer/src/components/settings/settings-setup-guide-progress-hook.test.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import { renderToStaticMarkup } from 'react-dom/server' -import { beforeEach, describe, expect, it, vi } from 'vitest' -import type { FeatureWallSetupProgress } from '../feature-wall/feature-wall-setup-progress' -import { useSettingsSetupGuideProgress } from './settings-setup-guide-progress' - -const mocks = vi.hoisted(() => ({ - useSetupGuideProgress: vi.fn() -})) - -vi.mock('../setup-guide/use-setup-guide-progress', () => ({ - useSetupGuideProgress: mocks.useSetupGuideProgress -})) - -function makeProgress(): FeatureWallSetupProgress { - return { - ready: true, - stepDone: { - 'default-agent': true, - 'add-two-repos': false, - notifications: true, - 'two-worktrees': true, - browser: false, - 'task-sources': true, - 'agent-capabilities': false, - 'setup-script': false - }, - coreDoneCount: 4, - coreTotal: 8 - } -} - -function SettingsProgressProbe(): React.JSX.Element { - const progress = useSettingsSetupGuideProgress(true) - return {`${progress.doneCount}/${progress.total}`} -} - -describe('useSettingsSetupGuideProgress', () => { - beforeEach(() => { - mocks.useSetupGuideProgress.mockReset() - }) - - it('uses the same setup progress path as the main sidebar', () => { - mocks.useSetupGuideProgress.mockReturnValue(makeProgress()) - - expect(renderToStaticMarkup()).toContain('4/8') - expect(mocks.useSetupGuideProgress).toHaveBeenCalledWith(true, false, false) - }) - - it('uses legacy-aware completion returned by the shared setup progress path', () => { - mocks.useSetupGuideProgress.mockReturnValue({ - ...makeProgress(), - stepDone: { - 'default-agent': true, - 'add-two-repos': true, - notifications: true, - 'two-worktrees': true, - browser: true, - 'task-sources': true, - 'agent-capabilities': true, - 'setup-script': true - }, - coreDoneCount: 8 - }) - - expect(renderToStaticMarkup()).toContain('8/8') - }) - - it('shows browser incomplete after the browser migration has already run for fresh users', () => { - mocks.useSetupGuideProgress.mockReturnValue({ - ...makeProgress(), - stepDone: { - 'default-agent': true, - 'add-two-repos': true, - notifications: true, - 'two-worktrees': true, - browser: false, - 'task-sources': true, - 'agent-capabilities': true, - 'setup-script': true - }, - coreDoneCount: 7 - }) - - expect(renderToStaticMarkup()).toContain('7/8') - }) -}) diff --git a/src/renderer/src/components/sidebar/AddRepoCreateStep.test.tsx b/src/renderer/src/components/sidebar/AddRepoCreateStep.test.tsx index fc5719674b9..022d81aa07d 100644 --- a/src/renderer/src/components/sidebar/AddRepoCreateStep.test.tsx +++ b/src/renderer/src/components/sidebar/AddRepoCreateStep.test.tsx @@ -39,18 +39,6 @@ function renderCreateStep({ } describe('CreateStep', () => { - it('renders the name-first create UI with advanced controls collapsed', () => { - const html = renderCreateStep() - - expect(html).toContain('Create a new project') - expect(html).toContain('Name') - expect(html).toContain('Git repository in ~/orca/projects') - expect(html).toContain('aria-expanded="false"') - expect(html).not.toContain('Project kind') - expect(html).not.toContain('Location') - expect(html).not.toContain('aria-label="Browse host filesystem"') - }) - it('shows the Git-required explanation in the collapsed summary', () => { const html = renderCreateStep({ createName: 'demo-project', diff --git a/src/renderer/src/components/sidebar/AddWslHostFormPanel.test.tsx b/src/renderer/src/components/sidebar/AddWslHostFormPanel.test.tsx index e43f5aa8fbc..f9903dd9a8a 100644 --- a/src/renderer/src/components/sidebar/AddWslHostFormPanel.test.tsx +++ b/src/renderer/src/components/sidebar/AddWslHostFormPanel.test.tsx @@ -43,24 +43,6 @@ describe('AddWslHostFormPanel', () => { expect([ubuntu, debian, legacy, added].map(canAddWslDistro)).toEqual([true, true, false, false]) }) - it('makes starting a stopped distro an explicit action', () => { - const html = render({ selectedDistro: 'Debian' }) - expect(html).toContain('Debian is stopped. Adding it starts it.') - expect(html).toContain('Start and add') - expect(render({ selectedDistro: 'Ubuntu' })).toContain('Add host') - }) - - it('shows a refusal inline and the setup progress while adding', () => { - expect(render({ error: 'Already runs the Orca server for “dev”.' })).toContain('role="alert"') - const adding = render({ selectedDistro: 'Ubuntu', adding: true }) - expect(adding).toContain('Setting up…') - expect(adding).toContain('The first time can take a minute.') - }) - - it('explains an empty list', () => { - expect(render({ distros: [] })).toContain('wsl --install') - }) - it('says this build cannot run WSL hosts and offers nothing to add', () => { const html = render({ selectedDistro: 'Ubuntu', serverFilesMissing: true }) expect(html).toContain('This build of Orca can’t run WSL hosts (server files missing).') @@ -68,9 +50,4 @@ describe('AddWslHostFormPanel', () => { expect(html).not.toContain('wsl --install') expect(html).toMatch(/]*disabled=""[^>]*>Add host<\/button>/) }) - - it('offers an explicit refresh, and none when nothing could be added', () => { - expect(render()).toContain('Refresh') - expect(render({ serverFilesMissing: true })).not.toContain('Refresh') - }) }) diff --git a/src/renderer/src/components/sidebar/CacheTimer.test.tsx b/src/renderer/src/components/sidebar/CacheTimer.test.tsx deleted file mode 100644 index 651de8e0b82..00000000000 --- a/src/renderer/src/components/sidebar/CacheTimer.test.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import { renderToStaticMarkup } from 'react-dom/server' -import { beforeEach, describe, expect, it, vi } from 'vitest' -import { usePromptCacheCountdownStartedAt } from './CacheTimer' - -const scanMocks = vi.hoisted(() => ({ - getMostUrgentPromptCacheStartedAt: vi.fn(() => 1_000) -})) - -type MockState = { - cacheTimerByKey: Record - settings?: { - promptCacheTimerEnabled?: boolean - promptCacheTtlMs?: number - } - tabsByWorktree: Record -} - -let mockState: MockState - -vi.mock('@/store', () => ({ - useAppStore: (selector: (state: MockState) => unknown) => selector(mockState) -})) - -vi.mock('./prompt-cache-timer-selection', () => ({ - getMostUrgentPromptCacheStartedAt: scanMocks.getMostUrgentPromptCacheStartedAt, - getPromptCacheCountdownForPane: vi.fn(() => null) -})) - -function AggregateTimerProbe({ active = true }: { active?: boolean }): React.JSX.Element { - const startedAt = usePromptCacheCountdownStartedAt('wt-1', active) - return {startedAt ?? 'none'} -} - -describe('usePromptCacheCountdownStartedAt', () => { - beforeEach(() => { - scanMocks.getMostUrgentPromptCacheStartedAt.mockClear() - mockState = { - cacheTimerByKey: { 'tab-1:11111111-1111-4111-8111-111111111111': 1_000 }, - settings: { - promptCacheTimerEnabled: true, - promptCacheTtlMs: 60_000 - }, - tabsByWorktree: { - 'wt-1': [{ id: 'tab-1' }] - } - } - }) - - it('does not scan aggregate cache timers while inactive', () => { - const markup = renderToStaticMarkup() - - expect(markup).toContain('none') - expect(scanMocks.getMostUrgentPromptCacheStartedAt).not.toHaveBeenCalled() - }) - - it('does not scan aggregate cache timers while disabled', () => { - mockState.settings = { - promptCacheTimerEnabled: false, - promptCacheTtlMs: 60_000 - } - - const markup = renderToStaticMarkup() - - expect(markup).toContain('none') - expect(scanMocks.getMostUrgentPromptCacheStartedAt).not.toHaveBeenCalled() - }) - - it('does not scan aggregate cache timers when ttl is zero', () => { - mockState.settings = { - promptCacheTimerEnabled: true, - promptCacheTtlMs: 0 - } - - const markup = renderToStaticMarkup() - - expect(markup).toContain('none') - expect(scanMocks.getMostUrgentPromptCacheStartedAt).not.toHaveBeenCalled() - }) - - it('scans aggregate cache timers only when the timer can render', () => { - const markup = renderToStaticMarkup() - - expect(markup).toContain('1000') - expect(scanMocks.getMostUrgentPromptCacheStartedAt).toHaveBeenCalledTimes(1) - expect(scanMocks.getMostUrgentPromptCacheStartedAt).toHaveBeenCalledWith( - mockState.tabsByWorktree['wt-1'], - mockState.cacheTimerByKey - ) - }) -}) diff --git a/src/renderer/src/components/sidebar/SetupScriptPromptCardShell.test.tsx b/src/renderer/src/components/sidebar/SetupScriptPromptCardShell.test.tsx deleted file mode 100644 index 368ac6ddab2..00000000000 --- a/src/renderer/src/components/sidebar/SetupScriptPromptCardShell.test.tsx +++ /dev/null @@ -1,43 +0,0 @@ -// @vitest-environment happy-dom - -import { render } from '@testing-library/react' -import { describe, expect, it, vi } from 'vitest' -import { TooltipProvider } from '@/components/ui/tooltip' -import { SetupScriptPromptCardShell } from './SetupScriptPromptCardShell' - -describe('SetupScriptPromptCardShell', () => { - it('floats above its anchor without reserving a sidebar background panel', () => { - const view = render( - - - - ) - const layer = view.container.querySelector('[data-setup-script-prompt-layer]') - const surface = layer?.firstElementChild - - expect(layer?.classList.contains('absolute')).toBe(true) - expect(layer?.classList.contains('inset-x-0')).toBe(true) - expect(layer?.classList.contains('bottom-full')).toBe(true) - expect(layer?.classList.contains('pointer-events-none')).toBe(true) - expect(surface?.classList.contains('pointer-events-auto')).toBe(true) - expect(surface?.classList.contains('bg-popover')).toBe(true) - expect(surface?.classList.contains('text-popover-foreground')).toBe(true) - }) -}) diff --git a/src/renderer/src/components/sidebar/WorktreeCardPorts.test.tsx b/src/renderer/src/components/sidebar/WorktreeCardPorts.test.tsx deleted file mode 100644 index db23d18dbc2..00000000000 --- a/src/renderer/src/components/sidebar/WorktreeCardPorts.test.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { renderToStaticMarkup } from 'react-dom/server' -import type { ReactNode } from 'react' -import { describe, expect, it, vi } from 'vitest' -import type { WorkspacePort } from '../../../../shared/workspace-ports' - -vi.mock('@/store', () => ({ - useAppStore: (selector: (state: unknown) => unknown) => - selector({ - createBrowserTab: vi.fn(), - setRemoteBrowserPageHandle: vi.fn(), - replaceWorkspacePortScans: vi.fn(), - setWorkspacePortScanRefreshing: vi.fn(), - settings: null - }) -})) - -vi.mock('@/components/ui/tooltip', () => ({ - Tooltip: ({ children }: { children: ReactNode }) => <>{children}, - TooltipContent: ({ children }: { children: ReactNode }) => <>{children}, - TooltipTrigger: ({ children }: { children: ReactNode }) => <>{children} -})) - -const port: WorkspacePort = { - id: '127.0.0.1:58941:1234', - bindHost: '127.0.0.1', - connectHost: '127.0.0.1', - port: 58941, - pid: 1234, - processName: 'node', - protocol: 'http', - kind: 'workspace', - owner: { - worktreeId: 'repo::/workspace/app', - repoId: 'repo', - displayName: 'app', - path: '/workspace/app', - confidence: 'cwd' - }, - advertisedUrl: 'http://dev.preview.localhost:58941' -} - -describe('WorktreeCardPortsDetails', () => { - it('shows advertised port addresses in workspace hover details', async () => { - const { WorktreeCardPortsDetails } = await import('./WorktreeCardPorts') - - const markup = renderToStaticMarkup() - - expect(markup).toContain('dev.preview.localhost:58941') - expect(markup).toContain('aria-label="Copy dev.preview.localhost:58941"') - expect(markup).toContain('Open in Browser. Shift+Ctrl+click for system browser') - expect(markup).toContain( - '
void -} - -const menuItems = vi.hoisted(() => ({ values: [] as MenuItemProps[] })) -const requestManualPark = vi.hoisted(() => vi.fn()) - -vi.mock('@/components/ui/dropdown-menu', async () => { - const React_ = await import('react') - const passthrough = ({ children }: { children?: ReactNode }) => - React_.createElement(React_.Fragment, null, children) - return { - DropdownMenuSub: passthrough, - DropdownMenuSubContent: passthrough, - DropdownMenuSubTrigger: passthrough, - DropdownMenuItem: (props: MenuItemProps) => { - menuItems.values.push(props) - return React_.createElement(React_.Fragment, null, props.children) - } - } -}) - -vi.mock('@/i18n/i18n', () => ({ translate: (_key: string, fallback: string) => fallback })) -vi.mock('@/lib/manual-terminal-worktree-parking', () => ({ - requestManualTerminalWorktreePark: requestManualPark -})) - -describe('WorktreeDeveloperMenu', () => { - beforeEach(() => { - menuItems.values = [] - requestManualPark.mockReset() - }) - - it('shows the developer submenu with the parking action', () => { - const markup = renderToStaticMarkup( - - ) - - expect(markup).toContain('Developer') - expect(markup).toContain('Park terminal') - expect(markup).toContain('lucide-code-xml') - expect(markup).toContain('lucide-square-parking') - }) - - it('requests parking for the context worktree', () => { - renderToStaticMarkup() - - menuItems.values[0]?.onSelect?.() - - expect(requestManualPark).toHaveBeenCalledWith('worktree-1') - }) -}) diff --git a/src/renderer/src/components/sidebar/WorktreeReferenceStack.test.tsx b/src/renderer/src/components/sidebar/WorktreeReferenceStack.test.tsx index 3a3c83b9744..c48ee3795e7 100644 --- a/src/renderer/src/components/sidebar/WorktreeReferenceStack.test.tsx +++ b/src/renderer/src/components/sidebar/WorktreeReferenceStack.test.tsx @@ -2,34 +2,11 @@ import { cleanup, render } from '@testing-library/react' import { afterEach, describe, expect, it } from 'vitest' import { WorktreeReferenceStack } from './WorktreeReferenceStack' -import { WorktreeCardMetaBadges } from './WorktreeCardMetaBadges' import { getWorkspaceAttachmentKey } from '../../../../shared/workspace-attachment-normalization' import { referenceAttachment, referenceReview } from './workspace-reference-fixtures.test-support' -import type { WorkspaceAttachment } from '../../../../shared/worktree/types' afterEach(cleanup) describe('fixed workspace reference stacks', () => { - it('preserves singleton brand/status icons without ghosts or a count', () => { - const linear: WorkspaceAttachment = { - provider: 'linear', - type: 'issue', - number: 0, - identifier: 'ENG-1' - } - const { container } = render( - <> - - - - ) - expect(container.querySelectorAll('[data-reference-front]')).toHaveLength(2) - expect(container.querySelectorAll('svg')).toHaveLength(2) - expect(container.querySelector('[data-reference-count]')).toBeNull() - expect(container.querySelector('.workspace-reference-stack-ghost')).toBeNull() - expect(container.textContent).toContain('Linked PR #7') - expect(container.textContent).toContain('Linked Linear ENG-1') - expect(container.textContent).not.toContain('1 linked') - }) it('prioritizes the review needing attention over a legacy primary and preserves sibling states', () => { const items = [referenceAttachment(1), referenceAttachment(2), referenceAttachment(3)] const details = Object.fromEntries( @@ -53,34 +30,4 @@ describe('fixed workspace reference stacks', () => { expect(container.querySelector('[data-reference-count]')?.textContent).toBe('3') expect(container.textContent).toContain('PR #2 · draft') }) - it('keeps two provider cells with the same six-pixel gap and no new metadata row at twelve references', () => { - const refs: WorkspaceAttachment[] = [ - ...Array.from({ length: 12 }, (_, index) => referenceAttachment(index + 1)), - ...Array.from({ length: 12 }, (_, index) => ({ - provider: 'linear' as const, - type: 'issue' as const, - number: 0, - identifier: `ENG-${index + 1}` - })) - ] - const { container } = render( - - ) - const group = container.querySelector('[data-workspace-reference-groups]') - expect(group?.classList.contains('gap-1.5')).toBe(true) - expect(group?.querySelectorAll('[data-workspace-reference-stack]')).toHaveLength(2) - expect( - Array.from(group?.querySelectorAll('[data-reference-count]') ?? []).map( - (node) => node.textContent - ) - ).toEqual(['12', '12']) - expect(container.textContent).not.toContain('24 linked') - }) }) diff --git a/src/renderer/src/components/sidebar/WorktreeTitleInlineRename.test.tsx b/src/renderer/src/components/sidebar/WorktreeTitleInlineRename.test.tsx deleted file mode 100644 index db35c8c2d35..00000000000 --- a/src/renderer/src/components/sidebar/WorktreeTitleInlineRename.test.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import { renderToStaticMarkup } from 'react-dom/server' -import { describe, expect, it, vi } from 'vitest' -import { TooltipProvider } from '@/components/ui/tooltip' -import { - WorktreeTitleInlineRename, - getWorktreeTitleRenameCommit, - isWorktreeTitleTruncated -} from './WorktreeTitleInlineRename' - -describe('getWorktreeTitleRenameCommit', () => { - it('cancels blank or unchanged inline titles', () => { - expect(getWorktreeTitleRenameCommit('feature/login', '')).toEqual({ kind: 'cancel' }) - expect(getWorktreeTitleRenameCommit('feature/login', ' ')).toEqual({ kind: 'cancel' }) - expect(getWorktreeTitleRenameCommit('feature/login', ' feature/login ')).toEqual({ - kind: 'cancel' - }) - }) - - it('trims and saves changed inline titles', () => { - expect(getWorktreeTitleRenameCommit('feature/login', ' Login polish ')).toEqual({ - kind: 'save', - displayName: 'Login polish' - }) - }) -}) - -describe('WorktreeTitleInlineRename', () => { - it('treats only actual text overflow as truncation', () => { - expect(isWorktreeTitleTruncated({ clientWidth: 120, scrollWidth: 120 })).toBe(false) - expect(isWorktreeTitleTruncated({ clientWidth: 120, scrollWidth: 119 })).toBe(false) - expect(isWorktreeTitleTruncated({ clientWidth: 120, scrollWidth: 121 })).toBe(true) - }) - - it('renders the title as the double-click inline rename target', () => { - const markup = renderToStaticMarkup( - - - - ) - - expect(markup).toContain('data-worktree-title-inline-rename=""') - expect(markup).not.toContain('cursor-text') - expect(markup).not.toContain('title="Feature workspace"') - expect(markup).toContain('tabindex="0"') - expect(markup).toContain('font-semibold text-foreground') - expect(markup).toContain('Unread:') - expect(markup).toContain('Feature workspace') - }) - - it('keeps read titles at the default foreground color unless requested', () => { - const markup = renderToStaticMarkup( - - - - ) - - expect(markup).toContain('font-normal text-foreground') - expect(markup).not.toContain('text-foreground/80') - expect(markup).not.toContain('Unread:') - }) - - it('dims read titles when requested by the experimental card style', () => { - const markup = renderToStaticMarkup( - - - - ) - - expect(markup).toContain('font-normal text-foreground/80') - expect(markup).not.toContain('Unread:') - }) -}) diff --git a/src/renderer/src/components/sidebar/host-section-local-label.test.ts b/src/renderer/src/components/sidebar/host-section-local-label.test.ts deleted file mode 100644 index 5434124ff50..00000000000 --- a/src/renderer/src/components/sidebar/host-section-local-label.test.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { describe, expect, it } from 'vitest' -import type { Repo } from '../../../../shared/repo-types' -import type { Row } from '../../../../shared/sidebar-list/row-types' -import { addHostSectionRows } from '../../../../shared/sidebar-list/host-section-rows' - -function repoHeader(id: string, connectionId?: string): Row { - const repo: Repo = { - id, - path: `/${id}`, - displayName: id, - badgeColor: '', - addedAt: 0, - connectionId - } - return { - type: 'header', - key: `repo:${id}`, - label: id, - count: 1, - icon: { kind: 'project' }, - repo - } -} - -describe('addHostSectionRows local host label', () => { - // The client’s platform must not determine the listed host’s label. - it('names an unlisted local host with the label it is given', () => { - const sectioned = addHostSectionRows({ - rows: [repoHeader('local'), repoHeader('ssh', 'ssh-1')], - hostOptions: [ - { id: 'ssh:ssh-1', kind: 'ssh', label: 'Builder', detail: 'SSH', health: 'available' }, - { id: 'ssh:ssh-2', kind: 'ssh', label: 'Other', detail: 'SSH', health: 'available' } - ], - workspaceHostScope: 'all', - visibleWorkspaceHostIds: ['local', 'ssh:ssh-1'], - defaultHostId: 'local', - localHostLabel: 'Local Windows' - }) - expect( - sectioned.find((row) => row.type === 'host-header' && row.hostId === 'local') - ).toMatchObject({ label: 'Local Windows', detail: 'This computer' }) - }) -}) diff --git a/src/renderer/src/components/sidebar/remote-file-browser-highlight.test.ts b/src/renderer/src/components/sidebar/remote-file-browser-highlight.test.ts index 7890eff6e47..bbd572035c3 100644 --- a/src/renderer/src/components/sidebar/remote-file-browser-highlight.test.ts +++ b/src/renderer/src/components/sidebar/remote-file-browser-highlight.test.ts @@ -8,10 +8,6 @@ const folder = (name: string) => ({ name, isDirectory: true }) const file = (name: string) => ({ name, isDirectory: false }) describe('initialRemoteFileBrowserHighlight', () => { - it('starts on the first row', () => { - expect(initialRemoteFileBrowserHighlight([folder('a'), folder('b')], false)).toBe(0) - }) - // Why: Enter on a filter that leaves one folder has always opened it, even past files. it('starts on the only folder a filter leaves', () => { expect(initialRemoteFileBrowserHighlight([file('src.md'), folder('src')], false)).toBe(1) @@ -24,12 +20,6 @@ describe('initialRemoteFileBrowserHighlight', () => { }) describe('stepRemoteFileBrowserHighlight', () => { - it('moves within the list and stops at both ends', () => { - expect(stepRemoteFileBrowserHighlight(0, 1, 3)).toBe(1) - expect(stepRemoteFileBrowserHighlight(2, 1, 3)).toBe(2) - expect(stepRemoteFileBrowserHighlight(0, -1, 3)).toBe(0) - }) - it('enters the list from no row at the end the key points to', () => { expect(stepRemoteFileBrowserHighlight(-1, 1, 3)).toBe(0) expect(stepRemoteFileBrowserHighlight(-1, -1, 3)).toBe(2) diff --git a/src/renderer/src/components/sidebar/worktree-list/rows/FolderPathStatusIndicator.test.tsx b/src/renderer/src/components/sidebar/worktree-list/rows/FolderPathStatusIndicator.test.tsx index ad43e5fda61..5e56fab20d9 100644 --- a/src/renderer/src/components/sidebar/worktree-list/rows/FolderPathStatusIndicator.test.tsx +++ b/src/renderer/src/components/sidebar/worktree-list/rows/FolderPathStatusIndicator.test.tsx @@ -22,13 +22,6 @@ function renderIndicator(reason: unknown): string { } describe('FolderPathStatusIndicator', () => { - it('marks a folder missing for a declared reason', () => { - const markup = renderIndicator('missing') - - expect(markup).toContain('aria-label="Folder not found"') - expect(markup).toContain('text-destructive') - }) - // Keep an imprecise marker for unknown host reasons so broken folders cannot appear healthy. it('still renders the marker when a newer host sends an undeclared reason', () => { const markup = renderIndicator('permission-denied') @@ -51,14 +44,4 @@ describe('FolderPathStatusIndicator', () => { // Why: hasOwn coerces its key, so ['missing'] must not be read as the confirmed-stale 'missing'. expect(markup).not.toContain('aria-label="Folder not found"') }) - - it('renders nothing for a healthy folder', () => { - const markup = renderToStaticMarkup( - - - - ) - - expect(markup).toBe('') - }) }) diff --git a/src/renderer/src/components/source-control/SourceControlActionVariableChips.test.tsx b/src/renderer/src/components/source-control/SourceControlActionVariableChips.test.tsx deleted file mode 100644 index eba8da8993c..00000000000 --- a/src/renderer/src/components/source-control/SourceControlActionVariableChips.test.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import type { ReactNode } from 'react' -import { renderToStaticMarkup } from 'react-dom/server' -import { describe, expect, it, vi } from 'vitest' -import { SourceControlActionVariableChips } from './SourceControlActionVariableChips' - -vi.mock('../ui/hover-card', () => ({ - HoverCard: ({ children }: { children: ReactNode }) => ( -
{children}
- ), - HoverCardContent: ({ children, className }: { children: ReactNode; className?: string }) => ( -
- {children} -
- ), - HoverCardTrigger: ({ children }: { children: ReactNode }) => ( -
{children}
- ) -})) - -describe('SourceControlActionVariableChips', () => { - it('renders variable details in a scrollable hover card', () => { - const markup = renderToStaticMarkup( - {}} - /> - ) - - expect(markup).toContain('data-slot="hover-card-content"') - expect(markup).toContain('scrollbar-sleek') - expect(markup).toContain('overflow-y-auto') - expect(markup).toContain('Generate a commit message.') - }) - - // Keep the linkedIssue warning beside previews: unlinked workspaces produce Fixes #, and GitLab has no value. - it.each([ - { label: 'a linked workspace', preview: '4242', expected: '4242' }, - { label: 'an unlinked workspace', preview: '', expected: '(empty)' } - ])( - 'keeps the linkedIssue description and example alongside $label preview', - ({ preview, expected }) => { - const markup = renderToStaticMarkup( - {}} - /> - ) - - expect(markup).toContain('Empty when no GitHub issue is linked') - expect(markup).toContain('GitLab-linked') - expect(markup).toContain('Example') - expect(markup).toContain('This workspace') - expect(markup).toContain(expected) - } - ) - - it('shows only the rendered prompt for a basePrompt preview', () => { - const markup = renderToStaticMarkup( - {}} - /> - ) - - expect(markup).not.toContain('built-in prompt for this action') - }) -}) diff --git a/src/renderer/src/components/ui/security-surface.test.tsx b/src/renderer/src/components/ui/security-surface.test.tsx deleted file mode 100644 index 9a6e8acc8ee..00000000000 --- a/src/renderer/src/components/ui/security-surface.test.tsx +++ /dev/null @@ -1,61 +0,0 @@ -// @vitest-environment happy-dom -import { readFileSync } from 'node:fs' -import { join } from 'node:path' -import { cleanup, render } from '@testing-library/react' -import { afterEach, describe, expect, it } from 'vitest' -import { Dialog, DialogContent, DialogDescription, DialogTitle } from './dialog' -import { SecuritySurface } from './security-surface' - -// Every dialog that asks the user to trust, approve, run or remove code. A new one belongs here. -const TRUST_DIALOGS = [ - 'sidebar/OrcaYamlTrustDialog.tsx', - 'editor/IpynbViewer.tsx', - 'settings/PluginConsentDialog.tsx', - 'settings/PluginInstallDialog.tsx', - 'settings/PluginMarketplacePreviewDialog.tsx', - 'settings/PluginMarketplaceSourceDialog.tsx', - 'settings/PluginRemoveDialog.tsx', - 'settings/PluginRollbackDialog.tsx' -] - -// Trust decisions made outside a dialog: agent command approvals and certificate overrides. -const INLINE_TRUST_SURFACES = [ - 'native-chat/NativeChatApprovalCard.tsx', - 'browser-pane/navigate/browser-load-failure-overlay.tsx' -] - -afterEach(cleanup) - -describe('security surfaces', () => { - it('applies the host-owned token set', () => { - render( - - - Trust - Review - - - ) - expect(document.querySelector('[role="dialog"]')?.classList).toContain('plugin-security-chrome') - }) - - it.each(TRUST_DIALOGS)('%s renders on the security surface', (file) => { - const source = readFileSync(join(import.meta.dirname, '..', file), 'utf8') - expect(source).toMatch(/]*surface="security"/) - expect(source).not.toContain('plugin-security-chrome') - }) - - it('applies the host-owned token set outside dialogs', () => { - const { container } = render( - -
Allow
-
- ) - expect(container.firstElementChild?.classList).toContain('plugin-security-chrome') - }) - - it.each(INLINE_TRUST_SURFACES)('%s renders inside a security surface', (file) => { - const source = readFileSync(join(import.meta.dirname, '..', file), 'utf8') - expect(source).toMatch(/]/) - }) -})