// usage: node shoot.mjs OUT_DIR QUERY_ID import { chromium } from "playwright"; const [outDir, queryId] = process.argv.slice(2); const BASE = "http://127.0.0.1:18080"; const PAGES = [ ["overview", "/overview?period=24h"], ["activity", "/activity"], ["query-detail", `/activity/queries/${queryId}`], ["clients", "/clients"], ["diagnostics", "/diagnostics"], ]; async function settle(page) { await page.waitForLoadState("networkidle").catch(() => {}); await page.waitForFunction( () => !document.querySelector('[aria-busy="true"], [role="progressbar"]'), null, { timeout: 15000 }, ).catch(() => console.log(" still busy after 15s")); await page.waitForTimeout(2500); } // The phone form is 390px wide, where the admin UI switches to its drawer layout, and touch-only, so // no hover state is drawn. const FORMS = [ ["", { viewport: { width: 1440, height: 900 }, deviceScaleFactor: 2 }], ["-phone", { viewport: { width: 390, height: 844 }, deviceScaleFactor: 3, isMobile: true, hasTouch: true }], ]; const browser = await chromium.launch(); for (const [suffix, form] of FORMS) for (const scheme of ["light", "dark"]) { const context = await browser.newContext({ ...form, colorScheme: scheme, timezoneId: "Europe/Paris", locale: "en-GB", }); const page = await context.newPage(); page.on("console", (m) => m.type() === "error" && console.log(` console error: ${m.text()}`)); await page.goto(`${BASE}/login`); await page.locator("#password").fill("nxdns-demo"); await page.locator('button[type="submit"]').click(); await page.waitForURL((u) => !u.pathname.startsWith("/login")); for (const [name, path] of PAGES) { await page.goto(BASE + path); if (!form.hasTouch) await page.mouse.move(1430, 890); await settle(page); if (name === "activity") await page.waitForTimeout(8000); // A phone stacks the health cards and filters above the episodes, so the first screen shows none. if (name === "diagnostics" && form.hasTouch) { await page.getByRole("heading", { name: "Active" }).evaluate((h) => h.scrollIntoView({ block: "start" })); await page.waitForTimeout(500); } const file = `${outDir}/${name}-${scheme}${suffix}.png`; await page.screenshot({ path: file }); console.log(file); } await context.close(); } await browser.close();