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%
4.4 KB · 74 lines javascript
Raw Blame History
1/**2 * Admin panel QA: wrong token → error; right token (deploy/.admin-token) → cookie → overview/coverage/runs/issues/raw3 * render; "Clear cache" server action round-trips; screenshots at 390 + 1440 → qa/screens/explore/admin_*.png.4 * Needs CA_ADMIN_TOKEN on the web process (and ADMIN_API_URL → an API instance with the same token).5 * Run: node qa/admin-explore.mjs [BASE_URL]6 */7import { chromium } from '/Users/simon-pierreboucher/Desktop/uqo-eval/node_modules/playwright/index.mjs';8import { readFileSync } from 'node:fs';910const BASE = process.argv[2] ?? 'http://localhost:8290';11const OUT = new URL('./screens/explore/', import.meta.url).pathname;12const TOKEN = readFileSync(new URL('../../../deploy/.admin-token', import.meta.url), 'utf8').trim();1314const browser = await chromium.launch();15for (const width of [390, 1440]) {16  const mobile = width < 768;17  const ctx = await browser.newContext({ viewport: { width, height: mobile ? 844 : 900 }, isMobile: mobile, hasTouch: mobile });18  const page = await ctx.newPage();19  const errors = [];20  page.on('pageerror', (e) => errors.push(String(e)));21  page.on('console', (m) => m.type() === 'error' && errors.push(m.text()));2223  // unauthenticated → redirect to login24  let res = await page.goto(BASE + '/admin/coverage', { waitUntil: 'networkidle' });25  console.log(width, 'gate:', page.url().endsWith('/admin/login') ? 'redirected to login OK' : `FAIL ${page.url()}`, res?.status());2627  // wrong token28  await page.fill('input[name=token]', 'nope');29  await page.click('button[type=submit]');30  await page.waitForURL(/error=1/, { timeout: 60_000 }).catch(() => {});31  await page.waitForLoadState('networkidle');32  const err = await page.getByText('Invalid token.').count();33  const stillNoCookie = !(await ctx.cookies()).some((k) => k.name === 'ca_admin');34  console.log(width, 'wrong token:', err && stillNoCookie ? 'error shown, no cookie OK' : `FAIL url=${page.url()} err=${err} noCookie=${stillNoCookie}`);3536  // right token37  await page.fill('input[name=token]', TOKEN);38  await page.click('button[type=submit]');39  await page.waitForURL(/\/admin(\?.*)?$/, { timeout: 60_000 });40  await page.waitForLoadState('networkidle');41  const cookies = await ctx.cookies();42  const c = cookies.find((k) => k.name === 'ca_admin');43  console.log(width, 'cookie:', c ? `httpOnly=${c.httpOnly} sameSite=${c.sameSite} path=${c.path} value≠token=${c.value !== TOKEN}` : 'MISSING');44  for (const p of ['/admin', '/admin/coverage', '/admin/runs', '/admin/issues', '/admin/raw']) {45    res = await page.goto(BASE + p, { waitUntil: 'networkidle', timeout: 120_000 });46    const h = await page.locator('h2').first().textContent().catch(() => '');47    const overflow = await page.evaluate(() => document.documentElement.scrollWidth - document.documentElement.clientWidth);48    await page.screenshot({ path: `${OUT}admin${p.replace(/\//g, '_')}-${width}.png`, fullPage: true });49    console.log(width, p.padEnd(16), res?.status(), `h2="${h?.trim()}"`, overflow > 0 ? `OVERFLOW +${overflow}` : 'ok');50  }51  // raw files for the latest run id from the runs page52  await page.goto(BASE + '/admin/runs', { waitUntil: 'networkidle' });53  const runId = await page.locator('a[href*="run_id="]').first().textContent();54  await page.goto(BASE + `/admin/raw?run_id=${runId?.trim()}`, { waitUntil: 'networkidle' });55  const files = await page.locator('table').count();56  console.log(width, 'raw run', runId?.trim(), files ? `${files} tables OK` : 'no table');57  // clear cache action58  await page.goto(BASE + '/admin', { waitUntil: 'networkidle' });59  await page.click('button:has-text("Clear cache")');60  await page.waitForURL(/[?&](ok|err)=/, { timeout: 60_000 });61  console.log(width, 'clear cache:', decodeURIComponent(page.url().split('?')[1] ?? ''));62  // refresh now (expected 409 → err=: no scheduler pid on this dev machine)63  await page.goto(BASE + '/admin', { waitUntil: 'networkidle' });64  await page.click('button:has-text("Refresh now")');65  await page.waitForURL(/[?&](ok|err)=/, { timeout: 60_000 });66  console.log(width, 'refresh now:', decodeURIComponent(page.url().split('?')[1] ?? '').slice(0, 120));67  // logout68  await page.click('button:has-text("Sign out")');69  await page.waitForURL(/\/admin\/login/, { timeout: 30_000 });70  console.log(width, 'logout OK', errors.length ? `console errors: ${errors.slice(0, 3).join(' | ')}` : 'no console errors');71  await ctx.close();72}73await browser.close();74