Files
Genarrative/apps/ai-game-creator-shell/tests/uiEditorKeyboardShortcuts.test.ts
T

110 lines
3.5 KiB
TypeScript

// @vitest-environment jsdom
import { fireEvent } from '@testing-library/react';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { handleUiEditorKeyDown } from '../src/view/ui-editor/uiEditorKeyboardShortcuts';
describe('ui editor keyboard shortcuts', () => {
afterEach(() => {
document.body.replaceChildren();
});
it.each([
{ key: 'z', ctrlKey: true },
{ key: 'z', ctrlKey: true, shiftKey: true },
{ key: 'y', ctrlKey: true },
])('does not change history from inside a modal (%o)', (shortcut) => {
const dialog = document.createElement('div');
dialog.setAttribute('role', 'dialog');
const button = document.createElement('button');
dialog.append(button);
document.body.append(dialog);
const historyUndo = vi.fn(() => true);
const historyRedo = vi.fn(() => true);
const listener = (event: KeyboardEvent) =>
handleUiEditorKeyDown(event, {
selectedNodeId: null,
activeImageId: null,
deleteNode: vi.fn(),
historyUndo,
historyRedo,
});
window.addEventListener('keydown', listener);
fireEvent.keyDown(button, shortcut);
expect(historyUndo).not.toHaveBeenCalled();
expect(historyRedo).not.toHaveBeenCalled();
window.removeEventListener('keydown', listener);
});
it('keeps undo available from a non-modal button', () => {
const button = document.createElement('button');
document.body.append(button);
const historyUndo = vi.fn(() => true);
const historyRedo = vi.fn(() => true);
const listener = (event: KeyboardEvent) =>
handleUiEditorKeyDown(event, {
selectedNodeId: null,
activeImageId: null,
deleteNode: vi.fn(),
historyUndo,
historyRedo,
});
window.addEventListener('keydown', listener);
fireEvent.keyDown(button, { key: 'z', ctrlKey: true });
expect(historyUndo).toHaveBeenCalledTimes(1);
window.removeEventListener('keydown', listener);
});
it.each(['Delete', 'Backspace'])(
'does not delete a node when the zoom slider has focus (%s)',
(key) => {
const slider = document.createElement('input');
slider.type = 'range';
document.body.append(slider);
const deleteNode = vi.fn(() => ({ ok: true }));
const listener = (event: KeyboardEvent) =>
handleUiEditorKeyDown(event, {
selectedNodeId: 'node-1',
activeImageId: 'page-1',
deleteNode,
historyUndo: vi.fn(() => true),
historyRedo: vi.fn(() => true),
});
window.addEventListener('keydown', listener);
fireEvent.keyDown(slider, { key });
expect(deleteNode).not.toHaveBeenCalled();
window.removeEventListener('keydown', listener);
},
);
it('keeps node deletion available after zoom button focus is released', () => {
const zoomControls = document.createElement('div');
const zoomIn = document.createElement('button');
zoomControls.append(zoomIn);
document.body.append(zoomControls);
const deleteNode = vi.fn(() => ({ ok: true }));
const listener = (event: KeyboardEvent) =>
handleUiEditorKeyDown(event, {
selectedNodeId: 'node-1',
activeImageId: 'page-1',
deleteNode,
historyUndo: vi.fn(() => true),
historyRedo: vi.fn(() => true),
});
window.addEventListener('keydown', listener);
fireEvent.click(zoomIn);
fireEvent.keyDown(window, { key: 'Delete' });
expect(deleteNode).toHaveBeenCalledWith('node-1', 'page-1');
window.removeEventListener('keydown', listener);
});
});