Current Dev State

This commit is contained in:
Tim Lorsbach
2025-06-23 20:13:54 +02:00
parent b4f9bb277d
commit ded50edaa2
22617 changed files with 4345095 additions and 174 deletions

View File

@ -0,0 +1,423 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.Cursor = undefined;
var _getIterator2 = require('babel-runtime/core-js/get-iterator');
var _getIterator3 = _interopRequireDefault(_getIterator2);
var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of');
var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _createClass2 = require('babel-runtime/helpers/createClass');
var _createClass3 = _interopRequireDefault(_createClass2);
var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');
var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
var _inherits2 = require('babel-runtime/helpers/inherits');
var _inherits3 = _interopRequireDefault(_inherits2);
var _tty = require('tty');
var _tty2 = _interopRequireDefault(_tty);
var _events = require('events');
var _events2 = _interopRequireDefault(_events);
var _supportsColor = require('supports-color');
var _supportsColor2 = _interopRequireDefault(_supportsColor);
var _sanitize = require('../helpers/sanitize');
var _sanitize2 = _interopRequireDefault(_sanitize);
var _ReporterStats = require('./ReporterStats');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var ISATTY = _tty2.default.isatty(1) && _tty2.default.isatty(2);
var COLORS = {
'pass': 90,
'fail': 31,
'bright pass': 92,
'bright fail': 91,
'bright yellow': 93,
'pending': 36,
'suite': 0,
'error title': 0,
'error message': 31,
'error stack': 90,
'checkmark': 32,
'fast': 90,
'medium': 33,
'slow': 31,
'green': 32,
'light': 90,
'diff gutter': 90,
'diff added': 32,
'diff removed': 31
};
var SYMBOLS_WIN = {
ok: '\u221A',
err: '\xD7',
dot: '.',
error: 'F'
};
var SYMBOLS = {
ok: '✓',
err: '✖',
dot: '',
error: 'F'
};
var BaseReporter = function (_events$EventEmitter) {
(0, _inherits3.default)(BaseReporter, _events$EventEmitter);
function BaseReporter() {
(0, _classCallCheck3.default)(this, BaseReporter);
var _this = (0, _possibleConstructorReturn3.default)(this, (BaseReporter.__proto__ || (0, _getPrototypeOf2.default)(BaseReporter)).call(this));
_this.reporters = [];
_this.printEpilogue = true;
_this.cursor = new Cursor();
_this.stats = new _ReporterStats.ReporterStats();
_this.on('start', function () {});
_this.on('runner:start', function (runner) {
_this.stats.runnerStart(runner);
_this.stats.specStart(runner);
});
_this.on('runner:init', function (runner) {
_this.stats.setSessionId(runner);
});
_this.on('runner:beforecommand', function (command) {
_this.stats.output('beforecommand', command);
});
_this.on('runner:command', function (command) {
_this.stats.output('command', command);
});
_this.on('runner:aftercommand', function (command) {
_this.stats.output('aftercommand', command);
});
_this.on('runner:result', function (result) {
_this.stats.output('result', result);
});
_this.on('runner:screenshot', function (screenshot) {
_this.stats.output('screenshot', screenshot);
});
_this.on('runner:log', function (log) {
_this.stats.output('log', log);
});
_this.on('suite:start', function (suite) {
_this.stats.suiteStart(suite);
});
_this.on('hook:start', function (hook) {
_this.stats.hookStart(hook);
});
_this.on('hook:end', function (hook) {
_this.stats.hookEnd(hook);
});
_this.on('test:start', function (test) {
_this.stats.testStart(test);
});
_this.on('test:pass', function (test) {
_this.stats.testPass(test);
});
_this.on('test:fail', function (test) {
_this.stats.testFail(test);
});
_this.on('test:pending', function (test) {
_this.stats.testPending(test);
});
_this.on('test:end', function (test) {
_this.stats.testEnd(test);
});
_this.on('suite:end', function (runner) {
_this.stats.suiteEnd(runner);
});
_this.on('error', function (runner) {
_this.printEpilogue = false;
var fmt = _this.color('error message', 'ERROR: %s');
console.log(fmt, runner.error.message);
fmt = _this.color('bright yellow', _sanitize2.default.caps(runner.capabilities));
console.log(fmt);
if (runner.error.stack) {
fmt = _this.color('error stack', runner.error.stack.replace('Error: ' + runner.error.message + '\n', ''));
} else {
fmt = _this.color('error stack', ' no stack available');
}
console.log(fmt);
});
_this.on('runner:end', function (runner) {
_this.stats.runnerEnd(runner);
});
_this.on('end', function (args) {
_this.stats.complete();
_this.printEpilogue = _this.printEpilogue && !args.sigint;
});
return _this;
}
/**
* Color `str` with the given `type`,
* allowing colors to be disabled,
* as well as user-defined color
* schemes.
*
* @param {String} type
* @param {String} str
* @return {String}
* @api private
*/
(0, _createClass3.default)(BaseReporter, [{
key: 'color',
value: function color(type, str) {
if (!_supportsColor2.default) return String(str);
return '\x1B[' + COLORS[type] + 'm' + str + '\x1B[0m';
}
}, {
key: 'limit',
value: function limit(val) {
return _sanitize2.default.limit(val);
}
/**
* Output common epilogue used by many of
* the bundled reporters.
*
* @api public
*/
}, {
key: 'epilogue',
value: function epilogue() {
if (!this.printEpilogue) {
return;
}
var counts = this.stats.getCounts();
console.log('\n');
// passes
var fmt = this.color('green', '%d passing') + this.color('light', ' (%ss)');
console.log(fmt, counts.passes || 0, (Math.round(this.stats.duration / 100) / 10).toFixed(2));
// pending
if (counts.pending) {
fmt = this.color('pending', '%d skipped');
console.log(fmt, counts.pending);
}
// failures
if (counts.failures) {
fmt = this.color('fail', '%d failing');
console.log(fmt, counts.failures);
this.listFailures();
}
console.log();
this.printEpilogue = false;
}
/**
* Outut the given failures as a list
*/
}, {
key: 'listFailures',
value: function listFailures() {
var _this2 = this;
console.log();
this.stats.getFailures().forEach(function (test, i) {
var fmt = _this2.color('error title', '%s) %s:\n') + _this2.color('error message', '%s') + _this2.color('bright yellow', '%s') + _this2.color('error stack', '\n%s\n');
var title = typeof test.fullTitle !== 'undefined' ? test.fullTitle : typeof test.parent !== 'undefined' ? test.parent + ' ' + test.title : test.title;
console.log(fmt, i + 1, title, test.err.message, test.runningBrowser, test.err.stack);
});
}
}, {
key: 'add',
value: function add(reporter) {
this.reporters.push(reporter);
}
// Although BaseReporter is an eventemitter, handleEvent() is called instead of emit()
// so that every event can be propagated to attached reporters
}, {
key: 'handleEvent',
value: function handleEvent() {
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
if (this.listeners(args[0]).length) {
this.emit.apply(this, args);
}
if (this.reporters.length === 0) {
return;
}
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = (0, _getIterator3.default)(this.reporters), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var reporter = _step.value;
/**
* skip reporter if
* - he isn't an eventemitter
* - event is not registered
*/
if (typeof reporter.emit !== 'function' || !reporter.listeners(args[0]).length) {
continue;
}
reporter.emit.apply(reporter, args);
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
}
/**
* Default color map.
*/
}, {
key: 'colors',
get: function get() {
return COLORS;
}
/**
* Default symbol map.
*/
}, {
key: 'symbols',
get: function get() {
/**
* With node.js on Windows: use symbols available in terminal default fonts
*/
if (process.platform === 'win32') {
return SYMBOLS_WIN;
}
return SYMBOLS;
}
}]);
return BaseReporter;
}(_events2.default.EventEmitter);
/**
* Expose some basic cursor interactions
* that are common among reporters.
*/
var Cursor = function () {
function Cursor() {
(0, _classCallCheck3.default)(this, Cursor);
}
(0, _createClass3.default)(Cursor, [{
key: 'hide',
value: function hide() {
ISATTY && process.stdout.write('\x1B[?25l');
}
}, {
key: 'show',
value: function show() {
ISATTY && process.stdout.write('\x1B[?25h');
}
}, {
key: 'deleteLine',
value: function deleteLine() {
ISATTY && process.stdout.write('\x1B[2K');
}
}, {
key: 'beginningOfLine',
value: function beginningOfLine() {
ISATTY && process.stdout.write('\x1B[0G');
}
}, {
key: 'CR',
value: function CR() {
if (ISATTY) {
this.deleteLine();
this.beginningOfLine();
} else {
process.stdout.write('\r');
}
}
}, {
key: 'isatty',
get: function get() {
return ISATTY;
}
}]);
return Cursor;
}();
exports.default = BaseReporter;
exports.Cursor = Cursor;

