Files
Psychotoxical-psysonic/src/store/queueUndoHotkey.test.ts
T
Frank Stellmacher b0418bf920 refactor(player): E.28 — extract storage + hotkey helpers cluster (#592)
Two small file-private bits move out of playerStore.ts:

  - `src/store/queueVisibilityStorage.ts` — `readInitialQueueVisibility`
    and `persistQueueVisibility` (the QueuePanel show/hide toggle
    survives reloads via a localStorage round-trip; SSR / private-mode
    failures swallowed silently).
  - `src/store/queueUndoHotkey.ts` — `installQueueUndoHotkey` (Ctrl+Z /
    Cmd+Z queue undo, Ctrl+Shift+Z redo, document-capture; skips text
    fields so native text undo stays; idempotent via window-scoped
    flag; mini-player window skipped). Added a `_resetQueueUndoHotkeyForTest`
    that removes the keydown listener too (needed so vitest specs
    don't accumulate handlers across tests).

playerStore re-exports `installQueueUndoHotkey` so bootstrap + tests
keep their existing `from './playerStore'` imports. The
queue-visibility helpers were file-private; no re-exports.

Side-cleanup: `getWindowKind` import is gone from playerStore (only
the moved hotkey installer used it).

16 focused tests pin the storage round-trip, the idempotency flag, the
modifier + key matching, the editable-target skip, and the
preventDefault contract.

playerStore 1940 → 1879 LOC.
2026-05-12 19:41:57 +02:00

131 lines
3.9 KiB
TypeScript

/**
* Smoke + behaviour tests for the queue-undo hotkey installer. Verifies
* idempotency (the second call is a no-op), Ctrl+Z → undo, Ctrl+Shift+Z
* → redo, and the editable-field guard that lets the browser keep
* native text undo inside input/textarea/contenteditable.
*/
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
const hoisted = vi.hoisted(() => ({
windowKindMock: vi.fn(() => 'main' as 'main' | 'mini'),
undoMock: vi.fn(() => true),
redoMock: vi.fn(() => true),
}));
vi.mock('../app/windowKind', () => ({ getWindowKind: hoisted.windowKindMock }));
vi.mock('./playerStore', () => ({
usePlayerStore: {
getState: () => ({
undoLastQueueEdit: hoisted.undoMock,
redoLastQueueEdit: hoisted.redoMock,
}),
},
}));
import { _resetQueueUndoHotkeyForTest, installQueueUndoHotkey } from './queueUndoHotkey';
beforeEach(() => {
hoisted.windowKindMock.mockReturnValue('main');
hoisted.undoMock.mockReset();
hoisted.undoMock.mockReturnValue(true);
hoisted.redoMock.mockReset();
hoisted.redoMock.mockReturnValue(true);
_resetQueueUndoHotkeyForTest();
});
afterEach(() => {
_resetQueueUndoHotkeyForTest();
});
function fireKey(key: string, opts: Partial<KeyboardEventInit> = {}, target?: HTMLElement): boolean {
const event = new KeyboardEvent('keydown', {
key,
code: 'Key' + key.toUpperCase(),
bubbles: true,
cancelable: true,
composed: true,
...opts,
});
(target ?? document.body).dispatchEvent(event);
return event.defaultPrevented;
}
describe('installQueueUndoHotkey', () => {
it('is a no-op when window kind is mini', () => {
hoisted.windowKindMock.mockReturnValue('mini');
installQueueUndoHotkey();
fireKey('z', { ctrlKey: true });
expect(hoisted.undoMock).not.toHaveBeenCalled();
});
it('fires undoLastQueueEdit on Ctrl+Z', () => {
installQueueUndoHotkey();
const prevented = fireKey('z', { ctrlKey: true });
expect(hoisted.undoMock).toHaveBeenCalledTimes(1);
expect(prevented).toBe(true);
});
it('fires redoLastQueueEdit on Ctrl+Shift+Z', () => {
installQueueUndoHotkey();
fireKey('z', { ctrlKey: true, shiftKey: true });
expect(hoisted.redoMock).toHaveBeenCalledTimes(1);
expect(hoisted.undoMock).not.toHaveBeenCalled();
});
it('honours Cmd+Z (metaKey) on macOS', () => {
installQueueUndoHotkey();
fireKey('z', { metaKey: true });
expect(hoisted.undoMock).toHaveBeenCalledTimes(1);
});
it('is idempotent — second install attaches no second listener', () => {
installQueueUndoHotkey();
installQueueUndoHotkey();
fireKey('z', { ctrlKey: true });
expect(hoisted.undoMock).toHaveBeenCalledTimes(1);
});
it('does NOT preventDefault when undoLastQueueEdit returns false', () => {
hoisted.undoMock.mockReturnValueOnce(false);
installQueueUndoHotkey();
const prevented = fireKey('z', { ctrlKey: true });
expect(prevented).toBe(false);
});
it('skips when the target is an INPUT element (native text undo)', () => {
installQueueUndoHotkey();
const input = document.createElement('input');
document.body.appendChild(input);
try {
fireKey('z', { ctrlKey: true }, input);
expect(hoisted.undoMock).not.toHaveBeenCalled();
} finally {
input.remove();
}
});
it('skips when the target is a TEXTAREA', () => {
installQueueUndoHotkey();
const ta = document.createElement('textarea');
document.body.appendChild(ta);
try {
fireKey('z', { ctrlKey: true }, ta);
expect(hoisted.undoMock).not.toHaveBeenCalled();
} finally {
ta.remove();
}
});
it('does nothing for keys other than Z', () => {
installQueueUndoHotkey();
fireKey('a', { ctrlKey: true });
expect(hoisted.undoMock).not.toHaveBeenCalled();
});
it('does nothing without Ctrl / Cmd modifier', () => {
installQueueUndoHotkey();
fireKey('z');
expect(hoisted.undoMock).not.toHaveBeenCalled();
});
});