Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CLI add current-coverage --baseline {Num} #766

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
language: node_js

node_js:
- "0.10"
- "0.12"
- "6.3.1"
- "7.5"

sudo: false

Expand All @@ -14,4 +14,4 @@ script:
- npm test --cover

after_script:
- if [[ `node --version` == *v0.12* ]]; then cat ./build/coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js; fi
- if [[ `node --version` == *v7.5* ]]; then cat ./build/coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js; fi
170 changes: 170 additions & 0 deletions lib/command/current-coverage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
/*
Copyright (c) 2012, Yahoo! Inc. All rights reserved.
Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
*/

var nopt = require('nopt'),
path = require('path'),
fs = require('fs'),
Collector = require('../collector'),
util = require('util'),
utils = require('../object-utils'),
filesFor = require('../util/file-matcher').filesFor,
Command = require('./index');

var COV_TYPES = [ 'statements',
'branches',
'lines',
'functions' ];

var CURRENT_COVERAGE_FILE = 'current-coverage.txt';

function isAbsolute(file) {
if (path.isAbsolute) {
return path.isAbsolute(file);
}

return path.resolve(file) === path.normalize(file);
}

function CurrentCoverageCommand() {
Command.call(this);
}

function removeFiles(covObj, root, files) {
var filesObj = {},
obj = {};

// Create lookup table.
files.forEach(function (file) {
filesObj[file] = true;
});

Object.keys(covObj).forEach(function (key) {
// Exclude keys will always be relative, but covObj keys can be absolute or relative
var excludeKey = isAbsolute(key) ? path.relative(root, key) : key;
// Also normalize for files that start with `./`, etc.
excludeKey = path.normalize(excludeKey);
if (filesObj[excludeKey] !== true) {
obj[key] = covObj[key];
}
});

return obj;
}

CurrentCoverageCommand.TYPE = 'current-coverage';
util.inherits(CurrentCoverageCommand, Command);

Command.mix(CurrentCoverageCommand, {
synopsis: function () {
return "shows and optionally checks overall coverage from coverage JSON files.";
},

usage: function () {
console.error('\nUsage: ' + this.toolName() + ' ' + this.type() + ' [<include-pattern>]');
console.error('\n\n');
},

run: function (args, callback) {

var template = {
root: path,
baseline: Number,
verbose: Boolean
};

var opts = nopt(template, { v : '--verbose' }, args, 0);

var baseline;
if (typeof opts.baseline !== "undefined") {
// CLI options should take priority
baseline = opts.baseline;
} else if (fs.existsSync(CURRENT_COVERAGE_FILE)) {
// If the file exists take the baseline from there
var fileContent = fs.readFileSync(CURRENT_COVERAGE_FILE, 'utf8');
baseline = parseFloat(fileContent);
} else {
// Fallback default
baseline = 0;
}

var includePattern = '**/coverage*.json';
var root;
var collector = new Collector();
var errors = [];

root = opts.root || process.cwd();
filesFor({
root: root,
includes: [ includePattern ]
}, function (err, files) {
if (err) { throw err; }
if (files.length === 0) {
return callback('ERROR: No coverage files found.');
}
files.forEach(function (file) {
var coverageObject = JSON.parse(fs.readFileSync(file, 'utf8'));
collector.add(coverageObject);
});
var rawCoverage = collector.getFinalCoverage(),
globalResults = utils.summarizeCoverage(removeFiles(rawCoverage, root, [])),
eachResults = removeFiles(rawCoverage, root, []);

// Summarize per-file results and mutate original results.
Object.keys(eachResults).forEach(function (key) {
eachResults[key] = utils.summarizeFileCoverage(eachResults[key]);
});

var coverageValuesAry = {};
COV_TYPES.forEach(function (key) {
coverageValuesAry[key] = [];
});

function processCov(name, actuals) {
COV_TYPES.forEach(function (key) {
var actual = actuals[key].pct;

coverageValuesAry[key].push(actual);
});
}

processCov("global", globalResults);

Object.keys(eachResults).forEach(function (key) {
processCov("per-file" + " (" + key + ") ", eachResults[key]);
});

var coverageAverages = {};
COV_TYPES.forEach(function (key) {
var sum = 0;
coverageValuesAry[key].forEach(function (val) {
sum += val;
});
var avg = sum / coverageValuesAry[key].length;
coverageAverages[key] = avg;
});

var masterCovSum = 0;
COV_TYPES.forEach(function (key) {
masterCovSum += coverageAverages[key];
});
var masterCovAverage = Number((masterCovSum / 4).toFixed(2));

var masterBaseline = baseline;
if (masterCovAverage < masterBaseline) {
errors.push('ERROR: Current Coverage (' + masterCovAverage +
'%) does not meet baseline threshold (' + masterBaseline + '%)');
} else {
console.log('SUCCESS: Current Coverage (' + masterCovAverage +
'%) is either equal or better than the baseline (' + masterBaseline + '%)');
// Update the new baseline
fs.writeFileSync(CURRENT_COVERAGE_FILE, masterCovAverage, 'utf8');
}

return callback(errors.length === 0 ? null : errors.join("\n"));
});
}
});

