node.jsjestjsnode-fetch

Jest, typeOfEnv is node, polyfill with node-fetch, complains about require() when clearly using import


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.


Solution

  • 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.