cc-haha/desktop/src/components/chat/composerUtils.test.ts
程序员阿江(Relakkes) caa164b62c fix: make desktop slash commands behave like the CLI
Desktop had no reliable handling for common local slash commands, so
commands like /clear, /help, /context, /cost, and /compact either looked
unresponsive or lost their CLI-specific side effects. This routes desktop
commands by behavior: local panels stay local, stateful clear resets the
session transcript, and CLI-local outputs are rendered in chat.

Constraint: Preserve existing CLI semantics for prompt, local, local-jsx, and compact slash commands
Rejected: Send every slash command through the normal chat path | local commands need desktop UI or session state side effects
Confidence: high
Scope-risk: moderate
Directive: Keep hidden aliases such as /plugins out of the visible command list unless they become canonical CLI commands
Tested: cd desktop && bun run test src/components/chat/composerUtils.test.ts src/stores/chatStore.test.ts --run
Tested: cd desktop && bun run test src/__tests__/pages.test.tsx --run -t "ActiveSession routes /plugin|ActiveSession routes /help|EmptySession slash picker includes dynamic skills"
Tested: bun test src/server/__tests__/conversations.test.ts
Tested: cd desktop && bun run lint
Tested: agent-browser E2E for /help, /plugin, /plugins, /cost, /clear, /context, /compact, /mcp, and normal text input
Not-tested: Root tsc project because it currently includes generated Tauri target assets and extracted native files
2026-04-28 16:38:16 +08:00

64 lines
2.1 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import {
findSlashToken,
insertSlashTrigger,
mergeSlashCommands,
replaceSlashCommand,
resolveSlashUiAction,
} from './composerUtils'
describe('composerUtils', () => {
it('finds slash token without trailing space', () => {
expect(findSlashToken('/rev', 4)).toEqual({ start: 0, filter: 'rev' })
expect(findSlashToken('hello /rev', 10)).toEqual({ start: 6, filter: 'rev' })
})
it('does not treat slash followed by a space as an active token', () => {
expect(findSlashToken('/ review', 8)).toBeNull()
})
it('inserts a slash trigger without appending a trailing space', () => {
expect(insertSlashTrigger('', 0)).toEqual({ value: '/', cursorPos: 1 })
expect(insertSlashTrigger('hello', 5)).toEqual({ value: 'hello /', cursorPos: 7 })
})
it('replaces the current slash token with a command and one trailing separator', () => {
expect(replaceSlashCommand('/rev', 4, 'review')).toEqual({
value: '/review ',
cursorPos: 8,
})
})
it('merges fallback commands so built-in entries like /clear remain visible', () => {
expect(
mergeSlashCommands([
{ name: 'help', description: '' },
]),
).toEqual(
expect.arrayContaining([
{ name: 'help', description: 'Show available desktop and agent commands' },
{ name: 'clear', description: 'Clear conversation history' },
{ name: 'context', description: 'Show current context usage' },
]),
)
})
it('keeps server-provided descriptions when they exist', () => {
expect(
mergeSlashCommands([
{ name: 'clear', description: 'Server description' },
]),
).toEqual(
expect.arrayContaining([
{ name: 'clear', description: 'Server description' },
]),
)
})
it('resolves hidden settings aliases without displaying duplicate fallback rows', () => {
expect(resolveSlashUiAction('plugins')).toEqual({ type: 'settings', tab: 'plugins' })
expect(mergeSlashCommands([]).map((command) => command.name)).toContain('plugin')
expect(mergeSlashCommands([]).map((command) => command.name)).not.toContain('plugins')
})
})