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 / modal-root.test.js

modal-root.test.js in Extendify 3.1.0, at tests/unit/QuickEdit/lib/modal-root.test.js

100 lines 3.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 // Net-new helper in the rebuild — mounts a single body-level <div> for
2 // wp-components portals. The React root is lazy and reused across
3 // mountModal calls; closeModal tears down both the React root and the
4 // DOM node and optionally reloads the page.
5
6 jest.mock('@wordpress/element', () => {
7 const render = jest.fn();
8 const unmount = jest.fn();
9 const createRoot = jest.fn(() => ({ render, unmount }));
10 return {
11 createRoot,
12 __getRoot: () => ({ render, unmount, createRoot }),
13 };
14 });
15
16 let renderFn;
17 let unmountFn;
18 let createRootFn;
19
20 beforeEach(() => {
21 jest.resetModules();
22 document.body.innerHTML = '';
23 const handles = require('@wordpress/element').__getRoot();
24 renderFn = handles.render;
25 unmountFn = handles.unmount;
26 createRootFn = handles.createRoot;
27 renderFn.mockClear();
28 unmountFn.mockClear();
29 createRootFn.mockClear();
30 });
31
32 describe('mountModal', () => {
33 it('creates the body-level root node with id="extendify-quick-edit-modal-root"', async () => {
34 const { mountModal } = await import('@quick-edit/lib/modal-root');
35 mountModal(<div>hi</div>);
36 const node = document.getElementById('extendify-quick-edit-modal-root');
37 expect(node).not.toBeNull();
38 expect(node.parentElement).toBe(document.body);
39 });
40
41 it('calls createRoot once and reuses it across repeated mounts', async () => {
42 const { mountModal } = await import('@quick-edit/lib/modal-root');
43 mountModal(<div>a</div>);
44 mountModal(<div>b</div>);
45 mountModal(<div>c</div>);
46 expect(createRootFn).toHaveBeenCalledTimes(1);
47 expect(renderFn).toHaveBeenCalledTimes(3);
48 });
49
50 it('forwards the React element to the root.render call', async () => {
51 const { mountModal } = await import('@quick-edit/lib/modal-root');
52 const element = <span data-x="1">hi</span>;
53 mountModal(element);
54 expect(renderFn).toHaveBeenCalledWith(element);
55 });
56
57 it('re-creates the node when it was removed from the DOM externally', async () => {
58 const { mountModal } = await import('@quick-edit/lib/modal-root');
59 mountModal(<div>a</div>);
60 document.getElementById('extendify-quick-edit-modal-root').remove();
61 mountModal(<div>b</div>);
62 expect(
63 document.getElementById('extendify-quick-edit-modal-root'),
64 ).not.toBeNull();
65 });
66 });
67
68 describe('closeModal', () => {
69 it('unmounts the React root and removes the DOM node when no reload arg is passed', async () => {
70 const { mountModal, closeModal } = await import(
71 '@quick-edit/lib/modal-root'
72 );
73 mountModal(<div>hi</div>);
74 closeModal();
75 expect(unmountFn).toHaveBeenCalledTimes(1);
76 expect(
77 document.getElementById('extendify-quick-edit-modal-root'),
78 ).toBeNull();
79 });
80
81 // closeModal(true) → window.location.reload() is one trivial line that
82 // jsdom can't drive without monkey-patching a non-configurable Location.
83 // reload-flows.spec.ts E2E covers the reload-after-save path.
84
85 it('is safe to call when nothing was mounted', async () => {
86 const { closeModal } = await import('@quick-edit/lib/modal-root');
87 expect(() => closeModal()).not.toThrow();
88 });
89
90 it('drops the React root reference so the next mountModal creates a fresh one', async () => {
91 const { mountModal, closeModal } = await import(
92 '@quick-edit/lib/modal-root'
93 );
94 mountModal(<div>a</div>);
95 closeModal();
96 mountModal(<div>b</div>);
97 expect(createRootFn).toHaveBeenCalledTimes(2);
98 });
99 });
100