mirror of
https://github.com/stablyai/orca.git
synced 2026-11-03 18:47:40 +00:00
test: remove low-value renderer predicates and wrapper suites (#33811)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
@@ -1,34 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import type { LinearIssue } from '../../../shared/linear/issue-types'
|
||||
import { buildLinearIssueBranchName } from './linear-issue-workspace-text'
|
||||
|
||||
function makeIssue(branchName?: string): LinearIssue {
|
||||
return {
|
||||
id: 'issue-1',
|
||||
identifier: 'ENG-123',
|
||||
title: 'Fix launch context handoff',
|
||||
url: 'https://linear.app/acme/issue/ENG-123/fix-launch-context-handoff',
|
||||
branchName,
|
||||
state: { name: 'Todo', type: 'unstarted', color: '#999999' },
|
||||
team: { id: 'team-1', name: 'Engineering', key: 'ENG' },
|
||||
labels: [],
|
||||
labelIds: [],
|
||||
priority: 3,
|
||||
estimate: null,
|
||||
updatedAt: '2026-05-29T12:00:00.000Z'
|
||||
}
|
||||
}
|
||||
|
||||
describe('buildLinearIssueBranchName', () => {
|
||||
it('prefers Linear’s branch name', () => {
|
||||
expect(buildLinearIssueBranchName(makeIssue(' team/eng-123-fix-launch-context '))).toBe(
|
||||
'team/eng-123-fix-launch-context'
|
||||
)
|
||||
})
|
||||
|
||||
it('falls back to Orca’s generated workspace slug', () => {
|
||||
expect(buildLinearIssueBranchName(makeIssue())).toBe('eng-123-fix-launch-context-handoff')
|
||||
expect(buildLinearIssueBranchName(makeIssue(' '))).toBe('eng-123-fix-launch-context-handoff')
|
||||
})
|
||||
})
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { canUseGitLabSmartSource } from './smart-workspace-provider-availability'
|
||||
|
||||
describe('SmartWorkspaceNameField provider boundaries', () => {
|
||||
it('advertises local GitLab smart lookup only when local glab is available', () => {
|
||||
expect(
|
||||
canUseGitLabSmartSource({
|
||||
localGitlabAvailable: true,
|
||||
repoBackedSourcesDisabled: false,
|
||||
sourceHostId: 'local'
|
||||
})
|
||||
).toBe(true)
|
||||
expect(
|
||||
canUseGitLabSmartSource({
|
||||
localGitlabAvailable: true,
|
||||
repoBackedSourcesDisabled: true,
|
||||
sourceHostId: 'local'
|
||||
})
|
||||
).toBe(false)
|
||||
expect(
|
||||
canUseGitLabSmartSource({
|
||||
localGitlabAvailable: false,
|
||||
repoBackedSourcesDisabled: false,
|
||||
sourceHostId: 'local'
|
||||
})
|
||||
).toBe(false)
|
||||
})
|
||||
|
||||
it('does not hide SSH or runtime GitLab sources based on local glab preflight', () => {
|
||||
expect(
|
||||
canUseGitLabSmartSource({
|
||||
localGitlabAvailable: false,
|
||||
repoBackedSourcesDisabled: false,
|
||||
sourceHostId: 'ssh:builder'
|
||||
})
|
||||
).toBe(true)
|
||||
expect(
|
||||
canUseGitLabSmartSource({
|
||||
localGitlabAvailable: false,
|
||||
repoBackedSourcesDisabled: false,
|
||||
sourceHostId: 'runtime:env-1'
|
||||
})
|
||||
).toBe(true)
|
||||
})
|
||||
})
|
||||
@@ -1,47 +0,0 @@
|
||||
// @vitest-environment happy-dom
|
||||
|
||||
import { act } from 'react'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { TopActivityOverflowMenu, type ActivityBarItem } from './activity-bar-buttons'
|
||||
|
||||
vi.mock('@/i18n/i18n', () => ({
|
||||
translate: (_key: string, fallback: string) => fallback
|
||||
}))
|
||||
|
||||
const roots: ReturnType<typeof createRoot>[] = []
|
||||
|
||||
afterEach(async () => {
|
||||
await act(async () => {
|
||||
for (const root of roots.splice(0)) {
|
||||
root.unmount()
|
||||
}
|
||||
})
|
||||
document.body.innerHTML = ''
|
||||
})
|
||||
|
||||
describe('TopActivityOverflowMenu', () => {
|
||||
it('announces a hidden plugin panel error from the More button', async () => {
|
||||
const container = document.createElement('div')
|
||||
document.body.appendChild(container)
|
||||
const root = createRoot(container)
|
||||
roots.push(root)
|
||||
const item: ActivityBarItem = {
|
||||
id: 'plugin:orca-samples.demo/dashboard',
|
||||
icon: () => <span />,
|
||||
title: 'Demo',
|
||||
shortcut: '',
|
||||
statusIndicator: 'failure'
|
||||
}
|
||||
|
||||
await act(async () => {
|
||||
root.render(
|
||||
<TopActivityOverflowMenu items={[item]} activeTab="explorer" onSelect={vi.fn()} />
|
||||
)
|
||||
})
|
||||
|
||||
expect(container.querySelector('button')?.getAttribute('aria-label')).toBe(
|
||||
'More sidebar tabs — Error'
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -1,44 +0,0 @@
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import {
|
||||
clearRightPanelCommentFocusTimer,
|
||||
scheduleRightPanelCommentFocusTimer,
|
||||
type RightPanelCommentFocusTimerRef
|
||||
} from './right-panel-comment-focus-timers'
|
||||
|
||||
function createTimerRef(): RightPanelCommentFocusTimerRef {
|
||||
return { current: null }
|
||||
}
|
||||
|
||||
describe('right panel comment focus timers', () => {
|
||||
afterEach(() => {
|
||||
vi.useRealTimers()
|
||||
})
|
||||
|
||||
it('clears pending deferred focus work', () => {
|
||||
vi.useFakeTimers()
|
||||
const timerRef = createTimerRef()
|
||||
const callback = vi.fn()
|
||||
|
||||
scheduleRightPanelCommentFocusTimer(timerRef, callback)
|
||||
clearRightPanelCommentFocusTimer(timerRef)
|
||||
vi.runOnlyPendingTimers()
|
||||
|
||||
expect(timerRef.current).toBeNull()
|
||||
expect(callback).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('replaces stale deferred focus work', () => {
|
||||
vi.useFakeTimers()
|
||||
const timerRef = createTimerRef()
|
||||
const staleCallback = vi.fn()
|
||||
const nextCallback = vi.fn()
|
||||
|
||||
scheduleRightPanelCommentFocusTimer(timerRef, staleCallback)
|
||||
scheduleRightPanelCommentFocusTimer(timerRef, nextCallback)
|
||||
vi.runOnlyPendingTimers()
|
||||
|
||||
expect(staleCallback).not.toHaveBeenCalled()
|
||||
expect(nextCallback).toHaveBeenCalledTimes(1)
|
||||
expect(timerRef.current).toBeNull()
|
||||
})
|
||||
})
|
||||
@@ -10,16 +10,6 @@ afterEach(() => {
|
||||
useAppStore.getState().clearAiVaultSearchFocusRequest()
|
||||
})
|
||||
|
||||
it('opens the sidebar on the session panel when Settings asks for it', () => {
|
||||
act(() => {
|
||||
useAppStore.getState().showAiVaultSearch()
|
||||
})
|
||||
const state = useAppStore.getState()
|
||||
expect(state.rightSidebarOpen).toBe(true)
|
||||
expect(state.rightSidebarTab).toBe('vault')
|
||||
expect(state.aiVaultSearchFocusRequested).toBe(true)
|
||||
})
|
||||
|
||||
it('runs the request once and clears it so a remount stays put', () => {
|
||||
const onRequest = vi.fn()
|
||||
const view = renderHook(() => useAiVaultSearchFocusRequest(onRequest))
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
import { describe, expect, it, afterEach, vi } from 'vitest'
|
||||
import { getSourceControlAiControllerDiscoveryHostKey } from './source-control/ai/use-ai'
|
||||
|
||||
describe('getSourceControlAiControllerDiscoveryHostKey', () => {
|
||||
it('keys generation settings by the active workspace connection', () => {
|
||||
const local = { kind: 'local' } as const
|
||||
|
||||
expect(getSourceControlAiControllerDiscoveryHostKey(local, null)).toBe('local')
|
||||
expect(getSourceControlAiControllerDiscoveryHostKey(local, undefined)).toBe('unknown')
|
||||
expect(getSourceControlAiControllerDiscoveryHostKey(local, 'ssh-1')).toBe('ssh:ssh-1')
|
||||
})
|
||||
|
||||
it('uses the repo owner runtime before SSH connection scope', () => {
|
||||
const owner = { kind: 'environment', environmentId: 'env-1' } as const
|
||||
|
||||
expect(getSourceControlAiControllerDiscoveryHostKey(owner, 'ssh-1')).toBe('runtime:env-1')
|
||||
})
|
||||
})
|
||||
|
||||
const { rejectAppStoreAccess } = vi.hoisted(() => ({
|
||||
rejectAppStoreAccess: vi.fn(() => {
|
||||
throw new Error('Pure helper tests must not access the app store')
|
||||
})
|
||||
}))
|
||||
|
||||
vi.mock('@/store', () => ({
|
||||
useAppStore: Object.assign(rejectAppStoreAccess, {
|
||||
getState: rejectAppStoreAccess,
|
||||
getInitialState: rejectAppStoreAccess,
|
||||
setState: rejectAppStoreAccess,
|
||||
subscribe: rejectAppStoreAccess
|
||||
})
|
||||
}))
|
||||
|
||||
afterEach(() => {
|
||||
expect(rejectAppStoreAccess).not.toHaveBeenCalled()
|
||||
})
|
||||
@@ -1,41 +0,0 @@
|
||||
import { renderToStaticMarkup } from 'react-dom/server'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { TaskSourceShowInTasksStep } from './TaskSourceShowInTasksStep'
|
||||
|
||||
describe('TaskSourceShowInTasksStep', () => {
|
||||
it('uses an explicit hide action when another provider can remain visible', () => {
|
||||
const markup = renderToStaticMarkup(
|
||||
<TaskSourceShowInTasksStep
|
||||
index={2}
|
||||
providerLabel="GitLab"
|
||||
visible
|
||||
canHide
|
||||
onToggleVisible={vi.fn()}
|
||||
/>
|
||||
)
|
||||
|
||||
expect(markup).not.toContain('aria-pressed')
|
||||
expect(markup).toContain('>Hide</button>')
|
||||
})
|
||||
|
||||
it('explains why the final visible provider cannot be hidden', () => {
|
||||
const markup = renderToStaticMarkup(
|
||||
<TaskSourceShowInTasksStep
|
||||
index={2}
|
||||
providerLabel="GitLab"
|
||||
visible
|
||||
canHide={false}
|
||||
onToggleVisible={vi.fn()}
|
||||
/>
|
||||
)
|
||||
|
||||
// aria-disabled, not disabled: the explanation must stay keyboard reachable.
|
||||
expect(markup).toContain('aria-disabled="true"')
|
||||
expect(markup).not.toContain('disabled=""')
|
||||
expect(markup).toContain(
|
||||
'aria-label="GitLab is shown in Tasks. At least one provider must stay visible."'
|
||||
)
|
||||
expect(markup).toContain('>Shown</button>')
|
||||
expect(markup).toContain('At least one provider must stay visible in Tasks.')
|
||||
})
|
||||
})
|
||||
@@ -1,38 +0,0 @@
|
||||
import { renderToStaticMarkup } from 'react-dom/server'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { encodePairingOffer, PAIRING_OFFER_VERSION } from '../../../../shared/pairing'
|
||||
import { parseHostAccessLink } from '../../../../shared/remote-pairing-address'
|
||||
import { RemoteServerFields } from './AddRemoteHostFields'
|
||||
|
||||
function loopbackAccessLink(): string {
|
||||
return encodePairingOffer({
|
||||
v: PAIRING_OFFER_VERSION,
|
||||
endpoint: 'ws://127.0.0.1:6768',
|
||||
deviceToken: 'token',
|
||||
publicKeyB64: 'key',
|
||||
scope: 'runtime'
|
||||
})
|
||||
}
|
||||
|
||||
describe('RemoteServerFields', () => {
|
||||
it('associates blocked loopback guidance with the access-link input', () => {
|
||||
const pairingCode = loopbackAccessLink()
|
||||
const markup = renderToStaticMarkup(
|
||||
<RemoteServerFields
|
||||
name="Remote workstation"
|
||||
pairingCode={pairingCode}
|
||||
parsedLink={parseHostAccessLink(pairingCode)}
|
||||
disabled={false}
|
||||
onNameChange={vi.fn()}
|
||||
onPairingCodeChange={vi.fn()}
|
||||
allowLoopback={false}
|
||||
onAllowLoopbackChange={vi.fn()}
|
||||
onSubmit={vi.fn()}
|
||||
/>
|
||||
)
|
||||
|
||||
expect(markup).toContain('aria-invalid="true"')
|
||||
expect(markup).toContain('aria-describedby="add-server-loopback-blocked"')
|
||||
expect(markup).toContain('id="add-server-loopback-blocked"')
|
||||
})
|
||||
})
|
||||
@@ -7,19 +7,6 @@ import { useWorkspaceKanbanDrawerLingering } from './use-workspace-kanban-drawer
|
||||
describe('workspace board close linger', () => {
|
||||
afterEach(() => vi.useRealTimers())
|
||||
|
||||
it('keeps drawer state through the close animation, then releases it at 300 ms', () => {
|
||||
vi.useFakeTimers()
|
||||
const { result, rerender } = renderHook(
|
||||
({ open }: { open: boolean }) => useWorkspaceKanbanDrawerLingering(open),
|
||||
{ initialProps: { open: true } }
|
||||
)
|
||||
act(() => rerender({ open: false }))
|
||||
act(() => vi.advanceTimersByTime(299))
|
||||
expect(result.current).toBe(true)
|
||||
act(() => vi.advanceTimersByTime(1))
|
||||
expect(result.current).toBe(false)
|
||||
})
|
||||
|
||||
it('cancels the pending release when reopened', () => {
|
||||
vi.useFakeTimers()
|
||||
const { result, rerender } = renderHook(
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { expandDraggedWorktreeIdsForVisibleLineage } from './worktree-manual-order'
|
||||
|
||||
describe('visible lineage expansion scaling', () => {
|
||||
it('reads each depth once even when every ancestor is selected', () => {
|
||||
let depthReads = 0
|
||||
const rows = Array.from({ length: 2_000 }, (_, index) => ({
|
||||
worktreeId: `wt-${index}`,
|
||||
get depth() {
|
||||
depthReads++
|
||||
return index
|
||||
}
|
||||
}))
|
||||
const selected = rows.map((row) => row.worktreeId)
|
||||
expect(expandDraggedWorktreeIdsForVisibleLineage(rows, selected)).toEqual(selected)
|
||||
expect(depthReads).toBe(rows.length)
|
||||
})
|
||||
|
||||
it('deduplicates many absent selected rows without searching the growing result', () => {
|
||||
const selected = Array.from({ length: 5_000 }, (_, index) => `missing-${index}`)
|
||||
const includes = vi.spyOn(Array.prototype, 'includes')
|
||||
let calls: number
|
||||
let result: string[]
|
||||
try {
|
||||
result = expandDraggedWorktreeIdsForVisibleLineage([], [...selected, ...selected])
|
||||
calls = includes.mock.calls.length
|
||||
} finally {
|
||||
includes.mockRestore()
|
||||
}
|
||||
expect(result).toEqual(selected)
|
||||
expect(calls).toBe(0)
|
||||
})
|
||||
})
|
||||
@@ -1,43 +0,0 @@
|
||||
// @vitest-environment happy-dom
|
||||
|
||||
import { act, cleanup, renderHook } from '@testing-library/react'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { SkillInstallProgress } from '../../../../shared/skill-sharing-contract'
|
||||
import { useSkillInstallProgress } from './skill-install-progress-state'
|
||||
|
||||
afterEach(() => {
|
||||
cleanup()
|
||||
vi.restoreAllMocks()
|
||||
})
|
||||
|
||||
describe('useSkillInstallProgress', () => {
|
||||
it('announces aggregate bundle progress and the current skill', () => {
|
||||
let listener: ((progress: SkillInstallProgress) => void) | null = null
|
||||
Object.defineProperty(window, 'api', {
|
||||
configurable: true,
|
||||
value: {
|
||||
skills: {
|
||||
onInstallProgress: vi.fn((next) => {
|
||||
listener = next
|
||||
return () => {
|
||||
listener = null
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
const hook = renderHook(() => useSkillInstallProgress())
|
||||
|
||||
act(() => hook.result.current.begin('operation_1'))
|
||||
expect(hook.result.current.phaseLabel).toBe('Authorizing package access…')
|
||||
|
||||
act(() =>
|
||||
listener?.({
|
||||
operationId: 'operation_1',
|
||||
phase: 'installing',
|
||||
currentSkill: { id: 'beta', name: 'beta', index: 2, total: 30 }
|
||||
})
|
||||
)
|
||||
expect(hook.result.current.phaseLabel).toBe('Installing 2 of 30: beta…')
|
||||
})
|
||||
})
|
||||
@@ -1,37 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import type { DiscoveredSkill } from '../../../../shared/skills'
|
||||
import { addShareableSkillResults, eligibleShareSkillCount } from './skill-share-selection'
|
||||
|
||||
function skill(name: string, overrides: Partial<DiscoveredSkill> = {}): DiscoveredSkill {
|
||||
return {
|
||||
id: `${overrides.sourceKind ?? 'home'}:${name}`,
|
||||
name,
|
||||
description: null,
|
||||
providers: ['agent-skills'],
|
||||
sourceKind: 'home',
|
||||
sourceLabel: 'Agent skills home',
|
||||
rootPath: '/home/dev/.agents/skills',
|
||||
directoryPath: `/home/dev/.agents/skills/${name}`,
|
||||
skillFilePath: `/home/dev/.agents/skills/${name}/SKILL.md`,
|
||||
installed: true,
|
||||
updatedAt: null,
|
||||
...overrides
|
||||
}
|
||||
}
|
||||
|
||||
describe('eligibleShareSkillCount', () => {
|
||||
it('matches what selecting all results would hold', () => {
|
||||
const results = [
|
||||
skill('alpha'),
|
||||
skill('alpha', { sourceKind: 'repo' }),
|
||||
skill('beta', { sourceKind: 'plugin' }),
|
||||
skill('gamma', { installed: false })
|
||||
]
|
||||
expect(eligibleShareSkillCount(results, true)).toBe(1)
|
||||
expect(addShareableSkillResults(new Set(), results, results, true).size).toBe(1)
|
||||
})
|
||||
|
||||
it('counts nothing when the skills live on another machine', () => {
|
||||
expect(eligibleShareSkillCount([skill('alpha'), skill('beta')], false)).toBe(0)
|
||||
})
|
||||
})
|
||||
@@ -1,29 +0,0 @@
|
||||
// @vitest-environment happy-dom
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import type * as React from 'react'
|
||||
import { keepFocusWhileMenuCloses } from './menu-item-closing-focus'
|
||||
|
||||
function pointerEventOnItem(menuState: 'open' | 'closed'): React.PointerEvent<HTMLDivElement> {
|
||||
const menu = document.createElement('div')
|
||||
menu.setAttribute('role', 'menu')
|
||||
menu.setAttribute('data-state', menuState)
|
||||
const item = menu.appendChild(document.createElement('div'))
|
||||
const event = { currentTarget: item, preventDefault: vi.fn() }
|
||||
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the handler reads only currentTarget and preventDefault.
|
||||
return event as unknown as React.PointerEvent<HTMLDivElement>
|
||||
}
|
||||
|
||||
describe('keepFocusWhileMenuCloses', () => {
|
||||
// A New Chat chosen from the menu lost every key after the first: the closing menu took focus back.
|
||||
it('cancels the library pointer focus only while the menu is closing, after the caller runs', () => {
|
||||
const caller = vi.fn()
|
||||
const closing = pointerEventOnItem('closed')
|
||||
keepFocusWhileMenuCloses(caller)(closing)
|
||||
expect(caller).toHaveBeenCalledWith(closing)
|
||||
expect(closing.preventDefault).toHaveBeenCalled()
|
||||
|
||||
const open = pointerEventOnItem('open')
|
||||
keepFocusWhileMenuCloses(undefined)(open)
|
||||
expect(open.preventDefault).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
@@ -1,35 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { collectComposerDropResult, type ComposerDropItemResult } from './composer-drop-result'
|
||||
|
||||
describe('composer drop result', () => {
|
||||
it('separates imported files and folders while summarizing failures', () => {
|
||||
const results: ComposerDropItemResult[] = [
|
||||
{ status: 'imported', kind: 'file', destPath: '/repo/.orca/drops/file.txt' },
|
||||
{ status: 'imported', kind: 'directory', destPath: '/repo/.orca/drops/folder' },
|
||||
{ status: 'skipped', reason: 'permission-denied' },
|
||||
{ status: 'failed', reason: 'disk full' }
|
||||
]
|
||||
|
||||
expect(collectComposerDropResult(results)).toEqual({
|
||||
filePaths: ['/repo/.orca/drops/file.txt'],
|
||||
folderPaths: ['/repo/.orca/drops/folder'],
|
||||
failureCount: 2,
|
||||
commonFailure: undefined
|
||||
})
|
||||
})
|
||||
|
||||
it('keeps a failure only when it explains the whole failed subset', () => {
|
||||
expect(
|
||||
collectComposerDropResult([
|
||||
{ status: 'skipped', reason: 'missing' },
|
||||
{ status: 'skipped', reason: 'missing' }
|
||||
]).commonFailure
|
||||
).toEqual({ status: 'skipped', reason: 'missing' })
|
||||
|
||||
expect(
|
||||
collectComposerDropResult([
|
||||
{ status: 'imported', kind: 'file', destPath: '/repo/.orca/drops/file.txt' }
|
||||
]).commonFailure
|
||||
).toBeUndefined()
|
||||
})
|
||||
})
|
||||
@@ -1,34 +0,0 @@
|
||||
// @vitest-environment happy-dom
|
||||
|
||||
import { renderHook } from '@testing-library/react'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { usePaletteSearchEvaluationContext } from './use-palette-search-evaluation-context'
|
||||
|
||||
afterEach(() => vi.restoreAllMocks())
|
||||
|
||||
describe('usePaletteSearchEvaluationContext', () => {
|
||||
it('captures one clock per snapshot without committing a stale ranking pass', () => {
|
||||
const clock = vi.spyOn(Date, 'now').mockReturnValue(1_000)
|
||||
const evaluations: number[] = []
|
||||
const snapshot = { query: 'atlas' }
|
||||
const { result, rerender } = renderHook(
|
||||
({ snapshot }) => {
|
||||
const context = usePaletteSearchEvaluationContext(snapshot)
|
||||
evaluations.push(context.nowMs)
|
||||
return context
|
||||
},
|
||||
{ initialProps: { snapshot } }
|
||||
)
|
||||
expect(evaluations).toEqual([1_000])
|
||||
const initial = result.current
|
||||
|
||||
clock.mockReturnValue(2_000)
|
||||
rerender({ snapshot })
|
||||
expect(result.current).toBe(initial)
|
||||
|
||||
evaluations.length = 0
|
||||
rerender({ snapshot: { query: 'atlas notes' } })
|
||||
expect(evaluations).toEqual([2_000])
|
||||
expect(result.current).not.toBe(initial)
|
||||
})
|
||||
})
|
||||
@@ -1,34 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import type { TaskSourceContext } from '../../../shared/task-source-context'
|
||||
import { shouldSkipLocalViewerQualifiers } from './useTaskPageGitHubWorkItemMutation'
|
||||
|
||||
function sourceContext(hostId: TaskSourceContext['hostId']): TaskSourceContext {
|
||||
return {
|
||||
kind: 'task-source',
|
||||
provider: 'github',
|
||||
projectId: 'project-1',
|
||||
hostId
|
||||
}
|
||||
}
|
||||
|
||||
describe('TaskPage GitHub work-item mutation host handling', () => {
|
||||
it('uses the local viewer only for local task sources', () => {
|
||||
expect(shouldSkipLocalViewerQualifiers(sourceContext('local'))).toBe(false)
|
||||
expect(shouldSkipLocalViewerQualifiers(sourceContext('ssh:builder'))).toBe(true)
|
||||
expect(shouldSkipLocalViewerQualifiers(sourceContext('runtime:environment-1'))).toBe(true)
|
||||
})
|
||||
|
||||
it('does not reuse the github.com viewer for a local enterprise host', () => {
|
||||
expect(
|
||||
shouldSkipLocalViewerQualifiers({
|
||||
...sourceContext('local'),
|
||||
providerIdentity: {
|
||||
provider: 'github',
|
||||
owner: 'acme',
|
||||
repo: 'project',
|
||||
host: 'github.acme.test'
|
||||
}
|
||||
})
|
||||
).toBe(true)
|
||||
})
|
||||
})
|
||||
@@ -1,32 +0,0 @@
|
||||
import { expect, it } from 'vitest'
|
||||
import en from './locales/en.json'
|
||||
import {
|
||||
agentLaunchRepairRefusal,
|
||||
projectRuntimeRepairSentence
|
||||
} from '../../../shared/project-runtime-repair-message'
|
||||
|
||||
const page = new Map(Object.entries(en.auto.components.settings.ProjectWindowsRuntimeSetting))
|
||||
const preferredRuntime = { kind: 'wsl', distro: 'Ubuntu' } as const
|
||||
const base = {
|
||||
projectId: 'p',
|
||||
preferredRuntime,
|
||||
source: 'project-override',
|
||||
cacheKey: 'k'
|
||||
} as const
|
||||
|
||||
// The window shows the catalog's English and the host writes the module's, so they must not drift.
|
||||
it("words each repair under the settings page's key, in the window's English", () => {
|
||||
const reasons = ['wsl-unavailable', 'wsl-distro-missing', 'wsl-distro-required'] as const
|
||||
const names = reasons.map((reason) =>
|
||||
projectRuntimeRepairSentence({ ...base, reason }, (key, english) => {
|
||||
const name = key.replace('auto.components.settings.ProjectWindowsRuntimeSetting.', '')
|
||||
expect(page.get(name)).toBe(english)
|
||||
return name
|
||||
})
|
||||
)
|
||||
expect(names).toEqual(['wslUnavailable', 'distroMissing', 'distroRequired'])
|
||||
const byDefault = { ...base, reason: 'wsl-unavailable', source: 'global-default' } as const
|
||||
expect(agentLaunchRepairRefusal(byDefault)).toMatch(
|
||||
/the default in Settings > Project Runtime\.$/
|
||||
)
|
||||
})
|
||||
@@ -1,40 +0,0 @@
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { useAppStore } from '@/store'
|
||||
import {
|
||||
queueHookCommandsForFirstWorktreeTab,
|
||||
resetHookCommandDelayedDeliveryForTests
|
||||
} from './hook-command-delayed-delivery'
|
||||
|
||||
const initialState = useAppStore.getState()
|
||||
|
||||
afterEach(() => {
|
||||
resetHookCommandDelayedDeliveryForTests()
|
||||
useAppStore.setState(initialState, true)
|
||||
})
|
||||
|
||||
describe('delayed hook-command subscription', () => {
|
||||
it('does not rescan pending worktrees for unrelated store updates', () => {
|
||||
const reads = { value: 0 }
|
||||
useAppStore.setState({
|
||||
tabsByWorktree: {},
|
||||
getKnownWorktreeById: ((worktreeId: string) => {
|
||||
reads.value += 1
|
||||
return { id: worktreeId }
|
||||
}) as never
|
||||
} as never)
|
||||
|
||||
for (let index = 0; index < 500; index += 1) {
|
||||
queueHookCommandsForFirstWorktreeTab({
|
||||
worktreeId: `runtime-worktree-${index}`,
|
||||
deliver: vi.fn()
|
||||
})
|
||||
}
|
||||
|
||||
reads.value = 0
|
||||
for (let update = 0; update < 100; update += 1) {
|
||||
useAppStore.setState({ activeView: update % 2 === 0 ? 'terminal' : 'settings' } as never)
|
||||
}
|
||||
|
||||
expect(reads.value).toBe(0)
|
||||
})
|
||||
})
|
||||
@@ -1,37 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import type { DiscoveredSkill } from '../../../shared/skills'
|
||||
import { getLinearAgentSkillUpdateTarget } from './linear-agent-skill-update-target'
|
||||
|
||||
function skill(name: string): DiscoveredSkill {
|
||||
return {
|
||||
id: name,
|
||||
name,
|
||||
description: null,
|
||||
providers: ['agent-skills'],
|
||||
sourceKind: 'home',
|
||||
sourceLabel: 'Agent skills home',
|
||||
rootPath: '/home/test/.agents/skills',
|
||||
directoryPath: `/home/test/.agents/skills/${name}`,
|
||||
skillFilePath: `/home/test/.agents/skills/${name}/SKILL.md`,
|
||||
installed: true,
|
||||
updatedAt: null
|
||||
}
|
||||
}
|
||||
|
||||
describe('getLinearAgentSkillUpdateTarget', () => {
|
||||
it('targets the canonical skill for missing, canonical, and dual-name installs', () => {
|
||||
expect(getLinearAgentSkillUpdateTarget([], false)).toEqual({ skillName: 'orca-linear' })
|
||||
expect(getLinearAgentSkillUpdateTarget([skill('orca-linear')], true)).toEqual({
|
||||
skillName: 'orca-linear'
|
||||
})
|
||||
expect(
|
||||
getLinearAgentSkillUpdateTarget([skill('orca-linear'), skill('linear-tickets')], true)
|
||||
).toEqual({ skillName: 'orca-linear' })
|
||||
})
|
||||
|
||||
it('keeps the update and freshness identity on a legacy-only install', () => {
|
||||
expect(getLinearAgentSkillUpdateTarget([skill('linear-tickets')], true)).toEqual({
|
||||
skillName: 'linear-tickets'
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,30 +0,0 @@
|
||||
import { expect, it } from 'vitest'
|
||||
import type { TuiAgent } from '../../../shared/tui-agent'
|
||||
import { buildNewTabAgentMenuRows } from './new-tab-agent-menu-rows'
|
||||
const disabledTuiAgents: TuiAgent[] = ['pi']
|
||||
const fromDetection = (detected: readonly TuiAgent[]) => (agent: TuiAgent) =>
|
||||
detected.includes(agent) ? ('available' as const) : ('missing' as const)
|
||||
const input = {
|
||||
settings: { experimentalNativeChat: true, disabledTuiAgents },
|
||||
detectedOnHost: ['claude', 'gemini'] as const,
|
||||
chatAgents: ['claude', 'codex', 'pi'] as const,
|
||||
hostChatAnswer: () => 'yes' as const,
|
||||
chatProgram: fromDetection(['claude', 'gemini'])
|
||||
}
|
||||
it('orders enabled chats, unavailable providers and CLI rows', () => {
|
||||
expect(buildNewTabAgentMenuRows(input)).toEqual([
|
||||
{ kind: 'chat', agent: 'claude' },
|
||||
{ kind: 'chat-unavailable', agent: 'codex', reason: 'not-installed' },
|
||||
{ kind: 'cli', agent: 'gemini' }
|
||||
])
|
||||
})
|
||||
it.each([true, false])('uses workspace installation for unchosen agents: %s', (installed) => {
|
||||
expect(
|
||||
buildNewTabAgentMenuRows({
|
||||
...input,
|
||||
detectedOnHost: installed ? ['opencode'] : [],
|
||||
chatAgents: ['opencode'],
|
||||
chatProgram: fromDetection(installed ? ['opencode'] : [])
|
||||
})
|
||||
).toEqual(installed ? [{ kind: 'chat', agent: 'opencode' }] : [])
|
||||
})
|
||||
@@ -1,32 +0,0 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { createRoot, type Root } from 'react-dom/client'
|
||||
import { getOrCreateRendererRoot } from './react-renderer-root'
|
||||
|
||||
vi.mock('react-dom/client', () => ({
|
||||
createRoot: vi.fn()
|
||||
}))
|
||||
|
||||
beforeEach(() => {
|
||||
vi.mocked(createRoot).mockReset()
|
||||
})
|
||||
|
||||
describe('getOrCreateRendererRoot', () => {
|
||||
it('reuses the root retained by an HMR entry module', () => {
|
||||
const hotData: { orcaRendererRoot?: Root } = {}
|
||||
const root = { render: vi.fn(), unmount: vi.fn() } as unknown as Root
|
||||
vi.mocked(createRoot).mockReturnValue(root)
|
||||
|
||||
expect(getOrCreateRendererRoot({} as HTMLElement, hotData)).toBe(root)
|
||||
expect(getOrCreateRendererRoot({} as HTMLElement, hotData)).toBe(root)
|
||||
expect(createRoot).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('creates an independent root without HMR state', () => {
|
||||
const first = { render: vi.fn(), unmount: vi.fn() } as unknown as Root
|
||||
const second = { render: vi.fn(), unmount: vi.fn() } as unknown as Root
|
||||
vi.mocked(createRoot).mockReturnValueOnce(first).mockReturnValueOnce(second)
|
||||
|
||||
expect(getOrCreateRendererRoot({} as HTMLElement)).toBe(first)
|
||||
expect(getOrCreateRendererRoot({} as HTMLElement)).toBe(second)
|
||||
})
|
||||
})
|
||||
@@ -1,34 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { buildMobileSessionTabSnapshots } from './mobile-session-snapshots'
|
||||
import { makeState } from '../sync-runtime-graph-test-harness'
|
||||
|
||||
function listedMarkdownTab(isUntitled: boolean | undefined) {
|
||||
const state = makeState({
|
||||
browserTabsByWorktree: {},
|
||||
tabBarOrderByWorktree: { 'wt-1': ['/repo/untitled.md'] },
|
||||
openFiles: [
|
||||
{
|
||||
id: '/repo/untitled.md',
|
||||
filePath: '/repo/untitled.md',
|
||||
relativePath: 'untitled.md',
|
||||
worktreeId: 'wt-1',
|
||||
language: 'markdown',
|
||||
mode: 'edit',
|
||||
isDirty: true,
|
||||
...(isUntitled === undefined ? {} : { isUntitled })
|
||||
}
|
||||
]
|
||||
})
|
||||
return buildMobileSessionTabSnapshots(state)[0]?.tabs.find((tab) => tab.type === 'markdown')
|
||||
}
|
||||
|
||||
describe('a listed Markdown tab', () => {
|
||||
it('says when its document is untitled, so a phone save cannot make the file', () => {
|
||||
expect(listedMarkdownTab(true)).toMatchObject({ isUntitled: true })
|
||||
})
|
||||
|
||||
it('omits the flag for a named document', () => {
|
||||
expect(listedMarkdownTab(undefined)).not.toHaveProperty('isUntitled')
|
||||
expect(listedMarkdownTab(false)).not.toHaveProperty('isUntitled')
|
||||
})
|
||||
})
|
||||
@@ -1,30 +0,0 @@
|
||||
import { beforeEach, describe, expect, it } from 'vitest'
|
||||
import {
|
||||
beginWebRuntimeWakeTerminalRespawn,
|
||||
clearWebRuntimeWakeTerminalRespawnForWorktree,
|
||||
endWebRuntimeWakeTerminalRespawn,
|
||||
resetWebRuntimeWakeTerminalRespawnForTests,
|
||||
shouldSkipWebRuntimeWakeTerminalRespawn
|
||||
} from './web-runtime-wake-terminal-respawn'
|
||||
|
||||
describe('web-runtime-wake-terminal-respawn', () => {
|
||||
beforeEach(() => {
|
||||
resetWebRuntimeWakeTerminalRespawnForTests()
|
||||
})
|
||||
|
||||
it('dedupes concurrent wake respawn requests for the same worktree', () => {
|
||||
expect(beginWebRuntimeWakeTerminalRespawn('wt-1')).toBe(true)
|
||||
expect(shouldSkipWebRuntimeWakeTerminalRespawn('wt-1')).toBe(true)
|
||||
expect(beginWebRuntimeWakeTerminalRespawn('wt-1')).toBe(false)
|
||||
endWebRuntimeWakeTerminalRespawn('wt-1')
|
||||
expect(shouldSkipWebRuntimeWakeTerminalRespawn('wt-1')).toBe(false)
|
||||
expect(beginWebRuntimeWakeTerminalRespawn('wt-1')).toBe(true)
|
||||
})
|
||||
|
||||
it('clears wake respawn tracking for a removed worktree', () => {
|
||||
beginWebRuntimeWakeTerminalRespawn('wt-1')
|
||||
clearWebRuntimeWakeTerminalRespawnForWorktree('wt-1')
|
||||
expect(shouldSkipWebRuntimeWakeTerminalRespawn('wt-1')).toBe(false)
|
||||
expect(beginWebRuntimeWakeTerminalRespawn('wt-1')).toBe(true)
|
||||
})
|
||||
})
|
||||
-31
@@ -1,31 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { makeDetectedResult } from '../../worktrees-detected-listing-fixtures'
|
||||
import { mergeDetectedWorktreesForHost } from './detected-worktree-host-merge'
|
||||
import { areDetectedWorktreeResultsEqual } from './worktree-catalog-visibility'
|
||||
|
||||
const failed = (unavailableReason?: string) =>
|
||||
makeDetectedResult('repo-1', [], {
|
||||
authoritative: false,
|
||||
source: 'metadata-fallback',
|
||||
...(unavailableReason ? { unavailableReason } : {})
|
||||
})
|
||||
|
||||
// Why: two failed scans differ only by cause; dropping that from equality would freeze the first
|
||||
// reason on the header until the listing's rows or authority changed.
|
||||
describe('detected listing unavailable reason', () => {
|
||||
it('is part of listing equality', () => {
|
||||
expect(areDetectedWorktreeResultsEqual(failed('distro gone'), failed('distro gone'))).toBe(true)
|
||||
expect(areDetectedWorktreeResultsEqual(failed('distro gone'), failed('mount hung'))).toBe(false)
|
||||
expect(areDetectedWorktreeResultsEqual(failed('distro gone'), failed())).toBe(false)
|
||||
})
|
||||
|
||||
it('survives the host merge when only the reason changed', () => {
|
||||
const merged = mergeDetectedWorktreesForHost(
|
||||
failed('distro gone'),
|
||||
failed('mount hung'),
|
||||
'local'
|
||||
)
|
||||
|
||||
expect(merged.unavailableReason).toBe('mount hung')
|
||||
})
|
||||
})
|
||||
@@ -1,31 +0,0 @@
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { createFallbackProxy } from './web-fallback-api'
|
||||
|
||||
describe('web fallback proxy', () => {
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks()
|
||||
})
|
||||
|
||||
it('warns once per unimplemented namespace and still returns the default result', async () => {
|
||||
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {})
|
||||
type Api = { first: () => Promise<unknown>; second: () => Promise<unknown> }
|
||||
const missing: Api = createFallbackProxy(['missingNamespace'])
|
||||
const other: Api = createFallbackProxy(['otherMissingNamespace'])
|
||||
|
||||
await expect(missing.first()).resolves.toBeUndefined()
|
||||
await missing.second()
|
||||
await other.first()
|
||||
|
||||
expect(warn).toHaveBeenCalledTimes(2)
|
||||
expect(warn.mock.calls[0]?.[0]).toContain('window.api.missingNamespace.first')
|
||||
expect(warn.mock.calls[1]?.[0]).toContain('window.api.otherMissingNamespace.first')
|
||||
})
|
||||
|
||||
it('does not warn when a namespace routes calls through an override', () => {
|
||||
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {})
|
||||
const routed: { call: () => unknown } = createFallbackProxy(['routedNamespace'], () => 'ok')
|
||||
|
||||
expect(routed.call()).toBe('ok')
|
||||
expect(warn).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
@@ -1,38 +0,0 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { PreloadApi } from '../../../preload/api-types'
|
||||
|
||||
// The fallback proxy answers then; returning it from an async helper would never settle.
|
||||
function installedApi(): PreloadApi {
|
||||
return (globalThis as unknown as { window: { api: PreloadApi } }).window.api
|
||||
}
|
||||
|
||||
describe('web preload linux package recovery methods', () => {
|
||||
beforeEach(() => {
|
||||
vi.resetModules()
|
||||
vi.stubGlobal('window', {
|
||||
localStorage: { getItem: () => null, setItem: () => {} },
|
||||
location: { protocol: 'http:' },
|
||||
addEventListener: vi.fn(),
|
||||
removeEventListener: vi.fn()
|
||||
})
|
||||
vi.stubGlobal('navigator', { userAgent: 'Linux', hardwareConcurrency: 8 })
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
vi.unstubAllGlobals()
|
||||
})
|
||||
|
||||
it('reports both package recovery actions as desktop-only', async () => {
|
||||
const { installWebPreloadApi } = await import('./web-preload-api')
|
||||
installWebPreloadApi()
|
||||
const api = installedApi()
|
||||
|
||||
// The recovery status can only originate in native main, so a web caller must fail loudly.
|
||||
await expect(api.updater.getLinuxPackageInstallInstructions()).rejects.toThrow(
|
||||
'only available in the desktop app'
|
||||
)
|
||||
await expect(api.updater.showLinuxPackage()).rejects.toThrow(
|
||||
'only available in the desktop app'
|
||||
)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user