import { UpdateBlockConfirm } from '@agent/workflows/block-selector/components/UpdateBlockConfirm'; import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; let mockAgentBlock; jest.mock('@shared/lib/variant-classes', () => ({ patchVariantClasses: (_html, el) => el.outerHTML, })); jest.mock('@quick-edit/state/store', () => ({ useQuickEditStore: (selector) => selector({ agentBlock: mockAgentBlock }), })); jest.mock('@wordpress/api-fetch', () => ({ __esModule: true, default: jest.fn(() => Promise.resolve({ content: '
New Block
', }), ), })); describe('UpdateBlockConfirm — undo on cancel/unmount', () => { let container; let originalBlock; beforeEach(() => { mockAgentBlock = { id: 'block-1' }; container = document.createElement('div'); originalBlock = document.createElement('div'); originalBlock.setAttribute('data-extendify-agent-block-id', 'block-1'); originalBlock.textContent = 'Original Block'; container.appendChild(originalBlock); document.body.appendChild(container); }); afterEach(() => { container.remove(); }); const inputs = { newContent: '
New Block
' }; test('restores original block when unmounted without confirming', async () => { const { unmount } = render( , ); await screen.findByText(/review and confirm/i); // Original block was detached, temp replacement exists expect( document.querySelector('[data-extendify-temp-replacement]'), ).toBeTruthy(); unmount(); // After unmount, temp replacement removed and original restored expect( document.querySelector('[data-extendify-temp-replacement]'), ).toBeNull(); expect(container.textContent).toContain('Original Block'); }); test('does not restore original block after confirming', async () => { const onConfirm = jest.fn(); const user = userEvent.setup(); const { unmount } = render( , ); await screen.findByText(/review and confirm/i); await user.click(screen.getByRole('button', { name: /save/i })); expect(onConfirm).toHaveBeenCalled(); unmount(); // The temp replacement should still be in the DOM (not removed by undo) expect( document.querySelector('[data-extendify-temp-replacement]'), ).toBeTruthy(); }); }); // A block-selector confirm is only mounted by a `requires: ['block']` workflow, // which Agent.jsx cancels before render when nothing is staged — so it should // never see a null `agentBlock` in the gated flow. This pins the read-site // null-safety (`block?.id`) directly, so a future workflow that renders the // confirm without that gate can't crash on it. describe('UpdateBlockConfirm — null-safe with no staged block', () => { beforeEach(() => { mockAgentBlock = null; }); test('cancels without crashing when no block is staged', async () => { const onCancel = jest.fn(); render( New Block' }} onConfirm={jest.fn()} onCancel={onCancel} onRetry={jest.fn()} />, ); await waitFor(() => expect(onCancel).toHaveBeenCalled()); }); });