spb/datacenterindex
Public
HTML 53.9%
TypeScript 44.5%
JavaScript 0.6%
SQL 0.5%
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