From ab1eea7f232fda8a8a77a754a5055961113dcb13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Sat, 6 Jun 2026 23:40:10 +0200 Subject: [PATCH] test(e2e): align export and integrations specs with removed surfaces Drop the obsolete doctor-report dialog spec (the interactive export-page dialog was removed; the report now lives under the health-record export), remove the doctor-report testid from the export smoke, and update the integrations layout spec to three cards after the moodLog removal. --- e2e/doctor-report.spec.ts | 98 ------------------------ e2e/settings-export.spec.ts | 8 +- e2e/settings-integrations-mobile.spec.ts | 23 +++--- 3 files changed, 15 insertions(+), 114 deletions(-) delete mode 100644 e2e/doctor-report.spec.ts diff --git a/e2e/doctor-report.spec.ts b/e2e/doctor-report.spec.ts deleted file mode 100644 index 1a2bc3378..000000000 --- a/e2e/doctor-report.spec.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { expect, test } from "@playwright/test"; - -import { STORAGE_STATE_PATH } from "./setup/global-setup"; - -/** - * Doctor-report PDF flow. The real flow: - * 1. User clicks "Configure & generate" on `/settings/export` - * (relocated from `/settings/advanced` in v1.4.16 phase B7) - * 2. The dialog opens; user picks dates and submits. - * 3. Frontend POSTs `/api/doctor-report` to fetch aggregated data - * 4. Frontend dynamic-imports `src/lib/doctor-report-pdf.ts` and - * generates the PDF client-side via jsPDF; calls `doc.save(...)` - * which triggers a download. - * - * This spec mocks step 3 (so we don't need 90 days of seeded data) and - * captures step 4's download via Playwright's download API. We then - * confirm the file is non-empty and starts with `%PDF` magic bytes. - */ -test.describe("doctor report PDF generation", () => { - test.use({ storageState: STORAGE_STATE_PATH }); - - test("button click triggers a non-empty PDF download", async ({ page }) => { - // Mock the data endpoint with a minimal-but-shape-correct payload. - // The shape below mirrors the `ReportData` interface in - // `src/lib/doctor-report-pdf.ts` — empty stats / medications / mood - // is enough to exercise the table-rendering path without 90 days - // of seeded readings. - await page.route("**/api/doctor-report", (route) => - route.fulfill({ - status: 200, - contentType: "application/json", - body: JSON.stringify({ - data: { - period: { - days: 90, - since: new Date(Date.now() - 90 * 86_400_000).toISOString(), - }, - patient: { - username: "e2e-tester", - dateOfBirth: "1985-06-15T00:00:00.000Z", - gender: "MALE", - heightCm: 180, - }, - stats: {}, - bmi: null, - compliance: {}, - medications: [], - mood: null, - }, - error: null, - }), - }), - ); - - // Capture page errors so a failed dynamic-import surfaces in the - // test output instead of timing out silently. - const pageErrors: string[] = []; - page.on("pageerror", (err) => pageErrors.push(`pageerror: ${err.message}`)); - page.on("console", (msg) => { - if (msg.type() === "error") - pageErrors.push(`console.error: ${msg.text()}`); - }); - - await page.goto("/settings/export", { waitUntil: "domcontentloaded" }); - - // v1.4.37 renamed the trigger testid from `export-action-doctor-report` - // to `export-hero-doctor-report-action` when the card was lifted to a - // hero block on the export page. - const reportBtn = page.getByTestId("export-hero-doctor-report-action"); - await expect(reportBtn).toBeVisible({ timeout: 10_000 }); - - // Click opens the dialog; the dialog's submit button triggers the PDF - // generation. Trigger the download and capture the resulting Download - // object once the user confirms. - const downloadPromise = page.waitForEvent("download", { timeout: 30_000 }); - await reportBtn.click(); - // Submit the dialog with the default dates (last 90 days). - const submitBtn = page.getByRole("button", { - name: /generate pdf|pdf generieren/i, - }); - await expect(submitBtn).toBeVisible({ timeout: 5_000 }); - await submitBtn.click(); - const download = await downloadPromise.catch((err) => { - throw new Error( - `Download did not fire. Page errors:\n${pageErrors.join("\n")}\n\nOriginal: ${err}`, - ); - }); - - const path = await download.path(); - expect(path).toBeTruthy(); - - // Read first 4 bytes — a valid PDF starts with `%PDF`. - const fs = await import("node:fs/promises"); - const buf = await fs.readFile(path!); - expect(buf.byteLength).toBeGreaterThan(1024); - expect(buf.subarray(0, 4).toString("ascii")).toBe("%PDF"); - }); -}); diff --git a/e2e/settings-export.spec.ts b/e2e/settings-export.spec.ts index d38f04aff..c468dd08c 100644 --- a/e2e/settings-export.spec.ts +++ b/e2e/settings-export.spec.ts @@ -24,16 +24,16 @@ test.describe("Settings → Export", () => { test("renders all export surfaces with stable testids", async ({ page }) => { await page.goto("/settings/export", { waitUntil: "domcontentloaded" }); - // v1.12 — the health-record export is the page hero; the - // doctor-report card (`export-hero-*` prefix) is demoted to the - // bottom; the four CSV/backup tiles keep the `export-card-*` shape. + // The health-record export is the page hero; the four CSV/backup + // tiles keep the `export-card-*` shape. The doctor report lives + // under the health-record export, so it is no longer a separate + // surface here. for (const id of [ "health-record-export-panel", "export-card-measurements-csv", "export-card-medications-csv", "export-card-mood-csv", "export-card-full-backup", - "export-hero-doctor-report", ]) { await expect(page.getByTestId(id)).toBeVisible({ timeout: 10_000 }); } diff --git a/e2e/settings-integrations-mobile.spec.ts b/e2e/settings-integrations-mobile.spec.ts index 899c0a183..5a7c79457 100644 --- a/e2e/settings-integrations-mobile.spec.ts +++ b/e2e/settings-integrations-mobile.spec.ts @@ -15,11 +15,10 @@ import { STORAGE_STATE_PATH } from "./setup/global-setup"; * verifies neither path leaks into a horizontal scrollbar on the * element. * - * 2. Render exactly one pill per integration card (Withings + Mood - * Log when both are configured). Belt-and-braces guard for the - * consolidation Marc requested — the Vitest spec covers this on - * the SSR markup level, but the Pixel-5 layout is what matters - * to him in practice. + * 2. Render exactly one pill per integration card. Belt-and-braces + * guard for the card consolidation — the Vitest spec covers this + * on the SSR markup level, but the Pixel-5 layout is what matters + * in practice. * * Mobile-only spec — desktop project skips it. */ @@ -144,18 +143,18 @@ test.describe("/settings/integrations Pixel-5 layout", () => { // Wait for the pills to mount (queries resolve after first paint). const pills = page.locator('[data-testid="integration-status-pill"]'); - // Four integration cards now (Withings, moodLog, WHOOP, Google Health) → - // four pills. - await expect(pills).toHaveCount(4, { timeout: 10_000 }); + // Three integration cards now (Withings, WHOOP, Google Health) → three + // pills. + await expect(pills).toHaveCount(3, { timeout: 10_000 }); - // Withings + moodLog are connected in this fixture; WHOOP is a BYO-keys - // card that stays dormant until an operator adds credentials, so assert - // the two connected pills order-independently. + // Withings is connected in this fixture; WHOOP is a BYO-keys card that + // stays dormant until an operator adds credentials, so assert the one + // connected pill. await expect( page.locator( '[data-testid="integration-status-pill"][data-state="connected"]', ), - ).toHaveCount(2); + ).toHaveCount(1); // The redundant v1.4.18 banner must be gone. await expect(