Created
April 5, 2017 23:08
-
-
Save felipeochoa/45293766f321803edf9fc0fa606f7078 to your computer and use it in GitHub Desktop.
JSON reporter for jest
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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: |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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