SPB Git forge
3commits 1branches 0releases
417.0 KBsize
maindefault branch
10 days agolast push
TypeScript 66.5% Python 30.9% JavaScript 1.4% CSS 0.7%
4.0 KB · 71 lines javascript
Raw Blame History
1/**2 * QA for the satellite / launches pages: screenshots at 390 and 1440, console errors, horizontal overflow,3 * DOM-order = visual-order check on the satellite detail page.4 *   node qa/satellite-qa.mjs [baseUrl]5 */6import { mkdirSync } from 'node:fs';7import path from 'node:path';8import { chromium } from '/Users/simon-pierreboucher/Desktop/uqo-eval/node_modules/playwright/index.mjs';910const BASE = process.argv[2] ?? 'http://localhost:8319';11const OUT = path.resolve(import.meta.dirname, 'screens/satellite');12mkdirSync(OUT, { recursive: true });1314const PAGES = [15  ['satellite-iss', '/satellite/iss-zarya-25544'],16  ['satellite-starlink', '/satellite/starlink-38436-100641'],17  ['satellite-sputnik', '/satellite/sputnik-1-2'],18  ['satellites', '/satellites'],19  ['satellites-starlink-active', '/satellites?constellation=starlink&status=ACTIVE'],20  ['satellites-empty', '/satellites?status=PLANNED&object_type=DEBRIS'],21  ['launches', '/launches'],22  ['launches-2026', '/launches?year=2026'],23  ['launch-1998-067', '/launch/1998-067'],24  ['launch-sites', '/launch-sites'],25  ['launch-site-baikonur', '/launch-sites/baikonur-cosmodrome-tyuratam'],26];27const VIEWPORTS = [28  ['390', { width: 390, height: 844 }],29  ['1440', { width: 1440, height: 900 }],30];3132const browser = await chromium.launch();33let failures = 0;34for (const [vpName, viewport] of VIEWPORTS) {35  const ctx = await browser.newContext({ viewport, deviceScaleFactor: 1, colorScheme: 'dark' });36  for (const [name, url] of PAGES) {37    const page = await ctx.newPage();38    const errors = [];39    page.on('console', (m) => m.type() === 'error' && errors.push(m.text()));40    page.on('pageerror', (e) => errors.push(`pageerror: ${e.message}`));41    const res = await page.goto(BASE + url, { waitUntil: 'networkidle', timeout: 90_000 });42    await page.waitForTimeout(1200);43    const metrics = await page.evaluate(() => {44      const de = document.documentElement;45      const overflow = de.scrollWidth - de.clientWidth;46      const wide = [...document.querySelectorAll('body *')].filter((el) => el.getBoundingClientRect().right > de.clientWidth + 1).slice(0, 5).map((el) => `${el.tagName.toLowerCase()}.${String(el.className).split(' ').slice(0, 2).join('.')}`);47      // DOM order == visual order for the detail-page sections48      const ids = ['live', 'orbit', 'mission', 'ownership', 'launch', 'history', 'registration', 'sources', 'events', 'related', 'identifiers'];49      const tops = ids.map((id) => document.getElementById(id)?.getBoundingClientRect().top).filter((t) => t !== undefined);50      const ordered = tops.every((t, i) => i === 0 || t >= tops[i - 1]);51      const h1 = document.querySelector('h1');52      const h1Top = h1 ? h1.getBoundingClientRect().top + window.scrollY : null;53      const firstSectionTop = tops[0] !== undefined ? tops[0] + window.scrollY : null;54      const usesOrder = [...document.querySelectorAll('main *')].some((el) => getComputedStyle(el).order !== '0');55      return { overflow, wide, ordered, heroFirst: h1Top === null || firstSectionTop === null || h1Top < firstSectionTop, usesOrder, scrollY: window.scrollY, title: document.title };56    });57    const file = path.join(OUT, `${name}-${vpName}.png`);58    await page.screenshot({ path: file, fullPage: true });59    const bad = (res?.status() ?? 0) >= 400 || errors.length || metrics.overflow > 0 || !metrics.ordered || !metrics.heroFirst || metrics.usesOrder || metrics.scrollY !== 0;60    if (bad) failures++;61    console.log(`${bad ? 'FAIL' : ' ok '} ${vpName.padEnd(4)} ${url.padEnd(52)} ${res?.status()} overflow=${metrics.overflow} order=${metrics.ordered ? 'ok' : 'BAD'} hero=${metrics.heroFirst ? 'first' : 'NOT-FIRST'} cssOrder=${metrics.usesOrder ? 'USED' : 'none'} errors=${errors.length}`);62    if (errors.length) console.log('     ', errors.slice(0, 3).join('\n      '));63    if (metrics.wide.length) console.log('      wide:', metrics.wide.join(', '));64    await page.close();65  }66  await ctx.close();67}68await browser.close();69console.log(failures ? `\n${failures} page(s) failed` : '\nall pages passed');70process.exit(failures ? 1 : 0);71