Jest + Typescript + Absolute paths (baseUrl) gives error: Cannot find module
I am setting a configuration to run my tests in a create-react-app + typescript app (from which I have ejected). I am using jest + enzyme. In my tsconfig.json I have set baseUrl='./src'
so I can use absolute paths when I import modules. For example this is a typical import statement in one of my files:
import LayoutFlexBoxItem from 'framework/components/ui/LayoutFlexBoxItem';
You can see that the path is absolute (from /src folder) and not relative.
This works fine when I run in debug mode ( yarn start
)
But when I run my test ( yarn test
), I get this error:
Cannot find module 'framework/components/Navigation' from 'index.tsx'
So it looks like jest is not able to resolve this absolute path although I have set it up in my tsconfig.json. This is my tsconfig.json:
{
"compilerOptions": {
"outDir": "dist",
"module": "esnext",
"target": "es5",
"lib": ["es6", "dom"],
"sourceMap": true,
"allowJs": true,
"jsx": "react",
"moduleResolution": "node",
"rootDir": "src",
"forceConsistentCasingInFileNames": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noImplicitAny": true,
"strictNullChecks": true,
"suppressImplicitAnyIndexErrors": true,
"noUnusedLocals": true,
"baseUrl": "./src"
},
"exclude": [
"node_modules",
"build",
"dist",
"config",
"scripts",
"acceptance-tests",
"webpack",
"jest",
"src/setupTests.ts"
]
}
Now I can see that there is a generated tsconfig.test.json
at the root of my project. This is the ts configuration used for test. And here is its content:
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "commonjs"
}
}
As you can see the "module" is commonjs
here whereas in the default configuration it is esnext
. Could this be one reason?
Has any one been able to unit test his typescript project with Jest and absolute path? or is this a known bug? Since I have ejected from default configuration, are there some settings to put in my webpack configuration?
Thanks for your input and suggestion.
Solution 1:
I was struggling with the same problem and actually it turns out that a simple change seems to do the trick.
I just updated the moduleDirectories
field in jest.config.js
.
Before
moduleDirectories: ['node_modules']
After
moduleDirectories: ['node_modules', 'src']
Hope it helps.
Solution 2:
As many here pointed out moduleNameMapper
in jest.config.js
needs to define paths specified in tsconfig.json
. For example, if you have paths in tsconfig.json
defined as follows
// tsconfig.json
{
...
"baseUrl": "src",
"paths": {
"@alias/*": [ 'path/to/alias/*' ]
}
...
}
then your jest.config.js
needs to provide those paths in moduleNameMapper
in the following format:
// jest.config.js
module.exports = {
'roots': [
'<rootDir>/src'
],
'transform': {
'^.+\\.tsx?$': 'ts-jest'
},
'moduleNameMapper': {
'@alias/(.*)': '<rootDir>/src/path/to/alias/$1'
}
};
Having that we can improve our jest.config.js
to convert paths defined in tsconfig.json
automatically. Here is a Gist code snippet for that:
// jest.config.js
function makeModuleNameMapper(srcPath, tsconfigPath) {
// Get paths from tsconfig
const {paths} = require(tsconfigPath).compilerOptions;
const aliases = {};
// Iterate over paths and convert them into moduleNameMapper format
Object.keys(paths).forEach((item) => {
const key = item.replace('/*', '/(.*)');
const path = paths[item][0].replace('/*', '/$1');
aliases[key] = srcPath + '/' + path;
});
return aliases;
}
const TS_CONFIG_PATH = './tsconfig.json';
const SRC_PATH = '<rootDir>/src';
module.exports = {
'roots': [
SRC_PATH
],
'transform': {
'^.+\\.tsx?$': 'ts-jest'
},
'moduleNameMapper': makeModuleNameMapper(SRC_PATH, TS_CONFIG_PATH)
};
Solution 3:
Here is how I got moduleNameMapper working.
With the below config in my tsconfig:
"paths": {
"@App/*": [
"src/*"
],
"@Shared/*": [
"src/Shared/*"
]
},
Here's the moduleNameMapper:
"moduleNameMapper": {
"@App/(.*)": "<rootDir>/src/$1",
"@Shared/(.*)": "<rootDir>/src/Shared/$1"
}