import React from 'react'; import { render, screen, fireEvent, waitFor } from '@testing-library/react'; import { vi, describe, it, expect, beforeEach } from 'vitest'; import PhotoModal from '@/components/PhotoModal'; describe('PhotoModal', () => { const mockOnClose = vi.fn(); const mockPhotoUrl = 'https://example.com/photo.jpg'; beforeEach(() => { mockOnClose.mockClear(); }); describe('Rendering', () => { it('should render photo modal with image', () => { render( ); const image = screen.getByAltText('Test Item'); expect(image).toBeInTheDocument(); expect(image).toHaveAttribute('src', mockPhotoUrl); }); it('should display title in header', () => { render( ); expect(screen.getByText('Test Item')).toBeInTheDocument(); }); it('should render with default title if not provided', () => { render( ); expect(screen.getByText('Photo')).toBeInTheDocument(); }); it('should render close button with rose color', () => { const { container } = render( ); const closeButton = screen.getByLabelText('Close modal'); expect(closeButton).toBeInTheDocument(); // Check for rose-colored X icon const xIcon = closeButton.querySelector('svg'); expect(xIcon).toHaveClass('text-rose-500'); }); }); describe('Close Interactions', () => { it('should close when clicking close button', () => { render( ); const closeButton = screen.getByLabelText('Close modal'); fireEvent.click(closeButton); expect(mockOnClose).toHaveBeenCalledTimes(1); }); it('should close when clicking outside modal (on backdrop)', () => { const { container } = render( ); // Find the backdrop element (has onClick handler directly) const backdrop = container.querySelector('[role="dialog"][class*="fixed"][class*="inset-0"]'); if (backdrop) { fireEvent.click(backdrop); expect(mockOnClose).toHaveBeenCalledTimes(1); } }); it('should NOT close when clicking on modal image', () => { render( ); const image = screen.getByAltText('Test Item'); fireEvent.click(image); expect(mockOnClose).not.toHaveBeenCalled(); }); it('should close when pressing Escape key', async () => { render( ); fireEvent.keyDown(window, { key: 'Escape' }); await waitFor(() => { expect(mockOnClose).toHaveBeenCalledTimes(1); }); }); it('should NOT close when pressing other keys', () => { render( ); fireEvent.keyDown(window, { key: 'Enter' }); expect(mockOnClose).not.toHaveBeenCalled(); }); }); describe('Image Properties', () => { it('should have lazy loading enabled', () => { render( ); const image = screen.getByAltText('Test Item'); expect(image).toHaveAttribute('loading', 'lazy'); }); it('should have object-contain class for proper scaling', () => { render( ); const image = screen.getByAltText('Test Item'); expect(image).toHaveClass('object-contain'); }); it('should render image with proper max-height constraint', () => { render( ); const image = screen.getByAltText('Test Item'); // Image has max-h constraint expect(image).toHaveClass('max-h-[calc(90vh-120px)]'); }); }); describe('Accessibility', () => { it('should have proper ARIA attributes for dialog', () => { render( ); const dialog = screen.getByRole('dialog'); expect(dialog).toHaveAttribute('aria-modal', 'true'); expect(dialog).toHaveAttribute('aria-label', 'Photo viewer for Test Item'); }); it('should have accessible close button', () => { render( ); const closeButton = screen.getByLabelText('Close modal'); expect(closeButton).toBeInTheDocument(); }); }); describe('Responsive Design', () => { it('should have responsive max-width classes', () => { const { container } = render( ); const backdrop = screen.getByRole('dialog'); // The modal content div is the second child of backdrop const modalContent = backdrop.querySelector('div[class*="rounded-3xl"]'); expect(modalContent).toHaveClass('max-w-2xl', 'w-full', 'max-h-[90vh]'); }); it('should have responsive padding in header', () => { const { container } = render( ); // Header should have responsive padding const header = screen.getByText('Test Item').closest('div'); expect(header).toHaveClass('p-4', 'md:p-6'); }); }); describe('Cleanup', () => { it('should remove keyboard listener on unmount', () => { const removeEventListenerSpy = vi.spyOn(window, 'removeEventListener'); const { unmount } = render( ); unmount(); expect(removeEventListenerSpy).toHaveBeenCalledWith( 'keydown', expect.any(Function) ); removeEventListenerSpy.mockRestore(); }); }); });