How to collect code coverage for spawned sub-processes using Jest? - node.js

I am writing integration tests for a CLI. All the unit tests are written using Jest as out-of-the-box it produces code coverage without any configuration, but unfortunately it does not instrument sub-processes, for example executed via Node's spawn and fork commands.
I have tried to introduce nyc into the mix as suggested in this comment on a GitHub issue however it has not worked for me.
I have played with various configurations (based on the initial aforementioned suggestion and also ideas from these issues: 1, 2), however either I get no coverage statistics at all or only coverage for my unit tests, not the integration tests that spawn sub-processes.
The relevant parts of my package.json which configures nyc and Jest:
"scripts": {
"test": "cross-env NODE_ENV=test nyc --clean jest --coverage",
},
"jest": {
"verbose": true,
"testURL": "http://localhost/",
"globalSetup": "./jest.setup.js",
"transform": {
"^.+\\.js$": "babel-jest"
},
"collectCoverage": false
},
"nyc": {
"include": [
"packages/*/src/**/*.js"
],
"reporter": [
"html"
]
},
I am using execa to run the sub-processes and do so as follows:
await execa("nyc --reporter none node", args);

Related

Jest fails when script imports external library

I just started testing with Jest for the first time. This is a very simple Typescript utils library. I basically followed the installation guide, and my first two tests passed. Yay. The next function I tested imported a few functions from lodash-es, and failed (error below).
This is how I added jest:
pnpm add -D jest ts-jest #types/jest
pnpx ts-jest config:init
My jest.config.js file, as generated by pnpx ts-jest config:init, except that I had to change the second line to export default, because my package.json specifies this to be a module.
/** #type {import('ts-jest').JestConfigWithTsJest} */
export default {
preset: 'ts-jest',
testEnvironment: 'node',
transformIgnorePatterns: [
"node_modules/(?!(lodash-es)/)"
],
moduleNameMapper: {
'lodash-es': '../node_modules/lodash-es/lodash.js',
},
}
I've tried adding transformIgnorePatterns and moduleNameMapper, based on others with similar issues. But no luck.
The error message:
D:\Dev\NodeJs\vexna\packages\util>pnpm test
> #vexna/util#1.0.0 test D:\Dev\NodeJs\vexna\packages\util
> jest
PASS test/reverseString.test.ts
PASS test/hash.test.ts
FAIL test/uid.test.ts
● Test suite failed to run
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:
D:\Dev\NodeJs\vexna\node_modules\.pnpm\lodash-es#4.17.21\node_modules\lodash-es\lodash.js:10
export { default as add } from './add.js';
^^^^^^
SyntaxError: Unexpected token 'export'
> 1 | import { now, random, padStart, padEnd } from "lodash-es"
| ^
2 | import { reverseString } from "./reverseString"
3 |
4 |
at Runtime.createScriptFromCode (../../node_modules/.pnpm/jest-runtime#29.4.1/node_modules/jest-runtime/build/index.js:1598:14)
at Object.<anonymous> (src/uid.ts:1:1)
at Object.<anonymous> (test/uid.test.ts:1:1)
Test Suites: 1 failed, 2 passed, 3 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 3.494 s
Ran all test suites.
 ELIFECYCLE  Test failed. See above for more details.
A few things, this project exists in pnpm monorepo, in case it matters.
This is the package.json:
{
"name": "#vexna/util",
"version": "1.0.0",
"description": "Generic utilities, uses lodash",
"private": true,
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"sideEffects": false,
"scripts": {
"build": "rimraf dist && tsc && rollup -c rollup.config.js",
"test": "jest",
"pretest": "npm run build"
},
"devDependencies": {
"#rollup/plugin-typescript": "^11.0.0",
"#types/jest": "^29.4.0",
"#types/lodash-es": "^4.17.6",
"jest": "^29.4.1",
"lodash-es": "^4.17.21",
"rimraf": "^4.1.2",
"rollup": "^3.12.1",
"ts-jest": "^29.0.5",
"typescript": "^4.9.5"
},
"files": [
"dist"
],
"peerDependencies": {
"lodash": "^4.17.21"
}
}
I suspect that perhaps Jest goes off into the boonies because I have externalized lodash?
Or perhaps I should add Babel? But could just as well be something else completely.
Some help is much appreciated!
Use Vitest.
Jest is quite painful to install correctly, whereas Vitest works out of the box without any config. This is my personal experience. It took me days to get Jest to sort-of work. I had Vitest working immediately after installation.
I don't want to hate on Jest, I actually think it's a wonderful and intuitive testing tool. But ultimately Vitest "Just Works" (tm), and you can use the same simple Jest style API we all have come to love.
Steps:
pnpm remove jest ts-jest #types/jest
pnpm add -D vite vitest
Delete jest.config.js, and create vite.config.ts:
/// <reference types="vitest" />
import { defineConfig } from 'vite'
export default defineConfig({
test: {
/* for example, use global to avoid globals imports (describe, test, expect): */
// globals: true,
},
})
Add to your tests:
import { assert, expect, test } from 'vitest'
Update your package.json:
"test": "vitest",

Config changes not being picked up when running test coverage with Jest

I am looking for a way to exclude entire folders/files from my testing coverage with Jest. I have gone through the official Jest documentations several times, but have been unable to get it to work. I've come to the conclusion that maybe my jest.config.js isn't being picked up, because no matter what changes I make to the collectCoverageFrom or testPathIgnorePatterns fields, the test coverage return does not change, and it includes everything, even the things I am trying to exclude. Here is my test coverage script in the package.json:
"test-coverage": "npm test --config='jest.config.js' --coverage "
and here is my jest.config.js:
module.exports = {
roots: ['<rootDir>', '<rootDir>/src', '<rootDir>/tests'],
testEnvironment: 'jsdom',
testPathIgnorePatterns: ['/node_modules/'],
collectCoverage: true,
collectCoverageFrom: ['./src/**/*.{js, jsx}', '!**/node_modules/**', '!**/vendor/**'],
coverageDirectory: './coverage',
}
For the files I am excluding, I am just using this format: !src/folder/file.js as per the official jest
documentation.
Any help appreciated.
Things I did to troubleshoot:
I added the path to the Jest config into the test-coverage command, but that was unsuccessful.
I have set the collectCoverage field above to true, no success.
I added the jest config to the actual package.json, but even that
did not change anything. Here is the format for it inside my
package.json:
"jest": {
"roots": ["<rootDir>", "<rootDir>/src","<rootDir>/tests"],
"testEnvironment": "jsdom",
"testPathIgnorePatterns": ["/node_modules/"],
"collectCoverage": true,
"collectCoverageFrom": ["./src/**/*.{js,jsx}","!**/node_modules/**","!**/vendor/**"],
"coverageDirectory": "./coverage"
}

Cannot use import statement outside a module

I'm faced with a problem with my API in Heroku. I have a Node JS API, built with Typescript and hosted in Heroku. Everything looks correct when I try to execute the local script and build the script, but when I need to run the start script, things don't work.
My configurations are:
Node: v18.12.1
NPM: v8.19.2
I have some scripts to transpile .ts files to .js files with babel
"build": "babel src --extensions \".js,.ts\" --out-dir dist --copy-files",
"dev:server": "ts-node-dev -r tsconfig-paths/register --inspect --transpile-only --ignore-watch node_modules src/shared/infra/http/server.ts",
"start": "node dist/shared/infra/http/server.js"
When I execute dev:server and build script, everything runs with success, but when I run the start script, I receive this error:
enter image description here
I checked some points, like my tsconfig.json and my babel.config, but everything looks correct.
module.exports = {
presets: [
['#babel/preset-env', { targets: { node: 'current' } }],
'#babel/preset-typescript',
],
plugins: [
[
'module-resolver',
{
alias: {
'#modules': './src/modules',
'#config': './src/config',
'#shared': './src/shared',
},
},
],
'babel-plugin-transform-typescript-metadata',
['#babel/plugin-proposal-decorators', { legacy: true }],
['#babel/plugin-proposal-class-properties', { loose: true }],
[
'const-enum',
{
transform: 'removeConst',
},
],
],
};
Because of it, when I deploy API in Heroku, I recive this error:
enter image description here.
I don't have an idea why this occur, because about 1 month ago the API was running perfectly on Heroku production instance.
I appreciate it if anyone can help and give me some tips, about this problem.
What I tried
Check and change my npm and node versions
Check babel confgs
Add "type":"module"in my packages.json

Reduce Loopback4 build times in development (build + restart dev server)

I am using Loopback4 to develop api in Node.
I was using the instruction given to build and watch with nodemon
It worked, but it was getting slow, like about 15 seconds in my case.
I search for other other solution and came up with idea of using Turborepo and the nodemon solution.
I wanted to know if there are better solutions or any issues with it
Gist of the solution
run build in watch mode and restart the dev server if js files in dist folder change
use Turbo repo to run these build and restart server tasks
Steps
Setup build and watch with nodemon as described in the thread
you should have something like this in the script
"scripts": {
"dev:server:watch": "nodemon server.js"
}
// watch src folder
// ignore dist
// ext only ts files
// npm start to start the dev server on any changes to the files
"nodemonConfig": {
"verbose": true,
"watch": [
"src/"
],
"ignore": [
"dist/*"
],
"ext": "ts",
"exec": "npm start"
}
Install turbo build system
yarn add turbo --dev
Update nodemon config to restart server on changes in js files in dist folder after build step
"nodemonConfig": {
"verbose": true,
"watch": [
"./dist/"
],
"ext": "js",
"exec": "yarn run start"
},
Add turbo.json
{
"pipeline": {
"dev": {
"dependsOn": ["build:watch", "dev:server:watch"]
},
"build:watch": {
"outputs": [
".dist/**"
]
},
"lint": {
"outputs": []
}
}
}
Add dev script in package.json "scripts"
"dev": "turbo run dev",
Run
yarn run dev
This seems to have reduced the build times to about 3 seconds
Can anyone confirm if this is an acceptable solution, points out any issues
Thanks

Jest test coverage does not fail when threshold is not met

Utilizing create-react-app, when running tests in my CI pipeline, if the code coverage thresholds are not met, I expect the console to return a non-zero response.
package.json
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"test:coverage": "npm run test -- --coverage --watchAll=false",
},
"jest": {
"collectCoverageFrom": [
"src/components/**/*.js",
"src/state/**/*.js",
"src/templates/**/*.js",
"src/routes/**/*.js"
],
"coverageThreshold": {
"global": {
"branches": 80,
"functions": 80,
"lines": 80,
"statements": 80
}
}
}
When running test:coverage the console reports that thresholds were not met, but still returns 0. My understanding from the Jest documentation is that an error should be returned when coverage thresholds are not met.
https://jestjs.io/docs/en/configuration#coveragethreshold-object
Specifically...
If thresholds aren't met, jest will fail.
Is anyone familiar with this issue? I have been through Jest and CRA github issues with mixed results and most findings are related to outdated versions.
To stop further execution when command fails:
command || exit 0
{
"test:coverage": "npm run test -- --coverage --watchAll=false || exit 0"
}
ref: don't fail jenkins build if execute shell fails
Add "collectCoverage": true, in your jest configuration. After that jest run operation will fail, if the coverage is not met.
Defining the config in the packjage.json file did not fail the jest command when coverage threshold was not met. But it worked when I defined it through the jest.config.js file!
This will make the test fail if the conditions are not met, but please note that this will slow down your tests:
"scripts": {
"test": "jest --collectCoverage=true",
"coverage" : "jest --coverage"
},

Resources