/** * 模拟点深色模式按钮后的 header 截图 * 找切换按钮:theme toggle / data-theme / aria-label 含亮暗 */ import { chromium } from 'playwright'; import fs from 'node:fs'; import path from 'node:path'; const OUT = path.join(process.cwd(), 'dogfood-ui-audit', 'logo-compare'); fs.mkdirSync(OUT, { recursive: true }); const TARGETS = [ { tag: 'prod', url: 'https://www.novalon.cn/' }, { tag: 'local', url: 'http://127.0.0.1:3000/' }, ]; const browser = await chromium.launch(); for (const t of TARGETS) { const ctx = await browser.newContext({ viewport: { width: 1440, height: 900 }, deviceScaleFactor: 2, }); const page = await ctx.newPage(); try { await page.goto(t.url, { waitUntil: 'networkidle', timeout: 60000 }); await page.waitForTimeout(1500); // 1) 找主题切换按钮 const btnInfo = await page.evaluate(() => { const candidates = [ ...document.querySelectorAll('header button, header [role="button"], [data-theme-toggle], [aria-label*="深" i], [aria-label*="暗" i], [aria-label*="dark" i], [aria-label*="theme" i], [aria-label*="切换" i]'), ]; return candidates.map(b => ({ tag: b.tagName, label: b.getAttribute('aria-label'), text: (b.textContent || '').slice(0, 30), className: (b.className || '').toString().slice(0, 60), data: JSON.stringify(b.dataset).slice(0, 80), })).slice(0, 6); }); console.log(`[${t.tag}] 候选切换按钮:`, JSON.stringify(btnInfo, null, 2)); // 2) 直接通过 localStorage 强制 dark(更稳) await page.evaluate(() => { try { localStorage.setItem('novalon-theme', 'dark'); } catch {} }); await page.reload({ waitUntil: 'networkidle' }); await page.waitForTimeout(1500); // 3) 截深色模式下的 header const header = page.locator('header').first(); await header.screenshot({ path: path.join(OUT, `${t.tag}-dark-header.png`) }).catch(async () => { await page.screenshot({ path: path.join(OUT, `${t.tag}-dark-header.png`), clip: { x: 0, y: 0, width: 1440, height: 80 } }); }); // 4) 探测 logo 在深色模式下的实际颜色 const logoState = await page.evaluate(() => { const img = [...document.querySelectorAll('img')].find(i => /logo\.svg$/.test(i.getAttribute('src') || '')); if (!img) return { found: false }; const r = img.getBoundingClientRect(); const parent = img.closest('header') || document.body; return { found: true, src: img.getAttribute('src'), renderedW: Math.round(r.width), renderedH: Math.round(r.height), parentBg: getComputedStyle(parent).backgroundColor, htmlClass: document.documentElement.className, htmlDataTheme: document.documentElement.getAttribute('data-theme'), }; }); console.log(`[${t.tag}] dark-mode logo state:`, JSON.stringify(logoState, null, 2)); } catch (e) { console.log(`[${t.tag}] FAILED ${e.message.slice(0, 200)}`); } await ctx.close(); } await browser.close(); console.log('\n✓ 输出:' + OUT);