View File

@ -0,0 +1,473 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
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 _fs = require('fs');
var _fs2 = _interopRequireDefault(_fs);
var _path = require('path');
var _path2 = _interopRequireDefault(_path);
var _glob = require('glob');
var _glob2 = _interopRequireDefault(_glob);
var _deepmerge = require('deepmerge');
var _deepmerge2 = _interopRequireDefault(_deepmerge);
var _detectSeleniumBackend = require('../helpers/detectSeleniumBackend');
var _detectSeleniumBackend2 = _interopRequireDefault(_detectSeleniumBackend);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var HOOKS = ['before', 'beforeSession', 'beforeSuite', 'beforeHook', 'beforeTest', 'beforeCommand', 'afterCommand', 'afterTest', 'afterHook', 'afterSuite', 'afterSession', 'after', 'beforeFeature', 'beforeScenario', 'beforeStep', 'afterFeature', 'afterScenario', 'afterStep', 'onError', 'onReload'];
var DEFAULT_TIMEOUT = 10000;
var NOOP = function NOOP() {};
var DEFAULT_CONFIGS = {
sync: true,
specs: [],
suites: {},
exclude: [],
logLevel: 'silent',
coloredLogs: true,
baseUrl: null,
bail: 0,
waitforInterval: 500,
waitforTimeout: 1000,
framework: 'mocha',
reporters: [],
reporterOptions: {},
maxInstances: 100,
maxInstancesPerCapability: 100,
connectionRetryTimeout: 90000,
connectionRetryCount: 3,
debug: false,
execArgv: null,
/**
* framework defaults
*/
mochaOpts: {
timeout: DEFAULT_TIMEOUT
},
jasmineNodeOpts: {
defaultTimeoutInterval: DEFAULT_TIMEOUT
},
/**
* hooks
*/
onPrepare: NOOP,
before: [],
beforeSession: [],
beforeSuite: [],
beforeHook: [],
beforeTest: [],
beforeCommand: [],
afterCommand: [],
afterTest: [],
afterHook: [],
afterSuite: [],
afterSession: [],
after: [],
onComplete: NOOP,
onError: [],
onReload: [],
/**
* cucumber specific hooks
*/
beforeFeature: [],
beforeScenario: [],
beforeStep: [],
afterFeature: [],
afterScenario: [],
afterStep: []
};
var ConfigParser = function () {
function ConfigParser() {
(0, _classCallCheck3.default)(this, ConfigParser);
this._config = DEFAULT_CONFIGS;
this._capabilities = [];
}
/**
* merges config file with default values
* @param {String} filename path of file relative to current directory
*/
(0, _createClass3.default)(ConfigParser, [{
key: 'addConfigFile',
value: function addConfigFile(filename) {
if (typeof filename !== 'string') {
throw new Error('addConfigFile requires filepath');
}
var filePath = _path2.default.resolve(process.cwd(), filename);
try {
/**
* clone the orginal config
*/
var fileConfig = (0, _deepmerge2.default)(require(filePath).config, {});
if (typeof fileConfig !== 'object') {
throw new Error('configuration file exports no config object');
}
/**
* merge capabilities
*/
this._capabilities = (0, _deepmerge2.default)(this._capabilities, fileConfig.capabilities || {});
delete fileConfig.capabilities;
/**
* add service hooks and remove them from config
*/
this.addService(fileConfig);
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = (0, _getIterator3.default)(HOOKS), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var hookName = _step.value;
delete fileConfig[hookName];
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
this._config = (0, _deepmerge2.default)(this._config, fileConfig);
/**
* detect Selenium backend
*/
this._config = (0, _deepmerge2.default)((0, _detectSeleniumBackend2.default)(this._config), this._config);
} catch (e) {
console.error('Failed loading configuration file: ' + filePath);
throw e;
}
}
/**
* merge external object with config object
* @param {Object} object desired object to merge into the config object
*/
}, {
key: 'merge',
value: function merge() {
var object = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
this._config = (0, _deepmerge2.default)(this._config, object);
/**
* run single spec file only
*/
if (typeof object.spec === 'string') {
var specs = [];
var specList = object.spec.split(/,/g);
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = (0, _getIterator3.default)(specList), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var spec = _step2.value;
if (_fs2.default.existsSync(spec)) {
specs.push(_path2.default.resolve(process.cwd(), spec));
}
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
if (specs.length === 0) {
throw new Error('spec file ' + object.spec + ' not found');
}
this._config.specs = specs;
}
/**
* user and key could get added via cli arguments so we need to detect again
* Note: cli arguments are on the right and overwrite config
* if host and port are default, remove them to get new values
*/
var defaultBackend = (0, _detectSeleniumBackend2.default)({});
if (this._config.host === defaultBackend.host && this._config.port === defaultBackend.port) {
delete this._config.host;
delete this._config.port;
}
this._config = (0, _deepmerge2.default)((0, _detectSeleniumBackend2.default)(this._config), this._config);
}
/**
* add hooks from services to runner config
* @param {Object} service a service is basically an object that contains hook methods
*/
}, {
key: 'addService',
value: function addService(service) {
var _iteratorNormalCompletion3 = true;
var _didIteratorError3 = false;
var _iteratorError3 = undefined;
try {
for (var _iterator3 = (0, _getIterator3.default)(HOOKS), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
var hookName = _step3.value;
if (!service[hookName]) {
continue;
} else if (typeof service[hookName] === 'function') {
this._config[hookName].push(service[hookName].bind(service));
} else if (Array.isArray(service[hookName])) {
var _iteratorNormalCompletion4 = true;
var _didIteratorError4 = false;
var _iteratorError4 = undefined;
try {
for (var _iterator4 = (0, _getIterator3.default)(service[hookName]), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
var hook = _step4.value;
if (typeof hook === 'function') {
this._config[hookName].push(hook.bind(service));
}
}
} catch (err) {
_didIteratorError4 = true;
_iteratorError4 = err;
} finally {
try {
if (!_iteratorNormalCompletion4 && _iterator4.return) {
_iterator4.return();
}
} finally {
if (_didIteratorError4) {
throw _iteratorError4;
}
}
}
}
}
} catch (err) {
_didIteratorError3 = true;
_iteratorError3 = err;
} finally {
try {
if (!_iteratorNormalCompletion3 && _iterator3.return) {
_iterator3.return();
}
} finally {
if (_didIteratorError3) {
throw _iteratorError3;
}
}
}
}
/**
* get excluded files from config pattern
*/
}, {
key: 'getSpecs',
value: function getSpecs(capSpecs, capExclude) {
var specs = ConfigParser.getFilePaths(this._config.specs);
var exclude = ConfigParser.getFilePaths(this._config.exclude);
/**
* check if user has specified a specific suites to run
*/
var suites = typeof this._config.suite === 'string' ? this._config.suite.split(',') : [];
if (Array.isArray(suites) && suites.length > 0) {
var suiteSpecs = [];
var _iteratorNormalCompletion5 = true;
var _didIteratorError5 = false;
var _iteratorError5 = undefined;
try {
for (var _iterator5 = (0, _getIterator3.default)(suites), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
var suiteName = _step5.value;
// ToDo: log warning if suite was not found
var suite = this._config.suites[suiteName];
if (suite && Array.isArray(suite)) {
suiteSpecs = suiteSpecs.concat(ConfigParser.getFilePaths(suite));
}
}
} catch (err) {
_didIteratorError5 = true;
_iteratorError5 = err;
} finally {
try {
if (!_iteratorNormalCompletion5 && _iterator5.return) {
_iterator5.return();
}
} finally {
if (_didIteratorError5) {
throw _iteratorError5;
}
}
}
if (suiteSpecs.length === 0) {
throw new Error('The suite(s) "' + suites.join('", "') + '" you specified don\'t exist ' + 'in your config file or doesn\'t contain any files!');
}
specs = suiteSpecs;
}
if (Array.isArray(capSpecs)) {
specs = specs.concat(ConfigParser.getFilePaths(capSpecs));
}
if (Array.isArray(capExclude)) {
exclude = exclude.concat(ConfigParser.getFilePaths(capExclude));
}
return specs.filter(function (spec) {
return exclude.indexOf(spec) < 0;
});
}
/**
* return configs
*/
}, {
key: 'getConfig',
value: function getConfig() {
return this._config;
}
/**
* return capabilities
*/
}, {
key: 'getCapabilities',
value: function getCapabilities(i) {
if (typeof i === 'number' && this._capabilities[i]) {
return this._capabilities[i];
}
return this._capabilities;
}
/**
* returns a flatten list of globed files
*
* @param {String[]} filenames list of files to glob
* @return {String[]} list of files
*/
}], [{
key: 'getFilePaths',
value: function getFilePaths(patterns, omitWarnings) {
var files = [];
if (typeof patterns === 'string') {
patterns = [patterns];
}
if (!Array.isArray(patterns)) {
throw new Error('specs or exclude property should be an array of strings');
}
var _iteratorNormalCompletion6 = true;
var _didIteratorError6 = false;
var _iteratorError6 = undefined;
try {
for (var _iterator6 = (0, _getIterator3.default)(patterns), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) {
var pattern = _step6.value;
var filenames = _glob2.default.sync(pattern);
filenames = filenames.filter(function (filename) {
return filename.slice(-3) === '.js' || filename.slice(-3) === '.ts' || filename.slice(-8) === '.feature' || filename.slice(-7) === '.coffee';
});
filenames = filenames.map(function (filename) {
return _path2.default.isAbsolute(filename) ? _path2.default.normalize(filename) : _path2.default.resolve(process.cwd(), filename);
});
if (filenames.length === 0 && !omitWarnings) {
console.warn('pattern', pattern, 'did not match any file');
}
files = (0, _deepmerge2.default)(files, filenames);
}
} catch (err) {
_didIteratorError6 = true;
_iteratorError6 = err;
} finally {
try {
if (!_iteratorNormalCompletion6 && _iterator6.return) {
_iterator6.return();
}
} finally {
if (_didIteratorError6) {
throw _iteratorError6;
}
}
}
return files;
}
}]);
return ConfigParser;
}();
exports.default = ConfigParser;
module.exports = exports['default'];

