spb/countryatlas
Public
TypeScript 57%
Python 38.6%
JavaScript 3.6%
CSS 0.6%
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