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 / Agent / workflows / block-selector / components / UpdateBlockConfirm.test.jsx

UpdateBlockConfirm.test.jsx in Extendify 3.1.1, at tests/unit/Agent/workflows/block-selector/components/UpdateBlockConfirm.test.jsx

108 lines 3.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { UpdateBlockConfirm } from '@agent/workflows/block-selector/components/UpdateBlockConfirm';
2 import { render, screen, waitFor } from '@testing-library/react';
3 import userEvent from '@testing-library/user-event';
4
5 let mockAgentBlock;
6 jest.mock('@shared/lib/variant-classes', () => ({
7 patchVariantClasses: (_html, el) => el.outerHTML,
8 }));
9 jest.mock('@quick-edit/state/store', () => ({
10 useQuickEditStore: (selector) => selector({ agentBlock: mockAgentBlock }),
11 }));
12 jest.mock('@wordpress/api-fetch', () => ({
13 __esModule: true,
14 default: jest.fn(() =>
15 Promise.resolve({
16 content: '<div data-extendify-temp-replacement="true">New Block</div>',
17 }),
18 ),
19 }));
20
21 describe('UpdateBlockConfirm — undo on cancel/unmount', () => {
22 let container;
23 let originalBlock;
24
25 beforeEach(() => {
26 mockAgentBlock = { id: 'block-1' };
27 container = document.createElement('div');
28 originalBlock = document.createElement('div');
29 originalBlock.setAttribute('data-extendify-agent-block-id', 'block-1');
30 originalBlock.textContent = 'Original Block';
31 container.appendChild(originalBlock);
32 document.body.appendChild(container);
33 });
34
35 afterEach(() => {
36 container.remove();
37 });
38
39 const inputs = { newContent: '<div>New Block</div>' };
40
41 test('restores original block when unmounted without confirming', async () => {
42 const { unmount } = render(
43 <UpdateBlockConfirm
44 inputs={inputs}
45 onConfirm={jest.fn()}
46 onCancel={jest.fn()}
47 onRetry={jest.fn()}
48 />,
49 );
50 await screen.findByText(/review and confirm/i);
51 // Original block was detached, temp replacement exists
52 expect(
53 document.querySelector('[data-extendify-temp-replacement]'),
54 ).toBeTruthy();
55 unmount();
56 // After unmount, temp replacement removed and original restored
57 expect(
58 document.querySelector('[data-extendify-temp-replacement]'),
59 ).toBeNull();
60 expect(container.textContent).toContain('Original Block');
61 });
62
63 test('does not restore original block after confirming', async () => {
64 const onConfirm = jest.fn();
65 const user = userEvent.setup();
66 const { unmount } = render(
67 <UpdateBlockConfirm
68 inputs={inputs}
69 onConfirm={onConfirm}
70 onCancel={jest.fn()}
71 onRetry={jest.fn()}
72 />,
73 );
74 await screen.findByText(/review and confirm/i);
75 await user.click(screen.getByRole('button', { name: /save/i }));
76 expect(onConfirm).toHaveBeenCalled();
77 unmount();
78 // The temp replacement should still be in the DOM (not removed by undo)
79 expect(
80 document.querySelector('[data-extendify-temp-replacement]'),
81 ).toBeTruthy();
82 });
83 });
84
85 // A block-selector confirm is only mounted by a `requires: ['block']` workflow,
86 // which Agent.jsx cancels before render when nothing is staged — so it should
87 // never see a null `agentBlock` in the gated flow. This pins the read-site
88 // null-safety (`block?.id`) directly, so a future workflow that renders the
89 // confirm without that gate can't crash on it.
90 describe('UpdateBlockConfirm — null-safe with no staged block', () => {
91 beforeEach(() => {
92 mockAgentBlock = null;
93 });
94
95 test('cancels without crashing when no block is staged', async () => {
96 const onCancel = jest.fn();
97 render(
98 <UpdateBlockConfirm
99 inputs={{ newContent: '<div>New Block</div>' }}
100 onConfirm={jest.fn()}
101 onCancel={onCancel}
102 onRetry={jest.fn()}
103 />,
104 );
105 await waitFor(() => expect(onCancel).toHaveBeenCalled());
106 });
107 });
108