inspector/clients/web/src/test
Repository files (latest commit first)
Filename Latest commit message Latest commit date
cliffhall 1e0f94bb31 fix(test): clear leaked timers so one cannot fail an unrelated CI run
A window.setTimeout that outlives its test file fires after happy-dom disposes
that file's window; React's dispatchSetState then throws an uncaught
ReferenceError: window is not defined. Vitest attributes it to whichever file
was running, so a single leak fails the ENTIRE run with every test passing —
landing on an innocent file under a step named "Enforce per-file coverage gate."
Seen twice in CI, both on docs-only branches.

Not a Mantine bug, and not a badly-written test. Both hooks do clear on unmount
(useTransition's clearAllTimeouts, useLockScroll's effect cleanup). It is a rAF
race: clearAllTimeouts cancels the *pending* rAF, but the transition schedules
rAF -> rAF -> setTimeout, so if the inner callback is already in flight when the
unmount lands, cancelAnimationFrame is a no-op and that callback then schedules
a timer after cleanup has run. Nothing owns it. Needs a loaded machine, which is
why it only ever appeared in CI.

Track every timer and clear whatever is still outstanding after cleanup(). The
ordering is load-bearing: legitimate unmount cleanups get their turn first, so
only true leaks are dropped. Fake timers pass through untracked, which is
correct — a fake timer cannot outlive the environment.

Also corrects the claim, in AGENTS.md and in setup.ts's own comment, that
env="test" prevents this. It does not: env is read only by Transition.mjs at its
render branch, while useTransition runs before that check (hooks cannot be
conditional) and still schedules real timers. Measured — opening a <Modal>
through renderWithMantine schedules three 200ms timers. Believing that guarantee
is why this went unexamined.

The regression test is mutation-verified: with the net disabled it fails, with
it restored it passes. It deliberately does not assert that Mantine schedules no
timers — it does, and that is normal; the contract is that none survive.

Closes #1984

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HDdo1rNRVQnVRRdsqSrvG3
Signed-off-by: cliffhall <cliff@futurescale.com>
2026-08-12 00:16:05 -04:00
..
core Merge pull request #1946 from modelcontextprotocol/v2/fix/firefox-sse-stream-hang 2026-08-11 11:22:34 -04:00
fixtures Thread cacheMode through managed list refresh via SDK cache-aware verbs (#1721) (#1723) 2026-07-19 01:06:31 -04:00
integration Merge pull request #1946 from modelcontextprotocol/v2/fix/firefox-sse-stream-hang 2026-08-11 11:22:34 -04:00
leakedTimers.test.tsx fix(test): clear leaked timers so one cannot fail an unrelated CI run 2026-08-12 00:16:05 -04:00
PreviewAnnotations.stories.tsx chore(storybook): derive the primary shade from the theme in the guard 2026-08-10 23:54:44 -04:00
renderWithMantine.tsx test(web): settle real-timer Transitions so the coverage gate can't leak 'window is not defined' (#1786) (#1793) 2026-07-26 15:49:33 -04:00
scrollAreaStoryAssertions.ts refactor(web): rename "History" tab/feature to "Protocol" (#1623) 2026-07-09 19:04:54 -04:00
setup.ts fix(test): clear leaked timers so one cannot fail an unrelated CI run 2026-08-12 00:16:05 -04:00