From 44d48efe53dcf2939c0b1b83d54dd52e9811ff32 Mon Sep 17 00:00:00 2001 From: lojhan Date: Sun, 5 Apr 2026 17:51:09 -0300 Subject: [PATCH] fix: preserve native Event/dispatchEvent in jsdom setup for Deno compatibility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Under isolation:process, Deno spawns a per-test subprocess and fires dispatchLoadEvent() on exit using its own native Event constructor. setupJsdomEnvironment() was unconditionally overwriting globalThis.Event with jsdom's implementation, causing setTarget() to receive undefined: TypeError: Cannot set properties of undefined (setting 'target') at setTarget (ext:deno_web/02_event.js:97:29) Apply the same save/restore pattern already present in setupHappyDomEnvironment(): capture the runtime's native Event, CustomEvent, and dispatchEvent before constructing JSDOM, then restore them after. isolation:none is unaffected as no subprocess teardown occurs. Fixes all 24 matrix combinations (24/24 ✅). --- src/dom-env.ts | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/dom-env.ts b/src/dom-env.ts index 34859c5..81f2e9d 100644 --- a/src/dom-env.ts +++ b/src/dom-env.ts @@ -82,6 +82,15 @@ export const setupJsdomEnvironment = async ( ); } + // Save native event primitives before jsdom replaces them. + // Deno's runtime teardown fires dispatchLoadEvent() on process exit using + // its own native Event constructor — if jsdom's Event is installed instead, + // setTarget() receives undefined and throws "Cannot set properties of + // undefined (setting 'target')". + const existingDispatchEvent = globalThis.dispatchEvent; + const existingEvent = globalThis.Event; + const existingCustomEvent = globalThis.CustomEvent; + const { JSDOM } = mod; const dom = new JSDOM('', { url: options.runtimeOptions.domUrl, @@ -96,11 +105,20 @@ export const setupJsdomEnvironment = async ( defineGlobal('Node', dom.window.Node); defineGlobal('Text', dom.window.Text); defineGlobal('SVGElement', dom.window.SVGElement); - defineGlobal('Event', dom.window.Event); - defineGlobal('CustomEvent', dom.window.CustomEvent); defineGlobal('MutationObserver', dom.window.MutationObserver); defineGlobal('requestAnimationFrame', dom.window.requestAnimationFrame); defineGlobal('cancelAnimationFrame', dom.window.cancelAnimationFrame); + + // Prefer the runtime's native Event constructors so that Deno's internal + // event dispatch (e.g. load/beforeunload) continues to work correctly. + defineGlobal('Event', typeof existingEvent === 'function' ? existingEvent : dom.window.Event); + defineGlobal('CustomEvent', typeof existingCustomEvent === 'function' ? existingCustomEvent : dom.window.CustomEvent); + + if (typeof existingDispatchEvent === 'function') { + globalThis.dispatchEvent = existingDispatchEvent; + } else { + globalThis.dispatchEvent = dom.window.dispatchEvent.bind(dom.window) as unknown as typeof globalThis.dispatchEvent; + } } applyReactActEnvironment(Boolean(options.enableReactActEnvironment));