module.exports = CurrentCoverageCommand;
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "istanbul",
"name": "istanbul-checker",
"version": "0.4.5",
"description": "Yet another JS code coverage tool that computes statement, line, function and branch coverage with module loader hooks to transparently add coverage when running tests. Supports all JS coverage use cases including unit tests, server side functional tests and browser tests. Built for scale",
"keywords": [
Expand Down Expand Up @@ -93,19 +93,19 @@
"scripts": {
"pretest": "jshint index.js lib/ test/ && ./download-escodegen-browser.sh",
"test": "node --harmony test/run.js",
"posttest": "node ./lib/cli.js check-coverage --statements 95 --branches 80",
"posttest": "node ./lib/cli.js check-coverage --statements 95 --branches 80 && node ./lib/cli.js current-coverage --baseline 95",
"docs": "npm install yuidocjs && node node_modules/yuidocjs/lib/cli.js ."
},
"bin": {
"istanbul": "./lib/cli.js"
"istanbul-checker": "./lib/cli.js"
},
"files": [
"index.js",
"lib/"
],
"repository": {
"type": "git",
"url": "git://github.com/gotwarlost/istanbul.git"
"url": "git://github.com/elgalu/istanbul-checker.git"
},
"dependencies": {
"abbrev": "1.0.x",
Expand Down
1 change: 1 addition & 0 deletions test/cli/sample-project/current-coverage.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
71.64
90 changes: 90 additions & 0 deletions test/cli/test-current-coverage-command.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*jslint nomen: true */
var path = require('path'),
fs = require('fs'),
rimraf = require('rimraf'),
mkdirp = require('mkdirp'),
COMMAND = 'current-coverage',
COVER_COMMAND = 'cover',
DIR = path.resolve(__dirname, 'sample-project'),
OUTPUT_DIR = path.resolve(DIR, 'coverage'),
helper = require('../cli-helper'),
existsSync = fs.existsSync || path.existsSync,
run = helper.runCommand.bind(null, COMMAND),
runCover = helper.runCommand.bind(null, COVER_COMMAND);

module.exports = {
setUp: function (cb) {
rimraf.sync(OUTPUT_DIR);
mkdirp.sync(OUTPUT_DIR);
helper.resetOpts();
runCover([ 'test/run.js', '--report', 'none' ], function (/* results */) {

// Mutate coverage.json to test relative key paths.
var covObj = require('./sample-project/coverage/coverage.json');
var relCovObj = {};
var relCovDotSlashObj = {};
Object.keys(covObj).forEach(function (key) {
var relKey = path.relative(__dirname + '/sample-project', key);
relCovObj[relKey] = covObj[key];
relCovDotSlashObj['./' + relKey] = covObj[key];
});
fs.writeFileSync(path.resolve(__dirname, 'sample-project/coverage/relative.json'), JSON.stringify(relCovObj));
fs.writeFileSync(path.resolve(__dirname, 'sample-project/coverage/relative-dot-slash.json'), JSON.stringify(relCovDotSlashObj));

cb();
});
},
tearDown: function (cb) {
rimraf.sync(OUTPUT_DIR);
cb();
},
"Current coverage": {
"should fail with a difficult baseline": function (test) {
test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json')));
run([ '--baseline', '99' ], function (results) {
test.ok(!results.succeeded());
test.ok(!results.grepError(/No coverage files found/));
test.ok(results.grepError(/does not meet baseline threshold/));
test.done();
});
},
"should pass with a reachable baseline": function (test) {
test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json')));
run([ '--baseline', '71' ], function (results) {
test.ok(results.succeeded());
test.ok(!results.grepError(/No coverage files found/));
test.ok(!results.grepError(/does not meet baseline threshold/));
test.ok(results.grepOutput(/SUCCESS: Current Coverage/));
test.ok(results.grepOutput(/71\.64%/));
test.ok(results.grepOutput(/is either equal or better than the baseline/));
test.ok(results.grepOutput(/71%/));
test.done();
});
},
"should pass with a 0 baseline": function (test) {
test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json')));
run([ '--baseline', '0' ], function (results) {
test.ok(results.succeeded());
test.ok(!results.grepError(/No coverage files found/));
test.ok(!results.grepError(/does not meet baseline threshold/));
test.ok(results.grepOutput(/SUCCESS: Current Coverage/));
test.ok(results.grepOutput(/71\.64%/));
test.ok(results.grepOutput(/is either equal or better than the baseline/));
test.ok(results.grepOutput(/0%/));
test.done();
});
},
"should pass without a baseline but using the txt file": function (test) {
test.ok(existsSync(path.resolve(OUTPUT_DIR, 'coverage.json')));
run([], function (results) {
test.ok(results.succeeded());
test.ok(!results.grepError(/No coverage files found/));
test.ok(!results.grepError(/does not meet baseline threshold/));
test.ok(results.grepOutput(/SUCCESS: Current Coverage/));
test.ok(results.grepOutput(/71\.64%/));
test.ok(results.grepOutput(/is either equal or better than the baseline/));
test.done();
});
}
}
};