#!/usr/bin/env node /** * Visual self-check script — captures screenshots of landing/auth pages. * Usage: * node scripts/screenshot.mjs [path] [viewport] * node scripts/screenshot.mjs / desktop → screenshots/landing-desktop.png * node scripts/screenshot.mjs /login mobile → screenshots/login-mobile.png * node scripts/screenshot.mjs --all → all pages, both viewports */ import { chromium } from '@playwright/test'; import { mkdirSync } from 'fs'; import { join, dirname } from 'path'; import { fileURLToPath } from 'url'; const __dirname = dirname(fileURLToPath(import.meta.url)); const screenshotsDir = join(__dirname, '..', 'screenshots'); mkdirSync(screenshotsDir, { recursive: true }); const BASE_URL = process.env.BASE_URL || 'http://localhost:5173'; const VIEWPORTS = { desktop: { width: 1440, height: 900 }, mobile: { width: 390, height: 844 }, }; const PAGES = [ { path: '/', slug: 'landing', fullPage: true }, { path: '/login', slug: 'login', fullPage: true }, { path: '/register', slug: 'register', fullPage: true }, ]; function slugify(p) { return p === '/' ? 'landing' : p.replace(/\//g, '').replace(/[^a-z0-9]/g, '-'); } async function screenshot(browser, pagePath, viewportName, fullPage = true) { const viewport = VIEWPORTS[viewportName] || VIEWPORTS.desktop; const context = await browser.newContext({ viewport }); const page = await context.newPage(); const url = BASE_URL + pagePath; await page.goto(url, { waitUntil: 'networkidle', timeout: 15000 }); // Scroll through the entire page to trigger whileInView animations await page.evaluate(async () => { await new Promise(resolve => { let totalHeight = 0; const distance = 400; const timer = setInterval(() => { window.scrollBy(0, distance); totalHeight += distance; if (totalHeight >= document.body.scrollHeight) { clearInterval(timer); window.scrollTo(0, 0); resolve(undefined); } }, 80); }); }); // Wait for animations to settle after scroll await page.waitForTimeout(1200); const filename = `${slugify(pagePath)}-${viewportName}.png`; const outPath = join(screenshotsDir, filename); await page.screenshot({ path: outPath, fullPage }); console.log(`✓ ${filename}`); await context.close(); return outPath; } async function main() { const args = process.argv.slice(2); const browser = await chromium.launch(); try { if (args[0] === '--all') { for (const { path: p, fullPage } of PAGES) { for (const vp of Object.keys(VIEWPORTS)) { await screenshot(browser, p, vp, fullPage); } } } else { const pagePath = args[0] || '/'; const viewportName = args[1] || 'desktop'; await screenshot(browser, pagePath, viewportName); } } finally { await browser.close(); } } main().catch(err => { console.error(err); process.exit(1); });