/** * Tests for ThemeToggle * Verifies theme toggle button functionality and dropdown menu */ import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { ThemeToggle } from '@/components/theme/ThemeToggle'; import { ThemeProvider, useTheme } from '@/components/theme/ThemeProvider'; // Mock theme provider for controlled testing jest.mock('@/components/theme/ThemeProvider', () => { const actual = jest.requireActual('@/components/theme/ThemeProvider'); return { ...actual, useTheme: jest.fn(), }; }); describe('ThemeToggle', () => { const mockSetTheme = jest.fn(); beforeEach(() => { jest.clearAllMocks(); // Default mock return value (useTheme as jest.Mock).mockReturnValue({ theme: 'system', setTheme: mockSetTheme, resolvedTheme: 'light', }); }); describe('Rendering', () => { it('renders theme toggle button', () => { render(); const button = screen.getByRole('button', { name: /toggle theme/i }); expect(button).toBeInTheDocument(); }); it('displays sun icon when resolved theme is light', () => { (useTheme as jest.Mock).mockReturnValue({ theme: 'light', setTheme: mockSetTheme, resolvedTheme: 'light', }); render(); const button = screen.getByRole('button', { name: /toggle theme/i }); // Sun icon should be visible expect(button.querySelector('svg')).toBeInTheDocument(); }); it('displays moon icon when resolved theme is dark', () => { (useTheme as jest.Mock).mockReturnValue({ theme: 'dark', setTheme: mockSetTheme, resolvedTheme: 'dark', }); render(); const button = screen.getByRole('button', { name: /toggle theme/i }); // Moon icon should be visible expect(button.querySelector('svg')).toBeInTheDocument(); }); }); describe('Dropdown Menu', () => { it('opens dropdown menu when button is clicked', async () => { const user = userEvent.setup(); render(); const button = screen.getByRole('button', { name: /toggle theme/i }); await user.click(button); await waitFor(() => { expect(screen.getByRole('menuitem', { name: /light/i })).toBeInTheDocument(); expect(screen.getByRole('menuitem', { name: /dark/i })).toBeInTheDocument(); expect(screen.getByRole('menuitem', { name: /system/i })).toBeInTheDocument(); }); }); it('calls setTheme with "light" when light option is clicked', async () => { const user = userEvent.setup(); render(); const button = screen.getByRole('button', { name: /toggle theme/i }); await user.click(button); const lightOption = await screen.findByRole('menuitem', { name: /light/i }); await user.click(lightOption); expect(mockSetTheme).toHaveBeenCalledWith('light'); }); it('calls setTheme with "dark" when dark option is clicked', async () => { const user = userEvent.setup(); render(); const button = screen.getByRole('button', { name: /toggle theme/i }); await user.click(button); const darkOption = await screen.findByRole('menuitem', { name: /dark/i }); await user.click(darkOption); expect(mockSetTheme).toHaveBeenCalledWith('dark'); }); it('calls setTheme with "system" when system option is clicked', async () => { const user = userEvent.setup(); render(); const button = screen.getByRole('button', { name: /toggle theme/i }); await user.click(button); const systemOption = await screen.findByRole('menuitem', { name: /system/i }); await user.click(systemOption); expect(mockSetTheme).toHaveBeenCalledWith('system'); }); }); describe('Active Theme Indicator', () => { it('shows checkmark for light theme when active', async () => { const user = userEvent.setup(); (useTheme as jest.Mock).mockReturnValue({ theme: 'light', setTheme: mockSetTheme, resolvedTheme: 'light', }); render(); const button = screen.getByRole('button', { name: /toggle theme/i }); await user.click(button); const lightOption = await screen.findByRole('menuitem', { name: /light/i }); expect(lightOption).toHaveTextContent('✓'); }); it('shows checkmark for dark theme when active', async () => { const user = userEvent.setup(); (useTheme as jest.Mock).mockReturnValue({ theme: 'dark', setTheme: mockSetTheme, resolvedTheme: 'dark', }); render(); const button = screen.getByRole('button', { name: /toggle theme/i }); await user.click(button); const darkOption = await screen.findByRole('menuitem', { name: /dark/i }); expect(darkOption).toHaveTextContent('✓'); }); it('shows checkmark for system theme when active', async () => { const user = userEvent.setup(); (useTheme as jest.Mock).mockReturnValue({ theme: 'system', setTheme: mockSetTheme, resolvedTheme: 'light', }); render(); const button = screen.getByRole('button', { name: /toggle theme/i }); await user.click(button); const systemOption = await screen.findByRole('menuitem', { name: /system/i }); expect(systemOption).toHaveTextContent('✓'); }); }); });