View File

@ -0,0 +1,151 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.WaitUntilTimeoutError = exports.WaitForTimeoutError = exports.RuntimeError = exports.ProtocolError = exports.CommandError = exports.ErrorHandler = undefined;
var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of');
var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _createClass2 = require('babel-runtime/helpers/createClass');
var _createClass3 = _interopRequireDefault(_createClass2);
var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');
var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
var _inherits2 = require('babel-runtime/helpers/inherits');
var _inherits3 = _interopRequireDefault(_inherits2);
var _constants = require('../helpers/constants');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var ErrorHandler = function (_Error) {
(0, _inherits3.default)(ErrorHandler, _Error);
function ErrorHandler(type, msg, details) {
(0, _classCallCheck3.default)(this, ErrorHandler);
var _this = (0, _possibleConstructorReturn3.default)(this, (ErrorHandler.__proto__ || (0, _getPrototypeOf2.default)(ErrorHandler)).call(this));
Error.captureStackTrace(_this, _this.constructor);
if (typeof msg === 'number') {
// if ID is not known error throw UnknownError
if (!_constants.ERROR_CODES[msg]) {
msg = 13;
}
_this.type = _constants.ERROR_CODES[msg].id;
_this.message = _constants.ERROR_CODES[msg].message;
if (msg === 7 && details) {
_this.message = _this.message.slice(0, -1) + ' ("' + details + '").';
}
} else if (arguments.length === 2) {
_this.message = msg;
_this.type = type;
} else if (arguments.length === 1) {
_this.type = 'WebdriverIOError';
_this.message = type;
}
if (typeof _this.message === 'object') {
var seleniumStack = _this.message;
if (seleniumStack.screenshot) {
_this.screenshot = seleniumStack.screenshot;
delete seleniumStack.screenshot;
}
if (seleniumStack.message && seleniumStack.type && seleniumStack.status) {
if (typeof seleniumStack.orgStatusMessage === 'string' && seleniumStack.orgStatusMessage.match(/"errorMessage":"NoSuchElement"/)) {
seleniumStack.type = 'NoSuchElement';
seleniumStack.status = 7;
seleniumStack.message = _constants.ERROR_CODES['7'].message;
}
_this.message = seleniumStack.message + ' (' + seleniumStack.type + ':' + seleniumStack.status + ')';
}
if (typeof seleniumStack.orgStatusMessage === 'string') {
var reqPos = seleniumStack.orgStatusMessage.indexOf(',"request"');
var problem = '';
if (reqPos > 0) {
problem = JSON.parse(seleniumStack.orgStatusMessage.slice(0, reqPos) + '}').errorMessage;
} else {
problem = seleniumStack.orgStatusMessage;
}
if (problem.indexOf('No enum constant org.openqa.selenium.Platform') > -1) {
problem = 'The Selenium backend you\'ve chosen doesn\'t support the desired platform (' + problem.slice(46) + ')';
}
// truncate errorMessage
if (problem.indexOf('(Session info:') > -1) {
problem = problem.slice(0, problem.indexOf('(Session info:')).trim();
}
// make assumption based on experience on certain error messages
if (problem.indexOf('unknown error: path is not absolute') !== -1) {
problem = 'You are trying to set a value to an input field with type="file", use the `uploadFile` command instead (Selenium error: ' + problem + ')';
}
_this.message = problem;
_this.seleniumStack = seleniumStack;
}
}
return _this;
}
/**
* make stack loggable
* @return {Object} error log
*/
(0, _createClass3.default)(ErrorHandler, [{
key: 'toJSON',
value: function toJSON() {
return {
name: this.type,
message: this.message
};
}
}]);
return ErrorHandler;
}(Error);
var CommandError = function CommandError(msg, details) {
return new ErrorHandler('CommandError', msg, details);
};
var ProtocolError = function ProtocolError(msg) {
return new ErrorHandler('ProtocolError', msg);
};
var RuntimeError = function RuntimeError(msg) {
return new ErrorHandler('RuntimeError', msg);
};
var WaitForTimeoutError = function WaitForTimeoutError(msg) {
return new ErrorHandler('WaitForTimeoutError', msg);
};
var WaitUntilTimeoutError = function WaitUntilTimeoutError(msg) {
return new ErrorHandler('WaitUntilTimeoutError', msg);
};
exports.ErrorHandler = ErrorHandler;
exports.CommandError = CommandError;
exports.ProtocolError = ProtocolError;
exports.RuntimeError = RuntimeError;
exports.WaitForTimeoutError = WaitForTimeoutError;
exports.WaitUntilTimeoutError = WaitUntilTimeoutError;
exports.default = ErrorHandler;

