File size: 6,747 Bytes
97ee7cb | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 | import { expect, test } from '@playwright/test';
/**
* Theme toggle E2E tests for the happy variant.
*
* Tests run against the dev server started by the webServer config
* (VITE_SITE_VARIANT=happy on port 4173).
*/
test.describe('theme toggle (happy variant)', () => {
test.beforeEach(async ({ page }) => {
// Set variant to happy, clear theme preference ONLY on first load
// (addInitScript runs on every navigation, so we use a flag)
await page.addInitScript(() => {
if (!sessionStorage.getItem('__test_init_done')) {
localStorage.removeItem('worldmonitor-theme');
localStorage.removeItem('worldmonitor-variant');
localStorage.setItem('worldmonitor-variant', 'happy');
sessionStorage.setItem('__test_init_done', '1');
}
});
});
test('happy variant defaults to light theme', async ({ page }) => {
await page.goto('/');
await page.waitForSelector('#headerThemeToggle', { timeout: 15000 });
const theme = await page.evaluate(() => document.documentElement.dataset.theme);
expect(theme).toBe('light');
// Background should be light
const bg = await page.evaluate(() =>
getComputedStyle(document.documentElement).getPropertyValue('--bg').trim(),
);
expect(bg).toBe('#FAFAF5'); // happy light bg
});
test('toggle to dark mode changes CSS variables', async ({ page }) => {
await page.goto('/');
await page.waitForSelector('#headerThemeToggle', { timeout: 15000 });
// Start in light mode
expect(await page.evaluate(() => document.documentElement.dataset.theme)).toBe('light');
// Click theme toggle
await page.click('#headerThemeToggle');
await page.waitForTimeout(200); // let theme-changed event propagate
// Should now be dark
const theme = await page.evaluate(() => document.documentElement.dataset.theme);
expect(theme).toBe('dark');
// Background should be dark navy
const bg = await page.evaluate(() =>
getComputedStyle(document.documentElement).getPropertyValue('--bg').trim(),
);
expect(bg).toBe('#1A2332'); // happy dark bg
// Text should be warm off-white
const text = await page.evaluate(() =>
getComputedStyle(document.documentElement).getPropertyValue('--text').trim(),
);
expect(text).toBe('#E8E4DC'); // happy dark text
});
test('toggle back to light mode restores light CSS variables', async ({ page }) => {
await page.goto('/');
await page.waitForSelector('#headerThemeToggle', { timeout: 15000 });
// Toggle to dark
await page.click('#headerThemeToggle');
await page.waitForTimeout(200);
expect(await page.evaluate(() => document.documentElement.dataset.theme)).toBe('dark');
// Toggle back to light
await page.click('#headerThemeToggle');
await page.waitForTimeout(200);
const theme = await page.evaluate(() => document.documentElement.dataset.theme);
expect(theme).toBe('light');
const bg = await page.evaluate(() =>
getComputedStyle(document.documentElement).getPropertyValue('--bg').trim(),
);
expect(bg).toBe('#FAFAF5'); // happy light bg
});
test('dark mode persists across page reload', async ({ page }) => {
await page.goto('/');
await page.waitForSelector('#headerThemeToggle', { timeout: 15000 });
// Toggle to dark
await page.click('#headerThemeToggle');
await page.waitForTimeout(200);
expect(await page.evaluate(() => document.documentElement.dataset.theme)).toBe('dark');
// Verify localStorage has 'dark'
const stored = await page.evaluate(() => localStorage.getItem('worldmonitor-theme'));
expect(stored).toBe('dark');
// Reload the page
await page.reload();
await page.waitForSelector('#headerThemeToggle', { timeout: 15000 });
// Should still be dark after reload
const theme = await page.evaluate(() => document.documentElement.dataset.theme);
expect(theme).toBe('dark');
const bg = await page.evaluate(() =>
getComputedStyle(document.documentElement).getPropertyValue('--bg').trim(),
);
expect(bg).toBe('#1A2332'); // happy dark bg, NOT #FAFAF5
});
test('theme toggle icon updates correctly', async ({ page }) => {
await page.goto('/');
await page.waitForSelector('#headerThemeToggle', { timeout: 15000 });
// In light mode, icon should be moon (dark mode switch)
const lightIcon = await page.locator('#headerThemeToggle svg path').count();
// Moon icon has a <path>, sun icon has <circle> + <line> elements
const hasMoon = lightIcon > 0;
expect(hasMoon).toBe(true);
// Toggle to dark
await page.click('#headerThemeToggle');
await page.waitForTimeout(200);
// In dark mode, icon should be sun (light mode switch)
const hasSun = await page.locator('#headerThemeToggle svg circle').count();
expect(hasSun).toBeGreaterThan(0);
});
test('panel backgrounds update on theme toggle', async ({ page }) => {
await page.goto('/');
await page.waitForSelector('.panel', { timeout: 20000 });
// Get panel bg in light mode
const lightPanelBg = await page.evaluate(() =>
getComputedStyle(document.documentElement).getPropertyValue('--panel-bg').trim(),
);
expect(lightPanelBg).toBe('#FFFFFF');
// Toggle to dark
await page.click('#headerThemeToggle');
await page.waitForTimeout(300);
// Panel bg should change
const darkPanelBg = await page.evaluate(() =>
getComputedStyle(document.documentElement).getPropertyValue('--panel-bg').trim(),
);
expect(darkPanelBg).toBe('#222E3E'); // happy dark panel bg
});
test('no FOUC: data-theme is set before main CSS loads', async ({ page }) => {
// Set dark preference before navigation
await page.addInitScript(() => {
localStorage.setItem('worldmonitor-theme', 'dark');
localStorage.setItem('worldmonitor-variant', 'happy');
});
await page.goto('/');
// The inline script should set data-theme="dark" before CSS loads
// Measure the data-theme immediately after navigation
const theme = await page.evaluate(() => document.documentElement.dataset.theme);
expect(theme).toBe('dark');
});
test('screenshot comparison: light vs dark', async ({ page }) => {
await page.goto('/');
await page.waitForSelector('.panel', { timeout: 20000 });
await page.waitForTimeout(2000); // let panels render
// Screenshot in light mode
await page.screenshot({ path: '/tmp/happy-light.png', fullPage: false });
// Toggle to dark
await page.click('#headerThemeToggle');
await page.waitForTimeout(1000);
// Screenshot in dark mode
await page.screenshot({ path: '/tmp/happy-dark.png', fullPage: false });
});
});
|