Files
enviPy-bayer/static/js/ketcher2/node_modules/webdriverio/build/lib/webdriverio.js
2025-06-23 20:13:54 +02:00

794 lines
26 KiB
JavaScript

'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _getIterator2 = require('babel-runtime/core-js/get-iterator');
var _getIterator3 = _interopRequireDefault(_getIterator2);
var _keys = require('babel-runtime/core-js/object/keys');
var _keys2 = _interopRequireDefault(_keys);
var _assign = require('babel-runtime/core-js/object/assign');
var _assign2 = _interopRequireDefault(_assign);
var _promise = require('babel-runtime/core-js/promise');
var _promise2 = _interopRequireDefault(_promise);
var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of');
var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
var _create = require('babel-runtime/core-js/object/create');
var _create2 = _interopRequireDefault(_create);
var _q = require('q');
var _q2 = _interopRequireDefault(_q);
var _fs = require('fs');
var _fs2 = _interopRequireDefault(_fs);
var _path = require('path');
var _path2 = _interopRequireDefault(_path);
var _deepmerge = require('deepmerge');
var _deepmerge2 = _interopRequireDefault(_deepmerge);
var _mkdirp = require('mkdirp');
var _mkdirp2 = _interopRequireDefault(_mkdirp);
var _events = require('events');
var _events2 = _interopRequireDefault(_events);
var _safeBuffer = require('safe-buffer');
var _RequestHandler = require('./utils/RequestHandler');
var _RequestHandler2 = _interopRequireDefault(_RequestHandler);
var _ErrorHandler = require('./utils/ErrorHandler');
var _Logger = require('./utils/Logger');
var _Logger2 = _interopRequireDefault(_Logger);
var _safeExecute = require('./helpers/safeExecute');
var _safeExecute2 = _interopRequireDefault(_safeExecute);
var _sanitize = require('./helpers/sanitize');
var _sanitize2 = _interopRequireDefault(_sanitize);
var _mobileDetector2 = require('./helpers/mobileDetector');
var _mobileDetector3 = _interopRequireDefault(_mobileDetector2);
var _detectSeleniumBackend = require('./helpers/detectSeleniumBackend');
var _detectSeleniumBackend2 = _interopRequireDefault(_detectSeleniumBackend);
var _errorHandler = require('./helpers/errorHandler');
var _errorHandler2 = _interopRequireDefault(_errorHandler);
var _hasElementResultHelper = require('./helpers/hasElementResultHelper');
var _hasElementResultHelper2 = _interopRequireDefault(_hasElementResultHelper);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var INTERNAL_EVENTS = ['init', 'command', 'error', 'result', 'end', 'screenshot'];
var PROMISE_FUNCTIONS = ['then', 'catch', 'finally'];
var EventEmitter = _events2.default.EventEmitter;
/**
* WebdriverIO v4
*/
var WebdriverIO = function WebdriverIO(args, modifier) {
var prototype = (0, _create2.default)(Object.prototype);
var rawCommands = {};
var eventHandler = new EventEmitter();
var fulFilledPromise = (0, _q2.default)();
var stacktrace = [];
var commandList = [];
var EVENTHANDLER_FUNCTIONS = (0, _getPrototypeOf2.default)(eventHandler);
/**
* merge default options with given user options
*/
var options = (0, _deepmerge2.default)({
protocol: 'http',
waitforTimeout: 1000,
waitforInterval: 500,
coloredLogs: true,
logLevel: 'silent',
baseUrl: null,
onError: [],
connectionRetryTimeout: 90000,
connectionRetryCount: 3
}, typeof args !== 'string' ? args : {});
/**
* define Selenium backend given on user options
*/
options = (0, _deepmerge2.default)((0, _detectSeleniumBackend2.default)(args), options);
/**
* only set globals we wouldn't get otherwise
*/
if (!process.env.WEBDRIVERIO_COLORED_LOGS) {
process.env.WEBDRIVERIO_COLORED_LOGS = options.coloredLogs;
}
var logger = new _Logger2.default(options, eventHandler);
var requestHandler = new _RequestHandler2.default(options, eventHandler, logger);
/**
* assign instance to existing session
*/
if (typeof args === 'string') {
requestHandler.sessionID = args;
}
/**
* sanitize error handler
*/
if (!Array.isArray(options.onError)) {
options.onError = [options.onError];
}
options.onError = options.onError.filter(function (fn) {
return typeof fn === 'function';
});
var desiredCapabilities = (0, _deepmerge2.default)({
javascriptEnabled: true,
locationContextEnabled: true,
handlesAlerts: true,
rotatable: true
}, options.desiredCapabilities || {});
var _mobileDetector = (0, _mobileDetector3.default)(desiredCapabilities),
isMobile = _mobileDetector.isMobile,
isIOS = _mobileDetector.isIOS,
isAndroid = _mobileDetector.isAndroid;
/**
* if no caps are specified fall back to firefox
*/
if (!desiredCapabilities.browserName && !isMobile) {
desiredCapabilities.browserName = 'firefox';
}
if (!isMobile && typeof desiredCapabilities.loggingPrefs === 'undefined') {
desiredCapabilities.loggingPrefs = {
browser: 'ALL',
driver: 'ALL'
};
}
var resolve = function resolve(result, onFulfilled, onRejected, context) {
if (typeof result === 'function') {
this.isExecuted = true;
result = result.call(this);
}
/**
* run error handler if command fails
*/
if (result instanceof Error) {
var _result = result;
this.defer.resolve(_promise2.default.all(_errorHandler2.default.map(function (fn) {
return fn.call(context, result);
})).then(function (res) {
var handlerResponses = res.filter(function (r) {
return typeof r !== 'undefined';
});
/**
* if no handler was triggered trough actual error
*/
if (handlerResponses.length === 0) {
return callErrorHandlerAndReject.call(context, _result, onRejected);
}
return onFulfilled.call(context, handlerResponses[0]);
}, function (e) {
return callErrorHandlerAndReject.call(context, e, onRejected);
}));
} else {
this.defer.resolve(result);
}
return this.promise;
};
/**
* middleware to call on error handler in wdio mode
*/
var callErrorHandlerAndReject = function callErrorHandlerAndReject(err, onRejected) {
var _this = this;
/**
* only call error handler if there is any and if error has bubbled up
*/
if (!this || this.depth !== 0 || options.onError.length === 0) {
return reject.call(this, err, onRejected);
}
return new _promise2.default(function (resolve, reject) {
return _promise2.default.all(options.onError.map(function (fn) {
if (!global.wdioSync) {
return fn.call(_this, err);
}
return new _promise2.default(function (resolve) {
return global.wdioSync(fn, resolve).call(_this, err);
});
})).then(resolve, reject);
}).then(function () {
return reject.call(_this, err, onRejected);
});
};
/**
* By using finally in our next method we omit the duty to throw an exception at some
* point. To avoid propagating rejected promises until everything crashes silently we
* check if the last and current promise got rejected. If so we can throw the error.
*/
var reject = function reject(err, onRejected) {
if (!options.isWDIO && !options.screenshotOnReject && typeof onRejected === 'function') {
delete err.screenshot;
return onRejected(err);
}
var onRejectedSafe = function onRejectedSafe(err) {
if (typeof onRejected === 'function') {
onRejected(err);
}
};
if (!this && !options.screenshotOnReject) {
onRejectedSafe(err);
throw err;
}
if (this && this.depth !== 0) {
onRejectedSafe(err);
return this.promise;
}
var shouldTakeScreenshot = options.screenshotOnReject || typeof options.screenshotPath === 'string';
if (!shouldTakeScreenshot || err.shotTaken || insideCommand('screenshot', this)) {
return fail(err, onRejected);
}
err.shotTaken = true;
return takeScreenshot(err).catch(function (e) {
return logger.log('\tFailed to take screenshot on reject:', e);
}).then(function () {
return fail(err, onRejected);
});
};
function insideCommand(command, unit) {
var commands = unit && unit.commandList;
return commands && commands[commands.length - 1].name === command;
}
function takeScreenshot(err) {
var client = unit();
var failDate = new Date();
if (typeof options.screenshotOnReject === 'object') {
client.requestHandler = createRequestHandlerDecorator(options.screenshotOnReject);
}
// don't take a new screenshot if we already got one from Selenium
var getScreenshot = typeof err.screenshot === 'string' ? function () {
return err.screenshot;
} : function () {
return rawCommands.screenshot.call(client).then(function (res) {
return res.value;
});
};
return _q2.default.fcall(getScreenshot).then(function (screenshot) {
if (options.screenshotOnReject) {
err.screenshot = screenshot;
}
if (typeof options.screenshotPath === 'string') {
var filename = saveScreenshotSync(screenshot, failDate);
client.emit('screenshot', { data: screenshot, filename: filename });
}
});
}
function createRequestHandlerDecorator(opts) {
return (0, _create2.default)(requestHandler, {
defaultOptions: {
value: (0, _assign2.default)({}, requestHandler.defaultOptions, opts)
}
});
}
function saveScreenshotSync(screenshot, date) {
var screenshotPath = _path2.default.isAbsolute(options.screenshotPath) ? options.screenshotPath : _path2.default.join(process.cwd(), options.screenshotPath);
/**
* create directory if not existing
*/
try {
_fs2.default.statSync(screenshotPath);
} catch (e) {
_mkdirp2.default.sync(screenshotPath);
}
var capId = _sanitize2.default.caps(desiredCapabilities);
var timestamp = date.toJSON().replace(/:/g, '-');
var filename = 'ERROR_' + capId + '_' + timestamp + '.png';
var filePath = _path2.default.join(screenshotPath, filename);
_fs2.default.writeFileSync(filePath, new _safeBuffer.Buffer(screenshot, 'base64'));
logger.log('\tSaved screenshot: ' + filename);
return filename;
}
function fail(e, onRejected) {
if (!e.stack) {
e = new Error(e);
}
var message = e.seleniumStack && e.seleniumStack.message || e.message;
var stack = stacktrace.slice().map(function (trace) {
return ' at ' + trace;
});
e.stack = e.name + ': ' + message + '\n' + stack.reverse().join('\n');
/**
* the waitUntil command can execute a lot of functions until it resolves
* to keep the stacktrace sane we just shrink down the stacktrack and
* only keep waitUntil in it
*/
if (e.name === 'WaitUntilTimeoutError') {
stack = e.stack.split('\n');
stack.splice(1, stack.length - 2);
e.stack = stack.join('\n');
}
/**
* ToDo useful feature for standalone mode:
* option that if true causes script to throw exception if command fails:
*
* process.nextTick(() => {
* throw e
* })
*/
if (typeof onRejected !== 'function') {
throw e;
}
return onRejected(e);
}
/**
* WebdriverIO Monad
*/
function unit(lastPromise) {
var client = (0, _create2.default)(prototype);
var defer = _q2.default.defer();
var promise = defer.promise;
client.defer = defer;
client.promise = promise;
client.lastPromise = lastPromise || fulFilledPromise;
client.desiredCapabilities = desiredCapabilities;
client.requestHandler = requestHandler;
client.logger = logger;
client.options = options;
client.commandList = commandList;
client.isMobile = isMobile;
client.isIOS = isIOS;
client.isAndroid = isAndroid;
/**
* actual bind function
*/
client.next = function (func, args, name) {
var _this2 = this;
/**
* use finally to propagate rejected promises up the chain
*/
return this.lastPromise.then(function (val) {
/**
* store command into command list so `getHistory` can return it
*/
var timestamp = Date.now();
commandList.push({ name: name, args: args, timestamp: timestamp });
/**
* allow user to leave out selector argument if they have already queried an element before
*/
var lastResult = val || _this2.lastResult;
if ((0, _hasElementResultHelper2.default)(lastResult) && args.length < func.length && func.toString().indexOf('function ' + name + '(selector') === 0) {
var isWaitFor = name.match(/^waitFor/);
var isWaitForWithSelector = isWaitFor && typeof args[0] === 'string';
/**
* Throw error if someone tries to call an action on a chained element call,
* otherwise the context would be ignored and a different element could
* selected.
* Ignore this behavior for all waitFor calls as we want to refetch elements
* in case they haven't been found. Also not throw an error for isExisting
* and isVisible since they use that information to return their result.
*/
if (!isWaitFor && !name.match(/^is(Existing|Visible)/) && lastResult.state === 'failure') {
var message = lastResult.message;
/**
* add selector parameter if no existing
*/
if (message.match(/using the given search parameters\.$/)) {
message = message.slice(0, -1) + ' ("' + lastResult.selector + '").';
}
var error = new Error(message);
return resolve.call(_this2, error, null, null, { depth: 0 });
}
/**
* propagate last element result if last result has a selector and we've
* called a waitFor command without selector
*
* e.g. when calling `waitForExist` via $('#notExisting').waitForExist()
*/
if (lastResult.selector && isWaitFor && !isWaitForWithSelector) {
_this2.lastResult = null;
args.unshift(lastResult.selector);
/**
* All non waitFor commands get null as selector which will be replaced by
* the actual web element id in the element(s) protocol commands
*
* e.g. when calling `getText` via $('#existing').getText()
*/
} else if (!isWaitForWithSelector) {
args.unshift(null);
}
}
return resolve.call(_this2, (0, _safeExecute2.default)(func, args));
}, function (e) {
/**
* this will get reached only in standalone mode if the command
* fails and doesn't get followed by a then or catch method
*/
return resolve.call(_this2, e, null, null, { depth: 0 });
});
};
client.finally = function (fn) {
var _this3 = this;
var client = unit(this.promise.finally(function () {
return resolve.call(client, (0, _safeExecute2.default)(fn, []).bind(_this3));
}));
return client;
};
client.call = function (fn) {
var _this4 = this;
var client = unit(this.promise.done(function () {
return resolve.call(client, (0, _safeExecute2.default)(fn, []).bind(_this4));
}));
return client;
};
client.then = function (onFulfilled, onRejected) {
var _this5 = this;
if (typeof onFulfilled !== 'function' && typeof onRejected !== 'function') {
return this;
}
/**
* execute then function in context of the new instance
* but resolve result with this
*/
var client = unit(this.promise.then(function () {
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
/**
* store result in commandList
*/
if (commandList.length) {
commandList[commandList.length - 1].result = args[0];
}
/**
* resolve command
*/
return resolve.call(client, (0, _safeExecute2.default)(onFulfilled, args).bind(_this5));
}, function (e) {
var result = (0, _safeExecute2.default)(onRejected, [e]).bind(_this5);
/**
* handle error once command was bubbled up the command chain
*/
if (_this5.depth === 0) {
result = e;
}
return resolve.call(client, result, onFulfilled, onRejected, _this5);
}));
return client;
};
client.catch = function (onRejected) {
return this.then(undefined, onRejected);
};
client.inspect = function () {
return this.promise.inspect();
};
/**
* internal helper method to handle command results
*
* @param {Promise[]} promises list of promises
* @param {Boolean} option if true extract value property from selenium result
*/
client.unify = function (promises) {
var option = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
promises = Array.isArray(promises) ? promises : [promises];
return _promise2.default.all(promises)
/**
* extract value property from result if desired
*/
.then(function (result) {
if (!option.extractValue || !Array.isArray(result)) {
return result;
}
return result.map(function (res) {
return res.value;
});
/**
* sanitize result for better assertion
*/
}).then(function (result) {
if (Array.isArray(result) && result.length === 1) {
result = result[0];
}
if (option.lowercase && typeof result === 'string') {
result = result.toLowerCase();
}
return result;
});
};
/**
* addCommand handler for async or standalone mode
* @param {String} fnName function name
* @param {Function} fn function
* @param {Boolean} forceOverwrite if true it can overwrite existing commands
*/
client.addCommand = function (fnName, fn, forceOverwrite) {
if (typeof fn === 'string') {
var namespace = arguments[0];
fnName = arguments[1];
fn = arguments[2];
forceOverwrite = arguments[3];
switch (typeof client[namespace]) {
case 'function':
throw new _ErrorHandler.RuntimeError('Command namespace "' + namespace + '" is used internally, and can\'t be overwritten!');
case 'object':
if (client[namespace][fnName] && !forceOverwrite) {
throw new _ErrorHandler.RuntimeError('Command "' + fnName + '" is already defined!');
}
break;
}
return unit.lift(namespace, fnName, fn);
}
if (client[fnName] && !forceOverwrite) {
throw new _ErrorHandler.RuntimeError('Command "' + fnName + '" is already defined!');
}
return unit.lift(fnName, fn);
};
client.getPrototype = function () {
return prototype;
};
client.transferPromiseness = function (target, promise) {
/**
* transfer WebdriverIO commands
*/
var clientFunctions = (0, _keys2.default)(prototype);
var functionsToTranfer = clientFunctions.concat(PROMISE_FUNCTIONS);
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = (0, _getIterator3.default)(functionsToTranfer), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var fnName = _step.value;
if (typeof promise[fnName] === 'function') {
target[fnName] = promise[fnName].bind(promise);
}
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
};
if (typeof modifier === 'function') {
client = modifier(client, options);
}
return client;
}
/**
* enhance base monad prototype with methods
*/
unit.lift = function (name, func) {
var commandGroup = prototype;
if (typeof func === 'string') {
var namespace = arguments[0];
name = arguments[1];
func = arguments[2];
if (!prototype[namespace]) {
prototype[namespace] = {};
}
commandGroup = prototype[namespace];
}
rawCommands[name] = func;
commandGroup[name] = function () {
var nextPromise = this.promise;
/**
* commands executed inside commands don't have to wait
* on any promise
*/
if (this.isExecuted) {
nextPromise = this.lastPromise;
}
var client = unit(nextPromise);
/**
* catch stack to find information about where the command that causes
* the error was used (stack line 2) and only save it when it was not
* within WebdriverIO context
*/
var stack = new Error().stack;
var lineInTest = stack.split('\n').slice(2, 3).join('\n');
var fileAndPosition = lineInTest.slice(lineInTest.indexOf('(') + 1, lineInTest.indexOf(')'));
var atCommand = lineInTest.trim().slice(3).split(' ')[0];
atCommand = atCommand.slice(atCommand.lastIndexOf('.') + 1);
for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
args[_key2] = arguments[_key2];
}
var trace = name + '(' + _sanitize2.default.args(args) + ') - ' + fileAndPosition.slice(fileAndPosition.lastIndexOf('/') + 1);
if ((0, _keys2.default)(prototype).indexOf(atCommand) === -1 && atCommand !== 'exports') {
stacktrace = [trace];
} else {
/**
* save trace for nested commands
*/
stacktrace.push(trace);
}
/**
* determine execution depth:
* This little tweak helps us to determine whether the command was executed
* by the test script or by another command. With that we can make sure
* that errors are getting thrown once they bubbled up the command chain.
*/
client.depth = stack.split('\n').filter(function (line) {
return !!line.match(/\/lib\/(commands|protocol)\/(\w+)\.js/);
}).length;
/**
* queue command
*/
client.name = name;
client.lastResult = this.lastResult;
client.next(func, args, name);
return client;
};
return unit;
};
/**
* register event emitter
*/
var _loop = function _loop(eventCommand) {
prototype[eventCommand] = function () {
for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
args[_key3] = arguments[_key3];
}
/**
* custom commands needs to get emitted and registered in order
* to prevent race conditions
*/
if (INTERNAL_EVENTS.indexOf(args[0]) === -1) {
return this.finally(function () {
return eventHandler[eventCommand].apply(eventHandler, args);
});
}
eventHandler[eventCommand].apply(eventHandler, args);
return this;
};
};
for (var eventCommand in EVENTHANDLER_FUNCTIONS) {
_loop(eventCommand);
}
return unit;
};
exports.default = WebdriverIO;
module.exports = exports['default'];