From 9fb0f8b798e03c728d1a6742b44085612210dcb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Sun, 2 Aug 2026 00:19:59 +0200 Subject: [PATCH] Anchor the checkup due fixtures to the zone the component reads The two calendar-day cases built their due instant with setHours on the process clock, which vitest pins to UTC, while the component floors the due date in the profile zone. Those two disagree about the date for the two hours before UTC midnight, so both cases went red every night in that window and green again by morning. Deriving the date parts from the display zone removes the disagreement rather than hoping the suite never runs at night. Noon is chosen deliberately: at any offset the zone uses, 12:00 UTC lands on the same calendar date there, so the instant cannot slide either way. Verified inside the failing window: red before, green after, and putting setHours back turns it red again. --- .../__tests__/vorsorge-section.test.tsx | 46 ++++++++++++++++--- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/src/components/measurement-reminders/__tests__/vorsorge-section.test.tsx b/src/components/measurement-reminders/__tests__/vorsorge-section.test.tsx index d667a56f0..8bd47bdce 100644 --- a/src/components/measurement-reminders/__tests__/vorsorge-section.test.tsx +++ b/src/components/measurement-reminders/__tests__/vorsorge-section.test.tsx @@ -32,6 +32,34 @@ vi.mock("@/hooks/use-measurement-reminders", () => ({ })); import { VorsorgeSection } from "../vorsorge-section"; +import { DISPLAY_TIMEZONE } from "@/lib/format-locale"; + +/** + * Noon on the day `offsetDays` from today, counted on the DISPLAY zone's + * calendar rather than the process clock's. + * + * The component reads `useDisplayTimezone()` and floors the due date there, + * while `vitest.config.mts` pins the suite to `TZ=UTC`. For the two hours + * before UTC midnight, Europe/Berlin is already on the next date, so a fixture + * built with `new Date().setHours(...)` lands a day behind what the component + * sees. Deriving the date parts from the same zone the component uses removes + * the disagreement instead of hoping the suite never runs at night. + * + * Noon is deliberate: any UTC offset in use puts 12:00 UTC on the same + * calendar date in the display zone, so the instant cannot slide either way. + */ +function noonOnDisplayDay(offsetDays: number): Date { + // `en-CA` formats as YYYY-MM-DD, which is the shape we want to add days to. + const today = new Intl.DateTimeFormat("en-CA", { + timeZone: DISPLAY_TIMEZONE, + year: "numeric", + month: "2-digit", + day: "2-digit", + }).format(new Date()); + const shifted = new Date(`${today}T12:00:00.000Z`); + shifted.setUTCDate(shifted.getUTCDate() + offsetDays); + return shifted; +} function render(node: React.ReactNode) { // v1.18.7 (Wave E) — a measurement-linked card now mounts the 7-day @@ -172,10 +200,14 @@ describe(" loading + empty", () => { // v1.18.9 recon — a reminder whose nextDue falls on the SAME local // calendar day must read "Due today" with the green status hue, even when // the wall-clock gap to now exceeds the 24h a rolling delta would key on. - // Anchor the due instant to local noon today so the floored calendar-day - // delta is 0 regardless of the hour the suite runs. - const todayNoon = new Date(); - todayNoon.setHours(12, 0, 0, 0); + // Anchor the due instant to noon on the DISPLAY zone's today, not the + // process clock's. `vitest.config.mts` pins the suite to TZ=UTC while the + // component floors in the profile zone (`DISPLAY_TIMEZONE`, Europe/Berlin), + // and those two disagree about the date for the two hours before UTC + // midnight. Anchoring on `setHours` made this case fail every night in + // that window, which is the same confusion of clocks the assertion exists + // to catch. + const todayNoon = noonOnDisplayDay(0); const reminder: MeasurementReminder = { id: "due-today", label: "Blood pressure check", @@ -204,9 +236,9 @@ describe(" loading + empty", () => { // A reminder whose due CALENDAR day is before today reads "Overdue", with // the warning hue — a rolling-24h delta would mis-bucket a yesterday-evening // due viewed this morning as "today". - const yesterday = new Date(); - yesterday.setDate(yesterday.getDate() - 1); - yesterday.setHours(20, 0, 0, 0); + // Same clock discipline as the case above: one day back on the DISPLAY + // zone's calendar, not on the process clock's. + const yesterday = noonOnDisplayDay(-1); const reminder: MeasurementReminder = { id: "overdue", label: "Annual physical",