jasmine-reporters
Version:
Reporters for the Jasmine BDD Framework
279 lines (257 loc) • 10.7 kB
JavaScript
(function(global) {
var UNDEFINED,
exportObject;
if (typeof module !== "undefined" && module.exports) {
exportObject = exports;
} else {
exportObject = global.jasmineReporters = global.jasmineReporters || {};
}
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 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 log(str) {
var con = global.console || console;
if (con && con.log && str && str.length) {
con.log(str);
}
}
/**
* Basic reporter that outputs spec results to the terminal.
* Use this reporter in your build pipeline.
*
* Usage:
*
* jasmine.getEnv().addReporter(new jasmineReporters.TerminalReporter(options));
*
* @param {object} [options]
* @param {number} [options.verbosity] meaningful values are 0 through 3; anything
* greater than 3 is treated as 3 (default: 2)
* @param {boolean} [options.color] print in color or not (default: false)
* @param {boolean} [options.showStack] show stack trace for failed specs (default: false)
*/
var DEFAULT_VERBOSITY = 2,
ATTRIBUTES_TO_ANSI = {
"off": 0,
"bold": 1,
"red": 31,
"green": 32,
"yellow": 33,
"blue": 34,
"magenta": 35,
"cyan": 36
};
exportObject.TerminalReporter = function(options) {
var self = this;
self.started = false;
self.finished = false;
// sanitize arguments
options = options || {};
self.verbosity = typeof options.verbosity === "number" ? options.verbosity : DEFAULT_VERBOSITY;
self.color = options.color;
self.showStack = options.showStack;
var indent_string = " ",
startTime,
currentSuite = null,
totalSpecsExecuted = 0,
totalSpecsSkipped = 0,
totalSpecsDisabled = 0,
totalSpecsFailed = 0,
totalSpecsDefined,
// when use use fit, jasmine never calls suiteStarted / suiteDone, so make a fake one to use
fakeFocusedSuite = {
id: "focused",
description: "focused specs",
fullName: "focused specs"
};
var __suites = {}, __specs = {};
function getSuite(suite) {
__suites[suite.id] = extend(__suites[suite.id] || {}, suite);
return __suites[suite.id];
}
function getSpec(spec, suite) {
__specs[spec.id] = extend(__specs[spec.id] || {}, spec);
var ret = __specs[spec.id];
if (suite && !ret._suite) {
ret._suite = suite;
ret._depth = suite._depth+1;
suite._specs++;
}
return ret;
}
self.jasmineStarted = function(summary) {
totalSpecsDefined = summary && summary.totalSpecsDefined || NaN;
startTime = exportObject.startTime = new Date();
self.started = true;
};
self.suiteStarted = function(suite) {
suite = getSuite(suite);
suite._specs = 0;
suite._nestedSpecs = 0;
suite._failures = 0;
suite._nestedFailures = 0;
suite._skipped = 0;
suite._nestedSkipped = 0;
suite._disabled = 0;
suite._nestedDisabled = 0;
suite._depth = currentSuite ? currentSuite._depth+1 : 1;
suite._parent = currentSuite;
currentSuite = suite;
if (self.verbosity > 2) {
log(indentWithLevel(suite._depth, inColor(suite.description, "bold")));
}
};
self.specStarted = function(spec) {
if (!currentSuite) {
// focused spec (fit) -- suiteStarted was never called
self.suiteStarted(fakeFocusedSuite);
}
spec = getSpec(spec, currentSuite);
if (self.verbosity > 2) {
log(indentWithLevel(spec._depth, spec.description + " ..."));
}
};
self.specDone = function(spec) {
spec = getSpec(spec, currentSuite);
var failed = false,
skipped = false,
disabled = false,
color = "green",
resultText = "";
if (isSkipped(spec)) {
skipped = true;
color = "cyan";
spec._suite._skipped++;
totalSpecsSkipped++;
}
if (isFailed(spec)) {
failed = true;
color = "red";
spec._suite._failures++;
totalSpecsFailed++;
}
if (isDisabled(spec)) {
disabled = true;
color = "yellow";
spec._suite._disabled++;
totalSpecsDisabled++;
}
totalSpecsExecuted++;
if (self.verbosity === 2) {
resultText = failed ? "F" : skipped ? "S" : disabled ? "D" : ".";
} else if (self.verbosity > 2) {
resultText = " " + (failed ? "Failed" : skipped ? "Skipped" : disabled ? "Disabled" : "Passed");
}
log(inColor(resultText, color));
if (skipped && spec.pendingReason) {
if (self.verbosity > 2) {
log(indentWithLevel(spec._depth, inColor(spec.pendingReason, color)));
}
else {
log(inColor(spec.pendingReason, color));
}
}
if (failed) {
if (self.verbosity === 1) {
log(spec.fullName);
} else if (self.verbosity === 2) {
log(" ");
log(indentWithLevel(spec._depth, spec.fullName));
}
for (var i = 0; i < spec.failedExpectations.length; i++) {
log(inColor(indentWithLevel(spec._depth, indent_string + spec.failedExpectations[i].message), color));
if (self.showStack){
logStackLines(spec._depth, spec.failedExpectations[i].stack.split("\n"));
}
}
}
};
self.suiteDone = function(suite) {
suite = getSuite(suite);
if (suite._parent === UNDEFINED) {
// disabled suite (xdescribe) -- suiteStarted was never called
self.suiteStarted(suite);
}
if (suite._parent) {
suite._parent._specs += suite._specs + suite._nestedSpecs;
suite._parent._failures += suite._failures + suite._nestedFailures;
suite._parent._skipped += suite._skipped + suite._nestedSkipped;
suite._parent._disabled += suite._disabled + suite._nestedDisabled;
}
currentSuite = suite._parent;
if (self.verbosity < 3) {
return;
}
var total = suite._specs + suite._nestedSpecs,
failed = suite._failures + suite._nestedFailures,
skipped = suite._skipped + suite._nestedSkipped,
disabled = suite._disabled + suite._nestedDisabled,
passed = total - failed - skipped,
color = failed ? "red+bold" : "green+bold",
str = passed + " of " + total + " passed (" + skipped + " skipped, " + disabled + " disabled)";
log(indentWithLevel(suite._depth, inColor(str+".", color)));
};
self.jasmineDone = function() {
if (currentSuite) {
// focused spec (fit) -- suiteDone was never called
self.suiteDone(fakeFocusedSuite);
}
var now = new Date(),
dur = elapsed(startTime, now),
total = totalSpecsDefined || totalSpecsExecuted,
disabled = total - totalSpecsExecuted + totalSpecsDisabled,
skipped = totalSpecsSkipped,
spec_str = total + (total === 1 ? " spec, " : " specs, "),
fail_str = totalSpecsFailed + (totalSpecsFailed === 1 ? " failure, " : " failures, "),
skip_str = skipped + " skipped, ",
disabled_str = disabled + " disabled in ",
summary_str = spec_str + fail_str + skip_str + disabled_str + dur + "s.",
result_str = (totalSpecsFailed && "FAILURE: " || "SUCCESS: ") + summary_str,
result_color = totalSpecsFailed && "red+bold" || "green+bold";
if (self.verbosity === 2) {
log("");
}
if (self.verbosity > 0) {
log(inColor(result_str, result_color));
}
//log("Specs skipped but not reported (entire suite skipped or targeted to specific specs)", totalSpecsDefined - totalSpecsExecuted + totalSpecsDisabled);
self.finished = true;
// this is so phantomjs-testrunner.js can tell if we're done executing
exportObject.endTime = now;
};
function indentWithLevel(level, string) {
if (!string || !string.length) {
return "";
}
return new Array(level).join(indent_string) + string;
}
function logStackLines(depth, lines) {
lines.forEach(function(line){
log(inColor(indentWithLevel(depth, indent_string + line), "magenta"));
});
}
function inColor(string, color) {
var color_attributes = color && color.split("+"),
ansi_string = "",
i;
if (!string || !string.length) {
return "";
} else if (!self.color || !color_attributes) {
return string;
}
for(i = 0; i < color_attributes.length; i++) {
ansi_string += "\u001b[" + ATTRIBUTES_TO_ANSI[color_attributes[i]] + "m";
}
ansi_string += string + "\u001b[" + ATTRIBUTES_TO_ANSI["off"] + "m";
return ansi_string;
}
};
})(this);