View File

@ -0,0 +1,312 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _stringify = require('babel-runtime/core-js/json/stringify');
var _stringify2 = _interopRequireDefault(_stringify);
var _keys = require('babel-runtime/core-js/object/keys');
var _keys2 = _interopRequireDefault(_keys);
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _createClass2 = require('babel-runtime/helpers/createClass');
var _createClass3 = _interopRequireDefault(_createClass2);
var _fs = require('fs');
var _fs2 = _interopRequireDefault(_fs);
var _path = require('path');
var _path2 = _interopRequireDefault(_path);
var _mkdirp = require('mkdirp');
var _mkdirp2 = _interopRequireDefault(_mkdirp);
var _constants = require('../helpers/constants');
var _sanitize = require('../helpers/sanitize');
var _sanitize2 = _interopRequireDefault(_sanitize);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Logger module
*
* A Logger helper with fancy colors
*/
var Logger = function () {
function Logger(options, eventHandler) {
var _this = this;
(0, _classCallCheck3.default)(this, Logger);
/**
* log level
* silent : no logs
* command : command only
* result : result only
* error : error only
* verbose : command + data + result
*/
this.logLevel = options.logLevel;
this.setupWriteStream(options);
/**
* disable colors if coloredLogs is set to false or if we pipe output into files
*/
if (!JSON.parse(process.env.WEBDRIVERIO_COLORED_LOGS) || this.writeStream) {
(0, _keys2.default)(_constants.COLORS).forEach(function (colorName) {
_constants.COLORS[colorName] = '';
});
}
// register event handler to log command events
eventHandler.on('command', function (data) {
if (_this.logLevel === 'command' || _this.logLevel === 'verbose') {
_this.command(data.method, data.uri.path);
}
if (_this.logLevel === 'data' || _this.logLevel === 'verbose') {
_this.data(data.data);
}
});
eventHandler.on('info', function (msg) {
if (_this.logLevel === 'verbose') {
_this.info(msg);
}
});
// register event handler to log result events
eventHandler.on('result', function (data) {
// only log result events if they got executed successfully
if (data.body && data.body.value && (_this.logLevel === 'result' || _this.logLevel === 'verbose')) {
_this.result(data.body.value ? data.body.value : data.body.orgStatusMessage);
}
});
// register event handler to log error events
eventHandler.on('error', function (data) {
if (data.err && data.err.code === 'ECONNREFUSED') {
_this.error('Couldn\'t find a running selenium server instance on ' + data.requestOptions.uri);
} else if (data.err && data.err.code === 'ENOTFOUND') {
_this.error('Couldn\'t resolve hostname ' + data.requestOptions.uri);
} else if (data.err && data.err.code === 'NOSESSIONID') {
_this.error('Couldn\'t get a session ID - ' + data.err.message);
} else if (_this.logLevel === 'error' || _this.logLevel === 'verbose') {
if (data.body && _constants.ERROR_CODES[data.body.status]) {
_this.error(_constants.ERROR_CODES[data.body.status].id + '\t' + _constants.ERROR_CODES[data.body.status].message + '\n\t\t\t' + data.body.value.message);
} else if (typeof data.message === 'string') {
_this.error('ServerError\t' + data.message);
} else {
_this.error(_constants.ERROR_CODES['-1'].id + '\t' + _constants.ERROR_CODES['-1'].message);
}
}
});
}
/**
* creates log file name and directories if not existing
* @param {Object} caps capabilities (required to create filename)
* @param {String} logOutputPath specified log directory
* @return {Buffer} log file buffer stream
*/
(0, _createClass3.default)(Logger, [{
key: 'getLogfile',
value: function getLogfile(caps, logOutputPath) {
logOutputPath = _path2.default.isAbsolute(logOutputPath) ? logOutputPath : _path2.default.join(process.cwd(), logOutputPath);
/**
* create directory if not existing
*/
try {
_fs2.default.statSync(logOutputPath);
} catch (e) {
_mkdirp2.default.sync(logOutputPath);
}
var newDate = new Date();
var dateString = newDate.toISOString().split(/\./)[0].replace(/:/g, '-');
var filename = _sanitize2.default.caps(caps) + '.' + dateString + '.' + process.pid + '.log';
return _fs2.default.createWriteStream(_path2.default.join(logOutputPath, filename));
}
/**
* create write stream if logOutput is a string
*/
}, {
key: 'setupWriteStream',
value: function setupWriteStream(options) {
if (typeof options.logOutput === 'string') {
this.writeStream = this.getLogfile(options.desiredCapabilities, options.logOutput);
this.logLevel = this.logLevel === 'silent' ? 'verbose' : this.logLevel;
} else if (typeof options.logOutput === 'object' && options.logOutput.writable) {
this.writeStream = options.logOutput;
this.logLevel = this.logLevel === 'silent' ? 'verbose' : this.logLevel;
}
}
}, {
key: 'write',
value: function write() {
for (var _len = arguments.length, messages = Array(_len), _key = 0; _key < _len; _key++) {
messages[_key] = arguments[_key];
}
var msgString = messages.join(' ');
if (this.writeStream) {
this.writeStream.write(msgString + '\n');
} else {
console.log(msgString);
}
}
/**
* main log function
*/
}, {
key: 'log',
value: function log(message, content) {
if (!this.logLevel || this.logLevel === 'silent') {
return;
}
var currentDate = new Date();
var dateString = currentDate.toString().match(/\d\d:\d\d:\d\d/)[0];
var preamble = _constants.COLORS.dkgray + '[' + dateString + '] ' + _constants.COLORS.reset;
if (!content) {
this.write(preamble, message);
} else {
this.write(preamble, message, '\t', (0, _stringify2.default)(_sanitize2.default.limit(content)));
}
}
/**
* logs command messages
* @param {String} method method of command request
* @param {String} path path of command request
*/
}, {
key: 'command',
value: function command(method, path) {
if (method && path) {
this.log(_constants.COLORS.violet + 'COMMAND\t' + _constants.COLORS.reset + method, path);
}
}
/**
* debugger info message
*/
}, {
key: 'debug',
value: function debug() {
this.write('');
this.log(_constants.COLORS.yellow + 'DEBUG\t' + _constants.COLORS.reset + 'Queue has stopped!');
this.log(_constants.COLORS.yellow + 'DEBUG\t' + _constants.COLORS.reset + 'You can now go into the browser or use the command line as REPL');
this.log(_constants.COLORS.yellow + 'DEBUG\t' + _constants.COLORS.dkgray + '(To exit, press ^C again or type .exit)' + _constants.COLORS.reset + '\n');
}
/**
* logs data messages
* @param {Object} data data object
*/
}, {
key: 'data',
value: function data(_data) {
_data = (0, _stringify2.default)(_sanitize2.default.limit(_data));
if (_data && (this.logLevel === 'data' || this.logLevel === 'verbose')) {
this.log(_constants.COLORS.brown + 'DATA\t\t' + _constants.COLORS.reset + _data);
}
}
/**
* logs info messages
* @param {String} msg message
*/
}, {
key: 'info',
value: function info(msg) {
this.log(_constants.COLORS.blue + 'INFO\t' + _constants.COLORS.reset + msg);
}
/**
* logs result messages
* @param {Object} result result object
*/
}, {
key: 'result',
value: function result(_result) {
_result = _sanitize2.default.limit((0, _stringify2.default)(_result));
this.log(_constants.COLORS.teal + 'RESULT\t\t' + _constants.COLORS.reset + _result);
}
/**
* logs error messages
* @param {String} msg error message
*/
}, {
key: 'error',
value: function error(msg) {
if (msg && typeof msg === 'string' && msg.indexOf('caused by Request') !== -1) {
msg = msg.substr(0, msg.indexOf('caused by Request') - 2);
}
if (msg && typeof msg === 'string' && msg.indexOf('Command duration or timeout') !== -1) {
msg = msg.substr(0, msg.indexOf('Command duration or timeout'));
}
if (msg && typeof msg === 'string' && msg.indexOf('ID does not correspond to an open view') !== -1) {
msg = msg.substr(0, msg.indexOf('ID does not correspond to an open view'));
msg += 'NOTE: you probably try to continue your tests after closing a tab/window. Please set the focus on a current opened tab/window to continue. Use the window protocol command to do so.';
}
if (msg) {
this.log(_constants.COLORS.red + 'ERROR\t' + _constants.COLORS.reset + msg, null);
}
}
/**
* print exception if command fails
* @param {String} type error type
* @param {String} message error message
* @param {String[]} stacktrace error stacktrace
*/
}], [{
key: 'printException',
value: function printException(type, message, stacktrace) {
stacktrace = stacktrace.map(function (trace) {
return ' at ' + trace;
});
this.write(_constants.COLORS.dkred + (type || 'Error') + ': ' + message + _constants.COLORS.reset, null);
this.write(_constants.COLORS.dkgray + stacktrace.reverse().join('\n') + _constants.COLORS.reset, null);
}
}]);
return Logger;
}();
exports.default = Logger;
module.exports = exports['default'];

