blob: c930ecf9cd72d5c7c9382fee42ad269a14802a98 [file] [log] [blame]
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(() => {
eventSender.dragMode = false;
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":
logDebug(() => `eventSender.mouseDown()`);
eventSender.mouseDown(action.button);
break;
case "pointerUp":
logDebug(() => `eventSender.mouseUp()`);
eventSender.mouseUp(action.button);
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.send_keys = function(element, keys)
{
if (!window.eventSender)
return Promise.reject(new Error("window.eventSender is undefined."));
// https://seleniumhq.github.io/selenium/docs/api/py/webdriver/selenium.webdriver.common.keys.html
// FIXME: Add more cases.
// FIXME: Add the support for modifier keys.
const SeleniumCharCodeToEventSenderKey = {
0xE003: 'delete',
0xE004: '\t',
0xE00D: ' ',
0xE012: 'leftArrow',
0xE013: 'upArrow',
0xE014: 'rightArrow',
0xE015: 'downArrow',
};
function convertSeleniumKeyCode(key)
{
const code = key.charCodeAt(0);
return SeleniumCharCodeToEventSenderKey[code] || key;
}
const keyList = [];
for (const key of keys)
keyList.push(convertSeleniumKeyCode(key));
if (testRunner.isIOSFamily && testRunner.isWebKit2) {
return new Promise((resolve) => {
testRunner.runUIScript(`
const keyList = JSON.parse('${JSON.stringify(keyList)}');
for (const key of keyList)
uiController.keyDown(key);`, resolve);
});
}
for (const key of keyList)
eventSender.keyDown(key);
return Promise.resolve();
}
window.test_driver_internal.click = function (element, coords)
{
if (!window.eventSender)
return Promise.reject(new Error("window.eventSender is undefined."));
if (testRunner.isIOSFamily && testRunner.isWebKit2) {
return new Promise((resolve) => {
testRunner.runUIScript(`
uiController.singleTapAtPoint(${coords.x}, ${coords.y}, function() {
uiController.uiScriptComplete();
});`, resolve);
});
}
eventSender.mouseMoveTo(coords.x, coords.y);
eventSender.mouseDown();
eventSender.mouseUp();
return Promise.resolve();
}
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);
};