71

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
  • 4,132
  • 3
  • 33
  • 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

53

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
  • 4,132
  • 3
  • 33
  • 41
46

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,091
  • 1
  • 16
  • 25
  • 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
  • 5
    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
  • 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
11

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
  • 608
  • 5
  • 8
7

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
  • 9,322
  • 2
  • 34
  • 49
  • 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
7

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

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

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

Obed Marquez Parlapiano
  • 1,689
  • 2
  • 19
  • 28
3

None of the prescribed answers worked for me.

Was able to solve 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
  • 881
  • 9
  • 18
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',
  },
2

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,025
  • 11
  • 11
0

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:

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