jasmine-istanbul-reporter
Version:
Jasmine report plugin that scrapes test pages for istanbul coverage stats and writes them to a set of files for eventual conversion to LCOV, Cobertura, etc.
119 lines (101 loc) • 5.4 kB
JavaScript
var merge = require('merge');
var browser = require('./lib/browser');
var fileSystem = require('./lib/file-system');
var uuid = require('uuid');
var path = require('path');
var ArgumentError = require('./lib/error').ArgumentError;
module.exports = JasmineReporterIstanbul;
function JasmineReporterIstanbul(options) {
var JasmineReporterIstanbulInstance = this;
JasmineReporterIstanbulInstance.defaultOptions = {
outputPath: './coverage',
functionBindingList: [],
driver: undefined
};
JasmineReporterIstanbulInstance.preserveCoverage = preserveCoverage;
JasmineReporterIstanbulInstance.specDone = specDone;
JasmineReporterIstanbulInstance.options = processOptions(options);
function preserveCoverage() {
// TODO - find a way to clean this up without breaking the synchronous nature of this method
// TODO - what about async methods that you wrap? maybe just easier to re-use/abuse sinon?
var originalFunction = arguments.callee.originalFunction;
var originalArguments = arguments;
browser.executeScript('return __coverage__;', function (error, coverageObject) {
if (error) throw error;
originalFunction.apply(this, originalArguments);
var coverageString = JSON.stringify(coverageObject);
browser.executeScript('__coverage__ = JSON.parse(\'' + coverageString + '\');', function (error) {
if (error) throw error;
});
});
}
function specDone() {
// TODO - find a way to clean this up without breaking the synchronous nature of this method
console.info("Attempting to gather coverage from the finished test");
var generatedCoverageFileName = uuid.v4() + '.json';
var generatedCoverageFilePath = path.join(JasmineReporterIstanbulInstance.options.outputPath, generatedCoverageFileName);
browser.executeScript('return __coverage__;', function (error, coverageObject) {
if (error || !coverageObject) {
console.info("Failed to gather coverage from the finished test");
return;
}
fileSystem.writeJsonToFile(coverageObject, generatedCoverageFilePath, function (error) {
if (error) {
console.info("Failed to write coverage for the finished test to " + generatedCoverageFilePath);
return;
}
console.info("Succeeded in writing coverage to " + generatedCoverageFilePath);
});
});
}
function processOptions(pendingOptions) {
// TODO - these are not quite all on the same level of abstraction
pendingOptions = merge(JasmineReporterIstanbulInstance.defaultOptions, pendingOptions);
validateOptions(pendingOptions);
applyOptions(pendingOptions);
return pendingOptions;
}
function validateOptions(pendingOptions) {
// TODO - these are not quite all on the same level of abstraction
if (typeof pendingOptions.outputPath !== 'string') throw new ArgumentError('please pass in a string for the output path');
if (!(pendingOptions.functionBindingList instanceof Array)) throw new ArgumentError('please pass in an array of function bindings to wrap');
validateFunctionBindingList(pendingOptions.functionBindingList);
if (!pendingOptions.driver) throw new ArgumentError('driver option is required');
}
function applyOptions(pendingOptions) {
wrapFunctionBindingList(pendingOptions.functionBindingList);
browser.setDriver(pendingOptions.driver);
}
function validateFunctionBindingList(functionBindingList) {
functionBindingList.forEach(function (functionBindingEntry) {
validateFunctionBindingEntry(functionBindingEntry);
});
}
function validateFunctionBindingEntry(functionBindingEntry) {
if (typeof functionBindingEntry !== 'object') throw new ArgumentError('please pass in an array of function bindings to wrap');
Object.keys(functionBindingEntry).forEach(function (functionBindingName) {
var functionBindingObject = functionBindingEntry[functionBindingName];
var functionBinding = functionBindingObject[functionBindingName];
if (!(functionBinding instanceof Function)) throw new ArgumentError('please pass in an array of function bindings to wrap');
});
}
function wrapFunctionBindingList(functionBindingList) {
functionBindingList.forEach(function (functionBindingEntry) {
wrapFunctionBindingEntry(functionBindingEntry);
});
}
function wrapFunctionBindingEntry(functionBindingEntry) {
Object.keys(functionBindingEntry).forEach(function (functionBindingName) {
var functionBindingObject = functionBindingEntry[functionBindingName];
var functionBinding = functionBindingObject[functionBindingName];
function Proxy() {
this.originalFunction = functionBinding;
this.originalFunction.displayName = this.originalFunction.name;
var f = JasmineReporterIstanbulInstance.preserveCoverage;
f.__proto__ = this;
return f;
}
functionBindingObject[functionBindingName] = new Proxy();
});
}
}