webdriverio-workflo
Version:
This is a customized version of webdriverio for use with workflo framework.
305 lines (260 loc) • 11 kB
JavaScript
Object.defineProperty(exports, "__esModule", {
value: true
});
var _keys = require('babel-runtime/core-js/object/keys');
var _keys2 = _interopRequireDefault(_keys);
var _promise = require('babel-runtime/core-js/promise');
var _promise2 = _interopRequireDefault(_promise);
exports.default = touchAction;
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
*
* The Touch Action API provides the basis of all gestures that can be automated in Appium.
* It is currently only available to native apps and can not be used to interact with webapps.
* At its core is the ability to chain together _ad hoc_ individual actions, which will then be
* applied to an element in the application on the device. The basic actions that can be used are:
*
* - press (pass selector or (x,y) or both)
* - longPress (pass selector or (x,y) or both)
* - tap (pass selector or (x,y) or both)
* - moveTo (pass selector or (x,y) or both)
* - wait (pass ms (as milliseconds))
* - release (no arguments)
*
* If you use the touchAction command with a selector you don't need to pass the selector to each
* action. It will be propagated by the internally (if no x or y parameters are given).
*
* <example>
:touchAction.js
it('should do a touch gesture', function () {
var screen = $('//UITextbox');
// simple touch action on element
screen.touchAction('tap');
// same as
browser.touchAction('//UITextbox', 'tap')
// simple touch action using x y variables
browser.touchAction({
action: 'tap', x: 300, y:200
})
// simple touch action using selector and x y variables
// tap location is 30px right and 20px down relative from the center of the element
browser.touchAction({
action: 'tap', x: 30, y:20, selector: '//UIAApplication[1]/UIAElement[2]'
})
// multi action on an element (drag&drop)
screen.touchAction([
'press',
{ action: 'moveTo', x: 200, y: 0 },
'release'
])
// same as
browser.touchAction('//UITextbox', [
'press',
{ action: 'moveTo', x: 200, y: 0},
'release'
])
// multi action using x y variables
// moveTo location is relative from the starting coordinate
browser.touchAction([
{ action: 'press', x: 20, y: 550 },
{ action: 'moveTo', x: 0, y: -500},
'release'
])
// drag&drop to element
screen.touchAction([
'press',
{ action: 'moveTo', selector: '//UIAApplication[1]/UIAElement[2]' },
'release'
])
});
:multiTouchAction.js
it('should do a multitouch gesture', function () {
// drag&drop with two fingers 200px down
browser.touchAction([
[{action: 'press', x: 10, y: 10}, { action: 'moveTo', x: 0, y: 200 }, 'release'],
[{action: 'press', x: 100, y: 10}, { action: 'moveTo', x: 0, y: 200 }, 'release']
])
})
* </example>
*
* @param {String} selector selector to execute the touchAction on
* @param {String} action action to execute
*
* @see https://saucelabs.com/blog/appium-sauce-labs-bootcamp-chapter-2-touch-actions
* @type mobile
* @for android, ios
* @uses mobile/performTouchAction, mobile/performMultiAction
*
*/
var TOUCH_ACTIONS = ['press', 'longPress', 'tap', 'moveTo', 'wait', 'release'];
var POS_ACTIONS = TOUCH_ACTIONS.slice(0, -2);
var ACCEPTED_OPTIONS = ['x', 'y', 'selector', 'element'];
function touchAction(selector, actions) {
var _this = this;
if (typeof selector !== 'string' || TOUCH_ACTIONS.indexOf(selector) > -1) {
actions = selector;
selector = this.lastResult;
}
if (!Array.isArray(actions)) {
actions = [actions];
}
/**
* check if multiAction
*/
if (Array.isArray(actions[0])) {
actions = formatArgs(selector, actions);
return _promise2.default.all(getSelectors.call(this, actions, true)).then(function (jsonElements) {
actions = replaceSelectorsById(actions, jsonElements);
return _this.performMultiAction({ actions });
});
}
actions = formatArgs(selector, actions);
return _promise2.default.all(getSelectors.call(this, actions)).then(function (jsonElements) {
actions = replaceSelectorsById(actions, jsonElements);
return _this.performTouchAction({ actions });
});
}
/**
* helper to determine if action has proper option arguments
* ('press', 'longPress', 'tap', 'moveTo' need at least some kind of position information)
* @param {String} action name of action
* @param {Object} options action options
* @return {Boolean} True if don't need any options or has a position option
*/
var hasValidActionOptions = function hasValidActionOptions(action, options) {
return POS_ACTIONS.indexOf(action) < 0 || POS_ACTIONS.indexOf(action) > -1 && (0, _keys2.default)(options).length > 0;
};
var formatArgs = function formatArgs(selector, actions) {
return actions.map(function (action) {
if (Array.isArray(action)) {
return formatArgs(selector, action);
}
var formattedAction = { action: action.action, options: {}
/**
* propagate selector or element to options object
*/
};if (selector &&
// selector is given as string `e.g. browser.touchAction(selector, 'tap')`
typeof selector === 'string' &&
// don't propagate for actions that don't require element options
POS_ACTIONS.indexOf(typeof action === 'string' ? action : formattedAction.action) > -1 &&
// don't propagate if user has x and y set
!(isFinite(action.x) && isFinite(action.y))) {
formattedAction.options.selector = selector;
} else if (selector &&
// selector is given by previous command
// e.g. $(selector).touchAction('tap')
selector.value &&
// don't propagate for actions that don't require element options
POS_ACTIONS.indexOf(typeof action === 'string' ? action : formattedAction.action) > -1 &&
// don't propagate if user has x and y set
!(isFinite(action.x) && isFinite(action.y))) {
formattedAction.options.element = selector.value.ELEMENT;
}
if (typeof action === 'string') {
if (!hasValidActionOptions(action, formattedAction.options)) {
throw new Error(`Touch action "${action}" doesn't have proper options. Make sure certain actions like ` + `${POS_ACTIONS.join(', ')} have position options like "selector", "x" or "y".`);
}
formattedAction.action = action;
/**
* remove options property if empty
*/
if ((0, _keys2.default)(formattedAction.options).length === 0) {
delete formattedAction.options;
}
return formattedAction;
}
if (isFinite(action.x)) formattedAction.options.x = action.x;
if (isFinite(action.y)) formattedAction.options.y = action.y;
if (action.ms) formattedAction.options.ms = action.ms;
if (action.selector && POS_ACTIONS.indexOf(formattedAction.action) > -1) {
formattedAction.options.selector = action.selector;
}
if (action.element) {
formattedAction.options.element = action.element;
delete formattedAction.options.selector;
}
/**
* remove options property if empty
*/
if ((0, _keys2.default)(formattedAction.options).length === 0) {
delete formattedAction.options;
}
/**
* option check
* make sure action has proper options before sending command to Appium
*/
if (formattedAction.action === 'release' && formattedAction.options) {
throw new Error('action "release" doesn\'t accept any options ' + `("${(0, _keys2.default)(formattedAction.options).join('", "')}" found)`);
} else if (formattedAction.action === 'wait' && ((0, _keys2.default)(formattedAction.options).indexOf('x') > -1 || (0, _keys2.default)(formattedAction.options).indexOf('y') > -1)) {
throw new Error('action "wait" doesn\'t accept x, y options');
} else if (POS_ACTIONS.indexOf(formattedAction.action) > -1) {
for (var option in formattedAction.options) {
if (ACCEPTED_OPTIONS.indexOf(option) === -1) {
throw new Error(`action "${formattedAction.action}" doesn't accept "${option}" as option`);
}
}
if ((0, _keys2.default)(formattedAction.options || {}).length === 0) {
throw new Error(`Touch actions like "${formattedAction.action}" need at least some kind of ` + 'position information like "selector", "x" or "y" options, you\'ve none given.');
}
}
return formattedAction;
});
};
var getSelectors = function getSelectors(actions) {
var _this2 = this;
var isMultiAction = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
var queriedSelectors = [];
/**
* flatten actions array
*/
if (isMultiAction) {
actions = [].concat.apply([], actions);
}
return actions
/**
* map down to list of selectors
*/
.map(function (action) {
return action.options && action.options.selector;
})
/**
* filter actions without selector and unique selectors
*/
.filter(function (selector) {
var res = Boolean(selector) && queriedSelectors.indexOf(selector) === -1;
queriedSelectors.push(selector);
return res;
})
/**
* call element command on selectors
*/
.map(function (selector) {
return _this2.element(selector);
});
};
/**
* replaces selector action properties with element ids after they got fetched
* @param {Object[]} actions list of actions
* @param {Object[]} elements list of fetched elements
* @return {Object[]} list of actions with proper element ids
*/
var replaceSelectorsById = function replaceSelectorsById(actions, elements) {
return actions.map(function (action) {
if (Array.isArray(action)) {
return replaceSelectorsById(action, elements);
}
if (!action.options || !action.options.selector) {
return action;
}
elements.forEach(function (element) {
if (action.options.selector === element.selector) {
action.options.element = element.value.ELEMENT;
delete action.options.selector;
}
});
return action;
});
};
module.exports = exports['default'];
;