What don't I get about babel with istanbul? - node.js

I'm trying to add code coverage to an existing library that transpiles from ES2015. I seem to be doing everything right. My makefile reads
test: .FORCE
export NODE_ENV=test
babel *.js --out-dir lib
nyc mocha
And my babelrc reads
{
"presets": ["es2015"],
"sourceMaps": true,
"env": {
"test": { "plugins": ["istanbul"] }
}
}
while my package.json includes
"nyc": {
"include": [
"**/*.js"
],
"require": [
"babel-register"
],
"sourceMap": false,
"instrument": false
}
And yet, after my tests run, the output simply reads:
----------|----------|----------|----------|----------|----------------|
File | % Stmts | % Branch | % Funcs | % Lines |Uncovered Lines |
----------|----------|----------|----------|----------|----------------|
All files | Unknown | Unknown | Unknown | Unknown | |
----------|----------|----------|----------|----------|----------------|
I've tried what feels like every combination of babel-node, babel-istanbul, and nyc, with various include directives, and I've never gotten it to so much as report a file with 0% coverage, even when using --include-all-sources. What gives? What am I missing?

You need to instrument the code that is getting transpiled. Use this babel plugin: https://github.com/istanbuljs/babel-plugin-istanbul
The package you're using babel-istanbul is a drop-in package meant to be used in replacement of babel.

Related

Jest passing tests but --covering option not picking up files

