"istanbul ignore next" command seems to be ignored - javascript

I'm trying to use Jest to test my code. It was working quite fine until I tried to exclude a class method from the tests.
The querySelector() call is the reason why I want to skip this method from being tested ("document" is obviously null unless I run the script in the browser)
I tried this solution, which seems to be the most suggested one:
/* istanbul ignore next */
appendNewInputFields() {
const howMany = Number(document.querySelector('#items-to-add').value);
[...Array(howMany)].forEach( i => {
const newInputField = this.createNewItemInputField();
this.inputItemsContainerNode.append(newInputField);
});
}
But the test keeps failing and the line /* istanbul ignore next */ seems to be ignored.
I've also tried putting the comment between the function signature and its body (as was suggested somewhere here on SO), but no luck:
FAIL js/DOMManager.test.js
● Test suite failed to run
TypeError: Cannot read property 'value' of null
135 | appendNewInputFields() /* istanbul ignore next */ {
136 |
> 137 | const howMany = Number(document.querySelector('#items-to-add').value);
| ^
138 |
I've read around that this might be related to babel-plugin-istanbul. I've tried
npm --save-dev uninstall babel-plugin-istanbul
which in the terminal returned:
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents#2.3.2 (node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents#2.3.2: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})
removed 18 packages and audited 521 packages in 2.178s
but the folder babel-plugin-istanbul is still present in /node_modules/ inside my working folder and the test keeps failing as if nothing changed.
If I comment out the body of the function, the other test suites work perfectly. If I try to apply the ignore next command to any other part of the code, the tests pass just fine and the line is completely ignored.
If I try to manually delete the /babel-plugin-istanbul/ folder (from the /node_modules/ in my working folder), Jest stops working.
(This is the first time I installed Node.js, and I did it only because I wanted to start unit testing with Jest. I'm pointing this out because these are my first steps venturing out of the vanilla world. I don't know how to deal with Node.js nor npm, I just launched a couple commands to install it, I wrote a few tests for Jest and they all immediately worked fine. I'm not using any other framework, I'm trying to stick to vanilla JS as much as possible.)
----------------------- Edit:
I tried changing the code to this:
appendNewInputFields() {
// TODO solve the istanbul ignore issue
let howMany;
/* istanbul ignore if */
if(document != null) {
howMany = Number(document.querySelector('#items-to-add').value);
[...Array(howMany)].forEach( i => {
const newInputField = this.createNewItemInputField();
this.inputItemsContainerNode.append(newInputField);
});
console.log("added " + howMany + " input fields");
}
}
I keep getting
FAIL js/DOMManager.test.js
● Test suite failed to run
TypeError: Cannot read property 'value' of null
139 | /* istanbul ignore if */
140 | if(document != null) {
> 141 | howMany = Number(document.querySelector('#items-to-add').value);
| ^
142 |
143 | [...Array(howMany)].forEach( i => {
144 | const newInputField = this.createNewItemInputField();
I'm totally clueless at this point. Given the conditional, "document" should be null and that block entirely skipped, but it keeps failing the test.
The same code works exactly as intended when run in the browser.

So I made a few tests in a blank new folder, and it finally struck me.
Apparently I had misunderstood the purpose of /* istanbul ignore next */.
Its function is not to skip code from being executed during the tests, but rather prevent that portion of code to be taken into account when determining the amount of total code that has been tested. The code runs (if there is anything calling that function), but those lines just don't count when Jest sums up the amount of lines it tested (which is the purpose of --coverage, I guess). If an error occurs, it is thrown as it would normally be.
My problem wasn't really related to Jest nor the istanbul package. I feel pretty dumb realizing this now, but what I needed was just proper exception handling.
I implemented a few old fashioned try/catch blocks: now everything is tested smoothly and the istanbul ignore directive correctly behaves as expected (now that I know what to expect, that is): functions that are not tested and flagged to be ignored, they just don't appear in the final coverage report.
I hope this helps anybody who might stumble in my same misunderstanding.
Of course, if anybody more competent than me can confirm this interpretation or has any suggestion or further explanation, it would obviously be much appreciated.

I was with the same error with window.alert command with jest:
ReferenceError: alert is not defined
4 |
5 | /* istanbul ignore next */
> 6 | alert(helloWorld());
| ^
7 |
so I tried to use try-catch and SUCCESS!
before:
alert(helloWorld());
after:
const alerta = () => {
try {
return alert(helloWorld());
} catch (error) {
return false;
}
}
testes:
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total

This might also happen if your bundling tool (esbuild, for instance) removes comments, therefore istanbul does not see them.
If that's the case, you might want to change your bundling tool to something like terser (make sure that comments are not omitted there, too) or adjust minification options of the bundling tool you use.
This is how it would look like if you use Vite:
build: {
minify: 'terser',
terserOptions: {
format: {
comments: 'all', // or regular expression /istanbul\signore\s/
},
},
},

Related

Jest causing a module to disappear

I'm trying to write tests for an old codebase of mine. It uses the isbn package from NPM, which is tiny but does the trick. But whenever I write tests that involve the module, it disappears. That is to say - the value of the module is set to {}.
I've written up two files to try and isolate this issue. I just can't figure out how this test is failing.
First file, isbnTest.js:
const { ISBN } = require("isbn");
function isbnExists() {
return ISBN !== undefined;
}
console.log(isbnExists());
module.exports = {
isbnExists,
};
Pretty simple:
import the module,
one function that just checks if the module has been successfully imported,
log the results, and
export the tester function.
Running this file from the console logs
true
But what happens when we run this code from within Jest?
The second file, ./isbnTest.test.js:
const { isbnExists } = require("./isbnTest.js");
test("isbn should exist", () => {
expect(isbnExists()).toBe(true);
});
When I run npm test with these two files, the test fails.
FAIL
./isbnTest.test.js
✕ isbn should exist (4ms)
● isbn should exist
expect(received).toBe(expected) // Object.is equality
Expected: true
Received: false
2 |
3 | test("isbn should exist", () => {
> 4 | expect(isbnExists()).toBe(true);
| ^
5 | });
6 |
at Object.<anonymous> (isbnTest.test.js:4:24)
console.log isbnTest.js:7
false
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 total
It seems as though Jest must be doing some custom importing stuff that's somehow missing this module. Almost like it's being replaced with an empty mock? But I really don't know.
I'd love to remove the isbn package from my program, but without any tests I don't feel confident that I can ensure I won't break anything.
EDIT:
A commenter has pointed out that this doesn't reproduce, which means there's something askew on my machine. Can anyone provide as guess as to what that might be? Deleting and reinstalling the NPM modules doesn't do the trick. I don't think I have any jest config files.
After some further investigation, the problem is being caused by this line in the module:
var exports = typeof window === 'object' && window ? window: module.exports;
If I comment that out, Jest picks it up fine.

Invalid configuration object: webpack-cli configuration.optimization.splitChunks Webpacker 5 Rails 6

After upgrading from Webpacker 4 to 5, I receive a new error while running rails webpacker:compile. Running yarn dev, alone; works without issue -- I can't seem to find what the cause of this bug is, or what file its located in. There's aren't many debugging tools in this case. How do I fix the problem where splitChunks is failing for webpacker 5?
Error:
➜ rails webpacker:compile
warning: parser/current is loading parser/ruby27, which recognizes
warning: 2.7.3-compliant syntax, but you are running 2.7.4.
warning: please see https://github.com/whitequark/parser#compatibility-with-ruby-mri.
I, [2022-01-04T14:59:51.4223 #20612] INFO -- : initializing Lit
Compiling...
Compilation failed:
[webpack-cli] Invalid configuration object. Webpack has been initialized using a configuration object that does not match the API schema.
- configuration.optimization.splitChunks should be one of these:
false | object { automaticNameDelimiter?, cacheGroups?, chunks?, defaultSizeTypes?, enforceSizeThreshold?, fallbackCacheGroup?, filename?, hidePathInfo?, maxAsyncRequests?, maxAsyncSize?, maxInitialRequests?, maxInitialSize?, maxSize?, minChunks?, minRemainingSize?, minSize?, minSizeReduction?, name?, usedExports? }
-> Optimize duplication and caching by splitting chunks by shared modules and cache group.
Details:
* configuration.optimization.splitChunks.name should be one of these:
false | string | function
-> Give chunks created a name (chunks with equal name are merged).
Details:
* configuration.optimization.splitChunks.name should be false.
* configuration.optimization.splitChunks.name should be a string.
* configuration.optimization.splitChunks.name should be an instance of function.
I don't what's your configurations, but the problem is in your configuration.optimization.splitChunks.name being something other then either false , string or instance of function

Calling node.js from makefile fails

I have a simple hello world js file I run from terminal with node, and everything works fine. When I try to run the exact same thing from my makefile it prints the hello world part but then fails. Here is the relevant target from makefile:
run: ${JS_DIR}/main.js
node ${JS_DIR}/main.js
And here is the error I get:
$ make run
node /some/dir/main.js
Hello World
makefile:44: recipe for target 'run' failed
make: *** [run] Error 254
What's going on here?
A make target will fail if any of its recipes return an error code (i.e., non-zero). node is returning a non-zero. Try running
node /some/dir/main.js; echo $?
to confirm.
You could either update main.js to not return an error (I believe this is done by calling process.exit() with no parameters or a 0 parameter, but I'm not a node or js expert, so take that with a grain of salt).
Alternatively, you could make make ignore the return code by adding a - to the beginning of the recipe:
run: ${JS_DIR}/main.js
-node ${JS_DIR}/main.js

Sonarqube does not retrieve my JavaScript coverage from LCOV

I have an application with the following structure:
my-application
+- pom.xml
+- app
| +- scripts
| | +- app.js
| | +- **/*.js
| +- 3rd-party-libs
+- build
+- node_modules
+- test
I've create the pom.xml only to run the SonarQube analysis. Otherwise, all the tasks are run by Grunt (tests are run with Karma).
The content of the pom.xml is the following:
<properties>
<sonar.language>js</sonar.language>
<sonar.sourceEncoding>UTF-8</sonar.sourceEncoding>
<sonar.javascript.coveragePlugin>lcov</sonar.javascript.coveragePlugin>
<sonar.javascript.lcov.reportPath>build/karma/coverage/lcov.info</sonar.javascript.lcov.reportPath>
<sonar.exclusions>app/3rd-party-libs/**,node_modules/**</sonar.exclusions>
<sonar.dynamicAnalysis>reuseReports</sonar.dynamicAnalysis>
</properties>
<build>
<sourceDirectory>app/scripts</sourceDirectory>
<testSourceDirectory>test</testSourceDirectory>
</build>
When I run grunt test, it creates a build/karma/coverage/lcov.info that contains the following information:
TN:
SF:./app/scripts/app.js
FN:16,(anonymous_1)
FN:26,(anonymous_2)
FNF:2
...
After the SonarQube analysis, the dashboard shows a 0% code coverage.
I suspected that the path in the SF: was the source of the error. Thus, I've changed the sonar.javascript.lcov.reportPath property to use another lcov.info to test different values: app.js, ./app.js, app/scripts/app.js, ./app/scripts/app.js, but none worked, keeping the coverage to 0%.
What I am missing?
Just in case, I have the following configuration in my karma.conf.js:
coverageReporter: {
reporters: [
{
type: 'lcov',
dir: 'build/karma/coverage',
subdir: '.'
}
]
},
ps: Sonar version is 3.7.2, but I also tried on a 4.3, with the same results...
Edit: I've updated my configuration to use Sonar-runner directly, I'm using the latest version of Sonar (5.0.1) and JS plugin (2.3). I've also modified manually the lcov.info to have a "good" format (at least one format that matches the Sonar repo example):
SF:./app/scripts/app.js
DA:2,1
DA:20,1
DA:29,1
DA:34,1
end_of_record
SF:./app/scripts/services/exampleService.js
DA:1,1
DA:11,1
DA:12,0
end_of_record
The sonar-project.properties looks like:
sonar.projectKey=xxx
sonar.projectName=xxx
sonar.projectVersion=xxx
sonar.sourceEncoding=UTF-8
sonar.sources=app/scripts
sonar.tests=test
sonar.exclusions=app/3rd-party-libs/**,node_modules/**
sonar.dynamicAnalysis=reuseReports
sonar.language=js
sonar.projectBaseDir=.
sonar.javascript.coveragePlugin=lcov
sonar.javascript.lcov.reportPath=build/karma/coverage/lcov.info
And still, 0% of coverage :(
I was clueless, so I decided to modif the JavaScript plugin to add more logs. And I finally found the error, which is a vicious problem of... case sensitivity!
Let me explain. Let's consider the saveMeasureFromLCOVFile method of the CoverageSensor.java:
protected void saveMeasureFromLCOVFile(SensorContext context) {
String providedPath = settings.getString(JavaScriptPlugin.LCOV_REPORT_PATH);
File lcovFile = getIOFile(fileSystem.baseDir(), providedPath);
...
LOG.info("Analysing {}", lcovFile);
LCOVParser parser = new LCOVParser(fileSystem.baseDir());
Map<String, CoverageMeasuresBuilder> coveredFiles = parser.parseFile(lcovFile);
for (InputFile inputFile : fileSystem.inputFiles(mainFilePredicate)) {
try {
CoverageMeasuresBuilder fileCoverage = coveredFiles.get(inputFile.file().getAbsolutePath());
org.sonar.api.resources.File resource = org.sonar.api.resources.File.create(inputFile.relativePath());
if (fileCoverage != null) {
for (Measure measure : fileCoverage.createMeasures()) {
context.saveMeasure(resource, measure);
}
} else {
// colour all lines as not executed
LOG.debug("Default value of zero will be saved for file: {}", resource.getPath());
LOG.debug("Because: either was not present in LCOV report either was not able to retrieve associated SonarQube resource");
saveZeroValueForResource(resource, context);
}
} catch (Exception e) {
LOG.error("Problem while calculating coverage for " + inputFile.absolutePath(), e);
}
}
}
First, it reads the lcov.info file given to know for which files we have coverage data (retrieved by parsing the file, done with LCOVParser class).
After that, it takes the same file from the coveredFiles map to do the matching between metrics and code. If the file is not found (else part of the if (fileCoverage != null) {), then the code coverage is forced to 0.
That's what happened on my project.
So why is it happening? Simply because in my environment, inputFile is equals to d:\dev\my-application\app\scripts\app.js and in coveredFiles map, I have D:\dev\my-application\app\scripts\app.js. Note the difference of the case in the drive letter (d: against D:). As the map.get(...) is case sensitive, fileCoverage is null and then no coverage is calculated.
Now, I have to investigate on how I can force the path to have correct case...
After more investigation, I found a modification in the plugin code that works (at least for me, I didn't get into all the possible impacts). In LCOVParser, the filePath = CoverageSensor.getIOFile(moduleBaseDir, filePath).getCanonicalPath(); could be modified to filePath = CoverageSensor.getIOFile(moduleBaseDir, filePath).getAbsolutePath();, since the first one returns a path like D:\... while the second will return d:\....
In fact, I'm not even what is the preferred case to use on Windows. The following code:
public static void main(String[] args) throws IOException {
System.out.println("PATH 1 : " + new File(".").getAbsolutePath());
System.out.println("PATH 2 : " + new File(".").getCanonicalPath());
}
will return:
PATH 1 : D:\dev\preclosing\preclosing-eme\.
PATH 2 : D:\dev\preclosing\preclosing-eme
Anyway, I'm stuck for the moment, and I'm not even sure how to solve my issue without waiting for a JS plugin fix (since my "official" Sonar is a little bit old for the moment and only support JS plugin up to v2.1).

Syntax check for JavaScript using command

Are there equivalent to perl -c syntax check for JavaScript from command? Given that I have NodeJS installed?
JSLint is not considered as it is not a real parser. I think YUI compressor is possible but I don't want to install Java on production machines, so I am checking if Node.JS already provided this syntax check mechanism.
If you want to perform a syntax check like that way we do in perl ( another scripting language) you can simply use node -c <js file-name>
e.g. a JS file as test.js has:
let x = 30
if ( x == 30 ) {
console.log("hello");
else {
console.log( "world");
}
now type in node -c test.js
it will show you
test.js:5
else {
^^^^
SyntaxError: Unexpected token else
at startup (bootstrap_node.js:144:11)
at bootstrap_node.js:509:3
Now after fixing the syntax issue as
let x = 30
if ( x == 30 ) {
console.log("hello");
} else {
console.log( "world");
}
check syntax - node -c test.js will show no syntax error!!
Note - we can even use it to check syntax for all files in a dir. - node -c *.js
Try uglify. You can install it via npm.
Edit: The package name has changed. It is uglify-js.
nodejs --help
explains the -p switch: it evaluates the supplied code and prints the results. So using nodejs -p < /path/to/file.js would be a disastrous way to check the validity of node.js code on your server. One possible solution is the one indicated in this SO thread. The one thing not so good about it - the syntax error messages it reports are not terribly helpful. For instance, it tell you something is wrong but without telling you where it is wrong.

Categories