From 0b1a6c1b09a4a011e821bc15fb2bd1ebe7497eb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Mon, 3 Aug 2026 19:12:11 +0200 Subject: [PATCH] Make the delegated-write journey actually run Every test in e2e/delegated-writes.spec.ts had been skipping since the day the invitation form shipped its level control. The skip guard looked for data-slot="grant-invite-level"; the control landed as "grant-invite-access-option". The file said out loud what to change and nobody changed it, so a quiet skip and a passing suite read the same in a CI summary. Two assertions inside it had never been executed and were both wrong: the header button reads "Add", not "Add measurement", and the form opens on blood pressure, which has no `value` field. Fixed against stable attributes rather than viewport text, with data-slot="measurement-add" added to the control the journey clicks. Adds the deep-link leg the SSR suite cannot hold: a query parameter is the same affordance as the button, and ?add= / ?new=1 open exactly what the resolved level admits. --- e2e/account-sharing.spec.ts | 29 --------------------- e2e/delegated-writes.spec.ts | 49 +++++++++++++++++++++-------------- src/app/measurements/page.tsx | 1 + 3 files changed, 31 insertions(+), 48 deletions(-) diff --git a/e2e/account-sharing.spec.ts b/e2e/account-sharing.spec.ts index 61fa91c58..b7e0a7984 100644 --- a/e2e/account-sharing.spec.ts +++ b/e2e/account-sharing.spec.ts @@ -386,35 +386,6 @@ test.describe("account sharing", () => { ).toBeVisible(); }); - test("a query parameter opens nothing the button withholds", async ({ - page, - }) => { - // Three deep links reach a create sheet without passing the control that - // normally opens it. A parameter that opens a sheet is the same affordance - // as the button, and the SSR suite cannot reach any of them: it holds the - // paint of a component, never a URL. - await page.goto("/measurements?add=WEIGHT"); - await expect( - page.locator('[data-slot="shared-record-banner"]'), - ).toBeVisible(); - await expect( - page.locator('[data-slot="responsive-sheet-content"]'), - ).toHaveCount(0); - // And the button it stands in for is gone too, so nothing on this page - // offers the form by either route. - await expect( - page.getByRole("button", { name: /add measurement/i }), - ).toHaveCount(0); - - await page.goto("/medications?new=1"); - await expect( - page.locator('[data-slot="shared-record-banner"]'), - ).toBeVisible(); - await expect( - page.locator('[data-slot="medication-wizard-dialog"]'), - ).toHaveCount(0); - }); - test("revoking from the owner's browser drops the delegate out", async ({ page, }) => { diff --git a/e2e/delegated-writes.spec.ts b/e2e/delegated-writes.spec.ts index e045f9448..42d7a972c 100644 --- a/e2e/delegated-writes.spec.ts +++ b/e2e/delegated-writes.spec.ts @@ -9,19 +9,22 @@ * * ## Why it gates itself instead of seeding a WRITE grant directly * - * The grant level is chosen in the invitation form, which is another chunk's - * work. Rather than mint a WRITE row behind the UI's back — which would prove - * the journey works for a grant no person can create — the journey looks for - * the level control and stands down when it is not there yet. + * The grant level is chosen in the invitation form. Rather than mint a WRITE + * row behind the UI's back — which would prove the journey works for a grant + * no person can create — the journey looks for the level control and stands + * down when it is not there yet. * - * **To enable this once the invite form ships its level control:** if it lands - * under a different `data-slot` than the constant below, change that one - * string. Nothing else in this file assumes anything about the control except - * that picking WRITE and submitting mints a WRITE grant. + * That guard was written against a `data-slot` the form never shipped under + * (`grant-invite-level`; the control landed as `grant-invite-access-option`), + * so from the day the control arrived until 2026-08-03 every test in this file + * skipped and the whole delegated-write journey ran nowhere. The file said out + * loud what to change and nobody changed it, which is the standing lesson + * about a check that cannot fail: the skip is quiet, and a quiet skip and a + * passing suite look identical in a CI summary. * - * The skip is deliberately loud rather than silent: it names the missing - * control, so a run where the control exists and the journey still does not - * execute reads as a bug in this file rather than as an absence upstream. + * The constant is the real one now. If it moves again, change that one string: + * nothing else here assumes anything about the control except that choosing + * WRITE and submitting mints a WRITE grant. * * ## What this spec cannot cover * @@ -45,10 +48,10 @@ import { * The invitation form's grant-level control. The journey runs when this is on * the page and stands down when it is not. One string, one place. */ -const GRANT_LEVEL_SLOT = "grant-invite-level"; +const GRANT_LEVEL_SLOT = "grant-invite-access-option"; /** The value the level control carries for a grant that may add entries. */ -const WRITE_LEVEL_VALUE = "write"; +const WRITE_LEVEL_VALUE = "WRITE"; test.describe("delegated writes", () => { // One journey in order, like the read-only sibling: each step is the next @@ -110,9 +113,11 @@ test.describe("delegated writes", () => { await expect(submit).toBeEnabled({ timeout: 1000 }); }).toPass({ timeout: 15_000 }); - await ownerPage - .locator(`[data-slot="${GRANT_LEVEL_SLOT}"]`) - .selectOption(WRITE_LEVEL_VALUE); + const writeOption = ownerPage.locator( + `[data-slot="${GRANT_LEVEL_SLOT}"][data-access="${WRITE_LEVEL_VALUE}"]`, + ); + await writeOption.click(); + await expect(writeOption).toHaveAttribute("data-selected", "true"); // Read the posted body: a level control that renders and sends a hardcoded // level would pass every render assertion and ship a read-only grant. @@ -125,7 +130,7 @@ test.describe("delegated writes", () => { access?: string; }; expect( - posted.access?.toLowerCase(), + posted.access, "the invitation must carry the level the owner chose", ).toBe(WRITE_LEVEL_VALUE); }); @@ -162,8 +167,14 @@ test.describe("delegated writes", () => { res.request().method() === "POST" && res.url().endsWith("/api/measurements"), ); - await page.getByRole("button", { name: /add measurement/i }).click(); - await page.locator('input[name="value"], #value').first().fill("71.5"); + // Stable attributes and the form's real fields, neither of which this + // step had. It looked for a button named "Add measurement" (the header + // reads "Add") and then for a `value` input (the form opens on blood + // pressure, which has three). Both were wrong from the day they were + // written and nobody found out, because the whole file was skipping. + await page.locator('[data-slot="measurement-add"]').click(); + await page.locator("#sys").fill("124"); + await page.locator("#dia").fill("78"); await page.getByRole("button", { name: /^save$/i }).click(); expect((await post).status(), "the write must be accepted").toBeLessThan( 300, diff --git a/src/app/measurements/page.tsx b/src/app/measurements/page.tsx index 821d564c6..3f753ed8e 100644 --- a/src/app/measurements/page.tsx +++ b/src/app/measurements/page.tsx @@ -157,6 +157,7 @@ export default function MeasurementsPage() { actions={ canAdd ? (