--- a/testing/marionette/action.js
+++ b/testing/marionette/action.js
@@ -1,477 +1,287 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://gre/modules/Log.jsm");
-Cu.import("resource://gre/modules/Preferences.jsm");
Cu.import("chrome://marionette/content/element.js");
-Cu.import("chrome://marionette/content/event.js");
+Cu.import("chrome://marionette/content/error.js");
-const CONTEXT_MENU_DELAY_PREF = "ui.click_hold_context_menus.delay";
-const DEFAULT_CONTEXT_MENU_DELAY = 750; // ms
-this.EXPORTED_SYMBOLS = ["action"];
+this.EXPORTED_SYMBOLS = ["action", "inputStateMap"];
const logger = Log.repository.getLogger("Marionette");
-this.action = {};
+// map between input id (string) and device state for that input source
+// TODO associate with session
+// populated by "dispatch tick actions"?
+this.inputStateMap = new Map();
+
+
+const ACTION_TYPES = new Set([
+ "none",
+ "key",
+ "pointer",
+]);
+
+this.action = {}
+
+// input source: virtual device that has an string id and a source type
+// This doesn't seem to be really used in spec. Each "actionSequence" has id
+// and type already, which is all we really need.
+// action.InputSource = function InputSource(id, type){
+// this.id = id;
+// // null, key, pointer
+// this.type = type;
+// }
+
+action.NullInputState = function NullInputState(){
+ // a named empty object
+}
+
+action.NullInputState.prototype.toString = function(){
+ return '[object NullInputState]';
+}
+
+action.KeyInputState = function KeyInputState(){
+ this.pressed = new Set();
+ this.alt = false;
+ this.shift = false;
+ this.ctrl = false;
+ this.meta = false;
+}
+
+action.KeyInputState.prototype.toString = function(){
+ return '[object KeyInputState]';
+}
+
+action.PointerInputState = function PointerInputState(subtype, primary){
+ this.pressed = new Set();
+ this.subtype = subtype;
+ this.primary = primary;
+ this.x = 0;
+ this.y = 0;
+}
-/**
- * Functionality for (single finger) action chains.
- */
-action.Chain = function(checkForInterrupted) {
- // for assigning unique ids to all touches
- this.nextTouchId = 1000;
- // keep track of active Touches
- this.touchIds = {};
- // last touch for each fingerId
- this.lastCoordinates = null;
- this.isTap = false;
- this.scrolling = false;
- // whether to send mouse event
- this.mouseEventsOnly = false;
- this.checkTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+action.PointerInputState.prototype.toString = function(){
+ return '[object PointerInputState]';
+}
+
+const ACTION_INPUT_STATE = {
+ none: action.NullInputState,
+ key: action.KeyInputState,
+ pointer: action.PointerInputState,
+}
+
+action.Action = function Action(id, type, subtype) {
+ // represents action object for actionByTick
+ this.id = id;
+ this.type = type;
+ this.subtype = subtype;
+};
- if (typeof checkForInterrupted == "function") {
- this.checkForInterrupted = checkForInterrupted;
- } else {
- this.checkForInterrupted = () => {};
+// actions: list of action sequences
+action.extractActionChain = function extractActionChain(actions) {
+ // TODO check current browsing context?
+ if (typeof actions === 'undefined' || !Array.isArray(actions)){
+ throw new InvalidArgumentError(`'actions' ${actions} is not an Array`);
+ }
+ let actionsByTick = [];
+ for (let actionSequence of actions){
+ let inputSourceActions = action.processInputSourceActionSequence(actionSequence);
+ for (var i = 0; i < inputSourceActions.length; i++){
+ if (actionsByTick.length < i + 1){
+ // new tick
+ actionsByTick.push([]);
+ }
+ actionsByTick[i].push(inputSourceActions[i]);
+ }
+ }
+ return actionsByTick;
+};
+
+// action_sequence has a list of actionItems for one input source
+action.processInputSourceActionSequence = function processInputSourceActionSequence(
+ actionSequence) {
+ let type = actionSequence.type;
+ if (!ACTION_TYPES.has(type)){
+ throw new InvalidArgumentError(`'actionSequence.type' must be one of
+ ${Array.from(ACTION_TYPES)}`);
+ }
+ let id = actionSequence.id;
+ if (typeof id === 'undefined'){
+ id = element.generateUUID();
+ }
+ else if (typeof id !== 'string'){
+ throw new InvalidArgumentError(`'id' ${id} is not a string`);
+ }
+ let actionItems = actionSequence.actions;
+ if (!Array.isArray(actionItems)){
+ throw new InvalidArgumentError(`'actionSequence.actions' ${actionSequence.actions} is not an Array`);
}
- // determines if we create touch events
- this.inputSource = null;
-};
-
-action.Chain.prototype.dispatchActions = function(
- args,
- touchId,
- container,
- seenEls,
- touchProvider) {
- // Some touch events code in the listener needs to do ipc, so we can't
- // share this code across chrome/content.
- if (touchProvider) {
- this.touchProvider = touchProvider;
+ let parameters;
+ if (type === "pointer"){
+ parameters = action.processPointerParameters(actionSequence.parameters);
}
- this.seenEls = seenEls;
- this.container = container;
- let commandArray = element.fromJson(
- args, seenEls, container.frame, container.shadowRoot);
-
- if (touchId == null) {
- touchId = this.nextTouchId++;
- }
-
- if (!container.frame.document.createTouch) {
- this.mouseEventsOnly = true;
+ if (inputStateMap.has(id) && !(inputStateMap.get(id) instanceof ACTION_INPUT_STATE[type])){
+ throw new InvalidArgumentError(
+ `${id} is already mapped to ${inputStateMap.get(id)}`);
}
- let keyModifiers = {
- shiftKey: false,
- ctrlKey: false,
- altKey: false,
- metaKey: false,
+ let actions = [];
+ for (let actionItem of actionItems){
+ if (Object.prototype.toString.call(actionItem) !== "[object Object]"){
+ throw new InvalidArgumentError(`${actionItem} is not an object`);
+ }
+ let act;
+ switch(type){
+ case("none"):
+ act = action.processNullAction(id, actionItem);
+ break;
+ case("key"):
+ act = action.processKeyAction(id, actionItem);
+ break;
+ case("pointer"):
+ act = action.processPointerAction(id, parameters, actionItem);
+ break;
+ }
+ actions.push(act);
+ }
+ return actions;
+};
+
+action.processPointerParameters = function processPointerParameters(parametersData){
+ let parameters = {
+ pointerType: "mouse",
+ primary: true,
};
+ if (typeof parametersData === 'undefined'){
+ return parameters;
+ }
+ if (Object.prototype.toString.call(parametersData) !== "[object Object]"){
+ throw new InvalidArgumentError(`parametersData ${parametersData} is not an object`);
+ }
+ let pointerType = parametersData.pointerType;
+ if (typeof pointerType !== 'undefined'){
+ let types = ["mouse", "pen", "touch"];
+ if(!types.includes(pointerType)){
+ throw new InvalidArgumentError(`pointerType must be one of ${types}`);
+ }
+ parameters.pointerType = pointerType;
+ }
+ let primary = parametersData.primary;
+ if (typeof primary !== 'undefined'){
+ if (typeof primary !== 'boolean'){
+ throw new InvalidArgumentError(`primary ${primary} must be a boolean`);
+ }
+ parameters.primary = primary;
+ }
+ return parameters;
+};
- return new Promise(resolve => {
- this.actions(commandArray, touchId, 0, keyModifiers, resolve);
- }).catch(this.resetValues);
+action.processNullAction = function processNullAction(id, actionItem){
+ let subtype = actionItem.type;
+ if (subtype !== "pause"){
+ throw new InvalidArgumentError("subtype must be 'pause'");
+ }
+ let act = new action.Action(id, "none", subtype);
+ action.processPauseAction(actionItem, act);
+ return act;
};
-/**
- * This function emit mouse event.
- *
- * @param {Document} doc
- * Current document.
- * @param {string} type
- * Type of event to dispatch.
- * @param {number} clickCount
- * Number of clicks, button notes the mouse button.
- * @param {number} elClientX
- * X coordinate of the mouse relative to the viewport.
- * @param {number} elClientY
- * Y coordinate of the mouse relative to the viewport.
- * @param {Object} modifiers
- * An object of modifier keys present.
- */
-action.Chain.prototype.emitMouseEvent = function(
- doc,
- type,
- elClientX,
- elClientY,
- button,
- clickCount,
- modifiers) {
- if (!this.checkForInterrupted()) {
- logger.debug(`Emitting ${type} mouse event ` +
- `at coordinates (${elClientX}, ${elClientY}) ` +
- `relative to the viewport, ` +
- `button: ${button}, ` +
- `clickCount: ${clickCount}`);
+action.processKeyAction = function processKeyAction(id, actionItem){
+ let subtype = actionItem.type;
+ let types = ["keyUp", "keyDown", "pause"];
+ if (!types.includes(subtype)){
+ throw new InvalidArgumentError(`subtype must be one of ${Array.from(types)}`);
+ }
+ let act = new action.Action(id, "key", subtype);
+ if (subtype === "pause"){
+ action.processPauseAction(actionItem, act);
+ return act;
+ }
+ let key = actionItem.value
+ // what about key codes like arrow, versus unicode chars?
+ if (typeof key !== 'string' || (typeof key === 'string' && key.length !== 1)){
+ throw new InvalidArgumentError("'key' must be a single-character String");
+ }
+ act.value = key;
+ return act;
+};
+
+action.processPointerAction = function processPointerAction(id, parameters, actionItem){
+ let subtype = actionItem.type;
+ let types = ["pause", "pointerUp", "pointerDown", "pointerMove", "pointerCancel"];
+ if (!types.includes(subtype)){
+ throw new InvalidArgumentError(`subtype must be one of ${Array.from(types)}`);
+ }
+
+ if (inputStateMap.has(id) && inputStateMap.get(id).subtype !== subtype){
+ throw new InvalidArgumentError(
+ `${id} is already mapped to InputState whose subtype is `
+ + `${inputStateMap.get(id).subtype} not ${subtype}`);
+ }
- let win = doc.defaultView;
- let domUtils = win.QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIDOMWindowUtils);
+ let act = new action.Action(id, "pointer", subtype);
+ if (subtype === "pause"){
+ action.processPauseAction(actionItem, act);
+ return act;
+ }
+ act.pointerType = parameters.pointerType;
+ act.primary = parameters.primary;
+ if (["pointerUp", "pointerDown"].includes(subtype)){
+ action.processPointerUpDownAction(actionItem, act);
+ }
+ if (subtype == "pointerMove"){
+ action.processPointerMoveAction(actionItem, act);
+ }
+ return act;
+};
- let mods;
- if (typeof modifiers != "undefined") {
- mods = event.parseModifiers_(modifiers);
- } else {
- mods = 0;
- }
+action.processPauseAction = function processPauseAction(actionItem, act){
+ act.duration = action.validatePositiveNum(actionItem.duration, 'duration');
+};
- domUtils.sendMouseEvent(
- type,
- elClientX,
- elClientY,
- button || 0,
- clickCount || 1,
- mods,
- false,
- 0,
- this.inputSource);
+action.processPointerUpDownAction = function processPointerUpDownAction(
+ actionItem,
+ act){
+ act.button = action.validatePositiveNum(actionItem.button, 'button');
+};
+
+action.processPointerMoveAction = function processPointerMoveAction(actionItem, act){
+ act.duration = action.validatePositiveNum(actionItem.duration, 'duration');
+ let webElement = actionItem.element;
+ let isElement = function(el){
+ let properties = Object.getOwnPropertyNames(el);
+ return properties.includes(element.Key) || properties.includes(element.LegacyKey);
+ }
+ if (typeof webElement !== "undefined" && !isElement(webElement)){
+ throw new InvalidArgumentError(
+ "'actionItem.duration' must be an Object that represents a web element");
+ }
+ act.element = webElement;
+
+ act.x = actionItem.x;
+ if (typeof act.x !== "undefined"){
+ act.x = action.validatePositiveNum(actionItem.x, 'x');
+ }
+ act.y = actionItem.y;
+ if (typeof act.y !== "undefined"){
+ act.y = action.validatePositiveNum(actionItem.y, 'y');
}
};
-/**
- * Reset any persisted values after a command completes.
- */
-action.Chain.prototype.resetValues = function() {
- this.container = null;
- this.seenEls = null;
- this.touchProvider = null;
- this.mouseEventsOnly = false;
-};
-
-/**
- * Emit events for each action in the provided chain.
- *
- * To emit touch events for each finger, one might send a [["press", id],
- * ["wait", 5], ["release"]] chain.
- *
- * @param {Array.<Array<?>>} chain
- * A multi-dimensional array of actions.
- * @param {Object.<string, number>} touchId
- * Represents the finger ID.
- * @param {number} i
- * Keeps track of the current action of the chain.
- * @param {Object.<string, boolean>} keyModifiers
- * Keeps track of keyDown/keyUp pairs through an action chain.
- * @param {function(?)} cb
- * Called on success.
- *
- * @return {Object.<string, number>}
- * Last finger ID, or an empty object.
- */
-action.Chain.prototype.actions = function(chain, touchId, i, keyModifiers, cb) {
- if (i == chain.length) {
- cb(touchId || null);
- this.resetValues();
- return;
- }
-
- let pack = chain[i];
- let command = pack[0];
- let el;
- let c;
- i++;
-
- if (["press", "wait", "keyDown", "keyUp", "click"].indexOf(command) == -1) {
- // if mouseEventsOnly, then touchIds isn't used
- if (!(touchId in this.touchIds) && !this.mouseEventsOnly) {
- this.resetValues();
- throw new WebDriverError("Element has not been pressed");
- }
+// helpers
+action.validatePositiveNum = function validatePositiveNum(value, name){
+ let v = parseInt(value);
+ if (isNaN(v) || v < 0){
+ throw new InvalidArgumentError(`Value of '${name}' should be integer >= 0`);
}
-
- switch (command) {
- case "keyDown":
- event.sendKeyDown(pack[1], keyModifiers, this.container.frame);
- this.actions(chain, touchId, i, keyModifiers, cb);
- break;
-
- case "keyUp":
- event.sendKeyUp(pack[1], keyModifiers, this.container.frame);
- this.actions(chain, touchId, i, keyModifiers, cb);
- break;
-
- case "click":
- el = this.seenEls.get(pack[1], this.container);
- let button = pack[2];
- let clickCount = pack[3];
- c = element.coordinates(el);
- this.mouseTap(el.ownerDocument, c.x, c.y, button, clickCount, keyModifiers);
- if (button == 2) {
- this.emitMouseEvent(el.ownerDocument, "contextmenu", c.x, c.y,
- button, clickCount, keyModifiers);
- }
- this.actions(chain, touchId, i, keyModifiers, cb);
- break;
-
- case "press":
- if (this.lastCoordinates) {
- this.generateEvents(
- "cancel",
- this.lastCoordinates[0],
- this.lastCoordinates[1],
- touchId,
- null,
- keyModifiers);
- this.resetValues();
- throw new WebDriverError(
- "Invalid Command: press cannot follow an active touch event");
- }
-
- // look ahead to check if we're scrolling,
- // needed for APZ touch dispatching
- if ((i != chain.length) && (chain[i][0].indexOf('move') !== -1)) {
- this.scrolling = true;
- }
- el = this.seenEls.get(pack[1], this.container);
- c = element.coordinates(el, pack[2], pack[3]);
- touchId = this.generateEvents("press", c.x, c.y, null, el, keyModifiers);
- this.actions(chain, touchId, i, keyModifiers, cb);
- break;
-
- case "release":
- this.generateEvents(
- "release",
- this.lastCoordinates[0],
- this.lastCoordinates[1],
- touchId,
- null,
- keyModifiers);
- this.actions(chain, null, i, keyModifiers, cb);
- this.scrolling = false;
- break;
-
- case "move":
- el = this.seenEls.get(pack[1], this.container);
- c = element.coordinates(el);
- this.generateEvents("move", c.x, c.y, touchId, null, keyModifiers);
- this.actions(chain, touchId, i, keyModifiers, cb);
- break;
-
- case "moveByOffset":
- this.generateEvents(
- "move",
- this.lastCoordinates[0] + pack[1],
- this.lastCoordinates[1] + pack[2],
- touchId,
- null,
- keyModifiers);
- this.actions(chain, touchId, i, keyModifiers, cb);
- break;
-
- case "wait":
- if (pack[1] != null) {
- let time = pack[1] * 1000;
-
- // standard waiting time to fire contextmenu
- let standard = Preferences.get(
- CONTEXT_MENU_DELAY_PREF,
- DEFAULT_CONTEXT_MENU_DELAY);
-
- if (time >= standard && this.isTap) {
- chain.splice(i, 0, ["longPress"], ["wait", (time - standard) / 1000]);
- time = standard;
- }
- this.checkTimer.initWithCallback(
- () => this.actions(chain, touchId, i, keyModifiers, cb),
- time, Ci.nsITimer.TYPE_ONE_SHOT);
- } else {
- this.actions(chain, touchId, i, keyModifiers, cb);
- }
- break;
-
- case "cancel":
- this.generateEvents(
- "cancel",
- this.lastCoordinates[0],
- this.lastCoordinates[1],
- touchId,
- null,
- keyModifiers);
- this.actions(chain, touchId, i, keyModifiers, cb);
- this.scrolling = false;
- break;
-
- case "longPress":
- this.generateEvents(
- "contextmenu",
- this.lastCoordinates[0],
- this.lastCoordinates[1],
- touchId,
- null,
- keyModifiers);
- this.actions(chain, touchId, i, keyModifiers, cb);
- break;
- }
-};
-
-/**
- * Given an element and a pair of coordinates, returns an array of the
- * form [clientX, clientY, pageX, pageY, screenX, screenY].
- */
-action.Chain.prototype.getCoordinateInfo = function(el, corx, cory) {
- let win = el.ownerDocument.defaultView;
- return [
- corx, // clientX
- cory, // clientY
- corx + win.pageXOffset, // pageX
- cory + win.pageYOffset, // pageY
- corx + win.mozInnerScreenX, // screenX
- cory + win.mozInnerScreenY // screenY
- ];
-};
-
-/**
- * @param {number} x
- * X coordinate of the location to generate the event that is relative
- * to the viewport.
- * @param {number} y
- * Y coordinate of the location to generate the event that is relative
- * to the viewport.
- */
-action.Chain.prototype.generateEvents = function(
- type, x, y, touchId, target, keyModifiers) {
- this.lastCoordinates = [x, y];
- let doc = this.container.frame.document;
-
- switch (type) {
- case "tap":
- if (this.mouseEventsOnly) {
- this.mouseTap(
- touch.target.ownerDocument,
- touch.clientX,
- touch.clientY,
- null,
- null,
- keyModifiers);
- } else {
- touchId = this.nextTouchId++;
- let touch = this.touchProvider.createATouch(target, x, y, touchId);
- this.touchProvider.emitTouchEvent("touchstart", touch);
- this.touchProvider.emitTouchEvent("touchend", touch);
- this.mouseTap(
- touch.target.ownerDocument,
- touch.clientX,
- touch.clientY,
- null,
- null,
- keyModifiers);
- }
- this.lastCoordinates = null;
- break;
-
- case "press":
- this.isTap = true;
- if (this.mouseEventsOnly) {
- this.emitMouseEvent(doc, "mousemove", x, y, null, null, keyModifiers);
- this.emitMouseEvent(doc, "mousedown", x, y, null, null, keyModifiers);
- } else {
- touchId = this.nextTouchId++;
- let touch = this.touchProvider.createATouch(target, x, y, touchId);
- this.touchProvider.emitTouchEvent("touchstart", touch);
- this.touchIds[touchId] = touch;
- return touchId;
- }
- break;
-
- case "release":
- if (this.mouseEventsOnly) {
- let [x, y] = this.lastCoordinates;
- this.emitMouseEvent(doc, "mouseup", x, y, null, null, keyModifiers);
- } else {
- let touch = this.touchIds[touchId];
- let [x, y] = this.lastCoordinates;
-
- touch = this.touchProvider.createATouch(touch.target, x, y, touchId);
- this.touchProvider.emitTouchEvent("touchend", touch);
-
- if (this.isTap) {
- this.mouseTap(
- touch.target.ownerDocument,
- touch.clientX,
- touch.clientY,
- null,
- null,
- keyModifiers);
- }
- delete this.touchIds[touchId];
- }
-
- this.isTap = false;
- this.lastCoordinates = null;
- break;
-
- case "cancel":
- this.isTap = false;
- if (this.mouseEventsOnly) {
- let [x, y] = this.lastCoordinates;
- this.emitMouseEvent(doc, "mouseup", x, y, null, null, keyModifiers);
- } else {
- this.touchProvider.emitTouchEvent("touchcancel", this.touchIds[touchId]);
- delete this.touchIds[touchId];
- }
- this.lastCoordinates = null;
- break;
-
- case "move":
- this.isTap = false;
- if (this.mouseEventsOnly) {
- this.emitMouseEvent(doc, "mousemove", x, y, null, null, keyModifiers);
- } else {
- let touch = this.touchProvider.createATouch(
- this.touchIds[touchId].target, x, y, touchId);
- this.touchIds[touchId] = touch;
- this.touchProvider.emitTouchEvent("touchmove", touch);
- }
- break;
-
- case "contextmenu":
- this.isTap = false;
- let event = this.container.frame.document.createEvent("MouseEvents");
- if (this.mouseEventsOnly) {
- target = doc.elementFromPoint(this.lastCoordinates[0], this.lastCoordinates[1]);
- } else {
- target = this.touchIds[touchId].target;
- }
-
- let [clientX, clientY, pageX, pageY, screenX, screenY] =
- this.getCoordinateInfo(target, x, y);
-
- event.initMouseEvent(
- "contextmenu",
- true,
- true,
- target.ownerDocument.defaultView,
- 1,
- screenX,
- screenY,
- clientX,
- clientY,
- false,
- false,
- false,
- false,
- 0,
- null);
- target.dispatchEvent(event);
- break;
-
- default:
- throw new WebDriverError("Unknown event type: " + type);
- }
- this.checkForInterrupted();
-};
-
-action.Chain.prototype.mouseTap = function(doc, x, y, button, count, mod) {
- this.emitMouseEvent(doc, "mousemove", x, y, button, count, mod);
- this.emitMouseEvent(doc, "mousedown", x, y, button, count, mod);
- this.emitMouseEvent(doc, "mouseup", x, y, button, count, mod);
-};
+ return v;
+}
--- a/testing/marionette/driver.js
+++ b/testing/marionette/driver.js
@@ -12,17 +12,17 @@ var loader = Cc["@mozilla.org/moz/jssubs
Cu.import("resource://gre/modules/Log.jsm");
Cu.import("resource://gre/modules/Preferences.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyServiceGetter(
this, "cookieManager", "@mozilla.org/cookiemanager;1", "nsICookieManager2");
-Cu.import("chrome://marionette/content/action.js");
+Cu.import("chrome://marionette/content/legacyaction.js");
Cu.import("chrome://marionette/content/atom.js");
Cu.import("chrome://marionette/content/browser.js");
Cu.import("chrome://marionette/content/element.js");
Cu.import("chrome://marionette/content/error.js");
Cu.import("chrome://marionette/content/evaluate.js");
Cu.import("chrome://marionette/content/event.js");
Cu.import("chrome://marionette/content/interaction.js");
Cu.import("chrome://marionette/content/logging.js");
--- a/testing/marionette/jar.mn
+++ b/testing/marionette/jar.mn
@@ -1,18 +1,18 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
marionette.jar:
% content marionette %content/
content/server.js (server.js)
content/driver.js (driver.js)
- content/webdriverAction.js (webdriverAction.js)
content/action.js (action.js)
+ content/legacyaction.js (legacyaction.js)
content/browser.js (browser.js)
content/interaction.js (interaction.js)
content/accessibility.js (accessibility.js)
content/listener.js (listener.js)
content/element.js (element.js)
content/simpletest.js (simpletest.js)
content/frame.js (frame.js)
content/event.js (event.js)
copy from testing/marionette/action.js
copy to testing/marionette/legacyaction.js
--- a/testing/marionette/listener.js
+++ b/testing/marionette/listener.js
@@ -8,17 +8,17 @@ var {classes: Cc, interfaces: Ci, utils:
var uuidGen = Cc["@mozilla.org/uuid-generator;1"]
.getService(Ci.nsIUUIDGenerator);
var loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
.getService(Ci.mozIJSSubScriptLoader);
Cu.import("chrome://marionette/content/accessibility.js");
-Cu.import("chrome://marionette/content/action.js");
+Cu.import("chrome://marionette/content/legacyaction.js");
Cu.import("chrome://marionette/content/atom.js");
Cu.import("chrome://marionette/content/capture.js");
Cu.import("chrome://marionette/content/cookies.js");
Cu.import("chrome://marionette/content/element.js");
Cu.import("chrome://marionette/content/error.js");
Cu.import("chrome://marionette/content/evaluate.js");
Cu.import("chrome://marionette/content/event.js");
Cu.import("chrome://marionette/content/interaction.js");
rename from testing/marionette/test_actions.js
rename to testing/marionette/test_action.js
--- a/testing/marionette/test_actions.js
+++ b/testing/marionette/test_action.js
@@ -1,110 +1,112 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
const {utils: Cu} = Components;
Cu.import("chrome://marionette/content/error.js");
Cu.import("chrome://marionette/content/element.js");
-Cu.import("chrome://marionette/content/webdriverAction.js");
+Cu.import("chrome://marionette/content/action.js");
add_test(function test_defaultPointerParameters() {
let defaultParameters = {pointerType: "mouse", primary: true};
- deepEqual(webdriveraction.processPointerParameters(), defaultParameters);
- deepEqual(webdriveraction.processPointerParameters({blah: 'nonsense'}), defaultParameters);
+ deepEqual(action.processPointerParameters(), defaultParameters);
+ deepEqual(action.processPointerParameters({blah: 'nonsense'}), defaultParameters);
run_next_test();
});
add_test(function test_processPointerParameters() {
let check = function(regex, message, args){
- checkErrors(regex, webdriveraction.processPointerParameters, args, message);
+ checkErrors(regex, action.processPointerParameters, args, message);
}
for (let d of [-1, 'a', [1,2], /blah/]){
let message = `parametersData: ${getTypeString(d)}`;
check(/not an object/, message, [d]);
}
let parametersData = {pointerType: "foo"};
let message = `parametersData: ${parametersData}`;
check(/must be one of/, message, [parametersData]);
parametersData.pointerType = "pen";
parametersData.primary = 'a';
check(/must be a boolean/, message, [parametersData]);
parametersData.primary = false;
- deepEqual(webdriveraction.processPointerParameters(parametersData),
+ deepEqual(action.processPointerParameters(parametersData),
{pointerType: "pen", primary: false});
run_next_test();
});
add_test(function test_processPointerUpDownAction() {
let actionItem = {};
for (let d of [-1, 'a']){
actionItem = {button: d};
checkErrors(/should be integer >= 0/,
- webdriveraction.processPointerUpDownAction,
+ action.processPointerUpDownAction,
[actionItem, {}],
`button: ${actionItem.button}`);
}
actionItem.button = 5;
- let action = new webdriveraction.Action("some id", "mouse", "pointerDown");
- webdriveraction.processPointerUpDownAction(actionItem, action);
- equal(action.button, actionItem.button);
+ let act = new action.Action("some id", "mouse", "pointerDown");
+ action.processPointerUpDownAction(actionItem, act);
+ equal(act.button, actionItem.button);
run_next_test();
});
add_test(function test_processPauseAction() {
let actionItem = {duration: 5};
- let action = new webdriveraction.Action("some id", "none", "pause");
- webdriveraction.processPauseAction(actionItem, action);
- equal(action.duration, actionItem.duration);
+ let act = new action.Action("some id", "none", "pause");
+ action.processPauseAction(actionItem, act);
+ equal(act.duration, actionItem.duration);
run_next_test();
});
add_test(function test_validateActionDuration() {
let actionItem = {};
let check = function(actionFunc, message){
let m = message + `; actionFunc: ${actionFunc.name}`;
checkErrors(/should be integer >= 0/,
actionFunc, [actionItem, {}], m);
};
for (let d of [-1, 'a', undefined]){
actionItem.duration = d;
let message = `duration: ${actionItem.duration}`;
- check(webdriveraction.processPauseAction, message);
- check(webdriveraction.processPointerMoveAction, message);
+ check(action.processPauseAction, message);
+ check(action.processPointerMoveAction, message);
}
actionItem.duration = 5;
for (let d of [-1, 'a']){
for (let name of ['x', 'y']){
actionItem[name] = d;
let message = `${name}: ${actionItem[name]}`;
- check(webdriveraction.processPointerMoveAction, message);
+ check(action.processPointerMoveAction, message);
}
}
run_next_test();
});
add_test(function test_processPointerMoveActionElementValidation() {
let actionItem = {
duration: 5,
type: "pointerMove",
};
for (let d of [-1, 'a', {a:"blah"}]){
actionItem.element = d;
checkErrors(/must be an Object that represents a web element/,
- webdriveraction.processPointerMoveAction,
+ action.processPointerMoveAction,
[actionItem, {}],
`actionItem.element: (${getTypeString(d)})`);
}
run_next_test();
});
add_test(function test_processPointerMoveActionElement() {
@@ -135,23 +137,23 @@ add_test(function test_processPointerMov
duration: 5,
type: "pointerMove",
x: 1,
y: 2,
element: undefined,
},
];
for (let a of actionItems){
- let action = new webdriveraction.Action("some id", "pointer", a.type);
- webdriveraction.processPointerMoveAction(a, action);
- ok(action instanceof webdriveraction.Action);
- equal(action.duration, a.duration);
- equal(action.element, a.element);
- equal(action.x, a.x);
- equal(action.y, a.y);
+ let act = new action.Action("some id", "pointer", a.type);
+ action.processPointerMoveAction(a, act);
+ ok(act instanceof action.Action);
+ equal(act.duration, a.duration);
+ equal(act.element, a.element);
+ equal(act.x, a.x);
+ equal(act.y, a.y);
}
run_next_test();
});
add_test(function test_processPointerAction() {
let pointerParams = {
pointerType: "touch",
@@ -168,119 +170,119 @@ add_test(function test_processPointerAct
},
{
type: "pointerUp",
button: 1,
}
];
let id = "some_id";
for (let a of actionItems){
- let action = webdriveraction.processPointerAction(id, pointerParams, a);
- ok(action instanceof webdriveraction.Action);
- equal(action.type, "pointer");
- equal(action.subtype, a.type);
- equal(action.id, id);
+ let act = action.processPointerAction(id, pointerParams, a);
+ ok(act instanceof action.Action);
+ equal(act.type, "pointer");
+ equal(act.subtype, a.type);
+ equal(act.id, id);
if (a.type === "pointerUp"){
- equal(action.button, a.button);
+ equal(act.button, a.button);
}
else {
- equal(action.duration, a.duration);
+ equal(act.duration, a.duration);
}
if (a.type !== "pause"){
- equal(action.primary, pointerParams.primary);
- equal(action.pointerType, pointerParams.pointerType);
+ equal(act.primary, pointerParams.primary);
+ equal(act.pointerType, pointerParams.pointerType);
}
}
run_next_test();
});
add_test(function test_processNullAction() {
let actionItem = {};
let id = "some_id";
actionItem.type = "pause";
actionItem.duration = 5;
- let action = webdriveraction.processNullAction(id, actionItem);
- ok(action instanceof webdriveraction.Action);
- equal(action.type, "none");
- equal(action.subtype, actionItem.type);
- equal(action.id, id);
- equal(action.duration, actionItem.duration);
+ let act = action.processNullAction(id, actionItem);
+ ok(act instanceof action.Action);
+ equal(act.type, "none");
+ equal(act.subtype, actionItem.type);
+ equal(act.id, id);
+ equal(act.duration, actionItem.duration);
run_next_test();
});
add_test(function test_processActionSubtypeValidation() {
var actionItem = {};
var id = "some_id";
actionItem.type = "dancing";
let check = function(regex, actionFunc, args=[id, actionItem]){
let message = `actionItem.type: ${actionItem.type}; actionFunc: ${actionFunc.name}`;
checkErrors(regex, actionFunc, args, message);
};
- check(/subtype must be 'pause'/, webdriveraction.processNullAction);
- check(/subtype must be one of/, webdriveraction.processKeyAction);
+ check(/subtype must be 'pause'/, action.processNullAction);
+ check(/subtype must be one of/, action.processKeyAction);
check(/subtype must be one of/,
- webdriveraction.processPointerAction,
+ action.processPointerAction,
[id, [], actionItem]);
run_next_test();
});
add_test(function test_processKeyActionPause() {
let actionItem = {};
let id = "some_id";
actionItem.type = "pause";
actionItem.duration = 5;
- let action = webdriveraction.processKeyAction(id, actionItem);
- ok(action instanceof webdriveraction.Action);
- equal(action.type, "key");
- equal(action.subtype, actionItem.type);
- equal(action.id, id);
- equal(action.duration, actionItem.duration);
+ let act = action.processKeyAction(id, actionItem);
+ ok(act instanceof action.Action);
+ equal(act.type, "key");
+ equal(act.subtype, actionItem.type);
+ equal(act.id, id);
+ equal(act.duration, actionItem.duration);
run_next_test();
});
add_test(function test_processKeyActionUpDown() {
let actionItem = {
type: "keyDown",
};
let id = "some_id";
for (let d of [-1, 'bad', undefined, [], ['a'], {length: 1}, null]){
actionItem.value = d;
let message = `actionItem.value: (${getTypeString(d)})`;
- Assert.throws(() => webdriveraction.processKeyAction(id, actionItem),
+ Assert.throws(() => action.processKeyAction(id, actionItem),
InvalidArgumentError, message);
- Assert.throws(() => webdriveraction.processKeyAction(id, actionItem),
+ Assert.throws(() => action.processKeyAction(id, actionItem),
/'key' must be a single-character String/,
message);
}
actionItem.value = 'a';
- let action = webdriveraction.processKeyAction(id, actionItem);
- ok(action instanceof webdriveraction.Action);
- equal(action.type, "key");
- equal(action.subtype, actionItem.type);
- equal(action.id, id);
- equal(action.value, actionItem.value);
+ let act = action.processKeyAction(id, actionItem);
+ ok(act instanceof action.Action);
+ equal(act.type, "key");
+ equal(act.subtype, actionItem.type);
+ equal(act.id, id);
+ equal(act.value, actionItem.value);
run_next_test();
});
add_test(function test_processInputSourceActionSequenceValidation() {
var actionSequence = {
type: "swim",
id: "some id",
};
let check = function(message, regex){
- checkErrors(regex, webdriveraction.processInputSourceActionSequence,
+ checkErrors(regex, action.processInputSourceActionSequence,
[actionSequence], message);
};
check(`actionSequence.type: ${actionSequence.type}`, /type' must be one of/);
actionSequence.type = "none";
actionSequence.id = -1;
check(`actionSequence.id: ${getTypeString(actionSequence.id)}`, /is not a string/);
@@ -301,19 +303,19 @@ add_test(function test_processInputSourc
type: "pause",
duration: 5,
};
let actionSequence = {
type: "none",
id: "some id",
actions: [actionItem],
};
- let expectedAction = new webdriveraction.Action(actionSequence.id, "none", actionItem.type);
+ let expectedAction = new action.Action(actionSequence.id, "none", actionItem.type);
expectedAction.duration = actionItem.duration;
- let actions = webdriveraction.processInputSourceActionSequence(actionSequence);
+ let actions = action.processInputSourceActionSequence(actionSequence);
equal(actions.length, 1);
deepEqual(actions[0], expectedAction);
run_next_test();
});
add_test(function test_processInputSourceActionSequencePointer() {
let actionItem = {
type: "pointerDown",
@@ -323,43 +325,43 @@ add_test(function test_processInputSourc
type: "pointer",
id: "9",
actions: [actionItem],
parameters: {
pointerType: "pen",
primary: false,
},
};
- let expectedAction = new webdriveraction.Action(actionSequence.id,
+ let expectedAction = new action.Action(actionSequence.id,
actionSequence.type,
actionItem.type);
expectedAction.pointerType = actionSequence.parameters.pointerType;
expectedAction.primary = actionSequence.parameters.primary;
expectedAction.button = actionItem.button;
- let actions = webdriveraction.processInputSourceActionSequence(actionSequence);
+ let actions = action.processInputSourceActionSequence(actionSequence);
equal(actions.length, 1);
deepEqual(actions[0], expectedAction);
run_next_test();
});
add_test(function test_processInputSourceActionSequenceKey() {
let actionItem = {
type: "keyUp",
value: 'a',
};
let actionSequence = {
type: "key",
id: "9",
actions: [actionItem],
};
- let expectedAction = new webdriveraction.Action(actionSequence.id,
+ let expectedAction = new action.Action(actionSequence.id,
actionSequence.type,
actionItem.type);
expectedAction.value = actionItem.value;
- let actions = webdriveraction.processInputSourceActionSequence(actionSequence);
+ let actions = action.processInputSourceActionSequence(actionSequence);
equal(actions.length, 1);
deepEqual(actions[0], expectedAction);
run_next_test();
});
add_test(function test_processInputSourceActionSequenceGenerateID() {
let actionItems = [
@@ -367,92 +369,92 @@ add_test(function test_processInputSourc
type: "pause",
duration: 5,
}
];
let actionSequence = {
type: "key",
actions: actionItems,
};
- let actions = webdriveraction.processInputSourceActionSequence(actionSequence);
+ let actions = action.processInputSourceActionSequence(actionSequence);
equal(typeof actions[0].id, "string");
equal(actions[0].id.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i)[0],
actions[0].id);
run_next_test();
});
add_test(function test_processInputSourceActionSequenceInputStateMap() {
let actionItem = {
type: "pause",
duration: 5,
};
let actionSequence = {
type: "pointer",
id: "1",
actions: [actionItem],
};
- wrongInputState = new webdriveraction.NullInputState();
+ let wrongInputState = new action.NullInputState();
inputStateMap.set(actionSequence.id, wrongInputState);
checkErrors(/already mapped to/,
- webdriveraction.processInputSourceActionSequence,
+ action.processInputSourceActionSequence,
[actionSequence],
`${actionSequence.type} using ${wrongInputState}`);
inputStateMap.clear();
run_next_test();
});
add_test(function test_processPointerActionInputStateMap() {
let actionItem = {
type: "pointerDown",
};
let id = 1;
let parameters = {
pointerType: "mouse",
primary: true,
};
- wrongInputState = new webdriveraction.PointerInputState("pause", true);
+ let wrongInputState = new action.PointerInputState("pause", true);
inputStateMap.set(id, wrongInputState)
checkErrors(/already mapped to InputState whose subtype/,
- webdriveraction.processPointerAction,
+ action.processPointerAction,
[id, parameters, actionItem],
`$subtype {actionItem.type} with ${wrongInputState.subtype} in inputState`);
inputStateMap.clear();
run_next_test();
});
add_test(function test_extractActionChainValidation() {
for (let actions of [-1, 'a', undefined, null]){
let message = `actions: ${getTypeString(actions)}`
- Assert.throws(() => webdriveraction.extractActionChain(actions),
+ Assert.throws(() => action.extractActionChain(actions),
InvalidArgumentError, message);
- Assert.throws(() => webdriveraction.extractActionChain(actions),
+ Assert.throws(() => action.extractActionChain(actions),
/is not an Array/,
message);
}
run_next_test();
});
add_test(function test_extractActionChain_empty() {
- deepEqual(webdriveraction.extractActionChain([]), []);
+ deepEqual(action.extractActionChain([]), []);
run_next_test();
});
add_test(function test_extractActionChain_oneTickOneInput() {
let actionItem = {
type: "pause",
duration: 5,
};
let actionSequence = {
type: "none",
id: "some id",
actions: [actionItem],
};
- let expectedAction = new webdriveraction.Action(actionSequence.id, "none", actionItem.type);
+ let expectedAction = new action.Action(actionSequence.id, "none", actionItem.type);
expectedAction.duration = actionItem.duration;
- let actionsByTick = webdriveraction.extractActionChain([actionSequence]);
+ let actionsByTick = action.extractActionChain([actionSequence]);
equal(1, actionsByTick.length);
equal(1, actionsByTick[0].length);
deepEqual(actionsByTick, [[expectedAction]]);
run_next_test();
});
add_test(function test_extractActionChain_twoAndThreeTicks() {
let mouseActionItems = [
@@ -488,30 +490,30 @@ add_test(function test_extractActionChai
value: 'a',
},
];
let keyActionSequence = {
type: "key",
id: "1",
actions: keyActionItems,
};
- let actionsByTick = webdriveraction.extractActionChain([keyActionSequence, mouseActionSequence]);
+ let actionsByTick = action.extractActionChain([keyActionSequence, mouseActionSequence]);
// number of ticks is same as longest action sequence
equal(keyActionItems.length, actionsByTick.length);
equal(2, actionsByTick[0].length);
equal(2, actionsByTick[1].length);
equal(1, actionsByTick[2].length);
- let expectedAction = new webdriveraction.Action(keyActionSequence.id,
+ let expectedAction = new action.Action(keyActionSequence.id,
"key",
keyActionItems[2].type);
expectedAction.value = keyActionItems[2].value;
deepEqual(actionsByTick[2][0], expectedAction);
// one empty action sequence
- actionsByTick = webdriveraction.extractActionChain([keyActionSequence,
+ actionsByTick = action.extractActionChain([keyActionSequence,
{type:"none", actions: []}]);
equal(keyActionItems.length, actionsByTick.length);
equal(1, actionsByTick[0].length);
run_next_test();
});
// helpers
function getTypeString(obj){
--- a/testing/marionette/unit.ini
+++ b/testing/marionette/unit.ini
@@ -2,13 +2,13 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# xpcshell unit tests for Marionette
[DEFAULT]
skip-if = appname == "thunderbird"
+[test_action.js]
[test_element.js]
[test_error.js]
[test_message.js]
[test_navigate.js]
-[test_actions.js]
deleted file mode 100644
--- a/testing/marionette/webdriverAction.js
+++ /dev/null
@@ -1,287 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
-
-Cu.import("resource://gre/modules/Log.jsm");
-
-Cu.import("chrome://marionette/content/element.js");
-Cu.import("chrome://marionette/content/error.js");
-
-
-this.EXPORTED_SYMBOLS = ["webdriveraction", "inputStateMap"];
-
-const logger = Log.repository.getLogger("Marionette");
-
-// map between input id (string) and device state for that input source
-// TODO associate with session
-// populated by "dispatch tick actions"?
-this.inputStateMap = new Map();
-
-
-const ACTION_TYPES = new Set([
- "none",
- "key",
- "pointer",
-]);
-
-this.webdriveraction = {}
-
-// input source: virtual device that has an string id and a source type
-// This doesn't seem to be really used in spec. Each "actionSequence" has id
-// and type already, which is all we really need.
-// webdriveraction.InputSource = function InputSource(id, type){
-// this.id = id;
-// // null, key, pointer
-// this.type = type;
-// }
-
-webdriveraction.NullInputState = function NullInputState(){
- // a named empty object
-}
-
-webdriveraction.NullInputState.prototype.toString = function(){
- return '[object NullInputState]';
-}
-
-webdriveraction.KeyInputState = function KeyInputState(){
- this.pressed = new Set();
- this.alt = false;
- this.shift = false;
- this.ctrl = false;
- this.meta = false;
-}
-
-webdriveraction.KeyInputState.prototype.toString = function(){
- return '[object KeyInputState]';
-}
-
-webdriveraction.PointerInputState = function PointerInputState(subtype, primary){
- this.pressed = new Set();
- this.subtype = subtype;
- this.primary = primary;
- this.x = 0;
- this.y = 0;
-}
-
-webdriveraction.PointerInputState.prototype.toString = function(){
- return '[object PointerInputState]';
-}
-
-const ACTION_INPUT_STATE = {
- none: webdriveraction.NullInputState,
- key: webdriveraction.KeyInputState,
- pointer: webdriveraction.PointerInputState,
-}
-
-webdriveraction.Action = function Action(id, type, subtype) {
- // represents action object for actionByTick
- this.id = id;
- this.type = type;
- this.subtype = subtype;
-};
-
-// actions: list of action sequences
-webdriveraction.extractActionChain = function extractActionChain(actions) {
- // TODO check current browsing context?
- if (typeof actions === 'undefined' || !Array.isArray(actions)){
- throw new InvalidArgumentError(`'actions' ${actions} is not an Array`);
- }
- let actionsByTick = [];
- for (let actionSequence of actions){
- let inputSourceActions = webdriveraction.processInputSourceActionSequence(actionSequence);
- for (var i = 0; i < inputSourceActions.length; i++){
- if (actionsByTick.length < i + 1){
- // new tick
- actionsByTick.push([]);
- }
- actionsByTick[i].push(inputSourceActions[i]);
- }
- }
- return actionsByTick;
-};
-
-// action_sequence has a list of actionItems for one input source
-webdriveraction.processInputSourceActionSequence = function processInputSourceActionSequence(
- actionSequence) {
- let type = actionSequence.type;
- if (!ACTION_TYPES.has(type)){
- throw new InvalidArgumentError(`'actionSequence.type' must be one of
- ${Array.from(ACTION_TYPES)}`);
- }
- let id = actionSequence.id;
- if (typeof id === 'undefined'){
- id = element.generateUUID();
- }
- else if (typeof id !== 'string'){
- throw new InvalidArgumentError(`'id' ${id} is not a string`);
- }
- let actionItems = actionSequence.actions;
- if (!Array.isArray(actionItems)){
- throw new InvalidArgumentError(`'actionSequence.actions' ${actionSequence.actions} is not an Array`);
- }
-
- let parameters;
- if (type === "pointer"){
- parameters = webdriveraction.processPointerParameters(actionSequence.parameters);
- }
-
- if (inputStateMap.has(id) && !(inputStateMap.get(id) instanceof ACTION_INPUT_STATE[type])){
- throw new InvalidArgumentError(
- `${id} is already mapped to ${inputStateMap.get(id)}`);
- }
-
- let actions = [];
- for (let actionItem of actionItems){
- if (Object.prototype.toString.call(actionItem) !== "[object Object]"){
- throw new InvalidArgumentError(`${actionItem} is not an object`);
- }
- let action = {};
- switch(type){
- case("none"):
- action = webdriveraction.processNullAction(id, actionItem);
- break;
- case("key"):
- action = webdriveraction.processKeyAction(id, actionItem);
- break;
- case("pointer"):
- action = webdriveraction.processPointerAction(id, parameters, actionItem);
- break;
- }
- actions.push(action);
- }
- return actions;
-};
-
-webdriveraction.processPointerParameters = function processPointerParameters(parametersData){
- let parameters = {
- pointerType: "mouse",
- primary: true,
- };
- if (typeof parametersData === 'undefined'){
- return parameters;
- }
- if (Object.prototype.toString.call(parametersData) !== "[object Object]"){
- throw new InvalidArgumentError(`parametersData ${parametersData} is not an object`);
- }
- let pointerType = parametersData.pointerType;
- if (typeof pointerType !== 'undefined'){
- let types = ["mouse", "pen", "touch"];
- if(!types.includes(pointerType)){
- throw new InvalidArgumentError(`pointerType must be one of ${types}`);
- }
- parameters.pointerType = pointerType;
- }
- let primary = parametersData.primary;
- if (typeof primary !== 'undefined'){
- if (typeof primary !== 'boolean'){
- throw new InvalidArgumentError(`primary ${primary} must be a boolean`);
- }
- parameters.primary = primary;
- }
- return parameters;
-};
-
-webdriveraction.processNullAction = function processNullAction(id, actionItem){
- let subtype = actionItem.type;
- if (subtype !== "pause"){
- throw new InvalidArgumentError("subtype must be 'pause'");
- }
- let action = new webdriveraction.Action(id, "none", subtype);
- webdriveraction.processPauseAction(actionItem, action);
- return action;
-};
-
-webdriveraction.processKeyAction = function processKeyAction(id, actionItem){
- let subtype = actionItem.type;
- let types = ["keyUp", "keyDown", "pause"];
- if (!types.includes(subtype)){
- throw new InvalidArgumentError(`subtype must be one of ${Array.from(types)}`);
- }
- let action = new webdriveraction.Action(id, "key", subtype);
- if (subtype === "pause"){
- webdriveraction.processPauseAction(actionItem, action);
- return action;
- }
- let key = actionItem.value
- // what about key codes like arrow, versus unicode chars?
- if (typeof key !== 'string' || (typeof key === 'string' && key.length !== 1)){
- throw new InvalidArgumentError("'key' must be a single-character String");
- }
- action.value = key;
- return action;
-};
-
-webdriveraction.processPointerAction = function processPointerAction(id, parameters, actionItem){
- let subtype = actionItem.type;
- let types = ["pause", "pointerUp", "pointerDown", "pointerMove", "pointerCancel"];
- if (!types.includes(subtype)){
- throw new InvalidArgumentError(`subtype must be one of ${Array.from(types)}`);
- }
-
- if (inputStateMap.has(id) && inputStateMap.get(id).subtype !== subtype){
- throw new InvalidArgumentError(
- `${id} is already mapped to InputState whose subtype is `
- + `${inputStateMap.get(id).subtype} not ${subtype}`);
- }
-
- let action = new webdriveraction.Action(id, "pointer", subtype);
- if (subtype === "pause"){
- webdriveraction.processPauseAction(actionItem, action);
- return action;
- }
- action.pointerType = parameters.pointerType;
- action.primary = parameters.primary;
- if (["pointerUp", "pointerDown"].includes(subtype)){
- webdriveraction.processPointerUpDownAction(actionItem, action);
- }
- if (subtype == "pointerMove"){
- webdriveraction.processPointerMoveAction(actionItem, action);
- }
- return action;
-};
-
-webdriveraction.processPauseAction = function processPauseAction(actionItem, action){
- action.duration = webdriveraction.validatePositiveNum(actionItem.duration, 'duration');
-};
-
-webdriveraction.processPointerUpDownAction = function processPointerUpDownAction(
- actionItem,
- action){
- action.button = webdriveraction.validatePositiveNum(actionItem.button, 'button');
-};
-
-webdriveraction.processPointerMoveAction = function processPointerMoveAction(actionItem, action){
- action.duration = webdriveraction.validatePositiveNum(actionItem.duration, 'duration');
- let webElement = actionItem.element;
- let isElement = function(el){
- let properties = Object.getOwnPropertyNames(el);
- return properties.includes(element.Key) || properties.includes(element.LegacyKey);
- }
- if (typeof webElement !== "undefined" && !isElement(webElement)){
- throw new InvalidArgumentError(
- "'actionItem.duration' must be an Object that represents a web element");
- }
- action.element = webElement;
-
- action.x = actionItem.x;
- if (typeof action.x !== "undefined"){
- action.x = webdriveraction.validatePositiveNum(actionItem.x, 'x');
- }
- action.y = actionItem.y;
- if (typeof action.y !== "undefined"){
- action.y = webdriveraction.validatePositiveNum(actionItem.y, 'y');
- }
-};
-
-// helpers
-webdriveraction.validatePositiveNum = function validatePositiveNum(value, name){
- let v = parseInt(value);
- if (isNaN(v) || v < 0){
- throw new InvalidArgumentError(`Value of '${name}' should be integer >= 0`);
- }
- return v;
-}