diff --git a/CHANGELOG.md b/CHANGELOG.md index 8354171b0ae3..72d350f52f52 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 ([#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 ## 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/e2e/override-globals/__tests__/index.js b/e2e/override-globals/__tests__/index.js index 59448c4a07a4..91f6dd01c9db 100644 --- a/e2e/override-globals/__tests__/index.js +++ b/e2e/override-globals/__tests__/index.js @@ -27,4 +27,13 @@ describe('parent', () => { }, 10); }); }); + + 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'); + }); }); diff --git a/packages/jest-environment-node/src/index.ts b/packages/jest-environment-node/src/index.ts index 1b387aaffee5..13be75062742 100644 --- a/packages/jest-environment-node/src/index.ts +++ b/packages/jest-environment-node/src/index.ts @@ -27,6 +27,7 @@ const denyList = new Set([ 'GLOBAL', 'root', 'global', + 'globalThis', 'Buffer', 'ArrayBuffer', 'Uint8Array', @@ -34,9 +35,11 @@ const denyList = new Set([ '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 +79,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,27 +89,24 @@ 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 value = globalThis[nodeGlobalsKey]; // 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', + value, + writable: true, }); - return val; + + 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, }); },