Plain node activation no longer navigates, case cards stay quiet evidence rows, and the terminal keeps one top-to-bottom scrollport. Co-authored-by: Cursor <cursoragent@cursor.com>
107 lines
3.8 KiB
TypeScript
107 lines
3.8 KiB
TypeScript
import {
|
|
applyCompletion,
|
|
isUnsafeNavigateTarget,
|
|
navigateTargetKeys,
|
|
parseCommand,
|
|
suggestCompletions,
|
|
} from './terminal-commands';
|
|
|
|
describe('terminal command grammar', () => {
|
|
it('parses help, history, clear, brew and rev', () => {
|
|
expect(parseCommand('help')).toEqual({ kind: 'help' });
|
|
expect(parseCommand('history')).toEqual({ kind: 'history' });
|
|
expect(parseCommand('clear')).toEqual({ kind: 'clear' });
|
|
expect(parseCommand('brew')).toEqual({ kind: 'brew' });
|
|
expect(parseCommand('rev')).toEqual({ kind: 'rev' });
|
|
expect(parseCommand(' Hilfe ')).toEqual({ kind: 'help' });
|
|
expect(parseCommand('')).toEqual({ kind: 'empty' });
|
|
});
|
|
|
|
it('resolves navigate targets including the four service fragments', () => {
|
|
expect(parseCommand('navigate home')).toEqual({
|
|
kind: 'navigate',
|
|
resolution: { kind: 'route', routeId: 'home' },
|
|
targetKey: 'home',
|
|
});
|
|
expect(parseCommand('navigate pitch')).toEqual({
|
|
kind: 'navigate',
|
|
resolution: { kind: 'route', routeId: 'pitch' },
|
|
targetKey: 'pitch',
|
|
});
|
|
expect(parseCommand('navigate services')).toEqual({
|
|
kind: 'navigate',
|
|
resolution: { kind: 'route', routeId: 'services' },
|
|
targetKey: 'services',
|
|
});
|
|
expect(parseCommand('navigate services ai')).toEqual({
|
|
kind: 'navigate',
|
|
resolution: { kind: 'route', routeId: 'services', fragment: 'ai-workflows' },
|
|
targetKey: 'services ai',
|
|
});
|
|
expect(parseCommand('navigate leistungen software')).toEqual({
|
|
kind: 'navigate',
|
|
resolution: { kind: 'route', routeId: 'services', fragment: 'software-data' },
|
|
targetKey: 'services software',
|
|
});
|
|
expect(parseCommand('navigate services infrastructure')).toEqual({
|
|
kind: 'navigate',
|
|
resolution: { kind: 'route', routeId: 'services', fragment: 'infrastructure-network' },
|
|
targetKey: 'services infrastructure',
|
|
});
|
|
expect(parseCommand('navigate services platform')).toEqual({
|
|
kind: 'navigate',
|
|
resolution: { kind: 'route', routeId: 'services', fragment: 'platform-operations' },
|
|
targetKey: 'services platform',
|
|
});
|
|
expect(parseCommand('navigate cv')).toEqual({
|
|
kind: 'navigate',
|
|
resolution: { kind: 'cv' },
|
|
targetKey: 'cv',
|
|
});
|
|
});
|
|
|
|
it('suggests valid targets for incomplete navigate input', () => {
|
|
const parsed = parseCommand('navigate');
|
|
expect(parsed.kind).toBe('incomplete');
|
|
if (parsed.kind === 'incomplete') {
|
|
expect(parsed.suggestions).toEqual([...navigateTargetKeys()]);
|
|
}
|
|
|
|
const unknown = parseCommand('navigate nowhere');
|
|
expect(unknown.kind).toBe('unknown-target');
|
|
if (unknown.kind === 'unknown-target') {
|
|
expect(unknown.target).toBe('nowhere');
|
|
}
|
|
});
|
|
|
|
it('completes the current token against commands and navigate targets', () => {
|
|
expect(suggestCompletions('he')).toEqual(['help']);
|
|
expect(applyCompletion('he')).toEqual({ value: 'help', candidates: ['help'] });
|
|
expect(suggestCompletions('navigate p')).toEqual(['pitch', 'profil', 'projects', 'projekte']);
|
|
expect(applyCompletion('navigate pit')).toEqual({
|
|
value: 'navigate pitch',
|
|
candidates: ['pitch'],
|
|
});
|
|
expect(suggestCompletions('navigate services a')).toEqual(['services ai']);
|
|
});
|
|
|
|
it('rejects unsafe navigate targets without treating them as routes', () => {
|
|
const hostile = [
|
|
'navigate https://example.com',
|
|
'navigate ../secret',
|
|
'navigate /etc/passwd',
|
|
'navigate javascript:alert(1)',
|
|
];
|
|
|
|
for (const input of hostile) {
|
|
const target = input.slice('navigate '.length);
|
|
expect(isUnsafeNavigateTarget(target), input).toBe(true);
|
|
expect(parseCommand(input)).toEqual({
|
|
kind: 'unknown-target',
|
|
input,
|
|
target,
|
|
});
|
|
}
|
|
});
|
|
});
|