In my jest.config.js file, I need to populate globals property. For populating the globals property I need to require local modules, as shown below:
const path = require('path')
const server = require('./server/cfg')
module.exports = {
rootDir: path.resolve(__dirname),
moduleFileExtensions: [
'js',
'json',
'vue',
'ts'
],
moduleNameMapper: {
'^#/(.*)$': '<rootDir>/src/$1'
},
transform: {
".*\\.(vue)$": "<rootDir>/node_modules/vue-jest",
"^.+\\.(js|jsx)?$": "<rootDir>/node_modules/babel-jest",
"^.+\\.ts$": "<rootDir>/node_modules/ts-jest"
},
testRegex: "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
snapshotSerializers: [
"jest-serializer-vue"
],
setupFiles: [
"<rootDir>/globals.js"
],
testEnvironment: "jsdom",
globals: {
server: {
server
}
}
}
Whit this configuration, I get the following error:
Error: Cannot find module './server/cfg'
This is my folder structure
server/
cfg.ts
src/
jest.config.js
webpack.config.js
However, I can require node's built-in modules. I'm not able to figure out why it is happening. Any ideas on how I can overcome this?
Jest is initiated by node, not ts-node and it's not able to resolve the .ts file by default.
Probably adding setupFilesAfterEnv will help you.
jest.config.js
module.exports = {
// ... your config
setupFilesAfterEnv: [
"<rootDir>/environmentWithServer.ts`
]
}
environmentWithServer.ts
global.server = require('./server/cfg');
Related
Have got a successful jest/esm setup, however occasionally a module is released that specifies both a main key (for commonjs) and a module key (for ESM) in its package.json. This leads to jest errors, for example with the uuid module:
/repo/path/node_modules/uuid/dist/esm-browser/index.js:1
({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,jest){export { default as v1 } from './v1.js';
^^^^^^
SyntaxError: Unexpected token 'export'
I can see that dist/esm-browser/index.js is the file specified by the module key in package.json.
How can Jest w/ESM be configured to handle these cases, where stuff in node_modules is ESM?
Jest config:
{
"resetMocks": true,
"testEnvironment": "jsdom",
"testMatch": [
"**/src/**/*.(spec|test).[tj]s?(x)"
],
"preset": "ts-jest/presets/default-esm",
"extensionsToTreatAsEsm": [
".ts",
".tsx"
],
"globals": {
"ts-jest": {
"useESM": true
}
},
"globalSetup": "<rootDir>/jest/setup.cjs",
"globalTeardown": "<rootDir>/jest/teardown.cjs",
"watchPathIgnorePatterns": [
"<rootDir>/.tmp"
],
"moduleNameMapper": {
"^~/(.*)$": "<rootDir>/src/$1",
"^~components/(.*)$": "<rootDir>/src/components/$1",
"^~util/(.*)$": "<rootDir>/src/util/$1",
"^~types/(.*)$": "<rootDir>/src/types/$1"
}
}
If transformIgnorePatterns doesn't work for some reason, you can solve it with moduleNameMapper.
moduleNameMapper: {
// '^uuid$': '<rootDir>/node_modules/uuid/dist/index.js',
'^uuid$': require.resolve('uuid'),
}
I've had the same problem and it was fixed the same way as mentioned in this comment: https://github.com/nrwl/nx/issues/812#issuecomment-429420861 in my jest.config.js:
// list to add ESM to ignore
const esModules = ['uuid'].join('|');
// ...
module.exports = {
//...
transformIgnorePatterns: [`/node_modules/(?!${esModules})`],
// ...
};
I created an app within a monorepo (Lerna) using yarn workspace.
The architecture of the app is as follow:
my-monorepo
├── node_modules
├── packages
│ ├── package1(shared components)
│ ├── package2(other package consuming the shared components)
│ │ └── ./jest.config.js
├── package.json
The problem
the problem is jest is throwing the following error when trying to use package1 in package2 in any test, and I haven't found a way to fix it.
● Test suite failed to run
Jest encountered an unexpected token
This usually means that you are trying to import a file which Jest ca
nnot parse, e.g. it's not plain JavaScript.
By default, if Jest sees a Babel config, it will use that to transfor
m your files, ignoring "node_modules".
Here's what you can do:
• If you are trying to use ECMAScript Modules, see https://jestjs.io
/docs/en/ecmascript-modules for how to enable it.
• To have some of your "node_modules" files transformed, you can spe
cify a custom "transformIgnorePatterns" in your config.
• If you need a custom transformation specify a "transform" option i
n your config.
• If you simply want to mock your non-JS modules (e.g. binary assets
) you can stub them out with the "moduleNameMapper" config option.
You'll find more details and examples of these config options in the
docs:
https://jestjs.io/docs/en/configuration.html
Details:
C:\Users\my-user\Desktop\my-monorepo\node_module
s\antd\es\date-picker\generatePicker\index.js:1
({"Object.<anonymous>":function(module,exports,require,__dirname,__fi
lename,global,jest){import _extends from "#babel/runtime/helpers/esm/exte
nds";
^^^^^^
According the error, I'm trying to import a file which Jest cannot parse, so the problem comes from the package1, so the first thing that comes to my mind is: maybe I'm doing something wrong in rollup in package1 and the final bundle comes in some format that jest doesn't understand...
Jest config
Jest config located in package2, where i want to consume package1:
// jest.config.js in package2
const config = {
roots: ['src'],
setupFilesAfterEnv: ['./jest.setup.ts'],
moduleFileExtensions: ['ts', 'tsx', 'js'],
testPathIgnorePatterns: ['node_modules/'],
transform: {
'^.+\\.tsx?$': 'ts-jest',
},
testMatch: ['**/*.test.(ts|tsx)'],
moduleNameMapper: {
// Mocks out all these file formats when tests are run.
'\\.(jpg|ico|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
'identity-obj-proxy',
'\\.(css|less|scss|sass)$': 'identity-obj-proxy',
},
};
export default config;
Rollup config
This is the rollup configuration in package1:
// rollup.config.js
import peerDepsExternal from 'rollup-plugin-peer-deps-external';
import resolve from '#rollup/plugin-node-resolve';
import commonjs from '#rollup/plugin-commonjs';
import typescript from 'rollup-plugin-typescript2';
import postcss from 'rollup-plugin-postcss';
import copy from 'rollup-plugin-copy';
import json from '#rollup/plugin-json';
import svgr from '#svgr/rollup';
import { babel } from '#rollup/plugin-babel';
import { visualizer } from 'rollup-plugin-visualizer';
import pkg from './package.json';
export default {
input: 'src/index.tsx',
output: [
{
file: pkg.main,
format: 'cjs',
exports: 'named',
sourcemap: true,
},
{
file: pkg.module,
format: 'esm',
exports: 'named',
sourcemap: true,
},
],
plugins: [
peerDepsExternal({
includeDependencies: true,
}),
json(),
svgr(),
resolve({ extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'] }),
commonjs({
include: /\**node_modules\**/,
}),
// UPDATE 3: Add babel
babel({
babelHelpers: 'bundled',
}),
typescript({
useTsconfigDeclarationDir: true,
exclude: ['*.d.ts', '**/*.d.ts', '**/*.test.tsx'],
rollupCommonJSResolveHack: true,
clean: true,
}),
postcss({
extensions: ['.css', '.less'],
use: {
less: { javascriptEnabled: true, modifyVars: {} },
},
}),
visualizer({ filename: 'stats-visualizer.html', gzipSize: true }),
copy({
targets: [
{
src: 'src/styles/themes/dark-variables.less',
dest: 'dist',
rename: 'theme/dark.less',
},
{
src: 'src/styles/themes/light-variables.less',
dest: 'dist',
rename: 'theme/light.less',
},
{
src: 'src/assets/icons',
dest: 'dist',
rename: 'assets/icons',
},
],
}),
],
};
UPDATE 1:
I've tried to use transform rules in jest.config.js as mentioned by Matt Carlota to transpile antdbut this doesn't work:
// jest.config.js
const config = {
// ... other jest settings
transform: {
'^.+\\.tsx?$': 'ts-jest',
'node_modules/antd/.+\\.(j|t)sx?$': 'ts-jest',
},
// I've tried with `antd/es` pattern too and doesn't work
transformIgnorePatterns: ['node_modules/(?!antd)'],
};
UPDATE 2:
Change manually antd/es by antd/lib in package1 resolve the problem temporarily, but there is one problem and that is that we are a large group of people working and it could be put as a convention to use only antd/lib but I feel it would be error prone.
every time someone forgets to use antd/lib and uses antd/en all tests break with the original error.
UPDATE 3:
Add babel config file and plugin in rollup configuration...
// babel.config.js
module.exports = {
plugins: [['import', { libraryName: 'antd', libraryDirectory: 'lib' }, 'antd']],
};
I've been having the same issue for the longest time and finally found a way.
The missing piece in my case was adding moduleNameMapper. A guide mentioned that here.
The solution doesn't require babel-jest.
jest.config.js
const path = require('path');
const { lstatSync, readdirSync } = require('fs');
// get listing of packages in the mono repo
const basePath = path.resolve(__dirname, '..', '..', 'packages');
const packages = readdirSync(basePath).filter((name) => {
return lstatSync(path.join(basePath, name)).isDirectory();
});
module.exports = {
preset: 'ts-jest',
verbose: true,
moduleFileExtensions: ['js', 'json', 'jsx', 'node', 'ts', 'tsx'],
moduleDirectories: ['node_modules', 'src'],
moduleNameMapper: {
...packages.reduce(
(acc, name) => ({
...acc,
[`#xyz/${name}(.*)$`]: `<rootDir>/../../packages/./${name}/src/$1`,
}),
{}
),
},
rootDir: './',
testRegex: '.spec.ts$',
transform: {
'^.+\\.(t)s$': 'ts-jest',
},
testEnvironment: 'node',
setupFilesAfterEnv: ['./jest.setup.ts'],
testMatch: null,
globals: {
'ts-jest': {
tsconfig: 'tsconfig.jest.json',
},
},
roots: ['<rootDir>'],
transformIgnorePatterns: [],
collectCoverage: false,
collectCoverageFrom: ['src/**/*.{js{,x},ts{,x}}', '!src/server/index.ts'],
};
tsconfig.json
{
"compilerOptions": {
"module": "esnext",
"target": "ES2019",
"lib": ["ES2019", "DOM"],
"noEmit": true,
"types": ["node", "jest"],
"rootDir": "./"
"paths": {
"#xyz/*": ["packages/*/src"]
},
},
"include": ["test/**/*.ts", "src/**/*.ts", "**/*.spec.ts"],
"references": [
{ "path": "../../packages/package1" },
]
}
package.json
...
scripts: {
"test": "NODE_ENV=production tsc --build ./tsconfig.jest.json && jest --env=node test --watch",
}
I've created a Yarn Workspace, it contains of two packages: Client and Service.
When my Component in the Client package imports a module from the Service package, Jest throws the above error.
Here is how mi jest.config.js looks like
const rootTestModule = '<rootDir>';
module.exports = {
testEnvironment: 'jest-environment-jsdom',
testMatch: ['**/?(*.)(test).js?(x)'],
transform: {
'^.+\\.jsx?$': `${rootTestModule}/jest/babel-preprocessor.js`,
},
moduleNameMapper: {
'\\.scss$': require.resolve('./style-mock.js'),
},
snapshotSerializers: ['jest-emotion'],
transformIgnorePatterns:['<rootDir>/node_modules/(?!#rppm)'],
};
Here is the babel-preprocessor.js file
const { createTransformer } = require('babel-jest');
const babelConfig = {
presets: ['#babel/env', '#babel/react'],
plugins: [
'#babel/plugin-proposal-class-properties',
'#babel/plugin-proposal-object-rest-spread',
'#babel/plugin-proposal-export-default-from',
'#babel/plugin-transform-runtime',
'transform-es2015-modules-commonjs',
]
};
Here is the package im trying to use
The tsconfig.json has paths setup like this:
"paths": {
"#fs/*": ["src/*"],
"#test/*": ["test/*"]
}
With that all #fs/blah/blah imports are resolving fine in VSCode. In order to get Jest to work with the same import declaration I added moduleMapper to jest.config.ts and this is the full config:
module.exports = {
roots: ["./src"],
transform: {
"^.+\\.tsx?$": "ts-jest"
},
testRegex: "(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$",
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
moduleNameMapper: {
"#fs/*": ["src/*"]
}
};
With that statements like import { isBoolean } from "#fs/is"; do not resolve.
When changed using a relative import it Jest does resolve it (import { isBoolean } from "./is";)
Thoughts?
According to the jest document, keys of moduleNameMapper are regular expressions. (not glob patterns)
And <rootDir> can be used to reference the root of the project. (docs)
So, we can configure like below:
moduleNameMapper: {
"^#fs/(.*)$": "<rootDir>/src/$1"
}
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
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'),
};
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"
}
},
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__/*"]
}
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>
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/"
}
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'
}
}
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',
},
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',
},
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"
]
}