// Rendered-state verification: full-page shots at key widths + interaction states import puppeteer from "puppeteer-core"; import { mkdirSync } from "node:fs"; const CHROME = process.env.CHROME; const BASE = process.env.BASE ?? "http://localhost:4173"; if (!CHROME) throw new Error("CHROME env (chrome.exe path) is required"); mkdirSync("shots", { recursive: true }); const browser = await puppeteer.launch({ executablePath: CHROME, headless: "new" }); const page = await browser.newPage(); // Layout passes: reduced motion = static truth of the full page await page.emulateMediaFeatures([{ name: "prefers-reduced-motion", value: "reduce" }]); for (const w of [1440, 1280, 960, 375]) { await page.setViewport({ width: w, height: 900 }); await page.goto(BASE, { waitUntil: "networkidle0" }); await page.evaluate(() => document.fonts.ready); await new Promise((r) => setTimeout(r, 1400)); await page.screenshot({ path: `shots/w${w}.png`, fullPage: true }); console.log(`shot w${w}`); } // Motion pass: real scroll, reveals must land every section at opacity 1 await page.emulateMediaFeatures([{ name: "prefers-reduced-motion", value: "no-preference" }]); await page.setViewport({ width: 1440, height: 900 }); await page.goto(BASE, { waitUntil: "networkidle0" }); await new Promise((r) => setTimeout(r, 1200)); await page.evaluate(async () => { for (let y = 0; y <= document.body.scrollHeight; y += 300) { window.scrollTo(0, y); await new Promise((r) => setTimeout(r, 60)); } }); await new Promise((r) => setTimeout(r, 1200)); const opacities = await page.evaluate(() => [".shot", ".feat", ".verify h2", ".foot p"].map( (s) => `${s}=${getComputedStyle(document.querySelector(s)).opacity}`, ), ); console.log("post-scroll opacities:", opacities.join(" ")); await page.screenshot({ path: "shots/motion-bottom.png" }); await page.setViewport({ width: 1440, height: 900 }); await page.goto(BASE, { waitUntil: "networkidle0" }); await new Promise((r) => setTimeout(r, 1400)); await page.keyboard.press("g"); await new Promise((r) => setTimeout(r, 300)); await page.screenshot({ path: "shots/grid-overlay.png", fullPage: false }); console.log("shot grid-overlay"); await page.keyboard.press("g"); const ctx = browser.defaultBrowserContext(); await ctx.overridePermissions(BASE, ["clipboard-read", "clipboard-write", "clipboard-sanitized-write"]).catch(() => {}); await page.hover(".cmd"); await new Promise((r) => setTimeout(r, 200)); await page.screenshot({ path: "shots/cmd-hover.png" }); await page.click("#copy-btn"); await new Promise((r) => setTimeout(r, 400)); const state = await page.evaluate(() => ({ label: document.querySelector(".copy-text").textContent, clip: navigator.clipboard.readText ? undefined : "n/a", })); const clip = await page.evaluate(() => navigator.clipboard.readText().catch(() => "READ-FAIL")); console.log("copy label:", state.label, "| clipboard:", clip.slice(0, 60) + "…"); await page.screenshot({ path: "shots/cmd-copied.png" }); await browser.close(); console.log("SHOTS-DONE");