wdio-workflo-spec-reporter
Version:
This is a customized version of wdio-spec-reporter for use with workflo framework.
722 lines (591 loc) • 25.5 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _events = require('events');
var _events2 = _interopRequireDefault(_events);
var _humanizeDuration = require('humanize-duration');
var _humanizeDuration2 = _interopRequireDefault(_humanizeDuration);
var _util = require('util');
var _util2 = _interopRequireDefault(_util);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var DURATION_OPTIONS = {
units: ['m', 's'],
round: true,
spacer: ''
};
var STACKTRACE_FILTER = /(node_modules(\/|\\)(\w+)*|wdio-sync\/build|- - - - -)/g;
/**
* Initialize a new `spec` test reporter.
*
* @param {Runner} runner
* @api public
*/
var SpecReporter = function (_events$EventEmitter) {
_inherits(SpecReporter, _events$EventEmitter);
function SpecReporter(baseReporter, config) {
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
_classCallCheck(this, SpecReporter);
var _this = _possibleConstructorReturn(this, (SpecReporter.__proto__ || Object.getPrototypeOf(SpecReporter)).call(this));
_this.baseReporter = baseReporter;
_this.config = config;
_this.options = options;
_this.shortEnglishHumanizer = _humanizeDuration2.default.humanizer({
language: 'shortEn',
languages: { shortEn: {
h: function h() {
return 'h';
},
m: function m() {
return 'm';
},
s: function s() {
return 's';
},
ms: function ms() {
return 'ms';
}
} }
});
_this.errorCount = 0;
_this.failureCount = 0;
_this.indents = {};
_this.stepIndents = {};
_this.stepIndentWidth = 2;
_this.stepIndentOffset = 1;
_this.suiteIndents = {};
_this.specs = {};
_this.results = {};
_this.startedSpecs = false;
_this.retryCount = 0;
_this.currentTest = undefined;
if (_this.config.reportResultsInstantly) {
_this.currentSuite = undefined;
}
_this.on('startSpecs', function (runner) {
if (!_this.startedSpecs) {
_this.results[runner.cid] = {
passing: 0,
pending: 0,
failing: 0,
broken: 0,
unvalidated: 0
};
_this.failureCount = 0;
_this.errorCount = 0;
_this.startedSpecs = true;
}
});
_this.on('runner:start', function (runner) {
this.suiteIndents[runner.cid] = {};
this.indents[runner.cid] = 0;
this.specs[runner.cid] = runner.specs;
this.results[runner.cid] = {
passing: 0,
pending: 0,
failing: 0,
broken: 0,
unvalidated: 0
};
if (this.startedSpecs) {
if (this.config.reportResultsInstantly) {
this.printRunnerInfo(runner);
}
}
});
// printNow
_this.on('runner:init', function (runner) {
if (_this.config.reportResultsInstantly) {
_this.printRunnerInfo(runner);
}
});
_this.on('suite:start', function (suite) {
this.suiteIndents[suite.cid][suite.uid] = ++this.indents[suite.cid];
if (this.config.reportResultsInstantly) {
this.currentSuite = suite;
this.printSuiteTitle(suite);
}
});
_this.on('test:setCurrentId', function (test) {
this.retryCount = 0;
this.currentTest = test;
if ((this.config.consoleLogLevel === 'testcases' || this.config.consoleLogLevel === 'steps') && !this.startedSpecs) {
this.printTestcaseTitle(test);
if (this.config.consoleLogLevel === 'steps') {
this.stepIndents[test.cid] = 0;
}
}
});
_this.on('test:pending', function (test) {
this.results[test.cid].pending++;
this.currentTest = undefined;
if (this.config.reportResultsInstantly) {
test.state = 'pending';
this.printTest(test);
}
});
_this.on('test:pass', function (test) {
this.results[test.cid].passing++;
this.currentTest = undefined;
if (this.config.reportResultsInstantly) {
test.state = 'pass';
this.printTest(test);
}
});
_this.on('test:fail', function (test) {
this.results[test.cid].failing++;
this.currentTest = undefined;
if (this.config.reportResultsInstantly) {
test.state = 'fail';
this.printTest(test);
}
});
_this.on('test:broken', function (test) {
this.results[test.cid].broken++;
this.currentTest = undefined;
if (this.config.reportResultsInstantly) {
test.state = 'broken';
this.printTest(test);
}
if (this.config.reportErrorsInstantly && !test.finishedTests) {
this.instantReportError(test.errs[test.errs.length - 1], 'bright yellow');
}
});
_this.on('test:unvalidated', function (test) {
this.results[test.cid].unvalidated++;
this.currentTest = undefined;
if (this.config.reportResultsInstantly) {
test.state = 'unvalidated';
this.printTest(test);
}
});
_this.on('validate:failure', function (data) {
if (this.config.reportErrorsInstantly) {
var assertion = this.config.cleanStackTraces ? this.cleanStack(data.assertion) : data.assertion;
this.instantReportError(assertion, 'error message');
}
});
_this.on('suite:end', function (suite) {
this.indents[suite.cid]--;
if ((this.config.consoleLogLevel === 'testcases' || this.config.consoleLogLevel === 'steps') && !this.startedSpecs) {
console.log();
}
});
_this.on('runner:end', function (runner) {
this.printSuiteResult(runner);
});
_this.on('end', function () {
if (this.startedSpecs) {
this.printSuitesSummary();
this.baseReporter.writeCompleteOutput();
}
});
_this.on('step:start', function (step) {
var _this2 = this;
if (step.title && !this.startedSpecs && this.config.consoleLogLevel === 'steps') {
this.stepIndents[step.cid]++;
if (this.logStep(step)) {
console.log(this.stepIndent(step.cid) + 'STEP: "' + step.description + '"');
var arg = JSON.parse(step.arg);
if (Object.keys(arg).length > 0) {
var argStr = _util2.default.inspect(arg);
var argLines = argStr.split('\n').map(function (line) {
return _this2.stepIndent(step.cid) + line;
});
argLines.forEach(function (line) {
return console.log(_this2.baseReporter.color('error stack', line));
});
}
}
}
});
_this.on('retry:failed', function (step) {
this.retryCount++;
if ((this.config.consoleLogLevel === 'testcases' || this.config.consoleLogLevel === 'steps') && !this.startedSpecs) {
this.stepIndents[step.cid] = 0;
this.printTestcaseTitle(this.currentTest, this.retryCount);
}
});
_this.on('retry:broken', function (step) {
this.retryCount++;
if (this.config.reportErrorsInstantly) {
this.instantReportError(step.assertion, 'bright yellow');
}
if ((this.config.consoleLogLevel === 'testcases' || this.config.consoleLogLevel === 'steps') && !this.startedSpecs) {
this.stepIndents[step.cid] = 0;
this.printTestcaseTitle(this.currentTest, this.retryCount);
}
});
_this.on('retry:validateFailure', function (message) {
if (this.config.reportErrorsInstantly) {
this.instantReportError(message.assertion, 'error message');
}
});
_this.on('step:end', function (step) {
if (!this.startedSpecs && this.config.consoleLogLevel === 'steps') {
this.stepIndents[step.cid]--;
}
});
return _this;
}
_createClass(SpecReporter, [{
key: 'indent',
value: function indent(cid, uid) {
var indents = this.suiteIndents[cid][uid];
return indents === 0 ? '' : Array(indents).join(' ');
}
}, {
key: 'stepIndent',
value: function stepIndent(cid) {
var inline = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
var indents = this.stepIndentOffset + inline + this.stepIndents[cid] * this.stepIndentWidth;
return indents === 0 ? '' : Array(indents).join(' ');
}
}, {
key: 'logStep',
value: function logStep(step) {
var title = step.title;
if (title && title !== 'Callback' && !title.startsWith('validate: {')) {
return true;
}
return false;
}
}, {
key: 'getSymbol',
value: function getSymbol(state) {
var symbols = this.baseReporter.symbols;
var symbol = '?'; // in case of an unknown state
switch (state) {
case 'pass':
symbol = symbols.ok;
break;
case 'pending':
symbol = '-';
break;
case 'fail':
this.errorCount++;
symbol = this.errorCount + ')';
break;
case 'broken':
this.errorCount++;
symbol = this.errorCount + ')';
break;
default:
this.errorCount++;
symbol = this.errorCount + ')';
break;
}
return symbol;
}
}, {
key: 'getColor',
value: function getColor(state) {
var color = null; // in case of an unknown state
switch (state) {
case 'pass':
case 'passing':
color = 'green';
break;
case 'pending':
color = 'pending';
break;
case 'fail':
case 'failing':
color = 'fail';
break;
case 'unvalidated':
color = 'unvalidated';
break;
case 'broken':
color = 'broken';
break;
}
return color;
}
}, {
key: 'getBrowserCombo',
value: function getBrowserCombo(caps) {
var verbose = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
var device = caps.deviceName;
var browser = caps.browserName || caps.browser;
var version = caps.version || caps.platformVersion || caps.browser_version;
var platform = caps.os ? caps.os + ' ' + caps.os_version : caps.platform || caps.platformName;
/**
* mobile capabilities
*/
if (device) {
var program = (caps.app || '').replace('sauce-storage:', '') || caps.browserName;
var executing = program ? 'executing ' + program : '';
if (!verbose) {
return device + ' ' + platform + ' ' + version;
}
return (device + ' on ' + platform + ' ' + version + ' ' + executing).trim();
}
if (!verbose) {
return (browser + ' ' + (version || '') + ' ' + (platform || '')).trim();
}
return browser + (version ? ' (v' + version + ')' : '') + (platform ? ' on ' + platform : '');
}
}, {
key: 'getResultList',
value: function getResultList(cid, suites) {
var preface = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
var output = '';
for (var specUid in suites) {
// Remove "before all" tests from the displayed results
if (specUid.indexOf('"before all"') === 0) {
continue;
}
var spec = suites[specUid];
var indent = this.indent(cid, specUid);
var specTitle = suites[specUid].title;
if (specUid.indexOf('"before all"') !== 0) {
output += preface + ' ' + indent + specTitle + '\n';
}
for (var testUid in spec.tests) {
var test = spec.tests[testUid];
var testTitle = spec.tests[testUid].title;
if (test.state === '') {
this.results[cid].pending++;
test.state = 'pending';
this.baseReporter.stats.counts.pending++;
}
output += preface;
output += ' ' + indent;
output += this.baseReporter.color(this.getColor(test.state), this.getSymbol(test.state));
output += ' ' + testTitle + '\n';
}
output += preface.trim() + '\n';
}
return output;
}
}, {
key: 'getSummary',
value: function getSummary(states, duration) {
var preface = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
var output = '';
var displayedDuration = false;
for (var state in states) {
var testCount = states[state];
var testDuration = '';
/**
* don't display 0 passing/pending/broken/unvalidated/failing test labels...
*/
if (testCount === 0) {
continue;
}
/**
* set duration
*/
if (!displayedDuration) {
testDuration = ' (' + this.shortEnglishHumanizer(duration, DURATION_OPTIONS) + ')';
}
var printedState = state === 'pending' ? 'skipped' : state;
output += preface + ' ';
output += this.baseReporter.color(this.getColor(state), testCount);
output += ' ' + this.baseReporter.color(this.getColor(state), printedState);
output += testDuration;
output += '\n';
displayedDuration = true;
}
return output;
}
}, {
key: 'getFailureList',
value: function getFailureList(failures, preface) {
var _this3 = this;
var output = '';
failures.forEach(function (test, i) {
var title = test.printTitle;
output += '\n';
output += _this3.baseReporter.color('error title', ++_this3.failureCount + ') ' + title.trim() + ':') + '\n\n';
var printErr = function printErr(err) {
var errMessageColor = typeof err.matcherName === 'undefined' && err.stack ? 'bright yellow' : 'error message';
if (test.unvalidated) {
errMessageColor = 'unvalidated';
}
err.message = err.message.trim();
var message = err.message.split(/\n/g).map(function (l) {
return '' + _this3.baseReporter.color(errMessageColor, l);
}).join('\n');
output += message + '\n';
if (err.stack) {
var stack = err.stack.split(/\n/g).map(function (l) {
return '' + _this3.baseReporter.color('error stack', l);
}).join('\n');
output += stack + '\n';
}
output += '\n';
};
if (test.errs && test.errs.length > 0) {
test.errs.forEach(function (err, j) {
printErr(err);
});
} else {
printErr(test.err);
}
});
return output;
}
}, {
key: 'getJobLink',
value: function getJobLink(results, preface) {
if (!results.config.host) {
return '';
}
var output = '';
if (results.config.host.indexOf('saucelabs.com') > -1) {
output += preface.trim() + '\n';
output += preface + ' Check out job at https://saucelabs.com/tests/' + results.sessionID + '\n';
return output;
}
return output;
}
}, {
key: 'printRunnerInfo',
value: function printRunnerInfo(runner) {
var cid = runner.cid;
var stats = this.baseReporter.stats;
var results = stats.runners[cid];
var combo = this.getBrowserCombo(results.capabilities);
var output = '\n------------------------------------------------------------------\n';
if (results.sessionID) {
output += this.getPhase() + ('Session ID: ' + results.sessionID + '\n');
}
if (this.startedSpecs) {
output += this.getPhase() + ('Spec File: ' + this.specs[cid] + '\n');
} else {
output += this.getPhase() + ('Testcase File: ' + this.specs[cid] + '\n');
output += this.getPhase() + ('Running: ' + combo);
}
this.baseReporter.log(output);
}
}, {
key: 'printSuiteTitle',
value: function printSuiteTitle(suite) {
var output = this.getPhase();
output += '\n';
output += this.getPhase();
output += this.indent(suite.cid, suite.uid);
output += suite.title;
this.baseReporter.log(output);
}
}, {
key: 'printTestcaseTitle',
value: function printTestcaseTitle(test, retry) {
var retryStr = retry ? ' (Retry ' + retry + ')' : '';
var output = 'TESTCASE: "' + test.id + '"...' + retryStr;
if (this.config.consoleLogLevel === 'steps') {
output = '\n' + output;
}
console.log(this.baseReporter.color('log testcase', output));
}
}, {
key: 'printTest',
value: function printTest(test) {
var output = this.getPhase();
output += ' ' + this.indent(this.currentSuite.cid, this.currentSuite.uid);
output += this.baseReporter.color(this.getColor(test.state), this.getSymbol(test.state));
output += ' ';
output += test.title;
this.baseReporter.log(output);
}
}, {
key: 'getPhase',
value: function getPhase() {
if (this.startedSpecs) {
return '[SPEC] ';
} else {
return '[TESTCASE] ';
}
}
}, {
key: 'getSuiteResult',
value: function getSuiteResult(runner) {
var cid = runner.cid;
var stats = this.baseReporter.stats;
var results = stats.runners[cid];
var preface = this.getPhase(); // `[${this.getBrowserCombo(results.capabilities, false)} #${cid}]`
var specHash = stats.getSpecHash(runner);
var spec = results.specs[specHash];
var combo = this.getBrowserCombo(results.capabilities);
var failures = stats.getFailures().filter(function (f) {
return f.cid === cid || Object.keys(f.runner).indexOf(cid) > -1;
});
/**
* don't print anything if no specs where executed
*/
if (Object.keys(spec.suites).length === 0) {
return '';
}
var output = '';
output += '------------------------------------------------------------------\n';
if (results.sessionID) {
output += preface + ' Session ID: ' + results.sessionID + '\n';
}
if (this.startedSpecs) {
output += this.getPhase() + (' Spec File: ' + this.specs[cid] + '\n');
} else {
output += this.getPhase() + (' Testcase File: ' + this.specs[cid] + '\n');
output += this.getPhase() + (' Running: ' + combo + '\n');
}
output += preface + '\n';
output += this.getResultList(cid, spec.suites, preface);
output += preface + '\n';
output += this.getSummary(this.results[cid], spec._duration, preface);
output += '------------------------------------------------------------------\n';
output += this.getFailureList(failures, preface);
output += this.getJobLink(results, preface);
return output;
}
}, {
key: 'printSuiteResult',
value: function printSuiteResult(runner) {
if (!this.config.reportResultsInstantly) {
this.baseReporter.log(this.getSuiteResult(runner));
}
}
}, {
key: 'printSuitesSummary',
value: function printSuitesSummary() {
var epilogue = this.baseReporter.epilogue;
epilogue.call(this.baseReporter);
}
}, {
key: 'cleanStack',
value: function cleanStack(error) {
var stack = error.stack.split('\n');
stack = stack.filter(function (line) {
return !line.match(STACKTRACE_FILTER) && line.startsWith(' at ');
});
error.stack = stack.join('\n');
return error;
}
}, {
key: 'instantReportError',
value: function instantReportError(err, errMessageColor) {
var _this4 = this;
var output = '';
err.message = err.message.trim();
var message = err.message.split(/\n/g).map(function (l) {
return '' + _this4.baseReporter.color(errMessageColor, l);
}).join('\n');
output += '\n' + message + '\n';
if (err.stack) {
var stack = err.stack.split(/\n/g).map(function (l) {
return '' + _this4.baseReporter.color('error stack', l);
}).join('\n');
output += stack + '\n';
}
output += '\n';
this.baseReporter.log(output);
}
}]);
return SpecReporter;
}(_events2.default.EventEmitter);
exports.default = SpecReporter;
module.exports = exports['default'];