test: remove 68 low-value cases and 18 suites (#34112)

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
OrcaWin
2026-10-26 15:43:25 -07:00
committed by GitHub
co-authored by OrcaWin
parent d7a8bb9240
commit b8c596ea88
28 changed files with 0 additions and 1203 deletions
-18
View File
@@ -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,
@@ -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<ComponentProps<typeof DashboardAgentRowToolStep>>): string {
return renderToStaticMarkup(
<DashboardAgentRowToolStep
expanded={false}
showsTool
reservesHeight={false}
toolName=""
toolInput=""
{...props}
/>
)
}
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')
})
})
@@ -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)
}
})
})
@@ -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()
})
})
@@ -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(
<TooltipProvider>
<SessionRowTrailingActions
session={session}
detailsExpanded={false}
detailsId="session-details"
detailsTooltip="Show Details"
resumeDisabled={false}
resumeLabel="Resume in New Tab"
worktreeInfo={null}
onToggleDetails={vi.fn()}
showJumpToWorktree={false}
onResume={vi.fn()}
onContinueInNewSession={onContinueInNewSession}
onCopyId={vi.fn()}
onCopyPath={vi.fn()}
deleteBlockedReason={null}
onRequestDelete={vi.fn()}
/>
</TooltipProvider>
)
}
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"')
})
})
@@ -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>): 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')
})
})
@@ -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(
<SourceControlBranchSection
branchSummary={summary}
filteredBranchEntries={Array.from({ length: fileCount }, (_, index) => ({
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')
})
})
@@ -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')
@@ -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(
<RuntimeHostAccessForm
name="Linux workstation"
accessLink={accessLink('ws://100.76.32.125:6768')}
busy={false}
failure={{
kind: 'environment-save-failed',
message: 'A server named "Linux workstation" already exists.'
}}
onNameChange={vi.fn()}
onAccessLinkChange={vi.fn()}
onCancel={vi.fn()}
onSubmit={vi.fn()}
/>
)
expect(markup).toContain('Could not save the host')
expect(markup).toContain('A server named &quot;Linux workstation&quot; already exists.')
})
})
@@ -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"')
@@ -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(<SparsePresetSettingsSection repoId="repo-1" />)
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' }
@@ -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<string, string>) =>
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(<UnsealedCredentialNotice protection="plaintext" credentialName="MiniMax API key" />)
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(<UnsealedCredentialNotice protection="sealed" credentialName="MiniMax API key" />)
expect(screen.queryByRole('alert')).toBeNull()
})
// Null means absent/unreadable storage, not evidence of plaintext exposure.
it('renders nothing when protection is unknown', () => {
render(<UnsealedCredentialNotice protection={null} credentialName="MiniMax API key" />)
expect(screen.queryByRole('alert')).toBeNull()
})
it('names the credential so a pane with several is unambiguous', () => {
render(
<UnsealedCredentialNotice protection="plaintext" credentialName="MiniMax Session Cookie" />
)
expect(screen.getByRole('alert').textContent).toContain('MiniMax Session Cookie')
})
})
@@ -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(<GitHubRateLimitPanel target={{ kind: 'local' }} />)
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(
<GitLabRateLimitPanel target={{ kind: 'environment', environmentId: 'runtime-1' }} />
)
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.'
)
})
})
@@ -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>): 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)
})
})
@@ -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 <span>{`${progress.doneCount}/${progress.total}`}</span>
}
describe('useSettingsSetupGuideProgress', () => {
beforeEach(() => {
mocks.useSetupGuideProgress.mockReset()
})
it('uses the same setup progress path as the main sidebar', () => {
mocks.useSetupGuideProgress.mockReturnValue(makeProgress())
expect(renderToStaticMarkup(<SettingsProgressProbe />)).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(<SettingsProgressProbe />)).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(<SettingsProgressProbe />)).toContain('7/8')
})
})
@@ -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</span>')
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',
@@ -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(/<button[^>]*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')
})
})
@@ -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<string, number | null>
settings?: {
promptCacheTimerEnabled?: boolean
promptCacheTtlMs?: number
}
tabsByWorktree: Record<string, { id: string }[]>
}
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 <span>{startedAt ?? 'none'}</span>
}
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(<AggregateTimerProbe active={false} />)
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(<AggregateTimerProbe />)
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(<AggregateTimerProbe />)
expect(markup).toContain('none')
expect(scanMocks.getMostUrgentPromptCacheStartedAt).not.toHaveBeenCalled()
})
it('scans aggregate cache timers only when the timer can render', () => {
const markup = renderToStaticMarkup(<AggregateTimerProbe />)
expect(markup).toContain('1000')
expect(scanMocks.getMostUrgentPromptCacheStartedAt).toHaveBeenCalledTimes(1)
expect(scanMocks.getMostUrgentPromptCacheStartedAt).toHaveBeenCalledWith(
mockState.tabsByWorktree['wt-1'],
mockState.cacheTimerByKey
)
})
})
@@ -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(
<TooltipProvider>
<SetupScriptPromptCardShell
repoBadgeColor="blue"
repoDisplayName="orca"
isInspectionError={false}
sharedSetupIgnored={false}
isPackageManagerSuggestion={false}
hasCandidate={false}
candidateSource={null}
candidateProvenance={null}
detectedSetupDraft=""
isImporting={false}
renderedStateOk
onDismiss={vi.fn()}
onRetryInspection={vi.fn()}
onConfigure={vi.fn()}
onImport={vi.fn()}
onSetupDraftChange={vi.fn()}
/>
</TooltipProvider>
)
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)
})
})
@@ -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(<WorktreeCardPortsDetails ports={[port]} />)
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(
'<section class="space-y-1.5"><div class="flex items-center gap-1.5 px-1'
)
expect(markup).toContain('class="border-l border-border/70 pl-3 space-y-0.5"')
})
})
@@ -1,58 +0,0 @@
import type { ReactNode } from 'react'
import { renderToStaticMarkup } from 'react-dom/server'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { WorktreeDeveloperMenu } from './WorktreeDeveloperMenu'
type MenuItemProps = {
children?: ReactNode
onSelect?: () => 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(
<WorktreeDeveloperMenu worktreeId="worktree-1" disabled={false} />
)
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(<WorktreeDeveloperMenu worktreeId="worktree-1" disabled={false} />)
menuItems.values[0]?.onSelect?.()
expect(requestManualPark).toHaveBeenCalledWith('worktree-1')
})
})
@@ -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(
<>
<WorktreeReferenceStack items={[referenceAttachment()]} />
<WorktreeReferenceStack items={[linear]} />
</>
)
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(
<WorktreeCardMetaBadges
linkedItemCount={refs.length}
referenceItems={refs}
issue={null}
linearIssue={null}
review={null}
comment={null}
/>
)
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')
})
})
@@ -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(
<TooltipProvider>
<WorktreeTitleInlineRename
displayName="Feature workspace"
showUnreadEmphasis
onRename={vi.fn()}
/>
</TooltipProvider>
)
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(
<TooltipProvider>
<WorktreeTitleInlineRename displayName="Feature workspace" onRename={vi.fn()} />
</TooltipProvider>
)
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(
<TooltipProvider>
<WorktreeTitleInlineRename
displayName="Feature workspace"
dimReadTitle
onRename={vi.fn()}
/>
</TooltipProvider>
)
expect(markup).toContain('font-normal text-foreground/80')
expect(markup).not.toContain('Unread:')
})
})
@@ -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' })
})
})
@@ -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)
@@ -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(
<TooltipProvider>
<FolderPathStatusIndicator status={{ path: '/srv/scans', exists: true }} />
</TooltipProvider>
)
expect(markup).toBe('')
})
})
@@ -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 }) => (
<div data-slot="hover-card">{children}</div>
),
HoverCardContent: ({ children, className }: { children: ReactNode; className?: string }) => (
<div data-slot="hover-card-content" className={className}>
{children}
</div>
),
HoverCardTrigger: ({ children }: { children: ReactNode }) => (
<div data-slot="hover-card-trigger">{children}</div>
)
}))
describe('SourceControlActionVariableChips', () => {
it('renders variable details in a scrollable hover card', () => {
const markup = renderToStaticMarkup(
<SourceControlActionVariableChips
actionId="commitMessage"
variablePreviews={{ basePrompt: 'Generate a commit message.\n\nInclude staged changes.' }}
onInsert={() => {}}
/>
)
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(
<SourceControlActionVariableChips
actionId="commitMessage"
variablePreviews={{ linkedIssue: preview }}
onInsert={() => {}}
/>
)
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(
<SourceControlActionVariableChips
actionId="commitMessage"
variablePreviews={{ basePrompt: 'Generate a commit message.' }}
onInsert={() => {}}
/>
)
expect(markup).not.toContain('built-in prompt for this action')
})
})
@@ -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(
<Dialog open>
<DialogContent surface="security">
<DialogTitle>Trust</DialogTitle>
<DialogDescription>Review</DialogDescription>
</DialogContent>
</Dialog>
)
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(/<DialogContent\s[^>]*surface="security"/)
expect(source).not.toContain('plugin-security-chrome')
})
it('applies the host-owned token set outside dialogs', () => {
const { container } = render(
<SecuritySurface>
<div>Allow</div>
</SecuritySurface>
)
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(/<SecuritySurface[\s>]/)
})
})