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,225 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
/**
* logger colors
*/
var COLORS = {
black: '\x1b[0;30m',
dkgray: '\x1b[1;30m',
brick: '\x1b[0;31m',
red: '\x1b[1;31m',
dkred: '\x1b[31m',
green: '\x1b[0;32m',
lime: '\x1b[1;32m',
brown: '\x1b[0;33m',
yellow: '\x1b[1;33m',
navy: '\x1b[0;34m',
blue: '\x1b[1;34m',
violet: '\x1b[0;35m',
magenta: '\x1b[1;35m',
teal: '\x1b[0;36m',
cyan: '\x1b[1;36m',
ltgray: '\x1b[0;37m',
white: '\x1b[1;37m',
reset: '\x1b[0m'
};
/**
* Webdriver Errors
*/
var Unknown = { id: 'Unknown', status: -1, message: 'Remote end send an unknown status code.' };
var Success = { id: 'Success', status: 0, message: 'The command executed successfully.' };
var NoSuchDriver = { id: 'NoSuchDriver', status: 6, message: 'A session is either terminated or not started' };
var NoSuchElement = { id: 'NoSuchElement', status: 404, message: 'An element could not be located on the page using the given search parameters.' };
var NoSuchFrame = { id: 'NoSuchFrame', status: 400, message: 'A request to switch to a frame could not be satisfied because the frame could not be found.' };
var UnknownCommand = { id: 'UnknownCommand', status: 404, message: 'The requested resource could not be found, or a request was received using an HTTP method that is not supported by the mapped resource.' };
var StaleElementReference = { id: 'StaleElementReference', status: 400, message: 'An element command failed because the referenced element is no longer attached to the DOM.' };
var ElementNotVisible = { id: 'ElementNotVisible', status: 11, message: 'An element command could not be completed because the element is not visible on the page.' };
var InvalidElementState = { id: 'InvalidElementState', status: 400, message: 'An element command could not be completed because the element is in an invalid state (e.g. attempting to click a disabled element).' };
var UnknownError = { id: 'UnknownError', status: 500, message: 'An unknown server-side error occurred while processing the command.' };
var ElementIsNotSelectable = { id: 'ElementIsNotSelectable', status: 400, message: 'An attempt was made to select an element that cannot be selected.' };
var JavaScriptError = { id: 'JavaScriptError', status: 500, message: 'An error occurred while executing user supplied JavaScript.' };
var XPathLookupError = { id: 'XPathLookupError', status: 19, message: 'An error occurred while searching for an element by XPath.' };
var Timeout = { id: 'Timeout', status: 408, message: 'An operation did not complete before its timeout expired.' };
var NoSuchWindow = { id: 'NoSuchWindow', status: 400, message: 'A request to switch to a different window could not be satisfied because the window could not be found.' };
var InvalidCookieDomain = { id: 'InvalidCookieDomain', status: 400, message: 'An illegal attempt was made to set a cookie under a different domain than the current page.' };
var UnableToSetCookie = { id: 'UnableToSetCookie', status: 500, message: 'A request to set a cookie\'s value could not be satisfied.' };
var UnexpectedAlertOpen = { id: 'UnexpectedAlertOpen', status: 500, message: 'A modal dialog was open, blocking this operation' };
var NoAlertOpenError = { id: 'NoAlertOpenError', status: 400, message: 'An attempt was made to operate on a modal dialog when one was not open.' };
var ScriptTimeout = { id: 'ScriptTimeout', status: 408, message: 'A script did not complete before its timeout expired.' };
var InvalidElementCoordinates = { id: 'InvalidElementCoordinates', status: 400, message: 'The coordinates provided to an interactions operation are invalid.' };
var IMENotAvailable = { id: 'IMENotAvailable', status: 30, message: 'IME was not available.' };
var IMEEngineActivationFailed = { id: 'IMEEngineActivationFailed', status: 31, message: 'An IME engine could not be started.' };
var InvalidSelector = { id: 'InvalidSelector', status: 400, message: 'Argument was an invalid selector (e.g. XPath/CSS).' };
var SessionNotCreatedException = { id: 'SessionNotCreatedException', status: 500, message: 'A new session could not be created.' };
var ElementNotScrollable = { id: 'ElementNotScrollable', status: 34, message: 'Element cannot be scrolled into view.' };
var SelectorTimeoutError = { id: 'SelectorTimeoutError', status: 100, message: 'Request timed out after the element was still found on the page.' };
var NoSessionIdError = { id: 'NoSessionIdError', status: 101, message: 'A session id is required for this command but wasn\'t found in the response payload' };
var GridApiError = { id: 'GridApiError', status: 102, message: 'A call to the Selenium Grid API failed' };
var ElementClickIntercepted = { id: 'ElementClickIntercepted', status: 400, message: 'The Element Click command could not be completed because the element receiving the events is obscuring the element that was requested clicked.' };
var ElementNotInteractable = { id: 'ElementNotInteractable', status: 400, message: 'A command could not be completed because the element is not pointer- or keyboard interactable.' };
var InsecureCertificate = { id: 'InsecureCertificate', status: 400, message: 'Navigation caused the user agent to hit a certificate warning, which is usually the result of an expired or invalid TLS certificate.' };
var InvalidArgument = { id: 'InvalidArgument', status: 400, message: 'The arguments passed to a command are either invalid or malformed.' };
var InvalidSessionId = { id: 'InvalidSessionId', status: 404, message: 'Occurs if the given session id is not in the list of active sessions, meaning the session either does not exist or that its not active.' };
var MoveTargetOutOfBounds = { id: 'MoveTargetOutOfBounds', status: 500, message: 'The target for mouse interaction is not in the browsers viewport and cannot be brought into that viewport.' };
var NoSuchCookie = { id: 'NoSuchCookie', status: 404, message: 'No cookie matching the given path name was found amongst the associated cookies of the current browsing contexts active document.' };
var UnableToCaptureScreen = { id: 'UnableToCaptureScreen', status: 500, message: 'A screen capture was made impossible.' };
var UnknownMethod = { id: 'UnknownMethod', status: 405, message: 'The requested command matched a known URL but did not match an method for that URL.' };
var UnsupportedOperation = { id: 'UnsupportedOperation', status: 500, message: 'Indicates that a command that should have executed properly cannot be supported for some reason.' };
/**
* selenium error codes
* https://w3c.github.io/webdriver/webdriver-spec.html#dfn-error-code
*/
var ERROR_CODES = {
'-1': Unknown,
'0': Success,
'6': NoSuchDriver,
'7': NoSuchElement,
'8': NoSuchFrame,
'9': UnknownCommand,
'10': StaleElementReference,
'11': ElementNotVisible,
'12': InvalidElementState,
'13': UnknownError,
'15': ElementIsNotSelectable,
'17': JavaScriptError,
'19': XPathLookupError,
'21': Timeout,
'23': NoSuchWindow,
'24': InvalidCookieDomain,
'25': UnableToSetCookie,
'26': UnexpectedAlertOpen,
'27': NoAlertOpenError,
'28': ScriptTimeout,
'29': InvalidElementCoordinates,
'30': IMENotAvailable,
'31': IMEEngineActivationFailed,
'32': InvalidSelector,
'33': SessionNotCreatedException,
'34': ElementNotScrollable,
// WebdriverIO specific error codes
'100': SelectorTimeoutError,
'101': NoSessionIdError,
'102': GridApiError,
// W3C Webdriver errors
'element click intercepted': ElementClickIntercepted,
'element not selectable': ElementIsNotSelectable,
'element not interactable': ElementNotInteractable,
'insecure certificate': InsecureCertificate,
'invalid argument': InvalidArgument,
'invalid cookie domain': InvalidCookieDomain,
'invalid coordinates': InvalidElementCoordinates,
'invalid element state': InvalidElementState,
'invalid selector': InvalidSelector,
'invalid session id': InvalidSessionId,
'javascript error': JavaScriptError,
'move target out of bounds': MoveTargetOutOfBounds,
'no such alert': NoAlertOpenError,
'no such cookie': NoSuchCookie,
'no such element': NoSuchElement,
'no such frame': NoSuchFrame,
'no such window': NoSuchWindow,
'script timeout': ScriptTimeout,
'session not created': SessionNotCreatedException,
'stale element reference': StaleElementReference,
'timeout': Timeout,
'unable to set cookie': UnableToSetCookie,
'unable to capture screen': UnableToCaptureScreen,
'unexpected alert open': UnexpectedAlertOpen,
'unknown command': UnknownCommand,
'unknown error': UnknownError,
'unknown method': UnknownMethod,
'unsupported operation': UnsupportedOperation
};
/**
* unicode characters
* https://w3c.github.io/webdriver/webdriver-spec.html#character-types
*/
var UNICODE_CHARACTERS = {
'NULL': '\uE000',
'Unidentified': '\uE000',
'Cancel': '\uE001',
'Help': '\uE002',
'Back space': '\uE003',
'Backspace': '\uE003',
'Tab': '\uE004',
'Clear': '\uE005',
'Return': '\uE006',
'Enter': '\uE007',
'Shift': '\uE008',
'Control': '\uE009',
'Alt': '\uE00A',
'Pause': '\uE00B',
'Escape': '\uE00C',
'Space': '\uE00D',
' ': '\uE00D',
'Pageup': '\uE00E',
'PageUp': '\uE00E',
'Page_Up': '\uE00E',
'Pagedown': '\uE00F',
'PageDown': '\uE00F',
'Page_Down': '\uE00F',
'End': '\uE010',
'Home': '\uE011',
'Left arrow': '\uE012',
'Arrow_Left': '\uE012',
'ArrowLeft': '\uE012',
'Up arrow': '\uE013',
'Arrow_Up': '\uE013',
'ArrowUp': '\uE013',
'Right arrow': '\uE014',
'Arrow_Right': '\uE014',
'ArrowRight': '\uE014',
'Down arrow': '\uE015',
'Arrow_Down': '\uE015',
'ArrowDown': '\uE015',
'Insert': '\uE016',
'Delete': '\uE017',
'Semicolon': '\uE018',
'Equals': '\uE019',
'Numpad 0': '\uE01A',
'Numpad 1': '\uE01B',
'Numpad 2': '\uE01C',
'Numpad 3': '\uE01D',
'Numpad 4': '\uE01E',
'Numpad 5': '\uE01F',
'Numpad 6': '\uE020',
'Numpad 7': '\uE021',
'Numpad 8': '\uE022',
'Numpad 9': '\uE023',
'Multiply': '\uE024',
'Add': '\uE025',
'Separator': '\uE026',
'Subtract': '\uE027',
'Decimal': '\uE028',
'Divide': '\uE029',
'F1': '\uE031',
'F2': '\uE032',
'F3': '\uE033',
'F4': '\uE034',
'F5': '\uE035',
'F6': '\uE036',
'F7': '\uE037',
'F8': '\uE038',
'F9': '\uE039',
'F10': '\uE03A',
'F11': '\uE03B',
'F12': '\uE03C',
'Command': '\uE03D',
'Meta': '\uE03D',
'Zenkaku_Hankaku': '\uE040',
'ZenkakuHankaku': '\uE040'
};
exports.COLORS = COLORS;
exports.ERROR_CODES = ERROR_CODES;
exports.UNICODE_CHARACTERS = UNICODE_CHARACTERS;

