Jest integration with Adonis V5 - node.js

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.

Related

How to call variables and function from outside of a module in NestJS?

I have some helper functions inside the /src/common/helper/cash.helper.ts. When I call this function from a module, I get the following error.
Error: Cannot find module './../../../src/common/helper/cast.helper' Require stack:
However, the e2e tests are working without any problem. Here, you can see the folder structure.
When I change the import to absolute path import { toNumber } from 'src/common/helper/cast.helper'; It's working, but the e2e tests are not working.
What's wrong here? How can I use common functions and constants across all the modules in NestJS?
I did the following to fix the issue.
I changed the import to absolute path import { toNumber } from 'src/common/helper/cast.helper';
To fix the e2e test, I have added "moduleDirectories": ["<rootDir>/../", "node_modules"] to the jest-e2e.json.

Next.js: Jest fails to process a file inside `node_modules`, giving error `Cannot use import statement outside a module`

I'm using Next.js, and I setup Jest using the official instructions. However, when I run a test for a component that uses the remark module, I get the following error:
Jest encountered an unexpected token
Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax.
Out of the box Jest supports Babel, which will be used to transform your files into valid JS based on your Babel configuration.
By default "node_modules" folder is ignored by transformers.
Here's what you can do:
• If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/ecmascript-modules for how to enable it.
• If you are trying to use TypeScript, see https://jestjs.io/docs/getting-started#using-typescript
• 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/configuration
For information about custom transformations, see:
https://jestjs.io/docs/code-transformation
Details:
/Users/mk/Code/github/hollowverse/hollowverse/node_modules/remark/index.js:1
({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,jest){import {unified} from 'unified'
^^^^^^
SyntaxError: Cannot use import statement outside a module
1 | import matter from 'gray-matter';
> 2 | import { remark } from 'remark';
| ^
3 | import remarkHtml from 'remark-html';
4 | import { sanityClient } from '~/lib/components/sanityio';
5 | import { groqRelatedPeople } from '~/lib/[celeb]/getStaticProps/groqRelatedPeople';
at Runtime.createScriptFromCode (node_modules/jest-runtime/build/index.js:1728:14)
at Object.<anonymous> (lib/[celeb]/getStaticProps/getParsedOldContent.ts:2:32)
My jest.config.js is the following
// jest.config.js
const tsconfig = require('./tsconfig.json');
const moduleNameMapper = require('tsconfig-paths-jest')(tsconfig);
const nextJest = require('next/jest');
const createJestConfig = nextJest({
// Provide the path to your Next.js app to load next.config.js and .env files in your test environment
dir: './',
});
// Add any custom config to be passed to Jest
const customJestConfig = {
// Add more setup options before each test is run
// setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
// if using TypeScript with a baseUrl set to the root directory then you need the below for alias' to work
moduleDirectories: ['node_modules', '<rootDir>/'],
testEnvironment: 'jest-environment-jsdom',
moduleNameMapper: {
...moduleNameMapper,
'^lodash-es$': 'lodash',
},
};
// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
module.exports = createJestConfig(customJestConfig);
Anyone know how to solve this problem?
I also encountered the same error while setting up Jest in my React app created using Webpack. I had to add #babel/preset-env and it was fixed. I have also written a blog article about the same.
{
"presets": ["#babel/react", "#babel/env"]
}

Jest Cannot find module 'cls-hooked'

I try to implement e2e tests in a simple NestJS project (generate with 'nest new' command, add some dependencies and config Project here), the existing tests (not e2e ones) run properly (all modules are resolved).
But, when I execute the default app.e2e-spec.ts (generated by nest cli), I got this following error
Cannot find module 'cls-hooked' from 'src/common/http-context.ts'
The only difference between working and broke version is the test file selected (in the jest config)
testRegex: '.e2e-spec.ts$', // Doesn't work
// testRegex: '.*\\.spec\\.ts$', // Work
The module resolution with TS works well when I look for the trace of tsc --traceResolution
The project is running correctly and works well. Then, globally, Jest & TS are well configured
The error is the same when I use a relative path
// src/common/http-context.ts
// import * as cls from 'cls-hooked';
import * as cls from '../../node_modules/#types/cls-hooked';
When I explicitly set the path to module
moduleNameMapper: {
'cls-hooked': '<rootDir>/node_modules/#types/cls-hooked/index.d.ts',
},
The error suggest an interoperability issue
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:
C:\Users\taquet_p\Sources\nestJsGrapQLTest\test-jest-imports\node_modules\#types\cls-hooked\index.d.ts:8
import { EventEmitter } from 'events';
^^^^^^
SyntaxError: Cannot use import statement outside a module
> 1 | import * as cls from 'cls-hooked';
| ^
2 |
3 | export class HttpContext {
4 |
at Runtime.createScriptFromCode (node_modules/jest-runtime/build/index.js:1350:14)
at Object.<anonymous> (src/common/http-context.ts:1:1)
I tried to configure support for ECMAScript module and other things in tsconfig.json and jest.config.js
I check most of SOF questions about Cannot find module within Jest
I have no more ideas to googling about.
Do anyone has a solution ? A suggestion ?
The github repo provide the atomic project to reproduce the issue directly. Just pull, npm install & run test (the jest config file is a bit dirty :-) )

create-react-app and jest - Cannot use import statement outside a module

I am using create-react-app. Running jest from the CLI causes this error (though in VS Code it shows in my test file that my test passes):
(base) ➜ component-library git:(setup) ✗ jest
FAIL src/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:
• 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:
/Users/Me/go/src/gitlab.com/tensile-payments/component-library/src/setupTests.js:5
import '#testing-library/jest-dom';
^^^^^^
SyntaxError: Cannot use import statement outside a module
at Runtime.createScriptFromCode (node_modules/jest-runtime/build/index.js:1350:14)
My setupTests.js file looks like this:
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '#testing-library/jest-dom';
import Enzyme from 'enzyme';
import Adapter from '#wojtekmaj/enzyme-adapter-react-17';
Enzyme.configure({ adapter: new Adapter() });
I understand from another question's answer that Jest Babel-transforms files before running tests, which should get rid of these import statements. I haven't ejected and so haven't changed the babel config. Other people had the issue that node modules weren't being transformed because the default config excludes them, but this error isn't coming from a node module. How can I fix this?
I fixed it by running tests with npm run test instead of jest, as well as removing
"jest": {
"setupFilesAfterEnv": [
"<rootDir>src/setupTests.js"
]
}
from my package.json (though Enzyme instructs to include it when using 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.

Resources