Repository Public
1import { describe, expect, it } from 'vitest';2import { applyModsToInput, consumeMods, ctrlChar, keySequence, NO_MODS, tapMod, type Mods } from '@/components/terminal/keys';34const m = (p: Partial<Mods>): Mods => ({ ...NO_MODS, ...p });56describe('terminal key sequences', () => {7 it('plain keys', () => {8 expect(keySequence('esc')).toBe('\x1b');9 expect(keySequence('tab')).toBe('\t');10 expect(keySequence('enter')).toBe('\r');11 expect(keySequence('up')).toBe('\x1b[A');12 expect(keySequence('down')).toBe('\x1b[B');13 expect(keySequence('right')).toBe('\x1b[C');14 expect(keySequence('left')).toBe('\x1b[D');15 expect(keySequence('|')).toBe('|');16 expect(keySequence('pgup')).toBe('\x1b[5~');17 });18 it('application cursor mode uses SS3, but not when modified', () => {19 expect(keySequence('up', NO_MODS, true)).toBe('\x1bOA');20 expect(keySequence('left', NO_MODS, true)).toBe('\x1bOD');21 expect(keySequence('up', m({ ctrl: 1 }), true)).toBe('\x1b[1;5A');22 });23 it('modifier parameter follows xterm (1 + shift + 2·alt + 4·ctrl)', () => {24 expect(keySequence('right', m({ shift: 1 }))).toBe('\x1b[1;2C');25 expect(keySequence('right', m({ alt: 1 }))).toBe('\x1b[1;3C');26 expect(keySequence('right', m({ ctrl: 2, shift: 1 }))).toBe('\x1b[1;6C');27 expect(keySequence('del', m({ ctrl: 1 }))).toBe('\x1b[3;5~');28 });29 it('shift+tab, alt prefixes', () => {30 expect(keySequence('tab', m({ shift: 1 }))).toBe('\x1b[Z');31 expect(keySequence('enter', m({ alt: 1 }))).toBe('\x1b\r');32 expect(keySequence('/', m({ alt: 1 }))).toBe('\x1b/');33 });34 it('control characters', () => {35 expect(ctrlChar('c')).toBe('\x03');36 expect(ctrlChar('C')).toBe('\x03');37 expect(ctrlChar('[')).toBe('\x1b');38 expect(ctrlChar(' ')).toBe('\x00');39 expect(ctrlChar('é')).toBeNull();40 });41 it('modifiers apply to one typed character only', () => {42 expect(applyModsToInput('c', m({ ctrl: 1 }))).toBe('\x03');43 expect(applyModsToInput('b', m({ alt: 1 }))).toBe('\x1bb');44 expect(applyModsToInput('a', m({ shift: 1 }))).toBe('A');45 expect(applyModsToInput('x', m({ ctrl: 1, alt: 1 }))).toBe('\x1b\x18');46 expect(applyModsToInput('\t', m({ shift: 1 }))).toBe('\x1b[Z');47 expect(applyModsToInput('pasted text', m({ ctrl: 2 }))).toBe('pasted text');48 expect(applyModsToInput('\x1b[A', m({ ctrl: 1 }))).toBe('\x1b[A');49 expect(applyModsToInput('q', NO_MODS)).toBe('q');50 });51 it('sticky state machine: tap = one-shot, double tap = lock, tap again = off', () => {52 expect(tapMod(0, false)).toBe(1);53 expect(tapMod(0, true)).toBe(1); // a stale "double tap" cannot lock from off54 expect(tapMod(1, true)).toBe(2);55 expect(tapMod(1, false)).toBe(0);56 expect(tapMod(2, false)).toBe(0);57 expect(consumeMods({ ctrl: 1, alt: 2, shift: 0 })).toEqual({ ctrl: 0, alt: 2, shift: 0 });58 });59});60