/** * Probes each demo route with Playwright. Captures console errors and * takes a screenshot. For Dialog/Collapsible/Popover/Toast also clicks * the trigger and captures the open state. Smoke test for the migrated * soma components. * * Usage: * 1. Start the dev server (`npm run dev`). * 2. Adjust `BASE` to the actual port if not 5174. * 3. `npx tsx scripts/demo-probe.ts`. */ import { chromium } from 'playwright'; import { mkdir } from 'node:fs/promises'; const BASE = 'http://localhost:5174'; const OUT = 'g:/tmp/demos'; const routes = [ '/', '/toggle', '/switch', '/collapsible', '/avatar', '/dialog', '/drawer', '/popover', '/toast' ]; async function main() { await mkdir(OUT, { recursive: true }); const browser = await chromium.launch(); const ctx = await browser.newContext({ viewport: { width: 900, height: 1100 } }); const page = await ctx.newPage(); let totalErrors = 0; for (const route of routes) { const errors: string[] = []; page.removeAllListeners('console'); page.removeAllListeners('pageerror'); page.on('console', (msg) => { if (msg.type() === 'error') errors.push(msg.text()); }); page.on('pageerror', (err) => errors.push(`pageerror: ${err.message}`)); const url = BASE + route; await page.goto(url, { waitUntil: 'networkidle' }); await page.waitForTimeout(300); const slug = route === '/' ? 'index' : route.slice(1); await page.screenshot({ path: `${OUT}/${slug}.png` }); // Interactive checks for components with open/close behavior. if (route === '/dialog') { await page.locator('[data-dialog-trigger]').first().click(); await page.waitForTimeout(400); await page.screenshot({ path: `${OUT}/${slug}-open.png` }); } else if (route === '/collapsible') { await page.locator('[data-collapsible-trigger]').first().click(); await page.waitForTimeout(200); await page.screenshot({ path: `${OUT}/${slug}-open.png` }); } else if (route === '/popover') { await page.locator('[data-popover-trigger]').first().click(); await page.waitForTimeout(400); await page.screenshot({ path: `${OUT}/${slug}-open.png` }); } else if (route === '/toast') { await page.locator('button:has-text("Fulfill")').first().click(); await page.waitForTimeout(400); await page.screenshot({ path: `${OUT}/${slug}-open.png` }); } const status = errors.length === 0 ? '✓' : `✗ ${errors.length} error(s)`; console.log(`${status} ${route}`); if (errors.length > 0) { for (const e of errors) console.log(` ${e.slice(0, 200)}`); // Avatar's network failure is a known sandbox limitation, not a real bug. if (!(route === '/avatar' && errors.every((e) => e.includes('ERR_NAME_NOT_RESOLVED')))) { totalErrors += errors.length; } } } await browser.close(); process.exit(totalErrors > 0 ? 1 : 0); } main().catch((err) => { console.error(err); process.exit(2); });