import { describe, expect, test, vi, beforeEach } from 'vitest'; import { render, screen, fireEvent } from '@testing-library/svelte'; vi.mock('$lib/api/listenbrainz', () => { // Mocks must look like a Svelte readable store. The page reads e.g. // $status.isPending, which is shorthand for "subscribe to status, // then read .isPending on the emitted value." Returning an object // that ALSO carries isPending/etc. as bare properties (without a // working subscribe) makes $status resolve to undefined. const stub = >(value: T) => ({ subscribe: (run: (v: T) => void) => { run(value); return () => {}; } }); return { createLBStatusQuery: () => stub({ isPending: false, isError: false, data: { token_set: false, enabled: false } }), createTokenMutation: () => stub({ mutate: vi.fn(), isPending: false }), createEnabledMutation: () => stub({ mutate: vi.fn(), isPending: false }) }; }); vi.mock('$lib/api/metrics', () => ({ createRecommendationMetricsQuery: () => ({ subscribe: (run: (v: unknown) => void) => { run({ isPending: false, isError: false, data: { window_days: 30, sources: [] } }); return () => {}; } }), sourceLabel: (s: string) => s })); beforeEach(() => { globalThis.localStorage.clear(); document.documentElement.removeAttribute('data-theme'); }); import SettingsPage from './+page.svelte'; describe('Settings → Appearance', () => { test('renders three theme options', () => { render(SettingsPage); expect(screen.getByText(/^Appearance$/)).toBeInTheDocument(); expect(screen.getByLabelText('dark', { exact: false })).toBeInTheDocument(); expect(screen.getByLabelText('light', { exact: false })).toBeInTheDocument(); expect(screen.getByLabelText('system', { exact: false })).toBeInTheDocument(); }); test('clicking light writes localStorage and sets data-theme', async () => { render(SettingsPage); const lightRadio = screen.getByLabelText('light', { exact: false }) as HTMLInputElement; await fireEvent.click(lightRadio); expect(localStorage.getItem('minstrel:theme')).toBe('light'); expect(document.documentElement.getAttribute('data-theme')).toBe('light'); }); test('clicking dark writes localStorage and sets data-theme', async () => { render(SettingsPage); const darkRadio = screen.getByLabelText('dark', { exact: false }) as HTMLInputElement; await fireEvent.click(darkRadio); expect(localStorage.getItem('minstrel:theme')).toBe('dark'); expect(document.documentElement.getAttribute('data-theme')).toBe('dark'); }); });