reactjsnode.jstypescriptjestjs

"Cannot find module" error when running jest test


I have react application that use Milkdown editor. I would like to test this application using jest. Whenever I run jest test that imports Milkdown modules I got following error:

Cannot find module '@milkdown/core' from 'src/MilkdownEditor.tsx'

I think the problem is related to the fact that @milkdown/core is an esm module.

Could someone help me to understand how I should configure jest in this particular case.

Current jest configuration:

import type { Config } from 'jest'

const config: Config = {
  preset: 'ts-jest',
  testEnvironment: 'jsdom',
  setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
  injectGlobals: true,
}

export default config

Sample test:

import React from 'react'
import { render } from '@testing-library/react'
import MilkdownEditor from '../src/MilkdownEditor'

test('test milkdown editor', () => {
  render(<MilkdownEditor />)
})

I created minimal project that reproduces the issue

I've already tried to apply some suggestions (mostly related to configuring babel), but nothing helps.


Solution

  • After some further examination I came up with two solutions to this problem:

    Solution 1

    As suggested in jest documentation using --experimental-vm-modules works fine. For this to work following jest.config.js is required:

    import type { Config } from 'jest'
    
    const config: Config = {
      preset: 'ts-jest/presets/default-esm',
      testEnvironment: 'jsdom',
      setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
      injectGlobals: true,
    }
    
    export default config
    

    and to make it easier to run tests following npm script in script section of package.json can be defined:

    "scripts": {
      "test": "cross-env NODE_OPTIONS=--experimental-vm-modules jest"
    },
    

    Full implementation of this solution

    Solution 2

    This solution base on babel @babel/plugin-transform-modules-commonjs. This is common solution that can be found around. However, in this particular scenario additional module name mapping in jest configuration (moduleNameMapper) is required. For now I can't explain what is the reason for this. Additionally, it is necessary to apply babel transformations to node_modules which is done by overriding transformIgnorePatters. So jest.config.js looks as follows:

    import type { Config } from 'jest'
    
    const config: Config = {
      preset: 'ts-jest',
      transform: {
        "^.+\\.[t]sx?$": "ts-jest",
        "^.+\\.[j]sx?$": "babel-jest"
      },
      transformIgnorePatterns: [
      ],
      moduleNameMapper: {
        "^@milkdown/prose/(.*)$": "<rootDir>/node_modules/@milkdown/prose/lib/$1.js",
        "^@milkdown/(.*)$": "<rootDir>/node_modules/@milkdown/$1"
      },
      testEnvironment: 'jsdom',
      setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
      injectGlobals: true,
    }
    
    export default config
    

    below content of babel.config.js:

    module.exports = {
      plugins: [
        "@babel/plugin-transform-modules-commonjs"
      ]
    }
    

    Full implementation for this solution