import { render, screen, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' import { describe, it, expect, vi, beforeEach } from 'vitest' import { ThemeProvider, useTheme } from './ThemeContext' function ThemeConsumer() { const { theme, resolvedTheme, setTheme, toggleTheme } = useTheme() return (
{theme}
{resolvedTheme}
) } describe('ThemeProvider', () => { beforeEach(() => { localStorage.clear() }) it('loads theme from localStorage and applies resolved theme', () => { localStorage.setItem('mp-theme', 'dark') const matchMediaMock = vi.fn().mockReturnValue({ matches: false, addEventListener: vi.fn(), removeEventListener: vi.fn() }) vi.stubGlobal('matchMedia', matchMediaMock) render( ) expect(screen.getByTestId('theme')).toHaveTextContent('dark') expect(screen.getByTestId('resolved')).toHaveTextContent('dark') expect(document.documentElement.classList.contains('dark')).toBe(true) expect(document.documentElement.getAttribute('data-theme')).toBe('dark') }) it('resolves system theme when set to system', async () => { const matchMediaMock = vi.fn().mockReturnValue({ matches: true, addEventListener: vi.fn(), removeEventListener: vi.fn() }) vi.stubGlobal('matchMedia', matchMediaMock) render( ) expect(screen.getByTestId('theme')).toHaveTextContent('system') expect(screen.getByTestId('resolved')).toHaveTextContent('dark') expect(document.documentElement.classList.contains('dark')).toBe(true) const user = userEvent.setup() await user.click(screen.getByRole('button', { name: /set dark/i })) expect(localStorage.getItem('mp-theme')).toBe('dark') }) it('toggles between light and dark', async () => { const matchMediaMock = vi.fn().mockReturnValue({ matches: false, addEventListener: vi.fn(), removeEventListener: vi.fn() }) vi.stubGlobal('matchMedia', matchMediaMock) render( ) const user = userEvent.setup() expect(screen.getByTestId('resolved')).toHaveTextContent('light') await user.click(screen.getByRole('button', { name: /toggle/i })) await waitFor(() => expect(screen.getByTestId('resolved')).toHaveTextContent('dark')) expect(document.documentElement.classList.contains('dark')).toBe(true) }) it('throws when useTheme is used outside provider', () => { const ConsoleError = console.error console.error = vi.fn() expect(() => render()).toThrow('useTheme must be used within ThemeProvider') console.error = ConsoleError }) })