import path from 'path'; import { test, expect } from '@playwright/test'; import type { Page } from '@playwright/test'; const DEMO_ACCOUNTS = { admin: { email: 'admin@example.com', password: 'demo123' }, }; async function takeScreenshot(page: Page, name: string) { const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); const fileName = `theme_${name}_${timestamp}.png`; await page.screenshot({ path: path.join('screenshots', fileName), fullPage: true }); return fileName; } async function loginAsAdmin(page: Page) { await page.goto('/login'); await page.fill('[data-testid="email-input"]', DEMO_ACCOUNTS.admin.email); await page.fill('[data-testid="password-input"]', DEMO_ACCOUNTS.admin.password); await page.click('[data-testid="login-button"]'); await expect(page).toHaveURL('/dashboard', { timeout: 10000 }); } async function getThemeFromLocalStorage(page: Page) { return await page.evaluate(() => localStorage.getItem('bct_theme')); } async function getDocumentTheme(page: Page) { return await page.evaluate(() => document.documentElement.classList.contains('dark')); } test.describe('Theme Switching', () => { test.beforeEach(async ({ page }) => { // Clear theme storage await page.evaluate(() => { localStorage.removeItem('bct_theme'); localStorage.removeItem('bct_auth_user'); localStorage.removeItem('bct_auth_remember'); }); }); test('should start with default light theme', async ({ page }) => { await page.goto('/'); // Should start with light theme const isDark = await getDocumentTheme(page); expect(isDark).toBe(false); await takeScreenshot(page, 'default-light-theme'); // Check theme toggle button shows correct state await expect(page.locator('[data-testid="theme-toggle"]')).toBeVisible(); }); test('should switch from light to dark theme', async ({ page }) => { await loginAsAdmin(page); // Verify starting in light theme let isDark = await getDocumentTheme(page); expect(isDark).toBe(false); await takeScreenshot(page, 'before-dark-switch'); // Click theme toggle await page.click('[data-testid="theme-toggle"]'); // Should switch to dark theme isDark = await getDocumentTheme(page); expect(isDark).toBe(true); // Verify theme is saved to localStorage const savedTheme = await getThemeFromLocalStorage(page); expect(savedTheme).toBe('dark'); await takeScreenshot(page, 'after-dark-switch'); // Check visual elements have dark theme classes await expect(page.locator('body')).toHaveClass(/dark/); }); test('should switch from dark to light theme', async ({ page }) => { // Set dark theme initially await page.evaluate(() => { localStorage.setItem('bct_theme', 'dark'); document.documentElement.classList.add('dark'); }); await loginAsAdmin(page); // Verify starting in dark theme let isDark = await getDocumentTheme(page); expect(isDark).toBe(true); await takeScreenshot(page, 'before-light-switch'); // Click theme toggle await page.click('[data-testid="theme-toggle"]'); // Should switch to light theme isDark = await getDocumentTheme(page); expect(isDark).toBe(false); // Verify theme is saved to localStorage const savedTheme = await getThemeFromLocalStorage(page); expect(savedTheme).toBe('light'); await takeScreenshot(page, 'after-light-switch'); }); test('should persist theme across page refreshes', async ({ page }) => { await loginAsAdmin(page); // Switch to dark theme await page.click('[data-testid="theme-toggle"]'); let isDark = await getDocumentTheme(page); expect(isDark).toBe(true); // Refresh the page await page.reload(); // Theme should persist isDark = await getDocumentTheme(page); expect(isDark).toBe(true); const savedTheme = await getThemeFromLocalStorage(page); expect(savedTheme).toBe('dark'); await takeScreenshot(page, 'dark-theme-after-refresh'); }); test('should persist theme across navigation', async ({ page }) => { await loginAsAdmin(page); // Switch to dark theme await page.click('[data-testid="theme-toggle"]'); await takeScreenshot(page, 'dark-theme-dashboard'); // Navigate to events page await page.click('[data-testid="nav-events"]'); await expect(page).toHaveURL('/events'); // Theme should persist const isDark = await getDocumentTheme(page); expect(isDark).toBe(true); await takeScreenshot(page, 'dark-theme-events-page'); // Navigate back to dashboard await page.click('[data-testid="nav-dashboard"]'); await expect(page).toHaveURL('/dashboard'); // Theme should still persist const stillDark = await getDocumentTheme(page); expect(stillDark).toBe(true); await takeScreenshot(page, 'dark-theme-dashboard-return'); }); test('should apply theme to all components', async ({ page }) => { await loginAsAdmin(page); // Take screenshot in light theme await takeScreenshot(page, 'components-light-theme'); // Switch to dark theme await page.click('[data-testid="theme-toggle"]'); // Check that key components have theme applied await expect(page.locator('[data-testid="sidebar"]')).toHaveClass(/dark/); await expect(page.locator('[data-testid="header"]')).toHaveClass(/dark/); await expect(page.locator('[data-testid="main-content"]')).toHaveClass(/dark/); await takeScreenshot(page, 'components-dark-theme'); }); test('should handle system theme preference', async ({ page }) => { // Mock system preference for dark theme await page.emulateMedia({ colorScheme: 'dark' }); await page.goto('/'); // Should respect system preference await takeScreenshot(page, 'system-dark-preference'); // Mock system preference for light theme await page.emulateMedia({ colorScheme: 'light' }); await page.reload(); await takeScreenshot(page, 'system-light-preference'); }); test('should show theme toggle with correct icon', async ({ page }) => { await loginAsAdmin(page); // In light theme, should show moon icon (to switch to dark) let isDark = await getDocumentTheme(page); expect(isDark).toBe(false); await expect(page.locator('[data-testid="theme-toggle"] [data-testid="moon-icon"]')).toBeVisible(); await takeScreenshot(page, 'theme-toggle-light-mode'); // Switch to dark theme await page.click('[data-testid="theme-toggle"]'); // In dark theme, should show sun icon (to switch to light) isDark = await getDocumentTheme(page); expect(isDark).toBe(true); await expect(page.locator('[data-testid="theme-toggle"] [data-testid="sun-icon"]')).toBeVisible(); await takeScreenshot(page, 'theme-toggle-dark-mode'); }); test('should handle theme toggle keyboard interaction', async ({ page }) => { await loginAsAdmin(page); // Focus the theme toggle button await page.focus('[data-testid="theme-toggle"]'); // Press Enter to toggle theme await page.keyboard.press('Enter'); // Should switch to dark theme const isDark = await getDocumentTheme(page); expect(isDark).toBe(true); await takeScreenshot(page, 'keyboard-theme-toggle'); // Press Space to toggle back await page.keyboard.press('Space'); // Should switch back to light theme const isLight = await getDocumentTheme(page); expect(isLight).toBe(false); }); test('should maintain proper contrast ratios in both themes', async ({ page }) => { await loginAsAdmin(page); // Test light theme contrast await takeScreenshot(page, 'contrast-light-theme'); // Check text is readable against background const lightTextColor = await page.locator('h1').evaluate((el) => getComputedStyle(el).color ); const lightBgColor = await page.locator('body').evaluate((el) => getComputedStyle(el).backgroundColor ); console.log('Light theme - Text:', lightTextColor, 'Background:', lightBgColor); // Switch to dark theme await page.click('[data-testid="theme-toggle"]'); await takeScreenshot(page, 'contrast-dark-theme'); // Check text is readable against background const darkTextColor = await page.locator('h1').evaluate((el) => getComputedStyle(el).color ); const darkBgColor = await page.locator('body').evaluate((el) => getComputedStyle(el).backgroundColor ); console.log('Dark theme - Text:', darkTextColor, 'Background:', darkBgColor); }); test('should handle rapid theme switching', async ({ page }) => { await loginAsAdmin(page); // Rapidly toggle theme multiple times for (let i = 0; i < 5; i++) { await page.click('[data-testid="theme-toggle"]'); await page.waitForTimeout(100); // Small delay to see the transition } // Should end up in dark theme (odd number of clicks) const isDark = await getDocumentTheme(page); expect(isDark).toBe(true); await takeScreenshot(page, 'rapid-theme-switching-end'); // Theme should be saved correctly const savedTheme = await getThemeFromLocalStorage(page); expect(savedTheme).toBe('dark'); }); test('should handle theme on login page', async ({ page }) => { await page.goto('/login'); // Theme toggle should be available on login page await expect(page.locator('[data-testid="theme-toggle"]')).toBeVisible(); await takeScreenshot(page, 'login-page-light-theme'); // Switch to dark theme await page.click('[data-testid="theme-toggle"]'); const isDark = await getDocumentTheme(page); expect(isDark).toBe(true); await takeScreenshot(page, 'login-page-dark-theme'); // Login should maintain theme await page.fill('[data-testid="email-input"]', DEMO_ACCOUNTS.admin.email); await page.fill('[data-testid="password-input"]', DEMO_ACCOUNTS.admin.password); await page.click('[data-testid="login-button"]'); await expect(page).toHaveURL('/dashboard', { timeout: 10000 }); // Theme should persist after login const stillDark = await getDocumentTheme(page); expect(stillDark).toBe(true); await takeScreenshot(page, 'dashboard-after-login-dark-theme'); }); });