SPB Git forge

spb/countryatlas

Public
20commits 1branches 0releases
268.3 MBsize
maindefault branch
12 days agolast push
TypeScript 57% Python 38.6% JavaScript 3.6% CSS 0.6%
3.2 KB · 67 lines javascript
Raw Blame History
1/**2 * Viewport-sized segments for visual review (the full-page PNGs of polish-sweep are too tall to read):3 * top + evenly spaced offsets per route × width × theme → qa/screens/polish/seg/<route>-<width>-<theme>-<i>.png4 *   node qa/polish-segments.mjs [BASE_URL] [routes…]   (defaults: key routes, widths 320 + 390 + 1440, light + dark at 390)5 */6import { chromium } from '/Users/simon-pierreboucher/Desktop/uqo-eval/node_modules/playwright/index.mjs';7import { mkdirSync } from 'node:fs';89const args = process.argv.slice(2);10const BASE = args.find((a) => a.startsWith('http')) ?? 'http://localhost:8290';11const custom = args.filter((a) => a.startsWith('/'));12const OUT = new URL('./screens/polish/seg/', import.meta.url).pathname;13mkdirSync(OUT, { recursive: true });14const ROUTES = custom.length ? custom : ['/', '/countries', '/countries/canada', '/countries/canada/economy', '/compare/canada/united-states/france', '/compare/canada/united-states/france?tab=economy', '/rankings', '/rankings/gdp-per-capita', '/indicators/life-expectancy', '/regions/oecd', '/explore', '/changes', '/sources', '/methodology', '/api', '/data', '/indicators', '/regions', '/compare'];15const ALL_COMBOS = [16  [320, 'light'],17  [390, 'light'],18  [390, 'dark'],19  [1440, 'light'],20  [1440, 'dark'],21];22// --desktop → 1440 only; --phone → 320/390 only23const COMBOS = args.includes('--desktop') ? ALL_COMBOS.filter(([w]) => w >= 1024) : args.includes('--phone') ? ALL_COMBOS.filter(([w]) => w < 1024) : ALL_COMBOS;24const slug = (p) => (p === '/' ? 'home' : p.slice(1).replace(/[/?=&]+/g, '_'));25const browser = await chromium.launch();26for (const [width, theme] of COMBOS) {27  const mobile = width < 768;28  const height = mobile ? 844 : 900;29  const ctx = await browser.newContext({ viewport: { width, height }, deviceScaleFactor: 1, isMobile: mobile, hasTouch: mobile, colorScheme: theme });30  const page = await ctx.newPage();31  for (const path of ROUTES) {32    let docH = 0;33    for (let attempt = 0; attempt < 3; attempt++) {34      try {35        await page.goto(BASE + path, { waitUntil: 'networkidle', timeout: 60_000 });36        await page.evaluate(() => document.fonts.ready);37        docH = await page.evaluate(async () => {38      const h = document.documentElement.scrollHeight;39      for (let y = 0; y < h; y += 700) {40        window.scrollTo(0, y);41        await new Promise((r) => setTimeout(r, 50));42      }43      window.scrollTo(0, 0);44      return document.documentElement.scrollHeight;45    });46        break;47      } catch (e) {48        if (attempt === 2) console.log(`FAILED ${path}: ${String(e).slice(0, 120)}`);49        await page.waitForTimeout(1500);50      }51    }52    if (!docH) continue;53    await page.waitForLoadState('networkidle').catch(() => {});54    await page.waitForTimeout(400);55    const n = Math.min(mobile ? 6 : 4, Math.max(1, Math.ceil(docH / height)));56    for (let i = 0; i < n; i++) {57      const y = n === 1 ? 0 : Math.round((i * (docH - height)) / (n - 1));58      await page.evaluate((yy) => window.scrollTo(0, yy), y);59      await page.waitForTimeout(250);60      await page.screenshot({ path: `${OUT}${slug(path)}-${width}-${theme}-${i}.png`, fullPage: false });61    }62    console.log(`${width} ${theme} ${path} h=${docH} segments=${n}`);63  }64  await ctx.close();65}66await browser.close();67