mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 13:22:14 +00:00
The doctor-report PDF entry-point used to share the Advanced section with CSV/JSON exports — three buttons crammed onto one row with no filter UI in front of them. v1.4.16 phase B7 promotes export to a first-class settings section, so the doctor-report card now lives inside `<ExportSection>` (its own card with the date-range dialog prominently surfaced via "Configure & generate"). `<AdvancedSection>` is reduced to the danger-zone card alone — the "wipe all data" surface should never live next to a one-click export button anyway. The matching e2e spec follows the move from `/settings/advanced` to `/settings/export` and now drives the dialog through the submit step instead of expecting an inline button. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
98 lines
3.7 KiB
TypeScript
98 lines
3.7 KiB
TypeScript
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" });
|
|
|
|
// The card carries a stable testid so the e2e suite can target it
|
|
// without depending on the localised "Configure & generate" label.
|
|
const reportBtn = page.getByTestId("export-action-doctor-report");
|
|
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");
|
|
});
|
|
});
|