SPB Git forge
38commits 1branches 0releases
338.7 MBsize
maindefault branch
3 h agolast push
HTML 53.9% TypeScript 44.5% JavaScript 0.6% SQL 0.5%
4.8 KB · 97 lines javascript
Raw Blame History
1// Entity-page QA screenshots → apps/web/qa/entities/<name>-<width>.png2//   node apps/web/qa/shoot-entities.mjs                       (all pages, 360/390/768/1280)3//   PAGES="facility,operator" WIDTHS=360,1280 THEME=dark node apps/web/qa/shoot-entities.mjs4//   EXTRA='name=/path,name2=/path2' adds ad-hoc pages. Reports horizontal overflow and HTTP status.5import { createRequire } from "node:module";6import { mkdirSync } from "node:fs";7import { dirname, resolve } from "node:path";8import { fileURLToPath } from "node:url";910const require = createRequire("/Users/simon-pierreboucher/Desktop/uqo-eval/package.json");11const { chromium } = require("playwright");12const out = resolve(dirname(fileURLToPath(import.meta.url)), "entities");13mkdirSync(out, { recursive: true });14const BASE = process.env.BASE ?? "http://localhost:8310";15const API = process.env.API ?? "http://127.0.0.1:8311/api/v1";1617async function firstSlug(path, pick = (d) => d[0]?.slug) {18  try {19    const r = await fetch(`${API}${path}`);20    const j = await r.json();21    return pick(Array.isArray(j.data) ? j.data : j.data?.items ?? []) ?? null;22  } catch {23    return null;24  }25}2627const fac = process.env.FACILITY ?? (await firstSlug("/datacenters?per_page=1&sort=completeness&order=desc"));28const op = process.env.OPERATOR ?? (await firstSlug("/operators?per_page=1&sort=facilities&order=desc"));29const country = process.env.COUNTRY ?? "united-states";30const metro = process.env.METRO ?? (await firstSlug("/metros", (d) => [...d].sort((a, b) => b.facilityCount - a.facilityCount)[0]?.slug));31const project = process.env.PROJECT ?? (await firstSlug("/projects?per_page=1"));32const ranking = process.env.RANKING ?? (await firstSlug("/rankings", (d) => (d.find((r) => r.total > 0) ?? d[0])?.key));33const cloud = process.env.CLOUD_REGION ?? (await firstSlug("/cloud-regions?per_page=1"));34const ixp = process.env.IXP ?? (await firstSlug("/ixps?per_page=1"));35const source = process.env.SOURCE ?? (await firstSlug("/sources", (d) => d[0]?.id));3637const PAGES = [38  ["facility", fac && `/datacenters/${fac}`],39  ["operators", "/operators"],40  ["operator", op && `/operators/${op}`],41  ["countries", "/countries"],42  ["country", `/countries/${country}`],43  ["markets", "/datacenters/markets"],44  ["market", metro && `/datacenters/markets/${metro}`],45  ["projects", "/projects"],46  ["project", project && `/projects/${project}`],47  ["rankings", "/rankings"],48  ["ranking", ranking && `/rankings/${ranking}`],49  ["cloud-regions", "/cloud-regions"],50  ["cloud-region", cloud && `/cloud-regions/${cloud}`],51  ["ixps", "/ixps"],52  ["ixp", ixp && `/ixps/${ixp}`],53  ["sources", "/sources"],54  ["source", source && `/sources/${source}`],55  ["methodology", "/methodology"],56  ["bot", "/bot"],57  ["notfound", "/datacenters/this-slug-does-not-exist"],58  ...(process.env.EXTRA ? process.env.EXTRA.split(",").map((s) => s.split("=")) : []),59].filter(([, p]) => p);6061const WIDTHS = (process.env.WIDTHS ?? "360,390,768,1280").split(",").map(Number);62const THEME = process.env.THEME ?? "light";63const ONLY = process.env.PAGES ? new Set(process.env.PAGES.split(",")) : null;64const suffix = THEME === "dark" ? "-dark" : "";65const browser = await chromium.launch();66const report = [];67for (const w of WIDTHS) {68  const ctx = await browser.newContext({ viewport: { width: w, height: w < 700 ? 800 : 900 }, deviceScaleFactor: 1, isMobile: w < 700, hasTouch: w < 700 });69  await ctx.addInitScript((theme) => {70    try {71      window.localStorage.setItem("theme", theme);72    } catch {}73  }, THEME);74  const page = await ctx.newPage();75  for (const [name, path] of PAGES) {76    if (ONLY && !ONLY.has(name)) continue;77    let status = 0;78    const res = await page.goto(`${BASE}${path}`, { waitUntil: "load", timeout: 60000 }).catch((e) => console.log("goto failed", name, w, String(e).slice(0, 80)));79    if (res) status = res.status();80    await page.waitForTimeout(1500);81    const overflow = await page.evaluate(() => {82      const doc = document.documentElement;83      const wide = [];84      for (const el of document.querySelectorAll("body *")) {85        const r = el.getBoundingClientRect();86        if (r.right > doc.clientWidth + 1 && r.width > 0 && getComputedStyle(el).position !== "fixed") wide.push(`${el.tagName.toLowerCase()}.${(el.className && typeof el.className === "string" ? el.className : "").split(" ").slice(0, 2).join(".")}:${Math.round(r.right - doc.clientWidth)}`);87      }88      return { scrollWidth: doc.scrollWidth, clientWidth: doc.clientWidth, wide: wide.slice(0, 5) };89    });90    report.push({ w, name, status, overflow: overflow.scrollWidth > overflow.clientWidth ? overflow : "ok" });91    await page.screenshot({ path: resolve(out, `${name}-${w}${suffix}.png`), fullPage: true });92  }93  await ctx.close();94}95await browser.close();96for (const r of report) console.log(r.w, r.name, r.status, typeof r.overflow === "string" ? r.overflow : JSON.stringify(r.overflow));97