From 276cac1d5b8cb58c6af705e6b66367726a80dcb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Thu, 8 Oct 2026 19:51:16 +0200 Subject: [PATCH] Point the e2e add flows at whichever add control a surface shows A list that is empty now carries its add action in the empty state instead of the header, and on the phone shell the dashboard's add opens the capture sheet. The overflow, mood timestamp, measurement flow, onboarding tour and delegated-write journeys accept either control; the measurement list's empty-state add gets a stable data-slot for that. --- e2e/delegated-writes.spec.ts | 9 ++++++++- e2e/dialog-horizontal-overflow.spec.ts | 10 ++++++---- e2e/measurement-flow.spec.ts | 8 +++++++- e2e/mood-timestamp.spec.ts | 4 +++- e2e/onboarding-tour-passthrough.spec.ts | 9 ++++++++- src/components/measurements/measurement-list.tsx | 5 ++++- 6 files changed, 36 insertions(+), 9 deletions(-) diff --git a/e2e/delegated-writes.spec.ts b/e2e/delegated-writes.spec.ts index 68150fffc..5c7cfa565 100644 --- a/e2e/delegated-writes.spec.ts +++ b/e2e/delegated-writes.spec.ts @@ -221,7 +221,14 @@ test.describe("delegated writes", () => { // 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(); + // The owner's list may be empty, in which case its empty state carries + // the add action and the header drops its own. + await page + .locator( + '[data-slot="measurement-add"], [data-slot="measurement-add-first"]', + ) + .first() + .click(); await page.locator("#sys").fill("124"); await page.locator("#dia").fill("78"); await page.getByRole("button", { name: /^save$/i }).click(); diff --git a/e2e/dialog-horizontal-overflow.spec.ts b/e2e/dialog-horizontal-overflow.spec.ts index 54267f858..95302437e 100644 --- a/e2e/dialog-horizontal-overflow.spec.ts +++ b/e2e/dialog-horizontal-overflow.spec.ts @@ -70,13 +70,15 @@ const DIALOGS: DialogCase[] = [ { name: "measurements add", path: "/measurements", - trigger: /^add$/i, + // While a list is empty the header drops its add button and the empty + // state carries the action, so either one opens the dialog. + trigger: /^(add|add your first measurement)$/i, ready: '[data-slot="date-time-field"]', }, { name: "mood add", path: "/mood", - trigger: /^add$/i, + trigger: /^(add|log your first mood)$/i, // The mood sheet opens on the five-face quick check; the timestamp row // (and everything else) appears once a face is picked. Measuring the // quick check alone would measure a sheet that has almost nothing in it. @@ -92,7 +94,7 @@ const DIALOGS: DialogCase[] = [ { name: "labs add", path: "/labs", - trigger: /^add$/i, + trigger: /^(add|add your first result)$/i, ready: '[data-slot="date-time-field"]', }, // The labs sheet's footer holds three buttons (Cancel, Save & add another, @@ -101,7 +103,7 @@ const DIALOGS: DialogCase[] = [ { name: "labs add (German)", path: "/labs", - trigger: /^hinzufügen$/i, + trigger: /^(hinzufügen|ersten wert erfassen)$/i, ready: '[data-slot="date-time-field"]', locale: "de", }, diff --git a/e2e/measurement-flow.spec.ts b/e2e/measurement-flow.spec.ts index 28b48c145..62e4d2137 100644 --- a/e2e/measurement-flow.spec.ts +++ b/e2e/measurement-flow.spec.ts @@ -135,7 +135,13 @@ test.describe("add measurement flow", () => { // v1.5 phase-5: the menu items now have distinct labels — the // measurement entry says "Measurement" / "Messung" instead of "Add", // so we can target it directly. - await page.getByRole("menuitem", { name: /measurement|messung/i }).click(); + // The desktop rail opens a dropdown, the phone shell the capture sheet + // the bottom nav's plus opens; the measurement entry is in either. + await page + .getByRole("menuitem", { name: /measurement|messung/i }) + .or(page.getByRole("button", { name: /^(measurement|messung)\b/i })) + .first() + .click(); // Form should be visible inside the dialog. Switch type to WEIGHT // (combo-box defaults to BLOOD_PRESSURE). diff --git a/e2e/mood-timestamp.spec.ts b/e2e/mood-timestamp.spec.ts index 28daa3df8..f157aa674 100644 --- a/e2e/mood-timestamp.spec.ts +++ b/e2e/mood-timestamp.spec.ts @@ -32,7 +32,9 @@ test.describe("mood timestamp capture", () => { }); await page.goto("/mood", { waitUntil: "domcontentloaded" }); - await page.getByRole("button", { name: "Add", exact: true }).click(); + // The list is mocked empty, so its empty state carries the add action + // (the header drops its own while there is nothing to list). + await page.getByRole("button", { name: "Log your first mood" }).click(); await page.getByRole("radio", { name: "Amazing" }).click(); const details = page.getByRole("button", { name: "Note & details" }); diff --git a/e2e/onboarding-tour-passthrough.spec.ts b/e2e/onboarding-tour-passthrough.spec.ts index a87ac6729..433797e8b 100644 --- a/e2e/onboarding-tour-passthrough.spec.ts +++ b/e2e/onboarding-tour-passthrough.spec.ts @@ -197,7 +197,14 @@ test.describe("v1.4.33 F2 — onboarding tour passes clicks through to the page" await quickAddButton.click(); await expect(quickAddButton).toHaveAttribute("aria-expanded", "true"); - await expect(page.getByRole("menuitem").first()).toBeVisible(); + // The desktop rail opens a dropdown; the phone shell opens the capture + // sheet the bottom nav's plus opens. Either proves the click landed. + await expect( + page + .getByRole("menuitem") + .or(page.locator('[role="dialog"][data-state="open"]')) + .first(), + ).toBeVisible(); // The tour stays mounted — the click went to the underlying button, // not anything in the tour layer. diff --git a/src/components/measurements/measurement-list.tsx b/src/components/measurements/measurement-list.tsx index f74c96bd6..032b28006 100644 --- a/src/components/measurements/measurement-list.tsx +++ b/src/components/measurements/measurement-list.tsx @@ -1087,7 +1087,10 @@ export function MeasurementList({ {t("measurements.emptyResetFilter")} ) : onAddFirst && canAddMeasurement ? ( -