Files

79 lines
3.8 KiB
JavaScript
Raw Permalink Normal View History

2026-07-12 02:26:01 +08:00
import test from 'node:test';
import assert from 'node:assert/strict';
import {
normalizeShortcutKey,
resolveGlobalShortcut,
resolveMemoryShortcut,
} from '../app/src/renderer/src/keyboard-shortcuts.mjs';
const context = {
isTextInput: false,
isListRoute: true,
hasSelection: false,
hasQuery: false,
};
test('global shortcuts switch between routed library views', () => {
assert.equal(resolveGlobalShortcut({ key: '1', metaKey: true }, context), 'open-sessions');
assert.equal(resolveGlobalShortcut({ key: '2', ctrlKey: true }, context), 'open-active-memories');
assert.equal(resolveGlobalShortcut({ key: '3', metaKey: true }, context), 'open-archived-memories');
assert.equal(resolveGlobalShortcut({ key: '4', metaKey: true }, context), null);
});
test('global shortcuts focus search and toggle list sorting', () => {
assert.equal(resolveGlobalShortcut({ key: '/' }, context), 'focus-search');
assert.equal(resolveGlobalShortcut({ key: '/' }, { ...context, isListRoute: false }), null);
assert.equal(resolveGlobalShortcut({ key: 's' }, context), 'toggle-sort');
assert.equal(resolveGlobalShortcut({ key: 's' }, { ...context, isListRoute: false }), null);
});
test('printable shortcut keys are normalized without changing named keys', () => {
assert.equal(normalizeShortcutKey({ key: 'J' }), 'j');
assert.equal(normalizeShortcutKey({ key: 'K' }), 'k');
assert.equal(normalizeShortcutKey({ key: 'ArrowDown' }), 'ArrowDown');
});
test('Escape clears selection before query and blurs text inputs', () => {
assert.equal(resolveGlobalShortcut({ key: 'Escape' }, { ...context, hasSelection: true, hasQuery: true }), 'clear-selection');
assert.equal(resolveGlobalShortcut({ key: 'Escape' }, { ...context, hasQuery: true }), 'clear-query');
assert.equal(resolveGlobalShortcut({ key: 'Escape' }, { ...context, isTextInput: true }), 'blur-input');
});
test('handled component events and text entry do not leak into global shortcuts', () => {
assert.equal(resolveGlobalShortcut({ key: '/', defaultPrevented: true }, context), null);
assert.equal(resolveGlobalShortcut({ key: '/' }, { ...context, isTextInput: true }), null);
assert.equal(resolveGlobalShortcut({ key: 's' }, { ...context, isTextInput: true }), null);
assert.equal(resolveGlobalShortcut({ key: 's', metaKey: true }, context), null);
assert.equal(resolveGlobalShortcut({ key: '/', ctrlKey: true }, context), null);
assert.equal(resolveGlobalShortcut({ key: '/', altKey: true }, context), null);
});
const memoryContext = {
isTextInput: false,
showDetail: false,
hasUndo: true,
hasCursor: true,
};
test('memory shortcuts navigate and extend selection with shifted keys', () => {
assert.deepEqual(resolveMemoryShortcut({ key: 'J', shiftKey: true }, memoryContext), {
type: 'move-cursor', direction: 1, extend: true,
});
assert.deepEqual(resolveMemoryShortcut({ key: 'ArrowUp' }, memoryContext), {
type: 'move-cursor', direction: -1, extend: false,
});
assert.deepEqual(resolveMemoryShortcut({ key: 'Enter' }, memoryContext), { type: 'open-detail' });
assert.deepEqual(resolveMemoryShortcut({ key: 'x' }, memoryContext), { type: 'toggle-selection' });
assert.deepEqual(resolveMemoryShortcut({ key: 'd' }, memoryContext), { type: 'mutate-selection' });
});
test('memory undo remains available from detail while route shortcuts bubble globally', () => {
const detail = { ...memoryContext, showDetail: true };
assert.deepEqual(resolveMemoryShortcut({ key: 'z', metaKey: true }, detail), { type: 'undo' });
assert.deepEqual(resolveMemoryShortcut({ key: 'Escape' }, detail), { type: 'close-detail' });
assert.deepEqual(resolveMemoryShortcut({ key: 'D' }, detail), { type: 'mutate-detail' });
assert.equal(resolveMemoryShortcut({ key: '2', metaKey: true }, detail), null);
assert.equal(resolveMemoryShortcut({ key: 'j' }, { ...detail, isTextInput: true }), null);
});