How can I configure jest to handle SvelteKit's `$lib` alias? - jestjs

When I try to run a test in jest that imports a file using the $lib alias, jest fails to run with the error Cannot find module '$lib/...'.

Add the following to jest.config.cjs:
module.exports = {
...,
moduleNameMapper: { '^\\$lib(.*)$': '<rootDir>/src/lib$1' },
}
The documentation can be found under Using with webpack.

Related

"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;
}

Jest resolver cannot find a file which path ressembles existing node modules

I have upgraded jest from version 27 to version 29.
Since then, some indirect file resolve do not work anymore.
Here is my config:
module.exports = {
roots: ['app/javascript/__tests__/'],
testMatch: ['**/?(*.)(spec|test).js?(x)'],
testEnvironment: 'jsdom',
testRunner: 'jest-jasmine2',
moduleDirectories: [
'node_modules',
'app/javascript',
'app/javascript/__tests__'
],
transform: {
'^.+\\.jsx?$': 'babel-jest'
},
moduleNameMapper: {
'^.+\\.(svg)$': '<rootDir>/app/javascript/__tests__/fileMock.js'
},
setupFiles: ['./app/javascript/__tests__/setup.jsx']
}
There is a file in my code base, let's say app/javascript/MyReactComponent.jsx, which is imported as part of my tested component, and that contains the following import line:
// app/javascript/MyReactComponent.jsx
import 'firebase/init'
Expected behavior
Until today, I could run jest, and it was finding all my code as expected, inclluding the above import, which is located here:
app/javascript/firebase/init.js
Error
Instead, running jest throws the following error.
Cannot find module 'firebase/init' from 'app/javascript/MyReactComponent.jsx'
FWIW, I have traced the resolver code up to the default jest resolver, and it seems like it tries to get the file from within the firebase node module, instead of fetching the init.js file in the firebase directory.
Question
Is there a way to adjust my configuration in order for the resolver to find my file?
Have you tried adding an entry for that aliased module in moduleNameMapper?
moduleNameMapper: {
'^.+\\.(svg)$': '<rootDir>/app/javascript/__tests__/fileMock.js',
'^firebase/init': '<rootDir>/app/javascript/firebase/init.js'
}

Jest integration with Adonis V5

