webdriverio-workflo
Version:
This is a customized version of webdriverio for use with workflo framework.
883 lines (716 loc) • 34.6 kB
JavaScript
var _promise = require('babel-runtime/core-js/promise');
var _promise2 = _interopRequireDefault(_promise);
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 _regenerator = require('babel-runtime/regenerator');
var _regenerator2 = _interopRequireDefault(_regenerator);
var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator');
var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2);
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 _gaze = require('gaze');
var _gaze2 = _interopRequireDefault(_gaze);
var _ConfigParser = require('./utils/ConfigParser');
var _ConfigParser2 = _interopRequireDefault(_ConfigParser);
var _ = require('../');
var _jsonfile = require('jsonfile');
var jsonfile = _interopRequireWildcard(_jsonfile);
var _fs = require('fs');
var fs = _interopRequireWildcard(_fs);
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 }; }
// there seems to be a bug with deasync on mac - disable multi-runner functionality for the while being
// since it is not supported yet anyhow
// import deasync from 'deasync'
var WATCH_NOTIFICATION = '\nWDIO is now in watch mode and is waiting for a change...';
var MERGE_OPTIONS = { clone: false };
var Runner = function () {
function Runner() {
(0, _classCallCheck3.default)(this, Runner);
this.haltSIGINT = false;
this.sigintWasCalled = false;
this.hasSessionID = false;
this.failures = 0;
this.forceKillingProcess = false;
this.isRunning = false;
this.fileTriggeredWhileRunning = null;
this.uid = undefined;
this.config = undefined;
this.uidStore = {};
}
// supports multiple parallel runner instances
// getUid (id) {
// let uid
// process.send({
// event: 'uid:request',
// id: id
// })
// function uidResponseListener (m) {
// if (m.event === 'uid:response') {
// uid = m.id
// process.removeListener('message', uidResponseListener)
// }
// }
// process.on('message', uidResponseListener)
// deasync.loopWhile(function () { return !uid })
// return uid
// }
(0, _createClass3.default)(Runner, [{
key: 'getUid',
value: function getUid(id) {
if (!(id in this.uidStore)) {
this.uidStore[id] = 0;
}
return `${id}_${++this.uidStore[id]}`;
}
}, {
key: 'importUidStore',
value: function importUidStore() {
if (fs.existsSync(this.config.uidStorePath)) {
this.uidStore = jsonfile.readFileSync(this.config.uidStorePath);
}
}
}, {
key: 'exportUidStore',
value: function exportUidStore() {
if (fs.existsSync(this.config.uidStorePath)) {
fs.unlinkSync(this.config.uidStorePath);
}
jsonfile.writeFileSync(this.config.uidStorePath, this.uidStore);
}
}, {
key: 'run',
value: function () {
var _ref = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee(m) {
var _this = this;
var config, self, res;
return _regenerator2.default.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
this.cid = m.cid;
this.specs = m.specs;
this.caps = m.caps;
this.configParser = new _ConfigParser2.default();
this.configParser.addConfigFile(m.configFile);
/**
* merge cli arguments into config
*/
this.configParser.merge(m.argv);
/**
* merge host/port changes by service launcher into config
*/
this.configParser.merge(m.server);
config = this.configParser.getConfig();
this.config = config;
this.addCommandHooks(config);
if (!m.argv.bailRunner) {
this.initialiseServices(config);
} else {
config.beforeTest = function () {};
config.afterTest = function () {};
config.after = function () {};
config.beforeCommand = function () {};
config.afterCommand = function () {};
config.beforeHook = function () {};
config.afterHook = function () {};
config.beforeSuite = function () {};
config.afterSuite = function () {};
}
_context.next = 13;
return this.runHook('beforeSession', config, this.caps, this.specs);
case 13:
this.framework = this.initialiseFramework(config);
global.browser = this.initialiseInstance(m.isMultiremote, this.caps);
this.initialisePlugins(config);
/**
* store end method before it gets fiberised by wdio-sync
*/
this.endSession = global.browser.end.bind(global.browser);
/**
* initialisation successful, send start message
*/
process.send({
event: 'runner:start',
cid: m.cid,
specs: m.specs,
capabilities: this.caps,
config
});
/**
* register runner events
*/
global.browser.on('init', function (payload) {
process.send({
event: 'runner:init',
cid: m.cid,
specs: _this.specs,
sessionID: payload.sessionID,
options: payload.options,
desiredCapabilities: payload.desiredCapabilities
});
_this.hasSessionID = true;
});
global.browser.on('command', function (payload) {
var command = {
event: 'runner:command',
cid: m.cid,
specs: _this.specs,
method: payload.method,
uri: payload.uri,
data: payload.data
};
process.send(_this.addTestDetails(command));
});
global.browser.on('result', function (payload) {
var result = {
event: 'runner:result',
cid: m.cid,
specs: _this.specs,
body: payload.body // ToDo figure out if this slows down the execution time
/**
* multiremote doesn't send request data and options
*/
};if (!global.browser.isMultiremote) {
var _payload$requestOptio = payload.requestOptions,
uri = _payload$requestOptio.uri,
method = _payload$requestOptio.method,
headers = _payload$requestOptio.headers,
timeout = _payload$requestOptio.timeout;
result.requestOptions = { uri, method, headers, timeout };
result.requestData = payload.requestData;
}
process.send(_this.addTestDetails(result));
/**
* update sessionId property
*/
if (payload.requestOptions && payload.requestOptions.method === 'POST' && payload.requestOptions.uri.path.match(/\/session$/)) {
global.browser.sessionId = payload.body.sessionId;
}
});
global.browser.on('screenshot', function (payload) {
var details = {
event: 'runner:screenshot',
cid: m.cid,
specs: _this.specs,
filename: payload.filename,
data: payload.data
};
process.send(_this.addTestDetails(details));
});
global.browser.on('log', function () {
for (var _len = arguments.length, data = Array(_len), _key = 0; _key < _len; _key++) {
data[_key] = arguments[_key];
}
var details = {
event: 'runner:log',
cid: m.cid,
specs: _this.specs,
data
};
process.send(_this.addTestDetails(details));
});
process.on('test:start', function (test) {
_this.currentTest = test;
});
global.browser.on('error', function (payload) {
process.send({
event: 'runner:error',
cid: m.cid,
specs: _this.specs,
error: payload,
capabilities: _this.caps
});
});
this.haltSIGINT = true;
this.inWatchMode = Boolean(config.watch);
if (!m.argv.bailRunner) {
/**
* register global helper method to fetch elements
*/
global.$ = function (selector) {
return global.browser.element(selector);
};
global.$$ = function (selector) {
return global.browser.elements(selector).value;
};
}
self = this;
global.getUid = function (id) {
return _this.getUid.call(self, id);
};
_context.prev = 30;
this.importUidStore(this.config);
if (m.argv.bailRunner) {
_context.next = 37;
break;
}
_context.next = 35;
return global.browser.init();
case 35:
res = _context.sent;
global.browser.sessionId = res.sessionId;
case 37:
this.haltSIGINT = false;
/**
* make sure init and end can't get called again
*/
global.browser.options.isWDIO = true;
/**
* kill session of SIGINT signal showed up while trying to
* get a session ID
*/
if (!this.sigintWasCalled) {
_context.next = 45;
break;
}
_context.next = 42;
return this.end(1);
case 42:
process.removeAllListeners();
global.browser.removeAllListeners();
return _context.abrupt('return');
case 45:
if (!this.inWatchMode) {
_context.next = 47;
break;
}
return _context.abrupt('return', this.runWatchMode(m.cid, config, m.specs));
case 47:
_context.next = 49;
return this.framework.run(m.cid, config, m.specs, this.caps);
case 49:
this.failures = _context.sent;
_context.next = 52;
return this.end(this.failures);
case 52:
_context.next = 54;
return this.runHook('afterSession', config, this.caps, this.specs);
case 54:
this.exportUidStore();
process.exit(this.failures === 0 ? 0 : 1);
_context.next = 67;
break;
case 58:
_context.prev = 58;
_context.t0 = _context['catch'](30);
process.send({
event: 'error',
cid: this.cid,
specs: this.specs,
capabilities: this.caps,
error: {
message: _context.t0.message,
stack: _context.t0.stack
}
});
_context.next = 63;
return this.end(1);
case 63:
process.removeAllListeners();
global.browser.removeAllListeners();
this.exportUidStore();
process.exit(1);
case 67:
case 'end':
return _context.stop();
}
}
}, _callee, this, [[30, 58]]);
}));
function run(_x) {
return _ref.apply(this, arguments);
}
return run;
}()
/**
* end test runner instance and exit process
*/
}, {
key: 'end',
value: function () {
var _ref2 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee2() {
var failures = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
var inWatchMode = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.inWatchMode;
var sendProcessEvent = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
return _regenerator2.default.wrap(function _callee2$(_context2) {
while (1) {
switch (_context2.prev = _context2.next) {
case 0:
if (!(this.hasSessionID && !inWatchMode)) {
_context2.next = 4;
break;
}
global.browser.options.isWDIO = false;
_context2.next = 4;
return this.endSession();
case 4:
if (sendProcessEvent) {
_context2.next = 6;
break;
}
return _context2.abrupt('return');
case 6:
process.send({
event: 'runner:end',
failures: failures,
cid: this.cid,
specs: this.specs
});
case 7:
case 'end':
return _context2.stop();
}
}
}, _callee2, this);
}));
function end() {
return _ref2.apply(this, arguments);
}
return end;
}()
/**
* run watcher
*/
}, {
key: 'runWatchMode',
value: function runWatchMode(cid, config, specs) {
var _this2 = this;
this.gaze = new _gaze2.default(specs, { interval: 1000 });
console.log(WATCH_NOTIFICATION);
this.gaze.on('changed', function () {
var _ref3 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee3(filepath) {
var failures;
return _regenerator2.default.wrap(function _callee3$(_context3) {
while (1) {
switch (_context3.prev = _context3.next) {
case 0:
if (!_this2.isRunning) {
_context3.next = 3;
break;
}
_this2.fileTriggeredWhileRunning = filepath;
return _context3.abrupt('return');
case 3:
/**
* check if file is in require.cache
* this is required to run specs multiple times
*/
if (require.cache[require.resolve(filepath)]) {
delete require.cache[require.resolve(filepath)];
}
console.log('change detected, running ...');
_this2.isRunning = true;
_context3.next = 8;
return _this2.framework.run(cid, config, [filepath], _this2.caps);
case 8:
failures = _context3.sent;
_context3.next = 11;
return _this2.end(failures, true);
case 11:
setTimeout(function () {
_this2.isRunning = false;
console.log(WATCH_NOTIFICATION);
/**
* retrigger onchange event if user has saved file while test
* was running
*/
if (_this2.fileTriggeredWhileRunning) {
_this2.gaze.emit('changed', _this2.fileTriggeredWhileRunning);
_this2.fileTriggeredWhileRunning = null;
}
}, 500);
case 12:
case 'end':
return _context3.stop();
}
}
}, _callee3, _this2);
}));
return function (_x5) {
return _ref3.apply(this, arguments);
};
}());
}
}, {
key: 'addTestDetails',
value: function addTestDetails(payload) {
if (this.currentTest) {
payload.title = this.currentTest.title;
payload.uid = this.currentTest.uid || this.currentTest.title;
payload.parent = this.currentTest.parent;
payload.parentUid = this.currentTest.parentUid || this.currentTest.parent;
}
return payload;
}
}, {
key: 'addCommandHooks',
value: function addCommandHooks(config) {
var _this3 = this;
config.beforeCommand.push(function (command, args) {
var payload = {
event: 'runner:beforecommand',
cid: _this3.cid,
specs: _this3.specs,
command,
args
};
process.send(_this3.addTestDetails(payload));
});
config.afterCommand.push(function (command, args, result, err) {
var payload = {
event: 'runner:aftercommand',
cid: _this3.cid,
specs: _this3.specs,
command,
args,
result,
err
};
process.send(_this3.addTestDetails(payload));
});
}
}, {
key: 'sigintHandler',
value: function sigintHandler() {
if (this.sigintWasCalled) {
return;
}
this.sigintWasCalled = true;
if (this.haltSIGINT) {
return;
}
this.exportUidStore();
this.end(1, false, !this.inWatchMode);
global.browser.removeAllListeners();
process.removeAllListeners();
if (this.gaze) {
this.gaze.close();
}
}
}, {
key: 'initialiseFramework',
value: function initialiseFramework(config) {
if (typeof config.framework !== 'string') {
throw new Error('You haven\'t defined a valid framework. ' + 'Please checkout http://webdriver.io/guide/testrunner/frameworks.html');
}
var frameworkLibrary = `wdio-${config.framework.toLowerCase()}-framework`;
try {
return require(frameworkLibrary).adapterFactory;
} catch (e) {
if (!e.message.match(`Cannot find module '${frameworkLibrary}'`)) {
throw new Error(`Couldn't initialise framework "${frameworkLibrary}".\n${e.stack}`);
}
throw new Error(`Couldn't load "${frameworkLibrary}" framework. You need to install ` + `it with \`$ npm install ${frameworkLibrary}\`!\n${e.stack}`);
}
}
}, {
key: 'initialiseInstance',
value: function initialiseInstance(isMultiremote, capabilities) {
var config = this.configParser.getConfig();
if (!isMultiremote) {
config.desiredCapabilities = capabilities;
return (0, _.remote)(config);
}
var options = {};
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = (0, _getIterator3.default)((0, _keys2.default)(capabilities)), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var browserName = _step.value;
options[browserName] = (0, _deepmerge2.default)(config, capabilities[browserName], MERGE_OPTIONS);
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
var browser = (0, _.multiremote)(options);
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = (0, _getIterator3.default)((0, _keys2.default)(capabilities)), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var _browserName = _step2.value;
global[_browserName] = browser.select(_browserName);
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
browser.isMultiremote = true;
return browser;
}
/**
* initialise WebdriverIO compliant plugins
*/
}, {
key: 'initialisePlugins',
value: function initialisePlugins(config) {
if (typeof config.plugins !== 'object') {
return;
}
var _iteratorNormalCompletion3 = true;
var _didIteratorError3 = false;
var _iteratorError3 = undefined;
try {
for (var _iterator3 = (0, _getIterator3.default)((0, _keys2.default)(config.plugins)), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
var pluginName = _step3.value;
var plugin = void 0;
try {
plugin = require(pluginName);
} catch (e) {
if (!e.message.match(`Cannot find module '${pluginName}'`)) {
throw new Error(`Couldn't initialise service "${pluginName}".\n${e.stack}`);
}
throw new Error(`Couldn't find plugin "${pluginName}". You need to install it ` + `with \`$ npm install ${pluginName}\`!\n${e.stack}`);
}
if (typeof plugin.init !== 'function') {
throw new Error(`The plugin "${pluginName}" is not WebdriverIO compliant!`);
}
plugin.init(global.browser, config.plugins[pluginName]);
}
} catch (err) {
_didIteratorError3 = true;
_iteratorError3 = err;
} finally {
try {
if (!_iteratorNormalCompletion3 && _iterator3.return) {
_iterator3.return();
}
} finally {
if (_didIteratorError3) {
throw _iteratorError3;
}
}
}
}
/**
* initialise WebdriverIO compliant services
*/
}, {
key: 'initialiseServices',
value: function initialiseServices(config) {
if (!Array.isArray(config.services)) {
return;
}
var _iteratorNormalCompletion4 = true;
var _didIteratorError4 = false;
var _iteratorError4 = undefined;
try {
for (var _iterator4 = (0, _getIterator3.default)(config.services), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
var serviceName = _step4.value;
var service = void 0;
/**
* allow custom services
*/
if (typeof serviceName === 'object') {
this.configParser.addService(serviceName);
continue;
}
try {
service = require(`wdio-${serviceName}-service`);
} catch (e) {
if (!e.message.match(`Cannot find module '${serviceName}'`)) {
throw new Error(`Couldn't initialise service "${serviceName}".\n${e.stack}`);
}
throw new Error(`Couldn't find service "${serviceName}". You need to install it ` + `with \`$ npm install wdio-${serviceName}-service\`!`);
}
this.configParser.addService(service);
}
} catch (err) {
_didIteratorError4 = true;
_iteratorError4 = err;
} finally {
try {
if (!_iteratorNormalCompletion4 && _iterator4.return) {
_iterator4.return();
}
} finally {
if (_didIteratorError4) {
throw _iteratorError4;
}
}
}
}
/**
* run before/after session hook
*/
}, {
key: 'runHook',
value: function runHook(hookName, config, caps, specs) {
var catchFn = function catchFn(e) {
return console.error(`Error in ${hookName}: ${e.stack}`);
};
return _promise2.default.all(config[hookName].map(function (hook) {
try {
return hook(config, caps, specs);
} catch (e) {
return catchFn(e);
}
})).catch(catchFn);
}
}]);
return Runner;
}();
var runner = new Runner();
process.on('message', function (m) {
if (m.command in runner) {
runner[m.command](m).catch(function (e) {
/**
* custom exit code to propagate initialisation error
*/
process.send({
event: 'runner:error',
error: {
message: e.message,
stack: e.stack
},
capabilities: runner.configParser.getCapabilities(runner.cid),
cid: runner.cid,
specs: runner.specs
});
process.exit(1);
});
}
});
/**
* catches ctrl+c event
*/
process.on('SIGINT', function () {
/**
* force killing process when 2nd SIGINT comes in
*/
if (runner.forceKillingProcess) {
return process.exit(1);
}
runner.forceKillingProcess = true;
runner.sigintHandler();
});
;