diff --git a/packages/happy-dom/src/window/BrowserWindow.ts b/packages/happy-dom/src/window/BrowserWindow.ts index ca97a1689..bd5fde4dc 100644 --- a/packages/happy-dom/src/window/BrowserWindow.ts +++ b/packages/happy-dom/src/window/BrowserWindow.ts @@ -155,6 +155,7 @@ const ORIGINAL_CLEAR_TIMEOUT = clearTimeout; const ORIGINAL_SET_INTERVAL = setInterval; const ORIGINAL_CLEAR_INTERVAL = clearInterval; const ORIGINAL_QUEUE_MICROTASK = queueMicrotask; +const IS_NODE_JS_TIMEOUT_ENVIRONMENT = setTimeout.toString().includes('new Timeout'); /** * Browser window. @@ -978,6 +979,11 @@ export default class BrowserWindow extends EventTarget implements IBrowserWindow * @param id ID of the timeout. */ public clearTimeout(id: NodeJS.Timeout): void { + // We need to make sure that the ID is a Timeout object, otherwise Node.js might throw an error. + // This is only necessary if we are in a Node.js environment. + if (IS_NODE_JS_TIMEOUT_ENVIRONMENT && (!id || id.constructor.name !== 'Timeout')) { + return; + } this.#clearTimeout(id); this.#browserFrame[PropertySymbol.asyncTaskManager].endTimer(id); } @@ -1017,6 +1023,11 @@ export default class BrowserWindow extends EventTarget implements IBrowserWindow * @param id ID of the interval. */ public clearInterval(id: NodeJS.Timeout): void { + // We need to make sure that the ID is a Timeout object, otherwise Node.js might throw an error. + // This is only necessary if we are in a Node.js environment. + if (IS_NODE_JS_TIMEOUT_ENVIRONMENT && (!id || id.constructor.name !== 'Timeout')) { + return; + } this.#clearInterval(id); this.#browserFrame[PropertySymbol.asyncTaskManager].endTimer(id); } @@ -1051,6 +1062,11 @@ export default class BrowserWindow extends EventTarget implements IBrowserWindow * @param id ID. */ public cancelAnimationFrame(id: NodeJS.Immediate): void { + // We need to make sure that the ID is an Immediate object, otherwise Node.js might throw an error. + // This is only necessary if we are in a Node.js environment. + if (IS_NODE_JS_TIMEOUT_ENVIRONMENT && (!id || id.constructor.name !== 'Immediate')) { + return; + } global.clearImmediate(id); this.#browserFrame[PropertySymbol.asyncTaskManager].endImmediate(id); } diff --git a/packages/happy-dom/test/window/BrowserWindow.test.ts b/packages/happy-dom/test/window/BrowserWindow.test.ts index 8d921709d..bce9597dd 100644 --- a/packages/happy-dom/test/window/BrowserWindow.test.ts +++ b/packages/happy-dom/test/window/BrowserWindow.test.ts @@ -765,6 +765,10 @@ describe('BrowserWindow', () => { }); window.clearTimeout(timeoutId); }); + + it('Supports number values.', () => { + window.clearTimeout((-1)); + }); }); describe('setInterval()', () => { @@ -863,6 +867,10 @@ describe('BrowserWindow', () => { }); window.clearInterval(intervalId); }); + + it('Supports number values.', () => { + window.clearInterval((-1)); + }); }); describe('requestAnimationFrame()', () => { @@ -922,6 +930,10 @@ describe('BrowserWindow', () => { }); window.cancelAnimationFrame(timeoutId); }); + + it('Supports number values.', () => { + window.cancelAnimationFrame((-1)); + }); }); describe('matchMedia()', () => {