107

I am looking to be able to use webpack aliases to resolve imports when using jest, and optimally, reference the webpack.aliases to avoid duplication.

Jest conf:

  "jest": {
    "modulePaths": ["src"],
    "moduleDirectories": ["node_modules"],
    "moduleNameMapper": {
      "^@shared$": "<rootDir>/shared/",
      "^@components$": "<rootDir>/shared/components/"
    }
  },

Webpack aliases:

exports.aliases = {
    '@shared': path.resolve(paths.APP_DIR, 'shared'),
    '@components': path.resolve(paths.APP_DIR, 'shared/components'),
};

Imports:

import Ordinal from '@shared/utils/Ordinal.jsx';
import Avatar from '@components/common/Avatar.jsx';

For some reason the @ causes issues, so when removed (in both alias and import), it can find shared but components still cannot be resolved.

 FAIL  src/shared/components/test/Test.spec.jsx
  ● Test suite failed to run

    Cannot find module '@shared/utils/Ordinal.jsx' from 'Test.jsx'

I have tried using jest-webpack-alias, babel-plugin-module-resolver and the Jest/Webpack docs

speak
  • 5,202
  • 4
  • 36
  • 41
  • since you use webpack-2 are you using `transform-es2015-modules-commonjs` in env:test ? see: [using-with-webpack-2](https://facebook.github.io/jest/docs/webpack.html#using-with-webpack-2) – birdspider Mar 08 '17 at 16:53
  • Possible duplicate of http://stackoverflow.com/questions/33190795/configuring-jest-to-mimic-webpack-resolve-root-and-resolve-alias – hazardous Mar 13 '17 at 07:01
  • The `@` symbol indicates a [scoped package](https://docs.npmjs.com/misc/scope), which is typically searched inside your third party packages path. – hazardous Mar 13 '17 at 07:11

9 Answers9

92

This seems to have been fixed.

Below is a working setup:

Versions

"jest": "~20.0.4"

"webpack": "^3.5.6"

package.json

"jest": {
  "moduleNameMapper": {
    "^@root(.*)$": "<rootDir>/src$1",
    "^@components(.*)$": "<rootDir>/src/components$1",
  } 
}

webpack.shared.js

const paths = {
  APP_DIR: path.resolve(__dirname, '..', 'src'),
};

exports.resolveRoot = [paths.APP_DIR, 'node_modules'];

exports.aliases = {
  '@root': path.resolve(paths.APP_DIR, ''),
  '@components': path.resolve(paths.APP_DIR, 'components'),
};
speak
  • 5,202
  • 4
  • 36
  • 41
  • 5
    As a note, configuration may otherwise be located in `jest.config.js`. – Pat Migliaccio Jul 07 '20 at 21:35
  • 2
    My question seems is `moduleNameMapper ` just work to your source code, but doesn't work to your package code. How to resolve some packages in node_modules which has `@` or `@@` alias in jest? Such as package `umi`. – jyjin Jul 31 '21 at 01:51
59

Since I had the same problem before I read again, and this time more carefully the documentation. Correct config should be:

  "jest": {
    "moduleNameMapper": {
      "^@shared(.*)$": "<rootDir>/shared$1",
      "^@components(.*)$": "<rootDir>/shared/components$1"
    }
  },
TheFullResolution
  • 1,251
  • 1
  • 15
  • 26
  • 3
    I tried implementing this configuration, but still have the same issue. It cannot find the imported file: `Cannot find module '@shared/utils/Ordinal.jsx' from 'Test.jsx'`. – speak Apr 14 '17 at 12:32
  • 6
    I had to resolve my path with `"modulePaths": ["src"]`. Then yes, this solution worked: `"^Commons$": "./Commons"` – mayid Oct 20 '17 at 18:20
  • 1
    I was having the same issue as the OP and @mayid's solution was what finally resolved the issue for me. – Ross Sheppard Feb 05 '19 at 19:46
  • 1
    In case you're a total newbie like me, this configuration is found in your ```package.json``` file – Fortune Jun 15 '20 at 12:55
41

Using: "jest": "^26.5.3", and "webpack": "4.41.5", I was able to properly match my webpack/typescript aliases in the jest.config.js with this pattern:

Webpack config:

module.exports = {
   // the rest of your config
    resolve: {
      alias: {
        'components': path.resolve(__dirname, 'js/app/components'),
        'modules': path.resolve(__dirname, 'js/app/modules'),
        'types': path.resolve(__dirname, 'js/types'),
        'hooks': path.resolve(__dirname, 'js/app/hooks'),
        'reducers': path.resolve(__dirname, 'js/app/reducers'),
        '__test-utils__': path.resolve(__dirname, 'js/app/__test-utils__') 
      }
    },
}

Jest.config.js:

  moduleNameMapper: {
    '^types/(.*)$':  '<rootDir>/js/types/$1',
    '^components/(.*)$': '<rootDir>/js/app/components/$1',
    '^modules/(.*)$':  '<rootDir>/js/app/modules/$1',
    '^hooks/(.*)$':  '<rootDir>/js/app/hooks/$1',
    '^reducers/(.*)$':  '<rootDir>/js/app/reducers/$1',
    '^__test-utils__/(.)$': '<rootDir>/js/app/__test-utils__/$1' 
  }

Here's an explanation of the symbols:

  • (.*)$: capture whatever comes after the exact match (the directory)
  • $1: map it to this value in the directory I specify.

and tsconfig.json:

    "paths": {
      "config": ["config/dev", "config/production"],
      "components/*": ["js/app/components/*"],
      "modules/*": ["js/app/modules/*"],
      "types/*": ["js/types/*"],
      "hooks/*": ["js/app/hooks/*"],
      "reducers/*": ["js/app/reducers/*"],
      "__test-utils__/*": ["js/app/__test-utils__/*"]
    }
kcent
  • 939
  • 7
  • 8
39

For anyone using @ as the root of their modules, you have to be more specific since other libs can use the @ in node modules.

 "jest": {
    moduleNameMapper: {
      "^@/(.*)$": "<rootDir>/src/$1"
    },
 }

It translates to "anything that matches @/ should be sent to <rootDir>/src/<rest of the path>

Obed Parlapiano
  • 3,226
  • 3
  • 21
  • 39
  • 1
    In addition, this mapping needs to be added in `package.json` file, under `jest` section. probably there is a section in that place already. – adnan ahmady May 21 '23 at 13:42
9

FWIW, Try switching the alias order, keep the more specific up and less specific down, e.g.

"moduleNameMapper": {
  "^@components$": "<rootDir>/shared/components/",
  "^@shared$": "<rootDir>/shared/"
}
hazardous
  • 10,627
  • 2
  • 40
  • 52
  • Tried in combination with leo's answer, still have the same errors. – speak Mar 14 '17 at 11:49
  • Is it possible for you to share the complete code, github preferably? – hazardous Mar 14 '17 at 12:02
  • unfortunately not as it's a private corporate repository. – speak Mar 14 '17 at 12:29
  • Could you create a git repo with the minimally viable code where this issue is reproducible? – hazardous Mar 14 '17 at 12:34
  • I'm also getting the same error message for a scoped package. I've created a very small project at https://gist.github.com/batwicket/da72ffa7d5324d8f7c5eb11f0ba07fad . I tried the alias approach.. no luck. I'm probably missing something simple. Any help appreciated. – James Fremen Apr 24 '17 at 06:47
  • Yes we have the same issue. Importing from a location called "@location" and it seems the "@" is causing an issue. – Wes Duff Jul 13 '17 at 21:22
9

None of the suggested answers worked for me.

Was able to solve it with the following pattern:

Jest version 25+

moduleNameMapper: {
    '^Screens(.*)': '<rootDir>/src/screens/$1',
    '^Components(.*)': '<rootDir>/src/components/$1',
    '^Assets(.*)': '<rootDir>/src/assets/$1',
    '^Services/(.*)': '<rootDir>/src/services/$1',
  },
Ishwar Rimal
  • 1,071
  • 11
  • 19
8

This is a real insanity but when I try to map module in package.json like this:

"jest": {
  "moduleNameMapper": {
    '@root/(.*)': '<rootDir>/../$1'
  }
}

It doesn't work.

But when I do the same in jest.config.js it works:

module.exports = {
   moduleNameMapper: {
     '@root/(.*)': '<rootDir>/../$1'
   }
}
Alonad
  • 1,986
  • 19
  • 17
2

this solution works well for me:

moduleNameMapper: {
    '^Screens(.*)': '<rootDir>/src/screens/$1',
    '^Components(.*)': '<rootDir>/src/components/$1',
    '^Assets(.*)': '<rootDir>/src/assets/$1',
    '^Services/(.*)': '<rootDir>/src/services/$1',
  },
1

Assume your webpack aliases lies in webpack.yml in "resolved_paths" block.

To add same aliases in jest, if your jest config is in package.json, use moduleDirectories:

"jest":{
  "moduleDirectories":[
     ".",
      "<rootDir>/shared/components/",
      "<rootDir>/shared/",
      "node_modules"
  ]
}