Skip to content

Latest commit

 

History

History
88 lines (58 loc) · 3.89 KB

ECMAScriptModules.md

File metadata and controls

88 lines (58 loc) · 3.89 KB
id title
ecmascript-modules
ECMAScript Modules

Jest ships with experimental support for ECMAScript Modules (ESM).

Note that due to its experimental nature there are many bugs and missing features in Jest's implementation, both known and unknown. You should check out the tracking issue and the label on the issue tracker for the latest status.

Also note that the APIs Jest uses to implement ESM support is still considered experimental by Node (as of version 18.8.0).

With the warnings out of the way, this is how you activate ESM support in your tests.

  1. Ensure you either disable code transforms by passing transform: {} or otherwise configure your transformer to emit ESM rather than the default CommonJS (CJS).

  2. Execute node with --experimental-vm-modules, e.g. node --experimental-vm-modules node_modules/jest/bin/jest.js or NODE_OPTIONS=--experimental-vm-modules npx jest etc..

    On Windows, you can use cross-env to be able to set environment variables.

    If you use Yarn, you can use yarn node --experimental-vm-modules $(yarn bin jest). This command will also work if you use Yarn Plug'n'Play.

  3. Beyond that, we attempt to follow node's logic for activating "ESM mode" (such as looking at type in package.json or .mjs files), see their docs for details.

  4. If you want to treat other file extensions (such as .jsx or .ts) as ESM, please use the extensionsToTreatAsEsm option.

Differences between ESM and CommonJS

Most of the differences are explained in Node's documentation, but in addition to the things mentioned there, Jest injects a special variable into all executed files - the jest object. To access this object in ESM, you need to import it from the @jest/globals module or use import.meta.

import {jest} from '@jest/globals';

jest.useFakeTimers();

// etc.

// alternatively
import.meta.jest.useFakeTimers();

// jest === import.meta.jest => true

Additionally, since ESM evaluates static import statements before looking at the code, hoisting on jest.mock calls that happens in CJS modules won't work in ESM. To mock modules in ESM, you need to use dynamic import() after jest.mock calls to load the mocked modules, same applies to modules which have to load the mocked modules.

const { BrowserWindow, app } = require('electron');

// etc.

module.exports = { example };
import { createRequire } from 'node:module';
import { jest } from '@jest/globals';

const require = createRequire(import.meta.url);

jest.mock('electron', () => ({
  app: {
    on: jest.fn(),
    whenReady: jest.fn(() => Promise.resolve()),
  },
  BrowserWindow: jest.fn().mockImplementation(() => ({
    // partial mocks.
  }))
}));

const { BrowserWindow } = require('electron');
const exported = require('main.cjs');

// etc.

Please note that we currently don't support jest.mock in a clean way in ESM, but that is something we intend to add proper support for in the future. Follow this issue for updates.

In short, jest.unstable_mockModule is needed to mock ESM for now. Its usage is essentially the same as jest.mock. See the example below:

import { jest } from '@jest/globals';

jest.unstable_mockModule('node:child_process', () => ({
  execSync: jest.fn(),
  // etc.
}));

const { execSync } = await import('node:child_process');

// etc.