'How to create nested moduleNameMapper in Jest?

My simplified jest.config.js is:

module.exports = {
  preset: "ts-jest",
  collectCoverage: true,
  collectCoverageFrom: [
    "src/**/*.ts",
    "!**/node_modules/**",
    "!src/config/*.ts",
    "!build/**/*"
  ],
  coverageReporters: ["text"],
  reporters: ["default"],
  moduleDirectories: [
    "src",
    "node_modules"
  ],
  moduleNameMapper: {
    "@helpers/(.*)": "<rootDir>/src/helpers/$1", <---- question about this
  },
  globals: {
    'ts-jest': {
      diagnostics: {
        pathRegex: /\.(spec|test)\.ts$/,
        ignoreCodes: [6133]
      }
    }
  },
  verbose: true,
  roots: ["src"],
  moduleFileExtensions: ["ts", "tsx", "js", "jsx"]
};

The moduleNameMapper maps to any level of nested paths:

  • @helpers/foo
  • @helpers/foo/foo
  • etc...

I'm using it in my tests like import foo from "@helpers/foo/foo";

However, I get this error:

Could not locate module @helpers/foo mapped as: /<PATH>/src/helpers/foo

Didn't find anything in the documentation about nested paths. I assumed, since @helpers/(.*) is a regex, this should work out of the box.

What am I doing wrong? Maybe it's a bug?



Solution 1:[1]

Don't know whether you resolved this or not, but i had the same issue. As I was using typescript i also had to add the paths to my tsconfig for it to understand the mappings in all contexts, in addition you would probably have to update its rootDir when using relative paths.

This worked for me when testing my electron/svelte app:

//jest.config.js
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'node',
  cacheDirectory: '.jest',
  transform: {
    '^.+\\.svelte$': 'svelte-jester',
  },
  moduleFileExtensions: [
    'js', 'ts', 'svelte'
  ],
  globals: {
    'ts-jest': {
      tsconfig: '<rootDir>/tests/tsconfig.json'
    }
  },
  moduleNameMapper: {
    '^@app/(.*)$': '<rootDir>/app/$1',
    '^@main/(.*)$': '<rootDir>/app/main/src/$1',
    '^@preload/(.*)$': '<rootDir>/app/preload/src/$1',
    '^@renderer/(.*)$': '<rootDir>/app/renderer/src/$1',
    '^@test/(.*)$': '<rootDir>/tests/$1',
    'electron': '<rootDir>/tests/mock/electron.ts'
  }
}

I had some issues with the utility method for mapping tsconfig paths to moduleNameMapper so i just referenced it directly instead. (The following tsconfig is from within the /tests folder itself):

{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "moduleResolution": "node",
    "useDefineForClassFields": true,
    "strict": true,
    "sourceMap": true,
    "resolveJsonModule": true,
    "esModuleInterop": true,
    "isolatedModules": false,
    "allowJs": true,
    "noFallthroughCasesInSwitch": false,
    "noImplicitAny": false,
    "lib": ["esnext", "dom"],
    "types": ["node", "vite/client"],
    "paths": {
      "@main/*": ["../app/main/src/*"],
      "@test/*": ["./*"],
      "@renderer/*": ["../app/renderer/src/*"],
      "@preload/*": ["../app/preload/src/*"]
    },
    "rootDirs": [".", "../app"]
  },
  "include": [
    "./**/*.d.ts",
    "./**/*.ts",
    "../node_modules/@types/jest/index.d.ts"
  ]
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 SlyOtis