837 lines
22 KiB
JavaScript
837 lines
22 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 { actions, CLICK_INTERVAL, ClickTracker } = ChromeUtils.importESModule(
|
|
"chrome://remote/content/shared/webdriver/Actions.sys.mjs"
|
|
);
|
|
|
|
const { setTimeout } = ChromeUtils.importESModule(
|
|
"resource://gre/modules/Timer.sys.mjs"
|
|
);
|
|
|
|
const XHTMLNS = "http://www.w3.org/1999/xhtml";
|
|
|
|
const domEl = {
|
|
nodeType: 1,
|
|
ELEMENT_NODE: 1,
|
|
namespaceURI: XHTMLNS,
|
|
};
|
|
|
|
add_task(function test_createInputState() {
|
|
for (let type of ["none", "key", "pointer" /*"wheel"*/]) {
|
|
const state = new actions.State();
|
|
const id = "device";
|
|
const actionSequence = {
|
|
type,
|
|
id,
|
|
actions: [],
|
|
};
|
|
actions.Chain.fromJSON(state, [actionSequence]);
|
|
equal(state.inputStateMap.size, 1);
|
|
equal(state.inputStateMap.get(id).constructor.type, type);
|
|
}
|
|
});
|
|
|
|
add_task(async function test_defaultPointerParameters() {
|
|
let state = new actions.State();
|
|
const inputTickActions = [
|
|
{ type: "pointer", subtype: "pointerDown", button: 0 },
|
|
];
|
|
const chain = await actions.Chain.fromJSON(
|
|
state,
|
|
chainForTick(inputTickActions)
|
|
);
|
|
const pointerAction = chain[0][0];
|
|
equal(
|
|
state.getInputSource(pointerAction.id).pointer.constructor.type,
|
|
"mouse"
|
|
);
|
|
});
|
|
|
|
add_task(async function test_processPointerParameters() {
|
|
for (let subtype of ["pointerDown", "pointerUp"]) {
|
|
for (let pointerType of [2, true, {}, []]) {
|
|
const inputTickActions = [
|
|
{
|
|
type: "pointer",
|
|
parameters: { pointerType },
|
|
subtype,
|
|
button: 0,
|
|
},
|
|
];
|
|
let message = `Action sequence with parameters: {pointerType: ${pointerType} subtype: ${subtype}}`;
|
|
await checkFromJSONErrors(
|
|
inputTickActions,
|
|
/Expected "pointerType" to be a string/,
|
|
message
|
|
);
|
|
}
|
|
|
|
for (let pointerType of ["", "foo"]) {
|
|
const inputTickActions = [
|
|
{
|
|
type: "pointer",
|
|
parameters: { pointerType },
|
|
subtype,
|
|
button: 0,
|
|
},
|
|
];
|
|
let message = `Action sequence with parameters: {pointerType: ${pointerType} subtype: ${subtype}}`;
|
|
await checkFromJSONErrors(
|
|
inputTickActions,
|
|
/Expected "pointerType" to be one of/,
|
|
message
|
|
);
|
|
}
|
|
}
|
|
|
|
for (let pointerType of ["mouse" /*"touch"*/]) {
|
|
let state = new actions.State();
|
|
const inputTickActions = [
|
|
{
|
|
type: "pointer",
|
|
parameters: { pointerType },
|
|
subtype: "pointerDown",
|
|
button: 0,
|
|
},
|
|
];
|
|
const chain = await actions.Chain.fromJSON(
|
|
state,
|
|
chainForTick(inputTickActions)
|
|
);
|
|
const pointerAction = chain[0][0];
|
|
equal(
|
|
state.getInputSource(pointerAction.id).pointer.constructor.type,
|
|
pointerType
|
|
);
|
|
}
|
|
});
|
|
|
|
add_task(async function test_processPointerDownAction() {
|
|
for (let button of [-1, "a"]) {
|
|
const inputTickActions = [
|
|
{ type: "pointer", subtype: "pointerDown", button },
|
|
];
|
|
await checkFromJSONErrors(
|
|
inputTickActions,
|
|
/Expected "button" to be a positive integer/,
|
|
`pointerDown with {button: ${button}}`
|
|
);
|
|
}
|
|
let state = new actions.State();
|
|
const inputTickActions = [
|
|
{ type: "pointer", subtype: "pointerDown", button: 5 },
|
|
];
|
|
const chain = await actions.Chain.fromJSON(
|
|
state,
|
|
chainForTick(inputTickActions)
|
|
);
|
|
equal(chain[0][0].button, 5);
|
|
});
|
|
|
|
add_task(async function test_validateActionDurationAndCoordinates() {
|
|
for (let [type, subtype] of [
|
|
["none", "pause"],
|
|
["pointer", "pointerMove"],
|
|
]) {
|
|
for (let duration of [-1, "a"]) {
|
|
const inputTickActions = [{ type, subtype, duration }];
|
|
await checkFromJSONErrors(
|
|
inputTickActions,
|
|
/Expected "duration" to be a positive integer/,
|
|
`{subtype} with {duration: ${duration}}`
|
|
);
|
|
}
|
|
}
|
|
for (let name of ["x", "y"]) {
|
|
const actionItem = {
|
|
type: "pointer",
|
|
subtype: "pointerMove",
|
|
duration: 5000,
|
|
};
|
|
actionItem[name] = "a";
|
|
await checkFromJSONErrors(
|
|
[actionItem],
|
|
/Expected "." to be a finite number/,
|
|
`${name}: "a", subtype: pointerMove`
|
|
);
|
|
}
|
|
});
|
|
|
|
add_task(async function test_processPointerMoveActionOriginStringValidation() {
|
|
for (let origin of ["", "viewports", "pointers"]) {
|
|
const inputTickActions = [
|
|
{
|
|
type: "pointer",
|
|
x: 0,
|
|
y: 0,
|
|
duration: 5000,
|
|
subtype: "pointerMove",
|
|
origin,
|
|
},
|
|
];
|
|
await checkFromJSONErrors(
|
|
inputTickActions,
|
|
/Expected "origin" to be undefined, "viewport", "pointer", or an element/,
|
|
`actionItem.origin: ${origin}`,
|
|
{ isElementOrigin: () => false }
|
|
);
|
|
}
|
|
});
|
|
|
|
add_task(async function test_processPointerMoveActionOriginElementValidation() {
|
|
const element = { foo: "bar" };
|
|
const inputTickActions = [
|
|
{
|
|
type: "pointer",
|
|
x: 0,
|
|
y: 0,
|
|
duration: 5000,
|
|
subtype: "pointerMove",
|
|
origin: element,
|
|
},
|
|
];
|
|
|
|
// invalid element origin
|
|
await checkFromJSONErrors(
|
|
inputTickActions,
|
|
/Expected "origin" to be undefined, "viewport", "pointer", or an element/,
|
|
`actionItem.origin: (${getTypeString(element)})`,
|
|
{ isElementOrigin: elem => "foo1" in elem }
|
|
);
|
|
|
|
let state = new actions.State();
|
|
const actionsOptions = {
|
|
isElementOrigin: elem => "foo" in elem,
|
|
getElementOrigin: elem => elem,
|
|
};
|
|
|
|
// valid element origin
|
|
const chain = await actions.Chain.fromJSON(
|
|
state,
|
|
chainForTick(inputTickActions),
|
|
actionsOptions
|
|
);
|
|
deepEqual(chain[0][0].origin, { element });
|
|
});
|
|
|
|
add_task(async function test_processPointerMoveActionDefaultOrigin() {
|
|
let state = new actions.State();
|
|
const inputTickActions = [
|
|
{ type: "pointer", x: 0, y: 0, duration: 5000, subtype: "pointerMove" },
|
|
];
|
|
const chain = await actions.Chain.fromJSON(
|
|
state,
|
|
chainForTick(inputTickActions),
|
|
{}
|
|
);
|
|
// The default is viewport coordinates which have an origin at [0,0] and don't depend on inputSource
|
|
deepEqual(chain[0][0].origin.getOriginCoordinates(null, null), {
|
|
x: 0,
|
|
y: 0,
|
|
});
|
|
});
|
|
|
|
add_task(async function test_processPointerMoveAction() {
|
|
let state = new actions.State();
|
|
const actionItems = [
|
|
{
|
|
duration: 5000,
|
|
type: "pointerMove",
|
|
origin: undefined,
|
|
x: 0,
|
|
y: 0,
|
|
},
|
|
{
|
|
duration: undefined,
|
|
type: "pointerMove",
|
|
origin: domEl,
|
|
x: 0,
|
|
y: 0,
|
|
},
|
|
{
|
|
duration: 5000,
|
|
type: "pointerMove",
|
|
x: 1,
|
|
y: 2,
|
|
origin: undefined,
|
|
},
|
|
];
|
|
const actionSequence = {
|
|
id: "some_id",
|
|
type: "pointer",
|
|
actions: actionItems,
|
|
};
|
|
let actionsOptions = {
|
|
isElementOrigin: elem => elem == domEl,
|
|
getElementOrigin: elem => elem,
|
|
};
|
|
|
|
let chain = await actions.Chain.fromJSON(
|
|
state,
|
|
[actionSequence],
|
|
actionsOptions
|
|
);
|
|
equal(chain.length, actionItems.length);
|
|
for (let i = 0; i < actionItems.length; i++) {
|
|
let actual = chain[i][0];
|
|
let expected = actionItems[i];
|
|
equal(actual.duration, expected.duration);
|
|
equal(actual.x, expected.x);
|
|
equal(actual.y, expected.y);
|
|
|
|
let originClass;
|
|
if (expected.origin === undefined || expected.origin == "viewport") {
|
|
originClass = "ViewportOrigin";
|
|
} else if (expected.origin === "pointer") {
|
|
originClass = "PointerOrigin";
|
|
} else {
|
|
originClass = "ElementOrigin";
|
|
}
|
|
deepEqual(actual.origin.constructor.name, originClass);
|
|
}
|
|
});
|
|
|
|
add_task(async function test_computePointerDestinationViewport() {
|
|
const state = new actions.State();
|
|
const inputTickActions = [
|
|
{
|
|
type: "pointer",
|
|
subtype: "pointerMove",
|
|
x: 100,
|
|
y: 200,
|
|
origin: "viewport",
|
|
},
|
|
];
|
|
const chain = await actions.Chain.fromJSON(
|
|
state,
|
|
chainForTick(inputTickActions),
|
|
{}
|
|
);
|
|
const actionItem = chain[0][0];
|
|
const inputSource = state.getInputSource(actionItem.id);
|
|
// these values should not affect the outcome
|
|
inputSource.x = "99";
|
|
inputSource.y = "10";
|
|
const target = await actionItem.origin.getTargetCoordinates(
|
|
inputSource,
|
|
[actionItem.x, actionItem.y],
|
|
null
|
|
);
|
|
equal(actionItem.x, target[0]);
|
|
equal(actionItem.y, target[1]);
|
|
});
|
|
|
|
add_task(async function test_computePointerDestinationPointer() {
|
|
const state = new actions.State();
|
|
const inputTickActions = [
|
|
{
|
|
type: "pointer",
|
|
subtype: "pointerMove",
|
|
x: 100,
|
|
y: 200,
|
|
origin: "pointer",
|
|
},
|
|
];
|
|
const chain = await actions.Chain.fromJSON(
|
|
state,
|
|
chainForTick(inputTickActions),
|
|
{}
|
|
);
|
|
const actionItem = chain[0][0];
|
|
const inputSource = state.getInputSource(actionItem.id);
|
|
inputSource.x = 10;
|
|
inputSource.y = 99;
|
|
const target = await actionItem.origin.getTargetCoordinates(
|
|
inputSource,
|
|
[actionItem.x, actionItem.y],
|
|
null
|
|
);
|
|
equal(actionItem.x + inputSource.x, target[0]);
|
|
equal(actionItem.y + inputSource.y, target[1]);
|
|
});
|
|
|
|
add_task(async function test_processPointerAction() {
|
|
for (let pointerType of ["mouse", "touch"]) {
|
|
const actionItems = [
|
|
{
|
|
duration: 2000,
|
|
type: "pause",
|
|
},
|
|
{
|
|
type: "pointerMove",
|
|
duration: 2000,
|
|
x: 0,
|
|
y: 0,
|
|
},
|
|
{
|
|
type: "pointerUp",
|
|
button: 1,
|
|
},
|
|
];
|
|
let actionSequence = {
|
|
type: "pointer",
|
|
id: "some_id",
|
|
parameters: {
|
|
pointerType,
|
|
},
|
|
actions: actionItems,
|
|
};
|
|
const state = new actions.State();
|
|
const chain = await actions.Chain.fromJSON(state, [actionSequence], {});
|
|
equal(chain.length, actionItems.length);
|
|
for (let i = 0; i < actionItems.length; i++) {
|
|
const actual = chain[i][0];
|
|
const expected = actionItems[i];
|
|
equal(actual.type, expected.type === "pause" ? "none" : "pointer");
|
|
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(
|
|
state.getInputSource(actual.id).pointer.constructor.type,
|
|
pointerType
|
|
);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
add_task(async function test_processPauseAction() {
|
|
for (let type of ["none", "key", "pointer"]) {
|
|
const state = new actions.State();
|
|
const actionSequence = {
|
|
type,
|
|
id: "some_id",
|
|
actions: [{ type: "pause", duration: 5000 }],
|
|
};
|
|
const chain = await actions.Chain.fromJSON(state, [actionSequence], {});
|
|
const actionItem = chain[0][0];
|
|
equal(actionItem.type, "none");
|
|
equal(actionItem.subtype, "pause");
|
|
equal(actionItem.id, "some_id");
|
|
equal(actionItem.duration, 5000);
|
|
}
|
|
const state = new actions.State();
|
|
const actionSequence = {
|
|
type: "none",
|
|
id: "some_id",
|
|
actions: [{ type: "pause" }],
|
|
};
|
|
const chain = await actions.Chain.fromJSON(state, [actionSequence], {});
|
|
const actionItem = chain[0][0];
|
|
equal(actionItem.duration, undefined);
|
|
});
|
|
|
|
add_task(async function test_processActionSubtypeValidation() {
|
|
for (let type of ["none", "key", "pointer"]) {
|
|
const message = `type: ${type}, subtype: dancing`;
|
|
const inputTickActions = [{ type, subtype: "dancing" }];
|
|
await checkFromJSONErrors(
|
|
inputTickActions,
|
|
new RegExp(`Expected known subtype for type`),
|
|
message
|
|
);
|
|
}
|
|
});
|
|
|
|
add_task(async function test_processKeyActionDown() {
|
|
for (let value of [-1, undefined, [], ["a"], { length: 1 }, null]) {
|
|
const inputTickActions = [{ type: "key", subtype: "keyDown", value }];
|
|
const message = `actionItem.value: (${getTypeString(value)})`;
|
|
await checkFromJSONErrors(
|
|
inputTickActions,
|
|
/Expected "value" to be a string that represents single code point/,
|
|
message
|
|
);
|
|
}
|
|
|
|
const state = new actions.State();
|
|
const actionSequence = {
|
|
type: "key",
|
|
id: "keyboard",
|
|
actions: [{ type: "keyDown", value: "\uE004" }],
|
|
};
|
|
const chain = await actions.Chain.fromJSON(state, [actionSequence], {});
|
|
const actionItem = chain[0][0];
|
|
|
|
equal(actionItem.type, "key");
|
|
equal(actionItem.id, "keyboard");
|
|
equal(actionItem.subtype, "keyDown");
|
|
equal(actionItem.value, "\ue004");
|
|
});
|
|
|
|
add_task(async function test_processInputSourceActionSequenceValidation() {
|
|
await checkFromJSONErrors(
|
|
[{ type: "swim", subtype: "pause", id: "some id" }],
|
|
/Expected known action type/,
|
|
"actionSequence type: swim"
|
|
);
|
|
|
|
await checkFromJSONErrors(
|
|
[{ type: "none", subtype: "pause", id: -1 }],
|
|
/Expected "id" to be a string/,
|
|
"actionSequence id: -1"
|
|
);
|
|
|
|
await checkFromJSONErrors(
|
|
[{ type: "none", subtype: "pause", id: undefined }],
|
|
/Expected "id" to be a string/,
|
|
"actionSequence id: undefined"
|
|
);
|
|
|
|
const state = new actions.State();
|
|
const actionSequence = [
|
|
{ type: "none", subtype: "pause", id: "some_id", actions: -1 },
|
|
];
|
|
const errorRegex = /Expected "actionSequence.actions" to be an array/;
|
|
const message = "actionSequence actions: -1";
|
|
|
|
await Assert.rejects(
|
|
actions.Chain.fromJSON(state, actionSequence, {}),
|
|
/InvalidArgumentError/,
|
|
message
|
|
);
|
|
await Assert.rejects(
|
|
actions.Chain.fromJSON(state, actionSequence, {}),
|
|
errorRegex,
|
|
message
|
|
);
|
|
});
|
|
|
|
add_task(async function test_processInputSourceActionSequence() {
|
|
const state = new actions.State();
|
|
const actionItem = { type: "pause", duration: 5 };
|
|
const actionSequence = {
|
|
type: "none",
|
|
id: "some id",
|
|
actions: [actionItem],
|
|
};
|
|
const chain = await actions.Chain.fromJSON(state, [actionSequence], {});
|
|
equal(chain.length, 1);
|
|
const tickActions = chain[0];
|
|
equal(tickActions.length, 1);
|
|
equal(tickActions[0].type, "none");
|
|
equal(tickActions[0].subtype, "pause");
|
|
equal(tickActions[0].duration, 5);
|
|
equal(tickActions[0].id, "some id");
|
|
});
|
|
|
|
add_task(async function test_processInputSourceActionSequencePointer() {
|
|
const state = new actions.State();
|
|
const actionItem = { type: "pointerDown", button: 1 };
|
|
const actionSequence = {
|
|
type: "pointer",
|
|
id: "9",
|
|
actions: [actionItem],
|
|
parameters: {
|
|
pointerType: "mouse", // TODO "pen"
|
|
},
|
|
};
|
|
const chain = await actions.Chain.fromJSON(state, [actionSequence], {});
|
|
equal(chain.length, 1);
|
|
const tickActions = chain[0];
|
|
equal(tickActions.length, 1);
|
|
equal(tickActions[0].type, "pointer");
|
|
equal(tickActions[0].subtype, "pointerDown");
|
|
equal(tickActions[0].button, 1);
|
|
equal(tickActions[0].id, "9");
|
|
const inputSource = state.getInputSource(tickActions[0].id);
|
|
equal(inputSource.constructor.type, "pointer");
|
|
equal(inputSource.pointer.constructor.type, "mouse");
|
|
});
|
|
|
|
add_task(async function test_processInputSourceActionSequenceKey() {
|
|
const state = new actions.State();
|
|
const actionItem = { type: "keyUp", value: "a" };
|
|
const actionSequence = {
|
|
type: "key",
|
|
id: "9",
|
|
actions: [actionItem],
|
|
};
|
|
const chain = await actions.Chain.fromJSON(state, [actionSequence], {});
|
|
equal(chain.length, 1);
|
|
const tickActions = chain[0];
|
|
equal(tickActions.length, 1);
|
|
equal(tickActions[0].type, "key");
|
|
equal(tickActions[0].subtype, "keyUp");
|
|
equal(tickActions[0].value, "a");
|
|
equal(tickActions[0].id, "9");
|
|
});
|
|
|
|
add_task(async function test_processInputSourceActionSequenceInputStateMap() {
|
|
const state = new actions.State();
|
|
const id = "1";
|
|
const actionItem = { type: "pause", duration: 5000 };
|
|
const actionSequence = {
|
|
type: "key",
|
|
id,
|
|
actions: [actionItem],
|
|
};
|
|
await actions.Chain.fromJSON(state, [actionSequence], {});
|
|
equal(state.inputStateMap.size, 1);
|
|
equal(state.inputStateMap.get(id).constructor.type, "key");
|
|
|
|
// Construct a different state with the same input id
|
|
const state1 = new actions.State();
|
|
const actionItem1 = { type: "pointerDown", button: 0 };
|
|
const actionSequence1 = {
|
|
type: "pointer",
|
|
id,
|
|
actions: [actionItem1],
|
|
};
|
|
await actions.Chain.fromJSON(state1, [actionSequence1], {});
|
|
equal(state1.inputStateMap.size, 1);
|
|
|
|
// Overwrite the state in the initial map with one of a different type
|
|
state.inputStateMap.set(id, state1.inputStateMap.get(id));
|
|
equal(state.inputStateMap.get(id).constructor.type, "pointer");
|
|
|
|
const message = "Wrong state for input id type";
|
|
await Assert.rejects(
|
|
actions.Chain.fromJSON(state, [actionSequence]),
|
|
/InvalidArgumentError/,
|
|
message
|
|
);
|
|
await Assert.rejects(
|
|
actions.Chain.fromJSON(state, [actionSequence]),
|
|
/Expected input source \[object String\] "1" to be type pointer/,
|
|
message
|
|
);
|
|
});
|
|
|
|
add_task(async function test_extractActionChainValidation() {
|
|
for (let action of [-1, "a", undefined, null]) {
|
|
const state = new actions.State();
|
|
let message = `actions: ${getTypeString(action)}`;
|
|
await Assert.rejects(
|
|
actions.Chain.fromJSON(state, action),
|
|
/InvalidArgumentError/,
|
|
message
|
|
);
|
|
await Assert.rejects(
|
|
actions.Chain.fromJSON(state, action),
|
|
/Expected "actions" to be an array/,
|
|
message
|
|
);
|
|
}
|
|
});
|
|
|
|
add_task(async function test_extractActionChainEmpty() {
|
|
const state = new actions.State();
|
|
deepEqual(await actions.Chain.fromJSON(state, [], {}), []);
|
|
});
|
|
|
|
add_task(async function test_extractActionChain_oneTickOneInput() {
|
|
const state = new actions.State();
|
|
const actionItem = { type: "pause", duration: 5000 };
|
|
const actionSequence = {
|
|
type: "none",
|
|
id: "some id",
|
|
actions: [actionItem],
|
|
};
|
|
const actionsByTick = await actions.Chain.fromJSON(
|
|
state,
|
|
[actionSequence],
|
|
{}
|
|
);
|
|
equal(1, actionsByTick.length);
|
|
equal(1, actionsByTick[0].length);
|
|
equal(actionsByTick[0][0].id, actionSequence.id);
|
|
equal(actionsByTick[0][0].type, "none");
|
|
equal(actionsByTick[0][0].subtype, "pause");
|
|
equal(actionsByTick[0][0].duration, actionItem.duration);
|
|
});
|
|
|
|
add_task(async function test_extractActionChain_twoAndThreeTicks() {
|
|
const state = new actions.State();
|
|
const mouseActionItems = [
|
|
{
|
|
type: "pointerDown",
|
|
button: 2,
|
|
},
|
|
{
|
|
type: "pointerUp",
|
|
button: 2,
|
|
},
|
|
];
|
|
const mouseActionSequence = {
|
|
type: "pointer",
|
|
id: "7",
|
|
actions: mouseActionItems,
|
|
parameters: {
|
|
pointerType: "mouse",
|
|
},
|
|
};
|
|
const keyActionItems = [
|
|
{
|
|
type: "keyDown",
|
|
value: "a",
|
|
},
|
|
{
|
|
type: "pause",
|
|
duration: 4,
|
|
},
|
|
{
|
|
type: "keyUp",
|
|
value: "a",
|
|
},
|
|
];
|
|
let keyActionSequence = {
|
|
type: "key",
|
|
id: "1",
|
|
actions: keyActionItems,
|
|
};
|
|
let actionsByTick = await actions.Chain.fromJSON(
|
|
state,
|
|
[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);
|
|
|
|
equal(actionsByTick[2][0].id, keyActionSequence.id);
|
|
equal(actionsByTick[2][0].type, "key");
|
|
equal(actionsByTick[2][0].subtype, "keyUp");
|
|
});
|
|
|
|
add_task(async function test_computeTickDuration() {
|
|
const state = new actions.State();
|
|
const expected = 8000;
|
|
const inputTickActions = [
|
|
{ type: "none", subtype: "pause", duration: 5000 },
|
|
{ type: "key", subtype: "pause", duration: 1000 },
|
|
{ type: "pointer", subtype: "pointerMove", duration: 6000, x: 0, y: 0 },
|
|
// invalid because keyDown should not have duration, so duration should be ignored.
|
|
{ type: "key", subtype: "keyDown", duration: 100000, value: "a" },
|
|
{ type: "pointer", subtype: "pause", duration: expected },
|
|
{ type: "pointer", subtype: "pointerUp", button: 0 },
|
|
];
|
|
const chain = await actions.Chain.fromJSON(
|
|
state,
|
|
chainForTick(inputTickActions),
|
|
{}
|
|
);
|
|
equal(1, chain.length);
|
|
const tickActions = chain[0];
|
|
equal(expected, tickActions.getDuration());
|
|
});
|
|
|
|
add_task(async function test_computeTickDuration_noDurations() {
|
|
const state = new actions.State();
|
|
const inputTickActions = [
|
|
// invalid because keyDown should not have duration, so duration should be ignored.
|
|
{ type: "key", subtype: "keyDown", duration: 100000, value: "a" },
|
|
// undefined duration permitted
|
|
{ type: "none", subtype: "pause" },
|
|
{ type: "pointer", subtype: "pointerMove", button: 0, x: 0, y: 0 },
|
|
{ type: "pointer", subtype: "pointerDown", button: 0 },
|
|
{ type: "key", subtype: "keyUp", value: "a" },
|
|
];
|
|
const chain = await actions.Chain.fromJSON(
|
|
state,
|
|
chainForTick(inputTickActions),
|
|
{}
|
|
);
|
|
equal(0, chain[0].getDuration());
|
|
});
|
|
|
|
add_task(function test_ClickTracker_setClick() {
|
|
const clickTracker = new ClickTracker();
|
|
const button1 = 1;
|
|
const button2 = 2;
|
|
|
|
clickTracker.setClick(button1);
|
|
equal(1, clickTracker.count);
|
|
|
|
// Make sure that clicking different mouse buttons doesn't increase the count.
|
|
clickTracker.setClick(button2);
|
|
equal(1, clickTracker.count);
|
|
|
|
clickTracker.setClick(button2);
|
|
equal(2, clickTracker.count);
|
|
|
|
clickTracker.reset();
|
|
equal(0, clickTracker.count);
|
|
});
|
|
|
|
add_task(function test_ClickTracker_reset_after_timeout() {
|
|
const clickTracker = new ClickTracker();
|
|
|
|
clickTracker.setClick(1);
|
|
equal(1, clickTracker.count);
|
|
|
|
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
|
|
setTimeout(() => equal(0, clickTracker.count), CLICK_INTERVAL + 10);
|
|
});
|
|
|
|
// helpers
|
|
function getTypeString(obj) {
|
|
return Object.prototype.toString.call(obj);
|
|
}
|
|
|
|
async function checkFromJSONErrors(
|
|
inputTickActions,
|
|
regex,
|
|
message,
|
|
options = {}
|
|
) {
|
|
const { isElementOrigin = () => true, getElementOrigin = elem => elem } =
|
|
options;
|
|
|
|
const state = new actions.State();
|
|
const actionsOptions = { isElementOrigin, getElementOrigin };
|
|
|
|
if (typeof message == "undefined") {
|
|
message = `fromJSON`;
|
|
}
|
|
|
|
await Assert.rejects(
|
|
actions.Chain.fromJSON(
|
|
state,
|
|
chainForTick(inputTickActions),
|
|
actionsOptions
|
|
),
|
|
/InvalidArgumentError/,
|
|
message
|
|
);
|
|
await Assert.rejects(
|
|
actions.Chain.fromJSON(
|
|
state,
|
|
chainForTick(inputTickActions),
|
|
actionsOptions
|
|
),
|
|
regex,
|
|
message
|
|
);
|
|
}
|
|
|
|
function chainForTick(tickActions) {
|
|
const actions = [];
|
|
let lastId = 0;
|
|
for (let { type, subtype, parameters, ...props } of tickActions) {
|
|
let id;
|
|
if (!props.hasOwnProperty("id")) {
|
|
id = `${type}_${lastId++}`;
|
|
} else {
|
|
id = props.id;
|
|
delete props.id;
|
|
}
|
|
const inputAction = { type, id, actions: [{ type: subtype, ...props }] };
|
|
if (parameters !== undefined) {
|
|
inputAction.parameters = parameters;
|
|
}
|
|
actions.push(inputAction);
|
|
}
|
|
return actions;
|
|
}
|