From 6cda892e71a3b58cd95714905e9a8ca033fece03 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Thu, 24 Aug 2023 09:29:53 +0200 Subject: [PATCH 1/6] fix: make sure `atob` and `btoa` are writeable --- CHANGELOG.md | 3 ++ packages/jest-environment-node/src/index.ts | 52 +++++++++++++++------ 2 files changed, 40 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8354171b0ae3..75b99aed7baa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,12 +5,15 @@ ### Fixes - `[jest-core]` Fix typo in `scheduleAndRun` performance marker ([#14434](https://github.com/jestjs/jest/pull/14434)) +- `[jest-environment-node]` Make sure `atob` and `btoa` are writeable in Node 20 - `[jest-worker]` Additional error wrapper for `parentPort.postMessage` to fix unhandled `DataCloneError`. ([#14437](https://github.com/jestjs/jest/pull/14437)) ### Chore & Maintenance ## 29.6.3 +### Fixes + - `[expect, @jest/expect-utils]` `ObjectContaining` support `sumbol` as key ([#14414](https://github.com/jestjs/jest/pull/14414)) - `[expect]` Remove `@types/node` from dependencies ([#14385](https://github.com/jestjs/jest/pull/14385)) - `[jest-core]` Use workers in watch mode by default to avoid crashes ([#14059](https://github.com/facebook/jest/pull/14059) & [#14085](https://github.com/facebook/jest/pull/14085)). diff --git a/packages/jest-environment-node/src/index.ts b/packages/jest-environment-node/src/index.ts index 1b387aaffee5..3d677f20f6b6 100644 --- a/packages/jest-environment-node/src/index.ts +++ b/packages/jest-environment-node/src/index.ts @@ -27,16 +27,20 @@ const denyList = new Set([ 'GLOBAL', 'root', 'global', + 'globalThis', 'Buffer', 'ArrayBuffer', 'Uint8Array', + 'crypto', // if env is loaded within a jest test 'jest-symbol-do-not-touch', ]); +type GlobalProperties = Array; + const nodeGlobals = new Map( - Object.getOwnPropertyNames(globalThis) - .filter(global => !denyList.has(global)) + (Object.getOwnPropertyNames(globalThis) as GlobalProperties) + .filter(global => !denyList.has(global as string)) .map(nodeGlobalsKey => { const descriptor = Object.getOwnPropertyDescriptor( globalThis, @@ -76,7 +80,9 @@ export default class NodeEnvironment implements JestEnvironment { ) as Global.Global; this.global = global; - const contextGlobals = new Set(Object.getOwnPropertyNames(global)); + const contextGlobals = new Set( + Object.getOwnPropertyNames(global) as GlobalProperties, + ); for (const [nodeGlobalsKey, descriptor] of nodeGlobals) { if (!contextGlobals.has(nodeGlobalsKey)) { if (descriptor.configurable) { @@ -84,20 +90,34 @@ export default class NodeEnvironment implements JestEnvironment { configurable: true, enumerable: descriptor.enumerable, get() { - // @ts-expect-error: no index signature - const val = globalThis[nodeGlobalsKey] as unknown; + const attributes: PropertyDescriptor = { + configurable: true, + }; + + if ('value' in descriptor) { + attributes.value = descriptor.value; + } + + if ('writable' in descriptor) { + attributes.writable = descriptor.writable; + } + + if ('enumerable' in descriptor) { + attributes.enumerable = descriptor.enumerable; + } + + if ('get' in descriptor) { + attributes.get = descriptor.get; + } + + if ('set' in descriptor) { + attributes.set = descriptor.set; + } // override lazy getter - Object.defineProperty(global, nodeGlobalsKey, { - configurable: true, - enumerable: descriptor.enumerable, - value: val, - writable: - descriptor.writable === true || - // Node 19 makes performance non-readable. This is probably not the correct solution. - nodeGlobalsKey === 'performance', - }); - return val; + Object.defineProperty(global, nodeGlobalsKey, attributes); + + return globalThis[nodeGlobalsKey]; }, set(val: unknown) { // override lazy getter @@ -135,6 +155,8 @@ export default class NodeEnvironment implements JestEnvironment { // different than the global one used by users in tests. This makes sure the // same constructor is referenced by both. global.Uint8Array = Uint8Array; + // for some reason, this cannot be assigned from its descriptor - ends up with some `null` error + global.crypto = crypto; installCommonGlobals(global, projectConfig.globals); From 69eda2abe8816977e89f868d41f5c7f75d4b1165 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Thu, 24 Aug 2023 09:48:23 +0200 Subject: [PATCH 2/6] conditional --- packages/jest-environment-node/src/index.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/jest-environment-node/src/index.ts b/packages/jest-environment-node/src/index.ts index 3d677f20f6b6..d6f157ca4f54 100644 --- a/packages/jest-environment-node/src/index.ts +++ b/packages/jest-environment-node/src/index.ts @@ -155,8 +155,10 @@ export default class NodeEnvironment implements JestEnvironment { // different than the global one used by users in tests. This makes sure the // same constructor is referenced by both. global.Uint8Array = Uint8Array; - // for some reason, this cannot be assigned from its descriptor - ends up with some `null` error - global.crypto = crypto; + if (typeof crypto !== 'undefined') { + // for some reason, this cannot be assigned from its descriptor - ends up with some `null` error + global.crypto = crypto; + } installCommonGlobals(global, projectConfig.globals); From 4f9b3575694063d2925296e8078c757b7d92f3e9 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Thu, 24 Aug 2023 10:40:12 +0200 Subject: [PATCH 3/6] always writeable --- e2e/override-globals/__tests__/index.js | 7 ++++ packages/jest-environment-node/src/index.ts | 37 ++++++--------------- 2 files changed, 17 insertions(+), 27 deletions(-) diff --git a/e2e/override-globals/__tests__/index.js b/e2e/override-globals/__tests__/index.js index 59448c4a07a4..0036da40cc86 100644 --- a/e2e/override-globals/__tests__/index.js +++ b/e2e/override-globals/__tests__/index.js @@ -27,4 +27,11 @@ describe('parent', () => { }, 10); }); }); + + it('can override atob and btoa', () => { + global.atob = () => 'hello'; + global.btoa = () => 'there'; + + expect(`${atob()} ${btoa()}`).toBe('hello there'); + }); }); diff --git a/packages/jest-environment-node/src/index.ts b/packages/jest-environment-node/src/index.ts index d6f157ca4f54..ec0f77463f14 100644 --- a/packages/jest-environment-node/src/index.ts +++ b/packages/jest-environment-node/src/index.ts @@ -90,41 +90,24 @@ export default class NodeEnvironment implements JestEnvironment { configurable: true, enumerable: descriptor.enumerable, get() { - const attributes: PropertyDescriptor = { - configurable: true, - }; - - if ('value' in descriptor) { - attributes.value = descriptor.value; - } - - if ('writable' in descriptor) { - attributes.writable = descriptor.writable; - } - - if ('enumerable' in descriptor) { - attributes.enumerable = descriptor.enumerable; - } - - if ('get' in descriptor) { - attributes.get = descriptor.get; - } - - if ('set' in descriptor) { - attributes.set = descriptor.set; - } + const value = globalThis[nodeGlobalsKey]; // override lazy getter - Object.defineProperty(global, nodeGlobalsKey, attributes); + Object.defineProperty(global, nodeGlobalsKey, { + configurable: true, + enumerable: descriptor.enumerable, + value, + writable: true, + }); - return globalThis[nodeGlobalsKey]; + return value; }, - set(val: unknown) { + set(value: unknown) { // override lazy getter Object.defineProperty(global, nodeGlobalsKey, { configurable: true, enumerable: descriptor.enumerable, - value: val, + value, writable: true, }); }, From 0e5dbedce42393d64b3db9ec3fafb451e8a5fc67 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Thu, 24 Aug 2023 10:41:39 +0200 Subject: [PATCH 4/6] simpler --- packages/jest-environment-node/src/index.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/jest-environment-node/src/index.ts b/packages/jest-environment-node/src/index.ts index ec0f77463f14..13be75062742 100644 --- a/packages/jest-environment-node/src/index.ts +++ b/packages/jest-environment-node/src/index.ts @@ -31,7 +31,6 @@ const denyList = new Set([ 'Buffer', 'ArrayBuffer', 'Uint8Array', - 'crypto', // if env is loaded within a jest test 'jest-symbol-do-not-touch', ]); @@ -138,10 +137,6 @@ export default class NodeEnvironment implements JestEnvironment { // different than the global one used by users in tests. This makes sure the // same constructor is referenced by both. global.Uint8Array = Uint8Array; - if (typeof crypto !== 'undefined') { - // for some reason, this cannot be assigned from its descriptor - ends up with some `null` error - global.crypto = crypto; - } installCommonGlobals(global, projectConfig.globals); From f6f71290bd96b78dd551842b33cd6c44d633c242 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Thu, 24 Aug 2023 11:39:21 +0200 Subject: [PATCH 5/6] lint --- e2e/override-globals/__tests__/index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/e2e/override-globals/__tests__/index.js b/e2e/override-globals/__tests__/index.js index 0036da40cc86..91f6dd01c9db 100644 --- a/e2e/override-globals/__tests__/index.js +++ b/e2e/override-globals/__tests__/index.js @@ -29,7 +29,9 @@ describe('parent', () => { }); it('can override atob and btoa', () => { + // eslint-disable-next-line no-restricted-globals global.atob = () => 'hello'; + // eslint-disable-next-line no-restricted-globals global.btoa = () => 'there'; expect(`${atob()} ${btoa()}`).toBe('hello there'); From db2a544a5e1c400b1b9514d7e2f92fdcc06b679e Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Thu, 24 Aug 2023 12:25:30 +0200 Subject: [PATCH 6/6] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 75b99aed7baa..72d350f52f52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ ### Fixes - `[jest-core]` Fix typo in `scheduleAndRun` performance marker ([#14434](https://github.com/jestjs/jest/pull/14434)) -- `[jest-environment-node]` Make sure `atob` and `btoa` are writeable in Node 20 +- `[jest-environment-node]` Make sure `atob` and `btoa` are writeable in Node 20 ([#14446](https://github.com/jestjs/jest/pull/14446)) - `[jest-worker]` Additional error wrapper for `parentPort.postMessage` to fix unhandled `DataCloneError`. ([#14437](https://github.com/jestjs/jest/pull/14437)) ### Chore & Maintenance