GdzieWjade/scripts/offline-e2e.mjs

51 lines
3.3 KiB
JavaScript
Raw Permalink Normal View History

// Offline end-to-end check against `pnpm preview` (production build, service worker on). Run: node scripts/offline-e2e.mjs
// 1) first visit saves the app + the map pack, 2) with the network cut the app reloads, shows the offline notice and still routes.
import { chromium } from 'playwright-core';
const URL = process.env.URL ?? 'http://localhost:4173/';
const browser = await chromium.launch();
const ctx = await browser.newContext({ viewport: { width: 390, height: 844 }, locale: 'pl-PL', isMobile: true, hasTouch: true });
const page = await ctx.newPage();
let step = 0;
const ok = (cond, msg) => { step++; if (!cond) { console.error(`FAIL ${step}: ${msg}`); process.exitCode = 1; } else console.log(`ok ${step}: ${msg}`); };
const caches = () => page.evaluate(async () => { const o = {}; for (const n of await window.caches.keys()) o[n] = (await (await window.caches.open(n)).keys()).length; return o; });
await page.goto(URL);
await page.click('text=Zaloguj jako Anna Kowalska');
await page.waitForSelector('#q', { state: 'attached', timeout: 60_000 });
ok(await page.evaluate(() => !!navigator.serviceWorker.controller), 'service worker controls the page');
let tiles = 0;
for (let i = 0; i < 150 && tiles < 1000; i++) { tiles = (await caches())['osm-tiles'] ?? 0; if (tiles < 1000) await page.waitForTimeout(1000); }
ok(tiles >= 1000, `offline map pack saved (${tiles} tiles)`);
let rec = null;
for (let i = 0; i < 60 && !rec; i++) { rec = JSON.parse((await page.evaluate(() => localStorage.getItem('kbb.offline'))) ?? 'null'); if (!rec) await page.waitForTimeout(500); }
ok(rec && rec.failed === 0 && rec.tiles >= 1000, 'pack record stored');
await ctx.setOffline(true);
await page.waitForSelector('.kbb-banner--warning', { timeout: 5000 });
ok(/Trasy na żywo/.test(await page.locator('.kbb-banner--warning').innerText()), 'offline notice says live routes are unavailable');
await page.click('.kbb-banner--warning .kbb-banner__close');
ok((await page.locator('.kbb-banner--warning').count()) === 0, 'offline notice can be dismissed');
await page.reload({ waitUntil: 'domcontentloaded' });
await page.waitForSelector('#q', { state: 'attached', timeout: 30_000 });
ok(true, 'app reloads and opens with no network');
ok((await page.locator('.kbb-banner--warning').count()) === 1, 'offline notice shown again after the reload');
await page.click('nav >> text=Trasa');
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")');
await page.waitForSelector('.kbb-modes', { timeout: 10_000 });
ok(true, 'walking route is computed offline');
await page.click('.kbb-mode:has-text("Komunikacja")');
// scoped to the planner: the offline banner also says "Brak połączenia", and the loader shares the #tr-title heading
const planner = page.locator('section[aria-labelledby=tr-title]');
await planner.locator('.kbb-jhead, .usa-alert').first().waitFor({ timeout: 20_000 });
const note = await page.locator('#tr-title + p').innerText();
ok(/Według rozkładu/.test(note), `transit planner answers from the saved timetable and says delays are unavailable ("${note.slice(0, 60)}")`);
await ctx.setOffline(false);
await page.waitForSelector('.kbb-banner--success', { timeout: 5000 });
ok(true, 'back-online notice appears');
await browser.close();