View File

@ -0,0 +1,523 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.ReporterStats = exports.TestStats = exports.SuiteStats = exports.SpecStats = exports.RunnerStats = exports.RunnableStats = undefined;
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 _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of');
var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');
var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
var _inherits2 = require('babel-runtime/helpers/inherits');
var _inherits3 = _interopRequireDefault(_inherits2);
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _createClass2 = require('babel-runtime/helpers/createClass');
var _createClass3 = _interopRequireDefault(_createClass2);
var _deepmerge = require('deepmerge');
var _deepmerge2 = _interopRequireDefault(_deepmerge);
var _crypto = require('crypto');
var _crypto2 = _interopRequireDefault(_crypto);
var _sanitize = require('../helpers/sanitize');
var _sanitize2 = _interopRequireDefault(_sanitize);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var RunnableStats = function () {
function RunnableStats(type) {
(0, _classCallCheck3.default)(this, RunnableStats);
this.type = type;
this.start = new Date();
this._duration = 0;
}
(0, _createClass3.default)(RunnableStats, [{
key: 'complete',
value: function complete() {
this.end = new Date();
this._duration = this.end - this.start;
}
}, {
key: 'duration',
get: function get() {
if (this.end) {
return this._duration;
}
return new Date() - this.start;
}
}]);
return RunnableStats;
}();
var RunnerStats = function (_RunnableStats) {
(0, _inherits3.default)(RunnerStats, _RunnableStats);
function RunnerStats(runner) {
(0, _classCallCheck3.default)(this, RunnerStats);
var _this = (0, _possibleConstructorReturn3.default)(this, (RunnerStats.__proto__ || (0, _getPrototypeOf2.default)(RunnerStats)).call(this, 'runner'));
_this.uid = ReporterStats.getIdentifier(runner);
_this.cid = runner.cid;
_this.capabilities = runner.capabilities;
_this.sanitizedCapabilities = runner.capabilities && _sanitize2.default.caps(runner.capabilities);
_this.config = runner.config;
_this.specs = {};
return _this;
}
return RunnerStats;
}(RunnableStats);
var SpecStats = function (_RunnableStats2) {
(0, _inherits3.default)(SpecStats, _RunnableStats2);
function SpecStats(runner) {
(0, _classCallCheck3.default)(this, SpecStats);
var _this2 = (0, _possibleConstructorReturn3.default)(this, (SpecStats.__proto__ || (0, _getPrototypeOf2.default)(SpecStats)).call(this, 'spec'));
_this2.uid = ReporterStats.getIdentifier(runner);
_this2.files = runner.specs;
_this2.specHash = runner.specHash;
_this2.suites = {};
_this2.output = [];
return _this2;
}
return SpecStats;
}(RunnableStats);
var SuiteStats = function (_RunnableStats3) {
(0, _inherits3.default)(SuiteStats, _RunnableStats3);
function SuiteStats(runner) {
(0, _classCallCheck3.default)(this, SuiteStats);
var _this3 = (0, _possibleConstructorReturn3.default)(this, (SuiteStats.__proto__ || (0, _getPrototypeOf2.default)(SuiteStats)).call(this, 'suite'));
_this3.uid = ReporterStats.getIdentifier(runner);
_this3.title = runner.title;
_this3.tests = {};
_this3.hooks = {};
return _this3;
}
return SuiteStats;
}(RunnableStats);
var TestStats = function (_RunnableStats4) {
(0, _inherits3.default)(TestStats, _RunnableStats4);
function TestStats(runner) {
(0, _classCallCheck3.default)(this, TestStats);
var _this4 = (0, _possibleConstructorReturn3.default)(this, (TestStats.__proto__ || (0, _getPrototypeOf2.default)(TestStats)).call(this, 'test'));
_this4.uid = ReporterStats.getIdentifier(runner);
_this4.title = runner.title;
_this4.state = '';
_this4.screenshots = [];
_this4.output = [];
return _this4;
}
return TestStats;
}(RunnableStats);
var HookStats = function (_RunnableStats5) {
(0, _inherits3.default)(HookStats, _RunnableStats5);
function HookStats(runner) {
(0, _classCallCheck3.default)(this, HookStats);
var _this5 = (0, _possibleConstructorReturn3.default)(this, (HookStats.__proto__ || (0, _getPrototypeOf2.default)(HookStats)).call(this, 'hook'));
_this5.uid = ReporterStats.getIdentifier(runner);
_this5.title = runner.title;
_this5.parent = runner.parent;
_this5.parenUid = runner.parentUid || runner.parent;
_this5.currentTest = runner.currentTest;
return _this5;
}
return HookStats;
}(RunnableStats);
var ReporterStats = function (_RunnableStats6) {
(0, _inherits3.default)(ReporterStats, _RunnableStats6);
function ReporterStats() {
(0, _classCallCheck3.default)(this, ReporterStats);
var _this6 = (0, _possibleConstructorReturn3.default)(this, (ReporterStats.__proto__ || (0, _getPrototypeOf2.default)(ReporterStats)).call(this, 'base'));
_this6.runners = {};
_this6.reset();
return _this6;
}
(0, _createClass3.default)(ReporterStats, [{
key: 'reset',
value: function reset() {
this.counts = {
suites: 0,
tests: 0,
hooks: 0,
passes: 0,
pending: 0,
failures: 0
};
this.failures = [];
}
}, {
key: 'getCounts',
value: function getCounts() {
return this.counts;
}
}, {
key: 'getFailures',
value: function getFailures() {
var _this7 = this;
return this.failures.map(function (test) {
test.runningBrowser = '';
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = (0, _getIterator3.default)((0, _keys2.default)(test.runner)), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var pid = _step.value;
var caps = test.runner[pid];
test.runningBrowser += '\nrunning';
if (caps.browserName) {
test.runningBrowser += ' ' + caps.browserName;
}
if (caps.version) {
test.runningBrowser += ' (v' + caps.version + ')';
}
if (caps.platform) {
test.runningBrowser += ' on ' + caps.platform;
}
var host = _this7.runners[pid].config.host;
if (host && host.indexOf('saucelabs') > -1) {
test.runningBrowser += '\nCheck out job at https://saucelabs.com/tests/' + _this7.runners[pid].sessionID;
}
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
return test;
});
}
}, {
key: 'runnerStart',
value: function runnerStart(runner) {
if (!this.runners[runner.cid]) {
this.runners[runner.cid] = new RunnerStats(runner);
}
}
}, {
key: 'getRunnerStats',
value: function getRunnerStats(runner) {
if (!this.runners[runner.cid]) throw Error('Unrecognised runner [' + runner.cid + ']');
return this.runners[runner.cid];
}
}, {
key: 'getSpecHash',
value: function getSpecHash(runner) {
if (!runner.specHash) {
if (!runner.specs) throw Error('Cannot generate spec hash for runner with no \'specs\' key');
runner.specHash = _crypto2.default.createHash('md5').update(runner.specs.join('')).digest('hex');
}
return runner.specHash;
}
}, {
key: 'specStart',
value: function specStart(runner) {
var specHash = this.getSpecHash(runner);
this.getRunnerStats(runner).specs[specHash] = new SpecStats(runner);
}
}, {
key: 'getSpecStats',
value: function getSpecStats(runner) {
var runnerStats = this.getRunnerStats(runner);
var specHash = this.getSpecHash(runner);
if (!runnerStats.specs[specHash]) throw Error('Unrecognised spec [' + specHash + '] for runner [' + runner.cid + ']');
return runnerStats.specs[specHash];
}
}, {
key: 'setSessionId',
value: function setSessionId(runner) {
this.getRunnerStats(runner).sessionID = runner.sessionID;
}
}, {
key: 'suiteStart',
value: function suiteStart(runner) {
this.getSpecStats(runner).suites[ReporterStats.getIdentifier(runner)] = new SuiteStats(runner);
this.counts.suites++;
}
}, {
key: 'getSuiteStats',
value: function getSuiteStats(runner, suiteTitle) {
var specStats = this.getSpecStats(runner);
/**
* if error occurs in root level hooks we haven't created any suites yet, so
* create one here if so
*/
if (!specStats.suites[suiteTitle]) {
this.suiteStart((0, _deepmerge2.default)(runner, { title: runner.parent }));
specStats = this.getSpecStats(runner);
}
return specStats.suites[suiteTitle];
}
}, {
key: 'hookStart',
value: function hookStart(runner) {
var suiteStat = this.getSuiteStats(runner, runner.parentUid || runner.parent);
if (!suiteStat) {
return;
}
suiteStat.hooks[ReporterStats.getIdentifier(runner)] = new HookStats(runner);
}
}, {
key: 'hookEnd',
value: function hookEnd(runner) {
var hookStats = this.getHookStats(runner);
if (!hookStats) {
return;
}
hookStats.complete();
this.counts.hooks++;
}
}, {
key: 'testStart',
value: function testStart(runner) {
this.getSuiteStats(runner, runner.parentUid || runner.parent).tests[ReporterStats.getIdentifier(runner)] = new TestStats(runner);
}
}, {
key: 'getHookStats',
value: function getHookStats(runner) {
var suiteStats = this.getSuiteStats(runner, runner.parentUid || runner.parent);
if (!suiteStats) {
return;
}
// Errors encountered inside hooks (e.g. beforeEach) can be identified by looking
// at the currentTest param (currently only applicable to the Mocha adapter).
var uid = runner.currentTest || ReporterStats.getIdentifier(runner);
if (!suiteStats.hooks[uid]) {
uid = ReporterStats.getIdentifier(runner);
}
if (!suiteStats.hooks[uid]) throw Error('Unrecognised hook [' + runner.title + '] for suite [' + runner.parent + ']');
return suiteStats.hooks[uid];
}
}, {
key: 'getTestStats',
value: function getTestStats(runner) {
var suiteStats = this.getSuiteStats(runner, runner.parentUid || runner.parent);
if (!suiteStats) {
return;
}
// Errors encountered inside hooks (e.g. beforeEach) can be identified by looking
// at the currentTest param (currently only applicable to the Mocha adapter).
var uid = runner.currentTest || ReporterStats.getIdentifier(runner);
if (!suiteStats.tests[uid]) {
uid = ReporterStats.getIdentifier(runner);
}
if (!suiteStats.tests[uid]) throw Error('Unrecognised test [' + runner.title + '] for suite [' + runner.parent + ']');
return suiteStats.tests[uid];
}
}, {
key: 'output',
value: function output(type, runner) {
runner.time = new Date();
// Remove the screenshot data to reduce RAM usage on the parent process
if (type === 'screenshot') {
runner.data = null;
}
if (ReporterStats.getIdentifier(runner) && runner.parent) {
this.getTestStats(runner).output.push({
type: type,
payload: runner
});
} else {
// Log commands, results and screenshots executed outside of a test
this.getSpecStats(runner).output.push({
type: type,
payload: runner
});
}
}
}, {
key: 'testPass',
value: function testPass(runner) {
this.getTestStats(runner).state = 'pass';
this.counts.passes++;
}
}, {
key: 'testPending',
value: function testPending(runner) {
// Pending tests don't actually start, so won't yet be registered
this.testStart(runner);
this.testEnd(runner);
this.getTestStats(runner).state = 'pending';
this.counts.pending++;
}
}, {
key: 'testFail',
value: function testFail(runner) {
var testStats = void 0;
/**
* replace "Ensure the done() callback is being called in this test." with more meaningful
* message
*/
var message = 'Ensure the done() callback is being called in this test.';
if (runner.err && runner.err.message && runner.err.message.indexOf(message) > -1) {
var replacement = 'The execution in the test "' + runner.parent + ' ' + runner.title + '" took ' + 'too long. Try to reduce the run time or increase your timeout for ' + 'test specs (http://webdriver.io/guide/testrunner/timeouts.html).';
runner.err.message = runner.err.message.replace(message, replacement);
runner.err.stack = runner.err.stack.replace(message, replacement);
}
message = 'For async tests and hooks, ensure "done()" is called;';
if (runner.err && runner.err.message && runner.err.message.indexOf(message) > -1) {
var _replacement = 'Try to reduce the run time or increase your timeout for ' + 'test specs (http://webdriver.io/guide/testrunner/timeouts.html);';
runner.err.message = runner.err.message.replace(message, _replacement);
runner.err.stack = runner.err.stack.replace(message, _replacement);
}
try {
testStats = this.getTestStats(runner);
} catch (e) {
// If a test fails during the before() or beforeEach() hook, it will not yet
// have been 'started', so start now
this.testStart(runner);
testStats = this.getTestStats(runner);
}
testStats.state = 'fail';
testStats.error = runner.err;
this.counts.failures++;
/**
* check if error also happened in other runners
*/
var duplicateError = false;
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = (0, _getIterator3.default)(this.failures), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var failure = _step2.value;
if (runner.err.message !== failure.err.message || ReporterStats.getIdentifier(failure) !== ReporterStats.getIdentifier(runner)) {
continue;
}
duplicateError = true;
failure.runner[runner.cid] = runner.runner[runner.cid];
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
if (!duplicateError) {
this.failures.push(runner);
}
}
}, {
key: 'testEnd',
value: function testEnd(runner) {
this.getTestStats(runner).complete();
this.counts.tests++;
}
}, {
key: 'suiteEnd',
value: function suiteEnd(runner) {
this.getSuiteStats(runner, ReporterStats.getIdentifier(runner)).complete();
}
}, {
key: 'runnerEnd',
value: function runnerEnd(runner) {
this.getSpecStats(runner).complete();
}
}], [{
key: 'getIdentifier',
value: function getIdentifier(runner) {
return runner.uid || runner.title;
}
}]);
return ReporterStats;
}(RunnableStats);
exports.RunnableStats = RunnableStats;
exports.RunnerStats = RunnerStats;
exports.SpecStats = SpecStats;
exports.SuiteStats = SuiteStats;
exports.TestStats = TestStats;
exports.ReporterStats = ReporterStats;