Problem description:
I have written two tests for a typescript class. Those two tests pass so jest successfully retrieves the test files. I then use the --coverage option but it appears jest is not picking the covered files here.
Here is the output I am getting:
api_jester | PASS src/tests/repositories/user.test.ts
api_jester | User Repository
api_jester | ✓ it should return an empty array (18ms)
api_jester | ✓ should successfully create a user and return its data (7ms)
api_jester |
api_jester | ----------|----------|----------|----------|----------|-------------------|
api_jester | File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
api_jester | ----------|----------|----------|----------|----------|-------------------|
api_jester | All files | 0 | 0 | 0 | 0 | |
api_jester | ----------|----------|----------|----------|----------|-------------------|
api_jester | Test Suites: 1 passed, 1 total
api_jester | Tests: 2 passed, 2 total
api_jester | Snapshots: 0 total
api_jester | Time: 3.208s
api_jester | Ran all test suites.
I have tried playing with the collectCoverageFrom option but without any success. I have tested covering with some simple examples found on github and those were working so the problem is not from my environment. I am guessing I somehow missed something in my configuration but I have spend so much time on this I am getting kind of frustrated so maybe some fresh looks could help..
Project architecture :
config
|__ jest.config.js
|__ tsconfig.json
src
|__tests
| |__repositories
| |__user.test.ts
|__repositories
|___ userRepository
|__User.ts
Jest.config.js :
module.exports = {
preset: "ts-jest",
testEnvironment: "node",
roots: ["../src/tests/"],
transform: {
"^.+\\.tsx?$": "ts-jest"
},
collectCoverageFrom: ["../src/"],
moduleFileExtensions: ["ts", "js", "json"],
coverageDirectory: "../coverage"
};
package.json
{
"name": "theralog_api",
"version": "1.0.0",
"description": "",
"main": "server.js",
"scripts": {
"build": "tsc",
"prettier": "npx prettier --write src/**/*.ts --config ./config/.prettierrc",
"eslint": "npx eslint --config ./config/.eslintrc ./src/**/**/*",
"start:dev": "npx nodemon -L --config ./config/api.nodemon.json",
"test:watch": "npx nodemon -L --config ./config/jester.nodemon.json",
"test:coverage": "npx jest --config ./config/jest.config.js --coverage --colors --watch"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"#types/compression": "^1.0.1",
"#types/express": "^4.17.1",
"#types/graphql-depth-limit": "^1.1.2",
"#types/jest": "^24.0.23",
"#types/node": "^12.7.12",
"#typescript-eslint/eslint-plugin": "^2.5.0",
"#typescript-eslint/parser": "^2.5.0",
"apollo-server-testing": "2.9.7",
"babel-jest": "^24.9.0",
"eslint": "^6.5.1",
"eslint-config-prettier": "^6.4.0",
"graphql-depth-limit": "^1.1.0",
"graphql-import": "^0.7.1",
"graphql-import-node": "0.0.4",
"jest": "^24.9.0",
"nodemon": "^1.19.3",
"prettier": "^1.18.2",
"ts-jest": "^24.1.0",
"ts-node": "^8.4.1",
"tsconfig-paths": "^3.9.0",
"typescript": "^3.7.2"
},
"dependencies": {
"apollo-server-express": "^2.9.6",
"compression": "^1.7.4",
"cors": "^2.8.5",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"graphql": "^14.5.8",
"http": "0.0.0",
"lodash": "^4.17.15",
"ncp": "^2.0.0",
"pg": "^7.12.1",
"winston": "3.2.1"
}
}
jester.nodemon.json
{
"watch": ["../src"],
"ext": "ts",
"exec": "npx jest --config ./config/jest.config.js --watchAll"
}
You are missing a setting in the jest.config.js, collectCoverage: true
module.exports = {
preset: "ts-jest",
testEnvironment: "node",
roots: ["../src/tests/"],
transform: {
"^.+\\.tsx?$": "ts-jest"
},
collectCoverage: true,
collectCoverageFrom: ["../src/"],
moduleFileExtensions: ["ts", "js", "json"],
coverageDirectory: "../coverage"
};
I also use a more descriptive collectCoverageFrom:
collectCoverageFrom: [
'<rootDir>/src/**/*.ts',
'!<rootDir>/src/**/*.interface.ts',
'!<rootDir>/src/**/*.mock.ts',
'!<rootDir>/src/**/*.module.ts',
'!<rootDir>/src/**/*.spec.ts',
'!<rootDir>/src/**/*.test.ts',
'!<rootDir>/src/**/*.d.ts'
],
This way I exclude a number of files I do not want to count coverage from, such as my modules, mocks, and tests.
My full file with the original Jest init process and the comments from that.
For a detailed explanation regarding each configuration property, visit: the Jest documentation
module.exports = {
// All imported modules in your tests should be mocked automatically
// automock: false,
// Stop running tests after the first failure
// bail: false,
// Respect "browser" field in package.json when resolving modules
// browser: false,
// The directory where Jest should store its cached dependency information
// cacheDirectory: "C:\\Users\\sscott\\AppData\\Local\\Temp\\jest",
// Automatically clear mock calls and instances between every test
// clearMocks: false,
// Indicates whether the coverage information should be collected while executing the test
collectCoverage: true,
// An array of glob patterns indicating a set of files for which coverage information should be collected
collectCoverageFrom: [
'<rootDir>/src/**/*.ts',
'!<rootDir>/src/**/*.mock.ts',
'!<rootDir>/src/**/*.module.ts',
'!<rootDir>/src/**/*.spec.ts',
'!<rootDir>/src/**/*.test.ts',
'!<rootDir>/src/**/*.d.ts'
],
// The directory where Jest should output its coverage files
coverageDirectory: "<rootDir>/docs",
// An array of regexp pattern strings used to skip coverage collection
coveragePathIgnorePatterns: [
"\\\\node_modules\\\\"
],
// A list of reporter names that Jest uses when writing coverage reports
coverageReporters: [
"lcov",
"clover",
"text-summary"
],
// An object that configures minimum threshold enforcement for coverage results
// coverageThreshold: null,
// Make calling deprecated APIs throw helpful error messages
errorOnDeprecated: true,
// Force coverage collection from ignored files usin a array of glob patterns
// forceCoverageMatch: [],
// A path to a module which exports an async function that is triggered once before all test suites
// globalSetup: null,
// A path to a module which exports an async function that is triggered once after all test suites
// globalTeardown: null,
// A set of global variables that need to be available in all test environments
globals: {
"ts-jest": {
"diagnostics": false,
"tsConfig": "tsconfig.json"
}
},
// An array of directory names to be searched recursively up from the requiring module's location
// moduleDirectories: [
// "node_modules"
// ],
// An array of file extensions your modules use
moduleFileExtensions: [
"ts",
"tsx",
"js"
],
// A map from regular expressions to module names that allow to stub out resources with a single module
// moduleNameMapper: {},
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
// modulePathIgnorePatterns: [],
// Activates notifications for test results
// notify: false,
// An enum that specifies notification mode. Requires { notify: true }
// notifyMode: "always",
// A preset that is used as a base for Jest's configuration
// preset: null,
// Run tests from one or more projects
// projects: null,
// Use this configuration option to add custom reporters to Jest
// reporters: undefined,
// Automatically reset mock state between every test
// resetMocks: false,
// Reset the module registry before running each individual test
// resetModules: false,
// A path to a custom resolver
// resolver: null,
// Automatically restore mock state between every test
// restoreMocks: false,
// The root directory that Jest should scan for tests and modules within
// rootDir: null,
// A list of paths to directories that Jest should use to search for files in
roots: [
"<rootDir>/src"
],
// Allows you to use a custom runner instead of Jest's default test runner
// runner: "jest-runner",
// The paths to modules that run some code to configure or set up the testing environment before each test
// setupFiles: [],
// The path to a module that runs some code to configure or set up the testing framework before each test
// setupTestFrameworkScriptFile: null,
// A list of paths to snapshot serializer modules Jest should use for snapshot testing
// snapshotSerializers: [],
// The test environment that will be used for testing
testEnvironment: "node",
// Options that will be passed to the testEnvironment
// testEnvironmentOptions: {},
// Adds a location field to test results
// testLocationInResults: false,
// The glob patterns Jest uses to detect test files
testMatch: [
"**/*.spec.ts"
],
// An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
// testPathIgnorePatterns: [
// "\\\\node_modules\\\\"
// ],
// The regexp pattern Jest uses to detect test files
// testRegex: "",
// This option allows the use of a custom results processor
// testResultsProcessor: null,
// "testResultsProcessor": "jest-jenkins-reporter",
// This option allows use of a custom test runner
// testRunner: "jasmine2",
// This option sets the URL for the jsdom environment. It is reflected in properties such as location.href
// testURL: "http://localhost",
// Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout"
// timers: "real",
// A map from regular expressions to paths to transformers
transform: {
"^.+\\.(ts|tsx)$": "ts-jest"
},
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
// transformIgnorePatterns: [
// "\\\\node_modules\\\\"
// ],
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
// unmockedModulePathPatterns: undefined,
// Indicates whether each individual test should be reported during the run
verbose: false
// An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
// watchPathIgnorePatterns: [],
// Whether to use watchman for file crawling
// watchman: true,
};
After a lot of research at several pages, this worked for me to get the coverage report:
put below line under scripts:
"test:coverage": "set CI=true && react-scripts test --coverage",
And, add below code for jest configuration in package.json file as below:
"jest": {
"collectCoverageFrom": [
"**/*.{js,jsx}",
"!**/node_modules/**",
"!**/coverage/**",
"!**/serviceWorker.js",
"!**/index.js"
],
"coveragePathIgnorePatterns": [
"/node_modules/",
"package.json",
"package-lock.json"
]
}
And, then run
npm run test:coverage
Apparently you have to add your source files to roots to make it work. See this PR comment.
Instead of this:
roots: ["../src/tests/"]
Also include your source files:
roots: ["../src/tests/", "../src/repositories/"]
After that and having the correct collectCoverageFrom, all files with 0% coverage were detected as expected.

