Node v16.20.2
package.json
:
{
"private": true,
"name": "baffled-i-tell-you",
"version": "0.1.0",
"main": "index.js",
"devDependencies": {
"@babel/core": "^7.24.4",
"@babel/preset-env": "^7.24.4",
"@cloudflare/wrangler": "^1.12.0",
"babel-jest": "^29.7.0",
"jest": "^29.7.0",
"node-fetch": "^3.3.2"
},
"scripts": {
"test": "jest"
}
}
babel.config.js
:
module.exports = {
presets: [
[
'@babel/preset-env',
{
targets: {
node: 'current',
},
},
],
],
env: {
test: {
presets: [
['@babel/preset-env', { targets: { node: 'current' } }],
],
},
},
};
jest.config.js
:
const config = {
testEnvironment: './tests/config/FixGlobalsEnvironment.js',
transform: {
"^.+\\.js$": "babel-jest",
},
globals: {
addEventListener: () => {} // Used in main piece of code, but not for testing
}
};
module.exports = config;
FixGlboalsEnvironment.js
:
import fetch, {Headers, Request, Response} from "node-fetch";
import NodeEnvironment from 'jest-environment-node';
export default class FixGlobalsEnvironment extends NodeEnvironment {
constructor(...args) {
super(...args);
this.global.fetch = fetch;
this.global.Headers = Headers;
this.global.Request = Request;
this.global.Response = Response;
}
}
Result when executing yarn test
:
Error [ERR_REQUIRE_ESM]: require() of ES Module /home/joy/.../node_modules/node-fetch/src/index.js from /home/joy/.../tests/config/FixGlobalsEnvironment.js not supported.
Instead change the require of index.js in /home/joy/.../tests/config/FixGlobalsEnvironment.js to a dynamic import() which is available in all CommonJS modules.
6 | super(...args);
7 |
> 8 | this.global.fetch = fetch;
| ^
9 | this.global.Headers = Headers;
10 | this.global.Request = Request;
11 | this.global.Response = Response;
at Object.newLoader [as .js] (node_modules/pirates/lib/index.js:121:7)
at Object.<anonymous> (tests/config/FixGlobalsEnvironment.js:8:42)
at Module._compile (node_modules/pirates/lib/index.js:117:24)
at Object.newLoader [as .js] (node_modules/pirates/lib/index.js:121:7)
at async Promise.all (index 0)
at async ScriptTransformer.requireAndTranspileModule (node_modules/@jest/transform/build/ScriptTransformer.js:798:22)
I am confused at the message: Instead change the require of index.js in /home/joy/.../tests/config/FixGlobalsEnvironment.js to a dynamic import() which is available in all CommonJS modules.
Clearly I have messed up the configuration between Babel and Jest, but I don't know where I have messed it up.
I am using the polyfill solution because "node" environment doesn't have Headers
, Response
, Request
. Main code tested is not running in a browser environment. When I try running this with default "node" environment:
Using node-fetch
v2 I get this error: (when I have polyfilled it...)
TypeError: Class constructor Request cannot be invoked without 'new'
8 | headers.append('Content-Type', 'application/json');
9 |
> 10 | return new Request(url, {
| ^
11 | method: 'GET',
12 | headers
13 | });
at mockConstructor.<anonymous> (node_modules/jest-mock/build/index.js:794:25)
at getRequestMock (tests/index.test.js:10:16)
at getRequestMock (tests/index.test.js:23:45)
at call (tests/index.test.js:2:1)
at Generator.tryCatch (tests/index.test.js:2:1)
at Generator._invoke [as next] (tests/index.test.js:2:1)
The cherry on top, if I remove everything, and leave environment on "node" but run the test from my IDE (PHPStorm)... it works great, all test pass accordingly, no problems with set up.
I have added transformIgnorePatterns: [],
to my jest.config.js
as I saw others do, but it did not work.
I have removed also removed jest.config.js
in favour of only having babel.config.js
. Of course I lose the possibility of doing the polyfill in a nice way through jest.config.js
so I have to create a file that has this code (I import the file as the first import on my tests).
import fetch, {Headers, Request, Response} from "node-fetch";
global.addEventListener = () => {};
global.fetch = fetch;
global.Headers = Headers;
global.Request = Request;
global.Response = Response;
Still doesn't work, error is now:
TypeError: Class constructor Request cannot be invoked without 'new'
12 | headers.append('Content-Type', 'application/json');
13 |
> 14 | return new Request(url, {
| ^
15 | method: 'GET',
16 | headers
17 | });
at mockConstructor.<anonymous> (node_modules/jest-mock/build/index.js:794:25)
at getRequestMock (tests/index.test.js:14:16)
at getRequestMock (tests/index.test.js:27:45)
I never found a way to "properly" fix this, so instead I opted for this:
import fetch, { Headers, Request, Response } from "node-fetch"; import NodeEnvironment from "jest-environment-node";
// This file is FixGlobalsEnvironment.js
export default class FixGlobalsEnvironment extends NodeEnvironment {
constructor(...args) {
super(...args);
// Jest is not loading in the global object the Fetch API
// variables , so we have o load them manually
this.global.fetch = fetch;
this.global.Headers = Headers;
this.global.Request = Request;
this.global.Response = Response;
this.global.Response.redirect = (url, code) => new Response(url, code);
}
}
And in jest.config.js
/** @type {import('jest').Config} */
const config = {
testEnvironment: './tests/config/FixGlobalsEnvironment.js',
transform: {
"^.+\\.(js)$": "babel-jest",
},
transformIgnorePatterns: [],
globals: {
addEventListener: () => {}
}
};
module.exports = config;
Maybe I'll find another solution, but at the moment this is it.