summaryrefslogtreecommitdiffstats
path: root/test/wpt/tests/resources/testdriver-actions.js
diff options
context:
space:
mode:
Diffstat (limited to 'test/wpt/tests/resources/testdriver-actions.js')
-rw-r--r--test/wpt/tests/resources/testdriver-actions.js599
1 files changed, 599 insertions, 0 deletions
diff --git a/test/wpt/tests/resources/testdriver-actions.js b/test/wpt/tests/resources/testdriver-actions.js
new file mode 100644
index 0000000..3e5ba74
--- /dev/null
+++ b/test/wpt/tests/resources/testdriver-actions.js
@@ -0,0 +1,599 @@
+(function() {
+ let sourceNameIdx = 0;
+
+ /**
+ * @class
+ * Builder for creating a sequence of actions
+ *
+ *
+ * The actions are dispatched once
+ * :js:func:`test_driver.Actions.send` is called. This returns a
+ * promise which resolves once the actions are complete.
+ *
+ * The other methods on :js:class:`test_driver.Actions` object are
+ * used to build the sequence of actions that will be sent. These
+ * return the `Actions` object itself, so the actions sequence can
+ * be constructed by chaining method calls.
+ *
+ * Internally :js:func:`test_driver.Actions.send` invokes
+ * :js:func:`test_driver.action_sequence`.
+ *
+ * @example
+ * let text_box = document.getElementById("text");
+ *
+ * let actions = new test_driver.Actions()
+ * .pointerMove(0, 0, {origin: text_box})
+ * .pointerDown()
+ * .pointerUp()
+ * .addTick()
+ * .keyDown("p")
+ * .keyUp("p");
+ *
+ * await actions.send();
+ *
+ * @param {number} [defaultTickDuration] - The default duration of a
+ * tick. Be default this is set ot 16ms, which is one frame time
+ * based on 60Hz display.
+ */
+ function Actions(defaultTickDuration=16) {
+ this.sourceTypes = new Map([["key", KeySource],
+ ["pointer", PointerSource],
+ ["wheel", WheelSource],
+ ["none", GeneralSource]]);
+ this.sources = new Map();
+ this.sourceOrder = [];
+ for (let sourceType of this.sourceTypes.keys()) {
+ this.sources.set(sourceType, new Map());
+ }
+ this.currentSources = new Map();
+ for (let sourceType of this.sourceTypes.keys()) {
+ this.currentSources.set(sourceType, null);
+ }
+ this.createSource("none");
+ this.tickIdx = 0;
+ this.defaultTickDuration = defaultTickDuration;
+ this.context = null;
+ }
+
+ Actions.prototype = {
+ ButtonType: {
+ LEFT: 0,
+ MIDDLE: 1,
+ RIGHT: 2,
+ BACK: 3,
+ FORWARD: 4,
+ },
+
+ /**
+ * Generate the action sequence suitable for passing to
+ * test_driver.action_sequence
+ *
+ * @returns {Array} Array of WebDriver-compatible actions sequences
+ */
+ serialize: function() {
+ let actions = [];
+ for (let [sourceType, sourceName] of this.sourceOrder) {
+ let source = this.sources.get(sourceType).get(sourceName);
+ let serialized = source.serialize(this.tickIdx + 1, this.defaultTickDuration);
+ if (serialized) {
+ serialized.id = sourceName;
+ actions.push(serialized);
+ }
+ }
+ return actions;
+ },
+
+ /**
+ * Generate and send the action sequence
+ *
+ * @returns {Promise} fulfilled after the sequence is executed,
+ * rejected if any actions fail.
+ */
+ send: function() {
+ let actions;
+ try {
+ actions = this.serialize();
+ } catch(e) {
+ return Promise.reject(e);
+ }
+ return test_driver.action_sequence(actions, this.context);
+ },
+
+ /**
+ * Set the context for the actions
+ *
+ * @param {WindowProxy} context - Context in which to run the action sequence
+ */
+ setContext: function(context) {
+ this.context = context;
+ return this;
+ },
+
+ /**
+ * Get the action source with a particular source type and name.
+ * If no name is passed, a new source with the given type is
+ * created.
+ *
+ * @param {String} type - Source type ('none', 'key', 'pointer', or 'wheel')
+ * @param {String?} name - Name of the source
+ * @returns {Source} Source object for that source.
+ */
+ getSource: function(type, name) {
+ if (!this.sources.has(type)) {
+ throw new Error(`${type} is not a valid action type`);
+ }
+ if (name === null || name === undefined) {
+ name = this.currentSources.get(type);
+ }
+ if (name === null || name === undefined) {
+ return this.createSource(type, null);
+ }
+ return this.sources.get(type).get(name);
+ },
+
+ setSource: function(type, name) {
+ if (!this.sources.has(type)) {
+ throw new Error(`${type} is not a valid action type`);
+ }
+ if (!this.sources.get(type).has(name)) {
+ throw new Error(`${name} is not a valid source for ${type}`);
+ }
+ this.currentSources.set(type, name);
+ return this;
+ },
+
+ /**
+ * Add a new key input source with the given name
+ *
+ * @param {String} name - Name of the key source
+ * @param {Bool} set - Set source as the default key source
+ * @returns {Actions}
+ */
+ addKeyboard: function(name, set=true) {
+ this.createSource("key", name);
+ if (set) {
+ this.setKeyboard(name);
+ }
+ return this;
+ },
+
+ /**
+ * Set the current default key source
+ *
+ * @param {String} name - Name of the key source
+ * @returns {Actions}
+ */
+ setKeyboard: function(name) {
+ this.setSource("key", name);
+ return this;
+ },
+
+ /**
+ * Add a new pointer input source with the given name
+ *
+ * @param {String} type - Name of the pointer source
+ * @param {String} pointerType - Type of pointing device
+ * @param {Bool} set - Set source as the default pointer source
+ * @returns {Actions}
+ */
+ addPointer: function(name, pointerType="mouse", set=true) {
+ this.createSource("pointer", name, {pointerType: pointerType});
+ if (set) {
+ this.setPointer(name);
+ }
+ return this;
+ },
+
+ /**
+ * Set the current default pointer source
+ *
+ * @param {String} name - Name of the pointer source
+ * @returns {Actions}
+ */
+ setPointer: function(name) {
+ this.setSource("pointer", name);
+ return this;
+ },
+
+ /**
+ * Add a new wheel input source with the given name
+ *
+ * @param {String} type - Name of the wheel source
+ * @param {Bool} set - Set source as the default wheel source
+ * @returns {Actions}
+ */
+ addWheel: function(name, set=true) {
+ this.createSource("wheel", name);
+ if (set) {
+ this.setWheel(name);
+ }
+ return this;
+ },
+
+ /**
+ * Set the current default wheel source
+ *
+ * @param {String} name - Name of the wheel source
+ * @returns {Actions}
+ */
+ setWheel: function(name) {
+ this.setSource("wheel", name);
+ return this;
+ },
+
+ createSource: function(type, name, parameters={}) {
+ if (!this.sources.has(type)) {
+ throw new Error(`${type} is not a valid action type`);
+ }
+ let sourceNames = new Set();
+ for (let [_, name] of this.sourceOrder) {
+ sourceNames.add(name);
+ }
+ if (!name) {
+ do {
+ name = "" + sourceNameIdx++;
+ } while (sourceNames.has(name))
+ } else {
+ if (sourceNames.has(name)) {
+ throw new Error(`Alreay have a source of type ${type} named ${name}.`);
+ }
+ }
+ this.sources.get(type).set(name, new (this.sourceTypes.get(type))(parameters));
+ this.currentSources.set(type, name);
+ this.sourceOrder.push([type, name]);
+ return this.sources.get(type).get(name);
+ },
+
+ /**
+ * Insert a new actions tick
+ *
+ * @param {Number?} duration - Minimum length of the tick in ms.
+ * @returns {Actions}
+ */
+ addTick: function(duration) {
+ this.tickIdx += 1;
+ if (duration) {
+ this.pause(duration);
+ }
+ return this;
+ },
+
+ /**
+ * Add a pause to the current tick
+ *
+ * @param {Number?} duration - Minimum length of the tick in ms.
+ * @param {String} sourceType - source type
+ * @param {String?} sourceName - Named key, pointer or wheel source to use
+ * or null for the default key, pointer or
+ * wheel source
+ * @returns {Actions}
+ */
+ pause: function(duration=0, sourceType="none", {sourceName=null}={}) {
+ if (sourceType=="none")
+ this.getSource("none").addPause(this, duration);
+ else
+ this.getSource(sourceType, sourceName).addPause(this, duration);
+ return this;
+ },
+
+ /**
+ * Create a keyDown event for the current default key source
+ *
+ * @param {String} key - Key to press
+ * @param {String?} sourceName - Named key source to use or null for the default key source
+ * @returns {Actions}
+ */
+ keyDown: function(key, {sourceName=null}={}) {
+ let source = this.getSource("key", sourceName);
+ source.keyDown(this, key);
+ return this;
+ },
+
+ /**
+ * Create a keyDown event for the current default key source
+ *
+ * @param {String} key - Key to release
+ * @param {String?} sourceName - Named key source to use or null for the default key source
+ * @returns {Actions}
+ */
+ keyUp: function(key, {sourceName=null}={}) {
+ let source = this.getSource("key", sourceName);
+ source.keyUp(this, key);
+ return this;
+ },
+
+ /**
+ * Create a pointerDown event for the current default pointer source
+ *
+ * @param {String} button - Button to press
+ * @param {String?} sourceName - Named pointer source to use or null for the default
+ * pointer source
+ * @returns {Actions}
+ */
+ pointerDown: function({button=this.ButtonType.LEFT, sourceName=null,
+ width, height, pressure, tangentialPressure,
+ tiltX, tiltY, twist, altitudeAngle, azimuthAngle}={}) {
+ let source = this.getSource("pointer", sourceName);
+ source.pointerDown(this, button, width, height, pressure, tangentialPressure,
+ tiltX, tiltY, twist, altitudeAngle, azimuthAngle);
+ return this;
+ },
+
+ /**
+ * Create a pointerUp event for the current default pointer source
+ *
+ * @param {String} button - Button to release
+ * @param {String?} sourceName - Named pointer source to use or null for the default pointer
+ * source
+ * @returns {Actions}
+ */
+ pointerUp: function({button=this.ButtonType.LEFT, sourceName=null}={}) {
+ let source = this.getSource("pointer", sourceName);
+ source.pointerUp(this, button);
+ return this;
+ },
+
+ /**
+ * Create a move event for the current default pointer source
+ *
+ * @param {Number} x - Destination x coordinate
+ * @param {Number} y - Destination y coordinate
+ * @param {String|Element} origin - Origin of the coordinate system.
+ * Either "pointer", "viewport" or an Element
+ * @param {Number?} duration - Time in ms for the move
+ * @param {String?} sourceName - Named pointer source to use or null for the default pointer
+ * source
+ * @returns {Actions}
+ */
+ pointerMove: function(x, y,
+ {origin="viewport", duration, sourceName=null,
+ width, height, pressure, tangentialPressure,
+ tiltX, tiltY, twist, altitudeAngle, azimuthAngle}={}) {
+ let source = this.getSource("pointer", sourceName);
+ source.pointerMove(this, x, y, duration, origin, width, height, pressure,
+ tangentialPressure, tiltX, tiltY, twist, altitudeAngle,
+ azimuthAngle);
+ return this;
+ },
+
+ /**
+ * Create a scroll event for the current default wheel source
+ *
+ * @param {Number} x - mouse cursor x coordinate
+ * @param {Number} y - mouse cursor y coordinate
+ * @param {Number} deltaX - scroll delta value along the x-axis in pixels
+ * @param {Number} deltaY - scroll delta value along the y-axis in pixels
+ * @param {String|Element} origin - Origin of the coordinate system.
+ * Either "viewport" or an Element
+ * @param {Number?} duration - Time in ms for the scroll
+ * @param {String?} sourceName - Named wheel source to use or null for the
+ * default wheel source
+ * @returns {Actions}
+ */
+ scroll: function(x, y, deltaX, deltaY,
+ {origin="viewport", duration, sourceName=null}={}) {
+ let source = this.getSource("wheel", sourceName);
+ source.scroll(this, x, y, deltaX, deltaY, duration, origin);
+ return this;
+ },
+ };
+
+ function GeneralSource() {
+ this.actions = new Map();
+ }
+
+ GeneralSource.prototype = {
+ serialize: function(tickCount, defaultTickDuration) {
+ let actions = [];
+ let data = {"type": "none", "actions": actions};
+ for (let i=0; i<tickCount; i++) {
+ if (this.actions.has(i)) {
+ actions.push(this.actions.get(i));
+ } else {
+ actions.push({"type": "pause", duration: defaultTickDuration});
+ }
+ }
+ return data;
+ },
+
+ addPause: function(actions, duration) {
+ let tick = actions.tickIdx;
+ if (this.actions.has(tick)) {
+ throw new Error(`Already have a pause action for the current tick`);
+ }
+ this.actions.set(tick, {type: "pause", duration: duration});
+ },
+ };
+
+ function KeySource() {
+ this.actions = new Map();
+ }
+
+ KeySource.prototype = {
+ serialize: function(tickCount) {
+ if (!this.actions.size) {
+ return undefined;
+ }
+ let actions = [];
+ let data = {"type": "key", "actions": actions};
+ for (let i=0; i<tickCount; i++) {
+ if (this.actions.has(i)) {
+ actions.push(this.actions.get(i));
+ } else {
+ actions.push({"type": "pause"});
+ }
+ }
+ return data;
+ },
+
+ keyDown: function(actions, key) {
+ let tick = actions.tickIdx;
+ if (this.actions.has(tick)) {
+ tick = actions.addTick().tickIdx;
+ }
+ this.actions.set(tick, {type: "keyDown", value: key});
+ },
+
+ keyUp: function(actions, key) {
+ let tick = actions.tickIdx;
+ if (this.actions.has(tick)) {
+ tick = actions.addTick().tickIdx;
+ }
+ this.actions.set(tick, {type: "keyUp", value: key});
+ },
+
+ addPause: function(actions, duration) {
+ let tick = actions.tickIdx;
+ if (this.actions.has(tick)) {
+ tick = actions.addTick().tickIdx;
+ }
+ this.actions.set(tick, {type: "pause", duration: duration});
+ },
+ };
+
+ function PointerSource(parameters={pointerType: "mouse"}) {
+ let pointerType = parameters.pointerType || "mouse";
+ if (!["mouse", "pen", "touch"].includes(pointerType)) {
+ throw new Error(`Invalid pointerType ${pointerType}`);
+ }
+ this.type = pointerType;
+ this.actions = new Map();
+ }
+
+ function setPointerProperties(action, width, height, pressure, tangentialPressure,
+ tiltX, tiltY, twist, altitudeAngle, azimuthAngle) {
+ if (width) {
+ action.width = width;
+ }
+ if (height) {
+ action.height = height;
+ }
+ if (pressure) {
+ action.pressure = pressure;
+ }
+ if (tangentialPressure) {
+ action.tangentialPressure = tangentialPressure;
+ }
+ if (tiltX) {
+ action.tiltX = tiltX;
+ }
+ if (tiltY) {
+ action.tiltY = tiltY;
+ }
+ if (twist) {
+ action.twist = twist;
+ }
+ if (altitudeAngle) {
+ action.altitudeAngle = altitudeAngle;
+ }
+ if (azimuthAngle) {
+ action.azimuthAngle = azimuthAngle;
+ }
+ return action;
+ }
+
+ PointerSource.prototype = {
+ serialize: function(tickCount) {
+ if (!this.actions.size) {
+ return undefined;
+ }
+ let actions = [];
+ let data = {"type": "pointer", "actions": actions, "parameters": {"pointerType": this.type}};
+ for (let i=0; i<tickCount; i++) {
+ if (this.actions.has(i)) {
+ actions.push(this.actions.get(i));
+ } else {
+ actions.push({"type": "pause"});
+ }
+ }
+ return data;
+ },
+
+ pointerDown: function(actions, button, width, height, pressure, tangentialPressure,
+ tiltX, tiltY, twist, altitudeAngle, azimuthAngle) {
+ let tick = actions.tickIdx;
+ if (this.actions.has(tick)) {
+ tick = actions.addTick().tickIdx;
+ }
+ let actionProperties = setPointerProperties({type: "pointerDown", button}, width, height,
+ pressure, tangentialPressure, tiltX, tiltY,
+ twist, altitudeAngle, azimuthAngle);
+ this.actions.set(tick, actionProperties);
+ },
+
+ pointerUp: function(actions, button) {
+ let tick = actions.tickIdx;
+ if (this.actions.has(tick)) {
+ tick = actions.addTick().tickIdx;
+ }
+ this.actions.set(tick, {type: "pointerUp", button});
+ },
+
+ pointerMove: function(actions, x, y, duration, origin, width, height, pressure,
+ tangentialPressure, tiltX, tiltY, twist, altitudeAngle, azimuthAngle) {
+ let tick = actions.tickIdx;
+ if (this.actions.has(tick)) {
+ tick = actions.addTick().tickIdx;
+ }
+ let moveAction = {type: "pointerMove", x, y, origin};
+ if (duration) {
+ moveAction.duration = duration;
+ }
+ let actionProperties = setPointerProperties(moveAction, width, height, pressure,
+ tangentialPressure, tiltX, tiltY, twist,
+ altitudeAngle, azimuthAngle);
+ this.actions.set(tick, actionProperties);
+ },
+
+ addPause: function(actions, duration) {
+ let tick = actions.tickIdx;
+ if (this.actions.has(tick)) {
+ tick = actions.addTick().tickIdx;
+ }
+ this.actions.set(tick, {type: "pause", duration: duration});
+ },
+ };
+
+ function WheelSource() {
+ this.actions = new Map();
+ }
+
+ WheelSource.prototype = {
+ serialize: function(tickCount) {
+ if (!this.actions.size) {
+ return undefined;
+ }
+ let actions = [];
+ let data = {"type": "wheel", "actions": actions};
+ for (let i=0; i<tickCount; i++) {
+ if (this.actions.has(i)) {
+ actions.push(this.actions.get(i));
+ } else {
+ actions.push({"type": "pause"});
+ }
+ }
+ return data;
+ },
+
+ scroll: function(actions, x, y, deltaX, deltaY, duration, origin) {
+ let tick = actions.tickIdx;
+ if (this.actions.has(tick)) {
+ tick = actions.addTick().tickIdx;
+ }
+ this.actions.set(tick, {type: "scroll", x, y, deltaX, deltaY, origin});
+ if (duration) {
+ this.actions.get(tick).duration = duration;
+ }
+ },
+
+ addPause: function(actions, duration) {
+ let tick = actions.tickIdx;
+ if (this.actions.has(tick)) {
+ tick = actions.addTick().tickIdx;
+ }
+ this.actions.set(tick, {type: "pause", duration: duration});
+ },
+ };
+
+ test_driver.Actions = Actions;
+})();