View File

@ -0,0 +1,312 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _promise = require('babel-runtime/core-js/promise');
var _promise2 = _interopRequireDefault(_promise);
var _stringify = require('babel-runtime/core-js/json/stringify');
var _stringify2 = _interopRequireDefault(_stringify);
var _keys = require('babel-runtime/core-js/object/keys');
var _keys2 = _interopRequireDefault(_keys);
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _createClass2 = require('babel-runtime/helpers/createClass');
var _createClass3 = _interopRequireDefault(_createClass2);
var _url = require('url');
var _url2 = _interopRequireDefault(_url);
var _request2 = require('request');
var _request3 = _interopRequireDefault(_request2);
var _deepmerge = require('deepmerge');
var _deepmerge2 = _interopRequireDefault(_deepmerge);
var _constants = require('../helpers/constants');
var _ErrorHandler = require('./ErrorHandler');
var _package = require('../../package.json');
var _package2 = _interopRequireDefault(_package);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* RequestHandler
*/
var RequestHandler = function () {
function RequestHandler(options, eventHandler, logger) {
(0, _classCallCheck3.default)(this, RequestHandler);
this.sessionID = null;
this.startPath = options.path === '/' ? '' : options.path || '/wd/hub';
this.gridApiStartPath = '/grid/api';
this.eventHandler = eventHandler;
this.logger = logger;
this.defaultOptions = options;
/**
* actually host is `hostname:port` but to keep config properties
* short we abuse host as hostname
*/
if (options.host !== undefined) {
options.hostname = options.host;
delete options.host;
}
/**
* set auth from user and password configs
*/
if (this.defaultOptions.user && this.defaultOptions.key) {
this.auth = {
user: this.defaultOptions.user,
pass: this.defaultOptions.key
};
delete this.defaultOptions.user;
delete this.defaultOptions.key;
}
}
/**
* merges default options with request options
*
* @param {Object} requestOptions request options
*/
(0, _createClass3.default)(RequestHandler, [{
key: 'createOptions',
value: function createOptions(requestOptions, data) {
var newOptions = {};
/**
* if we don't have a session id we set it here, unless we call commands that don't require session ids, for
* example /sessions. The call to /sessions is not connected to a session itself and it therefore doesn't
* require it
*/
if (requestOptions.path.match(/:sessionId/) && !this.sessionID && requestOptions.requiresSession !== false) {
// throw session id error
throw new _ErrorHandler.RuntimeError(101);
}
// Add query parameters to request options if it is an object
if (typeof this.defaultOptions.queryParams === 'object') {
newOptions.qs = this.defaultOptions.queryParams;
}
newOptions.uri = _url2.default.parse(this.defaultOptions.protocol + '://' + this.defaultOptions.hostname + ':' + this.defaultOptions.port + (requestOptions.gridCommand ? this.gridApiStartPath : this.startPath) + requestOptions.path.replace(':sessionId', this.sessionID || ''));
// send authentication credentials only when creating new session
if (requestOptions.path === '/session' && this.auth !== undefined) {
newOptions.auth = this.auth;
}
if (requestOptions.method) {
newOptions.method = requestOptions.method;
}
if (requestOptions.gridCommand) {
newOptions.gridCommand = requestOptions.gridCommand;
}
newOptions.json = true;
newOptions.followAllRedirects = true;
newOptions.headers = {
'Connection': 'keep-alive',
'Accept': 'application/json',
'User-Agent': 'webdriverio/webdriverio/' + _package2.default.version
};
if ((0, _keys2.default)(data).length > 0) {
newOptions.json = data;
newOptions.method = 'POST';
newOptions.headers = (0, _deepmerge2.default)(newOptions.headers, {
'Content-Type': 'application/json; charset=UTF-8',
'Content-Length': Buffer.byteLength((0, _stringify2.default)(data), 'UTF-8')
});
} else if (requestOptions.method === 'POST') {
newOptions.json = {};
}
newOptions.timeout = this.defaultOptions.connectionRetryTimeout;
return newOptions;
}
/**
* creates a http request with its given options and send the protocol
* command to the webdriver server
*
* @param {Object} requestOptions defines url, method and other request options
* @param {Object} data contains request data
*/
}, {
key: 'create',
value: function create(requestOptions, data) {
var _this = this;
data = data || {};
/**
* allow to pass a string as shorthand argument
*/
if (typeof requestOptions === 'string') {
requestOptions = {
path: requestOptions
};
}
var fullRequestOptions = this.createOptions(requestOptions, data);
this.eventHandler.emit('command', {
method: fullRequestOptions.method || 'GET',
uri: fullRequestOptions.uri,
data: data
});
return this.request(fullRequestOptions, this.defaultOptions.connectionRetryCount).then(function (_ref) {
var body = _ref.body,
response = _ref.response;
/**
* if no session id was set before we've called the init command
*/
if (_this.sessionID === null && requestOptions.requiresSession !== false) {
_this.sessionID = body.sessionId || body.value.sessionId;
_this.eventHandler.emit('init', {
sessionID: _this.sessionID,
options: body.value,
desiredCapabilities: data.desiredCapabilities
});
_this.eventHandler.emit('info', 'SET SESSION ID ' + _this.sessionID);
}
if (body === undefined) {
body = {
status: 0,
orgStatusMessage: _constants.ERROR_CODES[0].message
};
}
_this.eventHandler.emit('result', {
requestData: data,
requestOptions: fullRequestOptions,
response: response,
body: body
});
return body;
}, function (err) {
_this.eventHandler.emit('result', {
requestData: data,
requestOptions: fullRequestOptions,
body: err
});
throw err;
});
}
}, {
key: 'request',
value: function request(fullRequestOptions, totalRetryCount) {
var _this2 = this;
var retryCount = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
return new _promise2.default(function (resolve, reject) {
(0, _request3.default)(fullRequestOptions, function (err, response, body) {
/**
* Resolve with a healthy response
*/
if (!err && body && (body.status === 0 || body.value && !body.status && !(body.value.error || body.value.stackTrace))) {
return resolve({ body: body, response: response });
}
if (fullRequestOptions.gridCommand) {
if (body.success) {
return resolve({ body: body, response: response });
}
return reject(new _ErrorHandler.RuntimeError({
status: 102,
type: _constants.ERROR_CODES[102].id,
message: _constants.ERROR_CODES[102].message,
orgStatusMessage: body.msg || 'unknown'
}));
}
/**
* in Appium you find sometimes more exact error messages in origValue
*/
if (body && body.value && typeof body.value.origValue === 'string' && typeof body.value.message === 'string') {
body.value.message += ' ' + body.value.origValue;
}
if (body && typeof body === 'string') {
return reject(new _ErrorHandler.RuntimeError(body));
}
if (body) {
var errorCode = _constants.ERROR_CODES[body.status] || _constants.ERROR_CODES[body.value.error] || _constants.ERROR_CODES[-1];
var error = {
status: body.status || errorCode.status || -1,
type: errorCode ? errorCode.id : 'unknown',
message: errorCode ? errorCode.message : 'unknown',
orgStatusMessage: body.value ? body.value.message : ''
};
var screenshot = body.value && body.value.screen;
if (screenshot) {
error.screenshot = screenshot;
}
return reject(new _ErrorHandler.RuntimeError(error));
}
if (retryCount >= totalRetryCount) {
var _error = null;
if (err && err.message.indexOf('Nock') > -1) {
// for better unit test error output
_error = err;
} else {
_error = new _ErrorHandler.RuntimeError({
status: -1,
type: err.code || 'ECONNREFUSED',
message: 'Couldn\'t connect to selenium server',
orgStatusMessage: err.message
});
}
return reject(_error);
}
_this2.request(fullRequestOptions, totalRetryCount, ++retryCount).then(resolve).catch(reject);
});
});
}
}]);
return RequestHandler;
}();
exports.default = RequestHandler;
module.exports = exports['default'];

