// End-to-end smoke test against `pnpm preview` (http://localhost:4173). Run: node scripts/smoke.mjs import { chromium } from 'playwright-core'; const URL = process.env.URL ?? 'http://localhost:4173/'; const browser = await chromium.launch(); const page = await browser.newPage({ viewport: { width: 1280, height: 900 }, locale: 'pl-PL' }); const errors = []; page.on('pageerror', (e) => errors.push(`pageerror: ${e.message}`)); page.on('console', (m) => { if (m.type() === 'error') errors.push(`console: ${m.text()}`); }); let step = 0; const ok = (cond, msg) => { step++; if (!cond) { console.error(`FAIL ${step}: ${msg}`); process.exitCode = 1; } else console.log(`ok ${step}: ${msg}`); }; await page.goto(URL, { waitUntil: 'networkidle' }); ok(await page.locator('text=Zaloguj się').first().isVisible(), 'login screen shown'); // resident flow: report -> appears in own place detail as unverified await page.click('text=Zaloguj jako Anna Kowalska'); await page.waitForSelector('#q'); ok((await page.locator('text=Wyniki:').first().innerText()).includes('27'), 'places list loaded (~2.7k)'); await page.fill('#q', 'Pracownia Przykładowa'); await page.click('button[id^=place-]'); await page.waitForSelector('text=Szczegóły dostępności'); await page.click('text=Popraw lub uzupełnij >> nth=0'); await page.selectOption('#rep-attr', 'entrance'); await page.click('label[for=rep-v-no]'); await page.click('text=Zapisz zgłoszenie'); ok(await page.locator('text=Zgłoszenie zapisane').isVisible(), 'report saved and flagged unverified'); // settings persist per account await page.click('nav >> text=Profil'); await page.selectOption('#set-size', '125'); await page.reload({ waitUntil: 'networkidle' }); ok((await page.evaluate(() => document.documentElement.style.fontSize)) === '125%', 'text size persisted across reload'); // official flow: queue shows pending reports, accept one -> place becomes verified await page.click('nav >> text=Profil'); await page.click('text=Wyloguj'); await page.click('text=Zaloguj jako Maria Wiśniewska'); await page.waitForSelector('.kbb-kpis'); ok(await page.locator('text=0 / 82').first().isVisible(), 'dashboard shows operator data gap (0 / 82 stops)'); const pending = await page.locator('button:has-text("Zatwierdź")').count(); ok(pending >= 3, `moderation queue has ${pending} pending reports`); await page.locator('button:has-text("Zatwierdź")').first().click(); ok((await page.locator('button:has-text("Zatwierdź")').count()) === pending - 1, 'accepting removes it from the queue'); // map pins + route render without worker errors await page.click('nav >> text=Trasa'); await page.click('text=Sukiennice → Zamek Królewski na Wawelu'); await page.waitForSelector('text=Opis trasy tekstem'); await page.waitForTimeout(1500); const canvas = await page.locator('canvas.maplibregl-canvas').count(); ok(canvas === 1, 'map canvas present'); ok(await page.locator('text=Bariery i uwagi na trasie').isVisible(), 'route summary + barriers listed'); ok(await page.locator('ol.usa-list li').count() > 3, 'text route steps listed'); await page.click('text=Trasa >> nth=0').catch(() => {}); ok(errors.filter((e) => !/GL Driver|favicon/.test(e)).length === 0, `no console errors ${JSON.stringify(errors.slice(0, 3))}`); await browser.close();