Jest gives an error: "SyntaxError: Unexpected token export"

I'm using Jest to test my React app.
Recently, I added DeckGL to my app. My tests fail with this error:
Test suite failed to run
/my_project/node_modules/deck.gl/src/react/index.js:21
export {default as DeckGL} from './deckgl';
^^^^^^
SyntaxError: Unexpected token export
at ScriptTransformer._transformAndBuildScript (node_modules/jest-runtime/build/script_transformer.js:318:17)
at Object.<anonymous> (node_modules/deck.gl/dist/react/deckgl.js:9:14)
at Object.<anonymous> (node_modules/deck.gl/dist/react/index.js:7:15)
This looks like an issue with Jest transforming a node module before running it's tests.
Here is my .babelrc:
{
"presets": ["react", "es2015", "stage-1"]
}
Here is my jest setup:
"jest": {
"testURL": "http://localhost",
"setupFiles": [
"./test/jestsetup.js"
],
"snapshotSerializers": [
"<rootDir>/node_modules/enzyme-to-json/serializer"
],
"moduleDirectories": [
"node_modules",
"/src"
],
"moduleNameMapper": {
"\\.(css|scss)$": "<rootDir>/test/EmptyModule.js"
}
},
I seem to have the correct things necessary to transform export {default as DeckGL }. So any ideas whats going wrong?
This means, that a file is not transformed through TypeScript compiler, e.g. because it is a JS file with TS syntax, or it is published to npm as uncompiled source files. Here's what you can do.
Adjust your transformIgnorePatterns allowed list:
{
"jest": {
"transformIgnorePatterns": [
"node_modules/(?!#ngrx|(?!deck.gl)|ng-dynamic)"
]
}
}
By default Jest doesn't transform node_modules, because they should be valid JavaScript files. However, it happens that library authors assume that you'll compile their sources. So you have to tell this to Jest explicitly. Above snippet means that #ngrx, deck and ng-dynamic will be transformed, even though they're node_modules.
And if you are using 'create-react-app', it won't allow you to specify 'transformIgnorePatterns' via Jest property in package.json
As per this https://github.com/facebook/create-react-app/issues/2537#issuecomment-390341713
You can use CLI as below in your package.json to override and it works :
"scripts": {
"test": "react-scripts test --transformIgnorePatterns \"node_modules/(?!your-module-name)/\"",
},
This is because Node.js cannot handle ES6 modules.
You should transform your modules to CommonJS therefore.
Babel 7 >=
Install
npm install --save-dev #babel/plugin-transform-modules-commonjs
And to use only for test cases add to .babelrc,
Jest automatically gives NODE_ENV=test global variable.
"env": {
"test": {
"plugins": ["#babel/plugin-transform-modules-commonjs"]
}
}
Babel 6 >=
npm install --save-dev babel-plugin-transform-es2015-modules-commonjs
to .babelrc
"env": {
"test": {
"plugins": ["transform-es2015-modules-commonjs"]
}
}
Jest by default won't compile files in the node_modules directory.
transformIgnorePatterns [array]
Default: ["/node_modules/"]
An array of regexp pattern strings that are matched against all source
file paths before transformation. If the test path matches any of the
patterns, it will not be transformed.Default: ["/node_modules/"]
DeckGL seems to be in ES6, to make jest able to read it, you need to compile this as well.
To do that, just add an exception for DeckGL in the transformignorePatterns
"transformIgnorePatterns": ["/node_modules/(?!deck\.gl)"]
https://facebook.github.io/jest/docs/en/configuration.html#transformignorepatterns-array-string
I was having this issue with a monorepo. A package in the root node_modules was breaking my tests. I fixed by changing my local .babelrc file to babel.config.js. Explanation: https://github.com/facebook/jest/issues/6053#issuecomment-383632515
It was work around #1 on this page that fixed it for me though workaround #2 on that page is mentioned in above answers so they may also be valid.
"Specify the entry for the commonjs version of the corresponding package in the moduleNameMapper configuration"
jest.config.js
moduleNameMapper: {
"^uuid$": require.resolve("uuid"),
"^jsonpath-plus$": require.resolve("jsonpath-plus")
...
In my case I use this config in the file package.json:
"jest": {
"transformIgnorePatterns": [
"!node_modules/"
]
}
This code worked for me
// .babelrc
{
"presets": [
["env", {
"modules": "commonjs", // <- Check and see if you have this line
"targets": {
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
}
}],
"stage-2"
],
"plugins": ["transform-vue-jsx", "transform-runtime"],
"env": {
"test": {
"presets": ["env", "stage-2"],
"plugins": ["transform-vue-jsx", "transform-es2015-modules-commonjs", "dynamic-import-node"]
}
}
}
jest understands commonJs so it needs babel to transform the code for it before use. Also jest uses caching when running code. So make sure you run jest --clearCache before running jest.
Tested Environment:
Node v8.13.0
Babel v6+
Jest v27
I'm using a monorepo (it contains multiple packages for the frontend and backend).
The package I'm testing imports a file from another package that uses the dependency uuid.
All the files are in Typescript (not Javascript).
The package I'm testing has a tsconfig file for testing only, called tsconfig.test.json. It has the properties commonjs and allowJs. Adding allowJs solves the problem when importing uuid, I don't know why.
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"module": "commonjs",
"types": [
"jest",
"node"
],
// Necessary to import dependency uuid using CommonJS
"allowJs": true
},
"include": [
"jest.config.ts",
"**/*.test.ts",
"**/*.d.ts"
]
}
I was upgrading a project that uses a version of babel that reads the config from .babelrc, when I upgraded to a newer version I read:
https://babeljs.io/docs/en/configuration#whats-your-use-case
What's your use case?
You are using a monorepo?
You want to compile node_modules?
babel.config.json is for you!
On top of:
{
"jest": {
"transformIgnorePatterns": [
"node_modules/(?!(module))"
]
}
}
I renamed .babelrc to babel.config.json too.
I had the same error of importing dataSet from vis-data.js library
import { DataSet } from 'vis-data/esnext';
So I just removed /esnext from the path and now it works:
import { DataSet } from 'vis-data';

