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:
OrcaWin
2026-11-09 20:24:13 -08:00
committed by GitHub
co-authored by OrcaWin
parent d78a9b1e9a
commit 0dc718bd5f
8 changed files with 21 additions and 494 deletions
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()
})
})
@@ -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()
})
})
@@ -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'
])
})
})