jscoverage で instrumented version の src tree を作る
jscoverage src src-cov
instrumentation の command を grunt 化する (grunt-shell を使うと便利)
shell:
instrument:
command: 'jscoverage src src-cov'
これで、grunt shell:instrument というタスクが jscoverage src src-cov と同じになります。
instrumented version で jasmine test を走らせる。
jasmine:
cov:
src: 'src-cov/**/*.js'
jasmine に独自 reporter を追加して、jscoverage データを拾って console に出力する
jasmine.getEnv().addReporter() を呼ぶと独自の reporter を追加する事が出来る。 grunt-contrib-jasmine の config で spec の helper を登録出来るので、helper 内で下のような reporter を登録する。
jasmine.getEnv().addReporter({
reportRunnerResults: function (runner) {
if (window._$jscoverage != null) {
phantom.sendMessage('writeln', '\ncoverage:' + JSON.stringify(window._$jscoverage));
}
}
});
phantom.sendMessage('writeln', '\ncoverage:' + JSON.stringify(window._$jscoverage));
jscoverage では coverage データは window._$jscoverage という object 内にためられるので、これを reportRunnerResults
( spec 終了時 hook ) の中で拾う。
phantom.sendMessage
は、grunt-contrib-jasmine が提供している phantomjs から grunt に対して通信するための bridge method。
この呼び出しで grunt の出力に coverage:{...} という行が追加される。
jscoverage データを coveralls.io 形式に変換
#! /usr/bin/env node
/**
* take jscoverage data from stdin
* and output coveralls.io json to stdout
*/
var SOURCE_DIR = './';
var SERVICE_NAME = 'travis-ci';
var JOB_ID = process.env.TRAVIS_JOB_ID;
var readline = require('readline');
var fs = require('fs');
var reCoverageLine = /^coverage:(.*)/;
exports.main = function (done, logger) {
'use strict';
var coverage;
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
terminal: false
});
rl.on('line', function (line) {
var match = reCoverageLine.exec(line);
if (match) {
var data;
try {
data = JSON.parse(match[1]);
coverage = Coverage.createFromJscoverage(data, SERVICE_NAME, JOB_ID);
} catch (e) {
logger('coverage data broken');
logger(e);
}
}
});
rl.on('close', function () {
if (coverage != null) {
logger(coverage.toCoverallsJson());
};
done();
});
};
var Coverage = (function () {
'use strict';
var Coverage = function (files, service_name, job_id) {
this.files = files;
this.service_name = service_name;
this.job_id = job_id;
};
Coverage.createFromJscoverage = function (data, service_name, job_id) {
var files = [];
Object.keys(data).forEach(function (file) {
var cov = data[file];
cov.shift();
files.push({
name: file,
coverage: cov,
source: Coverage.readFile(file)
});
});
return new Coverage(files, service_name, job_id);
};
Coverage.readFile = function (file) {
return fs.readFileSync(SOURCE_DIR + file).toString();
};
var coveragePt = Coverage.prototype;
coveragePt.toCoverallsFormat = function () {
return {
service_job_id: this.job_id,
service_name: this.service_name,
source_files: this.files
};
};
coveragePt.toCoverallsJson = function () {
return JSON.stringify(this.toCoverallsFormat());
};
return Coverage;
}());
exports.main(function () {}, function (log) {
console.log(log);
});
(↑ travis-ci 上で動かす前提)
上の utility を
grunt cov --no-color | ./convert-to-coveralls.js
と実行すると coveralls.io 用の json が出力される。
これを travis-ci 環境から https://coveralls.io/api/v1/jobs に post すれば、coveralls.io 上にめでたく coverage データが生成される。
travis-ci 環境にいるかどうかは TRAVIS_JOB_ID
変数が定義されているかどうかで判定出来るのでたとえば下のような Makefile を作成する。
.PHONY: default test coveralls
GRUNT = ./node_modules/.bin/grunt
default:
ifndef TRAVIS_JOB_ID
@$(MAKE) test
else
@$(MAKE) coveralls
endif
test:
$(GRUNT)
coveralls:
$(GRUNT) cov --no-color | ./convert-to-coveralls.js | curl -F "json_file=@-" https://coveralls.io/api/v1/jobs
以上を用意した上で、package.json の scripts.test を make にしておけば、local では、普通に test が実行されて、travis-ci 上では test しつつ coveralls.io に coverage データを送りつける repository 環境が出来上がる。