Files

83 lines
3.3 KiB
JavaScript

// Drives the running CRM (npm run dev) and screenshots each manifest screen in dark mode.
// Prereq: in the CRM repo root run `npm run dev` (serves http://localhost:5173).
import { chromium } from "playwright";
import { readFile, mkdir } from "node:fs/promises";
import { fileURLToPath } from "node:url";
import { dirname, join } from "node:path";
const __dirname = dirname(fileURLToPath(import.meta.url));
const ROOT = join(__dirname, "..");
// tab label + identifier per role (password is always "password")
const ROLE_LOGIN = {
owner: { tab: "Owner", id: "justin" },
agentCody: { tab: "Employee", id: "LUP-1040" },
subCarlos: { tab: "Sub-Con", id: "carlos" },
};
async function login(page, baseUrl, role) {
const creds = ROLE_LOGIN[role];
if (!creds) throw new Error(`unknown role ${role}`);
await page.goto(`${baseUrl}/login`, { waitUntil: "networkidle" });
// The role tab appears before the demo chip of the same name in the DOM, so .first() targets the tab.
await page.getByRole("button", { name: creds.tab, exact: true }).first().click();
await page.fill("#identifier", creds.id);
await page.fill("#password", "password");
await page.getByRole("button", { name: "Sign In" }).click();
// Auth lives only in React state (no localStorage), so we must leave the
// /login route via client-side routing. Wait until we have left it.
await page.waitForFunction(() => !location.pathname.startsWith("/login"));
await page.waitForLoadState("networkidle");
}
// Auth state is in-memory only, so a full page reload (page.goto) would drop the
// session and bounce us to /login. Navigate inside the SPA via the History API
// and a popstate event, which React Router (BrowserRouter) listens to.
async function spaNavigate(page, route) {
await page.evaluate((r) => {
window.history.pushState({}, "", r);
window.dispatchEvent(new PopStateEvent("popstate"));
}, route);
await page.waitForFunction((r) => location.pathname === r, route);
}
async function main() {
const manifest = JSON.parse(await readFile(join(ROOT, "src/captures.manifest.json"), "utf8"));
const outDir = join(ROOT, "public/captures");
await mkdir(outDir, { recursive: true });
const browser = await chromium.launch();
const context = await browser.newContext({
viewport: { width: manifest.viewport.width, height: manifest.viewport.height },
deviceScaleFactor: manifest.viewport.deviceScaleFactor,
});
// Force dark theme before any app code runs.
await context.addInitScript((theme) => {
try { localStorage.setItem("theme", theme); } catch (e) {}
}, manifest.theme);
const page = await context.newPage();
// Group by role so we log in once per role, in manifest order.
let currentRole = null;
for (const cap of manifest.captures) {
if (cap.role !== currentRole) {
await login(page, manifest.baseUrl, cap.role);
currentRole = cap.role;
}
await spaNavigate(page, cap.route);
await page.waitForTimeout(cap.waitMs ?? 1500);
const path = join(outDir, `${cap.id}.png`);
await page.screenshot({ path }); // viewport-only → exact 16:9
console.log(`captured ${cap.id} -> ${path}`);
}
await browser.close();
console.log(`\nDone. ${manifest.captures.length} captures written to public/captures/`);
}
main().catch((err) => {
console.error(err);
process.exit(1);
});