Files
hapi/web/src/hooks/useTheme.test.ts
T
NightWatcher314andGitHub f8657dae3a feat(web): add color theme presets (#1040)
* feat(web): add color theme presets

* fix(web): preserve presets with custom colors

* fix(web): sync color theme across tabs

* fix(web): clear boot theme background

* feat(web): integrate palette presets with display settings
2026-07-16 12:32:54 +08:00

97 lines
3.8 KiB
TypeScript

import { act, renderHook } from '@testing-library/react'
import { beforeEach, describe, expect, it } from 'vitest'
import { getAppearanceOptions, getThemeColor, initializeTheme, useAppearance } from '@/hooks/useTheme'
describe('useTheme', () => {
beforeEach(() => {
localStorage.clear()
document.documentElement.removeAttribute('data-theme')
document.head.querySelectorAll('meta[name="theme-color"]').forEach((meta) => meta.remove())
})
it('applies the stored dark appearance to the document and browser theme color', () => {
localStorage.setItem('hapi-appearance', 'dark')
initializeTheme()
expect(document.documentElement).toHaveAttribute('data-theme', 'dark')
expect(document.querySelector<HTMLMetaElement>('meta[name="theme-color"]')?.content).toBe(getThemeColor('dark'))
})
it('creates a browser theme color meta tag when the page does not provide one', () => {
initializeTheme()
const meta = document.querySelector<HTMLMetaElement>('meta[name="theme-color"]')
expect(meta?.content).toBe(getThemeColor('light'))
expect(meta?.hasAttribute('media')).toBe(false)
})
it('clears the boot-time inline html background when runtime theme initializes', () => {
document.documentElement.style.backgroundColor = '#fbfbff'
localStorage.setItem('hapi-color-theme', 'one')
initializeTheme()
expect(document.documentElement.style.backgroundColor).toBe('')
expect(document.documentElement.style.getPropertyValue('--app-bg')).toBe('#fbfbff')
})
it('updates browser theme color after a cross-tab color theme change', () => {
localStorage.setItem('hapi-color-theme', 'one')
initializeTheme()
expect(document.querySelector<HTMLMetaElement>('meta[name="theme-color"]')?.content).toBe('#fbfbff')
localStorage.setItem('hapi-color-theme', 'notion')
act(() => {
window.dispatchEvent(new StorageEvent('storage', { key: 'hapi-color-theme', newValue: 'notion' }))
})
expect(document.querySelector<HTMLMetaElement>('meta[name="theme-color"]')?.content).toBe('#fafafa')
})
it('updates the browser theme color when appearance changes', () => {
const { result } = renderHook(() => useAppearance())
act(() => {
result.current.setAppearance('dark')
})
expect(document.documentElement).toHaveAttribute('data-theme', 'dark')
expect(document.querySelector<HTMLMetaElement>('meta[name="theme-color"]')?.content).toBe(getThemeColor('dark'))
act(() => {
result.current.setAppearance('light')
})
expect(document.documentElement).toHaveAttribute('data-theme', 'light')
expect(document.querySelector<HTMLMetaElement>('meta[name="theme-color"]')?.content).toBe(getThemeColor('light'))
})
it('exposes OLED Black as a selectable appearance option', () => {
expect(getAppearanceOptions().some((opt) => opt.value === 'oled')).toBe(true)
})
it('applies the OLED appearance with a pure-black browser theme color', () => {
localStorage.setItem('hapi-appearance', 'oled')
initializeTheme()
expect(document.documentElement).toHaveAttribute('data-theme', 'oled')
expect(getThemeColor('oled')).toBe('#000000')
expect(document.querySelector<HTMLMetaElement>('meta[name="theme-color"]')?.content).toBe('#000000')
})
it('does not auto-select OLED for the system appearance', () => {
// No stored appearance => system; system must resolve to light/dark, never OLED.
initializeTheme()
const theme = document.documentElement.getAttribute('data-theme')
expect(theme === 'light' || theme === 'dark').toBe(true)
expect(theme).not.toBe('oled')
})
})