Files
ycode/scripts/capability-picker-regression.tsx
T

80 lines
4.7 KiB
TypeScript

import assert from 'node:assert/strict'
import { renderToStaticMarkup } from 'react-dom/server'
import {
CapabilityPicker,
capabilitySelectionKey,
detectCapabilitySlashTrigger,
filterCapabilityItems,
resolveCapabilityPickerKey,
type CapabilityPickerItem
} from '../src/renderer/src/workbuddy/composer/CapabilityPicker'
const items: CapabilityPickerItem[] = [
{ id: 'skill:review', name: '代码审查', description: '检查实现缺陷与风险', kind: 'skill' },
{ id: 'mcp-server', name: 'postgres', description: '查询本地数据库', kind: 'mcp' },
{ id: 'compact', name: 'compact', description: '压缩当前上下文', kind: 'command' }
]
const checks: string[] = []
const check = (name: string, actual: unknown, expected: unknown): void => {
assert.deepEqual(actual, expected, name)
checks.push(name)
}
check('empty query preserves registration order', filterCapabilityItems(items, ''), items)
check('filter matches names and IDs case-insensitively',
filterCapabilityItems(items, '/POST').map(item => item.id), ['mcp-server'])
check('filter matches descriptions', filterCapabilityItems(items, '上下文').map(item => item.id), ['compact'])
check('filter matches kind labels', filterCapabilityItems(items, '命令').map(item => item.id), ['compact'])
check('multiple terms must all match', filterCapabilityItems(items, '数据库 本地').map(item => item.id), ['mcp-server'])
check('selection identity includes kind', capabilitySelectionKey({ id: 'compact', kind: 'command' }), 'command:compact')
check('slash at input start is detected',
detectCapabilitySlashTrigger('/comp', 5, items), { start: 0, end: 5, query: 'comp' })
const independent = '请使用 /mcp'
check('independent slash token is detected',
detectCapabilitySlashTrigger(independent, independent.length, items), { start: 4, end: independent.length, query: 'mcp' })
check('plain slash opens the catalog when items exist',
detectCapabilitySlashTrigger('/', 1, items), { start: 0, end: 1, query: '' })
for (const value of ['https://example.com/a', 'src/renderer/file.ts', 'C:\\work\\file.ts', '/Users/me/file.txt', 'file:///tmp/a']) {
check('path or URL does not trigger: ' + value, detectCapabilitySlashTrigger(value, value.length, items), null)
}
check('unknown leading token is rejected when catalog is available',
detectCapabilitySlashTrigger('/Users', 6, items), null)
check('slash inside ordinary text is not independent',
detectCapabilitySlashTrigger('path/compact', 12, items), null)
check('IME composition suppresses Enter',
resolveCapabilityPickerKey({ key: 'Enter', isComposing: true, itemCount: 3, activeIndex: 1 }), { type: 'none' })
check('IME keyCode 229 suppresses selection',
resolveCapabilityPickerKey({ key: 'Enter', keyCode: 229, itemCount: 3, activeIndex: 1 }), { type: 'none' })
check('ArrowDown wraps', resolveCapabilityPickerKey({ key: 'ArrowDown', itemCount: 3, activeIndex: 2 }),
{ type: 'move', index: 0 })
check('ArrowUp wraps', resolveCapabilityPickerKey({ key: 'ArrowUp', itemCount: 3, activeIndex: 0 }),
{ type: 'move', index: 2 })
check('Home and End select boundaries',
[resolveCapabilityPickerKey({ key: 'Home', itemCount: 3, activeIndex: 2 }),
resolveCapabilityPickerKey({ key: 'End', itemCount: 3, activeIndex: 0 })],
[{ type: 'move', index: 0 }, { type: 'move', index: 2 }])
check('Enter selects the active item',
resolveCapabilityPickerKey({ key: 'Enter', itemCount: 3, activeIndex: 1 }), { type: 'select', index: 1 })
check('Escape closes', resolveCapabilityPickerKey({ key: 'Escape', itemCount: 3, activeIndex: 1 }), { type: 'close' })
check('Enter is ignored on an empty result', resolveCapabilityPickerKey({ key: 'Enter', itemCount: 0, activeIndex: 0 }),
{ type: 'none' })
const html = renderToStaticMarkup(
<CapabilityPicker items={items} selected={[{ id: 'skill:review', kind: 'skill' }]}
onSelect={() => undefined} onClose={() => undefined} />
)
check('SSR renders combobox and listbox', [html.includes('role="combobox"'), html.includes('role="listbox"')], [true, true])
check('SSR exposes type labels', ['技能', 'MCP', '命令'].every(label => html.includes(label)), true)
check('SSR marks selected identity', html.includes('aria-selected="true"'), true)
check('SSR renders descriptions', html.includes('检查实现缺陷与风险'), true)
check('SSR seeds external query',
renderToStaticMarkup(<CapabilityPicker items={items} selected={[]} onSelect={() => undefined}
onClose={() => undefined} query="compact" />).includes('data-kind="command"'), true)
check('SSR renders explicit empty state',
renderToStaticMarkup(<CapabilityPicker items={items} selected={[]} onSelect={() => undefined}
onClose={() => undefined} query="missing" />).includes('没有匹配的能力'), true)
console.log(JSON.stringify({ result: 'PASS', checks: checks.length }))