Files
MobilityOps/frontend/e2e/greeting.spec.ts
T
NuklearRabbit e427313bce feat: add time-dependent Europe/Brussels dashboard greeting
The dashboard greeting was a fully static "Goedemorgen..." regardless of
actual time of day. New frontend/src/i18n/greeting.ts::getGreetingPeriod
is a pure, clock-injectable function resolving one of 4 periods (05:00-
11:59 morning, 12:00-17:59 afternoon, 18:00-22:59 evening, 23:00-04:59
night) against Europe/Brussels wall-clock time via
Intl.DateTimeFormat({ timeZone, hourCycle: "h23" }), which is DST-safe
by construction.

useGreetingPeriod.ts wires this into React with a 30s poll so the
greeting rolls over live while the app stays open, no reload required.
Each period now has its own greeting word and accompanying sentence in
all 3 languages (dashboard.json), replacing both the fixed "Goedemorgen"
and the fixed "Here's the fleet" follow-up sentence. Night never says
"Goedenacht" (used as a farewell, not a welcome, in Dutch).
2026-08-04 03:08:45 +02:00

69 lines
3.4 KiB
TypeScript

import { expect, test } from "@playwright/test";
import { getBrusselsHour, getGreetingPeriod } from "../src/i18n/greeting";
// Pure Node-context boundary tests for the central, clock-injectable greeting function
// (section 9 of the Fleet Ops final localization brief). Every case below constructs an
// explicit UTC instant that corresponds to a specific Europe/Brussels wall-clock time --
// this is what "clock injection" buys: no real time needs to pass, and DST is exercised
// by picking instants either side of the CET/CEST transition.
// 2026-01-15 is CET (UTC+1): 04:59 Brussels = 03:59 UTC.
function cet(hour: number, minute = 0): Date {
return new Date(Date.UTC(2026, 0, 15, hour - 1, minute));
}
// 2026-07-15 is CEST (UTC+2): 04:59 Brussels = 02:59 UTC.
function cest(hour: number, minute = 0): Date {
return new Date(Date.UTC(2026, 6, 15, hour - 2, minute));
}
test("period boundaries are correct in winter time (CET, UTC+1)", () => {
expect(getGreetingPeriod(cet(4, 59))).toBe("night");
expect(getGreetingPeriod(cet(5, 0))).toBe("morning");
expect(getGreetingPeriod(cet(11, 59))).toBe("morning");
expect(getGreetingPeriod(cet(12, 0))).toBe("afternoon");
expect(getGreetingPeriod(cet(17, 59))).toBe("afternoon");
expect(getGreetingPeriod(cet(18, 0))).toBe("evening");
expect(getGreetingPeriod(cet(22, 59))).toBe("evening");
expect(getGreetingPeriod(cet(23, 0))).toBe("night");
});
test("period boundaries are correct in summer time (CEST, UTC+2)", () => {
expect(getGreetingPeriod(cest(4, 59))).toBe("night");
expect(getGreetingPeriod(cest(5, 0))).toBe("morning");
expect(getGreetingPeriod(cest(11, 59))).toBe("morning");
expect(getGreetingPeriod(cest(12, 0))).toBe("afternoon");
expect(getGreetingPeriod(cest(17, 59))).toBe("afternoon");
expect(getGreetingPeriod(cest(18, 0))).toBe("evening");
expect(getGreetingPeriod(cest(22, 59))).toBe("evening");
expect(getGreetingPeriod(cest(23, 0))).toBe("night");
});
test("DST transition (2026-03-29, clocks spring forward 02:00 -> 03:00 CEST): the Brussels hour never regresses or skips a period incorrectly", () => {
// 00:30 UTC = 01:30 CET, still "night" (before the 05:00 boundary regardless).
const beforeTransition = new Date(Date.UTC(2026, 2, 29, 0, 30));
expect(getBrusselsHour(beforeTransition)).toBe(1);
expect(getGreetingPeriod(beforeTransition)).toBe("night");
// 09:00 UTC on transition day = 11:00 CEST (already sprung forward) -- still morning.
const afterTransition = new Date(Date.UTC(2026, 2, 29, 9, 0));
expect(getBrusselsHour(afterTransition)).toBe(11);
expect(getGreetingPeriod(afterTransition)).toBe("morning");
// Autumn transition, 2026-10-25: fall-back happens at 01:00 UTC (03:00 CEST -> 02:00
// CET), so 00:30 UTC is still CEST -> 02:30 Brussels.
const beforeFallBack = new Date(Date.UTC(2026, 9, 25, 0, 30));
expect(getBrusselsHour(beforeFallBack)).toBe(2);
expect(getGreetingPeriod(beforeFallBack)).toBe("night");
// 09:00 UTC on fall-back day = 10:00 CET (already fallen back) -- still morning.
const afterFallBack = new Date(Date.UTC(2026, 9, 25, 9, 0));
expect(getBrusselsHour(afterFallBack)).toBe(10);
expect(getGreetingPeriod(afterFallBack)).toBe("morning");
});
test("default argument uses the real current time when no clock is injected", () => {
const period = getGreetingPeriod();
expect(["morning", "afternoon", "evening", "night"]).toContain(period);
});