← back to Angels Flowers
components/__tests__/ThemeToggle.test.tsx
117 lines
import { render, screen } from '@/test-utils';
import ThemeToggle from '../ThemeToggle';
import { useTheme } from '../ThemeProvider';
// Mock the useTheme hook
jest.mock('../ThemeProvider', () => ({
useTheme: jest.fn(),
}));
describe('ThemeToggle', () => {
const mockToggleTheme = jest.fn();
beforeEach(() => {
jest.clearAllMocks();
});
it('renders the toggle button', () => {
(useTheme as jest.Mock).mockReturnValue({
theme: 'light',
toggleTheme: mockToggleTheme,
});
render(<ThemeToggle />);
const button = screen.getByRole('button');
expect(button).toBeInTheDocument();
});
it('displays sun icon in dark mode', () => {
(useTheme as jest.Mock).mockReturnValue({
theme: 'dark',
toggleTheme: mockToggleTheme,
});
render(<ThemeToggle />);
const button = screen.getByRole('button');
expect(button).toHaveAttribute('aria-label', 'Switch to light mode');
expect(button).toHaveAttribute('aria-pressed', 'true');
});
it('displays moon icon in light mode', () => {
(useTheme as jest.Mock).mockReturnValue({
theme: 'light',
toggleTheme: mockToggleTheme,
});
render(<ThemeToggle />);
const button = screen.getByRole('button');
expect(button).toHaveAttribute('aria-label', 'Switch to dark mode');
expect(button).toHaveAttribute('aria-pressed', 'false');
});
it('calls toggleTheme when clicked', () => {
(useTheme as jest.Mock).mockReturnValue({
theme: 'light',
toggleTheme: mockToggleTheme,
});
render(<ThemeToggle />);
const button = screen.getByRole('button');
button.click();
expect(mockToggleTheme).toHaveBeenCalledTimes(1);
});
it('applies custom className', () => {
(useTheme as jest.Mock).mockReturnValue({
theme: 'light',
toggleTheme: mockToggleTheme,
});
render(<ThemeToggle className="custom-class" />);
const button = screen.getByRole('button');
expect(button).toHaveClass('custom-class');
});
it('updates icon when theme prop changes', () => {
const { rerender } = render(<ThemeToggle />);
// Start with light theme
(useTheme as jest.Mock).mockReturnValue({
theme: 'light',
toggleTheme: mockToggleTheme,
});
rerender(<ThemeToggle />);
let button = screen.getByRole('button');
expect(button).toHaveAttribute('aria-label', 'Switch to dark mode');
expect(button).toHaveAttribute('aria-pressed', 'false');
// Switch to dark theme
(useTheme as jest.Mock).mockReturnValue({
theme: 'dark',
toggleTheme: mockToggleTheme,
});
rerender(<ThemeToggle />);
button = screen.getByRole('button');
expect(button).toHaveAttribute('aria-label', 'Switch to light mode');
expect(button).toHaveAttribute('aria-pressed', 'true');
});
it('handles missing theme context gracefully', () => {
// Mock useTheme to throw error (simulating missing provider)
(useTheme as jest.Mock).mockImplementation(() => {
throw new Error('useTheme must be used within a ThemeProvider');
});
// Expect the component to throw when rendered without provider
expect(() => render(<ThemeToggle />)).toThrow('useTheme must be used within a ThemeProvider');
});
});