mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 05:12:14 +00:00
test: cover the tile's rate attribute in the unit gate, and correct two notes
`data-latest-rate` is a production attribute whose only reader was the browser suite, so a change to the rate's shape would have surfaced three minutes and a whole server away from the gate that should own it. One unit test now renders the chart over a loaded window and asserts the root carries the last day's rate. Two comments were wrong. The card's take / skip buttons do not collapse to icons at narrow widths — both render their label unconditionally beside the icon — so only the true half of the reason for addressing them by `data-slot` survives: the labels are translated copy. And the Enter press in the wizard's times-of-day helper is not decoration. The field's overlay input holds what is being typed and hands it to the form only on Enter or on blur (`src/components/ui/time-field.tsx:175-184`), while the add button is disabled on an empty draft — so a fill followed straight by a click waits out the actionability timeout on a button that never enables. Proven by removing it: the flow died at exactly that click. The line stays, with the reason written beside it.
This commit is contained in:
1 parent
c39bf0d0ed
commit
db8ac95342
3 files changed
+54
-13
No files matched your search
@@ -13,19 +13,23 @@
|
||||
* 1. Journey — a twice-daily medication created through the real wizard, one
|
||||
* dose taken and one skipped from the card, and the resulting figure read
|
||||
* back on the card, in the intake history and on the dashboard tile.
|
||||
* 2. Refusal — a dose recorded against a schedule that does not exist is
|
||||
* refused, and the rate is exactly what it was before.
|
||||
* 2. Refusal — an intake against a medication this account does not own, and
|
||||
* one against a slot its schedule does not have, are both refused and the
|
||||
* rate is exactly what it was before.
|
||||
* 3. Cadence — a medication due on two weekdays does not count the other
|
||||
* five as missed. Its daily twin, given the identical doses, does.
|
||||
*
|
||||
* Every assertion addresses a `data-slot` / `data-*` attribute rather than
|
||||
* viewport text: the copy is i18n-driven, the wizard runs in German and the
|
||||
* card's own labels collapse to icons at narrow widths.
|
||||
* viewport text: the copy is i18n-driven and the wizard runs in German, so
|
||||
* addressing a control by its label would assert the translation.
|
||||
*
|
||||
* The flows mutate the one seeded account and read counts back off it, so —
|
||||
* like `visits.spec.ts` and `vaccinations.spec.ts` — this file runs in a
|
||||
* single project (see `playwright.config.ts`) and serial, and clears the
|
||||
* cabinet before each test so a rate counts only the doses the test wrote.
|
||||
* The flows write rows and read counts back off them, so they run on their own
|
||||
* account (`E2E_MEDICATION`) — the rate a card shows is a percentage over one
|
||||
* cabinet, and a sibling spec mounting `/medications` in the other worker
|
||||
* would otherwise be spending the same per-actor read allowance and sitting in
|
||||
* the same dashboard denominator. The file still runs serial and in a single
|
||||
* project (see `playwright.config.ts`), because its three tests share that one
|
||||
* cabinet with each other.
|
||||
*
|
||||
* Why the medications are aged (`ageMedication`): compliance is reconstructed
|
||||
* from the medication's creation stamp forward, so a medication created a
|
||||
@@ -252,7 +256,16 @@ async function clockAnchoredDoseTimes(
|
||||
});
|
||||
}
|
||||
|
||||
/** Add one `HH:mm` to the wizard's times-of-day chips. */
|
||||
/**
|
||||
* Add one `HH:mm` to the wizard's times-of-day chips.
|
||||
*
|
||||
* The Enter press is the commit, not a flourish: the field's overlay input
|
||||
* holds what is being typed and only hands it to the form on Enter or on blur
|
||||
* (`src/components/ui/time-field.tsx:175-184`). Until then the draft is empty,
|
||||
* and the add button is `disabled` on exactly that
|
||||
* (`times-of-day-chips.tsx:267`) — so a fill followed straight by a click
|
||||
* waits out the actionability timeout on a button that never enables.
|
||||
*/
|
||||
async function addDoseTime(page: Page, time: string): Promise<void> {
|
||||
const chips = page.locator('[data-slot="times-of-day-chips"]');
|
||||
const draft = chips.getByTestId("times-of-day-draft-input");
|
||||
|
||||
@@ -15,8 +15,16 @@ import {
|
||||
* aggregation helper produces the correct rates.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The daily points the mocked query serves. Mutable so one test can hand the
|
||||
* chart a loaded window; every other test leaves it empty.
|
||||
*/
|
||||
const queryState = vi.hoisted(() => ({
|
||||
data: [] as { date: string; scheduled: number; taken: number }[],
|
||||
}));
|
||||
|
||||
vi.mock("@tanstack/react-query", () => ({
|
||||
useQuery: () => ({ data: [], isLoading: false }),
|
||||
useQuery: () => ({ data: queryState.data, isLoading: false }),
|
||||
useQueryClient: () => ({
|
||||
cancelQueries: () => Promise.resolve(),
|
||||
getQueryData: () => undefined,
|
||||
@@ -195,4 +203,24 @@ describe("<MedicationComplianceChart>", () => {
|
||||
const html = render(<MedicationComplianceChart />);
|
||||
expect(html).not.toContain('data-slot="medication-trend-chip"');
|
||||
});
|
||||
|
||||
/**
|
||||
* The tile's adherence number exists only as bar geometry, so the root's
|
||||
* `data-latest-rate` is the one way to read it back — and the browser suite
|
||||
* was its only reader, which put a production attribute's shape three
|
||||
* minutes and a whole server away from the gate that should own it. Pinned
|
||||
* here: the value is the LAST day's rate, as an integer.
|
||||
*/
|
||||
it("carries the last day's rate on the root as data-latest-rate", () => {
|
||||
queryState.data = [
|
||||
{ date: "2026-05-01", scheduled: 2, taken: 2 }, // 100 %
|
||||
{ date: "2026-05-02", scheduled: 4, taken: 1 }, // 25 %
|
||||
];
|
||||
try {
|
||||
const html = render(<MedicationComplianceChart />);
|
||||
expect(html).toContain('data-latest-rate="25"');
|
||||
} finally {
|
||||
queryState.data = [];
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -24,9 +24,9 @@ interface MedicationIntakeActionsProps {
|
||||
* structurally rather than kept byte-equivalent by hand.
|
||||
*
|
||||
* Both buttons carry a `data-slot`. They are the only controls on the card
|
||||
* that write, and their labels are i18n copy that also collapses to an icon
|
||||
* on the narrow card — an e2e journey addressing them by text would be
|
||||
* asserting the translation, not the control.
|
||||
* that write, and their labels are translated copy — an e2e journey
|
||||
* addressing them by text would be asserting the translation, not the
|
||||
* control.
|
||||
*/
|
||||
export function MedicationIntakeActions({
|
||||
intakeLoading,
|
||||
|
||||
Reference in new issue
Block a user