I'm developing an application with Adonis v5 but its test runner isn't finished. So, my workaround is to apply Jest in its place. I got this working but I'm having trouble importing types from Adonis.
In any model, I have the following import:
import { BaseModel, column } from '#ioc:Adonis/Lucid/Orm'
To solve the alias, I added this rule in jest.config.js:
moduleNameMapper: {
'^App(.*)$': '<rootDir>/app$1',
'^#ioc:Adonis/Lucid/Database$': '#adonisjs/lucid/build/src/Database/index.js',
'^#ioc:Adonis/Lucid/Orm$': '#adonisjs/lucid/build/adonis-typings/orm.d.ts',
//'^#ioc:Adonis/Core/Validator$': '',
},
The third rule points to the previous import. Inside of the pointed file, I found the declaration of a module which exports the desired types.
declare module '#ioc:Adonis/Lucid/Orm' {
import { ScopeFn, LucidModel, HooksDecorator, ...
...
The complete file is this.
When I run Jest, I get this error. What am I missing? Before I forget, I'm using ts-jest to define the settings of Jest.

How to import a node module inside an angular web worker?

I try to import a node module inside an Angular 8 web worker, but get an compile error 'Cannot find module'. Anyone know how to solve this?
I created a new worker inside my electron project with ng generate web-worker app, like described in the above mentioned ng documentation.
All works fine until i add some import like path or fs-extra e.g.:
/// <reference lib="webworker" />
import * as path from 'path';
addEventListener('message', ({ data }) => {
console.log(path.resolve('/'))
const response = `worker response to ${data}`;
postMessage(response);
});
This import works fine in any other ts component but inside the web worker i get a compile error with this message e.g.
Error: app/app.worker.ts:3:23 - error TS2307: Cannot find module 'path'.
How can i fix this? Maybe i need some additional parameter in the generated tsconfig.worker.json?
To reproduce the error, run:
$ git clone https://github.com/hoefling/stackoverflow-57774039
$ cd stackoverflow-57774039
$ yarn build
Or check out the project's build log on Travis.
Note:
1) I only found this as a similar problem, but the answer handles only custom modules.
2) I tested the same import with a minimal electron seed which uses web workers and it worked, but this example uses plain java script without angular.
1. TypeScript error
As you've noticed the first error is a TypeScript error. Looking at the tsconfig.worker.json I've found that it sets types to an empty array:
{
"compilerOptions": {
"types": [],
// ...
}
// ...
}
Specifying types turns off the automatic inclusion of #types packages. Which is a problem in this case because path has its type definitions in #types/node.
So let's fix that by explicitly adding node to the types array:
{
"compilerOptions": {
"types": [
"node"
],
// ...
}
// ...
}
This fixes the TypeScript error, however trying to build again we're greeted with a very similar error. This time from Webpack directly.
2. Webpack error
ERROR in ./src/app/app.worker.ts (./node_modules/worker-plugin/dist/loader.js!./src/app/app.worker.ts)
Module build failed (from ./node_modules/worker-plugin/dist/loader.js):
ModuleNotFoundError: Module not found: Error: Can't resolve 'path' in './src/app'
To figure this one out we need to dig quite a lot deeper...
Why it works everywhere else
First it's important to understand why importing path works in all the other modules. Webpack has the concept of targets (web, node, etc). Webpack uses this target to decide which default options and plugins to use.
Ordinarily the target of a Angular application using #angular-devkit/build-angular:browser would be web. However in your case, the postinstall:electron script actually patches node_modules to change that:
postinstall.js (parts omitted for brevity)
const f_angular = 'node_modules/#angular-devkit/build-angular/src/angular-cli-files/models/webpack-configs/browser.js';
fs.readFile(f_angular, 'utf8', function (err, data) {
var result = data.replace(/target: "electron-renderer",/g, '');
var result = result.replace(/target: "web",/g, '');
var result = result.replace(/return \{/g, 'return {target: "electron-renderer",');
fs.writeFile(f_angular, result, 'utf8');
});
The target electron-renderer is treated by Webpack similarily to node. Especially interesting for us: It adds the NodeTargetPlugin by default.
What does that plugin do, you wonder? It adds all known built in Node.js modules as externals. When building the application, Webpack will not attempt to bundle externals. Instead they are resolved using require at runtime. This is what makes importing path work, even though it's not installed as a module known to Webpack.
Why it doesn't work for the worker
The worker is compiled separately using the WorkerPlugin. In their documentation they state:
By default, WorkerPlugin doesn't run any of your configured Webpack plugins when bundling worker code - this avoids running things like html-webpack-plugin twice. For cases where it's necessary to apply a plugin to Worker code, use the plugins option.
Looking at the usage of WorkerPlugin deep within #angular-devkit we see the following:
#angular-devkit/src/angular-cli-files/models/webpack-configs/worker.js (simplified)
new WorkerPlugin({
globalObject: false,
plugins: [
getTypescriptWorkerPlugin(wco, workerTsConfigPath)
],
})
As we can see it uses the plugins option, but only for a single plugin which is responsible for the TypeScript compilation. This way the default plugins, configured by Webpack, including NodeTargetPlugin get lost and are not used for the worker.
Solution
To fix this we have to modify the Webpack config. And to do that we'll use #angular-builders/custom-webpack. Go ahead and install that package.
Next, open angular.json and update projects > angular-electron > architect > build:
"build": {
"builder": "#angular-builders/custom-webpack:browser",
"options": {
"customWebpackConfig": {
"path": "./extra-webpack.config.js"
}
// existing options
}
}
Repeat the same for serve.
Now, create extra-webpack.config.js in the same directory as angular.json:
const WorkerPlugin = require('worker-plugin');
const NodeTargetPlugin = require('webpack/lib/node/NodeTargetPlugin');
module.exports = (config, options) => {
let workerPlugin = config.plugins.find(p => p instanceof WorkerPlugin);
if (workerPlugin) {
workerPlugin.options.plugins.push(new NodeTargetPlugin());
}
return config;
};
The file exports a function which will be called by #angular-builders/custom-webpack with the existing Webpack config object. We can then search all plugins for an instance of the WorkerPlugin and patch its options adding the NodeTargetPlugin.

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