test(web): disable Mantine transition timers in the unit env

CI failed with an unhandled `ReferenceError: window is not defined` thrown from
a Mantine `Transition` `setTimeout` (`use-transition.mjs`) firing after the test
environment was torn down — every test passed, but the leaked timer failed the
run. happy-dom has no `matchMedia`, so `useReducedMotion` resolves to
"motion allowed" and every Transition (ScreenStage, Modal, …) schedules real
enter/exit timers; a timer that outlives its test throws post-teardown. This
surfaced now because MonitoringScreen started wrapping its screens in
ScreenStage (a Transition) where it previously rendered them directly.

Mock `window.matchMedia` in the test setup to report
`prefers-reduced-motion: reduce`, so Mantine transitions render instantly (no
timers). All other queries return `false`, matching the prior absent-matchMedia
behavior, so `useMediaQuery`-driven layout is unchanged. Full unit suite passes
with no unhandled errors across repeated runs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BrihGWcrM9JGRyu41nzZYw
This commit is contained in:
cliffhall 2026-07-10 22:52:48 -04:00
commit 1e6718e2fd

View file

@ -57,6 +57,32 @@ Object.defineProperty(globalThis, "fetch", {
),
});
// happy-dom doesn't implement `matchMedia`, so Mantine's `useReducedMotion`
// resolves to "motion allowed" and every `Transition` (ScreenStage, Modal, …)
// schedules real enter/exit `setTimeout`s. A timer that outlives its test fires
// after the environment is torn down and throws `window is not defined` from
// deep in react-dom — an unhandled error that fails the run even though every
// test passed. Report `prefers-reduced-motion: reduce` so Mantine transitions
// render instantly (no timers); all other queries stay `false`, matching the
// prior (absent-matchMedia) behavior so `useMediaQuery`-driven layout is
// unchanged. Tests that need specific media results still mock `@mantine/hooks`
// or stub `matchMedia` themselves.
Object.defineProperty(window, "matchMedia", {
configurable: true,
writable: true,
value: (query: string): MediaQueryList =>
({
matches: /prefers-reduced-motion/.test(query),
media: query,
onchange: null,
addEventListener: () => {},
removeEventListener: () => {},
addListener: () => {},
removeListener: () => {},
dispatchEvent: () => false,
}) as unknown as MediaQueryList,
});
afterEach(() => {
cleanup();
window.localStorage.clear();