mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 20:42:43 +00:00
One-shot prettier pass so the new format:check gate starts from a clean baseline. Formatting only — no behaviour change. Generator-owned and vendored artifacts stay untouched via .prettierignore.
177 lines
5.3 KiB
TypeScript
177 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 }) => {
|
|
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,
|
|
});
|
|
});
|
|
});
|