import { render, screen, fireEvent, waitFor } from '@testing-library/react' import { ConfirmDialog } from '../ConfirmDialog' // Mock Radix portal to render inline jest.mock('@radix-ui/react-dialog', () => { const actual = jest.requireActual('@radix-ui/react-dialog') return { ...actual, Portal: ({ children }: { children: React.ReactNode }) => <>{children}, } }) describe('ConfirmDialog', () => { const defaultProps = { open: true, onClose: jest.fn(), onConfirm: jest.fn(), title: 'Delete item?', } beforeEach(() => { jest.clearAllMocks() }) it('renders title', () => { render() expect(screen.getByText('Delete item?')).toBeInTheDocument() }) it('renders string description', () => { render( ) expect(screen.getByText('This cannot be undone.')).toBeInTheDocument() }) it('renders ReactNode description', () => { render( Custom content} /> ) expect(screen.getByTestId('custom')).toBeInTheDocument() }) it('renders default button labels', () => { render() expect(screen.getByText('Confirm')).toBeInTheDocument() expect(screen.getByText('Cancel')).toBeInTheDocument() }) it('renders custom button labels', () => { render( ) expect(screen.getByText('Delete')).toBeInTheDocument() expect(screen.getByText('Keep')).toBeInTheDocument() }) it('calls onConfirm when confirm button is clicked', () => { render() fireEvent.click(screen.getByText('Confirm')) expect(defaultProps.onConfirm).toHaveBeenCalledTimes(1) }) it('calls onClose when cancel button is clicked', () => { render() fireEvent.click(screen.getByText('Cancel')) expect(defaultProps.onClose).toHaveBeenCalledTimes(1) }) it('disables buttons when loading', () => { render() const confirmBtn = screen.getByText('Confirm').closest('button') const cancelBtn = screen.getByText('Cancel').closest('button') expect(confirmBtn).toBeDisabled() expect(cancelBtn).toBeDisabled() }) it('shows spinner when loading', () => { const { container } = render() const spinner = container.querySelector('.animate-spin') expect(spinner).toBeInTheDocument() }) it('does not call onClose when cancel clicked during loading', () => { render() // The button is disabled so the click handler won't fire, // but also handleClose guards against it const cancelBtn = screen.getByText('Cancel').closest('button')! fireEvent.click(cancelBtn) expect(defaultProps.onClose).not.toHaveBeenCalled() }) it('applies destructive variant to confirm button', () => { render() const confirmBtn = screen.getByText('Confirm').closest('button') expect(confirmBtn?.className).toMatch(/destructive/) }) it('applies default variant when not destructive', () => { render() const confirmBtn = screen.getByText('Confirm').closest('button') expect(confirmBtn?.className).not.toMatch(/destructive/) }) it('does not render body when no description provided', () => { const { container } = render() // Body has class py-4, should not exist expect(container.querySelector('.py-4')).not.toBeInTheDocument() }) it('handles async onConfirm', async () => { const asyncConfirm = jest.fn().mockResolvedValue(undefined) render() fireEvent.click(screen.getByText('Confirm')) await waitFor(() => { expect(asyncConfirm).toHaveBeenCalledTimes(1) }) }) })