UNPKG

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
'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'];