Bug 1633441 [wpt PR 23267] - extended typing test for input events, a=testonly
authorJohannes Wilm <mail@johanneswilm.org>
Wed, 13 May 2020 03:56:08 +0000
changeset 531019 ee4eaea23d8a89fe14df44751bee361774d4435c
parent 531018 cb45dc3521454bf42d8f1bc149bcbf4279bb9a16
child 531020 07ef47b4bd6fa8fed4363348360dc284e646a4c5
push id116503
push userwptsync@mozilla.com
push dateWed, 20 May 2020 10:37:27 +0000
treeherderautoland@135acd548abf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstestonly
bugs1633441, 23267
milestone78.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1633441 [wpt PR 23267] - extended typing test for input events, a=testonly Automatic update from web-platform-tests extended typing test for input events (#23267) -- wpt-commits: e51f414776c4e7efa7cfa5fe63a3a8b3969e06ac wpt-pr: 23267
testing/web-platform/tests/input-events/input-events-typing.html
--- a/testing/web-platform/tests/input-events/input-events-typing.html
+++ b/testing/web-platform/tests/input-events/input-events-typing.html
@@ -1,82 +1,285 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
-<title>InputEvent have correct data/order when typing on textarea and contenteditable</title>
+<title>Input Event typing tests</title>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/resources/testdriver.js"></script>
 <script src="/resources/testdriver-vendor.js"></script>
 <script src="/resources/testdriver-actions.js"></script>
-<p>To manually run this test, please follow the steps below:<br/>
-1. Focus the first box and press key 'a' and then Shift-'b'.<br/>
-2. Focus the second box and press key 'c' and then Shift-'d'.<br/>
-<br/>
-If a "PASS" result appears the test passes, otherwise it fails</p>
-<textarea id='plain'></textarea>
-<div id='rich' style='border-style: solid;' contenteditable><br></div>
+<div id="rich" contenteditable></div>
+<textarea id="plain"></textarea>
 <script>
-async_test(t => {
+let inputEventsLog = [];
+const rich = document.getElementById('rich');
+const plain = document.getElementById('plain');
+
+function log(event) {
+    const clone = new event.constructor(event.type, event);
+    clone.state = rich.innerHTML;
+    inputEventsLog.push(clone);
+}
+
+function resetRich() {
+    inputEventsLog = [];
+    rich.innerHTML = '';
+}
+
+rich.addEventListener('beforeinput', log);
+rich.addEventListener('input', log);
+
+promise_test(async function() {
+    this.add_cleanup(resetRich);
+    rich.focus();
+    const message = 'Hello';
+    await test_driver.send_keys(rich, message);
+    // 10 events (5 beforeinput + 5 input events)
+    assert_equals(inputEventsLog.length, 10);
+    for (let i = 0; i < inputEventsLog.length; i += 2) {
+        const beforeInputEvent = inputEventsLog[i];
+        const inputEvent = inputEventsLog[i + 1];
+        assert_equals(beforeInputEvent.type, 'beforeinput');
+        assert_equals(inputEvent.type, 'input');
+        assert_equals(beforeInputEvent.inputType, 'insertText');
+        assert_equals(inputEvent.inputType, 'insertText');
+        assert_equals(beforeInputEvent.data, inputEvent.data);
+        assert_equals(inputEvent.data, message[i / 2]);
+        assert_equals(beforeInputEvent.state + message[i / 2], inputEvent.state);
+    }
+}, 'It triggers beforeinput and input events on text typing');
+
+promise_test(async function() {
+    this.add_cleanup(resetRich);
+    rich.focus();
+    await test_driver.send_keys(rich, "\uE006"); // Return
+
+    assert_equals(inputEventsLog.length, 2);
+    const beforeInputEvent = inputEventsLog[0];
+    const inputEvent = inputEventsLog[1];
+    assert_equals(beforeInputEvent.type, 'beforeinput');
+    assert_equals(inputEvent.type, 'input');
+    assert_equals(beforeInputEvent.inputType, 'insertParagraph');
+    assert_equals(inputEvent.inputType, 'insertParagraph');
+    assert_equals(beforeInputEvent.data, inputEvent.data);
+}, 'It triggers beforeinput and input events on typing RETURN');
+
+promise_test(async function() {
+    this.add_cleanup(resetRich);
+    rich.focus();
+    await new test_driver.Actions()
+        .keyDown('\uE008') // Shift
+        .keyDown('\uE006') // Return
+        .keyUp('\uE006')
+        .keyUp('\uE008')
+        .send();
+
+    assert_equals(inputEventsLog.length, 2);
+    const [beforeInputEvent, inputEvent] = inputEventsLog;
+    assert_equals(beforeInputEvent.type, 'beforeinput');
+    assert_equals(inputEvent.type, 'input');
+    assert_equals(beforeInputEvent.inputType, 'insertLineBreak');
+    assert_equals(inputEvent.inputType, 'insertLineBreak');
+    assert_equals(beforeInputEvent.data, inputEvent.data);
+}, 'It triggers beforeinput and input events on typing Shift+RETURN');
+
+promise_test(async function() {
+    this.add_cleanup(resetRich);
+    rich.innerHTML = '<p>Preexisting <i id="caret">c</i>ontent</p>';
+    const caret = document.querySelector('#caret');
+    await test_driver.click(caret);
+    await test_driver.send_keys(caret, "\uE017"); // Delete
+
+    assert_equals(inputEventsLog.length, 2);
+    const [beforeInputEvent, inputEvent] = inputEventsLog;
+    assert_equals(beforeInputEvent.type, 'beforeinput');
+    assert_equals(inputEvent.type, 'input');
+    assert_equals(beforeInputEvent.inputType, 'deleteContentForward');
+    assert_equals(inputEvent.inputType, 'deleteContentForward');
+    assert_equals(beforeInputEvent.data, inputEvent.data);
+}, 'It triggers beforeinput and input events on typing DELETE with pre-existing content');
+
+promise_test(async function() {
+    this.add_cleanup(resetRich);
+    rich.focus();
+    await test_driver.send_keys(rich, "\uE017"); // Delete
+    assert_equals(inputEventsLog.length, 2);
+    const [beforeInputEvent, inputEvent] = inputEventsLog;
+    assert_equals(beforeInputEvent.type, 'beforeinput');
+    assert_equals(inputEvent.type, 'input');
+    assert_equals(beforeInputEvent.inputType, 'deleteContentForward');
+    assert_equals(inputEvent.inputType, 'deleteContentForward');
+    assert_equals(beforeInputEvent.data, inputEvent.data);
+}, 'It triggers beforeinput and input events on typing DELETE with no pre-existing content');
+
+promise_test(async function() {
+    this.add_cleanup(resetRich);
+    rich.innerHTML = '<p>Preexisting <i id="caret">c</i>ontent</p>';
+
+    await test_driver.click(document.querySelector('#caret'));
+    await test_driver.send_keys(rich, "\uE003"); // Back Space
+
+    assert_equals(inputEventsLog.length, 2);
+    const [beforeInputEvent, inputEvent] = inputEventsLog;
+    assert_equals(beforeInputEvent.type, 'beforeinput');
+    assert_equals(inputEvent.type, 'input');
+    assert_equals(beforeInputEvent.inputType, 'deleteContentBackward');
+    assert_equals(inputEvent.inputType, 'deleteContentBackward');
+    assert_equals(beforeInputEvent.data, inputEvent.data);
+}, 'It triggers beforeinput and input events on typing BACK_SPACE with pre-existing content');
+
+promise_test(async function() {
+    this.add_cleanup(resetRich);
+    rich.focus();
+    await test_driver.send_keys(rich, "\uE003"); // Back Space
+
+    assert_equals(inputEventsLog.length, 2);
+    const [beforeInputEvent, inputEvent] = inputEventsLog;
+    assert_equals(beforeInputEvent.type, 'beforeinput');
+    assert_equals(inputEvent.type, 'input');
+    assert_equals(beforeInputEvent.inputType, 'deleteContentBackward');
+    assert_equals(inputEvent.inputType, 'deleteContentBackward');
+    assert_equals(beforeInputEvent.data, inputEvent.data);
+}, 'It triggers beforeinput and input events on typing BACK_SPACE with no pre-existing content');
+
+promise_test(async function() {
+    this.add_cleanup(resetRich);
+    rich.focus();
+    await test_driver.send_keys(rich, "hello");
+
+    // Decide whether to use  Key.COMMAND (mac) or Key.CONTROL (everything else)
+    const modifierKey = navigator.platform === "MacIntel" ? '\u2318' : '\uE009';
+
+    // Undo
+    await new test_driver.Actions()
+        .keyDown(modifierKey)
+        .keyDown('z')
+        .keyUp('z')
+        .keyUp(modifierKey)
+        .send();
+    // Redo
+    await new test_driver.Actions()
+        .keyDown(modifierKey)
+        .keyDown('\uE008') // Shift
+        .keyDown('z')
+        .keyUp('z')
+        .keyUp('\uE008')
+        .keyUp(modifierKey)
+        .send();
+
+    // Ignore the initial typing of 'hello'
+    const historyInputEventsLog = inputEventsLog.slice(10);
+
+    assert_equals(historyInputEventsLog.length, 4);
+    const inputTypes = ['historyUndo', 'historyRedo'];
+    for (let i = 0; i < historyInputEventsLog.length; i += 2) {
+        // We are increaisng i by 2 as there should always be matching beforeinput and input events.
+        const beforeInputEvent = historyInputEventsLog[i];
+        const inputEvent = historyInputEventsLog[i + 1];
+        assert_equals(beforeInputEvent.type, 'beforeinput');
+        assert_equals(inputEvent.type, 'input');
+        assert_equals(beforeInputEvent.inputType, inputTypes[i / 2]);
+        assert_equals(inputEvent.inputType, inputTypes[i / 2]);
+        assert_equals(beforeInputEvent.data, inputEvent.data);
+    }
+}, 'It triggers beforeinput and input events on typing Undo and Redo key combinations with an existing history');
+
+promise_test(async function() {
+    this.add_cleanup(resetRich);
+    rich.focus();
+    // Decide whether to use  Key.COMMAND (mac) or Key.CONTROL (everything else)
+    const modifierKey = navigator.platform === "MacIntel" ? '\u2318' : '\uE009';
+
+    // Undo
+    await new test_driver.Actions()
+        .keyDown(modifierKey)
+        .keyDown('z')
+        .keyUp('z')
+        .keyUp(modifierKey)
+        .send();
+    // Redo
+    await new test_driver.Actions()
+        .keyDown(modifierKey)
+        .keyDown('\uE008') // Shift
+        .keyDown('z')
+        .keyUp('z')
+        .keyUp('\uE008')
+        .keyUp(modifierKey)
+        .send();
+
+    assert_equals(inputEventsLog.length, 4);
+    const inputTypes = ['historyUndo', 'historyRedo'];
+    for (let i = 0; i < inputEventsLog.length; i += 2) {
+        const beforeInputEvent = inputEventsLog[i];
+        const inputEvent = inputEventsLog[i + 1];
+        assert_equals(beforeInputEvent.type, 'beforeinput');
+        assert_equals(inputEvent.type, 'input');
+        assert_equals(beforeInputEvent.inputType, inputTypes[i / 2]);
+        assert_equals(inputEvent.inputType, inputTypes[i / 2]);
+        assert_equals(beforeInputEvent.data, inputEvent.data);
+    }
+}, 'It triggers beforeinput and input events on typing Undo and Redo key combinations without an existing history');
+
+promise_test(async function() {
+    this.add_cleanup(resetRich);
     const expectedResult = [
         // Pressing 'a'.
         'plain-keydown-a',
         'plain-keypress-a',
-        'plain-beforeinput-a',
-        'plain-input-a',
+        'plain-beforeinput-a-null',
+        'plain-input-a-null',
         'plain-keyup-a',
         // Pressing Shift-'b'.
         'plain-keydown-B',
         'plain-keypress-B',
-        'plain-beforeinput-B',
-        'plain-input-B',
+        'plain-beforeinput-B-null',
+        'plain-input-B-null',
         'plain-keyup-B',
         // Pressing 'c'.
         'rich-keydown-c',
         'rich-keypress-c',
-        'rich-beforeinput-c',
-        'rich-input-c',
+        'rich-beforeinput-c-null',
+        'rich-input-c-null',
         'rich-keyup-c',
         // Pressing Shift-'d'.
         'rich-keydown-D',
         'rich-keypress-D',
-        'rich-beforeinput-D',
-        'rich-input-D',
+        'rich-beforeinput-D-null',
+        'rich-input-D-null',
         'rich-keyup-D',
     ];
-    let eventCounter = 0;
+    const result = [];
 
-    for (const targetId of ['plain', 'rich']) {
-        for (const eventType of ['beforeinput', 'input', 'keydown', 'keypress', 'keyup']) {
-            document.getElementById(targetId).addEventListener(eventType, t.step_func(event => {
-                if (event.key === 'Shift')
-                    return;
-                assert_equals(`${targetId}-${event.type}-${event.data || event.key}`, expectedResult[eventCounter]);
-                if (event instanceof InputEvent) {
-                    assert_equals(event.dataTransfer, null,
-                        'Plain text editing should have null |dataTransfer| field')
-                }
-                ++eventCounter;
-                if (eventCounter === expectedResult.length)
-                    t.done();
-            }));
+    for (const eventType of ['beforeinput', 'input', 'keydown', 'keypress', 'keyup']) {
+        const listener = event => {
+            if (event.key === 'Shift') return;
+            const eventInfo = [event.target.id, event.type, event.data || event.key];
+            if (event instanceof InputEvent) eventInfo.push(String(event.dataTransfer));
+            result.push(eventInfo.join('-'));
         }
+        rich.addEventListener(eventType, listener);
+        plain.addEventListener(eventType, listener);
     }
-    document.getElementById('plain').focus();
-    new test_driver.Actions()
-       .keyDown('a')
-       .keyUp('a')
-       .keyDown('\uE008') // Shift
-       .keyDown('B')
-       .keyUp('B')
-       .keyUp('\uE008')
-       .send()
-       .then(() => {
-           document.getElementById('rich').focus();
-           new test_driver.Actions()
-               .keyDown('c')
-               .keyUp('c')
-               .keyDown('\uE008') // Shift
-               .keyDown('D')
-               .keyUp('D')
-               .keyUp('\uE008')
-               .send();
-       });
-});</script>
+
+    plain.focus();
+    await new test_driver.Actions()
+        .keyDown('a')
+        .keyUp('a')
+        .keyDown('\uE008') // Shift
+        .keyDown('b')
+        .keyUp('b')
+        .keyUp('\uE008')
+        .send();
+
+    rich.focus();
+    await new test_driver.Actions()
+        .keyDown('c')
+        .keyUp('c')
+        .keyDown('\uE008') // Shift
+        .keyDown('d')
+        .keyUp('d')
+        .keyUp('\uE008')
+        .send();
+
+    assert_equals(expectedResult.length, result.length);
+    expectedResult.forEach((er, index) => assert_equals(er, result[index]));
+}, 'InputEvents have correct data/order when typing on textarea and contenteditable');
+</script>