PluginProbe
Extendify / 3.1.1
Extendify v3.1.1
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 / admin-bar.test.js

admin-bar.test.js in Extendify 3.1.1, at tests/unit/QuickEdit/lib/admin-bar.test.js

139 lines 4.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 // Mock zustand's persist middleware so it runs synchronously and we don't
2 // have to coordinate hydration timing per test.
3 jest.mock('zustand/middleware', () => ({
4 devtools: (fn) => fn,
5 persist: (config) => config,
6 }));
7
8 jest.mock('@quick-edit/lib/insights', () => ({
9 track: jest.fn(),
10 }));
11
12 const trackedDocListeners = [];
13 const originalDocAddEventListener = document.addEventListener.bind(document);
14 document.addEventListener = (type, handler, opts) => {
15 trackedDocListeners.push([type, handler, opts]);
16 return originalDocAddEventListener(type, handler, opts);
17 };
18
19 const NODE_ID = 'wp-admin-bar-extendify-quick-edit-toggle';
20
21 const renderAdminBar = () => {
22 document.body.innerHTML = `
23 <ul id="wpadminbar">
24 <li id="${NODE_ID}">
25 <a href="#" role="switch" aria-label="Quick Edit">Quick Edit</a>
26 </li>
27 </ul>
28 `;
29 return document.querySelector(`#${NODE_ID} a`);
30 };
31
32 const loadModules = async () => {
33 const { useEditModeStore } = await import('@quick-edit/state/edit-mode');
34 const { bindAdminBarToggle } = await import('@quick-edit/lib/admin-bar');
35 return { useEditModeStore, bindAdminBarToggle };
36 };
37
38 beforeEach(() => {
39 jest.resetModules();
40 jest.clearAllMocks();
41 document.documentElement.className = '';
42 document.body.innerHTML = '';
43 window.extQuickEditData = undefined;
44 });
45
46 afterEach(() => {
47 for (const [type, handler, opts] of trackedDocListeners) {
48 document.removeEventListener(type, handler, opts);
49 }
50 trackedDocListeners.length = 0;
51 });
52
53 describe('bindAdminBarToggle — no-op when pill is missing', () => {
54 it('returns silently when the admin-bar node is not present', async () => {
55 const { bindAdminBarToggle, useEditModeStore } = await loadModules();
56 expect(() => bindAdminBarToggle()).not.toThrow();
57 expect(useEditModeStore.getState().on).toBe(false);
58 });
59 });
60
61 describe('bindAdminBarToggle — initial aria-checked sync', () => {
62 it('writes aria-checked="false" when edit mode is off', async () => {
63 const link = renderAdminBar();
64 const { bindAdminBarToggle } = await loadModules();
65 bindAdminBarToggle();
66 expect(link.getAttribute('aria-checked')).toBe('false');
67 });
68
69 it('writes aria-checked="true" when edit mode is on at bind time', async () => {
70 window.extQuickEditData = { defaultOn: true };
71 const link = renderAdminBar();
72 const { bindAdminBarToggle } = await loadModules();
73 bindAdminBarToggle();
74 expect(link.getAttribute('aria-checked')).toBe('true');
75 });
76 });
77
78 describe('bindAdminBarToggle — click handler', () => {
79 it('toggles the edit-mode store and prevents default navigation', async () => {
80 const link = renderAdminBar();
81 const { bindAdminBarToggle, useEditModeStore } = await loadModules();
82 bindAdminBarToggle();
83
84 const event = new MouseEvent('click', { bubbles: true, cancelable: true });
85 const preventSpy = jest.spyOn(event, 'preventDefault');
86 link.dispatchEvent(event);
87
88 expect(preventSpy).toHaveBeenCalled();
89 expect(useEditModeStore.getState().on).toBe(true);
90 });
91
92 it('a second click toggles back to off', async () => {
93 const link = renderAdminBar();
94 const { bindAdminBarToggle, useEditModeStore } = await loadModules();
95 bindAdminBarToggle();
96
97 link.dispatchEvent(
98 new MouseEvent('click', { bubbles: true, cancelable: true }),
99 );
100 link.dispatchEvent(
101 new MouseEvent('click', { bubbles: true, cancelable: true }),
102 );
103
104 expect(useEditModeStore.getState().on).toBe(false);
105 });
106 });
107
108 describe('bindAdminBarToggle — store subscription mirrors aria-checked', () => {
109 it('updates aria-checked when the store flips externally', async () => {
110 const link = renderAdminBar();
111 const { bindAdminBarToggle, useEditModeStore } = await loadModules();
112 bindAdminBarToggle();
113
114 useEditModeStore.getState().setOn(true);
115 expect(link.getAttribute('aria-checked')).toBe('true');
116
117 useEditModeStore.getState().setOn(false);
118 expect(link.getAttribute('aria-checked')).toBe('false');
119 });
120 });
121
122 describe('bindAdminBarToggle — idempotency', () => {
123 it('only binds the click handler once across repeated calls', async () => {
124 const link = renderAdminBar();
125 const { bindAdminBarToggle, useEditModeStore } = await loadModules();
126 bindAdminBarToggle();
127 bindAdminBarToggle();
128 bindAdminBarToggle();
129
130 link.dispatchEvent(
131 new MouseEvent('click', { bubbles: true, cancelable: true }),
132 );
133
134 // One click → one toggle. If the listener double-bound, on would be
135 // false again after two toggles.
136 expect(useEditModeStore.getState().on).toBe(true);
137 });
138 });
139