View File

@ -0,0 +1,10 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = depcrecate;
function depcrecate(commandName) {
console.warn('WARNING: the "' + commandName + '" command will be depcrecated soon. Please use a ' + 'different command in order to avoid failures in your test after updating WebdriverIO.');
}
module.exports = exports['default'];

View File

@ -0,0 +1,67 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var DEFAULT_HOST = '127.0.0.1';
var DEFAULT_PORT = 4444;
/**
* helper to detect the Selenium backend according to given capabilities
*/
var detectSeleniumBackend = function detectSeleniumBackend(capabilities) {
/**
* don't detect anything if host or port is given
*/
if (capabilities.host || capabilities.port) {
return {
host: capabilities.host || DEFAULT_HOST,
port: capabilities.port || DEFAULT_PORT
};
}
/**
* local Selenium server
*/
if (!capabilities.user || !capabilities.key) {
return {
host: DEFAULT_HOST,
port: DEFAULT_PORT
};
}
/**
* browserstack
* e.g. zHcv9sZ39ip8ZPsxBVJ2
*/
if (capabilities.key.length === 20) {
return {
host: 'hub.browserstack.com',
port: 80
};
}
/**
* testingbot
* e.g. ec337d7b677720a4dde7bd72be0bfc67
*/
if (capabilities.key.length === 32) {
return {
host: 'hub.testingbot.com',
port: 80
};
}
/**
* Sauce Labs
* e.g. 50aa152c-1932-B2f0-9707-18z46q2n1mb0
*/
return {
protocol: 'https',
host: 'ondemand.saucelabs.com',
port: 443
};
};
exports.default = detectSeleniumBackend;
module.exports = exports['default'];

