2026-10-03 14:29:28 +02:00
|
|
|
// 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');
|
2026-10-03 18:22:27 +02:00
|
|
|
const total = Number(((await page.locator('text=Wyniki:').first().innerText()).match(/\d+/) ?? ['0'])[0]);
|
|
|
|
|
ok(total > 4000, `places list loaded (${total} places)`);
|
2026-10-03 14:29:28 +02:00
|
|
|
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');
|
2026-10-03 17:08:38 +02:00
|
|
|
await page.waitForSelector('[role=tablist]');
|
2026-10-03 14:29:28 +02:00
|
|
|
const pending = await page.locator('button:has-text("Zatwierdź")').count();
|
2026-10-03 17:08:38 +02:00
|
|
|
ok(pending >= 3, `moderation queue (first tab) has ${pending} pending reports`);
|
2026-10-03 14:29:28 +02:00
|
|
|
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');
|
2026-10-03 17:08:38 +02:00
|
|
|
await page.click('button:has-text("Cofnij"):not(:has-text("decyzję"))');
|
|
|
|
|
ok((await page.locator('button:has-text("Zatwierdź")').count()) === pending, 'undo puts it back');
|
|
|
|
|
await page.locator('button:has-text("Zatwierdź")').first().click();
|
|
|
|
|
await page.click('[role=tab]:has-text("Jakość")');
|
|
|
|
|
await page.waitForSelector('.kbb-kpis');
|
2026-10-03 18:22:27 +02:00
|
|
|
ok(await page.getByText(/^0 \/ \d+$/).first().isVisible(), 'data tab shows operator data gap (0 / N stops)');
|
2026-10-03 14:29:28 +02:00
|
|
|
|
2026-10-03 19:14:41 +02:00
|
|
|
// public transport: toggle in the route form, then either connections or an honest "none" (depends on the time of day)
|
|
|
|
|
await page.click('nav >> text=Trasa');
|
|
|
|
|
await page.fill('#route-from', 'Sukiennice');
|
|
|
|
|
await page.fill('#route-to', 'Tauron Arena Kraków');
|
|
|
|
|
await page.click('button[type=submit]:has-text("Wyznacz")');
|
|
|
|
|
await page.waitForSelector('.kbb-modes');
|
|
|
|
|
await page.click('.kbb-mode:has-text("Komunikacja")');
|
|
|
|
|
await page.locator('.kbb-jhead').first().or(page.getByText('Brak połączenia')).first().waitFor({ timeout: 20000 }).catch(async () => {
|
|
|
|
|
console.error('transit result text:', (await page.locator('.kbb-result').innerText()).slice(0, 300), '| errors:', errors.slice(0, 3));
|
|
|
|
|
});
|
|
|
|
|
const connections = await page.locator('.kbb-jhead').count();
|
|
|
|
|
ok(connections > 0 || (await page.getByText('Brak połączenia').count()) > 0, `transit planner answered (${connections} connections)`);
|
|
|
|
|
await page.click('.kbb-mode:has-text("Pieszo")');
|
|
|
|
|
|
2026-10-03 14:29:28 +02:00
|
|
|
// map pins + route render without worker errors
|
|
|
|
|
await page.click('nav >> text=Trasa');
|
2026-10-03 19:14:41 +02:00
|
|
|
await page.fill('#route-from', 'Sukiennice');
|
|
|
|
|
await page.fill('#route-to', 'Zamek Królewski na Wawelu');
|
|
|
|
|
await page.click('button[type=submit]:has-text("Wyznacz")');
|
2026-10-03 14:29:28 +02:00
|
|
|
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');
|
2026-10-03 19:14:41 +02:00
|
|
|
const steps = page.locator('details:has(summary:has-text("Opis trasy tekstem"))');
|
|
|
|
|
ok(!(await steps.evaluate((d) => d.open)), 'text route is collapsed by default');
|
|
|
|
|
await steps.locator('summary').click();
|
|
|
|
|
ok((await steps.locator('li').count()) > 3 && (await steps.locator('li').first().isVisible()), 'text route steps listed once opened');
|
2026-10-03 14:29:28 +02:00
|
|
|
|
|
|
|
|
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();
|