--- a/testing/marionette/action.js
+++ b/testing/marionette/action.js
@@ -7,281 +7,285 @@
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 = ["action", "inputStateMap"];
+this.EXPORTED_SYMBOLS = ["action"];
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();
+this.action = {};
-
-const ACTION_TYPES = new Set([
+action.Types = new Set([
"none",
"key",
"pointer",
]);
-this.action = {}
+// map between input id (string) and device state for that input source
+// TODO associate with session
+// populated by "dispatch tick actions"?
+action.inputStateMap = new Map();
// 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){
+// 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(){
+action.NullInputState.prototype.toString = function() {
return '[object NullInputState]';
-}
+};
-action.KeyInputState = function KeyInputState(){
+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(){
+action.KeyInputState.prototype.toString = function() {
return '[object KeyInputState]';
-}
+};
-action.PointerInputState = function PointerInputState(subtype, primary){
+action.PointerInputState = function PointerInputState(subtype, primary) {
this.pressed = new Set();
this.subtype = subtype;
this.primary = primary;
this.x = 0;
this.y = 0;
-}
+};
-action.PointerInputState.prototype.toString = function(){
+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;
};
// 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`);
+ if (!Array.isArray(actions)) {
+ throw new InvalidArgumentError(`Expected 'actions' to be an Array, got: ${actions}`);
}
let actionsByTick = [];
- for (let actionSequence of actions){
+ 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
+ for (let i = 0; i < inputSourceActions.length; i++) {
+ // new tick
+ if (actionsByTick.length < i + 1) {
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)}`);
+ if (!action.Types.has(type)) {
+ throw new InvalidArgumentError(`Invalid 'actionSequence.type', got: ${type}`);
}
let id = actionSequence.id;
- if (typeof id === 'undefined'){
+ if (typeof id == 'undefined') {
id = element.generateUUID();
- }
- else if (typeof id !== 'string'){
- throw new InvalidArgumentError(`'id' ${id} is not a string`);
+ } else if (typeof id != 'string') {
+ throw new InvalidArgumentError(`Expected 'id' to be a string, got: ${id}`);
}
let actionItems = actionSequence.actions;
- if (!Array.isArray(actionItems)){
- throw new InvalidArgumentError(`'actionSequence.actions' ${actionSequence.actions} is not an Array`);
+ if (!Array.isArray(actionItems)) {
+ throw new InvalidArgumentError(
+ `Expected 'actionSequence.actions' to be an Array, got: ${actionSequence.actions}`);
}
- let parameters;
- if (type === "pointer"){
- parameters = action.processPointerParameters(actionSequence.parameters);
+ let pointerParams;
+ if (type === "pointer") {
+ pointerParams = action.processPointerParameters(actionSequence.parameters);
}
- if (inputStateMap.has(id) && !(inputStateMap.get(id) instanceof ACTION_INPUT_STATE[type])){
+ if (action.inputStateMap.has(id) &&
+ !(action.inputStateMap.get(id) instanceof ACTION_INPUT_STATE[type])) {
throw new InvalidArgumentError(
- `${id} is already mapped to ${inputStateMap.get(id)}`);
+ `Expected ${id} to be mapped to ${ACTION_INPUT_STATE[type].name}, ` +
+ `got: ${action.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`);
- }
+ for (let actionItem of actionItems) {
let act;
- switch(type){
- case("none"):
+ switch(type) {
+ case "none":
act = action.processNullAction(id, actionItem);
break;
- case("key"):
+ case "key":
act = action.processKeyAction(id, actionItem);
break;
- case("pointer"):
- act = action.processPointerAction(id, parameters, actionItem);
+ case "pointer":
+ act = action.processPointerAction(id, pointerParams, actionItem);
break;
}
actions.push(act);
}
return actions;
};
-action.processPointerParameters = function processPointerParameters(parametersData){
- let parameters = {
+action.processPointerParameters = function processPointerParameters(parametersData) {
+ let pointerParams = {
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`);
+ if (typeof parametersData == 'undefined') {
+ return pointerParams;
}
let pointerType = parametersData.pointerType;
- if (typeof pointerType !== 'undefined'){
+ if (typeof pointerType != 'undefined') {
let types = ["mouse", "pen", "touch"];
if(!types.includes(pointerType)){
- throw new InvalidArgumentError(`pointerType must be one of ${types}`);
+ throw new InvalidArgumentError(
+ `Expected 'pointerType' to be one of ${types}, got: ${pointerType}`);
}
- parameters.pointerType = pointerType;
+ pointerParams.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;
+ if (typeof primary != 'undefined') {
+ assertBoolean(primary, 'primary');
+ pointerParams.primary = primary;
}
- return parameters;
+ return pointerParams;
};
-action.processNullAction = function processNullAction(id, actionItem){
+action.processNullAction = function processNullAction(id, actionItem) {
let subtype = actionItem.type;
- if (subtype !== "pause"){
- throw new InvalidArgumentError("subtype must be 'pause'");
+ if (subtype !== "pause") {
+ throw new InvalidArgumentError("Expected 'subtype' to be 'pause', got: " + subtype);
}
let act = new action.Action(id, "none", subtype);
action.processPauseAction(actionItem, act);
return act;
};
-action.processKeyAction = function processKeyAction(id, actionItem){
+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)}`);
+ throw new InvalidArgumentError(`Expected 'subtype' to be one of ${Array.from(types)}, ` +
+ `got: ${subtype}`);
}
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");
+ if (typeof key != 'string' || (typeof key == 'string' && key.length != 1)) {
+ throw new InvalidArgumentError("Expected 'key' to be a single-character String, " +
+ "got: " + key);
}
act.value = key;
return act;
};
-action.processPointerAction = function processPointerAction(id, parameters, actionItem){
+action.processPointerAction = function processPointerAction(id, pointerParams, 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 (!types.includes(subtype)) {
+ throw new InvalidArgumentError(`Expected 'subtype' to be one of ${Array.from(types)}, ` +
+ `got ${subtype}`);
}
- if (inputStateMap.has(id) && inputStateMap.get(id).subtype !== subtype){
+ if (action.inputStateMap.has(id) && action.inputStateMap.get(id).subtype !== subtype) {
throw new InvalidArgumentError(
- `${id} is already mapped to InputState whose subtype is `
- + `${inputStateMap.get(id).subtype} not ${subtype}`);
+ `Expected 'id' ${id} to be mapped to InputState whose subtype is ` +
+ `${action.inputStateMap.get(id).subtype}, got: ${subtype}`);
}
let act = new action.Action(id, "pointer", subtype);
- if (subtype === "pause"){
+ if (subtype === "pause") {
action.processPauseAction(actionItem, act);
return act;
}
- act.pointerType = parameters.pointerType;
- act.primary = parameters.primary;
- if (["pointerUp", "pointerDown"].includes(subtype)){
+ act.pointerType = pointerParams.pointerType;
+ act.primary = pointerParams.primary;
+ if (["pointerUp", "pointerDown"].includes(subtype)) {
action.processPointerUpDownAction(actionItem, act);
}
if (subtype == "pointerMove"){
action.processPointerMoveAction(actionItem, act);
}
return act;
};
-action.processPauseAction = function processPauseAction(actionItem, act){
- act.duration = action.validatePositiveNum(actionItem.duration, 'duration');
+action.processPauseAction = function processPauseAction(actionItem, act) {
+ assertPositiveInteger(actionItem.duration, 'duration');
+ act.duration = actionItem.duration;
};
action.processPointerUpDownAction = function processPointerUpDownAction(
actionItem,
- act){
- act.button = action.validatePositiveNum(actionItem.button, 'button');
+ act) {
+ assertPositiveInteger(actionItem.button, 'button');
+ act.button = actionItem.button;
};
-action.processPointerMoveAction = function processPointerMoveAction(actionItem, act){
- act.duration = action.validatePositiveNum(actionItem.duration, 'duration');
+action.processPointerMoveAction = function processPointerMoveAction(actionItem, act) {
+ assertPositiveInteger(actionItem.duration, 'duration');
+ act.duration = actionItem.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)){
+ if (typeof webElement != "undefined" && !isElement(webElement)) {
throw new InvalidArgumentError(
- "'actionItem.duration' must be an Object that represents a web element");
+ "Expected 'actionItem.element' to be an Object that " +
+ `represents a web element, got: ${webElement}`);
}
act.element = webElement;
act.x = actionItem.x;
- if (typeof act.x !== "undefined"){
- act.x = action.validatePositiveNum(actionItem.x, 'x');
+ if (typeof act.x != "undefined") {
+ assertPositiveInteger(actionItem.x, 'x');
}
act.y = actionItem.y;
- if (typeof act.y !== "undefined"){
- act.y = action.validatePositiveNum(actionItem.y, 'y');
+ if (typeof act.y != "undefined") {
+ assertPositiveInteger(actionItem.y, 'y');
}
};
// 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`);
+function assertPositiveInteger(value, name = undefined) {
+ if (!Number.isInteger(value) || value < 0) {
+ throw new InvalidArgumentError(`Expected '${name}' to be an integer >= 0, got: ${value}`);
}
- return v;
}
+
+function assertBoolean(value, name = undefined) {
+ if (typeof(value) != "boolean") {
+ throw new InvalidArgumentError(`Expected '${name}' to be a boolean, got: ${value}`);
+ }
+}
--- a/testing/marionette/test_action.js
+++ b/testing/marionette/test_action.js
@@ -14,44 +14,40 @@ add_test(function test_defaultPointerPar
let defaultParameters = {pointerType: "mouse", primary: true};
deepEqual(action.processPointerParameters(), defaultParameters);
deepEqual(action.processPointerParameters({blah: 'nonsense'}), defaultParameters);
run_next_test();
});
add_test(function test_processPointerParameters() {
- let check = function(regex, message, args){
+ let check = function(regex, message, args) {
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]);
+ check(/Expected 'pointerType' to be one of/, message, [parametersData]);
parametersData.pointerType = "pen";
parametersData.primary = 'a';
- check(/must be a boolean/, message, [parametersData]);
+ check(/Expected 'primary' to be a boolean/, message, [parametersData]);
parametersData.primary = false;
deepEqual(action.processPointerParameters(parametersData),
{pointerType: "pen", primary: false});
run_next_test();
});
add_test(function test_processPointerUpDownAction() {
let actionItem = {};
- for (let d of [-1, 'a']){
+ for (let d of [-1, 'a']) {
actionItem = {button: d};
- checkErrors(/should be integer >= 0/,
+ checkErrors(/to be an integer >= 0/,
action.processPointerUpDownAction,
[actionItem, {}],
`button: ${actionItem.button}`);
}
actionItem.button = 5;
let act = new action.Action("some id", "mouse", "pointerDown");
action.processPointerUpDownAction(actionItem, act);
equal(act.button, actionItem.button);
@@ -66,46 +62,46 @@ add_test(function test_processPauseActio
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 check = function(actionFunc, message) {
let m = message + `; actionFunc: ${actionFunc.name}`;
- checkErrors(/should be integer >= 0/,
+ checkErrors(/to be an integer >= 0/,
actionFunc, [actionItem, {}], m);
};
- for (let d of [-1, 'a', undefined]){
+ for (let d of [-1, 'a', undefined]) {
actionItem.duration = d;
let message = `duration: ${actionItem.duration}`;
check(action.processPauseAction, message);
check(action.processPointerMoveAction, message);
}
actionItem.duration = 5;
- for (let d of [-1, 'a']){
- for (let name of ['x', 'y']){
+ for (let d of [-1, 'a']) {
+ for (let name of ['x', 'y']) {
actionItem[name] = d;
let message = `${name}: ${actionItem[name]}`;
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"}]){
+ for (let d of [-1, 'a', {a:"blah"}]) {
actionItem.element = d;
- checkErrors(/must be an Object that represents a web element/,
+ checkErrors(/Expected 'actionItem.element' to be an Object that represents a web element/,
action.processPointerMoveAction,
[actionItem, {}],
`actionItem.element: (${getTypeString(d)})`);
}
run_next_test();
});
@@ -136,17 +132,17 @@ add_test(function test_processPointerMov
{
duration: 5,
type: "pointerMove",
x: 1,
y: 2,
element: undefined,
},
];
- for (let a of actionItems){
+ for (let a of actionItems) {
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);
}
@@ -169,29 +165,29 @@ add_test(function test_processPointerAct
duration: 2,
},
{
type: "pointerUp",
button: 1,
}
];
let id = "some_id";
- for (let a of actionItems){
+ for (let a of actionItems) {
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"){
+ if (a.type === "pointerUp") {
equal(act.button, a.button);
}
else {
equal(act.duration, a.duration);
}
- if (a.type !== "pause"){
+ if (a.type !== "pause") {
equal(act.primary, pointerParams.primary);
equal(act.pointerType, pointerParams.pointerType);
}
}
run_next_test();
});
@@ -209,23 +205,23 @@ add_test(function test_processNullAction
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 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'/, action.processNullAction);
- check(/subtype must be one of/, action.processKeyAction);
- check(/subtype must be one of/,
+ check(/Expected 'subtype' to be 'pause'/, action.processNullAction);
+ check(/Expected 'subtype' to be one of/, action.processKeyAction);
+ check(/Expected 'subtype' to be one of/,
action.processPointerAction,
[id, [], actionItem]);
run_next_test();
});
add_test(function test_processKeyActionPause() {
let actionItem = {};
@@ -244,24 +240,24 @@ add_test(function test_processKeyActionP
});
add_test(function test_processKeyActionUpDown() {
let actionItem = {
type: "keyDown",
};
let id = "some_id";
- for (let d of [-1, 'bad', undefined, [], ['a'], {length: 1}, null]){
+ for (let d of [-1, 'bad', undefined, [], ['a'], {length: 1}, null]) {
actionItem.value = d;
let message = `actionItem.value: (${getTypeString(d)})`;
Assert.throws(() => action.processKeyAction(id, actionItem),
InvalidArgumentError, message);
Assert.throws(() => action.processKeyAction(id, actionItem),
- /'key' must be a single-character String/,
- message);
+ /Expected 'key' to be a single-character String/,
+ message);
}
actionItem.value = 'a';
let act = action.processKeyAction(id, actionItem);
ok(act instanceof action.Action);
equal(act.type, "key");
equal(act.subtype, actionItem.type);
equal(act.id, id);
@@ -271,34 +267,32 @@ add_test(function test_processKeyActionU
});
add_test(function test_processInputSourceActionSequenceValidation() {
var actionSequence = {
type: "swim",
id: "some id",
};
- let check = function(message, regex){
+ let check = function(message, regex) {
checkErrors(regex, action.processInputSourceActionSequence,
[actionSequence], message);
};
- check(`actionSequence.type: ${actionSequence.type}`, /type' must be one of/);
+ check(`actionSequence.type: ${actionSequence.type}`,
+ /Invalid 'actionSequence\.type'/);
actionSequence.type = "none";
actionSequence.id = -1;
- check(`actionSequence.id: ${getTypeString(actionSequence.id)}`, /is not a string/);
+ check(`actionSequence.id: ${getTypeString(actionSequence.id)}`,
+ /Expected 'id' to be a string/);
actionSequence.id = "some_id";
actionSequence.actions = -1;
- check(`actionSequence.actions: ${getTypeString(actionSequence.actions)}`, /not an Array/);
-
- for (let a of [[2], [[1]], [/blah/]]){
- actionSequence.actions = a;
- check(`first action: ${getTypeString(actionSequence.actions[0])}`, /not an object/);
- }
+ check(`actionSequence.actions: ${getTypeString(actionSequence.actions)}`,
+ /Expected 'actionSequence.actions' to be an Array/);
run_next_test();
});
add_test(function test_processInputSourceActionSequence() {
let actionItem = {
type: "pause",
duration: 5,
@@ -387,51 +381,51 @@ add_test(function test_processInputSourc
duration: 5,
};
let actionSequence = {
type: "pointer",
id: "1",
actions: [actionItem],
};
let wrongInputState = new action.NullInputState();
- inputStateMap.set(actionSequence.id, wrongInputState);
- checkErrors(/already mapped to/,
+ action.inputStateMap.set(actionSequence.id, wrongInputState);
+ checkErrors(/to be mapped to/,
action.processInputSourceActionSequence,
[actionSequence],
`${actionSequence.type} using ${wrongInputState}`);
- inputStateMap.clear();
+ action.inputStateMap.clear();
run_next_test();
});
add_test(function test_processPointerActionInputStateMap() {
let actionItem = {
type: "pointerDown",
};
let id = 1;
let parameters = {
pointerType: "mouse",
primary: true,
};
let wrongInputState = new action.PointerInputState("pause", true);
- inputStateMap.set(id, wrongInputState)
- checkErrors(/already mapped to InputState whose subtype/,
+ action.inputStateMap.set(id, wrongInputState)
+ checkErrors(/to be mapped to InputState whose subtype is/,
action.processPointerAction,
[id, parameters, actionItem],
`$subtype {actionItem.type} with ${wrongInputState.subtype} in inputState`);
- inputStateMap.clear();
+ action.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(() => action.extractActionChain(actions),
InvalidArgumentError, message);
Assert.throws(() => action.extractActionChain(actions),
- /is not an Array/,
+ /Expected 'actions' to be an Array/,
message);
}
run_next_test();
});
add_test(function test_extractActionChain_empty() {
deepEqual(action.extractActionChain([]), []);
run_next_test();
@@ -511,19 +505,19 @@ add_test(function test_extractActionChai
actionsByTick = action.extractActionChain([keyActionSequence,
{type:"none", actions: []}]);
equal(keyActionItems.length, actionsByTick.length);
equal(1, actionsByTick[0].length);
run_next_test();
});
// helpers
-function getTypeString(obj){
+function getTypeString(obj) {
return Object.prototype.toString.call(obj);
-}
+};
-function checkErrors(regex, func, args, message){
- if (typeof message === 'undefined'){
+function checkErrors(regex, func, args, message) {
+ if (typeof message === 'undefined') {
message = `actionFunc: ${func.name}; args: ${args}`;
}
Assert.throws(() => func.apply(this, args), InvalidArgumentError, message);
Assert.throws(() => func.apply(this, args), regex, message);
};