diff --git a/docs/verification.md b/docs/verification.md index 772dc9c..846a68a 100644 --- a/docs/verification.md +++ b/docs/verification.md @@ -631,6 +631,29 @@ as accessible HTML text. console messages. Service worker `v2` activated and removed the old `v1` cache. Evidence is `output/brand/public-header-1030-20260719.png`. +## Current public functional replay + +The committed two-user medicine scenario now includes browser geolocation in +the primary path rather than relying only on the separate failure-recovery +scenario. + +- Against `https://whoneedhelp.imalto.site`, the current public release passed + request creation, helper acceptance, two-way realtime chat, explicit + geolocation consent, appearance of the helper's live marker in the + requester's browser, explicit tracking stop and marker deletion, the + one-time handover code, both completion confirmations, and double-blind + review reveal. The Chromium scenario passed 1/1 in 14.7 seconds. Exact + cleanup removed its two users, request, assignment, messages, reviews, + tracking session, abuse signals, and audit events; the before/after + application-table count diff was empty. Evidence is + `output/staging-e2e/20260719202129-3641894`. +- The complete browser suite with that expanded primary path passed 27/27: + 9 scenarios each in Chromium, Firefox, and WebKit against a fresh isolated + 2-web/2-worker Compose cluster. The independent active-node-failure path also + verified tracking and chat recovery after the serving BEAM node restarted. + Evidence is `output/e2e/20260719202632-3770163`; its containers, networks, + and database volume were removed by the run-scoped cleanup. + ## Known work before a public production launch - Replace the temporary staging origin with the production-owned domain and diff --git a/e2e/tests/active-failover.spec.ts b/e2e/tests/active-failover.spec.ts index 39c674f..9f137a3 100644 --- a/e2e/tests/active-failover.spec.ts +++ b/e2e/tests/active-failover.spec.ts @@ -1,5 +1,6 @@ import { expect, test } from "@playwright/test"; import { + markerKinds, projectEmail, projectText, registerAndConfirm, @@ -7,12 +8,6 @@ import { waitForLiveViewConnected, } from "./helpers"; -async function markerKinds(page: import("@playwright/test").Page): Promise { - const encoded = await page.locator("#show-map").getAttribute("data-markers"); - const markers = JSON.parse(encoded ?? "[]") as Array<{ title?: string }>; - return markers.map(({ title }) => title ?? ""); -} - async function positionCount(page: import("@playwright/test").Page): Promise { const raw = await page.locator("#e2e-runtime-node").getAttribute("data-position-count"); return Number(raw ?? "-1"); diff --git a/e2e/tests/helpers.ts b/e2e/tests/helpers.ts index 10bad22..98674b2 100644 --- a/e2e/tests/helpers.ts +++ b/e2e/tests/helpers.ts @@ -67,6 +67,12 @@ export async function waitForMapReady(page: Page): Promise { } } +export async function markerKinds(page: Page): Promise { + const encoded = await page.locator("#show-map").getAttribute("data-markers"); + const markers = JSON.parse(encoded ?? "[]") as Array<{ title?: string }>; + return markers.map(({ title }) => title ?? ""); +} + export function captureBrowserFailures(page: Page): () => void { const failures: string[] = []; diff --git a/e2e/tests/mutual-aid.spec.ts b/e2e/tests/mutual-aid.spec.ts index 0521428..1db13fe 100644 --- a/e2e/tests/mutual-aid.spec.ts +++ b/e2e/tests/mutual-aid.spec.ts @@ -2,6 +2,7 @@ import { expect, test } from "@playwright/test"; import { captureBrowserFailures, loginWithPassword, + markerKinds, projectEmail, projectText, registerAndConfirm, @@ -10,7 +11,7 @@ import { waitForMapReady, } from "./helpers"; -test("two users complete a medicine handover with realtime chat and blind reviews", async ({ +test("two users complete medicine tracking, handover, realtime chat, and blind reviews", async ({ browser, request, }, testInfo) => { @@ -73,6 +74,21 @@ test("two users complete a medicine handover with realtime chat and blind review await requester.page.getByRole("button", { name: "Send", exact: true }).click(); await expect(helper.page.getByText("Thank you. The order is waiting at the counter.")).toBeVisible(); + const origin = new URL(process.env.BASE_URL!).origin; + await helper.context.grantPermissions(["geolocation"], { origin }); + await helper.context.setGeolocation({ + latitude: 50.4504, + longitude: 30.5237, + accuracy: 5, + }); + await helper.page.getByRole("button", { name: "Share location" }).click(); + await expect(helper.page.getByRole("button", { name: "Stop and delete position" })).toBeVisible(); + await expect.poll(() => markerKinds(requester.page)).toContain("Shared live location"); + + await helper.page.getByRole("button", { name: "Stop and delete position" }).click(); + await expect(helper.page.getByRole("button", { name: "Share location" })).toBeVisible(); + await expect.poll(() => markerKinds(requester.page)).not.toContain("Shared live location"); + await helper.page.getByRole("button", { name: "Start helping" }).click(); const handoverCode = ( await requester.page.locator("text=HANDOVER CODE FOR THE HELPER").locator("..").locator(".font-mono").innerText()