import { chromium } from "playwright"; import { mkdirSync } from "node:fs"; const BASE = "http://localhost:3111"; const OUT = "runs/manual/shots"; mkdirSync(OUT, { recursive: true }); const browser = await chromium.launch(); const ctx = await browser.newContext({ viewport: { width: 1440, height: 900 }, deviceScaleFactor: 2 }); const page = await ctx.newPage(); const errors = []; page.on("pageerror", (e) => errors.push(String(e).slice(0, 150))); async function shot(name) { await page.waitForTimeout(500); await page.screenshot({ path: `${OUT}/${name}.png` }); console.log(`✓ ${name}`); } async function safeClick(text, fallback) { try { await page.getByRole("button", { name: text, timeout: 5000 }).click(); } catch { try { await page.getByText(text, { timeout: 3000 }).first().click(); } catch { if (fallback) fallback(); else console.log(` (skip: ${text})`); } } } async function safeType(selector, text) { try { const el = page.locator(selector).first(); await el.click({ timeout: 5000 }); await el.fill(text); } catch { console.log(` (skip type: ${selector})`); } } // ONBOARDING await page.goto(`${BASE}/onboard`); await page.waitForTimeout(1500); // redirect to /onboarding await page.goto(`${BASE}/onboarding`); await page.waitForTimeout(1500); await shot("onboard-01-start"); safeType("input", "Global News Desk"); await shot("onboard-02-named"); safeClick("Continue"); await shot("onboard-03-invite"); safeType("textarea", "sarah@globalnews.io, marcus@globalnews.io"); await shot("onboard-04-emails-filled"); safeClick("Continue"); await shot("onboard-05-connect"); // STREAMS await page.goto(`${BASE}/streams`); await page.waitForTimeout(1000); await shot("streams-01-overview"); await page.keyboard.press("j"); await page.keyboard.press("j"); await page.keyboard.press("j"); await shot("streams-02-item-selected"); await page.keyboard.press("r"); await shot("streams-03-repost-toast"); await page.keyboard.press("q"); await shot("streams-04-quote-popover"); try { const ta = page.locator("textarea").first(); if (await ta.isVisible({ timeout: 3000 })) { await ta.fill("This analysis is essential reading for anyone tracking the Fed's next move."); } } catch {} await shot("streams-05-quote-typed"); await page.keyboard.press("Escape"); // COMPOSE await page.goto(`${BASE}/compose`); await page.waitForTimeout(1000); await shot("compose-01-blank"); try { const ed = page.locator(".ProseMirror").first(); await ed.click({ timeout: 5000 }); await page.keyboard.type("Breaking: Central bank signals first rate cut of the year as inflation cools to 2.1%. Full analysis at globalnews.io/fed-rates #markets #economy"); } catch { console.log(" (compose editor not found)"); } await shot("compose-02-typed"); // CONNECTIONS await page.goto(`${BASE}/connections`); await page.waitForTimeout(800); await shot("connections-01-overview"); // TEAM await page.goto(`${BASE}/team`); await page.waitForTimeout(800); await shot("team-01-overview"); // INBOX await page.goto(`${BASE}/inbox`); await page.waitForTimeout(800); await shot("inbox-01-overview"); // AUDIT await page.goto(`${BASE}/audit`); await page.waitForTimeout(800); await shot("audit-01-overview"); try { await page.locator("tr").filter({ hasText: "credential" }).first().click({ timeout: 5000 }); } catch {} await shot("audit-02-diff-open"); await browser.close(); console.log(`\nShots: ${OUT}/`); console.log(`Errors: ${errors.length}`); for (const e of errors.slice(0, 5)) console.log(` ${e}`);