mirror of
https://github.com/stablyai/orca.git
synced 2026-11-14 01:47:24 +00:00
test: remove low-value intake, authoring, and picker suites (#40622)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -4682,7 +4682,6 @@
|
||||
|
||||
"src/renderer/src/components/automations/ExternalAutomationManagers.test.tsx": 852,
|
||||
"src/renderer/src/components/automations/hosts/automation-captured-owner.test.ts": 379,
|
||||
"src/renderer/src/components/automations/authoring/automation-create-projects.test.ts": 42,
|
||||
"src/renderer/src/components/automations/automation-host-cache-controller.test.ts": 411,
|
||||
"src/renderer/src/components/automations/hosts/automation-host-cache-error.test.ts": 391,
|
||||
"src/renderer/src/components/automations/hosts/automation-host-cache-health.test.ts": 52,
|
||||
@@ -4702,7 +4701,6 @@
|
||||
"src/renderer/src/components/automations/listing/automation-list-focus-recovery.test.ts": 48,
|
||||
|
||||
"src/renderer/src/components/automations/automation-list-view.test.ts": 257,
|
||||
"src/renderer/src/components/automations/authoring/automation-run-context.test.ts": 58,
|
||||
"src/renderer/src/components/automations/runs/automation-run-history-keyboard-navigation.test.ts": 434,
|
||||
"src/renderer/src/components/automations/runs/automation-run-open-target.test.ts": 442,
|
||||
"src/renderer/src/components/automations/automation-run-output-snapshot-equivalence.test.ts": 158,
|
||||
@@ -5231,7 +5229,6 @@
|
||||
"src/renderer/src/components/native-chat/composer/focus/native-chat-composer-reveal-focus.test.tsx": 526,
|
||||
"src/renderer/src/components/native-chat/attachments/intake/native-chat-composer-workspace-file-drop.test.tsx": 1166,
|
||||
"src/renderer/src/components/native-chat/drafts/native-chat-draft-retention.test.ts": 1415,
|
||||
"src/renderer/src/components/native-chat/attachments/intake/native-chat-element-file-drop.test.tsx": 2057,
|
||||
"src/renderer/src/components/native-chat/attachments/intake/native-chat-external-attachments-destination.test.tsx": 951,
|
||||
"src/renderer/src/components/native-chat/links/actions/native-chat-file-link-existence.test.ts": 1937,
|
||||
"src/renderer/src/components/native-chat/pane/actions/native-chat-header-ownership.test.tsx": 2796,
|
||||
@@ -5246,7 +5243,6 @@
|
||||
"src/renderer/src/components/native-chat/native-chat-new-rows-marker.test.tsx": 581,
|
||||
"src/renderer/src/components/native-chat/pane/routing/native-chat-open-wake.test.ts": 1807,
|
||||
"src/renderer/src/components/native-chat/pane/ownership/native-chat-owner-selectors.test.tsx": 525,
|
||||
"src/renderer/src/components/native-chat/attachments/intake/native-chat-pane-file-drop.test.tsx": 439,
|
||||
"src/renderer/src/components/native-chat/paste/native-chat-paste-routing.test.tsx": 2417,
|
||||
"src/renderer/src/components/native-chat/session/terminal/delivery/native-chat-pending.test.ts": 273,
|
||||
"src/renderer/src/components/native-chat/composer/editor/native-chat-prompt-document-text.test.ts": 219,
|
||||
|
||||
@@ -67,10 +67,7 @@ function Form({ query, onPick }: { query: string; onPick: (draft?: AgentPickerDr
|
||||
)
|
||||
}
|
||||
|
||||
it.each([
|
||||
['an agent row', ''],
|
||||
['a search-result row', 'astra low']
|
||||
])(
|
||||
it.each([['a search-result row', 'astra low']])(
|
||||
'applies each model and effort picked in %s setup to the form and keeps the panel on them',
|
||||
(_row, query) => {
|
||||
const onPick = vi.fn()
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import type { Repo } from '../../../../../shared/repo-types'
|
||||
import { getAutomationCreateRepos } from './automation-create-projects'
|
||||
|
||||
function repo(id: string, executionHostId: Repo['executionHostId']): Repo {
|
||||
return {
|
||||
id,
|
||||
path: `/${id}`,
|
||||
displayName: id,
|
||||
badgeColor: '#000000',
|
||||
addedAt: 1,
|
||||
executionHostId
|
||||
}
|
||||
}
|
||||
|
||||
describe('getAutomationCreateRepos', () => {
|
||||
const repos = [
|
||||
repo('local', 'local'),
|
||||
repo('remote', 'runtime:env-1'),
|
||||
repo('ssh', 'ssh:builder')
|
||||
]
|
||||
|
||||
it('selects only the destination runtime catalog', () => {
|
||||
expect(
|
||||
getAutomationCreateRepos(repos, { kind: 'environment', environmentId: 'env-1' })
|
||||
).toEqual([repos[1]])
|
||||
})
|
||||
|
||||
it('keeps local and direct SSH rows but excludes paired-runtime rows', () => {
|
||||
expect(getAutomationCreateRepos(repos, { kind: 'local' })).toEqual([repos[0], repos[2]])
|
||||
})
|
||||
|
||||
it('fails closed when the destination runtime has no project', () => {
|
||||
expect(
|
||||
getAutomationCreateRepos(repos, { kind: 'environment', environmentId: 'missing' })
|
||||
).toEqual([])
|
||||
})
|
||||
})
|
||||
@@ -1,79 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import type { ProjectHostSetup } from '../../../../../shared/project-types'
|
||||
import type { Repo } from '../../../../../shared/repo-types'
|
||||
import { buildAutomationRunContextForRepo } from './automation-run-context'
|
||||
|
||||
function repo(id: string, path = `/repos/${id}`, executionHostId?: Repo['executionHostId']): Repo {
|
||||
return {
|
||||
id,
|
||||
path,
|
||||
displayName: id,
|
||||
badgeColor: '#000000',
|
||||
addedAt: 1,
|
||||
executionHostId
|
||||
}
|
||||
}
|
||||
|
||||
function setup(overrides: Partial<ProjectHostSetup> = {}): ProjectHostSetup {
|
||||
return {
|
||||
id: 'setup-builder',
|
||||
projectId: 'github:stablyai/orca',
|
||||
hostId: 'ssh:builder',
|
||||
repoId: 'repo-builder',
|
||||
path: '/remote/orca',
|
||||
displayName: 'orca',
|
||||
setupState: 'ready',
|
||||
setupMethod: 'cloned',
|
||||
createdAt: 1,
|
||||
updatedAt: 1,
|
||||
...overrides
|
||||
}
|
||||
}
|
||||
|
||||
describe('buildAutomationRunContextForRepo', () => {
|
||||
it('persists logical project and host setup identity for the selected run repo', () => {
|
||||
expect(
|
||||
buildAutomationRunContextForRepo({
|
||||
repoId: 'repo-builder',
|
||||
repos: [
|
||||
repo('repo-local', '/local/orca'),
|
||||
repo('repo-builder', '/remote/orca', 'ssh:builder')
|
||||
],
|
||||
projectHostSetups: [
|
||||
setup({
|
||||
id: 'setup-local',
|
||||
hostId: 'local',
|
||||
repoId: 'repo-local',
|
||||
path: '/local/orca'
|
||||
}),
|
||||
setup()
|
||||
]
|
||||
})
|
||||
).toEqual({
|
||||
kind: 'workspace-run',
|
||||
projectId: 'github:stablyai/orca',
|
||||
hostId: 'ssh:builder',
|
||||
projectHostSetupId: 'setup-builder',
|
||||
repoId: 'repo-builder',
|
||||
path: '/remote/orca'
|
||||
})
|
||||
})
|
||||
|
||||
it('does not build a run context for missing or not-ready setups', () => {
|
||||
expect(
|
||||
buildAutomationRunContextForRepo({
|
||||
repoId: 'repo-builder',
|
||||
repos: [repo('repo-builder')],
|
||||
projectHostSetups: [setup({ hostId: 'local', setupState: 'setting-up' })]
|
||||
})
|
||||
).toBeNull()
|
||||
|
||||
expect(
|
||||
buildAutomationRunContextForRepo({
|
||||
repoId: 'repo-builder',
|
||||
repos: [],
|
||||
projectHostSetups: [setup()]
|
||||
})
|
||||
).toBeNull()
|
||||
})
|
||||
})
|
||||
-165
@@ -1,165 +0,0 @@
|
||||
// @vitest-environment happy-dom
|
||||
import { act, cleanup, fireEvent, render } from '@testing-library/react'
|
||||
import { useRef, useState } from 'react'
|
||||
import { createPortal } from 'react-dom'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { NativeChatComposerInput } from '../../composer/editor/native-chat-composer-input'
|
||||
import {
|
||||
NativeChatPaneFileDropSurface,
|
||||
useNativeChatPaneFileDropClaim
|
||||
} from './NativeChatPaneFileDropSurface'
|
||||
import { NativeChatPromptEditor } from '../../composer/editor/NativeChatPromptEditor'
|
||||
|
||||
const electron = vi.hoisted(() => ({
|
||||
on: vi.fn(),
|
||||
removeListener: vi.fn(),
|
||||
send: vi.fn(),
|
||||
getPathForFile: vi.fn((file: File) => `/drop/${file.name}`)
|
||||
}))
|
||||
vi.mock('electron', () => ({
|
||||
ipcRenderer: electron,
|
||||
webUtils: { getPathForFile: electron.getPathForFile }
|
||||
}))
|
||||
vi.mock('sonner', () => ({ toast: { error: vi.fn() } }))
|
||||
vi.mock('@/i18n/i18n', () => ({ translate: (_key: string, fallback: string) => fallback }))
|
||||
|
||||
const prepare = vi.fn(async ({ paths }: { paths: string[] }) => ({ paths, failures: [] }))
|
||||
function Composer({
|
||||
attach,
|
||||
disabled = false
|
||||
}: {
|
||||
attach: (paths: string[]) => void
|
||||
disabled?: boolean
|
||||
}) {
|
||||
const input = useRef<NativeChatComposerInput>(null)
|
||||
useNativeChatPaneFileDropClaim({
|
||||
destinationKey: 'draft',
|
||||
disabled,
|
||||
onDragOverCapture: () => {},
|
||||
onDropCapture: () => {},
|
||||
captureExternalDrop: () => async (paths) => attach(paths)
|
||||
})
|
||||
return (
|
||||
<NativeChatPromptEditor
|
||||
scopeKey="draft"
|
||||
inputRef={input}
|
||||
initialValue="Original"
|
||||
disabled={disabled}
|
||||
placeholder="Message"
|
||||
onChange={() => {}}
|
||||
onSelect={() => {}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
function Chat({
|
||||
attach,
|
||||
hidden = false,
|
||||
disabled = false
|
||||
}: {
|
||||
attach: (paths: string[]) => void
|
||||
hidden?: boolean
|
||||
disabled?: boolean
|
||||
}) {
|
||||
return (
|
||||
<div style={{ display: hidden ? 'none' : 'block' }}>
|
||||
<NativeChatPaneFileDropSurface className="chat">
|
||||
<Composer attach={attach} disabled={disabled} />
|
||||
</NativeChatPaneFileDropSurface>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
async function drop(target: Element, types = ['Files']) {
|
||||
const event = new Event('drop', { bubbles: true, cancelable: true, composed: true })
|
||||
Object.defineProperty(event, 'isTrusted', { value: true })
|
||||
Object.defineProperty(event, 'dataTransfer', {
|
||||
value: {
|
||||
types,
|
||||
files: [new File(['x'], 'a.png')],
|
||||
getData: (type: string) => (type === 'text/html' ? '<b>Injected</b>' : '')
|
||||
}
|
||||
})
|
||||
await act(async () => {
|
||||
target.dispatchEvent(event)
|
||||
})
|
||||
return event
|
||||
}
|
||||
function dragEvent(target: Element, type: string, transfer: { dropEffect: string }): Event {
|
||||
const event = new Event(type, { bubbles: true, cancelable: true, composed: true })
|
||||
Object.defineProperty(event, 'isTrusted', { value: true })
|
||||
Object.defineProperty(event, 'dataTransfer', { value: transfer })
|
||||
target.dispatchEvent(event)
|
||||
return event
|
||||
}
|
||||
function negotiateAndRelease(target: Element) {
|
||||
const transfer = { types: ['Files'], files: [new File(['x'], 'a.png')], dropEffect: 'move' }
|
||||
const hover = dragEvent(target, 'dragover', transfer)
|
||||
const release = dragEvent(target, transfer.dropEffect === 'none' ? 'dragleave' : 'drop', transfer)
|
||||
return { hover, release, transfer }
|
||||
}
|
||||
function AvailabilityComposer({ attach }: { attach: (paths: string[]) => void }) {
|
||||
const [disabled, setDisabled] = useState(true)
|
||||
return (
|
||||
<>
|
||||
<button onClick={() => setDisabled((value) => !value)}>Toggle availability</button>
|
||||
<Composer attach={attach} disabled={disabled} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
vi.stubGlobal('api', {
|
||||
fs: { getPathForFile: electron.getPathForFile, prepareDroppedPaths: prepare }
|
||||
})
|
||||
})
|
||||
afterEach(() => {
|
||||
cleanup()
|
||||
vi.unstubAllGlobals()
|
||||
})
|
||||
|
||||
describe('element-owned native chat drops', () => {
|
||||
it('claims a portaled chat while refusing the unowned container around it', async () => {
|
||||
const attach = vi.fn()
|
||||
const terminal = document.createElement('div')
|
||||
document.body.append(terminal)
|
||||
const terminalDrop = vi.fn()
|
||||
terminal.addEventListener('drop', terminalDrop)
|
||||
try {
|
||||
render(createPortal(<Chat attach={attach} />, terminal))
|
||||
await drop(terminal.querySelector('.ProseMirror')!)
|
||||
expect(attach).toHaveBeenCalledExactlyOnceWith(['/drop/a.png'])
|
||||
expect(terminalDrop).not.toHaveBeenCalled()
|
||||
expect(electron.send).not.toHaveBeenCalled()
|
||||
await drop(terminal)
|
||||
expect(electron.send).not.toHaveBeenCalled()
|
||||
expect(attach).toHaveBeenCalledOnce()
|
||||
} finally {
|
||||
terminal.remove()
|
||||
}
|
||||
})
|
||||
it('reads child-only availability changes on the immediate dragover and drop', async () => {
|
||||
const attach = vi.fn()
|
||||
const view = render(
|
||||
<NativeChatPaneFileDropSurface className="chat">
|
||||
<AvailabilityComposer attach={attach} />
|
||||
</NativeChatPaneFileDropSurface>
|
||||
)
|
||||
const target = view.container.querySelector('.ProseMirror')!
|
||||
const refused = negotiateAndRelease(target)
|
||||
expect(refused.transfer.dropEffect).toBe('none')
|
||||
expect(prepare).not.toHaveBeenCalled()
|
||||
fireEvent.click(view.getByText('Toggle availability'))
|
||||
let accepted: ReturnType<typeof negotiateAndRelease> | undefined
|
||||
await act(async () => {
|
||||
accepted = negotiateAndRelease(target)
|
||||
})
|
||||
expect(accepted?.transfer.dropEffect).toBe('copy')
|
||||
expect(accepted?.release.type).toBe('drop')
|
||||
expect(attach).toHaveBeenCalledExactlyOnceWith(['/drop/a.png'])
|
||||
fireEvent.click(view.getByText('Toggle availability'))
|
||||
await drop(target)
|
||||
expect(attach).toHaveBeenCalledOnce()
|
||||
expect(prepare).toHaveBeenCalledOnce()
|
||||
expect(electron.send).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
-121
@@ -1,121 +0,0 @@
|
||||
// @vitest-environment happy-dom
|
||||
|
||||
import type * as AttachmentOwnerModule from '../uploads/native-chat-attachment-owner'
|
||||
import { useState } from 'react'
|
||||
import { afterEach, expect, it, vi } from 'vitest'
|
||||
import { act, cleanup, fireEvent, render } from '@testing-library/react'
|
||||
|
||||
import { WORKSPACE_FILE_PATH_MIME } from '@/lib/workspace-file-drag'
|
||||
import {
|
||||
NativeChatPaneFileDropSurface,
|
||||
useNativeChatPaneFileDropClaim
|
||||
} from './NativeChatPaneFileDropSurface'
|
||||
|
||||
vi.mock('@/i18n/i18n', () => ({ translate: (_key: string, fallback: string) => fallback }))
|
||||
vi.mock('../../../../store/index', () => ({ useAppStore: { getState: () => ({}) } }))
|
||||
vi.mock('../uploads/native-chat-attachment-owner', async (original) => ({
|
||||
...(await original<typeof AttachmentOwnerModule>()),
|
||||
resolveNativeChatAttachmentOwnerForWorktree: () => ({ kind: 'local' })
|
||||
}))
|
||||
|
||||
class DropDataTransfer {
|
||||
dropEffect = 'none'
|
||||
effectAllowed = 'all'
|
||||
private readonly data = new Map<string, string>()
|
||||
get types(): string[] {
|
||||
return [...this.data.keys()]
|
||||
}
|
||||
setData(type: string, value: string): void {
|
||||
this.data.set(type, value)
|
||||
}
|
||||
getData(type: string): string {
|
||||
return this.data.get(type) ?? ''
|
||||
}
|
||||
}
|
||||
|
||||
function workspaceDrag(): DropDataTransfer {
|
||||
const transfer = new DropDataTransfer()
|
||||
transfer.setData(WORKSPACE_FILE_PATH_MIME, '/repo/a.ts')
|
||||
return transfer
|
||||
}
|
||||
|
||||
function fireDrag(
|
||||
target: HTMLElement,
|
||||
type: 'dragenter' | 'dragover' | 'dragleave' | 'drop',
|
||||
dataTransfer: DropDataTransfer,
|
||||
relatedTarget: EventTarget | null = null
|
||||
): void {
|
||||
const event = new Event(type, { bubbles: true, cancelable: true })
|
||||
Object.defineProperty(event, 'dataTransfer', { value: dataTransfer })
|
||||
Object.defineProperty(event, 'relatedTarget', { value: relatedTarget })
|
||||
act(() => {
|
||||
target.dispatchEvent(event)
|
||||
})
|
||||
}
|
||||
|
||||
afterEach(cleanup)
|
||||
|
||||
it('direct drops override last focus; background keeps stable focus through disable and remount, with questions independent', () => {
|
||||
function Composer({ scope, disabled = false }: { scope: string; disabled?: boolean }) {
|
||||
const [value, setValue] = useState('')
|
||||
useNativeChatPaneFileDropClaim({
|
||||
destinationKey: scope,
|
||||
focusScopeKey: scope,
|
||||
disabled,
|
||||
captureExternalDrop: () => async () => {},
|
||||
onDragOverCapture: (event) => {
|
||||
if (!disabled) {
|
||||
event.preventDefault()
|
||||
}
|
||||
},
|
||||
onDropCapture: (event) => {
|
||||
if (!disabled) {
|
||||
setValue((previous) => previous + event.dataTransfer.getData(WORKSPACE_FILE_PATH_MIME))
|
||||
}
|
||||
}
|
||||
})
|
||||
return (
|
||||
<div data-native-chat-composer-scope={scope}>
|
||||
<textarea aria-label={scope} value={value} readOnly disabled={disabled} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
function Pane({ disabled = false, revision = 0 }: { disabled?: boolean; revision?: number }) {
|
||||
return (
|
||||
<NativeChatPaneFileDropSurface className="pane">
|
||||
<span>background</span>
|
||||
<input aria-label="question" />
|
||||
<Composer scope="bottom" />
|
||||
<Composer key={revision} scope="queued-edit:row" disabled={disabled} />
|
||||
</NativeChatPaneFileDropSurface>
|
||||
)
|
||||
}
|
||||
const view = render(<Pane />)
|
||||
const input = (label: string): HTMLTextAreaElement => {
|
||||
const element = view.getByLabelText(label)
|
||||
if (!(element instanceof HTMLTextAreaElement)) {
|
||||
throw new Error('expected composer textarea')
|
||||
}
|
||||
return element
|
||||
}
|
||||
const bottom = () => input('bottom')
|
||||
const edit = () => input('queued-edit:row')
|
||||
fireEvent.focus(edit())
|
||||
fireDrag(bottom(), 'drop', workspaceDrag())
|
||||
expect(bottom().value).toBe('/repo/a.ts')
|
||||
expect(edit().value).toBe('')
|
||||
fireDrag(view.getByText('background'), 'drop', workspaceDrag())
|
||||
expect(edit().value).toBe('/repo/a.ts')
|
||||
fireEvent.focus(view.getByLabelText('question'))
|
||||
fireDrag(view.getByText('background'), 'drop', workspaceDrag())
|
||||
expect(edit().value).toBe('/repo/a.ts/repo/a.ts')
|
||||
view.rerender(<Pane disabled />)
|
||||
fireDrag(edit(), 'drop', workspaceDrag())
|
||||
expect(bottom().value).toBe('/repo/a.ts')
|
||||
view.rerender(<Pane />)
|
||||
fireDrag(view.getByText('background'), 'drop', workspaceDrag())
|
||||
expect(edit().value).toBe('/repo/a.ts/repo/a.ts/repo/a.ts')
|
||||
view.rerender(<Pane revision={1} />)
|
||||
fireDrag(view.getByText('background'), 'drop', workspaceDrag())
|
||||
expect(edit().value).toBe('/repo/a.ts')
|
||||
})
|
||||
@@ -1,68 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { buildNewTabAgentMenuRows, type HostChatAnswer } from './agent-launch-picker-rows'
|
||||
import type { TuiAgent } from '../catalog/tui-agent'
|
||||
|
||||
function rows(input: {
|
||||
surface: 'chat' | 'terminal'
|
||||
detected: TuiAgent[]
|
||||
answer?: HostChatAnswer
|
||||
offerAgentSetup?: boolean
|
||||
hostPlatform?: string | null
|
||||
chatAgents?: TuiAgent[]
|
||||
}) {
|
||||
return buildNewTabAgentMenuRows({
|
||||
settings: { agentDefaultSurface: input.surface },
|
||||
detectedOnHost: input.detected,
|
||||
chatAgents: input.chatAgents ?? ['claude', 'codex'],
|
||||
hostChatAnswer: () => input.answer ?? 'yes',
|
||||
chatProgram: (agent) => (input.detected.includes(agent) ? 'available' : 'missing'),
|
||||
offerAgentSetup: input.offerAgentSetup,
|
||||
hostPlatform: input.hostPlatform === undefined ? 'linux' : input.hostPlatform
|
||||
}).map((row) => `${row.kind}:${row.agent}`)
|
||||
}
|
||||
|
||||
describe('new-tab rows for agents the host lacks', () => {
|
||||
it('opens setup chats for missing agents when chat is preferred', () => {
|
||||
expect(rows({ surface: 'chat', detected: ['claude'], offerAgentSetup: true })).toEqual([
|
||||
'chat:claude',
|
||||
'chat:codex'
|
||||
])
|
||||
expect(rows({ surface: 'chat', detected: ['claude'] })).toEqual([
|
||||
'chat:claude',
|
||||
'chat-unavailable:codex'
|
||||
])
|
||||
})
|
||||
|
||||
it('offers setup chats to a terminal user only when the host has no agents', () => {
|
||||
expect(rows({ surface: 'terminal', detected: [], offerAgentSetup: true })).toEqual([
|
||||
'chat:claude',
|
||||
'chat:codex'
|
||||
])
|
||||
expect(rows({ surface: 'terminal', detected: ['claude'], offerAgentSetup: true })).toEqual([
|
||||
'cli:claude'
|
||||
])
|
||||
})
|
||||
|
||||
it('keeps hosts without chat to their own reason', () => {
|
||||
expect(rows({ surface: 'chat', detected: [], answer: 'ssh', offerAgentSetup: true })).toEqual([
|
||||
'chat-unavailable:claude',
|
||||
'chat-unavailable:codex'
|
||||
])
|
||||
})
|
||||
|
||||
it('does not open setup for a missing agent without an installer', () => {
|
||||
expect(
|
||||
rows({ surface: 'chat', detected: [], offerAgentSetup: true, chatAgents: ['grok'] })
|
||||
).toEqual(['chat-unavailable:grok'])
|
||||
expect(
|
||||
rows({ surface: 'terminal', detected: [], offerAgentSetup: true, chatAgents: ['grok'] })
|
||||
).toEqual([])
|
||||
})
|
||||
|
||||
it.each(['win32', null])('does not offer an installer on host platform %s', (hostPlatform) => {
|
||||
expect(rows({ surface: 'chat', detected: [], offerAgentSetup: true, hostPlatform })).toEqual([
|
||||
'chat-unavailable:claude',
|
||||
'chat-unavailable:codex'
|
||||
])
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user