/** * # 脚本 screenshot-pages * 说明:用 Playwright 对五个主题站点与关键页面截图 + 控制台错误采集。 * 用于交付前的视觉验收。 */ import { chromium } from "playwright"; import { mkdirSync } from "node:fs"; const OUT = "/tmp/aidir-shots"; mkdirSync(OUT, { recursive: true }); const targets = [ { name: "classic-home", url: "http://localhost:3001/" }, { name: "classic-rankings", url: "http://localhost:3001/rankings" }, { name: "classic-tool-detail", url: "http://localhost:3001/tools/claude" }, { name: "classic-tools-list", url: "http://localhost:3001/tools" }, { name: "classic-best", url: "http://localhost:3001/best/best-ai-writing-tools" }, { name: "classic-usecase", url: "http://localhost:3001/ai-tools-for/blogging" }, { name: "classic-compare", url: "http://localhost:3001/tools/cursor-vs-windsurf" }, { name: "classic-alt", url: "http://localhost:3001/tools/midjourney-alternatives" }, { name: "classic-zh", url: "http://localhost:3001/zh" }, { name: "classic-submit", url: "http://localhost:3001/submit" }, { name: "product-home", url: "http://localhost:3002/" }, { name: "editorial-home", url: "http://localhost:3003/" }, { name: "niche-home", url: "http://localhost:3004/" }, { name: "minimal-home", url: "http://localhost:3005/" }, { name: "minimal-mobile", url: "http://localhost:3005/", mobile: true }, { name: "product-mobile", url: "http://localhost:3002/", mobile: true }, ]; const browser = await chromium.launch(); const errors: string[] = []; for (const target of targets) { const context = await browser.newContext( target.mobile ? { viewport: { width: 390, height: 844 }, deviceScaleFactor: 2, isMobile: true, hasTouch: true } : { viewport: { width: 1440, height: 900 }, deviceScaleFactor: 1 } ); const page = await context.newPage(); page.on("console", (msg) => { if (msg.type() === "error") errors.push(`${target.name}: ${msg.text().slice(0, 200)}`); }); page.on("pageerror", (err) => errors.push(`${target.name}: PAGEERROR ${String(err).slice(0, 200)}`)); try { await page.goto(target.url, { waitUntil: "domcontentloaded", timeout: 30000 }); await page.waitForTimeout(1200); await page.screenshot({ path: `${OUT}/${target.name}.png`, fullPage: false }); // 顺便记录 H1 const h1 = await page.locator("h1").first().textContent().catch(() => "(no h1)"); console.log(`OK ${target.name} | h1: ${h1?.trim().slice(0, 60)}`); } catch (error) { console.log(`FAIL ${target.name}: ${String(error).slice(0, 120)}`); } await context.close(); } await browser.close(); console.log("\n=== 控制台错误 ==="); console.log(errors.length === 0 ? "(none)" : errors.slice(0, 12).join("\n"));