Files
HealthLog/e2e/setup-flow-child.spec.ts
Marc-André Bombeck fea1d1e0b5 chore: strip planning tags from comments and enable the rule
Output of `node scripts/strip-planning-tags.mjs --write`: 3,157 comment
lines in 1,308 files lose the work-item identifier at their head. Release
versions and issue references stay; nothing outside comment text
changes, which the script checks per file against a comment-free
printout. healthlog/no-planning-tags is enabled at error level with this
commit, so the tree it lands on is clean.
2026-10-20 09:08:40 +02:00

190 lines
7.3 KiB
TypeScript

/**
* v1.39 — the setup flow, path 4: a child's managed profile.
*
* Q1 "someone I look after" keeps the questions in the guardian's own record
* and creates the profile on the confirm screen, after the questions — the
* maintainer's answer to spec question 2, and the only order the routes
* allow, since the three writes refuse under an acting-account switch. The
* answers are then applied to the CHILD: the completion carries the new
* record's id, the derivation lands there (immunization log on, everything
* the answers did not name off), and the guardian's own module map is left
* exactly as it was. The first-result screen is not in the order (its tasks
* would write the guardian's record), and the done screen offers the new
* record through the same switch the account menu uses.
*
* This spec once asserted `glucose === false` on the GUARDIAN's map — the
* review's H1 — which pinned the child's answers re-ordering a parent's own
* record. It now reads the child's map through a real switch and the
* guardian's map for being untouched, so a regression to deriving onto the
* caller fails twice over.
*
* Creating the profile resolves `requireFreshMfa`, so the account carries a
* confirmed factor (stamped by global setup after its login) and the fixture
* makes the session's step-up fresh right before the walk.
*/
import {
E2E_SETUP_CHILD,
SETUP_CHILD_STORAGE_STATE_PATH,
} from "./setup/global-setup";
import {
clearManagedProfilesOf,
resetSetupFlow,
stampFreshMfa,
} from "./setup/setup-flow-fixture";
import { expect, test } from "./setup/test";
import {
acceptAndSetUp,
choose,
expectScreen,
next,
readMe,
} from "./setup-flow-helpers";
const PROFILE_NAME = "Setup journey child";
// Serial: both tests reset and walk the ONE account, and two workers doing
// that at once each undo the other's setup mid-flow.
test.describe.configure({ mode: "serial" });
test.describe("setup flow — a child's managed profile", () => {
test.use({ storageState: SETUP_CHILD_STORAGE_STATE_PATH });
test.beforeEach(async () => {
await clearManagedProfilesOf(E2E_SETUP_CHILD.username);
await resetSetupFlow(E2E_SETUP_CHILD.username);
await stampFreshMfa(E2E_SETUP_CHILD.username);
});
test("creates the profile on the confirm screen and offers its record", async ({
page,
}) => {
test.setTimeout(90_000);
await page.goto("/onboarding");
await acceptAndSetUp(page);
await choose(page, "who", ["someone-else"]);
await next(page);
await expectScreen(page, "areas");
await choose(page, "areas", ["blood-pressure"]);
await next(page);
await expectScreen(page, "medication");
await choose(page, "medication", ["no"]);
await next(page);
await expectScreen(page, "sources");
await choose(page, "sources", ["manual"]);
await next(page);
await expectScreen(page, "visit");
await choose(page, "visit", ["no"]);
await next(page);
// The confirm screen is the managed-profile form for this answer.
await expectScreen(page, "confirm");
const managed = page.locator('[data-slot="onboarding-confirm-managed"]');
await expect(managed).toBeVisible();
await managed
.locator('[data-slot="managed-profile-name"]')
.fill(PROFILE_NAME);
await managed
.locator('[data-slot="managed-profile-create-submit"]')
.click();
// No first-result screen for this answer: straight to done, with the new
// record offered.
await expectScreen(page, "done");
await expect(
page.locator('[data-slot="onboarding-open-managed-record"]'),
).toBeVisible({ timeout: 15_000 });
// The guardian's own record: flow complete, module map untouched — a
// fresh account carries no explicit map, so nothing reads `false`.
const me = await readMe(page);
expect(me.onboarding.completedAt).not.toBeNull();
expect(
me.onboarding.steps.find((step) => step.id === "first-result")?.status,
).toBe("skipped");
for (const key of ["glucose", "sleep", "mood", "labs", "medications"]) {
expect(me.modules[key], `guardian module ${key}`).not.toBe(false);
}
const managedEntries = (me.accountAccess?.accounts ?? []).filter(
(entry) => entry.recordKind === "managed",
);
expect(managedEntries).toHaveLength(1);
// The child's record, read through a real switch: the answers landed
// here. Switched back afterwards so the jar leaves the way it came.
const switched = await page.request.post("/api/account/switch", {
data: { accountId: managedEntries[0].accountId },
});
expect(switched.status(), "switching into the child's record").toBe(200);
try {
const child = await readMe(page);
expect(child.modules.vaccinations).not.toBe(false);
for (const key of ["glucose", "sleep", "mood", "labs", "medications"]) {
expect(child.modules[key], `child module ${key}`).toBe(false);
}
expect(child.onboarding.needs.recordTarget).toBe("someone-else");
expect(child.onboarding.completedAt).not.toBeNull();
} finally {
const back = await page.request.post("/api/account/switch", {
data: { accountId: null },
});
expect(back.status(), "switching back").toBe(200);
}
await page.locator('[data-slot="onboarding-open-dashboard"]').click();
await expect.poll(() => new URL(page.url()).pathname).toBe("/");
});
test("finishing without the profile derives nothing onto the guardian", async ({
page,
}) => {
// The other exit of the same screen (H1'): no profile is created, so
// there is no record the answers describe. The guardian's own map must
// stay exactly as it was, and the checklist carries the profile instead.
test.setTimeout(90_000);
await page.goto("/onboarding");
await acceptAndSetUp(page);
await choose(page, "who", ["someone-else"]);
await next(page);
await expectScreen(page, "areas");
await choose(page, "areas", ["blood-pressure"]);
await next(page);
await expectScreen(page, "medication");
await choose(page, "medication", ["yes"]);
await next(page);
await expectScreen(page, "sources");
await choose(page, "sources", ["oura"]);
await next(page);
await expectScreen(page, "visit");
await choose(page, "visit", ["within-a-month"]);
await next(page);
await expectScreen(page, "confirm");
await page
.locator('[data-slot="onboarding-finish-without-profile"]')
.click();
await expectScreen(page, "done");
await expect(
page.locator('[data-slot="onboarding-open-managed-record"]'),
).toHaveCount(0);
const me = await readMe(page);
expect(me.onboarding.completedAt).not.toBeNull();
for (const key of ["glucose", "sleep", "mood", "labs", "vaccinations"]) {
expect(me.modules[key], `guardian module ${key}`).not.toBe(false);
}
expect((me.accountAccess?.accounts ?? []).length).toBe(0);
await page.locator('[data-slot="onboarding-open-dashboard"]').click();
await expect.poll(() => new URL(page.url()).pathname).toBe("/");
const checklist = page.locator('[data-testid="onboarding-card"]');
await expect(checklist).toBeVisible({ timeout: 15_000 });
await expect(
checklist.locator('li[data-item-id="managedProfile"]'),
).toHaveAttribute("data-done", "false");
await expect(checklist.locator('li[data-item-id="visit"]')).toHaveCount(0);
});
});