const mocha = require("mocha"); const inherits = require("util").inherits; const Spec = mocha.reporters.Spec; const path = require('path'); /** * This file adapted from mocha's stats-collector * https://github.com/mochajs/mocha/blob/54475eb4ca35a2c9044a1b8c59a60f09c73e6c01/lib/stats-collector.js#L1-L83 */ const Date = global.Date; /** * Provides stats such as test duration, number of tests passed / failed etc., by * listening for events emitted by `runner`. */ function mochaStats(runner) { const stats = { suites: 0, tests: 0, passes: 0, pending: 0, failures: 0 }; if (!runner) throw new Error("Missing runner argument"); runner.stats = stats; runner.on("pass", () => stats.passes++); runner.on("fail", () => stats.failures++); runner.on("pending", () => stats.pending++); runner.on("test end", () => stats.tests++); runner.once("start", () => (stats.start = new Date())); runner.once("end", function() { stats.end = new Date(); stats.duration = stats.end - stats.start; }); } /** * Based on the Mocha 'Spec' reporter. * * Watches an Ethereum test suite run and collects data about which tests hit * which lines of code. This "test matrix" can be used as an input to fault localization tools * like: https://github.com/JoranHonig/tarantula * * Mocha's JSON reporter output is also generated and saved to a separate file * * @param {Object} runner mocha's runner * @param {Object} options reporter.options (see README example usage) */ function Matrix(runner, options) { // Spec reporter Spec.call(this, runner, options); const self = this; const tests = []; const failures = []; const passes = []; // Initialize stats for Mocha 6+ epilogue if (!runner.stats) { mochaStats(runner); this.stats = runner.stats; } runner.on("test end", (info) => { options.reporterOptions.collectTestMatrixData(info); tests.push(info); }); runner.on('pass', function(info) { passes.push(info) }) runner.on('fail', function(info) { failures.push(info) }); runner.once('end', function() { delete self.stats.start; delete self.stats.end; delete self.stats.duration; var obj = { stats: self.stats, tests: tests.map(clean), failures: failures.map(clean), passes: passes.map(clean) }; runner.testResults = obj; options.reporterOptions.saveMochaJsonOutput(obj) }); // >>>>>>>>>>>>>>>>>>>>>>>>> // Mocha JSON Reporter Utils // Code taken from: // https://mochajs.org/api/reporters_json.js.html // >>>>>>>>>>>>>>>>>>>>>>>>> function clean(info) { var err = info.err || {}; if (err instanceof Error) { err = errorJSON(err); } return { title: info.title, fullTitle: info.fullTitle(), file: path.relative(options.reporterOptions.cwd, info.file), currentRetry: info.currentRetry(), err: cleanCycles(err) }; } function cleanCycles(obj) { var cache = []; return JSON.parse( JSON.stringify(obj, function(key, value) { if (typeof value === 'object' && value !== null) { if (cache.indexOf(value) !== -1) { // Instead of going in a circle, we'll print [object Object] return '' + value; } cache.push(value); } return value; }) ); } function errorJSON(err) { var res = {}; Object.getOwnPropertyNames(err).forEach(function(key) { res[key] = err[key]; }, err); return res; } } /** * Inherit from `Base.prototype`. */ inherits(Matrix, Spec); module.exports = Matrix;