Files
kandev/apps/web/lib/keyboard/utils.test.ts
T
2026-01-15 02:23:09 +00:00

334 lines
9.9 KiB
TypeScript

import { describe, expect, it, beforeEach, vi } from 'vitest';
import { detectPlatform, isMac, formatShortcut, matchesShortcut, shortcutToCodeMirrorKeybinding } from './utils';
import { KEYS, SHORTCUTS } from './constants';
import type { KeyboardShortcut } from './constants';
describe('detectPlatform', () => {
beforeEach(() => {
// Reset navigator mock before each test
vi.unstubAllGlobals();
});
it('detects Mac platform from navigator.platform', () => {
vi.stubGlobal('navigator', {
platform: 'MacIntel',
userAgent: 'Mozilla/5.0',
});
expect(detectPlatform()).toBe('mac');
});
it('detects Mac platform from userAgent', () => {
vi.stubGlobal('navigator', {
platform: 'unknown',
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)',
});
expect(detectPlatform()).toBe('mac');
});
it('detects Windows platform', () => {
vi.stubGlobal('navigator', {
platform: 'Win32',
userAgent: 'Mozilla/5.0 (Windows NT 10.0)',
});
expect(detectPlatform()).toBe('windows');
});
it('detects Linux platform', () => {
vi.stubGlobal('navigator', {
platform: 'Linux x86_64',
userAgent: 'Mozilla/5.0 (X11; Linux x86_64)',
});
expect(detectPlatform()).toBe('linux');
});
it('returns unknown when navigator is undefined', () => {
vi.stubGlobal('navigator', undefined);
expect(detectPlatform()).toBe('unknown');
});
it('returns unknown for unrecognized platform', () => {
vi.stubGlobal('navigator', {
platform: 'FreeBSD',
userAgent: 'Mozilla/5.0',
});
expect(detectPlatform()).toBe('unknown');
});
});
describe('isMac', () => {
it('returns true on Mac platform', () => {
vi.stubGlobal('navigator', {
platform: 'MacIntel',
userAgent: 'Mozilla/5.0',
});
expect(isMac()).toBe(true);
});
it('returns false on Windows platform', () => {
vi.stubGlobal('navigator', {
platform: 'Win32',
userAgent: 'Mozilla/5.0',
});
expect(isMac()).toBe(false);
});
});
describe('formatShortcut', () => {
it('formats Cmd+Enter on Mac', () => {
const shortcut: KeyboardShortcut = {
key: KEYS.ENTER,
modifiers: { ctrlOrCmd: true },
};
expect(formatShortcut(shortcut, 'mac')).toBe('Cmd+Enter');
});
it('formats Ctrl+Enter on Windows', () => {
const shortcut: KeyboardShortcut = {
key: KEYS.ENTER,
modifiers: { ctrlOrCmd: true },
};
expect(formatShortcut(shortcut, 'windows')).toBe('Ctrl+Enter');
});
it('formats Ctrl+Enter on Linux', () => {
const shortcut: KeyboardShortcut = {
key: KEYS.ENTER,
modifiers: { ctrlOrCmd: true },
};
expect(formatShortcut(shortcut, 'linux')).toBe('Ctrl+Enter');
});
it('formats Ctrl+S', () => {
const shortcut: KeyboardShortcut = {
key: KEYS.S,
modifiers: { ctrl: true },
};
expect(formatShortcut(shortcut, 'windows')).toBe('Ctrl+S');
});
it('formats Cmd+S on Mac', () => {
const shortcut: KeyboardShortcut = {
key: KEYS.S,
modifiers: { cmd: true },
};
expect(formatShortcut(shortcut, 'mac')).toBe('Cmd+S');
});
it('formats Alt as Option on Mac', () => {
const shortcut: KeyboardShortcut = {
key: KEYS.A,
modifiers: { alt: true },
};
expect(formatShortcut(shortcut, 'mac')).toBe('Option+A');
});
it('formats Alt on Windows', () => {
const shortcut: KeyboardShortcut = {
key: KEYS.A,
modifiers: { alt: true },
};
expect(formatShortcut(shortcut, 'windows')).toBe('Alt+A');
});
it('formats Shift+Ctrl+S', () => {
const shortcut: KeyboardShortcut = {
key: KEYS.S,
modifiers: { ctrl: true, shift: true },
};
expect(formatShortcut(shortcut, 'windows')).toBe('Ctrl+Shift+S');
});
it('formats key without modifiers', () => {
const shortcut: KeyboardShortcut = {
key: KEYS.ESCAPE,
};
expect(formatShortcut(shortcut, 'mac')).toBe('Esc');
});
it('formats special keys correctly', () => {
expect(formatShortcut({ key: KEYS.ESCAPE }, 'mac')).toBe('Esc');
expect(formatShortcut({ key: KEYS.SPACE }, 'mac')).toBe('Space');
expect(formatShortcut({ key: KEYS.TAB }, 'mac')).toBe('Tab');
expect(formatShortcut({ key: KEYS.DELETE }, 'mac')).toBe('Del');
});
it('formats arrow keys with symbols', () => {
expect(formatShortcut({ key: KEYS.ARROW_UP }, 'mac')).toBe('↑');
expect(formatShortcut({ key: KEYS.ARROW_DOWN }, 'mac')).toBe('↓');
expect(formatShortcut({ key: KEYS.ARROW_LEFT }, 'mac')).toBe('←');
expect(formatShortcut({ key: KEYS.ARROW_RIGHT }, 'mac')).toBe('→');
});
it('uppercases single letter keys', () => {
expect(formatShortcut({ key: KEYS.A }, 'mac')).toBe('A');
expect(formatShortcut({ key: KEYS.Z }, 'mac')).toBe('Z');
});
it('uses current platform when not specified', () => {
vi.stubGlobal('navigator', {
platform: 'MacIntel',
userAgent: 'Mozilla/5.0',
});
const shortcut: KeyboardShortcut = {
key: KEYS.ENTER,
modifiers: { ctrlOrCmd: true },
};
expect(formatShortcut(shortcut)).toBe('Cmd+Enter');
});
});
describe('matchesShortcut', () => {
it('matches exact key without modifiers', () => {
const shortcut: KeyboardShortcut = { key: KEYS.ESCAPE };
const event = new KeyboardEvent('keydown', { key: 'Escape' });
expect(matchesShortcut(event, shortcut)).toBe(true);
});
it('does not match different key', () => {
const shortcut: KeyboardShortcut = { key: KEYS.ESCAPE };
const event = new KeyboardEvent('keydown', { key: 'Enter' });
expect(matchesShortcut(event, shortcut)).toBe(false);
});
it('matches ctrlOrCmd with Ctrl key', () => {
const shortcut: KeyboardShortcut = {
key: KEYS.ENTER,
modifiers: { ctrlOrCmd: true },
};
const event = new KeyboardEvent('keydown', { key: 'Enter', ctrlKey: true });
expect(matchesShortcut(event, shortcut)).toBe(true);
});
it('matches ctrlOrCmd with Meta key', () => {
const shortcut: KeyboardShortcut = {
key: KEYS.ENTER,
modifiers: { ctrlOrCmd: true },
};
const event = new KeyboardEvent('keydown', { key: 'Enter', metaKey: true });
expect(matchesShortcut(event, shortcut)).toBe(true);
});
it('does not match ctrlOrCmd without modifier', () => {
const shortcut: KeyboardShortcut = {
key: KEYS.ENTER,
modifiers: { ctrlOrCmd: true },
};
const event = new KeyboardEvent('keydown', { key: 'Enter' });
expect(matchesShortcut(event, shortcut)).toBe(false);
});
it('matches Ctrl modifier', () => {
const shortcut: KeyboardShortcut = {
key: KEYS.S,
modifiers: { ctrl: true },
};
const event = new KeyboardEvent('keydown', { key: 's', ctrlKey: true });
expect(matchesShortcut(event, shortcut)).toBe(true);
});
it('matches Cmd modifier', () => {
const shortcut: KeyboardShortcut = {
key: KEYS.S,
modifiers: { cmd: true },
};
const event = new KeyboardEvent('keydown', { key: 's', metaKey: true });
expect(matchesShortcut(event, shortcut)).toBe(true);
});
it('matches Alt modifier', () => {
const shortcut: KeyboardShortcut = {
key: KEYS.A,
modifiers: { alt: true },
};
const event = new KeyboardEvent('keydown', { key: 'a', altKey: true });
expect(matchesShortcut(event, shortcut)).toBe(true);
});
it('matches Shift modifier', () => {
const shortcut: KeyboardShortcut = {
key: KEYS.A,
modifiers: { shift: true },
};
const event = new KeyboardEvent('keydown', { key: 'a', shiftKey: true });
expect(matchesShortcut(event, shortcut)).toBe(true);
});
it('matches multiple modifiers', () => {
const shortcut: KeyboardShortcut = {
key: KEYS.S,
modifiers: { ctrl: true, shift: true },
};
const event = new KeyboardEvent('keydown', {
key: 's',
ctrlKey: true,
shiftKey: true,
});
expect(matchesShortcut(event, shortcut)).toBe(true);
});
it('does not match when extra modifier is pressed', () => {
const shortcut: KeyboardShortcut = {
key: KEYS.S,
modifiers: { ctrl: true },
};
const event = new KeyboardEvent('keydown', {
key: 's',
ctrlKey: true,
shiftKey: true,
});
expect(matchesShortcut(event, shortcut)).toBe(false);
});
it('does not match when required modifier is missing', () => {
const shortcut: KeyboardShortcut = {
key: KEYS.S,
modifiers: { ctrl: true, shift: true },
};
const event = new KeyboardEvent('keydown', { key: 's', ctrlKey: true });
expect(matchesShortcut(event, shortcut)).toBe(false);
});
it('matches ctrlOrCmd with additional modifiers', () => {
const shortcut: KeyboardShortcut = {
key: KEYS.S,
modifiers: { ctrlOrCmd: true, shift: true },
};
const event = new KeyboardEvent('keydown', {
key: 's',
ctrlKey: true,
shiftKey: true,
});
expect(matchesShortcut(event, shortcut)).toBe(true);
});
it('does not match when no modifiers expected but some pressed', () => {
const shortcut: KeyboardShortcut = { key: KEYS.ESCAPE };
const event = new KeyboardEvent('keydown', { key: 'Escape', ctrlKey: true });
expect(matchesShortcut(event, shortcut)).toBe(false);
});
it('works with predefined SHORTCUTS', () => {
const event = new KeyboardEvent('keydown', { key: 'Enter', metaKey: true });
expect(matchesShortcut(event, SHORTCUTS.SUBMIT)).toBe(true);
});
});
describe('shortcutToCodeMirrorKeybinding', () => {
it('formats ctrlOrCmd shortcuts with Mod', () => {
expect(shortcutToCodeMirrorKeybinding(SHORTCUTS.SUBMIT)).toBe('Mod-Enter');
});
it('formats explicit modifiers', () => {
const shortcut: KeyboardShortcut = {
key: KEYS.S,
modifiers: { ctrl: true, shift: true },
};
expect(shortcutToCodeMirrorKeybinding(shortcut)).toBe('Ctrl-Shift-s');
});
it('formats without modifiers', () => {
const shortcut: KeyboardShortcut = { key: KEYS.ESCAPE };
expect(shortcutToCodeMirrorKeybinding(shortcut)).toBe('Escape');
});
});