mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 13:21:05 +00:00
Bug 1563206 - Test top key combinations that affect gutenberg; r=ato,remote-protocol-reviewers,jdescottes
Differential Revision: https://phabricator.services.mozilla.com/D47032 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
dd9ea7239d
commit
dd650b37c4
@ -32,7 +32,7 @@ under _remote/test/browser_:
|
||||
% ./mach mochitest remote/test/browser/browser_cdp.js
|
||||
|
||||
The functional tests will appear under the `M` (for _mochitest_)
|
||||
category in the `bc` (_browser-chrome_) jobs on Treeherder.
|
||||
category in the `remote` jobs on Treeherder.
|
||||
|
||||
As the functional tests will sporadically pop up new Firefox
|
||||
application windows, a helpful tip is to run them in [headless
|
||||
|
@ -7,6 +7,7 @@
|
||||
var EXPORTED_SYMBOLS = ["Input"];
|
||||
|
||||
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const { Domain } = ChromeUtils.import(
|
||||
"chrome://remote/content/domains/Domain.jsm"
|
||||
);
|
||||
@ -26,11 +27,16 @@ class Input extends Domain {
|
||||
* - modifiers
|
||||
* - text (not supported)
|
||||
* - type
|
||||
* - unmodifiedTest (not supported)
|
||||
* - unmodifiedText (not supported)
|
||||
* - windowsVirtualKeyCode
|
||||
* - nativeVirtualKeyCode (not supported)
|
||||
* - keyIdentifier (not supported)
|
||||
* - isSystemKey (not supported)
|
||||
*/
|
||||
async dispatchKeyEvent(options) {
|
||||
// missing code, text, unmodifiedText, autorepeat, location, iskeypad
|
||||
const { key, modifiers, type, windowsVirtualKeyCode } = options;
|
||||
const { alt, ctrl, meta, shift } = Input.Modifier;
|
||||
|
||||
let domType;
|
||||
if (type == "keyDown" || type == "rawKeyDown") {
|
||||
@ -62,24 +68,23 @@ class Input extends Domain {
|
||||
} else {
|
||||
// Non printable keys should be prefixed with `KEY_`
|
||||
const eventUtilsKey = key.length == 1 ? key : "KEY_" + key;
|
||||
EventUtils.synthesizeKey(
|
||||
eventUtilsKey,
|
||||
{
|
||||
keyCode: windowsVirtualKeyCode,
|
||||
type: domType,
|
||||
altKey: !!(modifiers & 1),
|
||||
ctrlKey: !!(modifiers & 2),
|
||||
metaKey: !!(modifiers & 4),
|
||||
shiftKey: !!(modifiers & 8),
|
||||
},
|
||||
browserWindow
|
||||
);
|
||||
const eventInfo = {
|
||||
keyCode: windowsVirtualKeyCode,
|
||||
type: domType,
|
||||
altKey: !!(modifiers & alt),
|
||||
ctrlKey: !!(modifiers & ctrl),
|
||||
metaKey: !!(modifiers & meta),
|
||||
shiftKey: !!(modifiers & shift),
|
||||
};
|
||||
EventUtils.synthesizeKey(eventUtilsKey, eventInfo, browserWindow);
|
||||
}
|
||||
|
||||
await this.executeInChild("waitForContentEvent", eventId);
|
||||
}
|
||||
|
||||
async dispatchMouseEvent({ type, button, x, y, modifiers, clickCount }) {
|
||||
const { alt, ctrl, meta, shift } = Input.Modifier;
|
||||
|
||||
if (type == "mousePressed") {
|
||||
type = "mousedown";
|
||||
} else if (type == "mouseReleased") {
|
||||
@ -93,34 +98,18 @@ class Input extends Domain {
|
||||
if (type === "mousedown" && button === "right") {
|
||||
type = "contextmenu";
|
||||
}
|
||||
if (button == undefined || button == "none" || button == "left") {
|
||||
button = 0;
|
||||
} else if (button == "middle") {
|
||||
button = 1;
|
||||
} else if (button == "right") {
|
||||
button = 2;
|
||||
} else if (button == "back") {
|
||||
button = 3;
|
||||
} else if (button == "forward") {
|
||||
button = 4;
|
||||
} else {
|
||||
throw new Error(`Mouse button is not supported: ${button}`);
|
||||
}
|
||||
|
||||
// Gutenberg test packages/e2e-tests/specs/blocks/list.test.js:
|
||||
// "can be created by converting multiple paragraphs"
|
||||
// Works better with EventUtils, in order to make the Shift+Click to work
|
||||
const buttonID = Input.Button[button] || Input.Button.left;
|
||||
const { browser } = this.session.target;
|
||||
const currentWindow = browser.ownerGlobal;
|
||||
const EventUtils = this._getEventUtils(currentWindow);
|
||||
EventUtils.synthesizeMouse(browser, x, y, {
|
||||
type,
|
||||
button,
|
||||
button: buttonID,
|
||||
clickCount: clickCount || 1,
|
||||
altKey: !!(modifiers & 1),
|
||||
ctrlKey: !!(modifiers & 2),
|
||||
metaKey: !!(modifiers & 4),
|
||||
shiftKey: !!(modifiers & 8),
|
||||
altKey: !!(modifiers & alt),
|
||||
ctrlKey: !!(modifiers & ctrl),
|
||||
metaKey: !!(modifiers & meta),
|
||||
shiftKey: !!(modifiers & shift),
|
||||
});
|
||||
}
|
||||
|
||||
@ -143,3 +132,18 @@ class Input extends Domain {
|
||||
return this._eventUtils;
|
||||
}
|
||||
}
|
||||
|
||||
Input.Button = {
|
||||
left: 0,
|
||||
middle: 1,
|
||||
right: 2,
|
||||
back: 3,
|
||||
forward: 4,
|
||||
};
|
||||
|
||||
Input.Modifier = {
|
||||
alt: 1,
|
||||
ctrl: 2,
|
||||
meta: 4,
|
||||
shift: 8,
|
||||
};
|
||||
|
@ -5,6 +5,7 @@ prefs = remote.enabled=true
|
||||
support-files =
|
||||
chrome-remote-interface.js
|
||||
doc_input_dispatchKeyEvent_race.html
|
||||
doc_input_events.html
|
||||
doc_network_requestWillBeSent.html
|
||||
file_network_requestWillBeSent.js
|
||||
head.js
|
||||
|
@ -3,51 +3,42 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
// Basic test for dispatch key event API with input type text
|
||||
const { Input: I } = ChromeUtils.import(
|
||||
"chrome://remote/content/domains/parent/Input.jsm"
|
||||
);
|
||||
|
||||
const { alt, ctrl, meta, shift } = I.Modifier;
|
||||
|
||||
// Map of key codes used in this test.
|
||||
const KEYCODES = {
|
||||
a: 65,
|
||||
Backspace: 8,
|
||||
h: 72,
|
||||
H: 72,
|
||||
AltLeft: 18,
|
||||
ArrowLeft: 37,
|
||||
ArrowRight: 39,
|
||||
a: KeyboardEvent.DOM_VK_A,
|
||||
A: KeyboardEvent.DOM_VK_A,
|
||||
b: KeyboardEvent.DOM_VK_B,
|
||||
B: KeyboardEvent.DOM_VK_B,
|
||||
c: KeyboardEvent.DOM_VK_C,
|
||||
C: KeyboardEvent.DOM_VK_C,
|
||||
h: KeyboardEvent.DOM_VK_H,
|
||||
H: KeyboardEvent.DOM_VK_H,
|
||||
Alt: KeyboardEvent.DOM_VK_ALT,
|
||||
ArrowLeft: KeyboardEvent.DOM_VK_LEFT,
|
||||
ArrowRight: KeyboardEvent.DOM_VK_RIGHT,
|
||||
ArrowDown: KeyboardEvent.DOM_VK_DOWN,
|
||||
Backspace: KeyboardEvent.DOM_VK_BACK_SPACE,
|
||||
Control: KeyboardEvent.DOM_VK_CONTROL,
|
||||
Meta: KeyboardEvent.DM_VK_META,
|
||||
Shift: KeyboardEvent.DOM_VK_SHIFT,
|
||||
Tab: KeyboardEvent.DOM_VK_TAB,
|
||||
};
|
||||
|
||||
// Modifier for move forward shortcut is CTRL+RightArrow on Linux/Windows, ALT+RightArrow
|
||||
// on Mac.
|
||||
const isMac = Services.appinfo.OS === "Darwin";
|
||||
const ALT_MODIFIER = 1;
|
||||
const CTRL_MODIFIER = 2;
|
||||
const ARROW_MODIFIER = isMac ? ALT_MODIFIER : CTRL_MODIFIER;
|
||||
|
||||
add_task(async function() {
|
||||
// The selectionchange event was flagged behind dom.select_events.textcontrols.enabled,
|
||||
// which is only true on Nightly and Local builds. Force the pref to true so that
|
||||
// the test passes on all channels. See Bug 1309628 for more details.
|
||||
info("Enable selectionchange events on input elements");
|
||||
await new Promise(resolve => {
|
||||
const options = {
|
||||
set: [["dom.select_events.textcontrols.enabled", true]],
|
||||
};
|
||||
SpecialPowers.pushPrefEnv(options, resolve);
|
||||
});
|
||||
|
||||
const { client, tab } = await setupForURL(toDataURL("<input>"));
|
||||
is(gBrowser.selectedTab, tab, "Selected tab is the target tab");
|
||||
const PAGE_URL =
|
||||
"http://example.com/browser/remote/test/browser/doc_input_events.html";
|
||||
|
||||
add_task(async function testTypingPrintableCharacters() {
|
||||
const { client } = await setupForInput(toDataURL("<input>"));
|
||||
const { Input } = client;
|
||||
|
||||
info("Focus the input on the page");
|
||||
await ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
|
||||
const input = content.document.querySelector("input");
|
||||
input.focus();
|
||||
is(input, content.document.activeElement, "Input should be focused");
|
||||
});
|
||||
await checkInputContent("", 0);
|
||||
|
||||
info("Write 'h'");
|
||||
await sendTextKey(Input, "h");
|
||||
await checkInputContent("h", 1);
|
||||
@ -64,8 +55,16 @@ add_task(async function() {
|
||||
});
|
||||
await checkInputContent("hH’", 3);
|
||||
|
||||
await teardown(client);
|
||||
});
|
||||
|
||||
add_task(async function testArrowKeys() {
|
||||
const { client } = await setupForInput(toDataURL("<input>"));
|
||||
const { Input } = client;
|
||||
|
||||
await sendText(Input, "hH’");
|
||||
info("Send Left");
|
||||
await sendArrowKey(Input, "ArrowLeft");
|
||||
await sendRawKey(Input, "ArrowLeft");
|
||||
await checkInputContent("hH’", 2);
|
||||
|
||||
info("Write 'a'");
|
||||
@ -73,44 +72,303 @@ add_task(async function() {
|
||||
await checkInputContent("hHa’", 3);
|
||||
|
||||
info("Send Left");
|
||||
await sendArrowKey(Input, "ArrowLeft");
|
||||
await sendRawKey(Input, "ArrowLeft");
|
||||
await checkInputContent("hHa’", 2);
|
||||
|
||||
info("Send Left");
|
||||
await sendArrowKey(Input, "ArrowLeft");
|
||||
await sendRawKey(Input, "ArrowLeft");
|
||||
await checkInputContent("hHa’", 1);
|
||||
|
||||
info("Write 'a'");
|
||||
await sendTextKey(Input, "a");
|
||||
await checkInputContent("haHa’", 2);
|
||||
|
||||
info("Send ALT/CTRL + Right");
|
||||
await sendArrowKey(Input, "ArrowRight", ARROW_MODIFIER);
|
||||
info("Send ALT/CONTROL + Right");
|
||||
let modCode = isMac ? alt : ctrl;
|
||||
let modKey = isMac ? "Alt" : "Control";
|
||||
await dispatchKeyEvent(Input, modKey, "rawKeyDown", modCode);
|
||||
await dispatchKeyEvent(Input, "ArrowRight", "rawKeyDown", modCode);
|
||||
await dispatchKeyEvent(Input, "ArrowRight", "keyUp");
|
||||
await dispatchKeyEvent(Input, modKey, "keyUp");
|
||||
await checkInputContent("haHa’", 5);
|
||||
|
||||
info("Delete every character in the input");
|
||||
await sendBackspace(Input, "haHa");
|
||||
await sendBackspace(Input, "haH");
|
||||
await sendBackspace(Input, "ha");
|
||||
await sendBackspace(Input, "h");
|
||||
await sendBackspace(Input, "");
|
||||
|
||||
await client.close();
|
||||
ok(true, "The client is closed");
|
||||
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
|
||||
await RemoteAgent.close();
|
||||
await teardown(client);
|
||||
});
|
||||
|
||||
async function sendTextKey(Input, key) {
|
||||
await dispatchKeyEvent(Input, key, "keyDown");
|
||||
await dispatchKeyEvent(Input, key, "keyUp");
|
||||
add_task(async function testBackspace() {
|
||||
const { client } = await setupForInput(toDataURL("<input>"));
|
||||
const { Input } = client;
|
||||
|
||||
await sendText(Input, "haHa’");
|
||||
|
||||
info("Delete every character in the input");
|
||||
await checkBackspace(Input, "haHa");
|
||||
await checkBackspace(Input, "haH");
|
||||
await checkBackspace(Input, "ha");
|
||||
await checkBackspace(Input, "h");
|
||||
await checkBackspace(Input, "");
|
||||
|
||||
await teardown(client);
|
||||
});
|
||||
|
||||
add_task(async function testShiftSelect() {
|
||||
const { client } = await setupForInput(toDataURL("<input>"));
|
||||
const { Input } = client;
|
||||
await resetInput("word 2 word3");
|
||||
|
||||
info("Send Shift + Left (select one char to the left)");
|
||||
await dispatchKeyEvent(Input, "Shift", "rawKeyDown", shift);
|
||||
await sendRawKey(Input, "ArrowLeft", shift);
|
||||
await sendRawKey(Input, "ArrowLeft", shift);
|
||||
await sendRawKey(Input, "ArrowLeft", shift);
|
||||
info("(deleteContentBackward)");
|
||||
await checkBackspace(Input, "word 2 wo");
|
||||
await dispatchKeyEvent(Input, "Shift", "keyUp");
|
||||
|
||||
await resetInput("word 2 wo");
|
||||
info("Send Shift + Left (select one char to the left)");
|
||||
await dispatchKeyEvent(Input, "Shift", "rawKeyDown", shift);
|
||||
await sendRawKey(Input, "ArrowLeft", shift);
|
||||
await sendRawKey(Input, "ArrowLeft", shift);
|
||||
await sendTextKey(Input, "H");
|
||||
await checkInputContent("word 2 H", 8);
|
||||
await dispatchKeyEvent(Input, "Shift", "keyUp");
|
||||
|
||||
await teardown(client);
|
||||
});
|
||||
|
||||
add_task(async function testSelectWord() {
|
||||
const { client } = await setupForInput(toDataURL("<input>"));
|
||||
const { Input } = client;
|
||||
await resetInput("word 2 word3");
|
||||
|
||||
info("Send Shift + Ctrl/Alt + Left (select one word to the left)");
|
||||
const { primary, primaryKey } = keyForPlatform();
|
||||
const combined = shift | primary;
|
||||
await dispatchKeyEvent(Input, "Shift", "rawKeyDown", shift);
|
||||
await dispatchKeyEvent(Input, primaryKey, "rawKeyDown", combined);
|
||||
await sendRawKey(Input, "ArrowLeft", combined);
|
||||
await sendRawKey(Input, "ArrowLeft", combined);
|
||||
await dispatchKeyEvent(Input, "Shift", "keyUp", primary);
|
||||
await dispatchKeyEvent(Input, primaryKey, "keyUp");
|
||||
info("(deleteContentBackward)");
|
||||
await checkBackspace(Input, "word ");
|
||||
|
||||
await teardown(client);
|
||||
});
|
||||
|
||||
add_task(async function testSelectDelete() {
|
||||
const { client } = await setupForInput(toDataURL("<input>"));
|
||||
const { Input } = client;
|
||||
await resetInput("word 2 word3");
|
||||
|
||||
info("Send Ctrl/Alt + Backspace (deleteWordBackward)");
|
||||
const { primary, primaryKey } = keyForPlatform();
|
||||
await dispatchKeyEvent(Input, primaryKey, "rawKeyDown", primary);
|
||||
if (Services.appinfo.OS === "WINNT") {
|
||||
await checkBackspace(Input, "word 2 ", primary);
|
||||
}
|
||||
await dispatchKeyEvent(Input, primaryKey, "keyUp");
|
||||
|
||||
await resetInput("word 2 ");
|
||||
await sendText(Input, "word4");
|
||||
await sendRawKey(Input, "ArrowLeft");
|
||||
await sendRawKey(Input, "ArrowLeft");
|
||||
await checkInputContent("word 2 word4", 10);
|
||||
|
||||
if (isMac) {
|
||||
info("Send Meta + Backspace (deleteSoftLineBackward)");
|
||||
await dispatchKeyEvent(Input, "Meta", "rawKeyDown", meta);
|
||||
await sendRawKey(Input, "Backspace", meta);
|
||||
const { value, caret } = await getInputContent();
|
||||
await dispatchKeyEvent(Input, "Meta", "keyUp");
|
||||
todo_is(value, "d4", "Meta + Backspace should delete line backward");
|
||||
todo_is(caret, 0, "Check position after Meta + Backspace");
|
||||
}
|
||||
|
||||
await teardown(client);
|
||||
});
|
||||
|
||||
add_task(async function testShiftEvents() {
|
||||
const { client } = await setupForInput(PAGE_URL);
|
||||
const { Input } = client;
|
||||
await resetEvents();
|
||||
|
||||
await withModifier(Input, "Shift", "shift", "A");
|
||||
await checkInputContent("A", 1);
|
||||
let events = await getEvents();
|
||||
checkEvent(events[0], "keydown", "Shift", "shift", true);
|
||||
checkEvent(events[1], "keydown", "A", "shift", true);
|
||||
checkEvent(events[2], "keypress", "A", "shift", true);
|
||||
checkProperties({ data: "A", inputType: "insertText" }, events[3]);
|
||||
checkEvent(events[4], "keyup", "A", "shift", true);
|
||||
checkEvent(events[5], "keyup", "Shift", "shift", false);
|
||||
await resetEvents();
|
||||
|
||||
await withModifier(Input, "Shift", "shift", "Enter");
|
||||
events = await getEvents();
|
||||
checkEvent(events[2], "keypress", "Enter", "shift", true);
|
||||
await resetEvents();
|
||||
|
||||
await withModifier(Input, "Shift", "shift", "Tab");
|
||||
events = await getEvents();
|
||||
checkEvent(events[1], "keydown", "Tab", "shift", true);
|
||||
await ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
|
||||
const input = content.document.querySelector("input");
|
||||
isnot(input, content.document.activeElement, "input should lose focus");
|
||||
});
|
||||
|
||||
await teardown(client);
|
||||
});
|
||||
|
||||
add_task(async function testAltEvents() {
|
||||
const { client } = await setupForInput(PAGE_URL);
|
||||
const { Input } = client;
|
||||
|
||||
await withModifier(Input, "Alt", "alt", "a");
|
||||
if (isMac) {
|
||||
await checkInputContent("a", 1);
|
||||
} else {
|
||||
await checkInputContent("", 0);
|
||||
}
|
||||
let events = await getEvents();
|
||||
checkEvent(events[1], "keydown", "a", "alt", true);
|
||||
checkEvent(events[events.length - 1], "keyup", "Alt", "alt", false);
|
||||
await teardown(client);
|
||||
});
|
||||
|
||||
add_task(async function testControlEvents() {
|
||||
const { client } = await setupForInput(PAGE_URL);
|
||||
const { Input } = client;
|
||||
|
||||
await withModifier(Input, "Control", "ctrl", "`");
|
||||
let events = await getEvents();
|
||||
// no keypress or input event
|
||||
checkEvent(events[1], "keydown", "`", "ctrl", true);
|
||||
checkEvent(events[events.length - 1], "keyup", "Control", "ctrl", false);
|
||||
await teardown(client);
|
||||
});
|
||||
|
||||
add_task(async function testMetaEvents() {
|
||||
if (!isMac) {
|
||||
return;
|
||||
}
|
||||
const { client } = await setupForInput(PAGE_URL);
|
||||
const { Input } = client;
|
||||
|
||||
await withModifier(Input, "Meta", "meta", "a");
|
||||
let events = await getEvents();
|
||||
// no keypress or input event
|
||||
checkEvent(events[1], "keydown", "a", "meta", true);
|
||||
checkEvent(events[events.length - 1], "keyup", "Meta", "meta", false);
|
||||
|
||||
await teardown(client);
|
||||
});
|
||||
|
||||
add_task(async function testCtrlShiftArrows() {
|
||||
const { client } = await setupForURL(
|
||||
toDataURL('<select multiple size="3"><option>a<option>b<option>c</select>')
|
||||
);
|
||||
const { Input } = client;
|
||||
|
||||
await ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
|
||||
const select = content.document.querySelector("select");
|
||||
select.selectedIndex = 0;
|
||||
select.focus();
|
||||
});
|
||||
|
||||
const combined = shift | ctrl;
|
||||
await dispatchKeyEvent(Input, "Control", "rawKeyDown", shift);
|
||||
await dispatchKeyEvent(Input, "Shift", "rawKeyDown", combined);
|
||||
await sendRawKey(Input, "ArrowDown", combined);
|
||||
await dispatchKeyEvent(Input, "Control", "keyUp", shift);
|
||||
await dispatchKeyEvent(Input, "Shift", "keyUp");
|
||||
|
||||
await ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
|
||||
const select = content.document.querySelector("select");
|
||||
ok(select[0].selected, "First option should be selected");
|
||||
ok(select[1].selected, "Second option should be selected");
|
||||
ok(!select[2].selected, "Third option should not be selected");
|
||||
});
|
||||
await teardown(client);
|
||||
});
|
||||
|
||||
add_task(async function testShiftClick() {
|
||||
const { client } = await setupForURL(PAGE_URL);
|
||||
const { Input } = client;
|
||||
await resetEvents();
|
||||
|
||||
await dispatchKeyEvent(Input, "Shift", "rawKeyDown", shift);
|
||||
info("Click the 'pointers' div.");
|
||||
await Input.dispatchMouseEvent({
|
||||
type: "mousePressed",
|
||||
x: 80,
|
||||
y: 180,
|
||||
modifiers: shift,
|
||||
});
|
||||
await Input.dispatchMouseEvent({
|
||||
type: "mouseReleased",
|
||||
x: 80,
|
||||
y: 180,
|
||||
modifiers: shift,
|
||||
});
|
||||
await dispatchKeyEvent(Input, "Shift", "keyUp", shift);
|
||||
let events = await getEvents();
|
||||
checkProperties({ type: "click", shiftKey: true, button: 0 }, events[2]);
|
||||
|
||||
await teardown(client);
|
||||
});
|
||||
|
||||
function keyForPlatform() {
|
||||
// TODO add cases for other key-combinations as the need arises
|
||||
let primary = ctrl;
|
||||
let primaryKey = "Control";
|
||||
if (isMac) {
|
||||
primary = alt;
|
||||
primaryKey = "Alt";
|
||||
}
|
||||
return { primary, primaryKey };
|
||||
}
|
||||
|
||||
async function sendArrowKey(Input, arrowKey, modifiers = 0) {
|
||||
await dispatchKeyEvent(Input, arrowKey, "rawKeyDown", modifiers);
|
||||
await dispatchKeyEvent(Input, arrowKey, "keyUp", modifiers);
|
||||
async function setupForInput(url) {
|
||||
const { client, tab } = await setupForURL(url);
|
||||
info("Focus the input on the page");
|
||||
await ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
|
||||
const input = content.document.querySelector("input");
|
||||
input.focus();
|
||||
is(input, content.document.activeElement, "Input should be focused");
|
||||
});
|
||||
await checkInputContent("", 0);
|
||||
return { client, tab };
|
||||
}
|
||||
|
||||
async function sendTextKey(Input, key, modifiers = 0) {
|
||||
await dispatchKeyEvent(Input, key, "keyDown", modifiers);
|
||||
await dispatchKeyEvent(Input, key, "keyUp", modifiers);
|
||||
}
|
||||
|
||||
async function sendText(Input, text) {
|
||||
for (const sym of text) {
|
||||
await sendTextKey(Input, sym);
|
||||
}
|
||||
}
|
||||
|
||||
async function sendRawKey(Input, key, modifiers = 0) {
|
||||
await dispatchKeyEvent(Input, key, "rawKeyDown", modifiers);
|
||||
await dispatchKeyEvent(Input, key, "keyUp", modifiers);
|
||||
}
|
||||
|
||||
async function checkBackspace(Input, expected, modifiers = 0) {
|
||||
info("Send Backspace");
|
||||
await sendRawKey(Input, "Backspace", modifiers);
|
||||
await checkInputContent(expected, expected.length);
|
||||
}
|
||||
|
||||
async function withModifier(Input, modKey, mod, key) {
|
||||
await dispatchKeyEvent(Input, modKey, "rawKeyDown", I.Modifier[mod]);
|
||||
await dispatchKeyEvent(Input, key, "keyDown", I.Modifier[mod]);
|
||||
await dispatchKeyEvent(Input, key, "keyUp", I.Modifier[mod]);
|
||||
await dispatchKeyEvent(Input, modKey, "keyUp");
|
||||
}
|
||||
|
||||
function dispatchKeyEvent(Input, key, type, modifiers = 0) {
|
||||
@ -123,32 +381,53 @@ function dispatchKeyEvent(Input, key, type, modifiers = 0) {
|
||||
});
|
||||
}
|
||||
|
||||
function getInputContent() {
|
||||
return ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
|
||||
const input = content.document.querySelector("input");
|
||||
return { value: input.value, caret: input.selectionStart };
|
||||
});
|
||||
}
|
||||
|
||||
async function checkInputContent(expectedValue, expectedCaret) {
|
||||
const { value, caret } = await ContentTask.spawn(
|
||||
gBrowser.selectedBrowser,
|
||||
null,
|
||||
function() {
|
||||
const input = content.document.querySelector("input");
|
||||
return { value: input.value, caret: input.selectionStart };
|
||||
}
|
||||
);
|
||||
|
||||
is(
|
||||
value,
|
||||
expectedValue,
|
||||
`The input value is correct ("${value}"="${expectedValue}")`
|
||||
);
|
||||
is(
|
||||
caret,
|
||||
expectedCaret,
|
||||
`The input caret has the correct index ("${caret}"="${expectedCaret}")`
|
||||
);
|
||||
const { value, caret } = await getInputContent();
|
||||
is(value, expectedValue, "Check input content");
|
||||
is(caret, expectedCaret, "Check position of input caret");
|
||||
}
|
||||
|
||||
async function sendBackspace(Input, expected) {
|
||||
info("Send Backspace");
|
||||
await dispatchKeyEvent(Input, "Backspace", "rawKeyDown");
|
||||
await dispatchKeyEvent(Input, "Backspace", "keyUp");
|
||||
|
||||
await checkInputContent(expected, expected.length);
|
||||
// assuming doc_input_events.html
|
||||
async function getEvents() {
|
||||
const events = await ContentTask.spawn(gBrowser.selectedBrowser, null, () => {
|
||||
return content.eval("allEvents");
|
||||
});
|
||||
info(`Events: ${JSON.stringify(events)}`);
|
||||
return events;
|
||||
}
|
||||
|
||||
// assuming doc_input_events.html
|
||||
async function resetEvents() {
|
||||
await ContentTask.spawn(gBrowser.selectedBrowser, null, () => {
|
||||
content.eval("resetEvents()");
|
||||
const events = content.eval("allEvents");
|
||||
is(events.length, 0, "List of events should be empty");
|
||||
});
|
||||
}
|
||||
|
||||
function resetInput(value = "") {
|
||||
return ContentTask.spawn(gBrowser.selectedBrowser, value, function(arg) {
|
||||
const input = content.document.querySelector("input");
|
||||
input.value = arg;
|
||||
input.focus();
|
||||
});
|
||||
}
|
||||
|
||||
function checkEvent(event, type, key, property, expectedValue) {
|
||||
let expected = { type, key };
|
||||
expected[property] = expectedValue;
|
||||
checkProperties(expected, event, "Event");
|
||||
}
|
||||
|
||||
function checkProperties(expectedObj, targetObj, message = "Compare objects") {
|
||||
for (const prop in expectedObj) {
|
||||
is(targetObj[prop], expectedObj[prop], message + `: check ${prop}`);
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
"use strict";
|
||||
|
||||
add_task(async function testDispatchMouseEvent() {
|
||||
const { client, tab } = await setupForURL(toDataURL("<div>foo</div>"));
|
||||
const { client } = await setupForURL(toDataURL("<div>foo</div>"));
|
||||
|
||||
const { Input } = client;
|
||||
|
||||
@ -52,10 +52,5 @@ add_task(async function testDispatchMouseEvent() {
|
||||
return this.clickPromise;
|
||||
});
|
||||
|
||||
await client.close();
|
||||
ok(true, "The client is closed");
|
||||
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
|
||||
await RemoteAgent.close();
|
||||
await teardown(client);
|
||||
});
|
||||
|
148
remote/test/browser/doc_input_events.html
Normal file
148
remote/test/browser/doc_input_events.html
Normal file
@ -0,0 +1,148 @@
|
||||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<html>
|
||||
<head>
|
||||
<title>Input Events</title>
|
||||
<style>
|
||||
div { padding:0px; margin: 0px; }
|
||||
#trackPointer { position: fixed; }
|
||||
#resultContainer { width: 600px; height: 60px; }
|
||||
.area { width: 100px; height: 50px; background-color: #ccc; }
|
||||
.block { width: 5px; height: 5px; border: solid 1px red; }
|
||||
#dragArea { position: relative; }
|
||||
#dragTarget { position: absolute; top:22px; left:47px;}
|
||||
</style>
|
||||
<script>
|
||||
"use strict";
|
||||
var allEvents = [];
|
||||
function makeParagraph(message) {
|
||||
let paragraph = document.createElement("p");
|
||||
paragraph.textContent = message;
|
||||
return paragraph;
|
||||
}
|
||||
function displayMessage(message) {
|
||||
let eventNode = document.getElementById("events");
|
||||
eventNode.innerHTML = ""
|
||||
eventNode.appendChild(makeParagraph(message));
|
||||
}
|
||||
|
||||
function appendMessage(message) {
|
||||
document.getElementById("events").appendChild(makeParagraph(message));
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape |key| if it's in a surrogate-half character range.
|
||||
*
|
||||
* Example: given "\ud83d" return "U+d83d".
|
||||
*
|
||||
* Otherwise JSON.stringify will convert it to U+FFFD (REPLACEMENT CHARACTER)
|
||||
* when returning a value from executeScript, for example.
|
||||
*/
|
||||
function escapeSurrogateHalf(key) {
|
||||
if (typeof key !== "undefined" && key.length === 1) {
|
||||
var charCode = key.charCodeAt(0);
|
||||
var highSurrogate = charCode >= 0xD800 && charCode <= 0xDBFF;
|
||||
var surrogate = highSurrogate || (charCode >= 0xDC00 && charCode <= 0xDFFF);
|
||||
if (surrogate) {
|
||||
key = "U+" + charCode.toString(16);
|
||||
}
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
function recordKeyboardEvent(event) {
|
||||
var key = escapeSurrogateHalf(event.key);
|
||||
allEvents.push({
|
||||
"code": event.code,
|
||||
"key": key,
|
||||
"which": event.which,
|
||||
"location": event.location,
|
||||
"alt": event.altKey,
|
||||
"ctrl": event.ctrlKey,
|
||||
"meta": event.metaKey,
|
||||
"shift": event.shiftKey,
|
||||
"repeat": event.repeat,
|
||||
"type": event.type
|
||||
});
|
||||
appendMessage(event.type + " " +
|
||||
"code: " + event.code + ", " +
|
||||
"key: " + key + ", " +
|
||||
"which: " + event.which + ", " +
|
||||
"keyCode: " + event.keyCode);
|
||||
}
|
||||
function recordInputEvent(event) {
|
||||
allEvents.push({
|
||||
"data": event.data,
|
||||
"inputType": event.inputType,
|
||||
"isComposing": event.isComposing,
|
||||
});
|
||||
appendMessage("InputEvent " +
|
||||
"data: " + event.data + ", " +
|
||||
"inputType: " + event.inputType + ", " +
|
||||
"isComposing: " + event.isComposing);
|
||||
}
|
||||
function recordPointerEvent(event) {
|
||||
if (event.type === "contextmenu") {
|
||||
event.preventDefault();
|
||||
}
|
||||
allEvents.push({
|
||||
"type": event.type,
|
||||
"button": event.button,
|
||||
"buttons": event.buttons,
|
||||
"pageX": event.pageX,
|
||||
"pageY": event.pageY,
|
||||
"ctrlKey": event.ctrlKey,
|
||||
"metaKey": event.metaKey,
|
||||
"altKey": event.altKey,
|
||||
"shiftKey": event.shiftKey,
|
||||
"target": event.target.id
|
||||
});
|
||||
appendMessage(event.type + " " +
|
||||
"pageX: " + event.pageX + ", " +
|
||||
"pageY: " + event.pageY + ", " +
|
||||
"button: " + event.button + ", " +
|
||||
"buttons: " + event.buttons + ", " +
|
||||
"ctrlKey: " + event.ctrlKey + ", " +
|
||||
"altKey: " + event.altKey + ", " +
|
||||
"metaKey: " + event.metaKey + ", " +
|
||||
"shiftKey: " + event.shiftKey + ", " +
|
||||
"target id: " + event.target.id);
|
||||
}
|
||||
function resetEvents() {
|
||||
allEvents.length = 0;
|
||||
displayMessage("");
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
let keyReporter = document.getElementById("keys");
|
||||
keyReporter.addEventListener("keyup", recordKeyboardEvent);
|
||||
keyReporter.addEventListener("keypress", recordKeyboardEvent);
|
||||
keyReporter.addEventListener("keydown", recordKeyboardEvent);
|
||||
keyReporter.addEventListener("input", recordInputEvent);
|
||||
|
||||
let mouseReporter = document.getElementById("pointers");
|
||||
mouseReporter.addEventListener("click", recordPointerEvent);
|
||||
mouseReporter.addEventListener("dblclick", recordPointerEvent);
|
||||
mouseReporter.addEventListener("mousedown", recordPointerEvent);
|
||||
mouseReporter.addEventListener("mouseup", recordPointerEvent);
|
||||
mouseReporter.addEventListener("contextmenu", recordPointerEvent);
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="trackPointer" class="block"></div>
|
||||
<div>
|
||||
<h2>KeyReporter</h2>
|
||||
<input type="text" id="keys" size="80">
|
||||
</div>
|
||||
<div>
|
||||
<h2>ClickReporter</h2>
|
||||
<div id="pointers" class="area">
|
||||
</div>
|
||||
</div>
|
||||
<div id="resultContainer">
|
||||
<h2>Events</h2>
|
||||
<div id="events"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -116,6 +116,7 @@ async function setup() {
|
||||
*/
|
||||
async function setupForURL(url) {
|
||||
const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
|
||||
is(gBrowser.selectedTab, tab, "Selected tab is the target tab");
|
||||
|
||||
await RemoteAgent.listen(Services.io.newURI("http://localhost:9222"));
|
||||
const CDP = await getCDP();
|
||||
@ -157,3 +158,15 @@ function getContentProperty(prop) {
|
||||
_prop => content[_prop]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close tabs, client, remote agent.
|
||||
*/
|
||||
async function teardown(client) {
|
||||
await client.close();
|
||||
ok(true, "The client is closed");
|
||||
while (gBrowser.tabs.length > 1) {
|
||||
gBrowser.removeCurrentTab();
|
||||
}
|
||||
await RemoteAgent.close();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user