mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 20:42:43 +00:00
"Someone I look after" derived the child's answers onto the guardian's own record. The completion now takes the managed record the profile step just created, checks the same access rule the guardian's modules route applies, and lands the same derivation and dashboard seed on that record through the same record-keyed write; the guardian's own flow is stamped complete and latched without touching its map. Three integration cases pin it, and the child journey reads the child's map through a real switch — it asserted the guardian's map before, which pinned the wrong record. Also from the review: the checklist's visit row read a meta the visits list never publishes and now counts its body; the first-result tile read the measurements list as an array and now shows the reading; the demo allowlist admits the disclaimer and tour writes, with a structural test over every onboarding route file; restart clears the old first result, and the screen trusts the ledger before painting a result as done; the headroom sweep's raw-key check matches the flow's own key shapes; the demo migration script stops copying the dropped column; the questions-to-pass rule and the shell's onboarding-path rule are pure and pinned; the baseline form composes the shared action row; the schema diff loses the unrelated formatting churn.
151 lines
5.2 KiB
TypeScript
151 lines
5.2 KiB
TypeScript
/**
|
|
* v1.39 (C2) — the setup flow, path 1: blood pressure and weight, typed in.
|
|
*
|
|
* From the welcome screen to the dashboard on one account of its own: the
|
|
* questions, leaving after Q3 and coming back to Q4, the units question that
|
|
* only appears because weight was ticked, the confirm screen, one blood
|
|
* pressure reading entered inline as the first result, and the dashboard
|
|
* with the checklist open. Then the four things the design spec's definition
|
|
* of done names, read off the API rather than the viewport: the module map,
|
|
* the tile order, the checklist, and the resume.
|
|
*
|
|
* Runs in one project (see `playwright.config.ts`): it resets and mutates its
|
|
* own account.
|
|
*/
|
|
import {
|
|
E2E_SETUP_BP,
|
|
SETUP_BP_STORAGE_STATE_PATH,
|
|
} from "./setup/global-setup";
|
|
import { resetSetupFlow } from "./setup/setup-flow-fixture";
|
|
import { expect, test } from "./setup/test";
|
|
import {
|
|
acceptAndSetUp,
|
|
choose,
|
|
expectScreen,
|
|
next,
|
|
readMe,
|
|
readTileOrder,
|
|
} from "./setup-flow-helpers";
|
|
|
|
test.describe("setup flow — blood pressure and weight, typed in", () => {
|
|
test.use({ storageState: SETUP_BP_STORAGE_STATE_PATH });
|
|
|
|
test.beforeEach(async () => {
|
|
await resetSetupFlow(E2E_SETUP_BP.username);
|
|
});
|
|
|
|
test("walks from the welcome screen to a first reading on the dashboard", async ({
|
|
page,
|
|
}) => {
|
|
test.setTimeout(90_000);
|
|
|
|
await page.goto("/onboarding");
|
|
await acceptAndSetUp(page);
|
|
|
|
await choose(page, "who", ["me"]);
|
|
await next(page);
|
|
await expectScreen(page, "areas");
|
|
|
|
await choose(page, "areas", ["blood-pressure", "weight-body"]);
|
|
await next(page);
|
|
await expectScreen(page, "medication");
|
|
|
|
await choose(page, "medication", ["no"]);
|
|
await next(page);
|
|
await expectScreen(page, "sources");
|
|
|
|
// Leave after Q3 and come back: the first-run redirect lands on
|
|
// /onboarding, and the front door sends the flow to the step it owes.
|
|
await page.goto("/");
|
|
await expect
|
|
.poll(() => new URL(page.url()).pathname)
|
|
.toBe("/onboarding/sources");
|
|
await expectScreen(page, "sources");
|
|
|
|
await choose(page, "sources", ["manual"]);
|
|
await next(page);
|
|
await expectScreen(page, "visit");
|
|
|
|
await choose(page, "visit", ["no"]);
|
|
await next(page);
|
|
|
|
// Q6 appears because weight was ticked and the account holds no unit.
|
|
await expectScreen(page, "units");
|
|
await expect(
|
|
page.locator('[data-slot="onboarding-question-unit-preference"]'),
|
|
).toBeVisible();
|
|
await expect(
|
|
page.locator('[data-slot="onboarding-question-glucose-unit"]'),
|
|
).toHaveCount(0);
|
|
await choose(page, "unit-preference", ["metric"]);
|
|
await next(page);
|
|
|
|
await expectScreen(page, "confirm");
|
|
await expect(
|
|
page.locator('[data-slot="onboarding-confirm-modules"]'),
|
|
).toBeVisible();
|
|
await next(page);
|
|
|
|
// The first result: one blood-pressure reading, entered inline.
|
|
await expectScreen(page, "first-result");
|
|
const screen = page.locator("section[data-task]");
|
|
await expect(screen).toHaveAttribute("data-task", "log-reading");
|
|
await expect(screen).toHaveAttribute("data-target", "blood-pressure");
|
|
await page.locator("#sys").fill("124");
|
|
await page.locator("#dia").fill("78");
|
|
await page.locator("#puls").fill("66");
|
|
await page.getByRole("button", { name: /^save$/i }).click();
|
|
const done = page.locator('[data-slot="onboarding-first-result-done"]');
|
|
await expect(done).toBeVisible({ timeout: 15_000 });
|
|
// The reading on its tile, not only a tick: the value that was typed.
|
|
await expect(done).toContainText("124/78", { timeout: 15_000 });
|
|
await next(page);
|
|
|
|
await expectScreen(page, "done");
|
|
await page.locator('[data-slot="onboarding-open-dashboard"]').click();
|
|
await expect.poll(() => new URL(page.url()).pathname).toBe("/");
|
|
|
|
// The dashboard, with the checklist open.
|
|
const checklist = page.locator('[data-testid="onboarding-card"]');
|
|
await expect(checklist).toBeVisible({ timeout: 15_000 });
|
|
await expect(checklist.locator("#getting-started-body")).toBeVisible();
|
|
|
|
// The module map: nothing the answers did not name is on; the always-on
|
|
// trio is.
|
|
const me = await readMe(page);
|
|
for (const key of [
|
|
"glucose",
|
|
"sleep",
|
|
"recovery",
|
|
"mood",
|
|
"mentalHealth",
|
|
"labs",
|
|
"illness",
|
|
"workouts",
|
|
"medications",
|
|
"doctorReport",
|
|
"vaccinations",
|
|
]) {
|
|
expect(me.modules[key], `module ${key}`).toBe(false);
|
|
}
|
|
for (const key of ["insights", "achievements", "inboundDocuments"]) {
|
|
expect(me.modules[key], `module ${key}`).not.toBe(false);
|
|
}
|
|
expect(me.onboarding.completedAt).not.toBeNull();
|
|
expect(me.onboarding.firstResult?.task).toBe("log-reading");
|
|
expect(me.onboarding.firstResult?.completedAt).not.toBeNull();
|
|
expect(
|
|
me.onboarding.steps.every((step) => step.status !== "pending"),
|
|
"every step settled",
|
|
).toBe(true);
|
|
|
|
// The tile order: the two areas' five tiles lead, in the default
|
|
// layout's own relative order, and everything else sits behind them.
|
|
const order = await readTileOrder(page);
|
|
expect(new Set(order.slice(0, 5))).toEqual(
|
|
new Set(["weight", "bp", "pulse", "bodyFat", "bpInTarget"]),
|
|
);
|
|
expect(order.indexOf("hrv")).toBeGreaterThan(4);
|
|
});
|
|
});
|