Test Setup Recommendation ESM modules

The module system has changed to ESM modules with the 2025.06 release. Are there any recommendations for a testing setup with the new module system? Because Jest currently only supports ESM modules as an experimental feature.

In our project, we are currently experimenting with using Vitest in conjunction with Node.js mocking, and it appears that many of the A12 sub-projects are also switching to Vitest. Furthermore, we are considering using Redux Toolkit to create the store in the test setup, since it is now officially recommended that the createStore function from Redux itself is not used anymore.

Hi @niklas-sheer-ridge,

@jan-static-daemon and I have recently looked into the topic of testing setups in A12 for a training. As far as we were able to find there is no recommendation.

But we compiled an overview of some frontend components and their setup:

Component Test Framework e2e tests
Client + FE Mocha + c8 /
Overview Engine Vitest Playwright (previously Cypress)
Tree Engine Mocha + c8 Cypress
Widgets Vitest Playwright + Cypress
Kernel Mocha + Jest /
UAA Mocha /
Workflows Jest /
Print Engine Jest Playwright
Content Engine Vitest Cypress

(Though please take this information with a grain of salt. This is just based on what we found in Bitbucket and was not confirmed with the component teams.)

We also tried getting Jest tests to work with ESM and ran into a lot of issues, and from what we heard the component teams experienced the same. We have not tried Vitest or Mocha.

For the training, we tested out Node Test Runner - if you are interested I can add you to the repo. It has native ESM support and requires no additional dependencies.
However, the functionalities are rather limited (especially compared to Jest): No mocking, snapshots, or watch mode.
This means that e.g. testing React components is more complicated, and requires additional dependencies, like jsdom and testing-library (which pretty much defeats one of the major benefits). Because some of the A12 dependencies were referencing window, it was even necessary to mock the DOM for other tests.
So I would say it really depends what/how you test and your project, if Node Test Runner would be suitable.

I will ask some of the component teams to also add their input and experience regarding testing setup, so that you will get some more input.
And maybe some projects will also share their experiences :slight_smile:

Hi,

from Widgets, we are using Vitest for traditional React Unit test. We use Playwright for testing real browser behavior, in case a component need to work with real number to produce the correct behavior, e.g. measurement of height/width, or resizing using a mouse.

Recently I’m experimenting with Vitest browser mode, which is a drop-in replacement for traditional Vitest using jsdom. This mode run test in real browser, eliminating the need for mocking, and allow us to test usecases where previously we need Playwright for. The test run just as fast, so for Widgets project this is probably the way to go in the near future.

Hi,

in Workflows we had to do some adjustments regarding the test setup to run the tests with Jest.
I looked in our PRs and I will summarize the changes we did.

Let’s start with the package.json.
The following dependencies inside our package.json were added as dev dependencies:

"@jest/globals": "^29.7.0",
"@swc/core": "^1.11.16",
"@swc/jest": "^0.2.37",
"@types/jest": "^29.5.3",
"jest-environment-jsdom": "^29.7.0",
"jest-environment-node": "^29.7.0",

Babel related dependencies like “babel-jest” were removed.

Next a setup.ts was added inside the tests directory of our frontend-subproject workflows-core. This is the path to the setup.ts: “workflows-core/src/tests/setup.ts”
The content of the setups.ts:

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

globalThis.jest = jest;

The babel.config.js was removed and jest.config.js was renamed to jest.config.mjs. This is the relevant content:

export default {
    testEnvironment: "jsdom",

    extensionsToTreatAsEsm: [".ts", ".tsx"],
    transform: {
        "^.+\\.(t|j)s$": "@swc/jest"
    },
    // necessary because diagram-js and bpmn-js are not in the right format
    transformIgnorePatterns: ["node_modules/(?!bpmn-js|diagram-js|react-dnd|dnd-core|@react-dnd|antlr4)"],
    // Fixes .js extension resolution for ESM imports
    moduleNameMapper: { "^(\\.{1,2}/.*)\\.js$": "$1" },

    globals: {
        NODE_ENV: "test"
    },
    verbose: true,

    // The root directory that Jest should scan for tests and modules within
    rootDir: "src",

    // An array of directory names to be searched recursively up from the requiring module's location
    moduleDirectories: ["<rootDir>/../../node_modules", "<rootDir>/../node_modules"],

    // A list of paths to directories that Jest should use to search for files in
    roots: [
        "tests"
    ],

    // A list of paths to modules that run some code to configure or set up the testing framework before each test
    setupFilesAfterEnv: ["<rootDir>/tests/setup.ts"],

    [...]
};

Note that with […] I just want to hint that there are some other configurations in our files, that are not relevant for the ESM + Jest test setup.

Inside our package.json we had some scripts to run the tests. They looked like this:

"scripts": {
	[...]
	"test:unit": "jest unit --passWithNoTests",
    "test:int": "jest int --passWithNoTests",
	"test:all": "jest --passWithNoTests",
	"test:coverage": "jest --coverage",
	[...]
}

These scripts would not work and had to be adjusted like this:

"scripts": {
	[...]
	"test:unit": "npm run jest  -- unit --passWithNoTests",
	"test:int": "npm run jest  -- int --passWithNoTests",
	"test:all": "npm run jest -- --passWithNoTests",
	"test:coverage": "npm run jest -- --coverage",
	[...]
	"jest": "node --experimental-vm-modules ../node_modules/jest/bin/jest.js"
}

The flag “–experimental-vm-modules” is also needed to be configured in the “Run Configuration”, if you want to run tests via IntelliJ (probably works similar with different IDEs).
Open the “Run configuration” and paste “–experimental-vm-modules” in the “Node options” field.

Hi, in A12 Client & Form Engine we currently use mocha. About our testing experience, we found that:

  • using ESM in test files worked out of the box
  • running tests on the compiled JS files is more convenient (faster) than compiling the TS files “on the fly”
  • refactoring our test cases is way easier than setting up (complicated) workarounds for mocking ESM
  • its generally easier to refactor the code under test instead of changing its behavior with mocks (e.g. instead of mocking a function call, making it customizable from the outside and then passing a spy for tests)

Additionally, we found that for our repos we don’t really need any third party dependencies for tests:

  • assertion functionality like chai is builtin with node:assert/strict
  • spy/stub/mock functionality like sinon is builtin with mock via node:test

Going further, we plan on migrating from mocha to the builtin node:test runner, to eliminate our last test-only dependencies.

However, since node:test is rather new, we still need to evaluate whether it already provides tooling on the same level as our existing mocha setup (e.g. a VS Code extension to run and debug tests from the sidebar)