Skip to content

Instantly share code, notes, and snippets.

@felipeochoa
Created April 5, 2017 23:08
Show Gist options
  • Save felipeochoa/45293766f321803edf9fc0fa606f7078 to your computer and use it in GitHub Desktop.
Save felipeochoa/45293766f321803edf9fc0fa606f7078 to your computer and use it in GitHub Desktop.
JSON reporter for jest
const fs = require("fs");
const path = require("path");
const mkdirp = require("mkdirp");
// when use use fit, jasmine never calls suiteStarted / suiteDone, so make a fake one to use
const fakeFocusedSuite = {
id: 'focused',
description: 'focused specs',
fullName: 'focused specs'
};
/**
* Generates json output for the given spec run.
*
* Adapted from jasmine-reporters.
*
* Usage:
*
* jasmine.getEnv().addReporter(new ElReportero(options));
*
* @param {object} [options]
* @param {string} [options.savePath] directory to save the files (default: '')
* @param {string} [options.filename] name of output file (default: 'jest-results.json')
*/
module.exports = class ElReportero {
constructor(options) {
this.started = false;
this.finished = false;
options = options || {};
this.savePath = options.savePath || '';
this.filename = options.filename || 'jest-results.json';
this.suites = [];
this.currentSuite = null;
this.totalSpecsExecuted = 0;
this.totalSpecsSkipped = 0;
this.totalSpecsDisabled = 0;
this.totalSpecsFailed = 0;
this.totalSpecsDefined = NaN;
this.__specs = {};
this.__suites = {};
this.jasmineStarted = this.jasmineStarted.bind(this);
this.suiteStarted = this.suiteStarted.bind(this);
this.specStarted = this.specStarted.bind(this);
this.specDone = this.specDone.bind(this);
this.suiteDone = this.suiteDone.bind(this);
this.jasmineDone = this.jasmineDone.bind(this);
}
getSuite(suite) {
this.__suites[suite.id] = extend(this.__suites[suite.id] || {}, suite);
return this.__suites[suite.id];
}
getSpec(spec) {
this.__specs[spec.id] = extend(this.__specs[spec.id] || {}, spec);
return this.__specs[spec.id];
}
jasmineStarted(summary) {
this.totalSpecsDefined = summary && summary.totalSpecsDefined || NaN;
this.started = true;
}
suiteStarted(suite) {
suite = this.getSuite(suite);
suite._startTime = new Date();
suite._specs = [];
suite._suites = [];
suite._failures = 0;
suite._nestedFailures = 0;
suite._skipped = 0;
suite._disabled = 0;
suite._executed = 0;
suite._parent = this.currentSuite;
if (!this.currentSuite) {
this.suites.push(suite);
} else {
this.currentSuite._suites.push(suite);
}
this.currentSuite = suite;
}
specStarted(spec) {
if (!this.currentSuite) {
// focused spec (fit) -- suiteStarted was never called
this.suiteStarted(fakeFocusedSuite);
}
spec = this.getSpec(spec);
spec._startTime = new Date();
spec._suite = this.currentSuite;
this.currentSuite._specs.push(spec);
}
specDone(spec) {
spec = this.getSpec(spec);
spec._endTime = new Date();
if (isSkipped(spec)) {
spec._suite._skipped++;
this.totalSpecsSkipped++;
}
if (isDisabled(spec)) {
spec._suite._disabled++;
this.totalSpecsDisabled++;
}
if (isFailed(spec)) {
spec._suite._failures++;
for (let parent=spec._suite._parent; parent; parent=parent._parent) {
parent._nestedFailures++;
}
this.totalSpecsFailed++;
}
spec._suite._executed++;
this.totalSpecsExecuted++;
}
suiteDone(suite) {
suite = this.getSuite(suite);
if (suite._parent === undefined) {
// disabled suite (xdescribe) -- suiteStarted was never called
this.suiteStarted(suite);
suite._disabled = true;
}
suite._endTime = new Date();
this.currentSuite = suite._parent;
}
jasmineDone() {
if (this.currentSuite) {
// focused spec (fit) -- suiteDone was never called
this.suiteDone(fakeFocusedSuite);
}
writeFile(this.getResults(), this.savePath, this.filename);
this.finished = true;
}
getResults() {
const totalSpecs = this.totalSpecsDefined || this.totalSpecsExecuted;
const disabledSpecs = totalSpecs - this.totalSpecsExecuted + this.totalSpecsDisabled;
const skippedSpecs = this.totalSpecsSkipped + disabledSpecs;
return JSON.stringify({
description: '',
numSpecs: totalSpecs,
failures: this.totalSpecsFailed,
skipped: skippedSpecs,
suites: this.suites.map(suiteAsJSON),
});
}
};
function suiteAsJSON(suite) {
return {
description: suite.description,
time: elapsed(suite._startTime, suite._endTime),
numSpecs: suite._executed,
failures: suite._failures,
skipped: suite._disabled + suite._skipped,
suites: suite._suites.map(suiteAsJSON),
specs: suite._specs.map(specAsJSON),
// Capture failures in afterAll
failures: suite.failedExpectations && suite.failedExpectations.map(failure => ({
message: failure.message,
stack: failure.stack,
})),
};
}
function specAsJSON(spec) {
return {
name: spec.description,
status: (isSkipped(spec) || isDisabled(spec)) ? 'skipped' : (isFailed(spec) ? 'failed' : 'passed'),
time: elapsed(spec._startTime, spec._endTime),
failures: spec.failedExpectations.map(failure => ({
message: failure.message,
stack: failure.stack,
})),
};
}
function elapsed(start, end) { return (end - start)/1000; }
function isFailed(obj) { return obj.status === "failed"; }
function isSkipped(obj) { return obj.status === "pending"; }
function isDisabled(obj) { return obj.status === "disabled"; }
function pad(n) { return n < 10 ? '0'+n : n; }
function extend(dupe, obj) { // performs a shallow copy of all props of `obj` onto `dupe`
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
dupe[prop] = obj[prop];
}
}
return dupe;
}
function dateTimeString(date) {
const year = date.getFullYear();
const month = date.getMonth()+1; // 0-based
const day = date.getDate();
const hours = date.getHours();
const minutes = date.getMinutes();
const seconds = date.getSeconds();
return year + "-" + pad(month) + "-" + pad(day) + 'T' + pad(hours) + ":" + pad(minutes) + ":" + pad(seconds);
}
function writeFile(text, dirpath, filename) {
mkdirp.sync(dirpath);
const filepath = path.join(dirpath, filename);
let outfile;
try {
outfile = fs.openSync(filepath, "w");
fs.writeSync(outfile, text, 0);
} finally {
if (outfile) {
fs.closeSync(outfile);
}
}
}
// Local Variables:
// js2-include-node-externs: t
// End:
const ElReportero = require('./el-reportero.js');
const reporter = new ElReportero({
// Jest runs many instances of Jasmine in parallel. Force distinct file output
// per test to avoid collisions.
filename: new Date() + '-' + Math.random().toString().replace(/^[01]?\./, '') + '.json',
savePath: '/home/felipe/Documents/iq-design/jest-reports/',
});
jasmine.getEnv().addReporter(reporter);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment