Files
3dp-manager/client/test/ThemeContext.spec.tsx
2026-03-30 00:19:52 +03:00

182 lines
5.8 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from 'vitest'
import { renderHook, act } from '@testing-library/react'
import { ThemeProvider, useThemeContext } from '@/ThemeContext'
import { ReactNode } from 'react'
// Мокаем localStorage
const localStorageMock = (() => {
let store: Record<string, string> = {}
return {
getItem: vi.fn((key: string) => store[key] || null),
setItem: vi.fn((key: string, value: string) => {
store[key] = value
}),
removeItem: vi.fn((key: string) => {
delete store[key]
}),
clear: vi.fn(() => {
store = {}
}),
}
})()
Object.defineProperty(window, 'localStorage', {
value: localStorageMock,
})
// Мокаем useMediaQuery
const useMediaQueryMock = vi.fn()
vi.mock('@mui/material', async () => {
const actual = await vi.importActual('@mui/material')
return {
...(actual as object),
useMediaQuery: () => useMediaQueryMock(),
}
})
const wrapper = ({ children }: { children: ReactNode }) => (
<ThemeProvider>{children}</ThemeProvider>
)
describe('ThemeContext', () => {
beforeEach(() => {
localStorageMock.clear()
vi.clearAllMocks()
useMediaQueryMock.mockReturnValue(false) // light mode by default
})
describe('useThemeContext', () => {
it('должен предоставлять context', () => {
const { result } = renderHook(() => useThemeContext(), { wrapper })
expect(result.current).toBeDefined()
})
it('должен предоставлять mode и toggleColorMode', () => {
const { result } = renderHook(() => useThemeContext(), { wrapper })
expect(result.current.mode).toBeDefined()
expect(result.current.toggleColorMode).toBeDefined()
expect(typeof result.current.toggleColorMode).toBe('function')
})
})
describe('initial state', () => {
it('должен инициализироваться с mode из localStorage', () => {
localStorageMock.getItem.mockReturnValue('dark')
const { result } = renderHook(() => useThemeContext(), { wrapper })
expect(result.current.mode).toBe('dark')
})
it('должен инициализироваться с "system" если mode отсутствует в localStorage', () => {
localStorageMock.getItem.mockReturnValue(null)
const { result } = renderHook(() => useThemeContext(), { wrapper })
expect(result.current.mode).toBe('system')
})
})
describe('toggleColorMode', () => {
it('должен переключать light → dark → system → light', () => {
localStorageMock.getItem.mockReturnValue('light')
const { result } = renderHook(() => useThemeContext(), { wrapper })
expect(result.current.mode).toBe('light')
act(() => {
result.current.toggleColorMode()
})
expect(result.current.mode).toBe('dark')
act(() => {
result.current.toggleColorMode()
})
expect(result.current.mode).toBe('system')
act(() => {
result.current.toggleColorMode()
})
expect(result.current.mode).toBe('light')
})
it('должен сохранять mode в localStorage при переключении', () => {
localStorageMock.getItem.mockReturnValue('light')
const { result } = renderHook(() => useThemeContext(), { wrapper })
act(() => {
result.current.toggleColorMode()
})
expect(localStorageMock.setItem).toHaveBeenCalledWith('themeMode', 'dark')
})
})
describe('system mode', () => {
it('должен использовать dark когда prefers-color-scheme: dark', () => {
localStorageMock.getItem.mockReturnValue('system')
useMediaQueryMock.mockReturnValue(true) // prefers dark
const { result } = renderHook(() => useThemeContext(), { wrapper })
// mode должен быть 'system', но тема должна быть dark
expect(result.current.mode).toBe('system')
})
it('должен использовать light когда prefers-color-scheme: light', () => {
localStorageMock.getItem.mockReturnValue('system')
useMediaQueryMock.mockReturnValue(false) // prefers light
const { result } = renderHook(() => useThemeContext(), { wrapper })
expect(result.current.mode).toBe('system')
})
})
describe('localStorage persistence', () => {
it('должен сохранять mode в localStorage при инициализации', () => {
localStorageMock.getItem.mockReturnValue(null)
renderHook(() => useThemeContext(), { wrapper })
expect(localStorageMock.setItem).toHaveBeenCalledWith('themeMode', 'system')
})
it('должен читать mode из localStorage', () => {
localStorageMock.getItem.mockReturnValue('dark')
renderHook(() => useThemeContext(), { wrapper })
expect(localStorageMock.getItem).toHaveBeenCalledWith('themeMode')
})
})
describe('mode values', () => {
it('должен поддерживать light mode', () => {
localStorageMock.getItem.mockReturnValue('light')
const { result } = renderHook(() => useThemeContext(), { wrapper })
expect(result.current.mode).toBe('light')
})
it('должен поддерживать dark mode', () => {
localStorageMock.getItem.mockReturnValue('dark')
const { result } = renderHook(() => useThemeContext(), { wrapper })
expect(result.current.mode).toBe('dark')
})
it('должен поддерживать system mode', () => {
localStorageMock.getItem.mockReturnValue('system')
const { result } = renderHook(() => useThemeContext(), { wrapper })
expect(result.current.mode).toBe('system')
})
})
})