test(e2e): keep route mocks authoritative against the v1.18.6 service worker

The production build registers a service worker whose v1.18.6 data branch
serves allowlisted GET /api/* reads network-first. A worker-originated
fetch is not subject to Playwright's page.route interception, so the
worker re-fetched the real (empty) backend and bypassed every per-spec
route.fulfill — a just-created row never surfaced and read-after-write
assertions failed. Block the worker in the test context so the route
mocks are authoritative; the shipped offline cache is untouched for real
users. Separately, the dashboard now batches chart series into
/api/measurements/series-batch, a sub-path the **/api/measurements* glob
does not match, so it fell through to the empty backend and each chart
painted an empty preloaded slice; stub the endpoint with the same
populated set.
This commit is contained in:
Marc-André Bombeck committed 2026-06-18 17:48:57 +02:00
1 parent 9633d66eea
commit 3cf9a262ca
2 files changed
+54

No files matched your search

+43
View File
@@ -98,10 +98,53 @@ test.describe("authenticated dashboard render", () => {
}),
);
// v1.18.6 — the dashboard now batches every visible chart's daily
// series into ONE `/api/measurements/series-batch` round-trip and
// hands each chart its slice via `preloadedSeries`. The route is a
// sub-path of `/api/measurements`, which the `**/api/measurements*`
// glob below does NOT match (Playwright's `*` does not cross `/`), so
// without an explicit stub the request fell through to the real
// backend. The seed user has no measurements, so the route returned
// every requested type as an EMPTY array — and an empty-but-present
// slice satisfies the chart's preloaded-coverage check, so each chart
// rendered the batched empty slice instead of self-fetching. Mock the
// endpoint with the same populated set so the charts paint.
await page.route(/\/api\/measurements\/series-batch(\?|$)/, (route) => {
const url = new URL(route.request().url());
const types = (url.searchParams.get("types") ?? "WEIGHT").split(",");
const series: Record<
string,
Array<{ type: string; value: number; measuredAt: string; count: number }>
> = {};
for (const type of types) {
const baseValue =
type === "BLOOD_PRESSURE_SYS"
? 124
: type === "BLOOD_PRESSURE_DIA"
? 80
: type === "PULSE"
? 70
: 78.5;
series[type] = Array.from({ length: 10 }, (_, i) => ({
type,
value: baseValue + (i % 3) - 1,
measuredAt: new Date(Date.now() - i * 86_400_000).toISOString(),
count: 1,
}));
}
return route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({ data: { series }, error: null }),
});
});
// The chart's per-type fetch reads `json.data.measurements` and
// expects a `meta.total`. Return a small dataset so the chart has
// points to plot — otherwise Recharts mounts a placeholder instead
// of the recharts-wrapper SVG and the assertion below times out.
// (Self-fetch fallback for any chart not covered by the batch above,
// e.g. a range-tab change that drops batched coverage.)
await page.route("**/api/measurements*", (route) => {
const url = new URL(route.request().url());
const type = url.searchParams.get("type") ?? "WEIGHT";
+11
View File
@@ -33,6 +33,17 @@ export default defineConfig({
trace: "retain-on-failure",
screenshot: "only-on-failure",
video: "retain-on-failure",
// v1.18.6 — block the service worker in the test context. The
// production build registers `/sw.js`, whose v1.18.6 data branch
// serves allowlisted `/api/*` GET reads network-first. A worker-
// originated `fetch` is NOT subject to Playwright's `page.route`
// interception, so the SW would re-fetch the real (empty) backend
// and serve that — bypassing every per-spec `route.fulfill` mock and
// breaking read-after-write assertions (a just-created row never
// surfaced). Blocking the worker keeps the route mocks authoritative
// without weakening the shipped SW behaviour. Real users keep the
// offline data cache; only the test harness opts out.
serviceWorkers: "block",
// HealthLog ships dark mode as the default (Dracula theme) — `globals.css`
// sets `color-scheme: dark` on the root and the `<ThemeProvider>` defaults
// to "system". Playwright's stock context is `colorScheme: "light"`, which