import { describe, expect, it } from 'vitest'; import { ORBITAL } from '../../app/static/js/config.js'; import { codeToDirection, ComboInput, isPrefix, validateCommands, } from '../../app/static/js/logic/comboInput.js'; const COMMANDS = [ { id: 'strike', sequence: ['up', 'right', 'down', 'down'] }, { id: 'supply', sequence: ['down', 'down', 'up', 'right'] }, { id: 'turret', sequence: ['down', 'up', 'right', 'right', 'up'] }, ]; const typeAll = (combo, dirs) => dirs.map((d) => combo.push(d)); describe('codeToDirection', () => { it('bildet Pfeiltasten und WASD (physische Position) ab', () => { expect(codeToDirection('ArrowUp')).toBe('up'); expect(codeToDirection('KeyW')).toBe('up'); expect(codeToDirection('KeyA')).toBe('left'); expect(codeToDirection('ArrowRight')).toBe('right'); expect(codeToDirection('KeyS')).toBe('down'); expect(codeToDirection('KeyQ')).toBeNull(); }); }); describe('isPrefix', () => { it('erkennt Anfänge von Folgen', () => { expect(isPrefix([], ['up'])).toBe(true); expect(isPrefix(['up'], ['up', 'down'])).toBe(true); expect(isPrefix(['down'], ['up', 'down'])).toBe(false); expect(isPrefix(['up', 'down', 'up'], ['up', 'down'])).toBe(false); }); }); describe('validateCommands', () => { it('akzeptiert die Befehle aus der Konfiguration', () => { expect(() => validateCommands(ORBITAL.commands)).not.toThrow(); }); it('lehnt Folgen ab, die Anfang einer anderen sind', () => { expect(() => validateCommands([ { id: 'a', sequence: ['up', 'down'] }, { id: 'b', sequence: ['up', 'down', 'left'] }, ]), ).toThrow(/Anfang/); }); it('lehnt doppelte IDs, leere Folgen und unbekannte Richtungen ab', () => { expect(() => validateCommands([{ id: 'a', sequence: ['up'] }, { id: 'a', sequence: ['down'] }])).toThrow(); expect(() => validateCommands([{ id: 'a', sequence: [] }])).toThrow(); expect(() => validateCommands([{ id: 'a', sequence: ['oben'] }])).toThrow(); }); }); describe('ComboInput', () => { it('ignoriert Eingaben, solange die Halte-Taste nicht gedrückt ist', () => { const combo = new ComboInput(COMMANDS); expect(combo.push('up').status).toBe('inactive'); }); it('erkennt eine vollständige Folge', () => { const combo = new ComboInput(COMMANDS); combo.begin(); const results = typeAll(combo, ['up', 'right', 'down', 'down']); expect(results.slice(0, 3).map((r) => r.status)).toEqual(['progress', 'progress', 'progress']); expect(results[3]).toEqual({ status: 'match', commandId: 'strike' }); expect(combo.buffer).toEqual([]); }); it('meldet passende Kandidaten während der Eingabe', () => { const combo = new ComboInput(COMMANDS); combo.begin(); expect(combo.push('down')).toEqual({ status: 'progress', candidates: ['supply', 'turret'] }); expect(combo.push('up')).toEqual({ status: 'progress', candidates: ['turret'] }); }); it('meldet Fehleingaben und erlaubt direkt einen neuen Versuch', () => { const combo = new ComboInput(COMMANDS); combo.begin(); combo.push('up'); expect(combo.push('up').status).toBe('fail'); expect(combo.buffer).toEqual([]); const retry = typeAll(combo, ['down', 'down', 'up', 'right']); expect(retry.at(-1)).toEqual({ status: 'match', commandId: 'supply' }); }); it('erlaubt mehrere Befehle hintereinander, solange die Taste gehalten wird', () => { const combo = new ComboInput(COMMANDS); combo.begin(); expect(typeAll(combo, ['up', 'right', 'down', 'down']).at(-1).commandId).toBe('strike'); expect(typeAll(combo, ['down', 'up', 'right', 'right', 'up']).at(-1).commandId).toBe('turret'); }); it('verwirft halbe Eingaben beim Loslassen', () => { const combo = new ComboInput(COMMANDS); combo.begin(); typeAll(combo, ['up', 'right']); combo.end(); combo.begin(); expect(combo.push('down').status).toBe('progress'); expect(combo.buffer).toEqual(['down']); }); it('ignoriert unbekannte Richtungen ohne den Puffer zu verändern', () => { const combo = new ComboInput(COMMANDS); combo.begin(); combo.push('up'); expect(combo.push('diagonal').status).toBe('ignored'); expect(combo.buffer).toEqual(['up']); }); it('zeigt den Fortschritt je Befehl für das HUD', () => { const combo = new ComboInput(COMMANDS); combo.begin(); typeAll(combo, ['down', 'down']); expect(combo.progressFor('supply')).toBe(2); expect(combo.progressFor('turret')).toBe(0); expect(combo.progressFor('strike')).toBe(0); expect(combo.progressFor('unbekannt')).toBe(0); }); });