Summary
SUPPORTS_PROGRESS_EVENT is computed once at module load, but ProgressEvent is read from the bare
global at call time. If the global disappears between those two moments, the cached flag sends
execution into the branch that references it and throws ReferenceError: ProgressEvent is not defined — while ProgressEventPolyfill, which exists for exactly this case, is never reached.
Where
src/interceptors/XMLHttpRequest/utils/createEvent.ts (0.41.9 and still present in 0.42.3):
const SUPPORTS_PROGRESS_EVENT = typeof ProgressEvent !== 'undefined' // evaluated once, at import
// ...
const ProgressEventClass = SUPPORTS_PROGRESS_EVENT ? ProgressEvent : ProgressEventPolyfill // read later
How it happens
Under Vitest with the jsdom environment, each test file gets its own environment and it is torn
down when the file finishes.
- The module is imported while jsdom is alive, so
SUPPORTS_PROGRESS_EVENT caches true.
- A test file ends with a request still in flight.
- Vitest tears down the jsdom environment;
ProgressEvent is no longer a global.
- The response lands,
createEvent runs, takes the true branch, and references a global that is
gone.
The result is an unhandled rejection attributed to a test file whose tests all passed. Vitest counts
it as an error and fails the run, so a green suite reports red:
Tests 365 passed (365)
Errors 2 errors
ReferenceError: ProgressEvent is not defined
❯ createEvent .../XMLHttpRequest-Dw6Wm-UU.mjs:91:55
❯ XMLHttpRequestController.trigger .../XMLHttpRequest-Dw6Wm-UU.mjs:595:17
❯ XMLHttpRequestController.respondWith .../XMLHttpRequest-Dw6Wm-UU.mjs:407:9
It is timing-dependent, so it reproduces intermittently — ours appears on CI and not locally.
Suggested fix
Resolve the class at call time rather than caching the capability:
const ProgressEventClass = typeof ProgressEvent !== 'undefined' ? ProgressEvent : ProgressEventPolyfill
That keeps the React Native behaviour from #40 and also covers an environment where the global goes
away after import, at the cost of one typeof per event.
Versions
@mswjs/interceptors 0.41.9, via msw 2.15.0
- Verified the same code is present in 0.42.3
- Vitest 4.1.10,
environment: 'jsdom', Node 24
Workaround
Cancel in-flight requests before a test file's environment is torn down, so no response arrives after
teardown. That removes the trigger but not the cause — any consumer that leaves a request in flight
hits this.
Summary
SUPPORTS_PROGRESS_EVENTis computed once at module load, butProgressEventis read from the bareglobal at call time. If the global disappears between those two moments, the cached flag sends
execution into the branch that references it and throws
ReferenceError: ProgressEvent is not defined— whileProgressEventPolyfill, which exists for exactly this case, is never reached.Where
src/interceptors/XMLHttpRequest/utils/createEvent.ts(0.41.9 and still present in 0.42.3):How it happens
Under Vitest with the
jsdomenvironment, each test file gets its own environment and it is torndown when the file finishes.
SUPPORTS_PROGRESS_EVENTcachestrue.ProgressEventis no longer a global.createEventruns, takes thetruebranch, and references a global that isgone.
The result is an unhandled rejection attributed to a test file whose tests all passed. Vitest counts
it as an error and fails the run, so a green suite reports red:
It is timing-dependent, so it reproduces intermittently — ours appears on CI and not locally.
Suggested fix
Resolve the class at call time rather than caching the capability:
That keeps the React Native behaviour from #40 and also covers an environment where the global goes
away after import, at the cost of one
typeofper event.Versions
@mswjs/interceptors0.41.9, viamsw2.15.0environment: 'jsdom', Node 24Workaround
Cancel in-flight requests before a test file's environment is torn down, so no response arrives after
teardown. That removes the trigger but not the cause — any consumer that leaves a request in flight
hits this.