UNPKG

webdriverio-workflo

Version:

This is a customized version of webdriverio for use with workflo framework.

1,261 lines (1,032 loc) 75 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _toConsumableArray2 = require('babel-runtime/helpers/toConsumableArray'); var _toConsumableArray3 = _interopRequireDefault(_toConsumableArray2); var _slicedToArray2 = require('babel-runtime/helpers/slicedToArray'); var _slicedToArray3 = _interopRequireDefault(_slicedToArray2); var _regenerator = require('babel-runtime/regenerator'); var _regenerator2 = _interopRequireDefault(_regenerator); var _promise = require('babel-runtime/core-js/promise'); var _promise2 = _interopRequireDefault(_promise); var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator'); var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2); var _assign = require('babel-runtime/core-js/object/assign'); var _assign2 = _interopRequireDefault(_assign); var _keys = require('babel-runtime/core-js/object/keys'); var _keys2 = _interopRequireDefault(_keys); var _getIterator2 = require('babel-runtime/core-js/get-iterator'); var _getIterator3 = _interopRequireDefault(_getIterator2); var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); var _createClass2 = require('babel-runtime/helpers/createClass'); var _createClass3 = _interopRequireDefault(_createClass2); var _path = require('path'); var _path2 = _interopRequireDefault(_path); var _child_process = require('child_process'); var _child_process2 = _interopRequireDefault(_child_process); var _jsonfile = require('jsonfile'); var jsonfile = _interopRequireWildcard(_jsonfile); var _fs = require('fs'); var fs = _interopRequireWildcard(_fs); var _ConfigParser = require('./utils/ConfigParser'); var _ConfigParser2 = _interopRequireDefault(_ConfigParser); var _BaseReporter = require('./utils/BaseReporter'); var _BaseReporter2 = _interopRequireDefault(_BaseReporter); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var Launcher = function () { function Launcher(configFile, argv) { (0, _classCallCheck3.default)(this, Launcher); this.configParser = new _ConfigParser2.default(); this.configParser.addConfigFile(configFile); this.configParser.merge(argv); this.reporters = this.initReporters(); this.argv = argv; this.configFile = configFile; this.exitCode = 0; this.hasTriggeredExitRoutine = false; this.hasStartedAnyProcess = false; this.processes = []; this.schedule = []; this.rid = []; this.processesStarted = 0; this.runnerFailed = 0; this.argv.screenshotId = 0; // store validation results this.finishedTests = false; this.validationResults = {}; this.validationStates = {}; this.stepStates = {}; // this.uidStore = {} this.cidProcesses = {}; this.stackStore = {}; this.stackStoreLocks = {}; this.containStackFilter = { 'at new Promise': true, '/node_modules/': true, 'runMicrotasksCallback': true, '\\node_modules\\': true }; this.stackFilter = { 'at <anonymous>': true, 'at new Promise (<anonymous>)': true, 'at Promise (<anonymous>)': true, 'Error': true }; this.stepErrors = {}; this.waitUntilErrors = {}; this.endHandlerRunFunc = this.runTestcases; } /** * check if multiremote or wdio test */ (0, _createClass3.default)(Launcher, [{ key: 'isMultiremote', value: function isMultiremote() { var caps = this.configParser.getCapabilities(); return !Array.isArray(caps); } /** * initialise reporters */ }, { key: 'initReporters', value: function initReporters() { var config = this.configParser.getConfig(); var reporter = new _BaseReporter2.default(config); /** * if no reporter is set or config property is in a wrong format * just use the dot reporter */ if (!config.reporters || !Array.isArray(config.reporters) || !config.reporters.length) { config.reporters = ['dot']; } var reporters = {}; var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = (0, _getIterator3.default)(config.reporters), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var reporterName = _step.value; var Reporter = void 0; if (typeof reporterName === 'function') { Reporter = reporterName; if (!Reporter.reporterName) { throw new Error('Custom reporters must export a unique \'reporterName\' property'); } reporters[Reporter.reporterName] = Reporter; } else if (typeof reporterName === 'string') { try { var pkgName = reporterName.startsWith('@') ? reporterName : `wdio-${reporterName}-reporter`; Reporter = require(pkgName); } catch (e) { throw new Error(`reporter "wdio-${reporterName}-reporter" is not installed. Error: ${e.stack}`); } reporters[reporterName] = Reporter; } if (!Reporter) { throw new Error(`config.reporters must be an array of strings or functions, but got '${typeof reporterName}': ${reporterName}`); } } /** * if no reporter options are set or property is in a wrong format default to * empty object */ } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } if (!config.reporterOptions || typeof config.reporterOptions !== 'object') { config.reporterOptions = {}; } for (var _reporterName in reporters) { var Reporter = reporters[_reporterName]; var reporterOptions = {}; var _iteratorNormalCompletion2 = true; var _didIteratorError2 = false; var _iteratorError2 = undefined; try { for (var _iterator2 = (0, _getIterator3.default)((0, _keys2.default)(config.reporterOptions)), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { var option = _step2.value; if (option === _reporterName && typeof config.reporterOptions[_reporterName] === 'object') { // Copy over options specifically for this reporter type reporterOptions = (0, _assign2.default)(reporterOptions, config.reporterOptions[_reporterName]); } else if (reporters[option]) { // Don't copy options for other reporters continue; } else { // Copy over generic options reporterOptions[option] = config.reporterOptions[option]; } } } catch (err) { _didIteratorError2 = true; _iteratorError2 = err; } finally { try { if (!_iteratorNormalCompletion2 && _iterator2.return) { _iterator2.return(); } } finally { if (_didIteratorError2) { throw _iteratorError2; } } } reporter.add(new Reporter(reporter, config, reporterOptions)); } return reporter; } /** * run sequence * @return {Promise} that only gets resolves with either an exitCode or an error */ }, { key: 'run', value: function () { var _ref = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee() { var _this = this; var config, caps, launcher, _exitCode, cid, _iteratorNormalCompletion3, _didIteratorError3, _iteratorError3, _iterator3, _step3, capabilities, exitCode, _iteratorNormalCompletion4, _didIteratorError4, _iteratorError4, _iterator4, _step4, _capabilities; return _regenerator2.default.wrap(function _callee$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: config = this.configParser.getConfig(); caps = this.configParser.getCapabilities(); launcher = this.getLauncher(config); this.reporters.handleEvent('start', { isMultiremote: this.isMultiremote(), capabilities: caps, config }); // this.importUidStore(config) /** * run onPrepare hook */ _context.next = 6; return config.onPrepare(config, caps); case 6: _context.next = 8; return this.runServiceHook(launcher, 'onPrepare', config, caps); case 8: if (!this.isMultiremote()) { _context.next = 17; break; } _context.next = 11; return new _promise2.default(function (resolve) { _this.resolve = resolve; _this.startRunnerInstance(_this.configParser.getTestcases(), caps, 0); }); case 11: _exitCode = _context.sent; _context.next = 14; return this.runServiceHook(launcher, 'onComplete', _exitCode, config, caps); case 14: _context.next = 16; return config.onComplete(_exitCode, config, caps); case 16: return _context.abrupt('return', _exitCode); case 17: /** * schedule test runs */ cid = 0; _iteratorNormalCompletion3 = true; _didIteratorError3 = false; _iteratorError3 = undefined; _context.prev = 21; for (_iterator3 = (0, _getIterator3.default)(caps); !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { capabilities = _step3.value; this.schedule.push({ cid: cid++, caps: capabilities, testcases: this.configParser.getTestcases(capabilities.testcases, capabilities.exclude), availableInstances: capabilities.maxInstances || config.maxInstancesPerCapability, runningInstances: 0, seleniumServer: { host: config.host, port: config.port, protocol: config.protocol } }); } /** * catches ctrl+c event */ _context.next = 29; break; case 25: _context.prev = 25; _context.t0 = _context['catch'](21); _didIteratorError3 = true; _iteratorError3 = _context.t0; case 29: _context.prev = 29; _context.prev = 30; if (!_iteratorNormalCompletion3 && _iterator3.return) { _iterator3.return(); } case 32: _context.prev = 32; if (!_didIteratorError3) { _context.next = 35; break; } throw _iteratorError3; case 35: return _context.finish(32); case 36: return _context.finish(29); case 37: process.on('SIGINT', this.exitHandler.bind(this)); /** * make sure the program will not close instantly */ if (process.stdin.isPaused()) { process.stdin.resume(); } _context.next = 41; return new _promise2.default(function (resolve) { _this.resolve = resolve; /** * return immediately if no spec was run */ if (_this.runTestcases()) { resolve(0); } }); case 41: exitCode = _context.sent; // this.exportUidStore(config) // run specs this.finishedTests = true; cid = 0; this.schedule = []; _iteratorNormalCompletion4 = true; _didIteratorError4 = false; _iteratorError4 = undefined; _context.prev = 48; for (_iterator4 = (0, _getIterator3.default)(caps); !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) { _capabilities = _step4.value; this.schedule.push({ cid: cid++, caps: _capabilities, specs: this.configParser.getSpecs(_capabilities.specs, _capabilities.exclude), availableInstances: _capabilities.maxInstances || config.maxInstancesPerCapability, runningInstances: 0, seleniumServer: { host: config.host, port: config.port, protocol: config.protocol } }); } _context.next = 56; break; case 52: _context.prev = 52; _context.t1 = _context['catch'](48); _didIteratorError4 = true; _iteratorError4 = _context.t1; case 56: _context.prev = 56; _context.prev = 57; if (!_iteratorNormalCompletion4 && _iterator4.return) { _iterator4.return(); } case 59: _context.prev = 59; if (!_didIteratorError4) { _context.next = 62; break; } throw _iteratorError4; case 62: return _context.finish(59); case 63: return _context.finish(56); case 64: this.endHandlerRunFunc = this.runSpecs; this.mergeManualTestcaseResults(); _context.next = 68; return new _promise2.default(function (resolve) { _this.resolve = resolve; /** * return immediatelly if no spec was run */ if (_this.runSpecs()) { resolve(0); } }); case 68: _context.next = 70; return this.runServiceHook(launcher, 'onComplete', exitCode, config, caps); case 70: _context.next = 72; return config.onComplete(exitCode, config, caps); case 72: return _context.abrupt('return', exitCode); case 73: case 'end': return _context.stop(); } } }, _callee, this, [[21, 25, 29, 37], [30,, 32, 36], [48, 52, 56, 64], [57,, 59, 63]]); })); function run() { return _ref.apply(this, arguments); } return run; }() }, { key: 'importUidStore', value: function importUidStore(config) { if (fs.existsSync(config.uidStorePath)) { this.uidStore = jsonfile.readFileSync(config.uidStorePath); } } }, { key: 'exportUidStore', value: function exportUidStore(config) { if (fs.existsSync(config.uidStorePath)) { fs.unlinkSync(config.uidStorePath); } jsonfile.writeFileSync(config.uidStorePath, this.uidStore); } }, { key: 'mergeManualTestcaseResults', value: function mergeManualTestcaseResults() { var manualResults = this.configParser.getManualResults(); var cid = '0-0'; if (manualResults) { var _iteratorNormalCompletion5 = true; var _didIteratorError5 = false; var _iteratorError5 = undefined; try { for (var _iterator5 = (0, _getIterator3.default)(manualResults), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) { var filename = _step5.value; var manualTestcase = require(filename).default; // how to treat different capabilities??? if (!(this.getCidGroup(cid) in this.validationResults)) { this.validationResults[this.getCidGroup(cid)] = {}; } for (var story in manualTestcase) { if (!(story in this.validationResults[this.getCidGroup(cid)])) { this.validationResults[this.getCidGroup(cid)][story] = { successes: {}, failures: {}, arguments: {}, screenshots: {} }; } for (var criteria in manualTestcase[story]) { if (manualTestcase[story][criteria].result === true) { if (!(criteria in this.validationResults[this.getCidGroup(cid)][story])) { this.validationResults[this.getCidGroup(cid)][story].successes[criteria] = []; } this.validationResults[this.getCidGroup(cid)][story].successes[criteria].push({}); } else { if (!(criteria in this.validationResults[this.getCidGroup(cid)][story])) { this.validationResults[this.getCidGroup(cid)][story].failures[criteria] = []; } this.validationResults[this.getCidGroup(cid)][story].failures[criteria].push({ message: `Spec ${story}: Then ${criteria} failed manual tests!`, stack: '' }); } this.validationResults[this.getCidGroup(cid)][story].arguments[criteria] = { date: { caption: 'Last manual execution', value: manualTestcase[story][criteria].date }, comment: { caption: 'Comment', value: manualTestcase[story][criteria].comment } }; } } } } catch (err) { _didIteratorError5 = true; _iteratorError5 = err; } finally { try { if (!_iteratorNormalCompletion5 && _iterator5.return) { _iterator5.return(); } } finally { if (_didIteratorError5) { throw _iteratorError5; } } } } } // need to figure out multiple capabilities... }, { key: 'getValidationResults', value: function getValidationResults() { return this.validationResults['0']; } /** * run service launch sequences */ }, { key: 'runServiceHook', value: function () { var _ref2 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee2(launcher, hookName) { for (var _len = arguments.length, args = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) { args[_key - 2] = arguments[_key]; } return _regenerator2.default.wrap(function _callee2$(_context2) { while (1) { switch (_context2.prev = _context2.next) { case 0: _context2.prev = 0; _context2.next = 3; return _promise2.default.all(launcher.map(function (service) { if (typeof service[hookName] === 'function') { return service[hookName].apply(service, args); } })); case 3: return _context2.abrupt('return', _context2.sent); case 6: _context2.prev = 6; _context2.t0 = _context2['catch'](0); console.error(`A service failed in the '${hookName}' hook\n${_context2.t0.stack}\n\nContinue...`); case 9: case 'end': return _context2.stop(); } } }, _callee2, this, [[0, 6]]); })); function runServiceHook(_x, _x2) { return _ref2.apply(this, arguments); } return runServiceHook; }() /** * run multiple single remote tests * @return {Boolean} true if all specs have been run and all instances have finished */ }, { key: 'runTestcases', value: function runTestcases() { var _this2 = this; var config = this.configParser.getConfig(); /** * stop spawning new processes when CTRL+C was triggered */ if (this.hasTriggeredExitRoutine) { return true; } while (this.getNumberOfRunningInstances() < config.maxInstances) { var schedulableCaps = this.schedule /** * bail if number of errors exceeds allowed */ .filter(function () { if (_this2.argv.bailErrors && config.bail && _this2.argv.bailErrors <= config.bail) { _this2.argv.bailRunner = true; } return true; }) /** * make sure complete number of running instances is not higher than general maxInstances number */ .filter(function (a) { return _this2.getNumberOfRunningInstances() < config.maxInstances; }) /** * make sure the capabiltiy has available capacities */ .filter(function (a) { return a.availableInstances > 0; }) /** * make sure capabiltiy has still caps to run */ .filter(function (a) { return a.testcases.length > 0; }) /** * make sure we are running caps with less running instances first */ .sort(function (a, b) { return a.runningInstances > b.runningInstances; }); /** * continue if no capabiltiy were schedulable */ if (schedulableCaps.length === 0) { break; } this.startRunnerInstance([schedulableCaps[0].testcases.shift()], schedulableCaps[0].caps, schedulableCaps[0].cid, schedulableCaps[0].seleniumServer); schedulableCaps[0].availableInstances--; schedulableCaps[0].runningInstances++; } return this.getNumberOfRunningInstances() === 0 && this.getNumberOfTestcasesLeft() === 0; } }, { key: 'runSpecs', value: function runSpecs() { var _this3 = this; var config = this.configParser.getConfig(); /** * stop spawning new processes when CTRL+C was triggered */ if (this.hasTriggeredExitRoutine) { return true; } while (this.getNumberOfRunningInstances() < config.maxInstances) { var schedulableCaps = this.schedule /** * make sure complete number of running instances is not higher than general maxInstances number */ .filter(function (a) { return _this3.getNumberOfRunningInstances() < config.maxInstances; }) /** * make sure the capability has available capacities */ .filter(function (a) { return a.availableInstances > 0; }) /** * make sure capability has still caps to run */ .filter(function (a) { return a.specs.length > 0; }) /** * make sure we are running caps with less running instances first */ .sort(function (a, b) { return a.runningInstances > b.runningInstances; }); /** * continue if no capability were schedulable */ if (schedulableCaps.length === 0) { break; } this.startValidatorInstance([schedulableCaps[0].specs.shift()], schedulableCaps[0].caps, schedulableCaps[0].cid); schedulableCaps[0].availableInstances--; schedulableCaps[0].runningInstances++; } return this.getNumberOfRunningInstances() === 0 && this.getNumberOfSpecsLeft() === 0; } /** * gets number of all running instances * @return {number} number of running instances */ }, { key: 'getNumberOfRunningInstances', value: function getNumberOfRunningInstances() { return this.schedule.map(function (a) { return a.runningInstances; }).reduce(function (a, b) { return a + b; }, 0); } /** * get number of total testcases left to complete whole suites * @return {number} testcases left to complete suite */ // TODO: formulate differently -> testcases are only indirectly linked to spec suites! }, { key: 'getNumberOfTestcasesLeft', value: function getNumberOfTestcasesLeft() { return this.schedule.map(function (a) { return a.testcases.length; }).reduce(function (a, b) { return a + b; }); } }, { key: 'getNumberOfSpecsLeft', value: function getNumberOfSpecsLeft() { return this.schedule.map(function (a) { return a.specs.length; }).reduce(function (a, b) { return a + b; }, 0); } /** * Start instance in a child process. * @param {Array} specs Specs to run * @param {Number} cid Capabilities ID */ }, { key: 'startRunnerInstance', value: function startRunnerInstance(specs, caps, cid, server) { var config = this.configParser.getConfig(); cid = this.getRunnerId(cid); var processNumber = this.processesStarted + 1; // process.debugPort defaults to 5858 and is set even when process // is not being debugged. var debugArgs = []; var debugType = void 0; var debugHost = ''; var debugPort = process.debugPort; for (var i in process.execArgv) { var _debugArgs = process.execArgv[i].match('--(debug|inspect)(?:-brk)?(?:=(.*):)?'); if (_debugArgs) { var _debugArgs2 = (0, _slicedToArray3.default)(_debugArgs, 3), type = _debugArgs2[1], host = _debugArgs2[2]; if (type) { debugType = type; } if (host) { debugHost = `${host}:`; } } } if (debugType) { debugArgs.push(`--${debugType}=${debugHost}${debugPort + processNumber}`); } // if you would like to add --debug-brk, use a different port, etc... var capExecArgs = [].concat((0, _toConsumableArray3.default)(config.execArgv || []), (0, _toConsumableArray3.default)(caps.execArgv || [])); // The default value for child.fork execArgs is process.execArgs, // so continue to use this unless another value is specified in config. var defaultArgs = capExecArgs.length === 0 ? process.execArgv : []; // If an arg appears multiple times the last occurrence is used var execArgv = [].concat((0, _toConsumableArray3.default)(defaultArgs), debugArgs, (0, _toConsumableArray3.default)(capExecArgs)); var childProcess = _child_process2.default.fork(_path2.default.join(__dirname, '/runner.js'), process.argv.slice(2), { cwd: process.cwd(), execArgv }); /** * increase framework timeout to 10m when debugging is enabled */ var _iteratorNormalCompletion6 = true; var _didIteratorError6 = false; var _iteratorError6 = undefined; try { for (var _iterator6 = (0, _getIterator3.default)(execArgv), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) { var argv = _step6.value; if (argv !== '--inspect') { continue; } var frameworkOptsKey = config.framework === 'mocha' ? 'mochaOpts' : config.framework === 'jasmine' ? 'jasmineNodeOpts' : 'cucumberOpts'; var frameworkTimeoutKey = config.framework === 'jasmine' ? 'defaultTimeoutInterval' : 'timeout'; if (!this.argv[frameworkOptsKey]) { this.argv[frameworkOptsKey] = {}; } this.argv[frameworkOptsKey][frameworkTimeoutKey] = 10 * 60 * 1000; } } catch (err) { _didIteratorError6 = true; _iteratorError6 = err; } finally { try { if (!_iteratorNormalCompletion6 && _iterator6.return) { _iterator6.return(); } } finally { if (_didIteratorError6) { throw _iteratorError6; } } } this.processes.push(childProcess); childProcess.on('message', this.messageHandler.bind(this, cid)).on('exit', this.endHandler.bind(this, cid)); childProcess.send({ cid, command: 'run', configFile: this.configFile, argv: this.argv, caps, processNumber, specs, server, isMultiremote: this.isMultiremote() }); this.processesStarted++; } /** * Start instance in a child process. * @param {Array} specs Specs to run * @param {Number} cid Capabilities ID */ }, { key: 'startValidatorInstance', value: function startValidatorInstance(specs, caps, cid, server) { var config = this.configParser.getConfig(); var debug = caps.debug || config.debug; cid = this.getRunnerId(cid); var processNumber = this.processesStarted + 1; if (config.execArgv) { config.execArgv = config.execArgv.filter(function (param) { return param.indexOf('--inspect') === -1; }); } // process.debugPort defaults to 5858 and is set even when process // is not being debugged. var debugArgs = debug ? [`--debug=${process.debugPort + processNumber}`] : []; // if you would like to add --debug-brk, use a different port, etc... var capExecArgs = [].concat((0, _toConsumableArray3.default)(config.execArgv || []), (0, _toConsumableArray3.default)(caps.execArgv || [])); // The default value for child.fork execArgs is process.execArgs, // so continue to use this unless another value is specified in config. var defaultArgs = capExecArgs.length ? process.execArgv : []; // If an arg appears multiple times the last occurence is used var execArgv = [].concat((0, _toConsumableArray3.default)(defaultArgs), debugArgs, (0, _toConsumableArray3.default)(capExecArgs)); var childProcess = _child_process2.default.fork(_path2.default.join(__dirname, '/validator.js'), process.argv.slice(2), { cwd: process.cwd(), execArgv }); this.processes.push(childProcess); this.cidProcesses[cid] = childProcess; childProcess.on('message', this.messageHandler.bind(this, cid)).on('exit', this.endHandler.bind(this, cid)); var validationResults = this.getValidationResults(); this.reporters.handleEvent('startSpecs', { event: 'startSpecs', cid: cid }); childProcess.send({ cid, command: 'run', configFile: this.configFile, argv: this.argv, caps, processNumber, specs, validationResults, isMultiremote: this.isMultiremote() }); this.processesStarted++; } /** * generates a runner id * @param {Number} cid capability id (unique identifier for a capability) * @return {String} runner id (combination of cid and test id e.g. 0a, 0b, 1a, 1b ...) */ }, { key: 'getRunnerId', value: function getRunnerId(cid) { if (!this.rid[cid]) { this.rid[cid] = 0; } return `${cid}-${this.rid[cid]++}`; } }, { key: 'getCidGroup', value: function getCidGroup(cid) { return cid.split('-')[0]; } }, { key: 'getPatchedStack', value: function getPatchedStack(source, cid) { var self = this; var combinedStack = source.stack; if (source.errorType && this.stackStore[cid] && combinedStack.indexOf('at testcase (') < 0) { combinedStack += '\n' + this.stackStore[cid]; } return combinedStack.split('\n').filter(function (line) { var isFiltered = false; var trimmedLine = line; if (typeof line === 'string') { trimmedLine = line.trim(); if (trimmedLine.length === 0) { return false; } } if (self.config.cleanStackTraces) { (0, _keys2.default)(self.containStackFilter).forEach(function (filterLine) { if (trimmedLine.indexOf(filterLine) > 0) { isFiltered = true; } }); (0, _keys2.default)(self.stackFilter).forEach(function (filterLine) { if (filterLine === trimmedLine) { isFiltered = true; } }); } return !isFiltered; }).join('\n'); } }, { key: 'fillValidationResults', value: function fillValidationResults(assertion, m) { var _iteratorNormalCompletion7 = true; var _didIteratorError7 = false; var _iteratorError7 = undefined; try { for (var _iterator7 = (0, _getIterator3.default)((0, _keys2.default)(assertion.specObj)), _step7; !(_iteratorNormalCompletion7 = (_step7 = _iterator7.next()).done); _iteratorNormalCompletion7 = true) { var key = _step7.value; if (!(key in this.validationResults[this.getCidGroup(m.cid)])) { this.validationResults[this.getCidGroup(m.cid)][key] = { successes: {}, failures: {}, arguments: {}, screenshots: {} }; } var _iteratorNormalCompletion8 = true; var _didIteratorError8 = false; var _iteratorError8 = undefined; try { for (var _iterator8 = (0, _getIterator3.default)(assertion.specObj[key]), _step8; !(_iteratorNormalCompletion8 = (_step8 = _iterator8.next()).done); _iteratorNormalCompletion8 = true) { var criteria = _step8.value; if (!(criteria in this.validationResults[this.getCidGroup(m.cid)][key].failures)) { this.validationResults[this.getCidGroup(m.cid)][key].failures[criteria] = []; } this.validationResults[this.getCidGroup(m.cid)][key].failures[criteria].push({ matcherName: assertion.matcherName, message: assertion.message, stack: assertion.stack, screenshotId: assertion.screenshotId, screenshotFilename: assertion.screenshotFilename }); } } catch (err) { _didIteratorError8 = true; _iteratorError8 = err; } finally { try { if (!_iteratorNormalCompletion8 && _iterator8.return) { _iterator8.return(); } } finally { if (_didIteratorError8) { throw _iteratorError8; } } } } } catch (err) { _didIteratorError7 = true; _iteratorError7 = err; } finally { try { if (!_iteratorNormalCompletion7 && _iterator7.return) { _iterator7.return(); } } finally { if (_didIteratorError7) { throw _iteratorError7; } } } } }, { key: 'handleAssertion', value: function handleAssertion(assertion, m) { if (assertion.stack.trim().indexOf('at') !== 0) { var stacklines = assertion.stack.split('\n'); stacklines.shift(); assertion.stack = stacklines.join('\n'); } if (assertion.specObj) { this.fillValidationResults(assertion, m); } m.err.message += assertion.message + '\n\n'; // for allure reports if (assertion.stack.trim().indexOf('at') === 0) { m.err.stack += assertion.message + '\n'; } m.err.stack += assertion.stack + '\n\n'; m.errs.push(assertion); } /** * emit event from child process to reporter * @param {String} cid * @param {Object} m event object */ }, { key: 'messageHandler', value: function messageHandler(cid, m) { if (!this.config) { this.config = this.configParser.getConfig(); } this.hasStartedAnyProcess = true; if (!m.cid) { m.cid = cid; } if (m.event === 'runner:error') { this.reporters.handleEvent('error', m); } if (m.event === 'uid:request') { if (!(m.id in this.uidStore)) { this.uidStore[m.id] = 0; } this.cidProcesses[m.cid].send({ event: 'uid:response', id: `${m.id}_${++this.uidStore[m.id]}`, cid: m.cid }); } if (m.event.indexOf('test:') === 0) { if (m.title) { var testData = JSON.parse(m.title); m.title = testData.title; m.metadata = testData.metadata; } } if (this.finishedTests) { m.finishedTests = true; switch (m.event) { case 'test:start': m.spec = m.specs[0]; break; case 'step:end': m.status = 'passed'; if (m.validate) { if (!this.validationStates[this.getCidGroup(m.cid)]) { this.validationStates[this.getCidGroup(m.cid)] = {}; } this.validationStates[this.getCidGroup(m.cid)].storyId = m.validate.storyId; this.validationStates[this.getCidGroup(m.cid)].criteriaId = m.validate.criteriaId; var _status = this.evaluateValidations(m.validate.storyId, m.validate.criteriaId, m); m.event = `step:${_status}`; } break; case 'test:pass': var storyId = this.validationStates[this.getCidGroup(m.cid)].storyId; var criteriaId = this.validationStates[this.getCidGroup(m.cid)].criteriaId; var status = this.evaluateValidations(storyId, criteriaId, m); m.event = `test:${status}`; this.addTestArguments(storyId, criteriaId, m); break; } this.reporters.handleEvent(m.event, m); } else { if (m.event === 'runner:currentStack') { if (!this.stackStoreLocks[m.cid]) { this.stackStore[m.cid] = m.stack; if (m.name === 'waitUntil') { this.waitUntilErrors[m.cid] = m.stack; } } } if (m.event === 'runner:aftercommand') { if (m.err) { this.stackStoreLocks[m.cid] = true; if (m.command === 'waitUntil') { this.stackStore[m.cid] = this.waitUntilErrors[m.cid]; } else if (this.waitUntilErrors[m.cid]) { this.stackStore[m.cid] = this.waitUntilErrors[m.cid]; } } else if (m.command === 'waitUntil') { delete this.waitUntilErrors[m.cid]; } } if (m.event === 'test:start') { m.testcase = m.specs[0]; this.validationStates[this.getCidGroup(m.cid)] = {}; this.stackStoreLocks[m.cid] = false; delete this.stackStore[m.cid]; delete this.waitUntilErrors[m.cid]; this.stepErrors[this.getCidGroup(m.cid)] = { failed: [], broken: [] }; } i