Jest, ES6 modules "does not provide export named" - node.js

I have an express app with a CRUD API (with sequelize) and I want to test it with Jest. I'm pretty new in unit-testing so I follow this guide, recommended by Jest's website.
The problem I have is that my app is built with ES6 modules and Jest ES6 modules is experimental and it seems that it doesn't "import" packages.
I have this test (took from the guide)
import request from 'supertest';
import app from '../app';
describe('Test the root path', () => {
test('It should response the GET method', done => {
request(app)
.get('/')
.then(response => {
expect(response.statusCode).toBe(404);
done();
});
});
});
And when I launched it (with NODE_OPTIONS=--experimental-vm-modules npx jest I had to follow this jest wiki page), It says that
'sequelize' does not provide an export named 'DataTypes' and when I launch my app normally (like with npm start) it works fine, without any problems.
(the complete error log):
(node:49576) ExperimentalWarning: VM Modules is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
FAIL __tests__/app_test.js
● Test suite failed to run
SyntaxError: The requested module 'sequelize' does not provide an export named 'DataTypes'
at Runtime.linkAndEvaluateModule (node_modules/jest-runtime/build/index.js:779:5)
at TestScheduler.scheduleTests (node_modules/#jest/core/build/TestScheduler.js:333:13)
at runJest (node_modules/#jest/core/build/runJest.js:404:19)
at _run10000 (node_modules/#jest/core/build/cli/index.js:320:7)
at runCLI (node_modules/#jest/core/build/cli/index.js:173:3)
(and my Jest config)
// Sync object
/** #type {import('#jest/types').Config.InitialOptions} */
export default async () => {
return {
verbose: true,
transform: {},
};
};
Am I doing something wrong ? Should I change to commonJS instead of ES6
Thank you.

This is a known problem in Jest: #9771. It is said to be fixed in jest#28.0.0-alpha.0.
An interesting hack to work around this problem is to remove the main field from the package.json of the imported project.

Related

nestjs testing - how to start a server with jest mocked modules

I already have e2e backend tests running using jest and some mocked modules using fastify and now that when I use app.listen it will be able to receive calls.
My problem:
I would like to start the nestjs server with the mocked jest modules to be able to call it from outside without the jest scope e.g. for vulnerability scanning of the api endpoints. I have some external dependencies that I want to exclude - this is why I would like to use my jest mocked nestjs modules.
if I try to used ts-node an error will be thrown in those mocked modules
command:
npx ts-node run.ts
file: run.ts
import { initializeApplication } from './app';
(async () => {
const app = await initializeApplication();
app.listen(3005, '0.0.0.0');
})();
error:
jest.fn(
^
ReferenceError: jest is not defined
I would like to be able to start the nestjs backend with jest mocked nestjs modules to make external api calls against the backend using fake dependencies.

"Cannot use import statement outside a module" with Axios

I have a Vue.js application where two files contain:
import axios from "axios"
These files are located in src/lib within the application and include the import statement on their first line.
Running tests on Github causes Axios 1.0.0 to be installed, no matter what the package.json says, and now any test involving these files fails with the above error.
Changing the statement to const axios = require("axios") fails also; node_modules/axios/index.js contains an import statement on line 1 and the exception is thrown there.
A suggestion I've seen quite often for such issues is to add "type": "module" to package.json (which is at the same level as src/). This causes all tests to fail with a demand to rename vue.config.js as vue.config.cjs. Doing that gets me: Error: You appear to be using a native ECMAScript module configuration file, which is only supported when running Babel asynchronously, which I do not understand.
Can anyone suggest what to do here?
I was able to fix this error by forcing jest to import the commonjs axios build by adding
"jest": {
"moduleNameMapper": {
"axios": "axios/dist/node/axios.cjs"
}
},
to my package.json. Other solutions using transformIgnorePatterns didn't work for me.
The 1.x.x version of axios changed the module type from CommonJS to ECMAScript.
The 0.x.x version of axios index.js file
module.exports = require('./lib/axios');
The 1.x.x version of axiox index.js file
import axios from './lib/axios.js';
export default axios;
Basically, jest runs on Node.js environment, so it uses modules following the CommonJS.
If you want to use axios up to 1.x.x, you have to transpile the JavaScript module from ECMAScript type to CommonJS type.
Jest ignores /node_modules/ directory to transform basically.
https://jestjs.io/docs/27.x/configuration#transformignorepatterns-arraystring
So you have to override transformIgnorePatterns option.
There are two ways to override transformIgnorePatterns option.
jest.config.js
If your vue project uses jest.config.js file, you add this option.
module.exports = {
preset: "#vue/cli-plugin-unit-jest",
transformIgnorePatterns: ["node_modules/(?!axios)"],
...other options
};
package.json
If your vue project uses package.json file for jest, you add this option.
{
...other options
"jest": {
"preset": "#vue/cli-plugin-unit-jest",
"transformIgnorePatterns": ["node_modules\/(?!axios)"]
}
}
This regex can help you to transform axios module and ignore others under node_modules directory.
https://regexper.com/#node_modules%5C%2F%28%3F!axios%29
Updating the version of jest to v29 fixed this in my project. It could be the case that you have an incompatible jest version.
I had the same issues and was able to solve this by using jest-mock-axios library
I experience similar problem but the error is caused by jest.
All the tests trying to import axios fail and throw the same exception:
Test suite failed to run
Jest encountered an unexpected token
This usually means that you are trying to import a file which Jest cannot parse, e.g. it's not plain JavaScript.
By default, if Jest sees a Babel config, it will use that to transform 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 specify a custom "transformIgnorePatterns" in your config.
• If you need a custom transformation specify a "transform" option in 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:
/monorepo/node_modules/axios/index.js:1
({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){import axios from './lib/axios.js';
^^^^^^
SyntaxError: Cannot use import statement outside a module
1 | import { describe, expect, it } from '#jest/globals'
> 2 | import axios from 'axios'
The solution is simply tell jest that axios should be transformed with babel:
const esModules = ['lodash-es', 'axios'].join('|')
# add these entries in module.exports
transform: {
[`^(${esModules}).+\\.js$`]: 'babel-jest',
},
transformIgnorePatterns: [`node_modules/(?!(${esModules}))`],
Note: I'm using Quasar Vue and this is their implementation.
Quick fix
Update the npm run test script from
"test": "react-scripts test",
to
"test": "react-scripts test --transformIgnorePatterns \"node_modules/(?!axios)/\"",
In my case I had to add the following line to the moduleNameMapper object in the jest config:
axios: '<rootDir>/node_modules/axios/dist/node/axios.cjs',
I had the same issue, it works fine when changing axios to fetch.
axios (Fail)
try {
const response = await axios("api/fruit/all");
return response.data;
} catch (error) {
return error;
}
Fetch (Works fine)
try {
const response = await fetch("api/fruit/all",{method:"GET"});
const data = await response.json();
return data;
} catch (error) {
return error;
}

Why does Jest needs Babel to test async code?

The Jest "An Async Example" guide starts with:
First, enable Babel support in Jest...
But I miss to see why and where does Jest needs Babel for.
Node.js has supported async functions by default since version 7.6.0, so (as you suspected) Babel is not needed for Jest to run tests using async functions.
I just confirmed this by installing only Jest v24.6.0 and ran this test with Node.js v10.15.1:
test('hi', async () => {
const val = await Promise.resolve('hello');
expect(val).toBe('hello');
});
...and it passed just fine.
On the other hand, Babel is required to use ES6 module syntax.
Many of the examples in the "An Async Example" doc use ES6 module syntax (export default ..., import * as ..., etc.) so Babel is required for any of those examples to work.

Why simple babel plugin breaks jest test?

I wrote a simple plugin:
export default function() {
return {
visitor: {
Program: {
enter() {
console.log('Program Entered!');
},
exit() {
console.log('Program Exited!');
},
},
},
};
}
when I run webpack - it compiles my project successfully
but when I run jest test - it fails with error:
Starting script: test
FAIL unit-tests src/common/App.test.js
● Test suite failed to run
Jest encountered an unexpected token
This usually means that you are trying to import a file which Jest cannot parse, e.g. it's not plain JavaScript.
By default, if Jest sees a Babel config, it will use that to transform your files, ignoring "node_modules".
Here's what you can do:
• To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
• If you need a custom transformation specify a "transform" option in 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:
/../project/config/babel/myplugin.js:1
(function (exports, require, module, __filename, __dirname) { export default function() {
^^^^^^
SyntaxError: Unexpected token export
if I remove my plugin from babel configuration - tests pass
why it complaints on my plugin? it is located outside jests rootDir - src.
what is wrong?

How to test Angular2 pipe in nodejs with mocha without karma

I'd like to be able to test an Angular2 pipe purely in nodejs environment without including karma etc.
It is possible to use typescript files as test suites for mocha
https://templecoding.com/blog/2016/05/05/unit-testing-with-typescript-and-mocha/
But when I have a import {Pipe} from '#angular/core' it gives me
/Users/foo/node_modules/#angular/core/src/util/decorators.js:173
throw 'reflect-metadata shim is required when using class decorators';
^
reflect-metadata shim is required when using class decorators
Even if I write require('reflect-metadata') in my test file it still breaks with the same error.
Angular internally has this check:
(function checkReflect() {
if (!(Reflect && Reflect.getMetadata)) {
throw 'reflect-metadata shim is required when using class decorators';
}
})();
And after requireing reflect-matadata I indeed have Reflect on the global object, however it still doesn't work...
Anyway is there a way to test an Angular pipe purley in nodejs with mocha?
I'm using webpack to bundle the app so the file I'm requring in my test file looks like this:
import {Pipe} from '#angular/core';
#Pipe({
name: 'filterBy'
})
export class FilterByPipe {
transform(items = [], prop, val) {
return items.filter(someFilteringAlgorithm(prop, val));
}
}
I didn't test pipes yet, but here a post that explain how to test Angular 2 application in Node with Mocha. But It use Webpack instead of ts-node : Here
Hope It can help.

Resources