Search code examples
typescriptjestjsts-jest

Jest, ts-jest, typescript with ES Modules import : cannot find module


I have hard time getting jest to work with typescript project that use ES modules with import syntax. My project was initially written for commonjs, jest test run fine. But then I decided to switch to ES Modules(for learning purpose), jest is not happy ヽ(`Д´)ノ The tools that I am using: typescript, jest, ts-jest

The issue start with import syntax.

The following are codes that I had tried.

//  projectRoot/src/app.ts

export default someFunction = (): void => {
   // some code
}

If

// projectRoot/__tests__/app.test.ts

import someFunction from '../src/app';   // without file extension

/* This execute perfectly fine */

But

// projectRoot/__tests__/app.test.ts

import someFunction from '../src/app.ts'   // with .ts

/*
● Test suite failed to run

   __tests__/app.test.ts:1:25 - error TS2691: An import path cannot end with a '.ts' extension. Consider importing '../src/app' instead.

    1 import information from '../src/app.ts';
*/

And

// projectRoot/__tests__/app.test.ts

import someFunction from '../src/app.js';   // with .js

/*
● Test suite failed to run

    Cannot find module '../src/app.js' from '__tests__/app.test.ts'
*/

As above example, jest(or maybe ts-jest?) is not happy if I import the module with extension(which is a must for ES Modules). I did some searching online, but seem like jest doc is not very supportive for ES Modules. Same goes to ts-jest by this reading

My Project structure:

/projectRoot
 ├── /src/app.ts
 ├── /__tests__/app.test.ts

Inside package.json file has value "type": "module"

tsconfig.json:

{
  "compilerOptions": {
     "target": "ES2015",
     "module": "ESNEXT",
     "outDir": "./build",
     "strict": true,
     "moduleResolution": "node",
     "esModuleInterop": true,
     "skipLibCheck": true,
     "forceConsistentCasingInFileNames": true
  },
  "include": ["./src"],
  "exclude": ["node_modules", "**/*.test.ts"]
}

jest.config.js

export default {
    "roots": [
      //"<rootDir>/src"
      "<rootDir>"
    ],
    "testMatch": [
      "**/__tests__/**/*.+(ts|tsx|js)",
      "**/?(*.)+(spec|test).+(ts|tsx|js)"
    ],
    "transform": {
      "^.+\\.(ts|tsx)$": "ts-jest"
    },
    "preset": "ts-jest",
    "testEnvironment": 'node'
  }

Please help. Thank you.


Solution

  • With the import below

    // projectRoot/__tests__/app.test.ts
    
    import someFunction from '../src/app.js';   // with .js
    

    ts-jest can be configured to use this with the following configurations. In jest.config.js file, add the following:

    module.exports = {
      //... // your previous configurations
      extensionsToTreatAsEsm: ['.ts'],
      globals: {
        'ts-jest': {
          //... // your other configurations here
          useESM: true,
        },
      },
      moduleNameMapper: {
        '^(\\.{1,2}/.*)\\.js$': '$1',
      }
    

    The source of this configuration can be found on ts-jest's documentation page: ESM-Support

    Some of my sample code is below:

    // components_ts.ts
    export add(x: number, y: number): number {
        return x + y;
    }
    
    // render_ts.ts
    import * as ComponentsTS from './components_ts.js'; // <-- Extension is JS
    
    export function addedNumbers(): number {
        let addedNumbers: number = ComponentsTS.add(1, 2);
        return addedNumbers;
    }
    
    // render_ts.test.ts
    import * as RenderTS from '../ts_files/render_ts';
    
    it ('Test addNumbers function', () => {
        expect(RenderTS.addedNumbers()).toBe(3);
    });
    

    Run this with npm jest