3

I have a Sails project with a test/ folder containing all my mocha tests and want to create a test coverage report using following command:

mocha --require blanket --reporter html-cov > coverage.html

The blanket configuration inside my package.json looks following:

"blanket": {
  "pattern": ["lib", "api", "config"],
  "data-cover-never": "node_modules",
  "data-cover-reporter-options": {
    "shortnames": true
  }
}

I included both Sails folders api/ and config/ as they probably contain testable code and a folder lib/ containing most of my application's logic.

Sadly the blanket coverage module only covers files that are directly included in my test files. Since Sails loads most of my files in api/ and config/ dynamically they don't show up in my coverage reports.

Any ideas in how to integrate the Sails framework with blanket?

david.schreiber
  • 3,741
  • 2
  • 25
  • 45

1 Answers1

1

I am unfamilair with Sails but I had the same problem using Blanket.js and posted a comment with a work-around on the Blanket.js bugtracker, here it is:

https://github.com/alex-seville/blanket/issues/361#issuecomment-34002054

The workaround I suggested there felt very much like a hack. I eventually abandoned Blanket in favor of Istanbul: https://github.com/gotwarlost/istanbul

Istanbul gives you both more metrics (statement, line, function and branch coverage) and outputs an excellent bunch of .html files allowing you to analyze how to improve your code.

Blanket.js appears not to be maintained very well given the 79+ open issues currently.

If you do want to stick to blanket.js you can follow the suggestion I posted on the Blanket.js bug tracker and try to include all files within the test run by recursively looping through all relevant code directories. The code I used to do that at the time was as the following (I would definitely refactor this, but it shows the intent):

'use strict';

/**
 * This file is loaded by blanket.js automatically before it instruments code to generate a code coverage report.
 */

var fs = require('fs');
var log = require('winston');
var packageJson = require('./package.json');

// For some reason the blanket config in package.json does not work automatically, set the settings manually instead
require('blanket')({
    // Only files that match this pattern will be instrumented
    pattern: packageJson.config.blanket.pattern
});

/**
 * Walks through a directory structure recursively and executes a specified action on each file.
 * @param dir {(string|string[])} The directory path or paths.
 * @param action {function} The function that will be executed on any files found.
 *      The function expects two parameters, the first is an error object, the second the file path.
 */
function walkDir(dir, action) {

    // Assert that action is a function
    if (typeof action !== "function") {
        action = function (error, file) {
        };
    }

    if (Array.isArray(dir)) {
        // If dir is an array loop through all elements
        for (var i = 0; i < dir.length; i++) {
            walkDir(dir[i], action);
        }
    } else {
        // Make sure dir is relative to the current directory
        if (dir.charAt(0) !== '.') {
            dir = '.' + dir;
        }

        // Read the directory
        fs.readdir(dir, function (err, list) {

            // Return the error if something went wrong
            if (err) return action(err);

            // For every file in the list, check if it is a directory or file.
            // When it is a directory, recursively loop through that directory as well.
            // When it is a file, perform action on file.
            list.forEach(function (file) {
                var path = dir + "/" + file;
                fs.stat(path, function (err, stat) {
                    if (stat && stat.isDirectory()) {
                        walkDir(path, action);
                    } else {
                        action(null, path);
                    }
                });
            });
        });
    }
};

// Loop through all paths in the blanket pattern
walkDir(packageJson.config.blanket.pattern, function (err, path) {
    if (err) {
        log.error(err);
        return;
    }
    log.error('Including ' + path + ' for blanket.js code coverage');
    require(path);
});

My advice would be to drop Blanket.js for something else.

Niels Krijger
  • 186
  • 4
  • 8
  • I would have to agree. Unfortunately (and this is why I had started looking into using Blanket.js *instead* of Istanbul), with the current version of Sails.js there is an [issue](https://github.com/balderdashy/sails/issues/1883) with `sails.lower()` removing the listeners that Istanbul uses, so it does not output any coverage report. However there is a [pull request](https://github.com/balderdashy/sails/pull/1895) to fix it. – blah238 Jun 27 '14 at 21:45