test: remove 49 low-value cases and four suites (#34407)

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
OrcaWin
2026-10-27 05:25:04 -07:00
committed by GitHub
co-authored by OrcaWin
parent 44f4b1d255
commit e11b8ac37e
11 changed files with 2 additions and 1230 deletions
-1
View File
@@ -1120,7 +1120,6 @@
1 test-no-wall-clock:src/renderer/src/components/sidebar/SidebarToolbar.test.tsx
5 test-no-wall-clock:src/renderer/src/components/sidebar/use-workspace-reference-details.test.tsx
3 test-no-wall-clock:src/renderer/src/components/sidebar/worktree-agent-rows-subscriptions.bench.test.tsx
1 test-no-wall-clock:src/renderer/src/components/sidebar/worktree-card-agent-summary.test.ts
4 test-no-wall-clock:src/renderer/src/components/sidebar/WorktreeCard.compact-hover.test.tsx
13 test-no-wall-clock:src/renderer/src/components/sidebar/WorktreeCard.pr-display.test.tsx
2 test-no-wall-clock:src/renderer/src/components/sidebar/WorktreeCard.single-reference-details.test.tsx
-4
View File
@@ -7718,7 +7718,6 @@
"src/renderer/src/components/editor/rich-markdown-terminal-path-paste.test.ts": 146,
"src/renderer/src/components/editor/rich-markdown-toc-heading-target.test.ts": 130,
"src/renderer/src/components/editor/rich-markdown-trailing-paragraph.test.ts": 2127,
"src/renderer/src/components/editor/selection-copy.test.ts": 27,
"src/renderer/src/components/editor/setup-contextual-copy.test.ts": 116,
"src/renderer/src/components/editor/untitled-file-rename-path.test.ts": 31,
"src/renderer/src/components/editor/use-editor-content-change-handler.test.tsx": 1528,
@@ -9137,7 +9136,6 @@
"src/renderer/src/components/sidebar/worktree-lineage-projection.test.ts": 58,
"src/renderer/src/components/sidebar/worktree-lineage-toggle-handler-cache.test.ts": 32,
"src/renderer/src/components/sidebar/worktree-list-groups-host-collision.test.ts": 205,
"src/renderer/src/components/sidebar/worktree-list-groups-host-labels.test.ts": 204,
"src/renderer/src/components/sidebar/worktree-list-groups-host-name-collisions.test.ts": 194,
"src/renderer/src/components/sidebar/worktree-list-groups-imported-worktrees.test.ts": 185,
"src/renderer/src/components/sidebar/worktree-list-groups-lineage-nesting.test.ts": 234,
@@ -9502,7 +9500,6 @@
"src/renderer/src/components/task-page/task-page-default-repo-selection.test.ts": 42,
"src/renderer/src/components/task-page/task-page-initial-selection-scaling.test.tsx": 1703,
"src/renderer/src/components/task-page/task-page-pagination-page-numbers.test.ts": 54,
"src/renderer/src/components/task-source-context-summary.test.ts": 138,
"src/renderer/src/components/task-source-provider-availability.test.ts": 43,
"src/renderer/src/components/terminal-agent-tab-shortcut.test.ts": 6185,
"src/renderer/src/components/terminal-pane/CloseTerminalDialog.test.tsx": 460,
@@ -10016,7 +10013,6 @@
"src/renderer/src/components/terminal-pane/terminal-retention-churn-pins.test.tsx": 522,
"src/renderer/src/components/terminal-pane/terminal-retention-exempt-growth.test.ts": 361,
"src/renderer/src/components/terminal-pane/terminal-runtime-host-link-routing.test.ts": 1940,
"src/renderer/src/components/terminal-pane/terminal-selection-copy.test.ts": 1225,
"src/renderer/src/components/terminal-pane/terminal-selection-fit-guard.test.ts": 31,
"src/renderer/src/components/terminal-pane/terminal-shortcut-ctrl-arrow.test.ts": 54,
"src/renderer/src/components/terminal-pane/terminal-shortcut-forward-delete.test.ts": 82,
@@ -1,112 +0,0 @@
import { describe, expect, it } from 'vitest'
import {
canCopySelectionWithContext,
formatCopiedSelectionWithContext,
getContextualCopyLineRange
} from './selection-copy'
describe('contextual copy eligibility', () => {
it('matches formatting eligibility for line boundaries and whitespace-only selections', () => {
for (const startLineNumber of [1, 2, 44]) {
for (const endLineNumber of [1, 2, 3, 44, 45]) {
for (const endColumn of [1, 2, 15]) {
const selection = { startLineNumber, startColumn: 1, endLineNumber, endColumn }
const expected =
endLineNumber !== startLineNumber &&
(endColumn === 1 ? endLineNumber - 1 : endLineNumber) >= startLineNumber
expect(canCopySelectionWithContext(selection)).toBe(expected)
expect(
formatCopiedSelectionWithContext({
relativePath: '',
language: 'plaintext',
selection,
selectedText: ' \n'
}) !== null
).toBe(expected)
}
}
}
})
})
describe('formatCopiedSelectionWithContext', () => {
it('formats multi-line selections with file and line context', () => {
expect(
formatCopiedSelectionWithContext({
relativePath: 'src/main/git/status.ts',
language: 'typescript',
selection: {
startLineNumber: 44,
startColumn: 1,
endLineNumber: 47,
endColumn: 9
},
selectedText: "if (line.startsWith('1 ')) {\n const parts = line.split(' ')\n}"
})
).toBe(
[
'File: src/main/git/status.ts',
'Lines: 44-47',
'',
'```ts',
"if (line.startsWith('1 ')) {\n const parts = line.split(' ')\n}",
'```'
].join('\n')
)
})
it('treats column-1 end positions as the previous line for full-line selections', () => {
const selection = {
startLineNumber: 44,
startColumn: 1,
endLineNumber: 48,
endColumn: 1
}
expect(getContextualCopyLineRange(selection)).toEqual({
startLine: 44,
endLine: 47
})
expect(
formatCopiedSelectionWithContext({
relativePath: 'src/main/git/status.ts',
language: 'typescript',
selection,
selectedText: 'line 44\nline 45\nline 46\nline 47\n'
})
).toContain('Lines: 44-47')
})
it('keeps full-line single-line selections copyable', () => {
expect(
formatCopiedSelectionWithContext({
relativePath: 'src/main/git/status.ts',
language: 'typescript',
selection: {
startLineNumber: 44,
startColumn: 1,
endLineNumber: 45,
endColumn: 1
},
selectedText: 'line 44\n'
})
).toContain('Line: 44')
})
it('leaves single-line selections alone', () => {
expect(
formatCopiedSelectionWithContext({
relativePath: 'src/main/git/status.ts',
language: 'typescript',
selection: {
startLineNumber: 44,
startColumn: 4,
endLineNumber: 44,
endColumn: 15
},
selectedText: 'line.startsWith'
})
).toBeNull()
})
})
@@ -10,43 +10,6 @@ afterEach(() => {
})
describe('commit failure summary', () => {
it('collapses lint-staged, husky, and oxlint failures to a lint summary', () => {
const raw = [
'npm warn Unknown env config "python". This will stop working.',
'husky - pre-commit hook exited with code 1',
'lint-staged failed',
'oxlint found 3 errors'
].join('\n')
expect(summarizeCommitFailure(raw)).toBe('Lint failed during commit.')
})
it('collapses pre-commit hook failures without lint output to a hook summary', () => {
expect(summarizeCommitFailure('pre-commit hook failed: secret scan blocked commit')).toBe(
'Pre-commit hook failed.'
)
})
it('does not treat generic non-lint error counts as lint failures', () => {
expect(summarizeCommitFailure('tsc --noEmit\nFound 5 errors in 3 files.')).toBe('tsc --noEmit')
expect(summarizeCommitFailure('pre-commit hook failed\ntsc found 5 errors')).toBe(
'Pre-commit hook failed.'
)
})
it('falls back to the first meaningful line for generic failures', () => {
expect(summarizeCommitFailure('\n fatal: unable to auto-detect email address\nmore')).toBe(
'fatal: unable to auto-detect email address'
)
})
it('strips ANSI/control sequences and handles empty input', () => {
expect(summarizeCommitFailure('\u001b[31meslint found 2 errors\u001b[0m')).toBe(
'Lint failed during commit.'
)
expect(summarizeCommitFailure(' \n\t ')).toBe('Commit failed.')
})
it('summarizes newline-heavy commit output without splitting the full log', () => {
const split = vi.spyOn(String.prototype, 'split')
const raw = `${'npm notice noisy line\n'.repeat(1000)}husky - pre-commit hook\noxlint failed`
@@ -63,27 +26,4 @@ describe('commit failure summary', () => {
expect(hasExpandedCommitFailureDetails(raw, 'Commit failed.')).toBe(true)
expect(split).not.toHaveBeenCalled()
})
it('reports whether expanded details add information beyond the summary', () => {
expect(hasExpandedCommitFailureDetails('nothing to commit', 'nothing to commit')).toBe(false)
expect(
hasExpandedCommitFailureDetails(
'husky - pre-commit hook\neslint found 2 errors\nfull output',
'Lint failed during commit.'
)
).toBe(true)
expect(hasExpandedCommitFailureDetails('', 'Commit failed.')).toBe(false)
})
it('compares expanded details without whitespace regex replacement', () => {
const replace = vi.spyOn(String.prototype, 'replace')
const raw = ['nothing', String.fromCharCode(160), ' to\tcommit\n'].join('')
expect(hasExpandedCommitFailureDetails(raw, 'nothing to commit')).toBe(false)
expect(
replace.mock.calls.filter(
([pattern]) => pattern instanceof RegExp && pattern.source === '\\s+'
)
).toHaveLength(0)
})
})
@@ -1,14 +1,6 @@
import { createElement } from 'react'
import { renderToStaticMarkup } from 'react-dom/server'
import { describe, expect, it, vi } from 'vitest'
import { TooltipProvider } from '@/components/ui/tooltip'
import { describe, expect, it } from 'vitest'
import type { DashboardAgentRow as DashboardAgentRowData } from '@/components/dashboard/useDashboardData'
import { CompactAgentRow, getCompactAgentSecondary } from './worktree-card-compact-agent-row'
import {
buildSummaryAgentGroups,
getAgentDotState,
summarizeAgents
} from './worktree-card-agent-summary'
import { getAgentDotState } from './worktree-card-agent-summary'
import { buildSubagentChildRows } from './worktree-subagent-child-rows'
function monitoringAgent(): DashboardAgentRowData {
@@ -39,176 +31,7 @@ function monitoringAgent(): DashboardAgentRowData {
}
}
function orderedTitles(markup: string): string[] {
return [...markup.matchAll(/\stitle="([^"]*)"/g)].map((match) => match[1])
}
function renderCompactAgentRow(props: React.ComponentProps<typeof CompactAgentRow>): string {
return renderToStaticMarkup(
createElement(TooltipProvider, null, createElement(CompactAgentRow, props))
)
}
describe('worktree card agent summary', () => {
it("counts a running child by its own row's state", () => {
const running: DashboardAgentRowData = {
...monitoringAgent(),
paneKey: 'tab-1:leaf-1\u0000subagent:child',
rowSource: 'subagent',
state: 'working',
childRow: {
id: 'child',
kind: 'agent',
displayState: 'waiting',
name: 'Fuzz the tokenizer',
detail: null,
firstObservedAt: 1,
recencyAt: 1,
canStop: false,
settled: false,
owned: []
}
}
expect(getAgentDotState(running)).toBe('waiting')
expect(buildSummaryAgentGroups([running]).map((group) => group.state)).toEqual(['waiting'])
})
it('presents passive working as monitoring', () => {
const agent = monitoringAgent()
expect(getAgentDotState(agent)).toBe('monitoring')
expect(getCompactAgentSecondary(agent, Date.now())).toBe('Monitoring background tasks')
expect(summarizeAgents([agent], 'Agent')).toBe('Agent monitoring')
})
it('keeps monitoring visible before a compact row prompt', () => {
const agent = monitoringAgent()
agent.entry.prompt = 'Run background checks'
const markup = renderCompactAgentRow({ agent, now: 2000, onActivate: vi.fn() })
expect(markup).toContain('title="Monitoring background tasks - Run background checks"')
expect(markup).toMatch(
/Monitoring background tasks<\/span><span[^>]*> - Run background checks<\/span>/
)
})
it('hands the whole row to the send-target reason, and only then', () => {
const agent = monitoringAgent()
agent.entry.prompt = 'Run background checks'
const disabled = renderCompactAgentRow({
agent,
now: 2000,
onActivate: vi.fn(),
sendTargetStatus: 'disabled',
sendTargetDisabledReason: 'Agent needs permission'
})
// The dot sits inside the row, so its own state title would shadow the reason on hover.
expect(orderedTitles(disabled)).toEqual(['Agent needs permission', 'Claude'])
const eligible = renderCompactAgentRow({ agent, now: 2000, onActivate: vi.fn() })
expect(orderedTitles(eligible)).toEqual([
'Claude',
'Monitoring background tasks - Run background checks'
])
expect(eligible).toContain('data-slot="tooltip-trigger"')
})
it("lists a crash-cut turn as failed and a user's Stop as interrupted, before clean completions", () => {
const done = monitoringAgent()
done.state = 'done'
done.entry.state = 'done'
done.entry.workingMode = undefined
const interrupted = {
...done,
paneKey: 'tab-1:leaf-2',
entry: {
...done.entry,
paneKey: 'tab-1:leaf-2',
mainAgent: { state: 'done' as const, outcome: 'interruption' as const, stateStartedAt: 1 }
}
}
const stopped = {
...done,
paneKey: 'tab-1:leaf-3',
entry: { ...done.entry, paneKey: 'tab-1:leaf-3', interrupted: true }
}
expect(getAgentDotState(interrupted)).toBe('failed')
expect(getCompactAgentSecondary(interrupted, 0)).toBe('Failed')
expect(getAgentDotState(stopped)).toBe('interrupted')
expect(getCompactAgentSecondary(stopped, 0)).toBe('Interrupted by user')
const replaced = {
...done,
entry: {
...done.entry,
mainAgent: { state: 'done' as const, outcome: 'superseded' as const, stateStartedAt: 1 }
}
}
expect(getAgentDotState(replaced)).toBe('interrupted')
expect(getCompactAgentSecondary(replaced, 0)).toBe('Interrupted')
expect(summarizeAgents([done, interrupted, stopped], 'Agents')).toBe(
'Agents: 1 failed, 1 interrupted, 1 done'
)
})
it("lists a failed turn as failed, not done, ahead of a user's Stop", () => {
const done = monitoringAgent()
done.state = 'done'
done.entry.state = 'done'
done.entry.workingMode = undefined
const failed = {
...done,
paneKey: 'tab-1:leaf-3',
entry: {
...done.entry,
paneKey: 'tab-1:leaf-3',
mainAgent: { state: 'done' as const, outcome: 'failure' as const, stateStartedAt: 1 }
}
}
const interrupted = {
...done,
paneKey: 'tab-1:leaf-2',
entry: {
...done.entry,
paneKey: 'tab-1:leaf-2',
mainAgent: { state: 'done' as const, outcome: 'cancellation' as const, stateStartedAt: 1 }
}
}
expect(getAgentDotState(failed)).toBe('failed')
expect(getCompactAgentSecondary(failed, 0)).toBe('Failed')
expect(getCompactAgentSecondary(interrupted, 0)).toBe('Interrupted by user')
expect(summarizeAgents([done, interrupted, failed], 'Agents')).toBe(
'Agents: 1 failed, 1 interrupted, 1 done'
)
})
it('reads a main agent that failed while its subagent works as failed, ranked above working', () => {
const held = (outcome: 'failure' | 'success' | 'cancellation'): DashboardAgentRowData => {
const agent = monitoringAgent()
agent.entry.workingMode = undefined
agent.entry.mainAgent = { state: 'done', outcome, stateStartedAt: 1 }
return agent
}
const working = monitoringAgent()
working.paneKey = 'tab-1:leaf-2'
working.entry = { ...working.entry, paneKey: 'tab-1:leaf-2', workingMode: undefined }
expect(getAgentDotState(held('failure'))).toBe('failed')
expect(getCompactAgentSecondary(held('failure'), 0)).toBe('Failed')
expect(summarizeAgents([working, held('failure')], 'Agents')).toBe(
'Agents: 1 failed, 1 working'
)
// Only a failure outranks live work; a success or a stop with a live subagent reads working.
expect(getAgentDotState(held('success'))).toBe('working')
expect(getAgentDotState(held('cancellation'))).toBe('working')
expect(getCompactAgentSecondary(held('cancellation'), 0)).not.toBe('Interrupted by user')
})
it('keeps a subagent row on its own state while its failed main agent reads failed', () => {
const parent = monitoringAgent()
parent.entry = {
@@ -1,342 +0,0 @@
import { describe, expect, it } from 'vitest'
import { buildRows } from './worktree-list/grouping/build-rows.test-fixture'
import { getGroupKeyForWorktree } from '../../../../shared/sidebar-list/worktree-group-keys'
import {
LOCAL_HOST_LABEL,
repo,
worktree,
remoteRepo,
remoteWorktree,
project,
projectHostSetups
} from './worktree-list-groups-test-fixtures'
import type { ProjectHostSetup } from '../../../../shared/project-types'
import type { Repo } from '../../../../shared/repo-types'
import type { Worktree } from '../../../../shared/worktree/types'
describe('buildRows with pinned worktrees', () => {
it('groups Windows host and WSL setups on the same runtime host', () => {
const runtimeHostId = 'runtime:g16'
const windowsRepo: Repo = {
...repo,
id: 'repo-windows',
path: String.raw`C:\Users\alice\git\orca`,
displayName: 'orca',
executionHostId: runtimeHostId
}
const wslRepo: Repo = {
...repo,
id: 'repo-wsl',
path: String.raw`\\wsl.localhost\Ubuntu\home\alice\git\orca`,
displayName: 'orca',
executionHostId: runtimeHostId
}
const windowsWorktree: Worktree = {
...worktree,
id: 'wt-windows',
repoId: windowsRepo.id,
path: String.raw`C:\Users\alice\git\orca\feature`
}
const wslWorktree: Worktree = {
...worktree,
id: 'wt-wsl',
repoId: wslRepo.id,
path: String.raw`\\wsl.localhost\Ubuntu\home\alice\git\orca\feature`
}
const windowsSetup: ProjectHostSetup = {
...projectHostSetups[0]!,
id: windowsRepo.id,
hostId: runtimeHostId,
repoId: windowsRepo.id,
path: windowsRepo.path,
displayName: windowsRepo.displayName
}
const wslSetup: ProjectHostSetup = {
...windowsSetup,
id: wslRepo.id,
repoId: wslRepo.id,
path: wslRepo.path
}
const rows = buildRows(
'repo',
[windowsWorktree, wslWorktree],
new Map([
[windowsRepo.id, windowsRepo],
[wslRepo.id, wslRepo]
]),
null,
new Set(),
undefined,
undefined,
undefined,
{},
new Map([
[windowsWorktree.id, windowsWorktree],
[wslWorktree.id, wslWorktree]
]),
false,
undefined,
[],
new Set(),
new Map(),
new Map(),
[],
{
projects: [{ ...project, sourceRepoIds: [windowsRepo.id, wslRepo.id] }],
projectHostSetups: [windowsSetup, wslSetup]
}
)
expect(rows.filter((row) => row.type === 'header')).toMatchObject([
{
key: 'project:github:stablyai/orca',
label: 'Orca',
count: 2
}
])
})
it('uses saved host labels for mixed-host sidebar card badges', () => {
const runtimeRepo: Repo = {
...remoteRepo,
id: 'repo-runtime',
path: '/Users/alice/runtime-orca',
connectionId: null,
executionHostId: 'runtime:03ef704c-b180-4b10-998d-e28fbd5de9a3'
}
const runtimeWorktree: Worktree = {
...remoteWorktree,
id: 'wt-runtime',
repoId: runtimeRepo.id
}
const runtimeSetup: ProjectHostSetup = {
...projectHostSetups[1]!,
id: runtimeRepo.id,
hostId: 'runtime:03ef704c-b180-4b10-998d-e28fbd5de9a3',
repoId: runtimeRepo.id,
path: runtimeRepo.path
}
const rows = buildRows(
'repo',
[worktree, runtimeWorktree],
new Map([
[repo.id, repo],
[runtimeRepo.id, runtimeRepo]
]),
null,
new Set(),
undefined,
undefined,
undefined,
{},
new Map([
[worktree.id, worktree],
[runtimeWorktree.id, runtimeWorktree]
]),
false,
undefined,
[],
new Set(),
new Map(),
new Map(),
[],
{ projects: [project], projectHostSetups: [projectHostSetups[0]!, runtimeSetup] },
[],
new Map([
['local', LOCAL_HOST_LABEL],
['runtime:03ef704c-b180-4b10-998d-e28fbd5de9a3', 'dev box']
])
)
expect(rows).toMatchObject([
{ type: 'header', key: 'project:github:stablyai/orca', label: 'Orca', count: 2 },
{ type: 'item', worktree: { id: worktree.id }, hostContextLabel: LOCAL_HOST_LABEL },
{ type: 'item', worktree: { id: runtimeWorktree.id }, hostContextLabel: 'dev box' }
])
})
it('uses the registered SSH target label for openclaw rows', () => {
const sshRepo: Repo = {
...remoteRepo,
id: 'repo-openclaw',
connectionId: 'openclaw',
executionHostId: 'ssh:openclaw'
}
const sshWorktree: Worktree = {
...remoteWorktree,
id: 'wt-openclaw',
repoId: sshRepo.id
}
const rows = buildRows(
'workspace-status',
[worktree, sshWorktree],
new Map([
[repo.id, repo],
[sshRepo.id, sshRepo]
]),
null,
new Set(),
undefined,
undefined,
undefined,
{},
new Map([
[worktree.id, worktree],
[sshWorktree.id, sshWorktree]
]),
false,
undefined,
[],
new Set(),
new Map(),
new Map(),
[],
undefined,
[],
new Map([['ssh:openclaw', 'openclaw']])
)
expect(rows.filter((row) => row.type === 'item')).toMatchObject([
{ worktree: { id: worktree.id }, hostContextLabel: LOCAL_HOST_LABEL },
{ worktree: { id: sshWorktree.id }, hostContextLabel: 'openclaw' }
])
})
it('shows distinct Orca server names when status grouping mixes runtime hosts', () => {
const firstRepo: Repo = {
...repo,
id: 'repo-runtime-a',
executionHostId: 'runtime:env-a'
}
const secondRepo: Repo = {
...repo,
id: 'repo-runtime-b',
executionHostId: 'runtime:env-b'
}
const firstWorktree: Worktree = {
...worktree,
id: 'wt-runtime-a',
repoId: firstRepo.id
}
const secondWorktree: Worktree = {
...worktree,
id: 'wt-runtime-b',
repoId: secondRepo.id
}
const rows = buildRows(
'workspace-status',
[firstWorktree, secondWorktree],
new Map([
[firstRepo.id, firstRepo],
[secondRepo.id, secondRepo]
]),
null,
new Set(),
undefined,
undefined,
undefined,
{},
new Map([
[firstWorktree.id, firstWorktree],
[secondWorktree.id, secondWorktree]
]),
false,
undefined,
[],
new Set(),
new Map(),
new Map(),
[],
undefined,
[],
new Map([
['runtime:env-a', 'Remote Mac'],
['runtime:env-b', 'Build Linux']
])
)
expect(rows.filter((row) => row.type === 'item')).toMatchObject([
{ worktree: { id: firstWorktree.id }, hostContextLabel: 'Remote Mac' },
{ worktree: { id: secondWorktree.id }, hostContextLabel: 'Build Linux' }
])
})
it('omits host context labels when a project group only has one host', () => {
const secondLocalWorktree: Worktree = {
...worktree,
id: 'wt-local-2',
displayName: 'local-only'
}
const rows = buildRows(
'repo',
[worktree, secondLocalWorktree],
new Map([[repo.id, repo]]),
null,
new Set(),
undefined,
undefined,
undefined,
{},
new Map([
[worktree.id, worktree],
[secondLocalWorktree.id, secondLocalWorktree]
]),
false,
undefined,
[],
new Set(),
new Map(),
new Map(),
[],
{
projects: [{ ...project, sourceRepoIds: [repo.id] }],
projectHostSetups: [projectHostSetups[0]]
}
)
expect(rows).toMatchObject([
{ type: 'header', key: 'project:github:stablyai/orca', label: 'Orca', count: 2 },
{ type: 'item', worktree: { id: worktree.id } },
{ type: 'item', worktree: { id: secondLocalWorktree.id } }
])
for (const row of rows) {
if (row.type === 'item') {
expect(row.hostContextLabel).toBeUndefined()
}
}
})
it('keeps same-named repos separate without project setup identity', () => {
const rows = buildRows(
'repo',
[worktree, remoteWorktree],
new Map([
[repo.id, { ...repo, displayName: 'orca' }],
[remoteRepo.id, { ...remoteRepo, displayName: 'orca' }]
]),
null,
new Set()
)
expect(rows.filter((row) => row.type === 'header')).toMatchObject([
{ key: 'repo:repo-1' },
{ key: 'repo:repo-remote' }
])
})
it('returns project group keys for worktree reveal when project setup identity exists', () => {
expect(
getGroupKeyForWorktree(
'repo',
remoteWorktree,
new Map([[remoteRepo.id, remoteRepo]]),
null,
undefined,
{
projects: [project],
projectHostSetups
}
)
).toBe('project:github:stablyai/orca')
})
})
@@ -1,8 +1,6 @@
/** Disambiguate discovery notices by distinct host IDs from the unfiltered project universe, even when host labels match. */
import { describe, expect, it } from 'vitest'
import { buildRows } from './worktree-list/grouping/build-rows.test-fixture'
import { getNoticeHostContextLabels } from '../../../../shared/sidebar-list/host-labels'
import { buildProjectGroupingIndex } from '../../../../shared/project-section-grouping'
import { repo, worktree, project, projectHostSetups } from './worktree-list-groups-test-fixtures'
import type { ExecutionHostId } from '../../../../shared/execution-host'
import type { ProjectHostSetup } from '../../../../shared/project-types'
@@ -148,12 +146,6 @@ describe('discovery notice rows on a multi-host project', () => {
])
})
it('labels a lone eligible row on a project that spans hosts', () => {
expect(summarize(noticeRows({ eligibleRepoIds: [envTwin.id] }))).toEqual([
{ repoId: envTwin.id, label: 'openclaw', hostId: ENV_HOST_ID, count: 134 }
])
})
it('keeps each row its own label and count under either host filter', () => {
expect(summarize(noticeRows({ eligibleRepoIds: [sshTwin.id] }))).toEqual([
{ repoId: sshTwin.id, label: 'openclaw', hostId: SSH_HOST_ID, count: 61 }
@@ -163,22 +155,6 @@ describe('discovery notice rows on a multi-host project', () => {
])
})
it('returns labels for exactly the eligible records, never the filtered-out ones', () => {
// Assert exact map membership because rows cannot reveal extra ineligible entries.
const index = buildProjectGroupingIndex(TWIN_GROUPING)
for (const eligible of [[sshTwin.id], [envTwin.id], [sshTwin.id, envTwin.id]]) {
const labels = getNoticeHostContextLabels(
eligible,
TWIN_REPO_MAP.keys(),
TWIN_REPO_MAP,
index,
HOST_LABELS
)
expect([...(labels?.keys() ?? [])]).toEqual(eligible)
}
})
it('leaves a single-host project unlabelled even with several records on it', () => {
const secondLocal: Repo = { ...repo, id: 'repo-local-2', path: '/tmp/orca-second' }
const rows = noticeRows({
@@ -76,30 +76,6 @@ describe('pinned rows on a multi-host sidebar', () => {
}
)
it('labels pinned rows when the only other host is itself pinned', () => {
// Natural rows alone contain one host; both pinned hosts must still establish a mixed sidebar.
const pinnedLocal: Worktree = { ...worktree, isPinned: true }
const pinnedRemote: Worktree = { ...remoteWorktree, isPinned: true }
const localOnly: Worktree = { ...worktree, id: 'wt-local-2', displayName: 'local-2' }
const rows = itemRows(buildPinnedRows([pinnedLocal, pinnedRemote, localOnly]))
expect(rows).toEqual([
{ id: pinnedLocal.id, sectionKey: 'pinned', hostContextLabel: LOCAL_HOST_LABEL },
{ id: pinnedRemote.id, sectionKey: 'pinned', hostContextLabel: 'gpu-vm' },
{ id: localOnly.id, sectionKey: 'repo:repo-1', hostContextLabel: LOCAL_HOST_LABEL }
])
})
it('labels both copies when pinned worktrees also show in their groups', () => {
const pinnedRemote: Worktree = { ...remoteWorktree, isPinned: true }
const rows = itemRows(buildPinnedRows([worktree, pinnedRemote], 'repo', true))
expect(rows.filter((row) => row.id === pinnedRemote.id)).toEqual([
{ id: pinnedRemote.id, sectionKey: 'pinned', hostContextLabel: 'gpu-vm' },
{ id: pinnedRemote.id, sectionKey: 'repo:repo-remote', hostContextLabel: 'gpu-vm' }
])
})
it('draws no badge on a single-host sidebar even with a pinned row', () => {
const pinnedLocal: Worktree = { ...worktree, isPinned: true }
const localOnly: Worktree = { ...worktree, id: 'wt-local-2', displayName: 'local-2' }
@@ -1,374 +0,0 @@
import { describe, expect, it } from 'vitest'
import {
getTaskSourceAvailabilityNotice,
getTaskSourceContextSummary
} from './task-source-context-summary'
import { getExecutionHostLabel } from '../../../shared/execution-host'
const LOCAL_HOST_LABEL = getExecutionHostLabel('local')
describe('task source context summary', () => {
it('shows provider, host, and provider identity for a single repo-backed source', () => {
const summary = getTaskSourceContextSummary({
provider: 'github',
providerLabel: 'GitHub',
selectedRepoCount: 1,
repoContexts: [
{
kind: 'task-source',
provider: 'github',
projectId: 'github:stablyai/orca',
hostId: 'ssh:devbox',
projectHostSetupId: 'setup-1',
repoId: 'repo-1',
providerIdentity: { provider: 'github', owner: 'stablyai', repo: 'orca' }
}
]
})
expect(summary.label).toBe('GitHub · devbox · stablyai/orca')
expect(summary.title).toBe('GitHub · Host: devbox · Source: stablyai/orca')
})
it('shows repo-backed provider account labels when accounts can differ by host', () => {
const summary = getTaskSourceContextSummary({
provider: 'github',
providerLabel: 'GitHub',
selectedRepoCount: 2,
repoContexts: [
{
kind: 'task-source',
provider: 'github',
projectId: 'github:stablyai/orca',
hostId: 'local',
projectHostSetupId: 'setup-local',
repoId: 'repo-local',
providerIdentity: { provider: 'github', owner: 'stablyai', repo: 'orca' },
accountLabel: 'personal-gh'
},
{
kind: 'task-source',
provider: 'github',
projectId: 'github:stablyai/orca',
hostId: 'ssh:builder',
projectHostSetupId: 'setup-builder',
repoId: 'repo-builder',
providerIdentity: { provider: 'github', owner: 'stablyai', repo: 'orca' },
accountLabel: 'work-gh'
}
]
})
expect(summary.label).toBe(`GitHub · ${LOCAL_HOST_LABEL}, builder · personal-gh, work-gh`)
expect(summary.title).toBe(
`GitHub · Host: ${LOCAL_HOST_LABEL}, builder · Account: personal-gh, work-gh · Source: stablyai/orca · 2 selected projects`
)
})
it('shows disconnected source-host availability for a single SSH repo source', () => {
const summary = getTaskSourceContextSummary({
provider: 'github',
providerLabel: 'GitHub',
selectedRepoCount: 1,
repoContexts: [
{
kind: 'task-source',
provider: 'github',
projectId: 'github:stablyai/orca',
hostId: 'ssh:devbox',
repoId: 'repo-1',
providerIdentity: { provider: 'github', owner: 'stablyai', repo: 'orca' }
}
],
hostAvailability: [{ hostId: 'ssh:devbox', status: 'disconnected' }]
})
expect(summary.label).toBe('GitHub · devbox · disconnected · stablyai/orca')
expect(summary.title).toBe(
'GitHub · Host: devbox · Availability: devbox disconnected · Source: stablyai/orca'
)
})
it('summarizes multiple unavailable source hosts without cluttering the label', () => {
const summary = getTaskSourceContextSummary({
provider: 'github',
providerLabel: 'GitHub',
selectedRepoCount: 2,
repoContexts: [
{
kind: 'task-source',
provider: 'github',
projectId: 'project-a',
hostId: 'ssh:devbox',
repoId: 'repo-a'
},
{
kind: 'task-source',
provider: 'github',
projectId: 'project-b',
hostId: 'ssh:buildbox',
repoId: 'repo-b'
}
],
hostAvailability: [
{ hostId: 'ssh:devbox', status: 'auth-failed' },
{ hostId: 'ssh:buildbox', status: 'reconnecting' }
]
})
expect(summary.label).toBe('GitHub · devbox, buildbox · 2 unavailable · 2 projects')
expect(summary.title).toBe(
'GitHub · Host: devbox, buildbox · Availability: devbox auth needed, buildbox connecting · 2 selected projects'
)
})
it('summarizes multiple repo-backed hosts without hiding the selected count', () => {
const summary = getTaskSourceContextSummary({
provider: 'gitlab',
providerLabel: 'GitLab',
selectedRepoCount: 3,
repoContexts: [
{
kind: 'task-source',
provider: 'gitlab',
projectId: 'project-a',
hostId: 'local',
repoId: 'repo-a'
},
{
kind: 'task-source',
provider: 'gitlab',
projectId: 'project-b',
hostId: 'ssh:build',
repoId: 'repo-b'
},
{
kind: 'task-source',
provider: 'gitlab',
projectId: 'project-c',
hostId: 'runtime:linux',
repoId: 'repo-c'
}
]
})
expect(summary.label).toBe(`GitLab · ${LOCAL_HOST_LABEL} +2 · 3 projects`)
expect(summary.title).toBe(
`GitLab · Host: ${LOCAL_HOST_LABEL}, build, linux · 3 selected projects`
)
})
it('shows blocked remote-server source-host availability', () => {
const summary = getTaskSourceContextSummary({
provider: 'github',
providerLabel: 'GitHub',
selectedRepoCount: 1,
repoContexts: [
{
kind: 'task-source',
provider: 'github',
projectId: 'project-a',
hostId: 'runtime:old-server',
repoId: 'repo-a',
providerIdentity: { provider: 'github', owner: 'stablyai', repo: 'orca' }
}
],
hostAvailability: [{ hostId: 'runtime:old-server', health: 'blocked' }]
})
expect(summary.label).toBe('GitHub · old-server · server update needed · stablyai/orca')
expect(summary.title).toBe(
'GitHub · Host: old-server · Availability: old-server server update needed · Source: stablyai/orca'
)
})
it('shows remote-server task-source capability checks', () => {
const summary = getTaskSourceContextSummary({
provider: 'github',
providerLabel: 'GitHub',
selectedRepoCount: 1,
repoContexts: [
{
kind: 'task-source',
provider: 'github',
projectId: 'project-a',
hostId: 'runtime:old-server',
repoId: 'repo-a',
providerIdentity: { provider: 'github', owner: 'stablyai', repo: 'orca' }
}
],
hostAvailability: [
{ hostId: 'runtime:old-server', reason: 'checking-task-source-capability' }
]
})
expect(summary.label).toBe('GitHub · old-server · checking server capabilities · stablyai/orca')
expect(summary.title).toBe(
'GitHub · Host: old-server · Availability: old-server checking server capabilities · Source: stablyai/orca'
)
})
it('uses saved remote server labels in repo-backed source summaries and notices', () => {
const hostLabelById = new Map([['runtime:03ef704c-b180-4b10-998d-e28fbd5de9a3', 'dev box']])
expect(
getTaskSourceContextSummary({
provider: 'github',
providerLabel: 'GitHub',
selectedRepoCount: 1,
hostLabelById,
repoContexts: [
{
kind: 'task-source',
provider: 'github',
projectId: 'github:stablyai/orca',
hostId: 'runtime:03ef704c-b180-4b10-998d-e28fbd5de9a3',
repoId: 'repo-runtime',
providerIdentity: { provider: 'github', owner: 'stablyai', repo: 'orca' }
}
],
hostAvailability: [
{
hostId: 'runtime:03ef704c-b180-4b10-998d-e28fbd5de9a3',
health: 'blocked'
}
]
})
).toEqual({
label: 'GitHub · dev box · server update needed · stablyai/orca',
title:
'GitHub · Host: dev box · Availability: dev box server update needed · Source: stablyai/orca'
})
expect(
getTaskSourceAvailabilityNotice({
providerLabel: 'GitHub',
sourceCount: 1,
hostLabelById,
hostAvailability: [
{
hostId: 'runtime:03ef704c-b180-4b10-998d-e28fbd5de9a3',
reason: 'missing-task-source-capability'
}
]
})?.label
).toBe('GitHub source unavailable: dev box server update needed for task sources')
})
it('shows remote-server task-source capability version skew', () => {
expect(
getTaskSourceAvailabilityNotice({
providerLabel: 'GitHub',
sourceCount: 1,
hostAvailability: [
{ hostId: 'runtime:old-server', reason: 'missing-task-source-capability' }
]
})
).toEqual({
label: 'GitHub source unavailable: old-server server update needed for task sources',
title:
'Reconnect or update old-server server update needed for task sources to load this source.',
blocking: true
})
})
it('shows account-backed Linear and Jira sources', () => {
expect(
getTaskSourceContextSummary({
provider: 'linear',
providerLabel: 'Linear',
accountHostId: 'local',
linearWorkspaceName: 'Stably'
}).label
).toBe(`Linear · ${LOCAL_HOST_LABEL} · Stably`)
expect(
getTaskSourceContextSummary({
provider: 'jira',
providerLabel: 'Jira',
accountHostId: 'runtime:server',
jiraSiteName: 'Stably Jira'
}).label
).toBe('Jira · server · Stably Jira')
})
it('shows account-backed source host availability', () => {
const summary = getTaskSourceContextSummary({
provider: 'linear',
providerLabel: 'Linear',
accountHostId: 'runtime:old-server',
linearWorkspaceName: 'Stably',
hostAvailability: [{ hostId: 'runtime:old-server', health: 'blocked' }]
})
expect(summary.label).toBe('Linear · old-server · server update needed · Stably')
expect(summary.title).toBe(
'Linear source · Host: old-server · Availability: old-server server update needed · Account: Stably'
)
})
it('builds a visible unavailable-source notice from host availability', () => {
expect(
getTaskSourceAvailabilityNotice({
providerLabel: 'GitHub',
hostAvailability: [{ hostId: 'ssh:devbox', status: 'auth-failed' }]
})
).toEqual({
label: 'GitHub source unavailable: devbox auth needed',
title: 'Reconnect or update devbox auth needed to load this source.',
blocking: true
})
expect(
getTaskSourceAvailabilityNotice({
providerLabel: 'GitLab',
sourceCount: 3,
hostAvailability: [
{ hostId: 'ssh:devbox', status: 'disconnected' },
{ hostId: 'runtime:old-server', health: 'blocked' }
]
})?.label
).toBe('Some GitLab source hosts unavailable: 2 source hosts')
})
it('shows provider-specific source availability reasons', () => {
expect(
getTaskSourceContextSummary({
provider: 'github',
providerLabel: 'GitHub',
selectedRepoCount: 1,
repoContexts: [
{
kind: 'task-source',
provider: 'github',
projectId: 'github:stablyai/orca',
hostId: 'ssh:devbox',
repoId: 'repo-1',
providerIdentity: { provider: 'github', owner: 'stablyai', repo: 'orca' }
}
],
hostAvailability: [{ hostId: 'ssh:devbox', reason: 'missing-provider-auth' }]
})
).toEqual({
label: 'GitHub · devbox · provider auth needed · stablyai/orca',
title:
'GitHub · Host: devbox · Availability: devbox provider auth needed · Source: stablyai/orca'
})
expect(
getTaskSourceAvailabilityNotice({
providerLabel: 'GitHub',
sourceCount: 3,
hostAvailability: [
{ hostId: 'ssh:devbox', reason: 'unavailable-source-tool' },
{ hostId: 'runtime:linux', reason: 'unsupported-provider' }
]
})
).toEqual({
label: 'Some GitHub source hosts unavailable: 2 source hosts',
title:
'Reconnect or update devbox source tool unavailable, linux provider unsupported on this host to load this source.',
blocking: false
})
})
})
@@ -5,61 +5,6 @@ import { toRemoteRuntimePtyId } from '../../../../shared/remote-runtime/remote-r
const LEAF_ID = '11111111-1111-4111-8111-111111111111'
describe('copyTerminalHandleForPane', () => {
it('copies the runtime terminal handle for a pane key', async () => {
const callRuntime = vi.fn().mockResolvedValue({
id: 'req-1',
ok: true,
result: {
terminal: {
handle: 'term_worker',
tabId: 'tab-1',
leafId: LEAF_ID,
ptyId: 'pty-1'
}
},
_meta: { runtimeId: 'runtime-1' }
})
const writeClipboardText = vi.fn().mockResolvedValue(undefined)
await expect(
copyTerminalHandleForPane({
tabId: 'tab-1',
leafId: LEAF_ID,
callRuntime,
writeClipboardText
})
).resolves.toBe('term_worker')
expect(callRuntime).toHaveBeenCalledWith({
method: 'terminal.resolvePane',
params: { paneKey: `tab-1:${LEAF_ID}` }
})
expect(writeClipboardText).toHaveBeenCalledWith('term_worker')
})
it('surfaces runtime lookup failures without writing the clipboard', async () => {
const callRuntime = vi.fn().mockResolvedValue({
id: 'req-1',
ok: false,
error: {
code: 'terminal_not_found',
message: 'terminal not found'
}
})
const writeClipboardText = vi.fn()
await expect(
copyTerminalHandleForPane({
tabId: 'tab-1',
leafId: LEAF_ID,
callRuntime,
writeClipboardText
})
).rejects.toThrow('terminal not found')
expect(writeClipboardText).not.toHaveBeenCalled()
})
it('copies the owning server handle for a paired-server pane without asking this runtime', async () => {
// Why: this desktop's runtime has no record of a server-owned pane (#11664, #15553).
const callRuntime = vi.fn().mockResolvedValue({
@@ -1,55 +0,0 @@
import { describe, expect, it, vi } from 'vitest'
import { copyTerminalSelection } from './terminal-selection-copy'
function makeTerminal(selection: string) {
return {
getSelection: vi.fn(() => selection),
clearSelection: vi.fn()
}
}
describe('copyTerminalSelection', () => {
it('writes selected terminal text to the clipboard', async () => {
const terminal = makeTerminal('copilot answer')
const writeClipboardText = vi.fn<(text: string) => Promise<void>>().mockResolvedValue()
await expect(copyTerminalSelection({ terminal, writeClipboardText })).resolves.toBe(true)
expect(writeClipboardText).toHaveBeenCalledWith('copilot answer')
expect(terminal.clearSelection).not.toHaveBeenCalled()
})
it('does not claim success for an empty xterm selection', async () => {
const terminal = makeTerminal('')
const writeClipboardText = vi.fn<(text: string) => Promise<void>>().mockResolvedValue()
await expect(copyTerminalSelection({ terminal, writeClipboardText })).resolves.toBe(false)
expect(writeClipboardText).not.toHaveBeenCalled()
expect(terminal.clearSelection).not.toHaveBeenCalled()
})
it('clears xterm selection only after the clipboard write succeeds', async () => {
const terminal = makeTerminal('copilot answer')
const writeClipboardText = vi
.fn<(text: string) => Promise<void>>()
.mockRejectedValue(new Error('clipboard unchanged'))
await expect(
copyTerminalSelection({ terminal, writeClipboardText, clearSelectionOnSuccess: true })
).rejects.toThrow('clipboard unchanged')
expect(terminal.clearSelection).not.toHaveBeenCalled()
})
it('clears the xterm selection after a successful write when requested', async () => {
const terminal = makeTerminal('copilot answer')
const writeClipboardText = vi.fn<(text: string) => Promise<void>>().mockResolvedValue()
await expect(
copyTerminalSelection({ terminal, writeClipboardText, clearSelectionOnSuccess: true })
).resolves.toBe(true)
expect(terminal.clearSelection).toHaveBeenCalledOnce()
})
})