- theme: light and dark ignore prefers-color-scheme, choices and guard - i18n: ?locale= only with a preference, availableLocales from meta - usePreferences: cache validation, cycle order and PUT bodies, revert with a danger toast, profile apply, setLocale save, cache and reload - rail theme button, user menu language group, --shell-offset and the form save bar offset; fixture strings and resetState theme reset
72 lines
2.7 KiB
TypeScript
72 lines
2.7 KiB
TypeScript
import { afterEach, describe, expect, it } from 'vitest'
|
|
import { applyColorScheme, DARK_QUERY, isThemeChoice, THEME_CHOICES } from '../../src/app/theme'
|
|
import { fakeMedia } from '../helpers'
|
|
|
|
afterEach(() => {
|
|
document.documentElement.classList.remove('dark')
|
|
})
|
|
|
|
describe('applyColorScheme', () => {
|
|
it('sets .dark from prefers-color-scheme and follows changes until unsubscribed', () => {
|
|
const media = fakeMedia({ [DARK_QUERY]: true })
|
|
const stop = applyColorScheme()
|
|
expect(document.documentElement.classList.contains('dark')).toBe(true)
|
|
expect(media.listeners(DARK_QUERY)).toBe(1)
|
|
|
|
media.change(DARK_QUERY, false)
|
|
expect(document.documentElement.classList.contains('dark')).toBe(false)
|
|
media.change(DARK_QUERY, true)
|
|
expect(document.documentElement.classList.contains('dark')).toBe(true)
|
|
|
|
stop()
|
|
expect(media.listeners(DARK_QUERY)).toBe(0)
|
|
media.change(DARK_QUERY, false)
|
|
expect(document.documentElement.classList.contains('dark')).toBe(true)
|
|
})
|
|
|
|
it('stays light without matchMedia', () => {
|
|
document.documentElement.classList.add('dark')
|
|
const win = { document } as unknown as Window
|
|
const stop = applyColorScheme(win)
|
|
expect(document.documentElement.classList.contains('dark')).toBe(false)
|
|
expect(stop()).toBeUndefined()
|
|
})
|
|
|
|
it('light never sets .dark and ignores the media query', () => {
|
|
const media = fakeMedia({ [DARK_QUERY]: true })
|
|
document.documentElement.classList.add('dark')
|
|
const stop = applyColorScheme(window, 'light')
|
|
expect(document.documentElement.classList.contains('dark')).toBe(false)
|
|
expect(media.listeners(DARK_QUERY)).toBe(0)
|
|
media.change(DARK_QUERY, true)
|
|
expect(document.documentElement.classList.contains('dark')).toBe(false)
|
|
expect(stop()).toBeUndefined()
|
|
})
|
|
|
|
it('dark always sets .dark and ignores the media query', () => {
|
|
const media = fakeMedia({ [DARK_QUERY]: false })
|
|
const stop = applyColorScheme(window, 'dark')
|
|
expect(document.documentElement.classList.contains('dark')).toBe(true)
|
|
expect(media.listeners(DARK_QUERY)).toBe(0)
|
|
media.change(DARK_QUERY, false)
|
|
expect(document.documentElement.classList.contains('dark')).toBe(true)
|
|
stop()
|
|
})
|
|
|
|
it('dark works without matchMedia', () => {
|
|
const win = { document } as unknown as Window
|
|
applyColorScheme(win, 'dark')
|
|
expect(document.documentElement.classList.contains('dark')).toBe(true)
|
|
})
|
|
})
|
|
|
|
describe('theme choices', () => {
|
|
it('lists system, light and dark in cycle order and guards values', () => {
|
|
expect(THEME_CHOICES).toEqual(['system', 'light', 'dark'])
|
|
expect(isThemeChoice('dark')).toBe(true)
|
|
expect(isThemeChoice('blue')).toBe(false)
|
|
expect(isThemeChoice(null)).toBe(false)
|
|
expect(isThemeChoice(1)).toBe(false)
|
|
})
|
|
})
|