2f466e46bc
Implements device-aware theme defaults: - Mobile devices (≤768px): Default to light theme - Desktop devices (>768px): Default to auto theme - Saved preferences: Always respected regardless of device Implementation: 1. FOUC Prevention Script (templates/index.html): - Detects device type using window.innerWidth/clientWidth/screen.width - Sets initial theme before page render to prevent flash - Mobile: 'light', Desktop: 'auto' 2. Theme Initialization (static/js/color-theme.js): - Modified initColorTheme() to respect FOUC-detected theme - Saves FOUC-detected theme to localStorage for persistence - Prevents overwriting mobile detection with 'auto' default Test Coverage: - Test 1: Mobile first visit → light theme ✅ - Test 2: Desktop first visit → auto theme ✅ - Test 3: Saved preference honored → dark theme ✅ Files Modified: - templates/index.html: Added mobile detection in FOUC prevention - static/js/color-theme.js: Respect FOUC-detected theme - tests/mjs/49-mobile-light-theme-default.mjs: Comprehensive test suite Screenshots: - tests/screenshots/mobile-light-theme-default.png - tests/screenshots/desktop-auto-theme-default.png
105 lines
3.9 KiB
JavaScript
Executable File
105 lines
3.9 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
|
|
import { chromium } from 'playwright';
|
|
|
|
const MOBILE_VIEWPORT = { width: 375, height: 667 };
|
|
const DESKTOP_VIEWPORT = { width: 1920, height: 1080 };
|
|
|
|
(async () => {
|
|
const browser = await chromium.launch({ headless: true });
|
|
|
|
console.log('🧪 Testing Default Theme by Device Type\n');
|
|
|
|
// TEST 1: Mobile First Visit - Should Default to Light Theme
|
|
console.log('📱 TEST 1: Mobile First Visit (No localStorage)\n');
|
|
const mobileContext = await browser.newContext({ viewport: MOBILE_VIEWPORT });
|
|
const mobilePage = await mobileContext.newPage();
|
|
|
|
// Clear localStorage to simulate first visit
|
|
await mobilePage.goto('http://localhost:1999/?lang=en&view=extended');
|
|
await mobilePage.evaluate(() => localStorage.clear());
|
|
await mobilePage.reload();
|
|
await mobilePage.waitForLoadState('networkidle');
|
|
|
|
// Check theme attribute
|
|
const mobileTheme = await mobilePage.evaluate(() => {
|
|
return document.documentElement.getAttribute('data-color-theme');
|
|
});
|
|
|
|
console.log(`📊 Mobile Theme:`);
|
|
console.log(` • Theme attribute: ${mobileTheme}`);
|
|
console.log(` • Expected: light`);
|
|
console.log(` • Match: ${mobileTheme === 'light' ? '✅' : '❌'}\n`);
|
|
|
|
await mobilePage.screenshot({
|
|
path: 'tests/screenshots/mobile-light-theme-default.png',
|
|
fullPage: true
|
|
});
|
|
console.log('📸 Screenshot: tests/screenshots/mobile-light-theme-default.png\n');
|
|
|
|
await mobileContext.close();
|
|
|
|
// TEST 2: Desktop First Visit - Should Default to Auto Theme
|
|
console.log('🖥️ TEST 2: Desktop First Visit (No localStorage)\n');
|
|
const desktopContext = await browser.newContext({ viewport: DESKTOP_VIEWPORT });
|
|
const desktopPage = await desktopContext.newPage();
|
|
|
|
// Clear localStorage to simulate first visit
|
|
await desktopPage.goto('http://localhost:1999/?lang=en&view=extended');
|
|
await desktopPage.evaluate(() => localStorage.clear());
|
|
await desktopPage.reload();
|
|
await desktopPage.waitForLoadState('networkidle');
|
|
|
|
// Check theme attribute
|
|
const desktopTheme = await desktopPage.evaluate(() => {
|
|
return document.documentElement.getAttribute('data-color-theme');
|
|
});
|
|
|
|
console.log(`📊 Desktop Theme:`);
|
|
console.log(` • Theme attribute: ${desktopTheme}`);
|
|
console.log(` • Expected: auto`);
|
|
console.log(` • Match: ${desktopTheme === 'auto' ? '✅' : '❌'}\n`);
|
|
|
|
await desktopPage.screenshot({
|
|
path: 'tests/screenshots/desktop-auto-theme-default.png',
|
|
fullPage: true
|
|
});
|
|
console.log('📸 Screenshot: tests/screenshots/desktop-auto-theme-default.png\n');
|
|
|
|
await desktopContext.close();
|
|
|
|
// TEST 3: Mobile with Saved Preference - Should Respect localStorage
|
|
console.log('📱 TEST 3: Mobile with Saved Preference (Should Honor User Choice)\n');
|
|
const mobileContext2 = await browser.newContext({ viewport: MOBILE_VIEWPORT });
|
|
const mobilePage2 = await mobileContext2.newPage();
|
|
|
|
await mobilePage2.goto('http://localhost:1999/?lang=en&view=extended');
|
|
await mobilePage2.evaluate(() => {
|
|
localStorage.setItem('color-theme-mode', 'dark');
|
|
});
|
|
await mobilePage2.reload();
|
|
await mobilePage2.waitForLoadState('networkidle');
|
|
|
|
const savedTheme = await mobilePage2.evaluate(() => {
|
|
return document.documentElement.getAttribute('data-color-theme');
|
|
});
|
|
|
|
console.log(`📊 Mobile with Saved Preference:`);
|
|
console.log(` • Theme attribute: ${savedTheme}`);
|
|
console.log(` • Expected: dark (from localStorage)`);
|
|
console.log(` • Match: ${savedTheme === 'dark' ? '✅' : '❌'}\n`);
|
|
|
|
await mobileContext2.close();
|
|
|
|
// Final Results
|
|
const allPassed = mobileTheme === 'light' &&
|
|
desktopTheme === 'auto' &&
|
|
savedTheme === 'dark';
|
|
|
|
console.log(`${allPassed ? '✅' : '❌'} All tests ${allPassed ? 'PASSED' : 'FAILED'}\n`);
|
|
|
|
await browser.close();
|
|
|
|
process.exit(allPassed ? 0 : 1);
|
|
})();
|