Use Jest and spread operator without babel?

Is it possible to use the spread operator in code with Jest and without babel if the node engine is 8+?
I am dropping support for Node.js <8 in my app and assumed I could remove all babel dependencies and transpiling from package.json, however npm run jest fails immediately with these types of errors:
FAIL test/workers/repository/onboarding.spec.js
● Test suite failed to run
/Users/me/project/lib/workers/repository/onboarding.js: Unexpected token (13:17)
11 |
12 | async function createOnboardingBranch(inputConfig) {
> 13 | let config = { ...inputConfig };
| ^
Is there any way to get Jest to work without needing to add back all the babel dependencies and configuration?
Node.js version: 8.9.0
Jest version: 20.0.4
jest config in package.json:
"jest": {
"cacheDirectory": ".cache/jest",
"coverageDirectory": "./coverage",
"collectCoverage": true,
"collectCoverageFrom": [
"lib/**/*.js"
],
"coverageReporters": [
"json",
"lcov",
"text-summary"
],
"setupTestFrameworkScriptFile": "./test/chai.js"
},
Edit:
I have been able to narrow my babel configuration down to just one plugin: babel-plugin-transform-object-rest-spread and configure babel in package.json like so:
"babel": {
"plugins": [
"transform-object-rest-spread"
]
},
Taking this out or attempting to use babel-preset env causes Jest to fail on the spread operator again.

Running tests located in a separate directory with mocha and ts-node?

I have source code and tests separated as follows:
`src/main/ts/hello.ts` //SOURCE FILES HERE
`src/test/ts/hello.spec.ts` //SPEC FILES HERE
The import statement in src/test/ts/hello.spec.ts looks like this:
import hello from 'hello';
The hello.ts source code looks like this:
export function hello() {
return 'Hello World!';
}
export default hello;
My tsconfig.json is setup such that the test files can import source modules without using relative paths like this:
{
"include": [
"src/main/ts/**/*.ts"
],
"exclude": [
"node_modules"
],
"compilerOptions": {
"experimentalDecorators": true,
"noImplicitAny": true,
"moduleResolution": "node",
"target": "es6",
"baseUrl": ".",
"paths": {
"*": [
"*", "src/main/ts/*"
]
}
}
}
This way the hello.spec.ts file can import hello using the statement import hello from 'hello';
I'm trying to run the tests with npm test configured to run mocha and tsnode like this (Based on this article):
"scripts": {
"test": "mocha -r ts-node/register src/test/ts"
},
However it does not look like ts-node is picking up on my tsconfig.json configuration as I get this error:
mocha -r ts-node/register src/test/ts
Error: Cannot find module 'hello'
at Function.Module._resolveFilename (module.js:336:15)
at Function.Module._load (module.js:286:25)
The module resolution that you set through paths in tsconfig.json is purely an compile-time thing. (See this ts-node issue report and this TypeScript issue report for details.) It does not affect how the code is emitted, which means that your test file is doing a require("hello"), which Node cannot resolve. The consequence of paths being a compile-time thing is that your module loader needs to be configured to also perform the same kind of resolution that you specify in tsconfig.json. If you were using RequireJS, for instance, you'd need to have a configuration for it that does the same thing paths in tsconfig.json does. You are using Node, however...
What you can do in Node is use tsconfig-paths, which will read the tsconfig.json, parse the paths setting and change the module resolution in Node so that it works.
Using your code, I modified hello.spec.ts to have at least one test for feedback:
import hello from "hello";
import "mocha";
it("q", () => {
if (hello() !== "Hello World!") {
throw new Error("unequal");
}
});
I installed tsconfig-paths and #types/mocha (so that import "mocha" does the right thing compilation-wise in the test file I show above) and invoked Mocha like this:
$ ./node_modules/.bin/mocha --compilers ts:ts-node/register -r tsconfig-paths/register 'src/test/ts/**/*.ts'
I got this output:
✓ q
1 passing (20ms)

How to use nodemon with JSX?

I can compile and run my JSX app with one command:
jsx app.jsx | node
But I also want my server to automatically restart every time I modify app.jsx. I can do that with nodemon, but I can't quite figure out how to get nodemon to run my script through the JSX compiler beforehand.
I've got a nodemon.json file set up like this:
{
"execMap": {
"js": "node",
"jsx": "jsx {{filename}} | node"
},
"ext": "js jsx",
"ignore": [
".hg",
"node_modules",
".idea"
],
"verbose": true
}
But when I run nodemon it tells me:
8 Feb 21:58:48 - [nodemon] starting `jsx app.jsx | node`
8 Feb 21:58:48 - [nodemon] child pid: 10976
'\"jsx app.jsx | node\"' is not recognized as an internal or external command,
operable program or batch file.
Which is odd, because that command works verbatim when I paste it directly into my terminal.
Is there any way I get nodemon to run my JSX files?
It seems nodemon is attempting to run a program with the name you provide, rather than executing a shell.
Create a jsx.sh file with this content:
#!/bin/sh
jsx "$1" | node
Then chmod +x jsx.sh, and put this in your nodemon.json:
{
"execMap": {
"js": "node",
"jsx": "./jsx.sh"
},
"ext": "js jsx",
"ignore": [
".hg",
"node_modules",
".idea"
],
"verbose": true
}
* not tested
OR you can just locate the jsx command in your ./node_modules/.bin directory and run it off that instead:
{
script: "client.js",
options: {
execMap: {
"js": "node",
"jsx": "./node_modules/.bin/jsx \"$1\" | node"
},
ext: "js jsx",
callback: function (nodemon) {
nodemon.on("log", function (event) {
console.log(event.colour);
});
},
ignore: [
"node_modules/**/*.js",
"public/js/**",
"lib/api/**",
]
}
}
If you're on Windows (like me) you can create a .bat instead of a .sh like FakeRainBrigand suggests
#echo off
jsx %1 | node
This file has to be in the same directory as nodemon.json and package.json -- paths don't seem to work in the execMap for whatever reason.
Also, an even easier solution is to just not use any JSX in your main/server script, install node-jsx and then require your JSX files as needed.

Resources