Problem
An npm run coverage / CI build job can fail even when all tests pass, because Vitest exits non-zero on 1 unhandled error:
⎯⎯ Uncaught Exception ⎯⎯
ReferenceError: window is not defined
❯ resolveUpdatePriority node_modules/react-dom/cjs/react-dom-client.development.js:1308:7
❯ requestUpdateLane react-dom-client.development.js:16345:11
❯ dispatchSetState react-dom-client.development.js:9126:14
❯ Timeout._onTimeout @mantine/core/esm/components/Transition/use-transition.mjs:61:13
❯ listOnTimeout node:internal/timers:585:17
This error originated in "src/components/groups/ServerRemoveConfirmModal/ServerRemoveConfirmModal.test.tsx"
Observed on PR #1759's CI (build run 30098049855) — 298 passed / 4426 tests passed, but Errors: 1 error → exit 1.
Root cause
ServerRemoveConfirmModal renders a Mantine Modal, which drives an open/close Transition via a setTimeout. The test doesn't wait for the transition to settle / unmount cleanly, so a pending Transition timer can fire after the happy-dom environment (and its window) has been torn down at the end of the run. The timer's dispatchSetState then hits resolveUpdatePriority, which reads window → ReferenceError. It's timing-dependent, so it only trips occasionally (it did not reproduce under a local npm run ci).
This is a general class issue — any component test rendering a Mantine Transition/Modal/Menu/Popover through renderWithMantine is exposed, not just this one modal.
Recommended fix
clients/web/src/test/renderWithMantine.tsx wraps in <MantineProvider theme={theme} defaultColorScheme="light"> without env="test". Setting env="test" makes Mantine render transitions synchronously (no setTimeout), which eliminates the whole class of post-teardown timer races in one place:
<MantineProvider theme={theme} defaultColorScheme="light" env="test">
Validate that no test relies on transition timing after the switch (a few may need a waitFor/findBy adjustment). Alternative/narrower options: give the modal transitionProps={{ duration: 0 }} in the test, or vi.useFakeTimers() + flush before unmount — but env="test" on the shared provider is the durable, repo-wide fix.
Impact
Low correctness risk (all assertions pass), but it can red an otherwise-green CI run, so worth making deterministic.
Problem
An
npm run coverage/ CIbuildjob can fail even when all tests pass, because Vitest exits non-zero on 1 unhandled error:Observed on PR #1759's CI (
buildrun 30098049855) —298 passed / 4426 tests passed, butErrors: 1 error→ exit 1.Root cause
ServerRemoveConfirmModalrenders a MantineModal, which drives an open/closeTransitionvia asetTimeout. The test doesn't wait for the transition to settle / unmount cleanly, so a pending Transition timer can fire after the happy-dom environment (and itswindow) has been torn down at the end of the run. The timer'sdispatchSetStatethen hitsresolveUpdatePriority, which readswindow→ReferenceError. It's timing-dependent, so it only trips occasionally (it did not reproduce under a localnpm run ci).This is a general class issue — any component test rendering a Mantine
Transition/Modal/Menu/PopoverthroughrenderWithMantineis exposed, not just this one modal.Recommended fix
clients/web/src/test/renderWithMantine.tsxwraps in<MantineProvider theme={theme} defaultColorScheme="light">withoutenv="test". Settingenv="test"makes Mantine render transitions synchronously (nosetTimeout), which eliminates the whole class of post-teardown timer races in one place:Validate that no test relies on transition timing after the switch (a few may need a
waitFor/findByadjustment). Alternative/narrower options: give the modaltransitionProps={{ duration: 0 }}in the test, orvi.useFakeTimers()+ flush before unmount — butenv="test"on the shared provider is the durable, repo-wide fix.Impact
Low correctness risk (all assertions pass), but it can red an otherwise-green CI run, so worth making deterministic.