import { expect, type Page, test } from "@playwright/test"; // Browser-context evidence for the time-dependent Europe/Brussels dashboard greeting // (section 9 / 11 / 16 of the Fleet Ops final localization brief): the real rendered app, // in all 3 languages, at every required boundary instant, using Playwright's clock API to // control the browser's Date without waiting on real wall-clock time. Also proves the // greeting updates live (no reload) when the period rolls over while the app stays open. // 2026-01-15 is CET (UTC+1): Brussels hour = UTC hour + 1. function cet(hour: number, minute = 0, second = 0): Date { return new Date(Date.UTC(2026, 0, 15, hour - 1, minute, second)); } const BOUNDARY_CASES: Array<{ time: Date; label: string; period: string }> = [ { time: cet(4, 59), label: "04:59", period: "night" }, { time: cet(5, 0), label: "05:00", period: "morning" }, { time: cet(11, 59), label: "11:59", period: "morning" }, { time: cet(12, 0), label: "12:00", period: "afternoon" }, { time: cet(17, 59), label: "17:59", period: "afternoon" }, { time: cet(18, 0), label: "18:00", period: "evening" }, { time: cet(22, 59), label: "22:59", period: "evening" }, { time: cet(23, 0), label: "23:00", period: "night" }, ]; const EXPECTED_TITLE: Record> = { "nl-BE": { morning: "Goedemorgen. Hier is de status van je wagenpark voor vandaag.", afternoon: "Goedemiddag. Hier is het actuele overzicht van je wagenpark.", evening: "Goedenavond. Hier is het overzicht van je wagenpark voor vanavond.", night: "Welkom terug. Hier is het laatste overzicht van je wagenpark.", }, "en-GB": { morning: "Good morning. Here's today's fleet status.", afternoon: "Good afternoon. Here's the current overview of your fleet.", evening: "Good evening. Here's this evening's fleet overview.", night: "Welcome back. Here's the latest overview of your fleet.", }, "fr-BE": { morning: "Bonjour. Voici l'état de votre flotte pour aujourd'hui.", afternoon: "Bonjour. Voici l'aperçu actuel de votre flotte.", evening: "Bonsoir. Voici l'aperçu de votre flotte pour ce soir.", night: "Bon retour. Voici le dernier aperçu de votre flotte.", }, }; async function loginAsOperationsManager(page: Page) { await page.goto("/login"); await page.getByRole("button", { name: "Verken als Operationsmanager" }).click(); await expect(page).toHaveURL(/\/dashboard$/); } async function switchLanguage(page: Page, language: "nl-BE" | "en-GB" | "fr-BE") { // At the default desktop viewport, only the topbar's compact switcher is visible -- // the sidebar's full switcher is `display: none` until the <960px breakpoint. await page.locator(".language-switcher-compact select").selectOption(language); } const dashboardHeading = (page: Page) => page.locator(".page-header h1"); for (const language of ["nl-BE", "en-GB", "fr-BE"] as const) { test(`dashboard greeting matches every required boundary time in ${language}`, async ({ page }) => { await page.clock.install({ time: BOUNDARY_CASES[0].time }); await loginAsOperationsManager(page); if (language !== "nl-BE") { await switchLanguage(page, language); } for (const { time, label, period } of BOUNDARY_CASES) { await page.clock.setFixedTime(time); await page.reload(); await expect(dashboardHeading(page), `${language} @ ${label} Brussels time (expected period: ${period})`).toHaveText( EXPECTED_TITLE[language][period], ); } }); } test("dashboard greeting updates live across a period rollover without a page reload", async ({ page }) => { // Start at 11:59:31 Brussels -- 29s before the 12:00 boundary. await page.clock.install({ time: cet(11, 59, 31) }); await loginAsOperationsManager(page); await expect(dashboardHeading(page)).toHaveText(EXPECTED_TITLE["nl-BE"].morning); // Advance 30s of fake time (crossing 12:00) so the hook's 30s poll interval fires and // recomputes the period -- no page.reload() call anywhere in this test. await page.clock.fastForward(30_000); await expect(dashboardHeading(page)).toHaveText(EXPECTED_TITLE["nl-BE"].afternoon); }); test("dashboard greeting updates immediately on language switch without changing the time period", async ({ page }) => { await page.clock.install({ time: cet(9, 0) }); await loginAsOperationsManager(page); await expect(dashboardHeading(page)).toHaveText(EXPECTED_TITLE["nl-BE"].morning); await switchLanguage(page, "en-GB"); await expect(dashboardHeading(page)).toHaveText(EXPECTED_TITLE["en-GB"].morning); await switchLanguage(page, "fr-BE"); await expect(dashboardHeading(page)).toHaveText(EXPECTED_TITLE["fr-BE"].morning); }); test("dashboard never shows 'Goedenacht' as a greeting at any hour", async ({ page }) => { await page.clock.install({ time: cet(2, 0) }); await loginAsOperationsManager(page); await expect(dashboardHeading(page)).not.toContainText("Goedenacht"); await expect(dashboardHeading(page)).toContainText("Welkom terug"); });