fix(diagnostics): count the 24 hours from the moment the level was raised
The countdown's clock was the moment Settings mounted, so a level raised two minutes after opening the page read "24 h 2 m" until the first tick (measured on prod). The clock is read at render; the interval only re-renders.
This commit is contained in:
@@ -16,8 +16,14 @@ vi.mock("react-i18next", async (importOriginal) => {
|
||||
return {
|
||||
...actual,
|
||||
useTranslation: () => ({
|
||||
t: (key: string, opts?: { count?: number }) =>
|
||||
opts?.count != null ? `${key}:${opts.count}` : key,
|
||||
t: (key: string, opts?: { count?: number; h?: number; m?: number; time?: string }) =>
|
||||
opts?.count != null
|
||||
? `${key}:${opts.count}`
|
||||
: opts?.m != null
|
||||
? `${key}:${opts.h ?? 0}h${opts.m}m`
|
||||
: opts?.time != null
|
||||
? `${key}:${opts.time}`
|
||||
: key,
|
||||
i18n: { language: "en" },
|
||||
}),
|
||||
};
|
||||
@@ -106,6 +112,23 @@ describe("DiagnosticsSettings", () => {
|
||||
expect(setLevel).toHaveBeenLastCalledWith("INFO");
|
||||
});
|
||||
|
||||
it("counts the 24 hours from the moment the level was raised, not from when the page opened", async () => {
|
||||
vi.useFakeTimers({ toFake: ["Date"] });
|
||||
try {
|
||||
vi.setSystemTime(new Date("2026-09-19T02:54:00Z"));
|
||||
mount({});
|
||||
vi.setSystemTime(new Date("2026-09-19T02:56:30Z")); // the page sat open for 2.5 minutes
|
||||
fireEvent.click(levelButton("settings.diagnostics.levels.DEBUG"));
|
||||
fireEvent.click(screen.getByTestId("diagnostics-consent-confirm"));
|
||||
await waitFor(() => expect(screen.getByTestId("diagnostics-status")).toBeTruthy());
|
||||
expect(screen.getByTestId("diagnostics-status").textContent).toContain(
|
||||
"settings.diagnostics.hoursMinutes:24h0m",
|
||||
);
|
||||
} finally {
|
||||
vi.useRealTimers();
|
||||
}
|
||||
});
|
||||
|
||||
it("going back to the base level needs no consent", async () => {
|
||||
const until = new Date(Date.now() + 3_600_000).toISOString();
|
||||
mount({ diagnostics: { level: "DEBUG", until, retention_days: 7 } });
|
||||
|
||||
@@ -15,14 +15,17 @@ const LEVELS: readonly DiagnosticsLevel[] = ["INFO", "DEBUG", "TRACE"];
|
||||
|
||||
/** Hours and minutes until `until`, refreshed every 30 s — the status line counts down. */
|
||||
function useRemaining(until: string | null): { h: number; m: number } | null {
|
||||
const [now, setNow] = useState(() => Date.now());
|
||||
// The clock is read at render, and the interval only re-renders: a `now` held in state was the
|
||||
// moment the page MOUNTED, so a level raised two minutes after opening Settings read "24 h 2 m"
|
||||
// until the first tick (prod UAT 2026-09-19).
|
||||
const [, tick] = useState(0);
|
||||
useEffect(() => {
|
||||
if (!until) return;
|
||||
const id = window.setInterval(() => setNow(Date.now()), 30_000);
|
||||
const id = window.setInterval(() => tick((n) => n + 1), 30_000);
|
||||
return () => window.clearInterval(id);
|
||||
}, [until]);
|
||||
if (!until) return null;
|
||||
const minutes = Math.max(0, Math.ceil((Date.parse(until) - now) / 60_000));
|
||||
const minutes = Math.max(0, Math.ceil((Date.parse(until) - Date.now()) / 60_000));
|
||||
return { h: Math.floor(minutes / 60), m: minutes % 60 };
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user