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
| 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 |