/** * ConfirmDeleteModal Component Tests * * Tests the REAL ConfirmDeleteModal component with mocked callbacks. * Following TDD best practices: verify component behavior. */ import { describe, it, expect, vi, beforeEach } from 'vitest'; import { render, screen, fireEvent } from '@testing-library/react'; import { ConfirmDeleteModal, type ConfirmDeleteDetails } from '../../../src/renderer/components/ConfirmDeleteModal'; describe('ConfirmDeleteModal', () => { const mockOnClose = vi.fn(); const mockOnConfirm = vi.fn(); beforeEach(() => { vi.clearAllMocks(); }); describe('Rendering', () => { it('should not render when details is null', () => { const { container } = render( ); expect(container.firstChild).toBeNull(); }); it('should render modal when details is provided', () => { const details: ConfirmDeleteDetails = { itemType: 'post', itemTitle: 'Test Post', references: [], onConfirm: mockOnConfirm, }; render(); expect(screen.getByText('Confirm Deletion')).toBeInTheDocument(); expect(screen.getByText(/Test Post/)).toBeInTheDocument(); }); it('should display post deletion message for post type', () => { const details: ConfirmDeleteDetails = { itemType: 'post', itemTitle: 'My Blog Post', references: [], onConfirm: mockOnConfirm, }; render(); expect(screen.getByText(/delete the post/)).toBeInTheDocument(); expect(screen.getByText('My Blog Post')).toBeInTheDocument(); }); it('should display media deletion message for media type', () => { const details: ConfirmDeleteDetails = { itemType: 'media', itemTitle: 'image.jpg', references: [], onConfirm: mockOnConfirm, }; render(); expect(screen.getByText(/delete the media file/)).toBeInTheDocument(); expect(screen.getByText('image.jpg')).toBeInTheDocument(); }); }); describe('References Warning', () => { it('should not show warning when no references exist', () => { const details: ConfirmDeleteDetails = { itemType: 'post', itemTitle: 'Test Post', references: [], onConfirm: mockOnConfirm, }; render(); expect(screen.queryByText(/Warning/)).not.toBeInTheDocument(); }); it('should show warning when references exist', () => { const details: ConfirmDeleteDetails = { itemType: 'post', itemTitle: 'Test Post', references: [ { id: '1', title: 'Linked Post', type: 'link' }, ], onConfirm: mockOnConfirm, }; render(); expect(screen.getByText(/Warning/)).toBeInTheDocument(); expect(screen.getByText('Linked Post')).toBeInTheDocument(); }); it('should list all reference types correctly', () => { const details: ConfirmDeleteDetails = { itemType: 'post', itemTitle: 'Test Post', references: [ { id: '1', title: 'Some Post', type: 'post' }, { id: '2', title: 'Some Image', type: 'media' }, { id: '3', title: 'Some Link', type: 'link' }, ], onConfirm: mockOnConfirm, }; render(); expect(screen.getByText('Some Post')).toBeInTheDocument(); expect(screen.getByText('Some Image')).toBeInTheDocument(); expect(screen.getByText('Some Link')).toBeInTheDocument(); }); it('should show correct warning note', () => { const details: ConfirmDeleteDetails = { itemType: 'media', itemTitle: 'photo.jpg', references: [ { id: '1', title: 'Blog Post', type: 'post' }, ], onConfirm: mockOnConfirm, }; render(); expect(screen.getByText(/Deleting this media will remove all these references/)).toBeInTheDocument(); }); }); describe('User Actions', () => { it('should call onClose when Cancel button is clicked', () => { const details: ConfirmDeleteDetails = { itemType: 'post', itemTitle: 'Test Post', references: [], onConfirm: mockOnConfirm, }; render(); fireEvent.click(screen.getByText('Cancel')); expect(mockOnClose).toHaveBeenCalledTimes(1); }); it('should call onClose when close button is clicked', () => { const details: ConfirmDeleteDetails = { itemType: 'post', itemTitle: 'Test Post', references: [], onConfirm: mockOnConfirm, }; render(); fireEvent.click(screen.getByTitle('Close')); expect(mockOnClose).toHaveBeenCalledTimes(1); }); it('should call onClose when backdrop is clicked', () => { const details: ConfirmDeleteDetails = { itemType: 'post', itemTitle: 'Test Post', references: [], onConfirm: mockOnConfirm, }; render(); const backdrop = document.querySelector('.confirm-delete-modal-backdrop'); fireEvent.click(backdrop!); expect(mockOnClose).toHaveBeenCalledTimes(1); }); it('should not call onClose when modal content is clicked', () => { const details: ConfirmDeleteDetails = { itemType: 'post', itemTitle: 'Test Post', references: [], onConfirm: mockOnConfirm, }; render(); const modal = document.querySelector('.confirm-delete-modal'); fireEvent.click(modal!); expect(mockOnClose).not.toHaveBeenCalled(); }); it('should call onConfirm and onClose when Delete button is clicked', async () => { const details: ConfirmDeleteDetails = { itemType: 'post', itemTitle: 'Test Post', references: [], onConfirm: mockOnConfirm, }; render(); fireEvent.click(screen.getByText('Delete Post')); // Wait for the async handler to complete await vi.waitFor(() => { expect(mockOnConfirm).toHaveBeenCalledTimes(1); }); expect(mockOnClose).toHaveBeenCalledTimes(1); }); it('should show correct delete button text for post', () => { const details: ConfirmDeleteDetails = { itemType: 'post', itemTitle: 'Test Post', references: [], onConfirm: mockOnConfirm, }; render(); expect(screen.getByText('Delete Post')).toBeInTheDocument(); }); it('should show correct delete button text for media', () => { const details: ConfirmDeleteDetails = { itemType: 'media', itemTitle: 'image.jpg', references: [], onConfirm: mockOnConfirm, }; render(); expect(screen.getByText('Delete Media')).toBeInTheDocument(); }); it('should handle async onConfirm correctly', async () => { const asyncConfirm = vi.fn().mockResolvedValue(undefined); const details: ConfirmDeleteDetails = { itemType: 'post', itemTitle: 'Test Post', references: [], onConfirm: asyncConfirm, }; render(); fireEvent.click(screen.getByText('Delete Post')); // Wait for the async handler to complete await vi.waitFor(() => { expect(asyncConfirm).toHaveBeenCalledTimes(1); }); expect(mockOnClose).toHaveBeenCalledTimes(1); }); }); });