PluginProbe
Extendify / 3.1.0
Extendify v3.1.0
3.2.1 3.2.0 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.6 3.0.5 3.0.4 trunk 0.1.0 0.10.0 0.10.1 0.10.2 0.11.0 0.11.1 0.2.0 0.3.0 0.3.1 0.4.0 0.5.0 0.6.0 All 127 releases
extendify / tests / unit / QuickEdit / lib / cmd-enter-save.test.js

cmd-enter-save.test.js in Extendify 3.1.0, at tests/unit/QuickEdit/lib/cmd-enter-save.test.js

123 lines 4.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 // useCmdEnterSave attaches a capture-phase keydown listener while
2 // enabled=true and fires onSave on Cmd/Ctrl+Enter. Tests render a host
3 // component via @testing-library/react so the React effect lifecycle
4 // (mount / unmount / dep-change cleanup) drives the listener
5 // attach/detach instead of being mocked.
6
7 import { useCmdEnterSave } from '@quick-edit/lib/cmd-enter-save';
8 import { render } from '@testing-library/react';
9
10 const Host = ({ onSave, enabled }) => {
11 useCmdEnterSave(onSave, enabled);
12 return null;
13 };
14
15 const fireKeydown = (init) => {
16 const event = new KeyboardEvent('keydown', {
17 bubbles: true,
18 cancelable: true,
19 ...init,
20 });
21 const preventSpy = jest.spyOn(event, 'preventDefault');
22 const stopSpy = jest.spyOn(event, 'stopPropagation');
23 window.dispatchEvent(event);
24 return { event, preventSpy, stopSpy };
25 };
26
27 describe('useCmdEnterSave — modifier matrix', () => {
28 const cases = [
29 ['Cmd+Enter (metaKey)', { key: 'Enter', metaKey: true }, true],
30 ['Ctrl+Enter (ctrlKey)', { key: 'Enter', ctrlKey: true }, true],
31 ['plain Enter', { key: 'Enter' }, false],
32 ['Cmd+A', { key: 'a', metaKey: true }, false],
33 ['Cmd+Shift+Enter', { key: 'Enter', metaKey: true, shiftKey: true }, true],
34 ];
35
36 for (const [name, init, shouldFire] of cases) {
37 it(`${shouldFire ? 'fires' : 'ignores'} ${name}`, () => {
38 const onSave = jest.fn();
39 render(<Host onSave={onSave} enabled={true} />);
40 fireKeydown(init);
41 expect(onSave).toHaveBeenCalledTimes(shouldFire ? 1 : 0);
42 });
43 }
44 });
45
46 describe('useCmdEnterSave — event prevention', () => {
47 it('preventsDefault and stopsPropagation on a match', () => {
48 const onSave = jest.fn();
49 render(<Host onSave={onSave} enabled={true} />);
50 const { preventSpy, stopSpy } = fireKeydown({
51 key: 'Enter',
52 metaKey: true,
53 });
54 expect(preventSpy).toHaveBeenCalled();
55 expect(stopSpy).toHaveBeenCalled();
56 });
57
58 it('does not preventDefault on a miss', () => {
59 const onSave = jest.fn();
60 render(<Host onSave={onSave} enabled={true} />);
61 const { preventSpy } = fireKeydown({ key: 'Enter' });
62 expect(preventSpy).not.toHaveBeenCalled();
63 });
64 });
65
66 describe('useCmdEnterSave — enabled gate', () => {
67 it('does not attach the listener when enabled=false', () => {
68 const onSave = jest.fn();
69 render(<Host onSave={onSave} enabled={false} />);
70 fireKeydown({ key: 'Enter', metaKey: true });
71 expect(onSave).not.toHaveBeenCalled();
72 });
73
74 it('defaults enabled=true when the second arg is omitted', () => {
75 const Default = ({ onSave }) => {
76 useCmdEnterSave(onSave);
77 return null;
78 };
79 const onSave = jest.fn();
80 render(<Default onSave={onSave} />);
81 fireKeydown({ key: 'Enter', metaKey: true });
82 expect(onSave).toHaveBeenCalledTimes(1);
83 });
84
85 it('re-attaches when enabled flips false → true', () => {
86 const onSave = jest.fn();
87 const { rerender } = render(<Host onSave={onSave} enabled={false} />);
88 fireKeydown({ key: 'Enter', metaKey: true });
89 expect(onSave).not.toHaveBeenCalled();
90
91 rerender(<Host onSave={onSave} enabled={true} />);
92 fireKeydown({ key: 'Enter', metaKey: true });
93 expect(onSave).toHaveBeenCalledTimes(1);
94 });
95 });
96
97 describe('useCmdEnterSave — cleanup', () => {
98 it('removes the listener on unmount', () => {
99 const onSave = jest.fn();
100 const { unmount } = render(<Host onSave={onSave} enabled={true} />);
101 unmount();
102 fireKeydown({ key: 'Enter', metaKey: true });
103 expect(onSave).not.toHaveBeenCalled();
104 });
105
106 it('removes the prior listener when onSave changes (no duplicate fires)', () => {
107 const first = jest.fn();
108 const second = jest.fn();
109 const { rerender } = render(<Host onSave={first} enabled={true} />);
110 rerender(<Host onSave={second} enabled={true} />);
111 fireKeydown({ key: 'Enter', metaKey: true });
112 expect(first).not.toHaveBeenCalled();
113 expect(second).toHaveBeenCalledTimes(1);
114 });
115 });
116
117 describe('useCmdEnterSave — onSave null-safety', () => {
118 it('does not throw when onSave is null and the shortcut fires', () => {
119 render(<Host onSave={null} enabled={true} />);
120 expect(() => fireKeydown({ key: 'Enter', metaKey: true })).not.toThrow();
121 });
122 });
123