| |
| const isDebug = false; |
| |
| function logDebug(callback) |
| { |
| if (isDebug) |
| console.log(callback()); |
| } |
| |
| function dispatchMouseActions(actions) |
| { |
| if (!window.eventSender) |
| return Promise.reject(new Error("window.eventSender is undefined.")); |
| |
| return new Promise(resolve => { |
| setTimeout(() => { |
| for (let action of actions) { |
| switch (action.type) { |
| case "pointerMove": |
| let origin = { x: 0, y: 0 }; |
| if (action.origin instanceof Element) { |
| const bounds = action.origin.getBoundingClientRect(); |
| logDebug(() => `${action.origin.id} [${bounds.left}, ${bounds.top}, ${bounds.width}, ${bounds.height}]`); |
| origin.x = bounds.left + 1; |
| origin.y = bounds.top + 1; |
| } |
| logDebug(() => `eventSender.mouseMoveTo(${action.x + origin.x}, ${action.y + origin.y})`); |
| eventSender.mouseMoveTo(action.x + origin.x, action.y + origin.y); |
| break; |
| case "pointerDown": |
| // FIXME: what to do with "button"? |
| logDebug(() => `eventSender.mouseDown()`); |
| eventSender.mouseDown(); |
| break; |
| case "pointerUp": |
| // FIXME: what to do with "button"? |
| logDebug(() => `eventSender.mouseUp()`); |
| eventSender.mouseUp(); |
| break; |
| default: |
| return Promise.reject(new Error(`Unknown action type "${action.type}".`)); |
| } |
| } |
| resolve(); |
| }); |
| }); |
| } |
| |
| function dispatchTouchActions(actions, options = { insertPauseAfterPointerUp: false }) |
| { |
| if (!window.testRunner || typeof window.testRunner.runUIScript !== "function") |
| return Promise.reject(new Error("window.testRunner.runUIScript() is undefined.")); |
| |
| let x = 0; |
| let y = 0; |
| let timeOffset = 0; |
| let pointerDown = false; |
| let id = 1; |
| |
| const events = []; |
| for (let action of actions) { |
| const touch = { |
| inputType : "finger", |
| id, |
| x : action.x, |
| y : action.y, |
| pressure : 0 |
| }; |
| |
| const command = { |
| inputType : "hand", |
| coordinateSpace : "content", |
| touches : [touch], |
| timeOffset |
| }; |
| |
| let timeOffsetIncrease = 0.05; |
| |
| switch (action.type) { |
| case "pointerMove": |
| touch.phase = "moved"; |
| if (action.origin instanceof Element) { |
| const bounds = action.origin.getBoundingClientRect(); |
| touch.x += bounds.left + 1; |
| touch.y += bounds.top + 1; |
| } |
| break; |
| case "pointerDown": |
| pointerDown = true; |
| touch.phase = "began"; |
| if (action.x === undefined) |
| touch.x = x; |
| if (action.y === undefined) |
| touch.y = y; |
| break; |
| case "pointerUp": |
| pointerDown = false; |
| touch.phase = "ended"; |
| touch.x = x; |
| touch.y = y; |
| id++; |
| // We need to add a pause after a pointer up to ensure that a subsequent tap may be recognized as such. |
| if (options.insertPauseAfterPointerUp) |
| timeOffsetIncrease = 0.5; |
| break; |
| case "pause": |
| timeOffsetIncrease = action.duration / 1000; |
| touch.phase = "stationary"; |
| if (!pointerDown) |
| id++; |
| break; |
| default: |
| return Promise.reject(new Error(`Unknown action type "${action.type}".`)); |
| } |
| |
| x = touch.x; |
| y = touch.y; |
| |
| if (!pointerDown && touch.phase == "moved") |
| continue; |
| |
| timeOffset += timeOffsetIncrease; |
| |
| events.push(command); |
| } |
| |
| const stream = JSON.stringify({ events }); |
| logDebug(() => stream); |
| |
| return new Promise(resolve => testRunner.runUIScript(`(function() { |
| (function() { uiController.sendEventStream('${stream}') })(); |
| uiController.uiScriptComplete(); |
| })();`, resolve)); |
| } |
| |
| if (window.test_driver_internal === undefined) |
| window.test_driver_internal = { }; |
| |
| window.test_driver_internal.action_sequence = function(sources) |
| { |
| // https://w3c.github.io/webdriver/#processing-actions |
| |
| let noneSource; |
| let pointerSource; |
| for (let source of sources) { |
| switch (source.type) { |
| case "none": |
| noneSource = source; |
| break; |
| case "pointer": |
| pointerSource = source; |
| break; |
| default: |
| return Promise.reject(new Error(`Unknown source type "${action.type}".`)); |
| } |
| } |
| |
| if (!pointerSource) |
| return Promise.reject(new Error(`Unknown pointer type pointer type "${action.parameters.pointerType}".`)); |
| |
| const pointerType = pointerSource.parameters.pointerType; |
| if (pointerType !== "mouse" && pointerType !== "touch") |
| return Promise.reject(new Error(`Unknown pointer type "${pointerType}".`)); |
| |
| // If we have a "none" source, let's inject any pause with non-zero durations into the pointer source |
| // after the matching action in the pointer source. |
| if (noneSource) { |
| let injectedActions = 0; |
| noneSource.actions.forEach((action, index) => { |
| if (action.duration > 0) { |
| pointerSource.actions.splice(index + injectedActions + 1, 0, action); |
| injectedActions++; |
| } |
| }); |
| } |
| |
| logDebug(() => JSON.stringify(pointerSource)); |
| |
| if (pointerType === "touch") |
| return dispatchTouchActions(pointerSource.actions); |
| if ("createTouch" in document) |
| return dispatchTouchActions(pointerSource.actions, { insertPauseAfterPointerUp: true }); |
| if (pointerType === "mouse") |
| return dispatchMouseActions(pointerSource.actions); |
| }; |