From bc856ab2af9bb8089ff632521ee834e38d389aed Mon Sep 17 00:00:00 2001 From: npeter83 Date: Sun, 4 Oct 2026 20:09:58 +0200 Subject: [PATCH] test: warm cold dynamic imports before the timed cases Under a full coverage run on every core, a test file's first cold import (0.6-0.8 s alone) outran the 5 s case timeout and the half-done import failed every later case in the file: the vitest lane went red in 2 of 5 runs with no defect. A beforeAll warm-up in the 4 files of that pattern; 5 of 5 full runs green after. --- frontend/src/lib/accountPinnedStores.dom.test.ts | 11 ++++++++++- frontend/src/lib/diagnostics.dom.test.ts | 11 ++++++++++- frontend/src/lib/diagnostics.session.dom.test.ts | 10 +++++++++- frontend/src/lib/verifyEntry.dom.test.tsx | 10 +++++++++- 4 files changed, 38 insertions(+), 4 deletions(-) diff --git a/frontend/src/lib/accountPinnedStores.dom.test.ts b/frontend/src/lib/accountPinnedStores.dom.test.ts index 04e9ab0c..7f69c363 100644 --- a/frontend/src/lib/accountPinnedStores.dom.test.ts +++ b/frontend/src/lib/accountPinnedStores.dom.test.ts @@ -4,7 +4,7 @@ // tab (bookmark, typed URL: no pin in sessionStorage) that is before BootGate pins the account, so they // read as nobody's; they must read again on the pin. The notification store used to keep its empty, // nobody's history, and the tab's first notice replaced the account's stored history with it. -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const entry = (id: number, message: string) => ({ id, @@ -24,6 +24,15 @@ const storedMessages = (account: number) => }[] ).map((n) => n.message); +// Warm the transform cache once (see verifyEntry.dom.test.tsx): the first cold import costs ~0.8 s +// alone and outran the 5 s case timeout under a full coverage run. Transformed code outlives +// vi.resetModules, so each case still gets fresh module instances, only faster. +beforeAll(async () => { + await import("./notifications"); + await import("./hints"); + await import("./api/core"); +}, 60_000); + beforeEach(() => { localStorage.clear(); sessionStorage.clear(); diff --git a/frontend/src/lib/diagnostics.dom.test.ts b/frontend/src/lib/diagnostics.dom.test.ts index 57ff9feb..e4219a09 100644 --- a/frontend/src/lib/diagnostics.dom.test.ts +++ b/frontend/src/lib/diagnostics.dom.test.ts @@ -3,7 +3,7 @@ // E22 S5: the consented level applied to the tab, and the stream it opens. The module keeps its state // at module level (what is applied, the queue), so every test imports a fresh copy — and the log // from that same copy, or it would be reading a different ring. -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const sent: unknown[][] = []; const unloaded: unknown[][] = []; @@ -24,6 +24,15 @@ vi.mock("./api", async () => { }; }); +// Warm the transform cache once (see verifyEntry.dom.test.tsx): the first cold import costs ~0.8 s +// alone and outran the 5 s case timeout under a full coverage run. Transformed code outlives +// vi.resetModules, so each case still gets fresh module instances, only faster. +beforeAll(async () => { + await import("./diagnostics"); + await import("./log"); + await import("./api"); +}, 60_000); + beforeEach(() => { vi.useFakeTimers(); vi.resetModules(); diff --git a/frontend/src/lib/diagnostics.session.dom.test.ts b/frontend/src/lib/diagnostics.session.dom.test.ts index 0cb2ec0a..96e2d8f4 100644 --- a/frontend/src/lib/diagnostics.session.dom.test.ts +++ b/frontend/src/lib/diagnostics.session.dom.test.ts @@ -4,7 +4,7 @@ // consent), never lowers a raised one, tags every event it keeps, and its last seconds reach the // server before Stop. Its own file: `lib/diagnostics` and `lib/log` keep module-level state, so every // test imports fresh copies of both, as diagnostics.dom.test does. -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const sent: { event: string; debug_session_id?: string }[][] = []; @@ -45,6 +45,14 @@ const recording = (level: "DEBUG" | "TRACE", minutes = 60, over = {}) => ({ ...over, }); +// Warm the transform cache once (see verifyEntry.dom.test.tsx): the first cold import costs ~0.8 s +// alone and outran the 5 s case timeout under a full coverage run. Transformed code outlives +// vi.resetModules, so each case still gets fresh module instances, only faster. +beforeAll(async () => { + await import("./diagnostics"); + await import("./log"); +}, 60_000); + let current: typeof import("./diagnostics") | null = null; beforeEach(() => { vi.useFakeTimers(); diff --git a/frontend/src/lib/verifyEntry.dom.test.tsx b/frontend/src/lib/verifyEntry.dom.test.tsx index 728c00c8..3b20de3c 100644 --- a/frontend/src/lib/verifyEntry.dom.test.tsx +++ b/frontend/src/lib/verifyEntry.dom.test.tsx @@ -7,9 +7,17 @@ // // entryUrl snapshots window.location at IMPORT time, so each case resets the module registry and sets // the URL before importing — that is the behaviour under test, not a workaround. -import { describe, it, expect, afterEach, vi } from "vitest"; +import { describe, it, expect, afterEach, beforeAll, vi } from "vitest"; import { render, screen, cleanup, act } from "@testing-library/react"; +// The first import of the hook transforms its whole graph (./api and everything under it): ~0.6 s +// alone, past the 5 s case timeout when the full suite runs with coverage on every core — and the +// case it timed out in left a half-done import that failed every case after it. Transformed code +// outlives vi.resetModules, so paying it once here, with room, leaves each case its warm ~60 ms. +beforeAll(async () => { + await import("./useVerifyEntry"); +}, 60_000); + afterEach(() => { cleanup(); vi.resetModules();