test(web/m7-362): theme store unit test (default/persist/matchMedia)
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
import { describe, expect, test, beforeEach, vi } from 'vitest';
|
||||
|
||||
function setupLocalStorage(initial: Record<string, string> = {}) {
|
||||
const store: Record<string, string> = { ...initial };
|
||||
vi.stubGlobal('localStorage', {
|
||||
getItem: (k: string) => (k in store ? store[k] : null),
|
||||
setItem: (k: string, v: string) => { store[k] = v; },
|
||||
removeItem: (k: string) => { delete store[k]; }
|
||||
});
|
||||
return store;
|
||||
}
|
||||
|
||||
function setupMatchMedia(prefersLight: boolean) {
|
||||
const listeners: ((e: { matches: boolean }) => void)[] = [];
|
||||
const mql = {
|
||||
matches: prefersLight,
|
||||
addEventListener: (_: string, cb: (e: { matches: boolean }) => void) => listeners.push(cb),
|
||||
removeEventListener: () => {}
|
||||
};
|
||||
vi.stubGlobal('window', {
|
||||
matchMedia: () => mql
|
||||
});
|
||||
return { mql, listeners, fire: (matches: boolean) => listeners.forEach((cb) => cb({ matches })) };
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
vi.resetModules();
|
||||
vi.unstubAllGlobals();
|
||||
if (typeof document !== 'undefined') {
|
||||
document.documentElement.removeAttribute('data-theme');
|
||||
}
|
||||
});
|
||||
|
||||
describe('theme store', () => {
|
||||
test('default preference is system; resolves via matchMedia', async () => {
|
||||
setupLocalStorage();
|
||||
setupMatchMedia(false); // prefers-dark
|
||||
const mod = await import('./theme.svelte');
|
||||
expect(mod.theme.value).toBe('system');
|
||||
expect(mod.resolvedTheme.value).toBe('dark');
|
||||
});
|
||||
|
||||
test('explicit preference from localStorage is honored', async () => {
|
||||
setupLocalStorage({ 'minstrel:theme': 'light' });
|
||||
setupMatchMedia(false);
|
||||
const mod = await import('./theme.svelte');
|
||||
expect(mod.theme.value).toBe('light');
|
||||
expect(mod.resolvedTheme.value).toBe('light');
|
||||
});
|
||||
|
||||
test('setTheme persists, updates resolved, sets data-theme attribute', async () => {
|
||||
const store = setupLocalStorage();
|
||||
setupMatchMedia(true);
|
||||
const mod = await import('./theme.svelte');
|
||||
mod.setTheme('dark');
|
||||
expect(store['minstrel:theme']).toBe('dark');
|
||||
expect(mod.theme.value).toBe('dark');
|
||||
expect(mod.resolvedTheme.value).toBe('dark');
|
||||
expect(document.documentElement.getAttribute('data-theme')).toBe('dark');
|
||||
});
|
||||
|
||||
test('matchMedia change updates resolved while in system mode', async () => {
|
||||
setupLocalStorage();
|
||||
const mm = setupMatchMedia(false); // prefers-dark
|
||||
const mod = await import('./theme.svelte');
|
||||
expect(mod.resolvedTheme.value).toBe('dark');
|
||||
mm.fire(true); // OS flipped to light
|
||||
expect(mod.resolvedTheme.value).toBe('light');
|
||||
});
|
||||
|
||||
test('matchMedia change does NOT update resolved when explicit preference set', async () => {
|
||||
setupLocalStorage({ 'minstrel:theme': 'dark' });
|
||||
const mm = setupMatchMedia(false);
|
||||
const mod = await import('./theme.svelte');
|
||||
expect(mod.resolvedTheme.value).toBe('dark');
|
||||
mm.fire(true); // OS prefers light, but user explicitly chose dark
|
||||
expect(mod.resolvedTheme.value).toBe('dark');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user