View File

@ -0,0 +1,170 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _promise = require('babel-runtime/core-js/promise');
var _promise2 = _interopRequireDefault(_promise);
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _createClass2 = require('babel-runtime/helpers/createClass');
var _createClass3 = _interopRequireDefault(_createClass2);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var TIMEOUT_ERROR = 'timeout';
/**
* Promise-based Timer. Execute fn every tick.
* When fn is resolved — timer will stop
* @param {Number} delay - delay between ticks
* @param {Number} timeout - after that time timer will stop
* @param {Function} fn - function that returns promise. will execute every tick
* @param {Boolean} leading - should be function invoked on start
* @param {Boolean} isSync - true if test runner runs commands synchronously
* @return {promise} Promise-based Timer.
*/
var Timer = function () {
function Timer(delay, timeout, fn, leading, isSync) {
var _this = this;
(0, _classCallCheck3.default)(this, Timer);
this._delay = delay;
this._timeout = timeout;
this._fn = fn;
this._leading = leading;
this._conditionExecutedCnt = 0;
/**
* execute commands synchronously if method name is not async
*/
if (isSync && typeof global.wdioSync === 'function' && fn.name.match(/^(bound )*async$/) === null) {
this._fn = function () {
return new _promise2.default(function (resolve) {
return global.wdioSync(fn, resolve)();
});
};
}
var retPromise = new _promise2.default(function (resolve, reject) {
_this._resolve = resolve;
_this._reject = reject;
});
this.start();
return retPromise;
}
(0, _createClass3.default)(Timer, [{
key: 'start',
value: function start() {
var _this2 = this;
this._start = Date.now();
this._ticks = 0;
if (this._leading) {
this.tick();
} else {
this._timeoutId = setTimeout(this.tick.bind(this), this._delay);
}
this._mainTimeoutId = setTimeout(function () {
/**
* make sure that condition was executed at least once
*/
if (!_this2.wasConditionExecuted()) {
return;
}
var reason = _this2.lastError || new Error(TIMEOUT_ERROR);
_this2._reject(reason);
_this2.stop();
}, this._timeout);
}
}, {
key: 'stop',
value: function stop() {
if (this._timeoutId) {
clearTimeout(this._timeoutId);
}
this._timeoutId = null;
}
}, {
key: 'stopMain',
value: function stopMain() {
clearTimeout(this._mainTimeoutId);
}
}, {
key: 'tick',
value: function tick() {
var _this3 = this;
var result = this._fn();
if (typeof result.then !== 'function') {
this.stop();
this.stopMain();
return this._reject('Expected a promise as return value but got "' + result + '"');
}
result.then(function (res) {
return _this3.checkCondition(null, res);
}, function (err) {
return _this3.checkCondition(err);
});
}
}, {
key: 'checkCondition',
value: function checkCondition(err, res) {
++this._conditionExecutedCnt;
this.lastError = err;
// resolve timer only on truthy values
if (res) {
this._resolve(res);
this.stop();
this.stopMain();
return;
}
// autocorrect timer
var diff = Date.now() - this._start - this._ticks++ * this._delay;
var delay = Math.max(0, this._delay - diff);
// clear old timeoutID
this.stop();
// check if we have time to one more tick
if (this.hasTime(delay)) {
this._timeoutId = setTimeout(this.tick.bind(this), delay);
} else {
this.stopMain();
var reason = this.lastError || new Error(TIMEOUT_ERROR);
this._reject(reason);
}
}
}, {
key: 'hasTime',
value: function hasTime(delay) {
return Date.now() - this._start + delay <= this._timeout;
}
}, {
key: 'wasConditionExecuted',
value: function wasConditionExecuted() {
return this._conditionExecutedCnt > 0;
}
}]);
return Timer;
}();
exports.default = Timer;
module.exports = exports['default'];