test: remove 104 low-value test declarations (#37290)

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
OrcaWin
2026-11-01 19:23:32 -08:00
committed by GitHub
co-authored by OrcaWin
parent 347e9ca5d3
commit 263d3b16aa
32 changed files with 4 additions and 1545 deletions
@@ -1,7 +1,6 @@
import { describe, expect, it } from 'vitest'
import { parseHostPluginManifest, parsePluginManifest } from '../manifest/plugin-manifest'
import { parsePluginManifest } from '../manifest/plugin-manifest'
import { pluginSettingValueIssue } from './plugin-settings-contribution'
import { pluginSettingDefaults } from './plugin-settings-values'
function manifest(settings: unknown[], capabilities = [{ kind: 'settings:own' }]) {
return {
@@ -29,36 +28,6 @@ function issues(raw: unknown): string {
}
describe('contributes.settings', () => {
it('accepts a typed setting list and reads defaults from it', () => {
const parsed = parseHostPluginManifest(
manifest([
apiUrl,
{
key: 'pageSize',
title: 'Page size',
type: 'integer',
default: 20,
minimum: 1,
maximum: 100
},
{
key: 'mode',
title: 'Mode',
type: 'string',
enum: ['fast', 'safe'],
enumLabels: ['Fast', 'Safe']
}
])
)
expect(parsed.ok).toBe(true)
if (parsed.ok) {
expect(pluginSettingDefaults(parsed.manifest.contributes.settings)).toEqual({
apiUrl: 'https://example.com',
pageSize: 20
})
}
})
it('needs settings:own, unique keys, and defaults that fit the setting', () => {
expect(issues(manifest([apiUrl], []))).toContain('settings:own')
expect(issues(manifest([apiUrl, apiUrl]))).toContain('duplicate setting key')
@@ -1,13 +1,5 @@
import { describe, expect, it } from 'vitest'
import {
isPluginDeclarativeComponentName,
PLUGIN_DECLARATIVE_CATALOG
} from './plugin-declarative-catalog'
import { isPluginDeclarativeImagePath } from './plugin-declarative-content-props'
import {
PLUGIN_DECLARATIVE_EDIT_PROP,
PLUGIN_DECLARATIVE_VALUE_CONTROLS
} from './plugin-declarative-message-names'
import { PLUGIN_DECLARATIVE_LIMITS as LIMITS } from './plugin-declarative-limits'
import { parsePluginDeclarativeTree } from './plugin-declarative-tree'
@@ -20,51 +12,6 @@ const parse = (raw: unknown) => parsePluginDeclarativeTree(raw)
const columns = [{ key: 'name', label: 'Name', sortable: true }]
describe('catalog 2 components', () => {
it('gives every value control its value prop, change callback and edit tag', () => {
for (const [type, { value, change }] of Object.entries(PLUGIN_DECLARATIVE_VALUE_CONTROLS)) {
expect(isPluginDeclarativeComponentName(type)).toBe(true)
const shape: Record<string, unknown> = isPluginDeclarativeComponentName(type)
? PLUGIN_DECLARATIVE_CATALOG[type].props.shape
: {}
expect({
type,
props: [value, change, PLUGIN_DECLARATIVE_EDIT_PROP].filter((p) => p in shape)
}).toEqual({ type, props: [value, change, PLUGIN_DECLARATIVE_EDIT_PROP] })
}
})
it('accepts every new component in its place', () => {
const result = parse(
node('Stack', {}, [
node('Field', { label: 'Notes', error: 'Required' }, [
node('Textarea', { label: 'Notes', rows: 3 })
]),
node('Checkbox', { label: 'Agree', defaultChecked: true }),
node('RadioGroup', { label: 'Size', options: [{ value: 's', label: 'Small' }] }),
node('Slider', { label: 'Volume', min: 0, max: 10, step: 0.5 }),
node('Tabs', { label: 'Views', defaultValue: 'a' }, [
node('Tab', { value: 'a', label: 'A' }, [node('Text', {}, ['first'])]),
node('Tab', { value: 'b', label: 'B' })
]),
node('Collapsible', { title: 'More' }, [node('Text', {}, ['hidden'])]),
node('Toolbar', { label: 'Actions' }, [node('Button', { tooltip: 'Run it' }, ['Run'])]),
node('Grid', { columns: 3 }, [node('Card', { title: 'One' })]),
node('Table', { label: 'Files', columns, rows: [{ key: 'r1', cells: { name: 'a.ts' } }] }),
node('Tree', {
label: 'Folders',
items: [{ key: 'src', label: 'src', children: [{ key: 'src/a', label: 'a' }] }]
}),
node('Markdown', { content: '# Hi\n\n[docs](https://example.com)' }),
node('CodeView', { code: 'let a = 1', language: 'typescript', lineNumbers: true }),
node('Image', { src: 'assets/logo.png', alt: 'Logo' }),
node('KeyValue', { items: [{ label: 'Branch', value: 'main', code: true }] }),
node('Callout', { tone: 'warning', title: 'Heads up' }, ['Careful']),
node('Skeleton', { lines: 2 })
])
)
expect(result).toMatchObject({ ok: true, warnings: [] })
})
it('keeps Tab inside Tabs and Tabs holding only Tab', () => {
expect(parse(node('Stack', {}, [node('Tab', { value: 'a', label: 'A' })]))).toMatchObject({
ok: false,
@@ -80,15 +27,6 @@ describe('catalog 2 components', () => {
})
})
it('keeps new controls out of a pressable ListItem', () => {
const result = parse(
node('List', {}, [
node('ListItem', { onPress: { $callback: 'c1' } }, [node('Checkbox', { label: 'x' })])
])
)
expect(result).toMatchObject({ ok: false, error: expect.stringContaining('Checkbox') })
})
it('draws a placeholder for a Table or Tree with unusable data, and keeps the rest', () => {
const duplicateRows = node('Table', {
label: 'T',
@@ -1,58 +0,0 @@
import { describe, expect, it } from 'vitest'
import { parsePluginDeclarativeTree } from './plugin-declarative-tree'
import {
formatPluginRelativeTime,
pluginRelativeTimeParts,
pluginRelativeTimeTickMs
} from './plugin-declarative-relative-time'
const NOW = Date.UTC(2026, 10, 1, 12)
describe('relative time', () => {
it('picks the largest whole unit and words it in each format', () => {
expect(pluginRelativeTimeParts(-45_000)).toEqual({ value: -45, unit: 'second' })
expect(pluginRelativeTimeParts(-5 * 60_000 - 10)).toEqual({ value: -5, unit: 'minute' })
expect(pluginRelativeTimeParts(3 * 86_400_000)).toEqual({ value: 3, unit: 'day' })
const at = NOW - 5 * 60_000
expect(formatPluginRelativeTime(at, NOW, 'short', 'en')).toBe('5m ago')
expect(formatPluginRelativeTime(at, NOW, 'long', 'en')).toBe('5 minutes ago')
expect(formatPluginRelativeTime(at, NOW, 'duration', 'en')).toBe('5m')
expect(formatPluginRelativeTime(NOW + 2 * 3_600_000, NOW, 'long', 'en')).toBe('in 2 hours')
})
it('ticks often only while the words change often', () => {
expect(pluginRelativeTimeTickMs(-10_000)).toBe(1000)
expect(pluginRelativeTimeTickMs(-10 * 60_000)).toBe(15_000)
expect(pluginRelativeTimeTickMs(-5 * 3_600_000)).toBe(300_000)
})
it('accepts RelativeTime inside Text and relative Table cells', () => {
const parsed = parsePluginDeclarativeTree({
type: 'Stack',
props: {},
children: [
{
type: 'Text',
props: {},
children: ['Started ', { type: 'RelativeTime', props: { at: NOW, format: 'long' } }]
},
{
type: 'Table',
props: {
label: 'Agents',
columns: [{ key: 'since', label: 'Since' }],
rows: [
{ key: 'a', cells: { since: { type: 'relative', at: NOW, format: 'duration' } } }
]
}
}
]
})
expect(parsed).toMatchObject({ ok: true, warnings: [] })
const bad = parsePluginDeclarativeTree({
type: 'RelativeTime',
props: { at: Number.POSITIVE_INFINITY }
})
expect(bad.ok && bad.tree?.type).toBe('Unsupported')
})
})
@@ -1,13 +1,5 @@
import { describe, expect, it } from 'vitest'
import {
PLUGIN_DECLARATIVE_CATALOG,
PLUGIN_DECLARATIVE_COMPONENT_NAMES
} from './plugin-declarative-catalog'
import { PLUGIN_DECLARATIVE_LIMITS } from './plugin-declarative-limits'
import {
PLUGIN_DECLARATIVE_COMPONENT_PROPS,
PLUGIN_DECLARATIVE_EDIT_PROP
} from './plugin-declarative-message-names'
import {
parsePluginDeclarativeRender,
pluginDeclarativeEventPayloadSchema
@@ -26,47 +18,6 @@ function errorOf(tree: unknown): string {
}
describe('parsePluginDeclarativeTree', () => {
it('keeps the zod catalog and the runtime prop table in step', () => {
expect([...PLUGIN_DECLARATIVE_COMPONENT_NAMES].sort()).toEqual(
Object.keys(PLUGIN_DECLARATIVE_COMPONENT_PROPS).sort()
)
for (const name of PLUGIN_DECLARATIVE_COMPONENT_NAMES) {
const zodProps = Object.keys(PLUGIN_DECLARATIVE_CATALOG[name].props.shape).filter(
(prop) => prop !== PLUGIN_DECLARATIVE_EDIT_PROP
)
const runtimeProps: readonly string[] = PLUGIN_DECLARATIVE_COMPONENT_PROPS[name]
expect({ name, props: [...runtimeProps].sort() }).toEqual({ name, props: zodProps.sort() })
}
})
it('accepts a valid tree, normalizes numbers and drops nothing it knows', () => {
const code = node('Code', {}, ['x'])
const text = node('Text', { tone: 'accent', muted: true }, ['Count ', 4, code])
const rest = [
node('Button', { onPress: { $callback: 'c1' }, icon: 'add', tone: 'danger' }, ['Add']),
node('List', { label: 'Items' }, [{ ...node('ListItem', { title: 'A' }), key: 'a' }]),
node('Select', {
label: 'Pick',
options: [{ value: 'a', label: 'A' }],
onChange: { $callback: 'c2' }
}),
node('Link', { href: 'https://example.com' }, ['docs'])
]
const tree = node('Stack', { gap: 'sm' }, [text, ...rest])
const result = parsePluginDeclarativeTree(tree)
expect(result).toMatchObject({ ok: true, nodes: 8, warnings: [] })
expect(result.ok && result.tree).toEqual({
...tree,
children: [{ ...text, children: ['Count ', '4', code] }, ...rest]
})
expect(parsePluginDeclarativeTree(null)).toEqual({
ok: true,
tree: null,
nodes: 0,
warnings: []
})
})
it('drops unknown props and enum values with a warning instead of refusing', () => {
const result = parsePluginDeclarativeTree(
node('Stack', { className: 'p-4', gap: 'huge' }, [
@@ -134,11 +85,6 @@ describe('parsePluginDeclarativeTree', () => {
expect(errorOf(tree)).toMatch(message)
})
it('allows controls inside a ListItem that has no onPress', () => {
const tree = node('List', {}, [node('ListItem', {}, [node('Button', {}, ['x'])])])
expect(parsePluginDeclarativeTree(tree).ok).toBe(true)
})
it('caps depth without recursing', () => {
let tree = node('Text')
for (let depth = 0; depth < 10_000; depth++) {
@@ -1,175 +0,0 @@
import { readFileSync } from 'node:fs'
import path from 'node:path'
import ts from 'typescript-api'
import { describe, expect, it } from 'vitest'
import {
PLUGIN_DECLARATIVE_CATALOG,
PLUGIN_DECLARATIVE_COMPONENT_NAMES,
PLUGIN_DECLARATIVE_ICONS,
PLUGIN_DECLARATIVE_TONES
} from './plugin-declarative-catalog'
import { spliceOrcaHostTypings } from './plugin-declarative-host-typings'
import * as orcaModule from './runtime/orca-module'
const DECLARATIVE = path.resolve(import.meta.dirname, '../../../../resources/plugins/declarative')
const TYPINGS = path.join(DECLARATIVE, 'orca.d.ts')
// The `orca` module spans these files; `orca plugin types` writes them as one.
const FILES = [
'orca.d.ts',
'orca-components.d.ts',
'orca-components-layout.d.ts',
'orca-components-data.d.ts'
]
function orcaModules(): ts.ModuleBlock[] {
return FILES.flatMap((file) => {
const full = path.join(DECLARATIVE, file)
const source = ts.createSourceFile(
full,
readFileSync(full, 'utf8'),
ts.ScriptTarget.Latest,
true
)
return source.statements.flatMap((statement) =>
ts.isModuleDeclaration(statement) &&
ts.isStringLiteral(statement.name) &&
statement.name.text === 'orca' &&
statement.body &&
ts.isModuleBlock(statement.body)
? [statement.body]
: []
)
})
}
const aliases = new Map<string, ts.TypeNode>()
const components = new Map<string, string>()
for (const block of orcaModules()) {
for (const statement of block.statements) {
if (ts.isTypeAliasDeclaration(statement)) {
aliases.set(statement.name.text, statement.type)
}
if (ts.isVariableStatement(statement)) {
for (const declaration of statement.declarationList.declarations) {
const type = declaration.type
if (
ts.isIdentifier(declaration.name) &&
type &&
ts.isTypeReferenceNode(type) &&
type.typeName.getText() === 'OrcaComponent' &&
type.typeArguments?.[0]
) {
components.set(declaration.name.text, type.typeArguments[0].getText())
}
}
}
}
}
/** String literals of a union, following local aliases; null when it is not a literal union. */
function literals(node: ts.TypeNode): string[] | null {
if (ts.isTypeReferenceNode(node)) {
const target = aliases.get(node.typeName.getText())
return target ? literals(target) : null
}
if (ts.isUnionTypeNode(node)) {
const parts = node.types.map(literals)
return parts.every((part) => part !== null) ? parts.flat() : null
}
if (ts.isLiteralTypeNode(node) && ts.isStringLiteral(node.literal)) {
return [node.literal.text]
}
return null
}
type Member = { optional: boolean; type: ts.TypeNode | undefined }
/** Members of a props alias such as `WithChildren & { ... }`, plus whether it is a leaf. */
function propsOf(alias: string): { members: Map<string, Member>; leaf: boolean } {
const node = aliases.get(alias)
if (!node) {
throw new Error(`orca.d.ts has no type ${alias}`)
}
const parts = ts.isIntersectionTypeNode(node) ? node.types : [node]
const members = new Map<string, Member>()
let leaf = false
for (const part of parts) {
if (ts.isTypeReferenceNode(part)) {
leaf ||= part.typeName.getText() === 'Leaf'
} else if (ts.isTypeLiteralNode(part)) {
for (const member of part.members) {
if (ts.isPropertySignature(member) && member.name) {
members.set(member.name.getText(), {
optional: member.questionToken !== undefined,
type: member.type
})
}
}
}
}
return { members, leaf }
}
type ShapeEntry = {
safeParse(value: unknown): { success: boolean }
unwrap?: () => unknown
}
function enumOptions(schema: ShapeEntry): string[] | null {
const inner: unknown = schema.unwrap ? schema.unwrap() : schema
const options: unknown =
typeof inner === 'object' && inner !== null && 'options' in inner ? inner.options : undefined
return Array.isArray(options) && options.every((option) => typeof option === 'string')
? options
: null
}
describe('orca.d.ts matches the catalog', () => {
it('declares exactly the catalog components', () => {
expect([...components.keys()].sort()).toEqual([...PLUGIN_DECLARATIVE_COMPONENT_NAMES].sort())
})
it.each(PLUGIN_DECLARATIVE_COMPONENT_NAMES)('%s props, requiredness and enums', (name) => {
const entry = PLUGIN_DECLARATIVE_CATALOG[name]
const shape: Record<string, ShapeEntry> = entry.props.shape
const { members, leaf } = propsOf(components.get(name) ?? '')
expect(leaf).toBe(entry.children === 'none')
// `$`-props are wire-only (the runtime adds them); authors never write them.
const authored = Object.entries(shape).filter(([prop]) => !prop.startsWith('$'))
expect([...members.keys()].sort()).toEqual(authored.map(([prop]) => prop).sort())
for (const [prop, schema] of authored) {
const member = members.get(prop)
expect({ prop, optional: member?.optional }).toEqual({
prop,
optional: schema.safeParse(undefined).success
})
const options = enumOptions(schema)
if (options && member?.type) {
expect({ prop, values: literals(member.type)?.sort() }).toEqual({
prop,
values: [...options].sort()
})
}
}
})
it('lists the same component names, and generates host methods from the host API table', () => {
expect(literals(aliases.get('ComponentName')!)?.sort()).toEqual(
[...PLUGIN_DECLARATIVE_COMPONENT_NAMES].sort()
)
const file = readFileSync(TYPINGS, 'utf8')
expect(spliceOrcaHostTypings(file)).toBe(file)
})
it('exports every catalog component from the guest `orca` module, as its own tag', () => {
const exported: Record<string, unknown> = { ...orcaModule }
for (const name of PLUGIN_DECLARATIVE_COMPONENT_NAMES) {
expect({ name, tag: exported[name] }).toEqual({ name, tag: name })
}
})
it('names the same tones and icons', () => {
expect(literals(aliases.get('Tone')!)?.sort()).toEqual([...PLUGIN_DECLARATIVE_TONES].sort())
expect(literals(aliases.get('IconName')!)?.sort()).toEqual([...PLUGIN_DECLARATIVE_ICONS].sort())
})
})
@@ -69,10 +69,4 @@ describe('plugin errors as logged', () => {
})
expect(describePluginError(refused, base)).toBe('src/lib/a.ts:2:19: cannot import "react"')
})
it('logs the plain stack outside a guest', () => {
const error = Object.assign(new Error('boom'), { stack: 'Error: boom\n at x (/a.js:1:1)' })
expect(describePluginErrorForLog(error, null)).toBe('Error: boom\n at x (/a.js:1:1)')
expect(describePluginErrorForLog('thrown', base)).toBe('thrown')
})
})
@@ -1,12 +1,6 @@
import { afterEach, describe, expect, it, vi } from 'vitest'
import { PLUGIN_FRAME_RESERVED_SEGMENT } from '../../frames/plugin-frame-guest'
import { atom, read, set } from './declarative-atoms'
import { h } from './declarative-elements'
import {
describePluginError,
ORCA_RESERVED_PATH_PREFIX,
pluginErrorLocation
} from './declarative-error-location'
import { createOrcaDeclarativeRuntime, type OrcaBridge } from './declarative-renderer'
function booted() {
@@ -120,32 +114,3 @@ describe('dev hot reload of keyed memory atoms', () => {
runtime.dispose()
})
})
describe('plugin error locations', () => {
const base = 'orca-plugin-frame://0123456789abcdef/'
it('names the first plugin stack frame, skipping Orca runtime frames', () => {
const stack = [
'TypeError: x is undefined',
` at evaluate (${base}__orca/orca-runtime.mjs:120:7)`,
` at Panel (${base}src/my%20panel.tsx:12:5)`,
` at ${base}src/other.ts:1:1`
].join('\n')
expect(pluginErrorLocation(stack, base)).toBe('src/my panel.tsx:12:5')
const error = Object.assign(new TypeError('x is undefined'), { stack })
expect(describePluginError(error, base)).toBe('src/my panel.tsx:12:5: x is undefined')
})
it("keeps a message that already names its file (Orca's transpile errors)", () => {
const error = Object.assign(new Error('src/panel.tsx:3:5: Unexpected token'), {
stack: `Error: src/panel.tsx:3:5: Unexpected token\n at ${base}src/panel.tsx:1:7`
})
expect(describePluginError(error, base)).toBe('src/panel.tsx:3:5: Unexpected token')
})
it('falls back to the bare message outside a guest or without a plugin frame', () => {
expect(describePluginError(new Error('boom'), null)).toBe('boom')
expect(describePluginError('thrown string', base)).toBe('thrown string')
expect(ORCA_RESERVED_PATH_PREFIX).toBe(`/${PLUGIN_FRAME_RESERVED_SEGMENT}/`)
})
})
@@ -135,14 +135,6 @@ describe('components panel lifecycle', () => {
expect(seen.signal?.aborted).toBe(true)
})
it('says clearly when the module has no component', async () => {
const { runtime, host } = booted()
host.send('ui.hydrate', {})
await expect(runtime.boot({ default: 'nope' })).rejects.toThrow(
/must export a component function as its default export \(got string\)/
)
})
it('refuses duplicate atom keys', () => {
atom('lifecycle.once', 1)
expect(() => atom('lifecycle.once', 2)).toThrow(/already used/)
@@ -89,9 +89,4 @@ describe('orca/testing and the plugin bridge', () => {
await expect(settings.get('theme')).resolves.toBe('dark')
expect(host.data().storage).toEqual({ filter: 'open' })
})
it('refuses storage without the capability, as Orca would', async () => {
panel = await mount(() => h('Text', null, 'x'), { host: mockHost({ capabilities: [] }) })
await expect(storage.get('k')).rejects.toMatchObject({ code: 'capability_denied' })
})
})
@@ -158,41 +158,3 @@ describe('orca/testing with catalog 2', () => {
expect(panel.errors).toEqual([])
})
})
describe('summary and responsive columns', () => {
it('summarizes what a person notices first, and which columns fit a width', async () => {
panel = await mount(() =>
h(
'Stack',
null,
h('Heading', null, 'Review queue'),
h('Callout', { tone: 'danger', title: 'Could not load the queue' }, 'offline'),
h('Table', {
label: 'PRs',
columns: [
{ key: 'title', label: 'Title', minWidth: 'sm' },
{ key: 'author', label: 'Author', hideBelow: 'sm' },
{ key: 'checks', label: 'Checks', hideBelow: 'md' }
],
rows: [
{ key: 'a', cells: { title: 'One' } },
{ key: 'b', cells: { title: 'Two' } }
]
}),
h('List', { label: 'Later' }, h('ListItem', { title: 'x' })),
h('EmptyState', { title: 'All caught up' })
)
)
expect(panel.summary()).toEqual([
'Heading "Review queue"',
'Callout danger "Could not load the queue"',
'Table "PRs": 2 row(s)',
'List "Later": 1 item(s)',
'EmptyState "All caught up"'
])
const table = { type: 'Table' }
expect(panel.columnsOf(table)).toEqual(['title', 'author', 'checks'])
expect(panel.columnsOf(table, { width: 400 })).toEqual(['title', 'author'])
expect(panel.columnsOf(table, { width: 300 })).toEqual(['title'])
})
})
@@ -1,5 +1,4 @@
import { afterEach, describe, expect, it, vi } from 'vitest'
import { exportedTypeShape, runtimeShape } from '../../authoring/plugin-author-typings.test-fixture'
import { afterEach, describe, expect, it } from 'vitest'
import { agentStatusChangedPayloadSchema } from '../../events/plugin-events'
import { atom, read, set } from '../runtime/declarative-atoms'
import { h } from '../runtime/declarative-elements'
@@ -128,15 +127,6 @@ describe('mountWorker', () => {
)
).rejects.toThrow(/registered undeclared command other/)
})
it('hands activate the same shape of orca object as Orca does', async () => {
let captured: unknown
await mountWorker(
{ default: (orca: unknown) => (captured = orca) },
{ events: [], commands: [] }
)
expect(runtimeShape(captured)).toEqual(exportedTypeShape('orca/worker', 'OrcaWorkerApi').shape)
})
})
describe('panel refusals', () => {
@@ -197,30 +187,3 @@ describe('a panel and its worker on one host', () => {
await worker.deactivate()
})
})
describe('RelativeTime in the test kit', () => {
afterEach(() => {
vi.useRealTimers()
})
it('reads as Orca draws it now, in a node and in a Table cell', async () => {
// Only Date is faked: the kit settles on real macrotasks.
vi.useFakeTimers({ now: Date.UTC(2026, 10, 1, 12), toFake: ['Date'] })
const at = Date.UTC(2026, 10, 1, 12) - 5 * 60_000
panel = await mount(() =>
h(
'Stack',
null,
h('Text', null, 'Started ', h('RelativeTime', { at })),
h('Table', {
label: 'Agents',
columns: [{ key: 'since', label: 'Since' }],
rows: [{ key: 'a', cells: { since: { type: 'relative', at, format: 'duration' } } }]
})
)
)
expect(panel.find({ role: 'time' })?.text).toBe('5m ago')
expect(panel.find({ role: 'grid' })?.text).toBe('5m')
expect(panel.warnings).toEqual([])
})
})
@@ -1,7 +1,7 @@
import { afterEach, describe, expect, it } from 'vitest'
import { atom, read, set, update } from '../runtime/declarative-atoms'
import { h } from '../runtime/declarative-elements'
import { host, on, shell } from '../runtime/orca-module'
import { host, shell } from '../runtime/orca-module'
import { hostError, mockHost, mount, stored, type TestPanel } from './orca-testing'
let panel: TestPanel | null = null
@@ -156,28 +156,6 @@ describe('orca/testing', () => {
])
})
it('delivers events and visibility, and runs setup cleanup on unmount', async () => {
const seen: string[] = []
let cleaned = false
panel = await mount({
setup: (ctx: {
subscriptions: { dispose(): void }[]
visible: Parameters<typeof read<boolean>>[0]
}) => {
ctx.subscriptions.push(on('theme', (payload) => seen.push(JSON.stringify(payload))))
return () => {
cleaned = true
}
},
default: () => h('Text', null, 'x')
})
await panel.emit('theme', { colorScheme: 'light' })
expect(seen).toEqual(['{"colorScheme":"light"}'])
await panel.unmount()
panel = null
expect(cleaned).toBe(true)
})
it('fails a test that never settles instead of hanging', async () => {
const ticks = atom(0)
const loop = () => {
@@ -56,17 +56,6 @@ describe('window.orca.plugin', () => {
answer(sent.id, { output: [{ id: '1' }] })
await expect(todos).resolves.toEqual([{ id: '1' }])
})
it('hands worker posts to plugin.on by event name', () => {
const { orca, push } = mountShell()
const changed = vi.fn()
const other = vi.fn()
orca.plugin.on('todos.changed', changed)
orca.plugin.on('other', other)
push({ kind: 'message', event: 'todos.changed', payload: { count: 3 } })
expect(changed).toHaveBeenCalledWith({ count: 3 })
expect(other).not.toHaveBeenCalled()
})
})
describe('window.orca events and data', () => {
@@ -85,21 +74,4 @@ describe('window.orca events and data', () => {
push({ kind: 'event', event: 'agent.status.changed', payload: { state: 'done' } })
expect(first).toHaveBeenCalledWith({ state: 'done' })
})
it('reads storage and settings, and hears which key changed', async () => {
const { orca, requests, answer, push } = mountShell()
const value = orca.storage.get('todos')
const setting = orca.settings.get('apiUrl')
const [get, settings] = requests()
answer(get.id, { value: null })
answer(settings.id, { settings: { apiUrl: 'https://x' } })
await expect(value).resolves.toBeNull()
await expect(setting).resolves.toBe('https://x')
const changed = vi.fn()
orca.storage.onDidChange(changed)
push({ kind: 'changed', area: 'settings', key: 'apiUrl' })
push({ kind: 'changed', area: 'storage', key: 'todos' })
expect(changed).toHaveBeenCalledOnce()
expect(changed).toHaveBeenCalledWith({ key: 'todos' })
})
})
@@ -1,4 +1,3 @@
import { createHash } from 'node:crypto'
import { describe, expect, it } from 'vitest'
import {
isPluginFrameOriginUrl,
@@ -12,11 +11,6 @@ import {
const KEY = pluginFrameKey('acme.charts')
describe('plugin frame guest identity', () => {
it('derives the key as the first 16 hex chars of sha256(publisher.id)', () => {
expect(KEY).toBe(createHash('sha256').update('acme.charts').digest('hex').slice(0, 16))
expect(pluginFrameKey('acme.other')).not.toBe(KEY)
})
it('round-trips the partition and refuses look-alikes', () => {
expect(pluginFramePartition(KEY)).toBe(`persist:orca-plugin-${KEY}`)
expect(readPluginFramePartitionKey(pluginFramePartition(KEY))).toBe(KEY)
@@ -1,44 +0,0 @@
// @vitest-environment happy-dom
import { describe, expect, it } from 'vitest'
import {
exportedTypeShape,
runtimeShape,
type TypingsShape
} from '../authoring/plugin-author-typings.test-fixture'
import { buildPluginGuestShellHtml, buildPluginPanelShellHtml } from './plugin-panel-shell'
/** The `window.orca` a frame shell defines, read without leaving it on the test window. */
function frameOrca(html: string): unknown {
const script = html.match(/<script>\n([\s\S]*?)<\/script>/)?.[1] ?? ''
const transport = { send: () => {}, subscribe: () => {} }
Object.defineProperty(window, '__orcaPluginFrameTransport', {
value: transport,
configurable: true
})
window.eval(script.replaceAll('configurable: false', 'configurable: true'))
const orca: unknown = Object.getOwnPropertyDescriptor(window, 'orca')?.value
Reflect.deleteProperty(window, 'orca')
return orca
}
function without(shape: TypingsShape, keys: string[]): TypingsShape {
return typeof shape === 'string'
? shape
: Object.fromEntries(Object.entries(shape).filter(([key]) => !keys.includes(key)))
}
const typings = exportedTypeShape('orca/frame', 'OrcaFrameApi')
describe('orca-frame.d.ts matches window.orca', () => {
it('declares every member an isolated editor frame gets', () => {
expect(runtimeShape(frameOrca(buildPluginGuestShellHtml('', 'editor')))).toEqual(typings.shape)
})
it('marks optional exactly what an iframe panel lacks', () => {
const panel = runtimeShape(frameOrca(buildPluginPanelShellHtml('', 'panel')))
const absent = ['shell', 'files', 'document']
expect(typings.optional.filter((name) => !name.includes('.')).sort()).toEqual(absent.sort())
expect(panel).toEqual(without(typings.shape, absent))
})
})
@@ -17,11 +17,4 @@ describe('files.saveAs blocked extensions', () => {
])('refuses %s', (name, extension) => {
expect(blockedPluginSaveAsExtension(name)).toBe(extension)
})
it.each(['scene.excalidraw', 'chart.png', 'data.json', 'notes', '.bashrc', 'archive.tar.gz'])(
'allows %s',
(name) => {
expect(blockedPluginSaveAsExtension(name)).toBeNull()
}
)
})
@@ -2,13 +2,11 @@ import { describe, expect, it } from 'vitest'
import { z } from 'zod'
import { pluginEngineFloor } from './plugin-engine-floor'
import { PLUGIN_ENUM_SINCE_KEY } from './plugin-engine-floor-keys'
import { PLUGIN_SCHEMA_NEWEST_SINCE, pluginHostVersion } from '../installation/plugin-host-version'
import { parseHostPluginManifest, parsePluginManifest } from './plugin-manifest'
import {
PLUGIN_DERIVED_PUBLISHER_SINCE,
PLUGIN_MANIFEST_V2_SINCE
} from './plugin-schema-field-docs'
import { PLUGIN_RESERVED_CONTRIBUTION_POINTS } from '../contributions/plugin-contribution-points'
function v2(overrides: Record<string, unknown> = {}): Record<string, unknown> {
return {
@@ -69,49 +67,6 @@ describe('manifest format 2 rules', () => {
).toMatchObject({ ok: true })
})
it('refuses host API majors this Orca does not implement', () => {
const worker = { execution: 'trusted-worker', main: 'main.mjs' }
expect(parsePluginManifest(v2({ ...worker, pluginApi: 2 }))).toEqual({
ok: false,
error: 'pluginApi: targets host API 2, but this Orca supports only 1; update Orca'
})
expect(parsePluginManifest(v2({ ...worker, pluginApi: 0 }))).toEqual({
ok: false,
error: 'pluginApi: must be 1'
})
})
it('keeps contributes.agentHarnesses reserved and leaves agents unclaimed', () => {
expect(
parsePluginManifest(
v2({ execution: 'declarative', contributes: { agentHarnesses: [{ path: 'a.json' }] } })
)
).toEqual({
ok: false,
error:
'contributes.agentHarnesses: is reserved for coding-agent harness definitions and must be empty for now'
})
expect(parsePluginManifest(v2({ contributes: { agents: [] } }))).toMatchObject({ ok: false })
})
it.each(Object.entries(PLUGIN_RESERVED_CONTRIBUTION_POINTS))(
'reserves contributes.%s: empty is accepted, any item is refused',
(key, purpose) => {
expect(parsePluginManifest(v2({ contributes: { [key]: [] } }))).toMatchObject({ ok: true })
expect(parsePluginManifest(v2({ contributes: { [key]: [{}] } }))).toEqual({
ok: false,
error: `contributes.${key}: is reserved for ${purpose} and must be empty for now`
})
}
)
it('requires engines.orca to cover every field the manifest uses', () => {
expect(parsePluginManifest(v2({ engines: { orca: '>=1.4.160' } }))).toEqual({
ok: false,
error: `engines.orca: must be at least >=${PLUGIN_MANIFEST_V2_SINCE}, because manifestVersion needs Orca ${PLUGIN_MANIFEST_V2_SINCE}`
})
})
it('skips only the engines floor when reading a manifest at runtime', () => {
const underDeclared = v2({ engines: { orca: '>=1.4.160' } })
expect(parseHostPluginManifest(underDeclared)).toMatchObject({ ok: true })
@@ -143,10 +98,4 @@ describe('manifest format 2 rules', () => {
})
expect(pluginEngineFloor(schema, { kind: 'old', list: [], extra: 'y' })?.version).toBe('5.0.0')
})
it('gates development builds as the newest release their schemas describe', () => {
expect(pluginHostVersion('1.4.214')).toBe(PLUGIN_SCHEMA_NEWEST_SINCE)
expect(pluginHostVersion('1.4.214-local.1.abc')).toBe(PLUGIN_SCHEMA_NEWEST_SINCE)
expect(pluginHostVersion('99.0.0')).toBe('99.0.0')
})
})
@@ -72,21 +72,6 @@ describe('pluginManifestSchema boundaries', () => {
expect(result.ok).toBe(false)
})
it('accepts documented dotted command namespaces with camel-case actions', () => {
const result = parsePluginManifest(
manifest({
main: 'main.mjs',
contributes: {
panels: [],
commands: [{ id: 'jupyter.restartKernel', title: 'Restart kernel' }],
events: []
}
})
)
expect(result).toMatchObject({ ok: true })
})
it('rejects oversized identities and invalid semantic versions', () => {
expect(parsePluginManifest(manifest({ id: 'a'.repeat(PLUGIN_ID_MAX_LENGTH + 1) })).ok).toBe(
false
@@ -97,33 +82,6 @@ describe('pluginManifestSchema boundaries', () => {
expect(parsePluginManifest(manifest({ version: '1.0.0-alpha.1+build.5' })).ok).toBe(true)
})
it('reports one specific issue per bad id or path', () => {
const reserved = pluginManifestSchema.safeParse(manifest({ id: 'constructor' }))
expect(reserved.error?.issues.map((issue) => issue.message)).toEqual([
'must not be a reserved name'
])
const badPath = pluginManifestSchema.safeParse(manifest({ main: '../escape.js' }))
expect(badPath.error?.issues).toHaveLength(1)
const device = pluginManifestSchema.safeParse(manifest({ main: 'dist/CON.js' }))
expect(device.error?.issues).toHaveLength(1)
})
it('accepts a $schema URL in v2 manifests only as an ignored hint', () => {
const $schema = 'https://example.com/orca-plugin.schema.json'
const v2 = parsePluginManifest(
manifest({
manifestVersion: 2,
execution: 'data',
contributes: {},
pluginApi: undefined,
engines: { orca: `>=${PLUGIN_MANIFEST_V2_SINCE}` },
$schema
})
)
expect(v2).toMatchObject({ ok: true, manifest: { $schema } })
expect(parsePluginManifest(manifest({ $schema }))).toMatchObject({ ok: true })
})
it('rejects duplicate contribution ids', () => {
const parsed = pluginManifestSchema.safeParse(
manifest({
@@ -2,45 +2,6 @@ import { describe, expect, it } from 'vitest'
import { parsePluginShotActs, PLUGIN_SHOT_MAX_ACTS } from './plugin-shot-acts'
describe('parsePluginShotActs', () => {
it('takes the four verbs, and one act without a list', () => {
const json = JSON.stringify([
{ press: { text: 'Add' } },
{ type: { label: 'New task' }, text: 'Ship', submit: true },
{ select: { label: 'View' }, value: 'done' },
{ toggle: { testId: 'hide-done' } }
])
expect(parsePluginShotActs(json)).toMatchObject({ ok: true, acts: { length: 4 } })
expect(parsePluginShotActs('{"press":{"role":"button"}}')).toEqual({
ok: true,
acts: [{ press: { role: 'button' } }]
})
})
it("takes the kit's Slider, Table and Tree verbs with their arguments", () => {
const table = { type: 'Table' }
const tree = { label: 'Folders' }
const json = JSON.stringify([
{ slide: { label: 'Volume' }, value: 7 },
{ sort: table, column: 'name' },
{ selectRows: table, keys: ['a', 'b'] },
{ activateRow: table, key: 'a' },
{ loadMore: table },
{ expand: tree, key: 'src' },
{ collapse: tree, key: 'src' },
{ selectItem: tree, key: 'src/main.ts' },
{ activateItem: tree, key: 'src/main.ts' }
])
expect(parsePluginShotActs(json)).toMatchObject({ ok: true, acts: { length: 9 } })
expect(parsePluginShotActs('{"slide":{"label":"Volume"},"value":"7"}')).toMatchObject({
ok: false,
error: expect.stringMatching(/^action 1 \(slide\) \(value\): /)
})
expect(parsePluginShotActs('{"expand":{"label":"Folders"}}')).toMatchObject({
ok: false,
error: expect.stringMatching(/^action 1 \(expand\) \(key\): /)
})
})
it('names the act and field that is wrong', () => {
expect(parsePluginShotActs('[{"press":{"text":"A"}},{"click":{}}]')).toEqual({
ok: false,
-13
View File
@@ -165,19 +165,6 @@ describe('Windows secure files through the native ACL addon', () => {
})
describe('directory relock', () => {
it('relocks the parent once, on the thread pool, across many writes', async () => {
const dir = tempDir()
for (let i = 0; i < 5; i++) {
writeSecureFile(join(dir, `secret-${i}.json`), `contents-${i}`)
}
await settle()
expect(fake.callsFor('setAsync', dir)).toEqual([
expect.objectContaining({ op: 'setAsync', path: dir, sddl: DIRECTORY_SDDL })
])
expect(fake.callsFor('set', dir)).toEqual([])
})
it('never rewrites a directory the icacls-era hardening already locked', async () => {
const dir = tempDir()
fake.dacls.set(dir, ICACLS_ERA_DIRECTORY_SDDL)
@@ -104,27 +104,4 @@ describe('resolveSourceControlNextStep', () => {
expect(step({ review: { kind: 'none' } }).kind).toBe('none')
expect(step({ review: { kind: 'loading' }, reviewCreation: 'create' }).kind).toBe('none')
})
it('turns on auto-merge while GitHub waits on a requirement, and says why when nothing can', () => {
expect(
step({
review: {
kind: 'github',
pr: { ...OPEN_PR, mergeStateStatus: 'BLOCKED' },
failedChecks: 0,
pendingChecks: 2
}
})
).toEqual({ kind: 'merge_when_ready', mergeState: 'blocked', pendingChecks: 2 })
expect(step({}, { reviewDecision: 'REVIEW_REQUIRED', mergeStateStatus: 'BLOCKED' })).toEqual({
kind: 'merge_when_ready',
mergeState: 'review_required',
pendingChecks: 0
})
expect(step({}, { mergeStateStatus: 'BLOCKED', autoMergeAllowed: false })).toEqual({
kind: 'merge_blocked',
mergeState: 'blocked'
})
expect(step({}, { state: 'draft' })).toEqual({ kind: 'merge_blocked', mergeState: 'draft' })
})
})
@@ -78,34 +78,6 @@ describe('SSH retained payload admission', () => {
expect(getUtf8ByteLength(detail ?? '')).toBe(SSH_CONNECTION_ERROR_MAX_UTF8_BYTES)
})
it('keeps ordinary connection state while stripping unknown payload fields', () => {
const admitted = admitSshConnectionState(
{
targetId: 'ssh-a',
status: 'connected',
error: null,
reconnectAttempt: 2,
providerEpoch: 'provider-a',
connectionGeneration: 3,
supportsFolderDownload: true,
remotePlatform: 'linux',
unexpected: 'x'.repeat(1024)
},
'ssh-a'
)
expect(admitted).toEqual({
targetId: 'ssh-a',
status: 'connected',
error: null,
reconnectAttempt: 2,
providerEpoch: 'provider-a',
connectionGeneration: 3,
supportsFolderDownload: true,
remotePlatform: 'linux'
})
})
it('admits the plain SSH mode and drops a malformed one without dropping the state', () => {
const state = { targetId: 'ssh-a', status: 'connected', error: null, reconnectAttempt: 0 }
const plainSsh = { reason: 'home_noexec', message: 'Home is noexec.' }
@@ -116,15 +88,6 @@ describe('SSH retained payload admission', () => {
expect(malformed).not.toHaveProperty('plainSsh')
})
it('admits only a literal Host Node runtime flag', () => {
const state = { targetId: 'ssh-a', status: 'connected', error: null, reconnectAttempt: 0 }
const flagged = admitSshConnectionState({ ...state, hostNodeRuntime: true }, 'ssh-a')
expect(flagged?.hostNodeRuntime).toBe(true)
const malformed = admitSshConnectionState({ ...state, hostNodeRuntime: 'yes' }, 'ssh-a')
expect(malformed).not.toHaveProperty('hostNodeRuntime')
})
it("drops an older host's move offer and admits only a literal elsewhere flag", () => {
const state = { targetId: 'ssh-a', status: 'connected', error: null, reconnectAttempt: 0 }
const relay = { kind: 'relay', reason: 'relay_terminals_live', terminals: 2 }
@@ -147,23 +110,6 @@ describe('SSH retained payload admission', () => {
).toEqual(relay)
})
it('admits only a literal "Move anyway" offer', () => {
const state = { targetId: 'ssh-a', status: 'connected', error: null, reconnectAttempt: 0 }
const refused = { kind: 'relay', reason: 'refused', detail: 'saved tabs and panes' }
expect(
admitSshConnectionState(
{ ...state, managedServer: { ...refused, canLeaveBehind: true } },
'ssh-a'
)?.managedServer
).toEqual({ ...refused, canLeaveBehind: true })
expect(
admitSshConnectionState(
{ ...state, managedServer: { ...refused, canLeaveBehind: 1 } },
'ssh-a'
)?.managedServer
).toEqual(refused)
})
it('rejects partial and malformed provider authority', () => {
const state = {
targetId: 'ssh-a',
@@ -238,26 +184,6 @@ describe('SSH retained payload admission', () => {
).toBeNull()
})
it('normalizes legacy authority to unknown', () => {
expect(
admitSshConnectionState(
{
targetId: 'ssh-a',
status: 'disconnected',
error: null,
reconnectAttempt: 0
},
'ssh-a'
)
).toEqual({
targetId: 'ssh-a',
status: 'disconnected',
error: null,
reconnectAttempt: 0,
providerEpoch: null
})
})
it('caps connection errors without splitting a UTF-8 code point', () => {
const admitted = admitSshConnectionState(
{
@@ -105,10 +105,6 @@ describe.skipIf(process.platform === 'win32')(
expect(fishRequirementViolation(FISH)).toBeNull()
})
it('found shells to test against', () => {
expect(SHELLS.map((shell) => shell.name)).toContain('sh')
})
describe.each(SHELLS)('$name', (shell) => {
it.each(QUOTING_CASES)('quotes %j so the shell yields it back verbatim', (value) => {
expect(runInShell(shell, `printf '%s' ${quoteStartupArg(value, 'posix')}`)).toBe(value)
@@ -103,27 +103,6 @@ describe('resolveTabAgentFromSignals — Pi/OMP identity', () => {
).toBe('omp')
})
it('does not flap between OMP and Pi as a launchAgent-less pane cycles hooks', () => {
const withLiveHook = resolveTabAgentFromSignals({
hasObservedAgentSignal: true,
isRemote: true,
title: '⠋ Pi',
hookAgent: 'omp',
focusedCompletedHookAgent: 'omp',
launchAgent: undefined
})
const afterHookCleared = resolveTabAgentFromSignals({
hasObservedAgentSignal: true,
isRemote: true,
title: '⠋ Pi',
hookAgent: null,
focusedCompletedHookAgent: 'omp',
launchAgent: undefined
})
expect(withLiveHook).toBe('omp')
expect(afterHookCleared).toBe('omp')
})
it('does not flap between OMP and Pi as the foreground process oscillates', () => {
// OMP’s nested pi alternates foreground reads at command boundaries; normalize both to the known OMP owner instead of flickering icons.
const readsPi = resolveTabAgentFromSignals({
@@ -204,19 +183,6 @@ describe('resolveTabAgentFromSignals — Pi/OMP identity', () => {
// Hook records identify panes across working/done turns; title activity must never replace durable identity.
describe('resolveTabAgentFromSignals — identity vs liveness', () => {
it('surfaces the focused idle identity from the record, not the title', () => {
expect(
resolveTabAgentFromSignals({
hasObservedAgentSignal: true,
isRemote: true,
title: 'Terminal',
hookAgent: null,
focusedCompletedHookAgent: 'omp',
launchAgent: undefined
})
).toBe('omp')
})
it('ranks the focused idle identity above a hibernated session and launch bootstrap', () => {
// Completed local hook identity beats sleeping records and stale launch intent.
expect(
@@ -257,28 +223,6 @@ describe('resolveTabAgentFromSignals — identity vs liveness', () => {
).toBe('claude')
})
it('keeps a launchAgent-less pane with a live Pi hook stable on Pi', () => {
expect(
resolveTabAgentFromSignals({
hasObservedAgentSignal: true,
isRemote: true,
title: '⠋ Pi',
hookAgent: 'pi',
launchAgent: undefined
})
).toBe('pi')
expect(
resolveTabAgentFromSignals({
hasObservedAgentSignal: true,
isRemote: true,
title: '⠋ Pi',
hookAgent: null,
focusedCompletedHookAgent: 'pi',
launchAgent: undefined
})
).toBe('pi')
})
it('keeps a sibling idle identity when the focused pane returns to its shell', () => {
// Focused-pane exit evidence must not clear still-live sibling identity.
expect(
-10
View File
@@ -14,11 +14,6 @@ describe('detectTerminalFileUriLinks', () => {
expect(link.displayText).toBe('file:///Users/dev/orca/report.html')
})
it('percent-decodes spaces in the path', () => {
const [link] = detectTerminalFileUriLinks('open file:///Users/dev/My%20Reports/out.html now')
expect(link.pathText).toBe('/Users/dev/My Reports/out.html')
})
it('keeps standard unescaped parentheses and apostrophes inside the path', () => {
const uri = "file:///tmp/Brennan's%20Report%20(final)"
const [link] = detectTerminalFileUriLinks(`open (${uri})`)
@@ -56,11 +51,6 @@ describe('detectTerminalFileUriLinks', () => {
expect(detectTerminalFileUriLinks('file:///tmp/%E0%A4%A.txt')).toEqual([])
})
it('finds multiple file URIs on one line', () => {
const links = detectTerminalFileUriLinks('a file:///tmp/a.txt b file:///tmp/b.txt')
expect(links.map((link) => link.pathText)).toEqual(['/tmp/a.txt', '/tmp/b.txt'])
})
it('does not expose oversized terminal tokens to filesystem probing', () => {
expect(detectTerminalFileUriLinks(`file:///tmp/${'a'.repeat(10_000)}`)).toEqual([])
})
-137
View File
@@ -15,41 +15,8 @@ import {
SEARCH_JSON_STRUCTURE_LIMITS
} from './text-search'
import { splitSearchGlobPatterns, toGitGlobPathspecs } from './text-search-glob-patterns'
import { normalizeRelativePath } from './text-search-paths'
describe('normalizeRelativePath', () => {
it('collapses mixed separators and strips leading slashes', () => {
expect(normalizeRelativePath('a\\b\\c')).toBe('a/b/c')
expect(normalizeRelativePath('/a/b')).toBe('a/b')
expect(normalizeRelativePath('///a//b')).toBe('a/b')
})
})
describe('buildRgArgs', () => {
it('defaults to case-insensitive, fixed-strings, hidden, exclude .git', () => {
const args = buildRgArgs('needle', '/root', {})
expect(args).toContain('--json')
expect(args).toContain('--hidden')
expect(args).toContain('--ignore-case')
expect(args).toContain('--fixed-strings')
expect(args.indexOf('!.git')).toBeGreaterThan(args.indexOf('--glob'))
expect(args.slice(-3)).toEqual(['--', 'needle', '/root'])
})
it('honors caseSensitive, wholeWord, useRegex', () => {
const args = buildRgArgs('q', '/r', { caseSensitive: true, wholeWord: true, useRegex: true })
expect(args).not.toContain('--ignore-case')
expect(args).toContain('--word-regexp')
expect(args).not.toContain('--fixed-strings')
})
it('splits comma-separated include/exclude patterns', () => {
const args = buildRgArgs('q', '/r', { includePattern: '*.ts, *.tsx', excludePattern: '*.md' })
expect(args).toContain('*.ts')
expect(args).toContain('*.tsx')
expect(args).toContain('!*.md')
})
it('keeps escaped commas inside a single generated folder glob', () => {
const args = buildRgArgs('q', '/r', { includePattern: 'foo\\,bar/**, *.ts' })
expect(args).toContain('foo\\,bar/**')
@@ -58,14 +25,6 @@ describe('buildRgArgs', () => {
})
describe('splitSearchGlobPatterns', () => {
it('splits comma-separated patterns while preserving escaped commas', () => {
expect(splitSearchGlobPatterns('foo\\,bar/**, *.ts, dist/**')).toEqual([
'foo\\,bar/**',
'*.ts',
'dist/**'
])
})
it('preserves trailing escapes as literal glob input', () => {
expect(splitSearchGlobPatterns('src\\')).toEqual(['src\\'])
})
@@ -104,19 +63,6 @@ describe('ingestRgJsonLine', () => {
expect(files[0].matches[0]).toEqual({ line: 2, column: 1, matchLength: 3, lineContent: 'abc' })
})
it('ignores non-match messages', () => {
const acc = createAccumulator()
ingestRgJsonLine(JSON.stringify({ type: 'begin', data: {} }), '/root', acc, 100)
expect(acc.totalMatches).toBe(0)
})
it('skips malformed JSON', () => {
const acc = createAccumulator()
const verdict = ingestRgJsonLine('not json', '/root', acc, 100)
expect(verdict).toBe('continue')
expect(acc.totalMatches).toBe(0)
})
it('rejects excessive nesting before JSON.parse', () => {
const parseSpy = vi.spyOn(JSON, 'parse')
const acc = createAccumulator()
@@ -218,26 +164,6 @@ describe('ingestRgJsonLine', () => {
})
describe('buildGitGrepArgs', () => {
it('adds -i, --fixed-strings and `.` default pathspec', () => {
const args = buildGitGrepArgs('q', {})
expect(args).toContain('-i')
expect(args).toContain('--fixed-strings')
expect(args).toContain('--no-recurse-submodules')
expect(args.at(-1)).toBe('.')
})
it('uses --extended-regexp when useRegex is true', () => {
const args = buildGitGrepArgs('q', { useRegex: true })
expect(args).toContain('--extended-regexp')
expect(args).not.toContain('--fixed-strings')
})
it('includes/excludes use :(glob) pathspecs', () => {
const args = buildGitGrepArgs('q', { includePattern: '*.ts', excludePattern: 'dist/**' })
expect(args).toContain(':(glob)**/*.ts')
expect(args).toContain(':(exclude,glob)dist/**')
})
it('keeps escaped commas inside one generated folder pathspec', () => {
const args = buildGitGrepArgs('q', { includePattern: 'foo\\,bar/**, *.ts' })
expect(args).toContain(':(glob)foo\\,bar/**')
@@ -246,12 +172,6 @@ describe('buildGitGrepArgs', () => {
})
describe('toGitGlobPathspecs', () => {
it('wraps bare globs with **/ to match recursively', () => {
expect(toGitGlobPathspecs('*.ts')).toContain(':(glob)**/*.ts')
expect(toGitGlobPathspecs('src/*.ts')).toContain(':(glob)src/*.ts')
expect(toGitGlobPathspecs('*.ts', true)).toContain(':(exclude,glob)**/*.ts')
})
it('also covers the subtree so a directory name behaves like rg --glob', () => {
expect(toGitGlobPathspecs('node_modules', true)).toEqual([
':(exclude,glob)**/node_modules',
@@ -265,30 +185,9 @@ describe('toGitGlobPathspecs', () => {
expect(toGitGlobPathspecs('node_modules/', true)).toEqual([':(exclude,glob)**/node_modules/**'])
expect(toGitGlobPathspecs('src/')).toEqual([':(glob)**/src/**'])
})
it('drops a pattern that is only separators', () => {
expect(toGitGlobPathspecs('/')).toEqual([])
})
})
describe('buildSubmatchRegex', () => {
it('escapes literal query by default, gi flags', () => {
const re = buildSubmatchRegex('a.b', {})
expect(re?.flags).toBe('gi')
expect(re?.source).toBe('a\\.b')
})
it('wholeWord wraps in \\b', () => {
const re = buildSubmatchRegex('foo', { wholeWord: true })
expect(re?.source).toBe('\\bfoo\\b')
})
it('useRegex passes through', () => {
const re = buildSubmatchRegex('a|b', { useRegex: true, caseSensitive: true })
expect(re?.source).toBe('a|b')
expect(re?.flags).toBe('g')
})
it('returns null for queries git grep accepts but JS RegExp rejects', () => {
// Unbalanced group — git grep ERE accepts it as literal, JS RegExp throws.
expect(buildSubmatchRegex('(foo', { useRegex: true })).toBeNull()
@@ -460,42 +359,6 @@ describe('ingestGitGrepLine', () => {
})
describe('finalize', () => {
it('returns the expected SearchResult shape', () => {
const acc = createAccumulator()
acc.fileMap.set('/r/a.ts', {
filePath: '/r/a.ts',
relativePath: 'a.ts',
matchCount: 1,
matches: [{ line: 1, column: 1, matchLength: 3, lineContent: 'foo' }]
})
acc.totalMatches = 1
acc.truncated = true
expect(finalize(acc)).toEqual({
files: [
{
filePath: '/r/a.ts',
relativePath: 'a.ts',
matchCount: 1,
matches: [{ line: 1, column: 1, matchLength: 3, lineContent: 'foo' }]
}
],
totalMatches: 1,
truncated: true
})
})
it('filters impossible empty file rows before returning results', () => {
const acc = createAccumulator()
acc.fileMap.set('/r/a.ts', { filePath: '/r/a.ts', relativePath: 'a.ts', matches: [] })
acc.fileMap.set('/r/b.ts', {
filePath: '/r/b.ts',
relativePath: 'b.ts',
matches: [{ line: 1, column: 1, matchLength: 3, lineContent: 'foo' }]
})
acc.totalMatches = 1
expect(finalize(acc).files.map((file) => file.relativePath)).toEqual(['b.ts'])
})
it('normalizes missing and too-low per-file match counts', () => {
const acc = createAccumulator()
acc.fileMap.set('/r/a.ts', {
+1 -44
View File
@@ -1,12 +1,5 @@
import { describe, expect, it } from 'vitest'
import {
buildShellCommandFromArgv,
clearEnvCommand,
commandSeparator,
isPosixStartupShell,
quoteStartupArg,
tokenizeStartupCommand
} from './tui-agent-startup-shell'
import { clearEnvCommand, quoteStartupArg, tokenizeStartupCommand } from './tui-agent-startup-shell'
import { buildAgentDraftLaunchPlan } from './tui-agent-startup'
function expectSpansCoverTokens(source: string, shell: 'powershell' | 'cmd'): string[] {
@@ -104,42 +97,6 @@ describe('one Unix startup dialect', () => {
expect(quoteStartupArg('', 'posix')).toBe(`''`)
})
it('keeps the sh-family grammar claims that do hold for fish', () => {
expect(commandSeparator('posix')).toBe('; ')
expect(isPosixStartupShell('posix')).toBe(true)
expect(isPosixStartupShell('powershell')).toBe(false)
expect(buildShellCommandFromArgv(['codex', 'resume', 'a b'], 'posix')).toBe(
`'codex' 'resume' 'a b'`
)
})
it('round-trips an adversarial argument through quote then tokenize', () => {
for (const value of [
String.raw`use \d+ and \\server\share`,
"it's mine",
'ends\\',
'a "b" c',
'$PATH *.ts'
]) {
const tokenized = tokenizeStartupCommand(quoteStartupArg(value, 'posix'), 'posix')
expect(tokenized.ok && tokenized.tokens).toEqual([value])
}
})
it('clears an agent draft prefill variable with the portable teardown', () => {
const plan = buildAgentDraftLaunchPlan({
agent: 'pi',
draft: 'hello',
cmdOverrides: {},
platform: 'darwin'
})
expect(plan?.launchCommand).toBe(
`pi; command test -n "$fish_pid" && set --erase -g ORCA_PI_PREFILL; command test -z "$fish_pid" && unset ORCA_PI_PREFILL; true`
)
expect(plan?.env?.ORCA_PI_PREFILL).toBe('hello')
})
it('leaves a multi-line argv draft to the paste fallback in cmd.exe', () => {
const draft = 'Linked Linear issue: ENG-1\nhttps://linear.app/acme/issue/ENG-1'
const args = { agent: 'claude' as const, draft, cmdOverrides: {}, platform: 'win32' as const }
-286
View File
@@ -6,7 +6,6 @@ import {
buildShellCommandFromArgv,
planAgentCliArgsSuffix
} from './tui-agent-startup'
import { TUI_AGENT_CONFIG } from './tui-agent-config'
import { normalizeTuiAgentArgsRecord, resolveTuiAgentLaunchArgs } from './tui-agent-launch-defaults'
import { tokenizeStartupCommand } from './tui-agent-startup-shell'
import {
@@ -41,21 +40,6 @@ describe('tui agent startup plans', () => {
}
)
it('passes the whole configured argument string to a terminal launch', () => {
const plan = buildAgentStartupPlan({
agent: 'claude',
prompt: '',
agentArgs: resolveTuiAgentLaunchArgs('claude', {
agentDefaultArgs: { claude: '--dangerously-skip-permissions --model Opus' }
}),
cmdOverrides: {},
platform: 'linux',
allowEmptyPromptLaunch: true
})
expect(plan?.launchCommand).toBe("claude '--dangerously-skip-permissions' '--model' 'Opus'")
})
it('uses POSIX quoting when the target shell is Linux', () => {
const plan = buildAgentStartupPlan({
agent: 'claude',
@@ -109,28 +93,6 @@ describe('tui agent startup plans', () => {
expect(plan?.launchCommand).toBe('claude "fix ^"quoted^" ^& ^%PATH^%"')
})
it('terminates Grok options before a flag-shaped POSIX prompt', () => {
const plan = buildAgentStartupPlan({
agent: 'grok',
prompt: '--version',
cmdOverrides: {},
platform: 'linux'
})
expect(plan?.launchCommand).toBe("grok -- '--version'")
})
it('terminates Grok options before a flag-shaped PowerShell prompt', () => {
const plan = buildAgentStartupPlan({
agent: 'grok',
prompt: '-h',
cmdOverrides: {},
platform: 'win32'
})
expect(plan?.launchCommand).toBe("grok -- '-h'")
})
it('terminates Grok options before a subcommand-shaped cmd prompt', () => {
const plan = buildAgentStartupPlan({
agent: 'grok',
@@ -155,17 +117,6 @@ describe('tui agent startup plans', () => {
expect(plan?.launchCommand).toBe("grok '--always-approve' -- '--version'")
})
it('does not add the Grok prompt separator to other argv agents', () => {
const plan = buildAgentStartupPlan({
agent: 'codex',
prompt: '--version',
cmdOverrides: {},
platform: 'linux'
})
expect(plan?.launchCommand).toBe("codex '-c' 'disable_paste_burst=true' '--version'")
})
it.each([
{ platform: 'linux' as const, shell: 'posix' as const },
{ platform: 'win32' as const, shell: 'powershell' as const },
@@ -230,18 +181,6 @@ describe('tui agent startup plans', () => {
expect(tokens.ok && tokens.tokens.at(-1)).toMatch(/\\0[0-7]{3}/)
})
it('does not launch Codex with the Orca profile when agent status hooks are enabled', () => {
const plan = buildAgentStartupPlan({
agent: 'codex',
prompt: 'fix it',
cmdOverrides: {},
platform: 'linux'
})
expect(plan?.launchCommand).toBe("codex '-c' 'disable_paste_burst=true' 'fix it'")
expect(plan?.startupCommandDelivery).toBe('shell-ready')
})
it('keeps plain empty Codex startup on the fast delivery path', () => {
const plan = buildAgentStartupPlan({
agent: 'codex',
@@ -260,18 +199,6 @@ describe('tui agent startup plans', () => {
})
})
it('launches Claude without Orca settings injection', () => {
const plan = buildAgentStartupPlan({
agent: 'claude',
prompt: 'fix it',
cmdOverrides: {},
platform: 'linux'
})
expect(plan?.launchCommand).toBe("claude 'fix it'")
expect(plan?.launchCommand).not.toContain('--settings')
})
it('uses the Linux Orca CLI command for Claude Agent Teams launches', () => {
const plan = buildAgentStartupPlan({
agent: 'claude-agent-teams',
@@ -312,101 +239,6 @@ describe('tui agent startup plans', () => {
expect(plan?.launchCommand).toBe('orca.cmd claude-teams')
})
it('keeps the Linux orca-ide wrapper for local (non-remote) Claude Agent Teams', () => {
// Local Linux keeps orca-ide to avoid the GNOME screen-reader command clash.
const plan = buildAgentStartupPlan({
agent: 'claude-agent-teams',
prompt: '',
cmdOverrides: {},
platform: 'linux',
isRemote: false,
allowEmptyPromptLaunch: true
})
expect(plan?.launchCommand).toBe('orca-ide claude-teams')
})
it('launches OpenClaude as a distinct argv agent', () => {
const plan = buildAgentStartupPlan({
agent: 'openclaude',
prompt: 'fix it',
cmdOverrides: {},
platform: 'linux'
})
expect(plan).toEqual({
agent: 'openclaude',
launchCommand: "openclaude 'fix it'",
expectedProcess: 'openclaude',
followupPrompt: null,
launchConfig: { agentCommand: 'openclaude', agentArgs: '', agentEnv: {} }
})
})
it('launches Mistral Vibe through the installed vibe executable', () => {
const plan = buildAgentStartupPlan({
agent: 'mistral-vibe',
prompt: 'fix it',
cmdOverrides: {},
platform: 'linux'
})
expect(plan).toEqual({
agent: 'mistral-vibe',
launchCommand: 'vibe',
expectedProcess: 'vibe',
followupPrompt: 'fix it',
launchConfig: { agentCommand: 'vibe', agentArgs: '', agentEnv: {} }
})
})
it('launches Qwen Code through the installed qwen executable', () => {
const plan = buildAgentStartupPlan({
agent: 'qwen-code',
prompt: 'fix it',
cmdOverrides: {},
platform: 'linux'
})
expect(plan).toEqual({
agent: 'qwen-code',
launchCommand: 'qwen',
expectedProcess: 'qwen',
followupPrompt: 'fix it',
launchConfig: { agentCommand: 'qwen', agentArgs: '', agentEnv: {} }
})
})
it.each([
['yolo', 'linux', 'posix', undefined, "muse --trust-workspace '--yolo'"],
['manual', 'linux', 'posix', { muse: '' }, 'muse --trust-workspace'],
['yolo', 'darwin', 'posix', undefined, "muse --trust-workspace '--yolo'"],
['manual', 'darwin', 'posix', { muse: '' }, 'muse --trust-workspace'],
['yolo', 'win32', 'powershell', undefined, "muse --trust-workspace '--yolo'"],
['manual', 'win32', 'powershell', { muse: '' }, 'muse --trust-workspace'],
['yolo', 'win32', 'cmd', undefined, 'muse --trust-workspace "--yolo"'],
['manual', 'win32', 'cmd', { muse: '' }, 'muse --trust-workspace']
] as const)(
'launches Muse in %s mode on %s/%s before delivering its prompt',
(_, platform, shell, defaults, command) => {
const plan = buildAgentStartupPlan({
agent: 'muse',
prompt: 'fix it',
cmdOverrides: {},
platform,
shell,
agentArgs: resolveTuiAgentLaunchArgs('muse', { agentDefaultArgs: defaults })
})
expect(plan).toMatchObject({
agent: 'muse',
launchCommand: command,
expectedProcess: 'muse',
followupPrompt: 'fix it'
})
}
)
it.each(['exec', 'resume', '--help'])(
'delivers the reserved Muse prompt %s as text',
(prompt) => {
@@ -421,17 +253,6 @@ describe('tui agent startup plans', () => {
}
)
it('leaves Claude command overrides untouched', () => {
const plan = buildAgentStartupPlan({
agent: 'claude',
prompt: 'fix it',
cmdOverrides: { claude: 'claude --dangerously-skip-permissions' },
platform: 'linux'
})
expect(plan?.launchCommand).toBe("claude --dangerously-skip-permissions 'fix it'")
})
it('leaves Codex command overrides untouched', () => {
const plan = buildAgentStartupPlan({
agent: 'codex',
@@ -507,19 +328,6 @@ describe('tui agent startup plans', () => {
)
})
it('honors command overrides when building POSIX resume plans', () => {
const plan = buildAgentResumeStartupPlan({
agent: 'codex',
providerSession: { key: 'session_id', id: 's1' },
cmdOverrides: { codex: 'codex --profile work' },
platform: 'linux'
})
expect(plan?.launchCommand).toBe(
"codex --profile work '-c' 'disable_paste_burst=true' 'resume' 's1'"
)
})
it('uses a captured launch command when building resume plans after overrides change', () => {
const plan = buildAgentResumeStartupPlan({
agent: 'codex',
@@ -583,25 +391,6 @@ describe('tui agent startup plans', () => {
expect(plan?.launchCommand).toBe("claude '--model' 'sonnet' '--name' 'Bob''s' 'fix it'")
})
it('carries agent launch environment defaults into startup plans', () => {
const plan = buildAgentStartupPlan({
agent: 'goose',
prompt: '',
cmdOverrides: {},
agentEnv: { GOOSE_MODE: 'auto' },
platform: 'linux',
allowEmptyPromptLaunch: true
})
expect(plan?.launchCommand).toBe('goose')
expect(plan?.env).toEqual({ GOOSE_MODE: 'auto' })
expect(plan?.launchConfig).toEqual({
agentCommand: 'goose',
agentArgs: '',
agentEnv: { GOOSE_MODE: 'auto' }
})
})
it('captures empty args and env as explicit launch config values', () => {
const plan = buildAgentStartupPlan({
agent: 'claude',
@@ -631,75 +420,6 @@ describe('tui agent startup plans', () => {
expect(plan?.launchCommand).toBe("opencode --prompt 'fix it'")
})
it('keeps opencode and mimo-code on the cursor-gated paste draft route', () => {
expect(TUI_AGENT_CONFIG.opencode.draftPasteReadySignal).toBe(
'render-cursor-after-bracketed-paste'
)
expect(TUI_AGENT_CONFIG.opencode.draftPromptFlag).toBeUndefined()
expect(TUI_AGENT_CONFIG.opencode.draftPromptEnvVar).toBeUndefined()
expect(TUI_AGENT_CONFIG['mimo-code'].draftPasteReadySignal).toBe(
'render-cursor-after-bracketed-paste'
)
expect(TUI_AGENT_CONFIG['mimo-code'].draftPromptFlag).toBeUndefined()
expect(TUI_AGENT_CONFIG['mimo-code'].draftPromptEnvVar).toBeUndefined()
// Without native draft plans, these agents must use cursor-gated paste readiness.
expect(
buildAgentDraftLaunchPlan({
agent: 'opencode',
draft: 'x',
cmdOverrides: {},
platform: 'darwin'
})
).toBeNull()
expect(
buildAgentDraftLaunchPlan({
agent: 'mimo-code',
draft: 'x',
cmdOverrides: {},
platform: 'darwin'
})
).toBeNull()
})
it('keeps grok on the composer-glyph paste draft route', () => {
// Grok has no native prefill and its animated logo never settles; drafts must wait for its composer glyph.
expect(TUI_AGENT_CONFIG.grok.draftPasteReadySignal).toBe('grok-composer-prompt')
expect(TUI_AGENT_CONFIG.grok.draftPromptFlag).toBeUndefined()
expect(TUI_AGENT_CONFIG.grok.draftPromptEnvVar).toBeUndefined()
expect(
buildAgentDraftLaunchPlan({
agent: 'grok',
draft: 'x',
cmdOverrides: {},
platform: 'darwin'
})
).toBeNull()
})
it('appends Kiro trust defaults to the chat subcommand that accepts them', () => {
const plan = buildAgentStartupPlan({
agent: 'kiro',
prompt: 'fix it',
cmdOverrides: {},
agentArgs: '--trust-all-tools',
platform: 'linux'
})
expect(plan?.launchCommand).toBe("kiro-cli chat --tui '--trust-all-tools'")
})
it('launches Continue through the documented cn binary', () => {
const plan = buildAgentStartupPlan({
agent: 'continue',
prompt: 'fix it',
cmdOverrides: {},
agentArgs: '--allow "*"',
platform: 'linux'
})
expect(plan?.launchCommand).toBe("cn '--allow' '*'")
})
it('clears draft environment variables with the target shell syntax', () => {
expect(
buildAgentDraftLaunchPlan({
@@ -812,10 +532,4 @@ describe('tui agent startup plans', () => {
agentEnv: { ORCA_AGENT_MODE: 'managed' }
})
})
it('appends Devin default permission-mode bypass before stdin prompt delivery', () => {
expect(resolveTuiAgentLaunchArgs('devin', null)).toBe(
'--permission-mode bypass --respect-workspace-trust false'
)
})
})
-55
View File
@@ -1,55 +0,0 @@
import { describe, expect, it } from 'vitest'
import {
formatWindowMaterialArgument,
parseWindowMaterialArgument,
resolveWindowMaterial
} from './window-material'
const mac = { platform: 'darwin', osRelease: '25.0.0' } as const
const windows11 = { platform: 'win32', osRelease: '10.0.22631' } as const
const linux = { platform: 'linux', osRelease: '6.8.0' } as const
describe('resolveWindowMaterial', () => {
it('migrates the legacy blur switch when windowMaterial is unset, except on Linux', () => {
const legacy = { windowBackgroundBlur: true, terminalBackgroundOpacity: 0.8 }
expect(resolveWindowMaterial(legacy, mac, false)).toBe('blur')
expect(resolveWindowMaterial(legacy, linux, false)).toBe('off')
expect(resolveWindowMaterial({ ...legacy, windowMaterial: 'off' }, mac, false)).toBe('off')
})
it('stays off while nothing translucent can reveal the material (#8482)', () => {
for (const terminalBackgroundOpacity of [undefined, 1]) {
expect(
resolveWindowMaterial({ windowMaterial: 'blur', terminalBackgroundOpacity }, mac, false)
).toBe('off')
}
})
it('turns off for Reduce Visual Effects and the OS reduced-transparency preference', () => {
const settings = { windowMaterial: 'blur' as const, terminalBackgroundOpacity: 0.8 }
expect(
resolveWindowMaterial({ ...settings, reduceVisualEffects: true }, windows11, false)
).toBe('off')
expect(resolveWindowMaterial(settings, windows11, true)).toBe('off')
})
it('maps a choice onto what the platform can render', () => {
const translucent = { windowMaterial: 'translucent' as const, terminalBackgroundOpacity: 0.5 }
const blur = { windowMaterial: 'blur' as const, terminalBackgroundOpacity: 0.5 }
expect(resolveWindowMaterial(translucent, mac, false)).toBe('translucent')
expect(resolveWindowMaterial(translucent, windows11, false)).toBe('off')
expect(resolveWindowMaterial(blur, linux, false)).toBe('translucent')
// Acrylic needs Windows 11 22H2; older builds would paint the clear window black.
expect(resolveWindowMaterial(blur, { platform: 'win32', osRelease: '10.0.19045' }, false)).toBe(
'off'
)
})
})
describe('window material argument', () => {
it('round-trips and rejects unknown values', () => {
expect(parseWindowMaterialArgument(['x', formatWindowMaterialArgument('blur')])).toBe('blur')
expect(parseWindowMaterialArgument(['--orca-window-material=glass'])).toBe('off')
expect(parseWindowMaterialArgument([])).toBe('off')
})
})
@@ -68,10 +68,6 @@ function requestFrom(args: readonly string[]) {
}
describe('relay Windows breakaway launcher', () => {
it('is not requested by an ordinary relay launch', () => {
expect(parseRelayWindowsBreakawayLaunch(['node', 'relay.js', '--detached'])).toBeNull()
})
it('takes its own flags before -- and hands the rest to the relay', () => {
expect(parseRelayWindowsBreakawayLaunch(argv)).toEqual({
stdoutPath: 'C:/Users/me user/relay.log',
@@ -138,13 +134,6 @@ describe('relay Windows breakaway launcher', () => {
})
})
it('reports a missing launcher as unavailable', () => {
expect(launchRelayOutsideJob(requestFrom(argv), 'addon-missing', runtime)).toEqual({
report: { method: 'unavailable', reason: 'addon-missing' },
exitCode: RELAY_WINDOWS_BREAKAWAY_EXIT_CODES.unavailable
})
})
it('names an absent addon and one that predates the launcher', () => {
expect(
loadWindowsBreakawayLauncher(() => {
-18
View File
@@ -42,19 +42,6 @@ describe('resolveEmptyPageOutcome', () => {
).toEqual({ reason: 'load-failed', clampTotalPagesTo: null })
})
it('reports a failure without clamping for transient or unclassified errors', () => {
expect(resolveEmptyPageOutcome({ ...base, target: 5, failedCount: 2 })).toEqual({
reason: 'load-failed',
clampTotalPagesTo: null
})
for (const type of ['permission_denied', 'not_found', 'rate_limited', 'unknown'] as const) {
expect(resolveEmptyPageOutcome({ ...base, target: 5, errorTypes: [type] })).toEqual({
reason: 'load-failed',
clampTotalPagesTo: null
})
}
})
it('withdraws the speculative page on clean empty only while the count is unknown', () => {
expect(resolveEmptyPageOutcome({ ...base, target: 2 })).toEqual({
reason: 'end-of-data',
@@ -84,11 +71,6 @@ describe('applyEmptyPageClamp', () => {
expect(applyEmptyPageClamp(33, { ...clean, target: 2 })).toBe(33)
})
it('withdraws the speculative page while the count is unknown or failed', () => {
expect(applyEmptyPageClamp(null, { ...clean, target: 2 })).toBe(2)
expect(applyEmptyPageClamp(0, { ...clean, target: 2 })).toBe(2)
})
it('never touches the count for window or failure outcomes', () => {
expect(
applyEmptyPageClamp(39, { target: 33, failedCount: 0, errorTypes: ['validation_error'] })
@@ -2,15 +2,12 @@ import { describe, expect, it } from 'vitest'
import { MoveTab } from '../../rpc-contract/session-tabs-schemas-params'
import { TerminalCreateParams } from '../../rpc-contract/terminal-unary-params'
import {
translateCreateTerminalTab,
translateSessionTabClose,
translateSessionTabMove,
translateSetTabProps,
translateTerminalClose,
translateTerminalCloseTab,
translateTerminalCreate,
translateTerminalRename,
translateTerminalSplit,
type MethodTranslation
} from './old-client-method-translation'
import { translateUpdatePaneLayout } from './old-client-pane-layout-translation'
@@ -132,28 +129,6 @@ describe('translateTerminalCreate', () => {
)
})
it('creates a tab with unused ids', () => {
const { model } = setup()
expect(
translateTerminalCreate(
model,
WS,
createParams({ tabId: 'tab-new', leafId: NEW_LEAF, title: 'api' })
)
).toEqual({
ok: true,
commands: [
{
type: 'createTerminalTab',
workspace: WS,
tabId: 'tab-new',
leafId: NEW_LEAF,
title: 'api'
}
]
})
})
it.each([
['no ids (CLI, orchestration)', {}],
['a tab id alone', { tabId: 'tab-new' }],
@@ -258,28 +233,6 @@ describe('session.tabs translations', () => {
translateSetTabProps(WS, model.workspaces[WS]!, { tabId: 'gone', isPinned: true })
).toEqual({ ok: true, commands: [] })
})
it('creates a terminal tab after the tab a row names', () => {
const { model, tabA, leafA } = setup()
expect(
translateCreateTerminalTab(WS, model.workspaces[WS], {
afterTabId: terminalRowId(tabA, leafA),
targetGroupId: 'group-1',
viewMode: 'terminal'
})
).toEqual({
ok: true,
commands: [
{
type: 'createTerminalTab',
workspace: WS,
afterTabId: tabA,
groupId: 'group-1',
viewMode: 'terminal'
}
]
})
})
})
describe('session.tabs.move translation', () => {
@@ -438,19 +391,3 @@ describe('terminal.close and terminal.closeTab keep the pinned refusal', () => {
).toEqual([{ type: 'closePane', workspace: WS, tabId: tabA, leafId: leafA }])
})
})
describe('terminal.* translations on a resolved pane', () => {
it('splits below by default and resets an empty title', () => {
const { model, tabA, leafA } = setup()
const pane = { tab: terminalTab(model, tabA), leafId: leafA }
expect(commandsOf(translateTerminalSplit(WS, pane, undefined))).toEqual([
{ type: 'splitPane', workspace: WS, tabId: tabA, leafId: leafA, direction: 'horizontal' }
])
expect(commandsOf(translateTerminalRename(WS, pane, ''))).toEqual([
{ type: 'renameTab', workspace: WS, tabId: tabA, kind: 'custom', title: null }
])
expect(commandsOf(translateTerminalRename(WS, pane, 'api'))).toEqual([
{ type: 'renameTab', workspace: WS, tabId: tabA, kind: 'custom', title: 'api' }
])
})
})