gecko-dev/testing/marionette/test_action.js
Andreas Tolfsen c8ef9c9e6b Bug 1400256 - Adapt actions for implicitly unmarshaled elements. r=automatedtester
Since web element references are now implicitly unmarshaled when
they are passed to the content frame script, there is no need for
the actions module to check that the element origin is a reference
and try to look it up from the known element store.

MozReview-Commit-ID: 3BGBIBQMtR3

--HG--
extra : rebase_source : 1505fe15b060f85ad34628f34f67cfe96aaaa9c1
2017-10-09 19:55:27 +01:00

636 lines
20 KiB
JavaScript

/* 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/action.js");
const {ContentWebElement} = Cu.import("chrome://marionette/content/element.js", {});
Cu.import("chrome://marionette/content/error.js");
const XHTMLNS = "http://www.w3.org/1999/xhtml";
const domEl = {
nodeType: 1,
ELEMENT_NODE: 1,
namespaceURI: XHTMLNS,
};
action.inputStateMap = new Map();
add_test(function test_createAction() {
Assert.throws(() => new action.Action(), InvalidArgumentError,
"Missing Action constructor args");
Assert.throws(() => new action.Action(1, 2), InvalidArgumentError,
"Missing Action constructor args");
Assert.throws(
() => new action.Action(1, 2, "sometype"), /Expected string/, "Non-string arguments.");
ok(new action.Action("id", "sometype", "sometype"));
run_next_test();
});
add_test(function test_defaultPointerParameters() {
let defaultParameters = {pointerType: action.PointerType.Mouse};
deepEqual(action.PointerParameters.fromJSON(), defaultParameters);
run_next_test();
});
add_test(function test_processPointerParameters() {
let check = (regex, message, arg) => checkErrors(
regex, action.PointerParameters.fromJSON, [arg], message);
let parametersData;
for (let d of ["foo", "", "get", "Get"]) {
parametersData = {pointerType: d};
let message = `parametersData: [pointerType: ${parametersData.pointerType}]`;
check(/Unknown pointerType/, message, parametersData);
}
parametersData.pointerType = "mouse"; //TODO "pen";
deepEqual(action.PointerParameters.fromJSON(parametersData),
{pointerType: "mouse"}); //TODO action.PointerType.Pen});
run_next_test();
});
add_test(function test_processPointerUpDownAction() {
let actionItem = {type: "pointerDown"};
let actionSequence = {type: "pointer", id: "some_id"};
for (let d of [-1, "a"]) {
actionItem.button = d;
checkErrors(
/Expected 'button' \(.*\) to be >= 0/, action.Action.fromJSON, [actionSequence, actionItem],
`button: ${actionItem.button}`);
}
actionItem.button = 5;
let act = action.Action.fromJSON(actionSequence, actionItem);
equal(act.button, actionItem.button);
run_next_test();
});
add_test(function test_validateActionDurationAndCoordinates() {
let actionItem = {};
let actionSequence = {id: "some_id"};
let check = function (type, subtype, message = undefined) {
message = message || `duration: ${actionItem.duration}, subtype: ${subtype}`;
actionItem.type = subtype;
actionSequence.type = type;
checkErrors(/Expected '.*' \(.*\) to be >= 0/,
action.Action.fromJSON, [actionSequence, actionItem], message);
};
for (let d of [-1, "a"]) {
actionItem.duration = d;
check("none", "pause");
check("pointer", "pointerMove");
}
actionItem.duration = 5000;
for (let name of ["x", "y"]) {
actionItem[name] = "a";
actionItem.type = "pointerMove";
actionSequence.type = "pointer";
checkErrors(/Expected '.*' \(.*\) to be an Integer/,
action.Action.fromJSON, [actionSequence, actionItem],
`duration: ${actionItem.duration}, subtype: pointerMove`);
}
run_next_test();
});
add_test(function test_processPointerMoveActionOriginValidation() {
let actionSequence = {type: "pointer", id: "some_id"};
let actionItem = {duration: 5000, type: "pointerMove"};
for (let d of [-1, {a: "blah"}, []]) {
actionItem.origin = d;
checkErrors(/Expected \'origin\' to be undefined, "viewport", "pointer", or an element/,
action.Action.fromJSON,
[actionSequence, actionItem],
`actionItem.origin: (${getTypeString(d)})`);
}
run_next_test();
});
add_test(function test_processPointerMoveActionOriginStringValidation() {
let actionSequence = {type: "pointer", id: "some_id"};
let actionItem = {duration: 5000, type: "pointerMove"};
for (let d of ["a", "", "get", "Get"]) {
actionItem.origin = d;
checkErrors(/Unknown pointer-move origin/,
action.Action.fromJSON,
[actionSequence, actionItem],
`actionItem.origin: ${d}`);
}
run_next_test();
});
add_test(function test_processPointerMoveActionElementOrigin() {
let actionSequence = {type: "pointer", id: "some_id"};
let actionItem = {duration: 5000, type: "pointerMove"};
actionItem.origin = domEl;
let a = action.Action.fromJSON(actionSequence, actionItem);
deepEqual(a.origin, actionItem.origin);
run_next_test();
});
add_test(function test_processPointerMoveActionDefaultOrigin() {
let actionSequence = {type: "pointer", id: "some_id"};
// origin left undefined
let actionItem = {duration: 5000, type: "pointerMove"};
let a = action.Action.fromJSON(actionSequence, actionItem);
deepEqual(a.origin, action.PointerOrigin.Viewport);
run_next_test();
});
add_test(function test_processPointerMoveAction() {
let actionSequence = {id: "some_id", type: "pointer"};
let actionItems = [
{
duration: 5000,
type: "pointerMove",
origin: undefined,
x: undefined,
y: undefined,
},
{
duration: undefined,
type: "pointerMove",
origin: domEl,
x: undefined,
y: undefined,
},
{
duration: 5000,
type: "pointerMove",
x: 0,
y: undefined,
origin: undefined,
},
{
duration: 5000,
type: "pointerMove",
x: 1,
y: 2,
origin: undefined,
},
];
for (let expected of actionItems) {
let actual = action.Action.fromJSON(actionSequence, expected);
ok(actual instanceof action.Action);
equal(actual.duration, expected.duration);
equal(actual.x, expected.x);
equal(actual.y, expected.y);
let origin = expected.origin;
if (typeof origin == "undefined") {
origin = action.PointerOrigin.Viewport;
}
deepEqual(actual.origin, origin);
}
run_next_test();
});
add_test(function test_computePointerDestinationViewport() {
let act = { type: "pointerMove", x: 100, y: 200, origin: "viewport"};
let inputState = new action.InputState.Pointer(action.PointerType.Mouse);
// these values should not affect the outcome
inputState.x = "99";
inputState.y = "10";
let target = action.computePointerDestination(act, inputState);
equal(act.x, target.x);
equal(act.y, target.y);
run_next_test();
});
add_test(function test_computePointerDestinationPointer() {
let act = { type: "pointerMove", x: 100, y: 200, origin: "pointer"};
let inputState = new action.InputState.Pointer(action.PointerType.Mouse);
inputState.x = 10;
inputState.y = 99;
let target = action.computePointerDestination(act, inputState);
equal(act.x + inputState.x, target.x);
equal(act.y + inputState.y, target.y);
run_next_test();
});
add_test(function test_computePointerDestinationElement() {
// origin represents a web element
// using an object literal instead to test default case in computePointerDestination
let act = {type: "pointerMove", x: 100, y: 200, origin: {}};
let inputState = new action.InputState.Pointer(action.PointerType.Mouse);
let elementCenter = {x: 10, y: 99};
let target = action.computePointerDestination(act, inputState, elementCenter);
equal(act.x + elementCenter.x, target.x);
equal(act.y + elementCenter.y, target.y);
Assert.throws(
() => action.computePointerDestination(act, inputState, {a: 1}),
InvalidArgumentError,
"Invalid element center coordinates.");
Assert.throws(
() => action.computePointerDestination(act, inputState, undefined),
InvalidArgumentError,
"Undefined element center coordinates.");
run_next_test();
});
add_test(function test_processPointerAction() {
let actionSequence = {
type: "pointer",
id: "some_id",
parameters: {
pointerType: "mouse" //TODO "touch"
},
};
let actionItems = [
{
duration: 2000,
type: "pause",
},
{
type: "pointerMove",
duration: 2000,
},
{
type: "pointerUp",
button: 1,
}
];
for (let expected of actionItems) {
let actual = action.Action.fromJSON(actionSequence, expected);
equal(actual.type, actionSequence.type);
equal(actual.subtype, expected.type);
equal(actual.id, actionSequence.id);
if (expected.type === "pointerUp") {
equal(actual.button, expected.button);
} else {
equal(actual.duration, expected.duration);
}
if (expected.type !== "pause") {
equal(actual.pointerType, actionSequence.parameters.pointerType);
}
}
run_next_test();
});
add_test(function test_processPauseAction() {
let actionItem = {type: "pause", duration: 5000};
let actionSequence = {id: "some_id"};
for (let type of ["none", "key", "pointer"]) {
actionSequence.type = type;
let act = action.Action.fromJSON(actionSequence, actionItem);
ok(act instanceof action.Action);
equal(act.type, type);
equal(act.subtype, actionItem.type);
equal(act.id, actionSequence.id);
equal(act.duration, actionItem.duration);
}
actionItem.duration = undefined;
let act = action.Action.fromJSON(actionSequence, actionItem);
equal(act.duration, actionItem.duration);
run_next_test();
});
add_test(function test_processActionSubtypeValidation() {
let actionItem = {type: "dancing"};
let actionSequence = {id: "some_id"};
let check = function (regex) {
let message = `type: ${actionSequence.type}, subtype: ${actionItem.type}`;
checkErrors(regex, action.Action.fromJSON, [actionSequence, actionItem], message);
};
for (let type of ["none", "key", "pointer"]) {
actionSequence.type = type;
check(new RegExp(`Unknown subtype for ${type} action`));
}
run_next_test();
});
add_test(function test_processKeyActionUpDown() {
let actionSequence = {type: "key", id: "some_id"};
let actionItem = {type: "keyDown"};
for (let v of [-1, undefined, [], ["a"], {length: 1}, null]) {
actionItem.value = v;
let message = `actionItem.value: (${getTypeString(v)})`;
Assert.throws(() => action.Action.fromJSON(actionSequence, actionItem),
InvalidArgumentError, message);
Assert.throws(() => action.Action.fromJSON(actionSequence, actionItem),
/Expected 'value' to be a string that represents single code point/, message);
}
actionItem.value = "\uE004";
let act = action.Action.fromJSON(actionSequence, actionItem);
ok(act instanceof action.Action);
equal(act.type, actionSequence.type);
equal(act.subtype, actionItem.type);
equal(act.id, actionSequence.id);
equal(act.value, actionItem.value);
run_next_test();
});
add_test(function test_processInputSourceActionSequenceValidation() {
let actionSequence = {type: "swim", id: "some id"};
let check = (message, regex) => checkErrors(
regex, action.Sequence.fromJSON, [actionSequence], message);
check(`actionSequence.type: ${actionSequence.type}`, /Unknown action type/);
action.inputStateMap.clear();
actionSequence.type = "none";
actionSequence.id = -1;
check(`actionSequence.id: ${getTypeString(actionSequence.id)}`,
/Expected 'id' to be a string/);
action.inputStateMap.clear();
actionSequence.id = undefined;
check(`actionSequence.id: ${getTypeString(actionSequence.id)}`,
/Expected 'id' to be defined/);
action.inputStateMap.clear();
actionSequence.id = "some_id";
actionSequence.actions = -1;
check(`actionSequence.actions: ${getTypeString(actionSequence.actions)}`,
/Expected 'actionSequence.actions' to be an array/);
action.inputStateMap.clear();
run_next_test();
});
add_test(function test_processInputSourceActionSequence() {
let actionItem = { type: "pause", duration: 5};
let actionSequence = {
type: "none",
id: "some id",
actions: [actionItem],
};
let expectedAction = new action.Action(actionSequence.id, "none", actionItem.type);
expectedAction.duration = actionItem.duration;
let actions = action.Sequence.fromJSON(actionSequence);
equal(actions.length, 1);
deepEqual(actions[0], expectedAction);
action.inputStateMap.clear();
run_next_test();
});
add_test(function test_processInputSourceActionSequencePointer() {
let actionItem = {type: "pointerDown", button: 1};
let actionSequence = {
type: "pointer",
id: "9",
actions: [actionItem],
parameters: {
pointerType: "mouse" // TODO "pen"
},
};
let expectedAction = new action.Action(
actionSequence.id, actionSequence.type, actionItem.type);
expectedAction.pointerType = actionSequence.parameters.pointerType;
expectedAction.button = actionItem.button;
let actions = action.Sequence.fromJSON(actionSequence);
equal(actions.length, 1);
deepEqual(actions[0], expectedAction);
action.inputStateMap.clear();
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 action.Action(
actionSequence.id, actionSequence.type, actionItem.type);
expectedAction.value = actionItem.value;
let actions = action.Sequence.fromJSON(actionSequence);
equal(actions.length, 1);
deepEqual(actions[0], expectedAction);
action.inputStateMap.clear();
run_next_test();
});
add_test(function test_processInputSourceActionSequenceInputStateMap() {
let id = "1";
let actionItem = {type: "pause", duration: 5000};
let actionSequence = {
type: "key",
id: id,
actions: [actionItem],
};
let wrongInputState = new action.InputState.Null();
action.inputStateMap.set(actionSequence.id, wrongInputState);
checkErrors(/to be mapped to/, action.Sequence.fromJSON, [actionSequence],
`${actionSequence.type} using ${wrongInputState}`);
action.inputStateMap.clear();
let rightInputState = new action.InputState.Key();
action.inputStateMap.set(id, rightInputState);
let acts = action.Sequence.fromJSON(actionSequence);
equal(acts.length, 1);
action.inputStateMap.clear();
run_next_test();
});
add_test(function test_processPointerActionInputStateMap() {
let actionItem = {type: "pointerDown"};
let id = "1";
let parameters = {pointerType: "mouse"};
let a = new action.Action(id, "pointer", actionItem.type);
let wrongInputState = new action.InputState.Key();
action.inputStateMap.set(id, wrongInputState);
checkErrors(
/to be mapped to InputState whose type is/, action.processPointerAction,
[id, parameters, a],
`type "pointer" with ${wrongInputState.type} in inputState`);
action.inputStateMap.clear();
// TODO - uncomment once pen is supported
//wrongInputState = new action.InputState.Pointer("pen");
//action.inputStateMap.set(id, wrongInputState);
//checkErrors(
// /to be mapped to InputState whose subtype is/, action.processPointerAction,
// [id, parameters, a],
// `subtype ${parameters.pointerType} with ${wrongInputState.subtype} in inputState`);
//action.inputStateMap.clear();
let rightInputState = new action.InputState.Pointer("mouse");
action.inputStateMap.set(id, rightInputState);
action.processPointerAction(id, parameters, a);
action.inputStateMap.clear();
run_next_test();
});
add_test(function test_createInputState() {
for (let kind in action.InputState) {
let state;
if (kind == "Pointer") {
state = new action.InputState[kind]("mouse");
} else {
state = new action.InputState[kind]();
}
ok(state);
if (kind === "Null") {
equal(state.type, "none");
} else {
equal(state.type, kind.toLowerCase());
}
}
Assert.throws(() => new action.InputState.Pointer(), InvalidArgumentError,
"Missing InputState.Pointer constructor arg");
Assert.throws(() => new action.InputState.Pointer("foo"), InvalidArgumentError,
"Invalid InputState.Pointer constructor arg");
run_next_test();
});
add_test(function test_extractActionChainValidation() {
for (let actions of [-1, "a", undefined, null]) {
let message = `actions: ${getTypeString(actions)}`;
Assert.throws(() => action.Chain.fromJSON(actions),
InvalidArgumentError, message);
Assert.throws(() => action.Chain.fromJSON(actions),
/Expected 'actions' to be an array/, message);
}
run_next_test();
});
add_test(function test_extractActionChainEmpty() {
deepEqual(action.Chain.fromJSON([]), []);
run_next_test();
});
add_test(function test_extractActionChain_oneTickOneInput() {
let actionItem = {type: "pause", duration: 5000};
let actionSequence = {
type: "none",
id: "some id",
actions: [actionItem],
};
let expectedAction = new action.Action(actionSequence.id, "none", actionItem.type);
expectedAction.duration = actionItem.duration;
let actionsByTick = action.Chain.fromJSON([actionSequence]);
equal(1, actionsByTick.length);
equal(1, actionsByTick[0].length);
deepEqual(actionsByTick, [[expectedAction]]);
action.inputStateMap.clear();
run_next_test();
});
add_test(function test_extractActionChain_twoAndThreeTicks() {
let mouseActionItems = [
{
type: "pointerDown",
button: 2,
},
{
type: "pointerUp",
button: 2,
},
];
let mouseActionSequence = {
type: "pointer",
id: "7",
actions: mouseActionItems,
parameters: {
pointerType: "mouse" //TODO "touch"
},
};
let keyActionItems = [
{
type: "keyDown",
value: "a",
},
{
type: "pause",
duration: 4,
},
{
type: "keyUp",
value: "a",
},
];
let keyActionSequence = {
type: "key",
id: "1",
actions: keyActionItems,
};
let actionsByTick = action.Chain.fromJSON([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 action.Action(keyActionSequence.id, "key", keyActionItems[2].type);
expectedAction.value = keyActionItems[2].value;
deepEqual(actionsByTick[2][0], expectedAction);
action.inputStateMap.clear();
// one empty action sequence
actionsByTick = action.Chain.fromJSON(
[keyActionSequence, {type: "none", id: "some", actions: []}]);
equal(keyActionItems.length, actionsByTick.length);
equal(1, actionsByTick[0].length);
action.inputStateMap.clear();
run_next_test();
});
add_test(function test_computeTickDuration() {
let expected = 8000;
let tickActions = [
{type: "none", subtype: "pause", duration: 5000},
{type: "key", subtype: "pause", duration: 1000},
{type: "pointer", subtype: "pointerMove", duration: 6000},
// invalid because keyDown should not have duration, so duration should be ignored.
{type: "key", subtype: "keyDown", duration: 100000},
{type: "pointer", subtype: "pause", duration: expected},
{type: "pointer", subtype: "pointerUp"},
];
equal(expected, action.computeTickDuration(tickActions));
run_next_test();
});
add_test(function test_computeTickDuration_empty() {
equal(0, action.computeTickDuration([]));
run_next_test();
});
add_test(function test_computeTickDuration_noDurations() {
let tickActions = [
// invalid because keyDown should not have duration, so duration should be ignored.
{type: "key", subtype: "keyDown", duration: 100000},
// undefined duration permitted
{type: "none", subtype: "pause"},
{type: "pointer", subtype: "pointerMove"},
{type: "pointer", subtype: "pointerDown"},
{type: "key", subtype: "keyUp"},
];
equal(0, action.computeTickDuration(tickActions));
run_next_test();
});
// helpers
function getTypeString(obj) {
return Object.prototype.toString.call(obj);
};
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);
};