Our app imports files using import ES2015 style syntax, utilizing Webpack 4.6.0 native support for ES2015 modules. We also use an alias to shorten our relative file paths.
Webpack.conf.js
resolve: {
extensions: ['.js', '.json', '.less'],
alias: {
'#': resolve('public/js'),
'handlebars': 'handlebars/dist/handlebars.js',
},
modules: ['less', 'node_modules']
},
example.js
import widget from '#/widgets/widget';
file structure
- webpack.conf.js
- .babelrc
- test/
- public/
- - js/
- - - widgets/
- - - - widget.js
When I imported for example example.js, which has an alias'd import, Jest would throw an error, "cannot resolve module '#/widgets/widget'.
According to a remarkably specific article as well as the Jest documentation, the solution is to use Jest's ModuleNameMapper config property to set up matching alias'. I have attempted to do so:
package.json
"jest": {
"moduleNameMapper": {
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/fileMock.js",
"\\.(css|less)$": "<rootDir>/__mocks__/styleMock.js",
"#(.*)$": "<rootDir>/public/js/$1"
},
"verbose": true,
"transform": {
"^.+\\.js$": "babel-jest"
},
"globals": {
"NODE_ENV": "test"
},
"moduleFileExtensions": [
"js"
],
"moduleDirectories": [
"node_modules"
]
},
As well as properly configure babel:
.babelrc
{
"presets": [
[
"env",
{
"modules": false,
"test": {
"plugins": ["transform-es2015-modules-commonjs"]
}
}
],
"es2015",
"stage-2"
],
"plugins": [
"syntax-dynamic-import"
]
}
Now, when I run Jest (with the --no-cache flag just in case), I get this error:
test/test.test.js
● Test suite failed to run
Configuration error:
Could not locate module #babel/code-frame (mapped as /home/calebjay/Documents/ide/public/js/babel/code-frame)
Please check:
"moduleNameMapper": {
"/#(.*)$/": "/home/calebjay/Documents/ide/public/js/$1"
},
"resolver": undefined
I can't find #babel/code-frame anywhere outside of package-lock.json, and just for giggles I stripped all mentions of #{{anything}} from there and ran tests again, same result.
Is jest stepping over babel somehow? How can I get my tests to run with Jest using aliases?
EDIT: To try to narrow down what is calling #babel/code-frame, I tried deleting es2015 and stage-2 from .babelrc, to no effect. I tried deleting the transform property of the Jest config in package.json, to no effect. I tried deleting the env.test.plugins property from .babelrc, to no effect. Same error.
EDIT2: Thinking maybe some other package is requiring it, I checked package.json. It seems jest-message-util requires #babel/code-frame. I do see #babel/code-frame in my node_modules though... so perhaps the problem is that jester is saying "ok, all instances of #, turn into public/js" ?
"#(.*)$": "<rootDir>/public/js/$1"
will convert #babel/code-frame to
"<rootDir>/public/js/babel/code-frame"
which doesn't exist. You need to make your pattern more specific and do
"#/(.*)$": "<rootDir>/public/js/$1"
Note the additional / at the beginning. That way it will still match your #/widgets/widget, but it won't match other scoped packages.
Related
I'm switching my TypeScript project to a (pnpm) monorepo and have troubles getting tests to run properly. I have a jest.config.js that uses a custom testEnvironment that's written in TypeScript as well. However, ever since I moved the specific project into my packages directory for the monorepo restructuring, jest throws an Error and doesn't run any tests:
TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts" for C:\workspaces\repos\the-monorepo\packages\testproject\source\testtools\jsdom-environment-global.spec.ts
I tried it with #swc/jest as well as with ts-jest, had a look at How to use TypeScript in a Custom Test Environment file in Jest? (which makes me think "why did this ever work?") and, for whatever reason, it worked fine yesterday. I cleaned jest cache and reinstalled all node_modules to no avail. I also found answers related to "type": "module" in package.json, but this doesn't apply to my package. It's not an ESM.
Here's how the jest.config.js looks like:
/** #type {import('#jest/types').Config.InitialOptions} */
const config = {
silent: true,
testEnvironment: "<rootDir>/source/testtools/jsdom-environment-global.spec.ts",
roots: [
"<rootDir>/source"
],
maxWorkers: "50%",
transform: {
"^.+\\.(t|j)s$": ["#swc/jest", {
sourceMaps: "inline",
module: {
strict: false,
strictMode: false
},
jsc: {
target: "es2021",
parser: {
syntax: "typescript",
dynamicImport: true
}
}
}]
},
transformIgnorePatterns: [
"node_modules"
],
testMatch: [
"**/*/*.spec.ts",
"**/*/*.test.ts",
"!**/playwright-tests/**",
"!**/playwright-tests-smoke/**"
],
moduleFileExtensions: ["ts", "js", "node", "json"],
reporters: [
"default"
],
globals: {
self: {},
navigator: {},
jasmine: {},
__UNIT__: true
},
coverageDirectory: "test-results",
collectCoverage: false,
collectCoverageFrom: [
"./source/**/*.ts"
],
coveragePathIgnorePatterns: [
"/\\.spec\\.ts$/i",
"/.*node_modules.*/",
"/.*testtools.*/"
],
coverageReporters: [
"lcov", "cobertura"
],
coverageProvider: "v8",
resetMocks: true,
restoreMocks: true,
resetModules: true,
setupFilesAfterEnv: [
"jest-extended/all",
"<rootDir>/source/testtools/setup.spec.ts"
],
testPathIgnorePatterns: [
"<rootDir>/source/testtools/",
"<rootDir>/source/smoke-tests/",
"<rootDir>/source/performance-tests/",
"<rooDir>/source/playwright-tests/",
"<rooDir>/source/playwright-tests-smoke/"
],
moduleNameMapper: {
"^#test-helpers": "<rootDir>/source/testtools/index.spec.ts",
"^#test-tools/(.*)": "<rootDir>/source/testtools/$1",
'^(\\.{1,2}/.*)\\.js$': '$1'
}
};
module.exports = config;
Why is jest not able to parse the testEnvironment if it's a TypeScript file?
I found the issue: there seems to be some confusion around the transformer not being applied to ESM files. In my case, the jsdom-environment-global.spec.ts imported an ESM module from a different package within my monorepo. This import triggers an exception because jest tries to import it via require(), which is caught and then again imported via dynamic imports. This dynamic import then throws the exception that ts is an unknown exception. I'm not sure why these files haven't been transformed, but as of How to use TypeScript in a Custom Test Environment file in Jest? this seems to be normal.
Bottom line: don't import from ESM files within your jest testEnvironment module.
I'm trying to understand how to fix the following error using Jest in my unit tests in NodeJS.
The test run with this command "test": "NODE_ENV=test jest spec/* -i --coverage --passWithNoTests",
I'm also using babel and this is my config
{
"presets": [["#babel/env", { "targets": { "node": "current" } }]],
"plugins": [
"#babel/plugin-syntax-dynamic-import",
["babel-plugin-inline-import", { "extensions": [".gql"] }],
["#babel/plugin-proposal-decorators", { "legacy": true }]
]
}
In package.json I have this
"jest": {
"verbose": true,
"collectCoverageFrom": [
"spec/**/*.js"
]
},
I tried several guides online but cannot find a solution to this
You've got Jest successfully configured to transform your code, but it is not transforming modules that you're importing—in this case node-fetch, which has the import keyword in its source code (as seen in your error). This is because, by default, Jest is configured not to transform files in node_modules:
transformIgnorePatterns [array]
Default: ["/node_modules/", "\.pnp\.[^\/]+$"]
An array of regexp pattern strings that are matched against all source file paths before transformation. If the file path matches any of the patterns, it will not be transformed.
You can set transformIgnorePatterns to exclude certain packages in node_modules with a jest.config.js like this:
const esModules = [
'node-fetch',
'data-uri-to-buffer',
'fetch-blob',
'formdata-polyfill',
].join('|');
module.exports = {
transformIgnorePatterns: [
`/node_modules/(?!${esModules})`,
'\\.pnp\\.[^\\/]+$',
],
};
(see https://github.com/nrwl/nx/issues/812#issuecomment-429420861)
If you have .babelrc try to rename it to babel.config.js
Source:
https://babeljs.io/docs/en/configuration#whats-your-use-case
but also this (there's more in the discussion)
Jest won't transform the module - SyntaxError: Cannot use import statement outside a module
I've been trying for a while now to get #babel/plugin-proposal-class-properties plugin to work nicely with #babel/eslint-parser and eslint without success.
This is my .eslintrc.js:
...
"parser": "#babel/eslint-parser",
"parserOptions": {
"ecmaVersion": 11,
"requireConfigFile": false,
},
"plugins": [
"#babel",
],
...
And these are the related installed packages:
+-- #babel/core#7.11.1
+-- #babel/eslint-parser#7.11.3
+-- #babel/eslint-plugin#7.11.3
+-- #babel/plugin-proposal-class-properties#7.10.4
+-- eslint#7.7.0
Under this configuration, ESLint errors with this message:
Parsing error: \eg\example.js: Support for the experimental syntax 'classPrivateProperties' isn't currently enabled (xx:yy): (Fatal)
But if I add #babel/plugin-proposal-class-properties to plugins in .eslintrc.js like this:
"plugins": [
"#babel",
"#babel/plugin-proposal-class-properties",
],
I get this error:
Error while running ESLint: Failed to load plugin '#babel/plugin-proposal-class-properties' declared in '.eslintrc.js': Cannot find module '#babel/eslint-plugin-plugin-proposal-class-properties'.
It seems like this isn't the correct way to declare plugins for #babel/eslint-parser in .eslintrc.js. Still, I suspect it is possible due to this quote here:
#babel/eslint-parser also supports applying Babel configuration through your ESLint configuration.
So my question is:
Is it actually possible to declare babel plugins in .eslintrc? If so how exactly?
It's actually simpler than I thought...
So it turns out that since #babel/plugin-proposal-class-properties is a babel plugin, it needs to be declared in the plugins property of babel's configuration. According to the documentation of #babel/eslint-parser, those can be passed in with babelOptions property.
Therefore it's really as simple as this:
...
"parserOptions": {
...
"babelOptions": {
"plugins": [
"#babel/plugin-proposal-class-properties",
...
],
},
},
"plugins": [
"#babel",
],
...
When using #babel/eslint-parser as eslintrc parser, I met this question too.
Just like this question.
For example, the eslintrc used by eslint node api in a global cli , and the cli provide a command A.
After go to the directory B, executing command A.The process.cwd() is B directory, but the #babel/xxx deps is in cli node_modules.The babel/core can not find plugins in B.
Parsing error: Cannot find module '#babel/plugin-proposal-decorators'\nRequire stack:
- /Users/a1/.nvm/versions/node/v14.16.1/lib/node_modules/test-a-cli/node_modules/#babel/core/lib/config/files/plugins.js
- /Users/a1/.nvm/versions/node/v14.16.1/lib/node_modules/test-a-cli/node_modules/#babel/core/lib/config/files/index.js
- /Users/a1/.nvm/versions/node/v14.16.1/lib/node_modules/test-a-cli/node_modules/#babel/core/lib/index.js
- /Users/a1/.nvm/versions/node/v14.16.1/lib/node_modules/test-a-cli/node_modules/#babel/eslint-parser/lib/worker/babel-core.cjs
- /Users/a1/.nvm/versions/node/v14.16.1/lib/node_modules/test-a-cli/node_modules/#babel/eslint-parser/lib/worker/handle-message.cjs
- /Users/a1/.nvm/versions/node/v14.16.1/lib/node_modules/test-a-cli/node_modules/#babel/eslint-parser/lib/client.cjs
- /Users/a1/.nvm/versions/node/v14.16.1/lib/node_modules/test-a-cli/node_modules/#babel/eslint-parser/lib/index.cjs
- /Users/a1/.nvm/versions/node/v14.16.1/lib/node_modules/test-a-cli/node_modules/#eslint/eslintrc/lib/config-array-factory.js
- /Users/a1/.nvm/versions/node/v14.16.1/lib/node_modules/test-a-cli/node_modules/#eslint/eslintrc/lib/index.js
- /Users/a1/.nvm/versions/node/v14.16.1/lib/node_modules/test-a-cli/node_modules/eslint/lib/cli-engine/cli-engine.js
- /Users/a1/.nvm/versions/node/v14.16.1/lib/node_modules/test-a-cli/node_modules/eslint/lib/cli-engine/index.js
- /Users/a1/.nvm/versions/node/v14.16.1/lib/node_modules/test-a-cli/node_modules/eslint/lib/api.js
- /Users/a1/.nvm/versions/node/v14.16.1/lib/node_modules/test-a-cli/node_modules/test-a-es-checker/dist/index.js
- /Users/a1/.nvm/versions/node/v14.16.1/lib/node_modules/test-a-cli/dist/index.js
I resolved it by provide cwd for babelOption in eslintrc.
module.exports = {
...
parser: '#babel/eslint-parser',
babelOptions: {
cwd: __dirname, // The working directory that all paths in the programmatic options will be resolved relative to.
presets: ['#babel/preset-env', '#babel/preset-react', '#babel/preset-typescript'],
plugins: [
['#babel/plugin-proposal-decorators', { legacy: true }],
['#babel/plugin-proposal-class-properties', { loose: true }],
],
},
...
};
The Story:
Currently, we are extending the recommended ESLint configuration:
{
"extends": "eslint:recommended",
...
"plugins": [
"angular",
"jasmine",
"protractor"
],
"rules": {
"no-multiple-empty-lines": 2,
"no-trailing-spaces": 2,
"jasmine/valid-expect": 2
}
}
And also using angular, jasmine and protractor ESLint plugins which also ship with their own recommended configurations (default rule strictness levels and default rule parameters).
The Question:
How can we use all the recommended configurations at the same time - the one that ESLint and all the used plugins ship with?
Tried the following:
{
"extends": [
"eslint:recommended",
"plugin:protractor/recommended",
"plugin:jasmine/recommended",
"plugin:angular/recommended"
],
...
}
but got the following error:
Cannot read property 'recommended' of undefined
How can we use all the recommended configurations at the same time -
the one that ESLint and all the used plugins ship with?
Your syntax is correct, and multiple extensions are loaded like this:
{
"extends": [
"eslint:recommended",
"plugin:protractor/recommended",
"plugin:jasmine/recommended",
"plugin:angular/recommended"
]
}
However, this requires that the plugins in question actually come bundled with recommended settings. eslint-plugin-angular does not, and you have to install it yourself:
npm install --save-dev eslint-config-angular
Change your eslint settings to
{
"extends": [
"eslint:recommended",
"plugin:protractor/recommended",
"plugin:jasmine/recommended",
"angular"
]
}
and it should work.
I'm attempting to use the ESLint linter with the Jest testing framework.
Jest tests run with some globals like jest, which I'll need to tell the linter about; but the tricky thing is the directory structure, with Jest the tests are embedded with the source code in __tests__ folders, so the directory structure looks something like:
src
foo
foo.js
__tests__
fooTest.js
bar
bar.js
__tests__
barTest.js
Normally, I'd have all my tests under a single dir, and I could just add an .eslintrc file there to add the globals... but I certainly don't want to add a .eslintrc file to every single __test__ dir.
For now, I've just added the test globals to the global .eslintrc file, but since that means I could now reference jest in non-testing code, that doesn't seem like the "right" solution.
Is there a way to get eslint to apply rules based on some pattern based on the directory name, or something like that?
The docs show you are now able to add:
"env": {
"jest/globals": true
}
To your .eslintrc which will add all the jest related things to your environment, eliminating the linter errors/warnings.
You may need to include plugins: ["jest"] to your esconfig, and add the eslint-plugin-jest plugin if it still isn't working.
ESLint supports this as of version >= 4:
/*
.eslintrc.js
*/
const ERROR = 2;
const WARN = 1;
module.exports = {
extends: "eslint:recommended",
env: {
es6: true
},
overrides: [
{
files: [
"**/*.test.js"
],
env: {
jest: true // now **/*.test.js files' env has both es6 *and* jest
},
// Can't extend in overrides: https://github.com/eslint/eslint/issues/8813
// "extends": ["plugin:jest/recommended"]
plugins: ["jest"],
rules: {
"jest/no-disabled-tests": "warn",
"jest/no-focused-tests": "error",
"jest/no-identical-title": "error",
"jest/prefer-to-have-length": "warn",
"jest/valid-expect": "error"
}
}
],
};
Here is a workaround (from another answer on here, vote it up!) for the "extend in overrides" limitation of eslint config :
overrides: [
Object.assign(
{
files: [ '**/*.test.js' ],
env: { jest: true },
plugins: [ 'jest' ],
},
require('eslint-plugin-jest').configs.recommended
)
]
From https://github.com/eslint/eslint/issues/8813#issuecomment-320448724
You can also set the test env in your test file as follows:
/* eslint-env jest */
describe(() => {
/* ... */
})
To complete Zachary's answer, here is a workaround for the "extend in overrides" limitation of eslint config :
overrides: [
Object.assign(
{
files: [ '**/*.test.js' ],
env: { jest: true },
plugins: [ 'jest' ],
},
require('eslint-plugin-jest').configs.recommended
)
]
From https://github.com/eslint/eslint/issues/8813#issuecomment-320448724
As of 2021, I think the correct way or at least the one that works is to install #types/jest and eslint-plugin-jest:
npm i -D eslint-plugin-jest #types/jest
And adding the Jest plugin into .eslintrc.js with the overrides instruction mentioned by #Loren:
module.exports = {
...
plugins: ["jest"],
...
overrides: [
{
files: ["**/*.test.js"],
env: { "jest/globals": true },
plugins: ["jest"],
extends: ["plugin:jest/recommended"],
},
],
...
};
This way you get linting errors in your source files as well as in test files, but in test files you don't get linting errors for test and other Jest's functions, but you will get them in your source files as they will appear as undefined there.
I solved the problem REF
Run
# For Yarn
yarn add eslint-plugin-jest -D
# For NPM
npm i eslint-plugin-jest -D
And then add in your .eslintrc file
{
"extends": ["airbnb","plugin:jest/recommended"],
}
some of the answers assume you have eslint-plugin-jest installed, however without needing to do that, you can simply do this in your .eslintrc file, add:
"globals": {
"jest": true,
}
First install eslint-plugin-jest
Running:
yarn add eslint-plugin-jest or npm install eslint-plugin-jest
Then edit .eslintrc.json
{
"env":{
"jest": true
}
}
As of ESLint V 6 (released in late 2019), you can use extends in the glob based config as follows:
"overrides": [
{
"files": ["*.test.js"],
"env": {
"jest": true
},
"plugins": ["jest"],
"extends": ["plugin:jest/recommended"]
}
]
Add environment only for __tests__ folder
You could add a .eslintrc.yml file in your __tests__ folders, that extends you basic configuration:
extends: <relative_path to .eslintrc>
env:
jest: true
If you have only one __tests__folder, this solution is the best since it scope jest environment only where it is needed.
Dealing with many test folders
If you have more test folders (OPs case), I'd still suggest to add those files. And if you have tons of those folders can add them with a simple zsh script:
#!/usr/bin/env zsh
for folder in **/__tests__/ ;do
count=$(($(tr -cd '/' <<< $folder | wc -c)))
echo $folder : $count
cat <<EOF > $folder.eslintrc.yml
extends: $(printf '../%.0s' {1..$count}).eslintrc
env:
jest: true
EOF
done
This script will look for __tests__ folders and add a .eslintrc.yml file with to configuration shown above. This script has to be launched within the folder containing your parent .eslintrc.
Pattern based configs are scheduled for 2.0.0 release of ESLint. For now, however, you will have to create two separate tasks (as mentioned in the comments). One for tests and one for the rest of the code and run both of them, while providing different .eslintrc files.
P.S. There's a jest environment coming in the next release of ESLint, it will register all of the necessary globals.
I got it running after spending some time trying out different options. Hope this helps anyone else getting stuck.
.eslintrc.json (in root project folder):
{
"env": {
"browser": true,
"es2021": true,
"jest/globals": true
},
"extends": [
"standard",
"plugin:jest/all"
],
"parser": "#babel/eslint-parser",
"parserOptions": {
"ecmaVersion": 12,
"sourceType": "module"
},
"rules": {
"jest/no-hooks": [
"error",
{
"allow": [
"afterEach",
"beforeEach"
]
}
]
},
"plugins": [
"jest"
]
}
Empty .babelrc (in root project folder):
{}
.package.json (in root project folder):
{
"scripts": {
"test": "jest",
"lint": "npx eslint --format=table .",
"lintfix": "npx eslint --fix ."
},
"devDependencies": {
"#babel/core": "^7.15.0",
"#babel/eslint-parser": "^7.15.0",
"aws-sdk-mock": "^5.2.1",
"eslint": "^7.32.0",
"eslint-config-standard": "^16.0.3",
"eslint-plugin-import": "^2.24.0",
"eslint-plugin-jest": "^24.4.0",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^5.1.0",
"jest": "^27.0.6"
}
}
VS Code settings.xml (editor configuration: enables auto fix on save + babel parser):
"eslint.alwaysShowStatus": true,
"eslint.format.enable": true,
"eslint.lintTask.enable": true,
"eslint.options": {
"parser": "#babel/eslint-parser"
},
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"eslint.validate": [
"javascript"
]
In your .eslintignore file add the following value:
**/__tests__/
This should ignore all instances of the __tests__ directory and their children.