From 497db1dbdb4dd4167c513a34669f2993b50bb4c5 Mon Sep 17 00:00:00 2001 From: David Ortner Date: Thu, 21 Sep 2023 00:52:40 +0200 Subject: [PATCH] #1049@patch: Allows global properties to be overwritten when using GlobalRegistrator from @happy-dom/global-registrator, as some global properties may behave different from the Happy DOM implementation. The properties will be restored when unregistering. --- packages/global-registrator/package.json | 2 +- .../src/GlobalRegistrator.ts | 34 ++++++++++++++----- .../test/react/React.test.tsx | 19 +++++++++++ 3 files changed, 46 insertions(+), 9 deletions(-) diff --git a/packages/global-registrator/package.json b/packages/global-registrator/package.json index 0ed7fcaa9..1ae7518ab 100644 --- a/packages/global-registrator/package.json +++ b/packages/global-registrator/package.json @@ -71,7 +71,7 @@ "lint": "eslint --ignore-path .gitignore --max-warnings 0 .", "lint:fix": "eslint --ignore-path .gitignore --max-warnings 0 --fix .", "test": "tsc --project ./test && node ../happy-dom/bin/change-file-extension.cjs --dir=./tmp --fromExt=.js --toExt=.cjs && node ./tmp/react/React.test.cjs", - "test:debug": "tsc --project ./test && node --inspect-brk ./tmp/react/React.test.js" + "test:debug": "tsc --project ./test && node ../happy-dom/bin/change-file-extension.cjs --dir=./tmp --fromExt=.js --toExt=.cjs && node --inspect-brk ./tmp/react/React.test.cjs" }, "dependencies": { "happy-dom": "0.0.0" diff --git a/packages/global-registrator/src/GlobalRegistrator.ts b/packages/global-registrator/src/GlobalRegistrator.ts index 1e78f9245..af5ab5ca6 100644 --- a/packages/global-registrator/src/GlobalRegistrator.ts +++ b/packages/global-registrator/src/GlobalRegistrator.ts @@ -1,39 +1,57 @@ import { GlobalWindow } from 'happy-dom'; +const IGNORE_LIST = ['undefined', 'NaN', 'global', 'globalThis', 'window', 'globalThis']; +const SELF_REFERING = ['self', 'top', 'parent', 'window']; + /** * */ export default class GlobalRegistrator { - private static registered = []; + private static registered: { [key: string]: string } | null = null; /** * Registers Happy DOM globally. */ public static register(): void { - if (this.registered.length) { + if (this.registered !== null) { throw new Error('Failed to register. Happy DOM has already been globally registered.'); } + const window = new GlobalWindow(); + + this.registered = {}; + for (const key of Object.keys(window)) { - if (global[key] === undefined && key !== 'undefined') { + if (global[key] !== window[key] && !IGNORE_LIST.includes(key)) { + this.registered[key] = global[key] !== window[key] ? global[key] : undefined; global[key] = window[key]; - this.registered.push(key); } } + + for (const key of SELF_REFERING) { + this.registered[key] = undefined; + global[key] = global; + } } /** * Registers Happy DOM globally. */ public static unregister(): void { - if (!this.registered.length) { + if (this.registered === null) { throw new Error( 'Failed to unregister. Happy DOM has not previously been globally registered.' ); } - while (this.registered.length) { - const key = this.registered.pop(); - delete global[key]; + + for (const key of Object.keys(this.registered)) { + if (this.registered[key] !== undefined) { + global[key] = this.registered[key]; + } else { + delete global[key]; + } } + + this.registered = null; } } diff --git a/packages/global-registrator/test/react/React.test.tsx b/packages/global-registrator/test/react/React.test.tsx index b5978c6f4..ff85ad463 100644 --- a/packages/global-registrator/test/react/React.test.tsx +++ b/packages/global-registrator/test/react/React.test.tsx @@ -3,6 +3,11 @@ import React from 'react'; import ReactDOM from 'react-dom'; import ReactComponent from './ReactComponent.js'; +const selfReferingProperties = ['self', 'top', 'parent', 'window']; + +// eslint-disable-next-line @typescript-eslint/consistent-type-assertions +const originalSetTimeout = global.setTimeout; + GlobalRegistrator.register(); const appElement = document.createElement('app'); @@ -24,7 +29,21 @@ function unmountReactComponent(): void { } } +if (global.setTimeout !== window.setTimeout) { + throw Error('Happy DOM function not registered.'); +} + +for (const property of selfReferingProperties) { + if (global[property] !== global) { + throw Error('Self refering property property was not registered.'); + } +} + mountReactComponent(); unmountReactComponent(); GlobalRegistrator.unregister(); + +if (global.setTimeout !== originalSetTimeout) { + throw Error('Global property was not restored.'); +}