// 验证 header logo 明暗双版:浅色应显示 logo.svg(黑字),深色应显示 logo-white.svg(白字) // 用法:node scripts/audit/logo-dark-verify.mjs import { chromium } from 'playwright'; const BASE = 'http://127.0.0.1:3000'; const OUT = 'dogfood-output/logo-dark-verify'; const shots = []; async function run() { const browser = await chromium.launch(); const ctx = await browser.newContext({ viewport: { width: 1280, height: 800 } }); const page = await ctx.newPage(); // 浅色(默认) await page.goto(BASE, { waitUntil: 'networkidle' }); await page.waitForTimeout(800); const lightSrc = await page.$eval('header img', (el) => el.getAttribute('src')); const visibleLight = await getVisible(page); await page.screenshot({ path: `${OUT}/header-light.png` }); shots.push({ theme: 'light', htmlDataTheme: await page.getAttribute('html', 'data-theme'), firstImgSrc: lightSrc, visible: visibleLight }); // 深色:设 localStorage 后 reload(与站点内联脚本一致) await page.evaluate(() => localStorage.setItem('novalon-theme', 'dark')); await page.goto(BASE, { waitUntil: 'networkidle' }); await page.waitForTimeout(800); const visibleDark = await getVisible(page); const darkSrc = await page.$eval('header img', (el) => el.getAttribute('src')).catch(() => 'N/A'); await page.screenshot({ path: `${OUT}/header-dark.png` }); shots.push({ theme: 'dark', htmlDataTheme: await page.getAttribute('html', 'data-theme'), firstImgSrc: darkSrc, visible: visibleDark }); await browser.close(); console.log(JSON.stringify(shots, null, 2)); // 断言:浅色无 data-theme 属性(null),可见 logo.svg;深色 data-theme=dark,可见 logo-white.svg const lightOk = shots[0].visible === 'logo.svg' && shots[0].htmlDataTheme !== 'dark'; const darkOk = shots[1].visible === 'logo-white.svg' && shots[1].htmlDataTheme === 'dark'; console.log(`\n[assert] light shows logo.svg: ${lightOk}`); console.log(`[assert] dark shows logo-white.svg: ${darkOk}`); console.log(`[result] ${lightOk && darkOk ? 'PASS' : 'FAIL'}`); } // 返回当前实际可见(display !== none)的 logo img 的 src async function getVisible(page) { return page.evaluate(() => { const imgs = Array.from(document.querySelectorAll('header img')); const vis = imgs.filter((i) => { const s = getComputedStyle(i); return s.display !== 'none' && s.visibility !== 'hidden' && Number(s.opacity) > 0; }); return vis.length ? vis[0].getAttribute('src') : 'NONE_VISIBLE'; }); } run().catch((e) => { console.error(e); process.exit(1); });