SPB Git

Repository Public

keys.test.ts 2.8 KB 60 lines typescript

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
Files main
Menu

Theme