Files
Marc-André Bombeck 594898d350 Anchor the document visit-suggestion on a full instant
The suggestion anchored on the document's own date, which the DTO carries
as a date only. The suggest endpoint takes a full instant, so every
document asked the question as `2025-10-14` and got 422 back; the field
then rendered nothing and no document ever offered the visit its date
falls near. Lift the date to noon UTC before asking, the shape the two
lab moments already send.

The e2e that should have caught it never authenticated: the visits spec
carried no storage state, so its `page` redirected to login and its
`request` reads answered 401. It also asserts a global verdict — one
visit in the document's window — so clear the account's visits before
each test, run the file serially, and keep it to one project, since two
projects mutating the shared account would leave two candidates in the
window.

The accessibility mock lagged the same feature: the detail sheet reads
`encounterLinks` off the document and the mock omitted it, so the sheet
threw before it painted. Add the field the server always sends.
2026-08-08 18:46:55 +02:00

125 lines
4.7 KiB
TypeScript

/**
* Visits — the incidental-linking promise, proven end to end once.
*
* The flow this milestone is checked against, in one script:
*
* 1. create a visit with ONLY a date — nothing else touched, Save enabled;
* 2. it appears in the list;
* 3. open a document dated inside the ±7-day window;
* 4. the suggestion offers that visit;
* 5. take the offer;
* 6. the document's detail names the visit it now belongs to.
*
* Every assertion addresses a stable `data-slot`, never viewport text: the
* copy is i18n-driven and the mobile project renders the same slots at a
* different size, so a text assertion would break for the wrong reason.
*
* The visit is created through the API rather than by driving the sheet twice:
* step 1 is about the SAVE being possible with only a date, which the sheet
* assertion below covers, and the linking half needs a visit whose instant is
* pinned relative to the seeded document's date.
*/
import { STORAGE_STATE_PATH } from "./setup/global-setup";
import { expect, test } from "./setup/test";
import {
ensureVaultFixture,
MRT_DOC_ID,
resetEncounters,
} from "./setup/vault-fixture";
test.beforeEach(async () => {
// Own the window the linking case counts against: clear the account's visits
// before each test so a retry or a prior run cannot leave a second candidate
// in the ±7-day window and flip the single-candidate verdict to the picker.
await resetEncounters();
await ensureVaultFixture();
});
// Serial, and single-project below: the two tests share one account, and the
// reset above would race a sibling running in parallel — the linking test would
// clear the visit the save test is still asserting. In order, in one worker,
// each test owns the account for its span.
test.describe.configure({ mode: "serial" });
test.describe("visits", () => {
// The seeded account owns the visits and the document this flow drives, and
// both the `page` and the `request` fixture read them under its session.
// Without this the whole file runs anonymous: `/checkups` redirects to login
// and every `/api/*` read answers 401, which is a test-setup gap, not a
// product one.
test.use({ storageState: STORAGE_STATE_PATH });
test("a visit saves with a date and nothing else", async ({ page }) => {
await page.goto("/checkups");
await page.getByTestId("checkups-tab-visits").click();
const add = page.locator('[data-slot="visits-add"]').first();
await expect(add).toBeVisible();
await add.click();
// The date is pre-filled with now, and NOTHING else has been touched.
const save = page.locator('[data-slot="encounter-save"]').first();
await expect(save).toBeVisible();
// The whole product constraint: not disabled, with no practitioner, no
// reason, no outcome and no link chosen.
await expect(save).toBeEnabled();
await save.click();
// It appears in the list. A visit dated now is in the past arm.
await expect(page.locator('[data-slot="visit-card"]').first()).toBeVisible({
timeout: 15_000,
});
});
test("a document offers the visit its date falls near, and files itself against it", async ({
page,
request,
}) => {
// The seeded MRI report carries a fixed filing date; the visit is placed
// two days before it so the ±7-day window resolves to exactly one
// candidate — the branch that pre-selects.
const document = await request
.get(`/api/documents/inbound/${MRT_DOC_ID}`)
.then((res) => res.json());
const anchorDay: string =
document.data.reportDate ?? document.data.documentDate;
expect(
anchorDay,
"the fixture document carries a filing date",
).toBeTruthy();
const occurredAt = new Date(`${anchorDay}T09:00:00.000Z`);
occurredAt.setUTCDate(occurredAt.getUTCDate() - 2);
const created = await request.post("/api/encounters", {
data: {
occurredAt: occurredAt.toISOString(),
status: "DONE",
kind: "SPECIALIST",
},
});
expect(created.status()).toBe(201);
await page.goto(`/documents?doc=${MRT_DOC_ID}`);
const suggestion = page
.locator('[data-slot="document-encounter-suggestion"]')
.first();
await expect(suggestion).toBeVisible({ timeout: 15_000 });
// One candidate, so the single-candidate branch: a pre-selectable offer
// rather than a picker.
await expect(suggestion).toHaveAttribute("data-verdict", "one");
await page
.locator('[data-slot="document-encounter-suggestion-option"]')
.first()
.click();
// The filing landed: the read-only "belongs to visit" row appears.
await expect(
page.locator('[data-slot="document-visit-links"]').first(),
).toBeVisible({ timeout: 15_000 });
});
});