import { expect, test, type Page } from '@playwright/test'; import { contextForUser } from './helpers'; /** * Theme-Umschalter (issue #180): die Hell/Dunkel/System-Wahl in den * Einstellungen wirkt sofort, überlebt den Reload (localStorage) und folgt * im System-Modus Live-Änderungen des OS-Schemas. Klassen-Hooks statt * lokalisierter Texte (UI-Sprache folgt dem Profil-Locale). */ const BASE = process.env.E2E_BASE_URL ?? 'http://localhost:5173'; const radio = (page: Page, value: string) => page.locator(`.settings-fieldset input[value="${value}"]`); const effectiveTheme = (page: Page) => page.evaluate(() => document.documentElement.dataset.theme); test('theme choice applies instantly, persists, and system mode follows the OS', async ({ browser, }) => { const context = await contextForUser(browser, BASE, 'fixture-user'); const page = await context.newPage(); await page.emulateMedia({ colorScheme: 'light' }); await page.goto('/settings'); await page.locator('input[name="theme-mode"]').first().waitFor(); // Default: System, auf einem hellen OS also light. await expect(radio(page, 'system')).toBeChecked(); expect(await effectiveTheme(page)).toBe('light'); // Explizit Dunkel: wirkt sofort, ohne Reload, trotz hellem OS. await radio(page, 'dark').check(); expect(await effectiveTheme(page)).toBe('dark'); await expect(page.locator('meta[name="theme-color"]').first()).toHaveAttribute( 'content', '#10161d', ); // Persistenz: Wahl und Theme überleben den Reload (theme-init.js liest // denselben localStorage-Key vor dem ersten Paint). await page.reload(); await page.locator('input[name="theme-mode"]').first().waitFor(); await expect(radio(page, 'dark')).toBeChecked(); expect(await effectiveTheme(page)).toBe('dark'); expect(await page.evaluate(() => window.localStorage.getItem('ui.theme.mode'))).toBe('"dark"'); // Zurück auf System: folgt dem hellen OS … await radio(page, 'system').check(); expect(await effectiveTheme(page)).toBe('light'); // … und Live-Wechseln des OS-Schemas ohne Interaktion. await page.emulateMedia({ colorScheme: 'dark' }); await expect .poll(async () => effectiveTheme(page), { message: 'system mode follows OS change' }) .toBe('dark'); await page.emulateMedia({ colorScheme: 'light' }); await expect.poll(async () => effectiveTheme(page)).toBe('light'); // Explizite Wahl gewinnt gegen das OS-Schema. await page.emulateMedia({ colorScheme: 'dark' }); await radio(page, 'light').check(); expect(await effectiveTheme(page)).toBe('light'); await context.close(); }); test('the top-bar toggle cycles the mode and stays in sync with the radios', async ({ browser, }) => { const context = await contextForUser(browser, BASE, 'fixture-user'); const page = await context.newPage(); await page.emulateMedia({ colorScheme: 'light' }); await page.goto('/settings'); await page.locator('input[name="theme-mode"]').first().waitFor(); const toggle = page.locator('.topbar__theme'); // Default System → ein Klick zykelt in Radio-Reihenfolge weiter zu Hell, // dann Dunkel, dann zurück zu System; die Radios folgen (issue #182). await toggle.click(); await expect(radio(page, 'light')).toBeChecked(); expect(await effectiveTheme(page)).toBe('light'); await toggle.click(); await expect(radio(page, 'dark')).toBeChecked(); expect(await effectiveTheme(page)).toBe('dark'); await toggle.click(); await expect(radio(page, 'system')).toBeChecked(); expect(await effectiveTheme(page)).toBe('light'); // helles OS // Auch andersherum: eine Radio-Wahl versetzt den Zyklus des Toggles. await radio(page, 'dark').check(); await toggle.click(); await expect(radio(page, 'system')).toBeChecked(); // Persistenz wie bei den Radios (gleicher localStorage-Key). await toggle.click(); // → light await page.reload(); await page.locator('input[name="theme-mode"]').first().waitFor(); await expect(radio(page, 'light')).toBeChecked(); expect(await effectiveTheme(page)).toBe('light'); await context.close(); }); test('the theme toggle works for signed-out visitors', async ({ page }) => { await page.emulateMedia({ colorScheme: 'light' }); await page.goto(`${BASE}/login`); const toggle = page.locator('.topbar__theme'); await toggle.waitFor(); await toggle.click(); // System → Hell await toggle.click(); // Hell → Dunkel expect(await effectiveTheme(page)).toBe('dark'); });