View File

@ -0,0 +1,41 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _fs = require('fs');
var _fs2 = _interopRequireDefault(_fs);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var WGXPATH_PATH = require.resolve('wgxpath');
var wgxpathSrc = void 0;
/**
* Ensures document.evaluate() in the browser.
*/
var ensureClientSideSelectorSupport = function ensureClientSideSelectorSupport() {
var _this = this;
return this.execute('return !!document.evaluate;').then(function (res) {
if (res.value) {
return true;
}
/**
* Don't read in unless necessary
*/
if (!wgxpathSrc) {
wgxpathSrc = _fs2.default.readFileSync(WGXPATH_PATH);
wgxpathSrc = wgxpathSrc.toString().split('module.exports')[0];
}
return _this.execute(wgxpathSrc + '\nwgxpath.install(window);');
});
};
exports.default = ensureClientSideSelectorSupport;
module.exports = exports['default'];

View File

@ -0,0 +1,114 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _getIterator2 = require('babel-runtime/core-js/get-iterator');
var _getIterator3 = _interopRequireDefault(_getIterator2);
var _q = require('q');
var _q2 = _interopRequireDefault(_q);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
exports.default = [
/**
* stale reference error handler
*/
function (e) {
if (!e.seleniumStack || e.seleniumStack.type !== 'StaleElementReference') {
return;
}
/**
* get through command list and find most recent command where an element(s)
* command contained the failing json web element
*/
var failingCommand = this.commandList.slice(-1)[0];
var commandToRepeat = void 0;
for (var i = this.commandList.length - 1; i >= 0; --i) {
var command = this.commandList[i];
if (!command.result || !command.result.value) {
continue;
}
if (command.name !== 'element' && command.name !== 'elements') {
continue;
}
if (command.name === 'element' && command.result.value.ELEMENT !== failingCommand.args[0]) {
continue;
}
// Ensure an array when evaluating the result, so the logic is the same for 'element' and 'elements' commands
var results = Array.isArray(command.result.value) ? command.result.value : command.result.value !== null ? [command.result.value] : [];
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = (0, _getIterator3.default)(results), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var result = _step.value;
if (result.ELEMENT === failingCommand.args[0]) {
commandToRepeat = this.commandList[i - 1];
/**
* when using elements as first citizen , e.g. div.getTagName() and rerun
* the command due to StaleElementReference exception we store the unshifted
* `null` as selector. In order to have a valid selector when rerunning it we
* have to put in the actual selector
*/
var preSelector = this.commandList[i].result.selector;
if (commandToRepeat.args[0] === null && typeof preSelector === 'string') {
commandToRepeat.args[0] = preSelector;
/**
* clear lastResult as we inject the actual selector to parameter list
*/
if (this.lastResult) {
delete this.lastResult.value;
}
}
break;
}
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
if (commandToRepeat) {
break;
}
}
if (!commandToRepeat) {
return;
}
/**
* reset lastPromise so we can resolve it after rerun
*/
this.lastPromise = (0, _q2.default)();
return this[commandToRepeat.name].apply(this, commandToRepeat.args);
}];
module.exports = exports['default'];

View File

