Skip to content

Commit

Permalink
fix: make sure atob and btoa are writeable (#14446)
Browse files Browse the repository at this point in the history
  • Loading branch information
SimenB committed Aug 24, 2023
1 parent 834d9d6 commit c2ef4df
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 13 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Expand Up @@ -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)).
Expand Down
9 changes: 9 additions & 0 deletions e2e/override-globals/__tests__/index.js
Expand Up @@ -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');
});
});
28 changes: 15 additions & 13 deletions packages/jest-environment-node/src/index.ts
Expand Up @@ -27,16 +27,19 @@ const denyList = new Set([
'GLOBAL',
'root',
'global',
'globalThis',
'Buffer',
'ArrayBuffer',
'Uint8Array',
// if env is loaded within a jest test
'jest-symbol-do-not-touch',
]);

type GlobalProperties = Array<keyof typeof globalThis>;

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,
Expand Down Expand Up @@ -76,35 +79,34 @@ export default class NodeEnvironment implements JestEnvironment<Timer> {
) 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) {
Object.defineProperty(global, nodeGlobalsKey, {
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,
});
},
Expand Down

0 comments on commit c2ef4df

Please sign in to comment.