Files
HealthLog/e2e/medications-wizard-compose.spec.ts
T
Marc-André Bombeck 1dd9b92b7d test(e2e): mark the medication-wizard variants and AI-flow group slow
The wizard-variant specs (rolling/weekdays/compose/monthly/biweekly/oneshot)
and the document-AI flow group are legitimately long multi-step flows; under
a loaded CI runner they race the default 30s and flake green paths — a
different one fails each run. Give them the tripled timeout, matching the
already-slow daily wizard.
2026-07-08 22:38:30 +02:00

178 lines
5.3 KiB
TypeScript

import { expect, test, type Route } from "@playwright/test";
import {
STUB_MEDICATION_ID,
clickNext,
clickSave,
expectMedicationOnList,
expectRedirectToMedication,
expectStep,
fillStep1Name,
fillStep3Dose,
openCreateWizard,
pickCadenceRow,
pickTreatmentRow,
stubDashboardAnalytics,
} from "./medications-wizard-helpers";
import { STORAGE_STATE_PATH } from "./setup/global-setup";
/**
* v1.5.4 — Medication wizard end-to-end — compose-mode.
*
* Walks the create flow through to Step 8, then taps
* "Weiteren Zeitplan hinzufügen" to append a second weekly Wednesday
* schedule on the same medication. The list view surfaces both
* summary cards before save, and the POST body carries both
* schedules so the route can persist them in parallel.
*
* Use case the maintainer cares about: short-acting + long-acting
* insulin on a single medication. Today the wizard collapses to one
* schedule on save; compose-mode closes that data-loss gap.
*/
test.describe("medication wizard — compose-mode", () => {
test.use({ storageState: STORAGE_STATE_PATH });
test("creates a 2-schedule medication end-to-end", async ({ page }) => {
test.slow();
await stubDashboardAnalytics(page);
const listEntry = {
id: STUB_MEDICATION_ID,
name: "Insulin",
dose: "10 IE",
category: "DIABETES",
active: true,
notificationsEnabled: true,
pausedAt: null,
lastTakenAt: null,
startsOn: null,
endsOn: null,
oneShot: false,
schedules: [
{
id: "sch_e2e_1",
windowStart: "08:00",
windowEnd: "09:00",
label: null,
dose: null,
daysOfWeek: null,
timesOfDay: ["08:00"],
rrule: "FREQ=DAILY",
rollingIntervalDays: null,
reminderGraceMinutes: null,
},
{
id: "sch_e2e_2",
windowStart: "20:00",
windowEnd: "21:00",
label: null,
dose: null,
daysOfWeek: null,
timesOfDay: ["20:00"],
rrule: "FREQ=WEEKLY;BYDAY=WE",
rollingIntervalDays: null,
reminderGraceMinutes: null,
},
],
};
const capture: { postBody: unknown } = { postBody: null };
void page.route("**/api/medications**", async (route: Route) => {
const req = route.request();
const url = new URL(req.url());
if (url.pathname !== "/api/medications") {
await route.fallback();
return;
}
if (req.method() === "POST") {
capture.postBody = JSON.parse(req.postData() ?? "null");
await route.fulfill({
status: 201,
contentType: "application/json",
body: JSON.stringify({ data: { ...listEntry }, error: null }),
});
return;
}
if (req.method() === "GET") {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify({ data: [listEntry], error: null }),
});
return;
}
await route.fallback();
});
await openCreateWizard(page);
// Steps 1-4 — medication-global.
await fillStep1Name(page, { name: "Insulin" });
await clickNext(page);
await expectStep(page, 2);
await pickTreatmentRow(page, "diabetes");
await clickNext(page);
await expectStep(page, 3);
await fillStep3Dose(page, { amount: "10" });
await clickNext(page);
await expectStep(page, 4);
await clickNext(page);
// Steps 5-7 — first schedule, daily cadence.
await expectStep(page, 5, 8);
await pickCadenceRow(page, "daily");
await expectStep(page, 5, 7);
await clickNext(page);
await expectStep(page, 6, 7);
await clickNext(page);
// Step 8 — list view with the first schedule rendered. Tap
// "Weiteren Zeitplan hinzufügen" to append a second draft.
await expectStep(page, 7, 7);
let cards = page.locator('[data-slot="wizard-schedule-card"]');
await expect(cards).toHaveCount(1);
await page.locator('[data-slot="wizard-schedule-add"]').click();
// Second schedule — weekdays on Wednesday.
await expectStep(page, 5);
await pickCadenceRow(page, "weekdays");
await clickNext(page);
await expectStep(page, 6);
const weChip = page.locator(
'[data-slot="wizard-weekday-chip"][data-token="WE"]',
);
if ((await weChip.getAttribute("data-active")) === "false") {
await weChip.click();
}
// Default MO chip — toggle off so the rrule is WE-only.
const moChip = page.locator(
'[data-slot="wizard-weekday-chip"][data-token="MO"]',
);
if ((await moChip.getAttribute("data-active")) === "true") {
await moChip.click();
}
await clickNext(page);
await expectStep(page, 7);
await clickNext(page);
// Back on Step 8 — both summary cards should land before save.
await expectStep(page, 8);
cards = page.locator('[data-slot="wizard-schedule-card"]');
await expect(cards).toHaveCount(2);
await clickSave(page);
await expectRedirectToMedication(page, STUB_MEDICATION_ID);
expect(capture.postBody).toMatchObject({
name: "Insulin",
oneShot: false,
schedules: [{ rrule: "FREQ=DAILY" }, { rrule: "FREQ=WEEKLY;BYDAY=WE" }],
});
await expectMedicationOnList(page, {
name: "Insulin",
withNextDueChip: true,
});
});
});