@ -0,0 +1,151 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _ErrorHandler = require('../utils/ErrorHandler');
var DEFAULT_SELECTOR = 'css selector';
var DIRECT_SELECTOR_REGEXP = /^(id|css selector|xpath|link text|partial link text|name|tag name|class name|-android uiautomator|-ios uiautomation|accessibility id):(.+)/;
var findStrategy = function findStrategy() {
var value = arguments.length <= 0 ? undefined : arguments[0];
var relative = arguments.length > 1 ? arguments.length <= 1 ? undefined : arguments[1] : false;
var xpathPrefix = relative ? './/' : '//';
/**
* set default selector
*/
var using = DEFAULT_SELECTOR;
if (typeof value !== 'string') {
throw new _ErrorHandler.ProtocolError('selector needs to be typeof `string`');
}
if (arguments.length === 3) {
return {
using: arguments.length <= 0 ? undefined : arguments[0],
value: arguments.length <= 1 ? undefined : arguments[1]
};
}
/**
* check if user has specified locator strategy directly
*/
var match = value.match(DIRECT_SELECTOR_REGEXP);
if (match) {
return {
using: match[1],
value: match[2]
};
}
// check value type
// use id strategy if value starts with # and doesnt contain any other CSS selector-relevant character
// regex to match ids from http://stackoverflow.com/questions/18938390/regex-to-match-ids-in-a-css-file
if (value.search(/^#-?[_a-zA-Z]+[_a-zA-Z0-9-]*$/) > -1) {
using = 'id';
value = value.slice(1);
// use xPath strategy if value starts with //
} else if (value.indexOf('/') === 0 || value.indexOf('(') === 0 || value.indexOf('../') === 0 || value.indexOf('./') === 0 || value.indexOf('*/') === 0) {
using = 'xpath';
// use link text startegy if value startes with =
} else if (value.indexOf('=') === 0) {
using = 'link text';
value = value.slice(1);
// use partial link text startegy if value startes with *=
} else if (value.indexOf('*=') === 0) {
using = 'partial link text';
value = value.slice(2);
// recursive element search using the UiAutomator library (Android only)
} else if (value.indexOf('android=') === 0) {
using = '-android uiautomator';
value = value.slice(8);
// recursive element search using the UIAutomation library (iOS-only)
} else if (value.indexOf('ios=') === 0) {
using = '-ios uiautomation';
value = value.slice(4);
// recursive element search using accessibility id
} else if (value.indexOf('~') === 0) {
using = 'accessibility id';
value = value.slice(1);
// class name mobile selector
// for iOS = UIA...
// for Android = android.widget
} else if (value.slice(0, 3) === 'UIA' || value.slice(0, 15) === 'XCUIElementType' || value.slice(0, 14).toLowerCase() === 'android.widget') {
using = 'class name';
// use tag name strategy if value contains a tag
// e.g. "<div>" or "<div />"
} else if (value.search(/<[a-zA-Z-]+( \/)*>/g) >= 0) {
using = 'tag name';
value = value.replace(/<|>|\/|\s/g, '');
// use name strategy if value queries elements with name attributes
// e.g. "[name='myName']" or '[name="myName"]'
} else if (value.search(/^\[name=("|')([a-zA-z0-9\-_. ]+)("|')]$/) >= 0) {
using = 'name';
value = value.match(/^\[name=("|')([a-zA-z0-9\-_. ]+)("|')]$/)[2];
// any element with given text e.g. h1=Welcome
} else if (value.search(/^[a-z0-9]*=(.)+$/) >= 0) {
var query = value.split(/=/);
var tag = query.shift();
using = 'xpath';
value = '' + xpathPrefix + (tag.length ? tag : '*') + '[normalize-space() = "' + query.join('=') + '"]';
// any element containing given text
} else if (value.search(/^[a-z0-9]*\*=(.)+$/) >= 0) {
var _query = value.split(/\*=/);
var _tag = _query.shift();
using = 'xpath';
value = '' + xpathPrefix + (_tag.length ? _tag : '*') + '[contains(., "' + _query.join('*=') + '")]';
// any element with certian class or id + given content
} else if (value.search(/^[a-z0-9]*(\.|#)-?[_a-zA-Z]+[_a-zA-Z0-9-]*=(.)+$/) >= 0) {
var _query2 = value.split(/=/);
var _tag2 = _query2.shift();
var classOrId = _tag2.substr(_tag2.search(/(\.|#)/), 1) === '#' ? 'id' : 'class';
var classOrIdName = _tag2.slice(_tag2.search(/(\.|#)/) + 1);
_tag2 = _tag2.substr(0, _tag2.search(/(\.|#)/));
using = 'xpath';
value = '' + xpathPrefix + (_tag2.length ? _tag2 : '*') + '[contains(@' + classOrId + ', "' + classOrIdName + '") and normalize-space() = "' + _query2.join('=') + '"]';
// any element with certian class or id + has certain content
} else if (value.search(/^[a-z0-9]*(\.|#)-?[_a-zA-Z]+[_a-zA-Z0-9-]*\*=(.)+$/) >= 0) {
var _query3 = value.split(/\*=/);
var _tag3 = _query3.shift();
var _classOrId = _tag3.substr(_tag3.search(/(\.|#)/), 1) === '#' ? 'id' : 'class';
var _classOrIdName = _tag3.slice(_tag3.search(/(\.|#)/) + 1);
_tag3 = _tag3.substr(0, _tag3.search(/(\.|#)/));
using = 'xpath';
value = xpathPrefix + (_tag3.length ? _tag3 : '*') + '[contains(@' + _classOrId + ', "' + _classOrIdName + '") and contains(., "' + _query3.join('*=') + '")]';
value = '' + xpathPrefix + (_tag3.length ? _tag3 : '*') + '[contains(@' + _classOrId + ', "' + _classOrIdName + '") and contains(., "' + _query3.join('*=') + '")]';
// allow to move up to the parent or select current element
} else if (value === '..' || value === '.') {
using = 'xpath';
}
return {
using: using,
value: value
};
};
exports.default = findStrategy;
module.exports = exports['default'];

View File

@ -0,0 +1,95 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _getIterator2 = require('babel-runtime/core-js/get-iterator');
var _getIterator3 = _interopRequireDefault(_getIterator2);
var _path = require('path');
var _path2 = _interopRequireDefault(_path);
var _fs = require('fs');
var _fs2 = _interopRequireDefault(_fs);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var COMMAND_TYPES = ['protocol', 'commands'];
/**
* helper to find all implemented commands
*
* @return {String[]} list of implemented command names
*/
var getImplementedCommands = function getImplementedCommands() {
var commands = {};
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = (0, _getIterator3.default)(COMMAND_TYPES), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var commandType = _step.value;
var dir = _path2.default.join(__dirname, '..', commandType);
var files = _fs2.default.readdirSync(dir);
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = (0, _getIterator3.default)(files), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var filename = _step2.value;
var commandName = filename.slice(0, -3);
/**
* addCommand only there for documentation purposes
*/
if (commandName === 'addCommand') {
continue;
}
commands[commandName] = require(_path2.default.join(dir, commandName));
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
return commands;
};
exports.default = getImplementedCommands;
module.exports = exports['default'];

View File

@ -0,0 +1,51 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _ErrorHandler = require('../utils/ErrorHandler');
/**
* call must be scoped to the webdriverio client
*/
var handleMouseButtonCommand = function handleMouseButtonCommand(selector, button, xoffset, yoffset) {
var _this = this;
/**
* mobile only supports simple clicks
*/
if (this.isMobile) {
return this.click(selector);
}
/**
* just press button if no selector is given
*/
if (selector === undefined) {
return this.buttonPress(button);
}
return this.element(selector).then(function (res) {
/**
* check if element was found and throw error if not
*/
if (!res.value) {
throw new _ErrorHandler.RuntimeError(7);
}
/**
* simulate event in safari
*/
if (_this.desiredCapabilities.browserName === 'safari') {
return _this.moveTo(res.value.ELEMENT, xoffset, yoffset).execute(function (elem, x, y, button) {
return window._wdio_simulate(elem, 'mousedown', 0, 0, button) && window._wdio_simulate(elem, 'mouseup', 0, 0, button);
}, res.value, xoffset, yoffset, button);
}
return _this.moveTo(res.value.ELEMENT, xoffset, yoffset).buttonPress(button);
});
};
exports.default = handleMouseButtonCommand;
module.exports = exports['default'];

View File

@ -0,0 +1,24 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var BUTTON_ENUM = {
left: 0,
middle: 1,
right: 2
};
/**
* call must be scoped to the webdriverio client
*/
var handleMouseButtonProtocol = function handleMouseButtonProtocol(requestPath, button) {
if (typeof button !== 'number') {
button = BUTTON_ENUM[button || 'left'];
}
return this.requestHandler.create(requestPath, { button: button });
};
exports.default = handleMouseButtonProtocol;
module.exports = exports['default'];

View File

@ -0,0 +1,35 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
/**
* check if selenium response contains an element result
* @param {object} result response object from the driver
* @return {Boolean} returns
* 0 if response was not an element result
* 1 if response was a element result
* 2 if response was an elements result
*/
function hasElementResult(result) {
/**
* check for element call
*/
if (result && (result.value && result.value.ELEMENT || typeof result.selector === 'string' && result.value === null)) {
return 1;
}
/**
* check for elements call
*/
if (result && Array.isArray(result.value) && result.value.filter(function (r) {
return !r.ELEMENT;
}).length === 0) {
return 2;
}
return 0;
}
exports.default = hasElementResult;
module.exports = exports['default'];

View File

@ -0,0 +1,25 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
/**
* check if current platform is mobile device
*
* @param {Object} caps capabilities
* @return {Boolean} true if platform is mobile device
*/
var mobileDetector = function mobileDetector(caps) {
var isMobile = !!(typeof caps['appium-version'] !== 'undefined' || typeof caps['device-type'] !== 'undefined' || typeof caps['deviceType'] !== 'undefined' || typeof caps['device-orientation'] !== 'undefined' || typeof caps['deviceOrientation'] !== 'undefined' || typeof caps.deviceName !== 'undefined' ||
// Check browserName for specific values
caps.browserName === '' || caps.browserName !== undefined && (caps.browserName.toLowerCase() === 'ipad' || caps.browserName.toLowerCase() === 'iphone' || caps.browserName.toLowerCase() === 'android'));
var isIOS = !!(caps.platformName && caps.platformName.match(/iOS/i) || caps.deviceName && caps.deviceName.match(/(iPad|iPhone)/i));
var isAndroid = !!(caps.platformName && caps.platformName.match(/Android/i) || caps.browserName && caps.browserName.match(/Android/i));
return { isMobile: isMobile, isIOS: isIOS, isAndroid: isAndroid };
};
exports.default = mobileDetector;
module.exports = exports['default'];

View File

@ -0,0 +1,116 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _getIterator2 = require('babel-runtime/core-js/get-iterator');
var _getIterator3 = _interopRequireDefault(_getIterator2);
var _cssValue = require('css-value');
var _cssValue2 = _interopRequireDefault(_cssValue);
var _rgb2hex = require('rgb2hex');
var _rgb2hex2 = _interopRequireDefault(_rgb2hex);
var _sanitize = require('./sanitize');
var _sanitize2 = _interopRequireDefault(_sanitize);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var parse = function parse(cssPropertyValue, cssProperty) {
if (!cssPropertyValue || !cssPropertyValue.value) {
return null;
}
var parsedValue = {
property: cssProperty,
value: cssPropertyValue.value.toLowerCase().trim()
};
if (parsedValue.value.indexOf('rgb') === 0) {
/**
* remove whitespaces in rgb values
*/
parsedValue.value = parsedValue.value.replace(/\s/g, '');
/**
* parse color values
*/
var color = parsedValue.value;
parsedValue.parsed = (0, _rgb2hex2.default)(parsedValue.value);
parsedValue.parsed.type = 'color';
parsedValue.parsed[/[rgba]+/g.exec(color)[0]] = color;
} else if (parsedValue.property === 'font-family') {
var font = (0, _cssValue2.default)(cssPropertyValue.value);
var string = parsedValue.value;
var value = cssPropertyValue.value.split(/,/).map(_sanitize2.default.css);
parsedValue.value = _sanitize2.default.css(font[0].value || font[0].string);
parsedValue.parsed = { value: value, type: 'font', string: string };
} else {
/**
* parse other css properties
*/
try {
parsedValue.parsed = (0, _cssValue2.default)(cssPropertyValue.value);
if (parsedValue.parsed.length === 1) {
parsedValue.parsed = parsedValue.parsed[0];
}
if (parsedValue.parsed.type && parsedValue.parsed.type === 'number' && parsedValue.parsed.unit === '') {
parsedValue.value = parsedValue.parsed.value;
}
} catch (e) {
// TODO improve css-parse lib to handle properties like
// `-webkit-animation-timing-function : cubic-bezier(0.25, 0.1, 0.25, 1)
}
}
return parsedValue;
};
var parseCSS = function parseCSS(response, cssProperty) {
var parsedCSS = [];
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = (0, _getIterator3.default)(response), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var res = _step.value;
parsedCSS.push(parse(res, cssProperty));
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
if (parsedCSS.length === 1) {
return parsedCSS[0];
} else if (parsedCSS.length === 0) {
return null;
}
return parsedCSS;
};
exports.default = parseCSS;
module.exports = exports['default'];

View File

@ -0,0 +1,44 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
/**
* executes methods in try/catch block
*/
var safeExecute = function safeExecute(f, param) {
return function exec() {
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
var result = void 0;
args = param || args;
if (typeof f !== 'function') {
return args[0];
}
/**
* we need to catch errors here as we would stop the
* execution and the promise (and the test) will never
* finish
*/
try {
result = f.apply(this, args);
} catch (e) {
var error = e;
if (e instanceof Error === false) {
error = new Error(e);
}
return error;
}
return result;
};
};
exports.default = safeExecute;
module.exports = exports['default'];

View File

@ -0,0 +1,148 @@
'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 _jsonStringifySafe = require('json-stringify-safe');
var _jsonStringifySafe2 = _interopRequireDefault(_jsonStringifySafe);
var _validator = require('validator');
var _validator2 = _interopRequireDefault(_validator);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var OBJLENGTH = 10;
var ARRLENGTH = 10;
var STRINGLIMIT = 1000;
var STRINGTRUNCATE = 200;
var sanitizeString = function sanitizeString(str) {
if (!str) {
return '';
}
return String(str).replace(/^.*\/([^/]+)\/?$/, '$1').replace(/\./g, '_').replace(/\s/g, '').toLowerCase();
};
/**
* formats capability object into sanitized string for e.g.filenames
* @param {Object} caps Selenium capabilities
*/
var caps = function caps(_caps) {
if (!_caps) {
return '';
}
var result = void 0;
/**
* mobile caps
*/
if (_caps.deviceName) {
result = [sanitizeString(_caps.deviceName), sanitizeString(_caps.platformName), sanitizeString(_caps.platformVersion), sanitizeString(_caps.app)];
} else {
result = [sanitizeString(_caps.browserName), sanitizeString(_caps.version), sanitizeString(_caps.platform), sanitizeString(_caps.app)];
}
result = result.filter(function (n) {
return n !== undefined && n !== '';
});
return result.join('.');
};
/**
* formats arguments into string
* @param {Array} args arguments object
*/
var args = function args(_args) {
if (!_args || !Array.isArray(_args)) {
return '';
}
return _args.map(function (arg) {
if (typeof arg === 'function' || typeof arg === 'string' && arg.indexOf('return (function') === 0) {
return '<Function>';
} else if (typeof arg === 'string') {
return '"' + arg + '"';
} else if (Array.isArray(arg)) {
return arg.join(', ');
}
return arg;
}).join(', ');
};
var css = function css(value) {
if (!value) {
return value;
}
return value.trim().replace(/'/g, '').replace(/"/g, '').toLowerCase();
};
/**
* Limit the length of an arbitrary variable of any type, suitable for being logged or displayed
* @param {Any} val Any variable
* @return {Any} Limited var of same type
*/
var limit = function limit(val) {
if (!val) return val;
// Ensure we're working with a copy
val = JSON.parse((0, _jsonStringifySafe2.default)(val));
switch (Object.prototype.toString.call(val)) {
case '[object String]':
if (val.length > 100 && _validator2.default.isBase64(val)) {
return '[base64] ' + val.length + ' bytes';
}
if (val.length > STRINGLIMIT) {
return val.substr(0, STRINGTRUNCATE) + (' ... (' + (val.length - STRINGTRUNCATE) + ' more bytes)');
}
return val;
case '[object Array]':
var length = val.length;
if (length > ARRLENGTH) {
val = val.slice(0, ARRLENGTH);
val.push('(' + (length - ARRLENGTH) + ' more items)');
}
return val.map(limit);
case '[object Object]':
var keys = (0, _keys2.default)(val);
var removed = [];
for (var i = 0, l = keys.length; i < l; i++) {
if (i < OBJLENGTH) {
val[keys[i]] = limit(val[keys[i]]);
} else {
delete val[keys[i]];
removed.push(keys[i]);
}
}
if (removed.length) {
val._ = keys.length - OBJLENGTH + ' more keys: ' + (0, _stringify2.default)(removed);
}
return val;
}
return val;
};
exports.default = {
css: css,
args: args,
caps: caps,
limit: limit
};
module.exports = exports['default'];

View File

@ -0,0 +1,321 @@
exports.config = {
<% if(answers.host && answers.port) { %>//
// =====================
// Server Configurations
// =====================
// Host address of the running Selenium server. This information is usually obsolete as
// WebdriverIO automatically connects to localhost. Also, if you are using one of the
// supported cloud services like Sauce Labs, Browserstack, or Testing Bot you don't
// need to define host and port information because WebdriverIO can figure that out
// according to your user and key information. However, if you are using a private Selenium
// backend you should define the host address, port, and path here.
//
host: '<%= answers.host %>',
port: <%= answers.port %>,
path: '<%= answers.path %>',
<% }
if(answers.env_user && answers.env_key) { %>
//
// =================
// Service Providers
// =================
// WebdriverIO supports Sauce Labs, Browserstack, and Testing Bot (other cloud providers
// should work too though). These services define specific user and key (or access key)
// values you need to put in here in order to connect to these services.
//
user: process.env.<%= answers.env_user %>,
key: process.env.<%= answers.env_key %>,
<% }
if(answers.backend.indexOf('In the cloud') > -1) { %>
<% } %>
//
// ==================
// Specify Test Files
// ==================
// Define which test specs should run. The pattern is relative to the directory
// from which `wdio` was called. Notice that, if you are calling `wdio` from an
// NPM script (see https://docs.npmjs.com/cli/run-script) then the current working
// directory is where your package.json resides, so `wdio` will be called from there.
//
specs: [
'<%= answers.specs %>'
],
// Patterns to exclude.
exclude: [
// 'path/to/excluded/files'
],
//
// ============
// Capabilities
// ============
// Define your capabilities here. WebdriverIO can run multiple capabilities at the same
// time. Depending on the number of capabilities, WebdriverIO launches several test
// sessions. Within your capabilities you can overwrite the spec and exclude options in
// order to group specific specs to a specific capability.
//
// First, you can define how many instances should be started at the same time. Let's
// say you have 3 different capabilities (Chrome, Firefox, and Safari) and you have
// set maxInstances to 1; wdio will spawn 3 processes. Therefore, if you have 10 spec
// files and you set maxInstances to 10, all spec files will get tested at the same time
// and 30 processes will get spawned. The property handles how many capabilities
// from the same test should run tests.
//
maxInstances: 10,
//
// If you have trouble getting all important capabilities together, check out the
// Sauce Labs platform configurator - a great tool to configure your capabilities:
// https://docs.saucelabs.com/reference/platforms-configurator
//
capabilities: [{
// maxInstances can get overwritten per capability. So if you have an in-house Selenium
// grid with only 5 firefox instances available you can make sure that not more than
// 5 instances get started at a time.
maxInstances: 5,
//
browserName: 'firefox'
}],
//
// ===================
// Test Configurations
// ===================
// Define all options that are relevant for the WebdriverIO instance here
//
// By default WebdriverIO commands are executed in a synchronous way using
// the wdio-sync package. If you still want to run your tests in an async way
// e.g. using promises you can set the sync option to false.
sync: true,
//
// Level of logging verbosity: silent | verbose | command | data | result | error
logLevel: '<%= answers.logLevel %>',
//
// Enables colors for log output.
coloredLogs: true,
//
// If you only want to run your tests until a specific amount of tests have failed use
// bail (default is 0 - don't bail, run all tests).
bail: 0,
//
// Saves a screenshot to a given path if a command fails.
screenshotPath: '<%= answers.screenshotPath %>',
//
// Set a base URL in order to shorten url command calls. If your url parameter starts
// with "/", then the base url gets prepended.
baseUrl: '<%= answers.baseUrl %>',
//
// Default timeout for all waitFor* commands.
waitforTimeout: 10000,
//
// Default timeout in milliseconds for request
// if Selenium Grid doesn't send response
connectionRetryTimeout: 90000,
//
// Default request retries count
connectionRetryCount: 3,
//
// Initialize the browser instance with a WebdriverIO plugin. The object should have the
// plugin name as key and the desired plugin options as properties. Make sure you have
// the plugin installed before running any tests. The following plugins are currently
// available:
// WebdriverCSS: https://github.com/webdriverio/webdrivercss
// WebdriverRTC: https://github.com/webdriverio/webdriverrtc
// Browserevent: https://github.com/webdriverio/browserevent
// plugins: {
// webdrivercss: {
// screenshotRoot: 'my-shots',
// failedComparisonsRoot: 'diffs',
// misMatchTolerance: 0.05,
// screenWidth: [320,480,640,1024]
// },
// webdriverrtc: {},
// browserevent: {}
// },
//
// Test runner services
// Services take over a specific job you don't want to take care of. They enhance
// your test setup with almost no effort. Unlike plugins, they don't add new
// commands. Instead, they hook themselves up into the test process.
<% if(answers.services.length) {
%>services: ['<%- answers.services.map(function(service) {
return service.slice(5, -8);
}).join("','") %>'],
<% } else {
%>// services: [],<% } %>//
// Framework you want to run your specs with.
// The following are supported: Mocha, Jasmine, and Cucumber
// see also: http://webdriver.io/guide/testrunner/frameworks.html
//
// Make sure you have the wdio adapter package for the specific framework installed
// before running any tests.
framework: '<%= answers.framework %>',
//
// Test reporter for stdout.
// The only one supported by default is 'dot'
// see also: http://webdriver.io/guide/testrunner/reporters.html
<% if(answers.reporters.length) {
%>reporters: ['<%- answers.reporters.map(function(reporter) {
return reporter.slice(5, -9);
}).join("','") %>'],
<% } else {
%>// reporters: ['dot'],<%
}
if(answers.outputDir) { %>//
// Some reporters require additional information which should get defined here
reporterOptions: {
//
// If you are using the "xunit" reporter you should define the directory where
// WebdriverIO should save all unit reports.
outputDir: './'
},
<% }
if(answers.framework === 'mocha') { %>
//
// Options to be passed to Mocha.
// See the full list at http://mochajs.org/
mochaOpts: {
ui: 'bdd'
},<% }
if(answers.framework === 'jasmine') { %>
//
// Options to be passed to Jasmine.
jasmineNodeOpts: {
//
// Jasmine default timeout
defaultTimeoutInterval: 10000,
//
// The Jasmine framework allows interception of each assertion in order to log the state of the application
// or website depending on the result. For example, it is pretty handy to take a screenshot every time
// an assertion fails.
expectationResultHandler: function(passed, assertion) {
// do something
}
},
<% }
if(answers.framework === 'cucumber') { %>//
// If you are using Cucumber you need to specify the location of your step definitions.
cucumberOpts: {
require: ['<%= answers.stepDefinitions %>'], // <string[]> (file/dir) require files before executing features
backtrace: false, // <boolean> show full backtrace for errors
compiler: [], // <string[]> ("extension:module") require files with the given EXTENSION after requiring MODULE (repeatable)
dryRun: false, // <boolean> invoke formatters without executing steps
failFast: false, // <boolean> abort the run on first failure
format: ['pretty'], // <string[]> (type[:path]) specify the output format, optionally supply PATH to redirect formatter output (repeatable)
colors: true, // <boolean> disable colors in formatter output
snippets: true, // <boolean> hide step definition snippets for pending steps
source: true, // <boolean> hide source uris
profile: [], // <string[]> (name) specify the profile to use
strict: false, // <boolean> fail if there are any undefined or pending steps
tags: [], // <string[]> (expression) only execute the features or scenarios with tags matching the expression
timeout: 20000, // <number> timeout for step definitions
ignoreUndefinedDefinitions: false, // <boolean> Enable this config to treat undefined definitions as warnings.
},
<% } %>
//
// =====
// Hooks
// =====
// WebdriverIO provides several hooks you can use to interfere with the test process in order to enhance
// it and to build services around it. You can either apply a single function or an array of
// methods to it. If one of them returns with a promise, WebdriverIO will wait until that promise got
// resolved to continue.
/**
* Gets executed once before all workers get launched.
* @param {Object} config wdio configuration object
* @param {Array.<Object>} capabilities list of capabilities details
*/
// onPrepare: function (config, capabilities) {
// },
/**
* Gets executed just before initialising the webdriver session and test framework. It allows you
* to manipulate configurations depending on the capability or spec.
* @param {Object} config wdio configuration object
* @param {Array.<Object>} capabilities list of capabilities details
* @param {Array.<String>} specs List of spec file paths that are to be run
*/
// beforeSession: function (config, capabilities, specs) {
// },
/**
* Gets executed before test execution begins. At this point you can access to all global
* variables like `browser`. It is the perfect place to define custom commands.
* @param {Array.<Object>} capabilities list of capabilities details
* @param {Array.<String>} specs List of spec file paths that are to be run
*/
// before: function (capabilities, specs) {
// },
//
/**
* Hook that gets executed before the suite starts
* @param {Object} suite suite details
*/
// beforeSuite: function (suite) {
// },
/**
* Hook that gets executed _before_ a hook within the suite starts (e.g. runs before calling
* beforeEach in Mocha)
*/
// beforeHook: function () {
// },
/**
* Hook that gets executed _after_ a hook within the suite starts (e.g. runs after calling
* afterEach in Mocha)
*/
// afterHook: function () {
// },
/**
* Function to be executed before a test (in Mocha/Jasmine) or a step (in Cucumber) starts.
* @param {Object} test test details
*/
// beforeTest: function (test) {
// },
/**
* Runs before a WebdriverIO command gets executed.
* @param {String} commandName hook command name
* @param {Array} args arguments that command would receive
*/
// beforeCommand: function (commandName, args) {
// },
/**
* Runs after a WebdriverIO command gets executed
* @param {String} commandName hook command name
* @param {Array} args arguments that command would receive
* @param {Number} result 0 - command success, 1 - command error
* @param {Object} error error object if any
*/
// afterCommand: function (commandName, args, result, error) {
// },
/**
* Function to be executed after a test (in Mocha/Jasmine) or a step (in Cucumber) starts.
* @param {Object} test test details
*/
// afterTest: function (test) {
// },
/**
* Hook that gets executed after the suite has ended
* @param {Object} suite suite details
*/
// afterSuite: function (suite) {
// },
/**
* Gets executed after all tests are done. You still have access to all global variables from
* the test.
* @param {Number} result 0 - test pass, 1 - test fail
* @param {Array.<Object>} capabilities list of capabilities details
* @param {Array.<String>} specs List of spec file paths that ran
*/
// after: function (result, capabilities, specs) {
// },
/**
* Gets executed right after terminating the webdriver session.
* @param {Object} config wdio configuration object
* @param {Array.<Object>} capabilities list of capabilities details
* @param {Array.<String>} specs List of spec file paths that ran
*/
// afterSession: function (config, capabilities, specs) {
// },
/**
* Gets executed after all workers got shut down and the process is about to exit. It is not
* possible to defer the end of the process using a promise.
* @param {Object} exitCode 0 - success, 1 - fail
*/
// onComplete: function(exitCode) {
// }
}