import { beforeEach, describe, expect, it, vi } from 'vitest' import { applyClientAnimationMode, applyThemeTokens, buildThemeTokens, CLIENT_SETTINGS_STORAGE_KEY, DARK_THEME_TOKENS, DEFAULT_POST_LIST_ORDER, getClientListLimit, getClientListOrder, getClientThemeMode, LIGHT_THEME_TOKENS, loadClientSettings, setClientListSettings, setClientThemeAppearance } from '@/lib/settings' import type { FetchPostsOrder } from '@/types' const installMatchMedia = (matches: boolean) => { Object.defineProperty (window, 'matchMedia', { configurable: true, value: vi.fn ().mockImplementation (() => ({ matches, addEventListener: vi.fn (), removeEventListener: vi.fn () })) }) } describe ('settings', () => { beforeEach (() => { localStorage.clear () document.documentElement.removeAttribute ('data-animation') document.documentElement.removeAttribute ('style') document.documentElement.className = '' installMatchMedia (false) }) it ('stores list settings under the shared client settings key', () => { setClientListSettings ('postSearch', { limit: 50, order: 'title:asc' }) expect (getClientListLimit ('postSearch')).toBe (50) expect (getClientListOrder ('postSearch')).toBe ('title:asc') expect (JSON.parse (localStorage.getItem (CLIENT_SETTINGS_STORAGE_KEY) ?? '{}')) .toMatchObject ({ lists: { postSearch: { limit: 50, order: 'title:asc' } } }) }) it ('ignores invalid list limits and keeps stored order strings', () => { localStorage.setItem ( CLIENT_SETTINGS_STORAGE_KEY, JSON.stringify ({ lists: { postList: { limit: 30, order: DEFAULT_POST_LIST_ORDER } } })) expect (getClientListLimit ('postList')).toBeNull () expect (getClientListOrder ('postList')).toBe ( DEFAULT_POST_LIST_ORDER) }) it ('stores theme mode and light/dark slot selections in client settings', () => { setClientThemeAppearance ({ themeMode: 'dark', activeLightThemeSlotNo: 2, activeDarkThemeSlotNo: 3 }) expect (getClientThemeMode ()).toBe ('dark') expect (loadClientSettings ().appearance).toMatchObject ({ themeMode: 'dark', activeLightThemeSlotNo: 2, activeDarkThemeSlotNo: 3 }) }) it ('applies animation mode to the document dataset', () => { applyClientAnimationMode ('off') expect (document.documentElement.dataset.animation).toBe ('off') applyClientAnimationMode ('reduced') expect (document.documentElement.dataset.animation).toBe ('reduced') applyClientAnimationMode ('normal') expect (document.documentElement.dataset.animation).toBeUndefined () }) it ('normalises TopNav menu link tokens to complete CSS colours', () => { const tokens = buildThemeTokens ('light', { link: '221.2 83.2% 53.3%', topNavMenuLink: '221.2 83.2% 53.3%' }) expect (tokens.link).toBe ('221.2 83.2% 53.3%') expect (tokens.topNavMenuLink).toBe ('hsl(221.2 83.2% 53.3%)') }) it ('derives TopNav colours without mixing mobile active backgrounds', () => { const tokens = buildThemeTokens ('light', { }) expect (tokens.topNavMobileActiveBackground).toBe ( LIGHT_THEME_TOKENS.topNavRootBackgroundDesktop) expect (tokens.topNavMobileActiveBackground).not.toBe ( tokens.topNavRootBackgroundMobile) expect (buildThemeTokens ('dark', { }).topNavMobileActiveBackground).toBe ( DARK_THEME_TOKENS.topNavActiveBackground) }) it ('sets shadcn tokens and TopNav tokens with their different value formats', () => { const tokens = buildThemeTokens ('dark', { link: '213.1 93.9% 67.8%' }) applyThemeTokens (tokens) expect (document.documentElement.style.getPropertyValue ('--theme-link')).toBe ( '213.1 93.9% 67.8%') expect (document.documentElement.style.getPropertyValue ('--top-nav-menu-link')).toBe ( 'hsl(213.1 93.9% 67.8%)') }) })