tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

input-events-typing.html (14684B)


      1 <!DOCTYPE html>
      2 <meta charset="utf-8">
      3 <title>Input Event typing tests</title>
      4 <script src="/resources/testharness.js"></script>
      5 <script src="/resources/testharnessreport.js"></script>
      6 <script src="/resources/testdriver.js"></script>
      7 <script src="/resources/testdriver-vendor.js"></script>
      8 <script src="/resources/testdriver-actions.js"></script>
      9 <div id="rich" contenteditable></div>
     10 <textarea id="plain"></textarea>
     11 <script>
     12 let inputEventsLog = [];
     13 const rich = document.getElementById('rich');
     14 const plain = document.getElementById('plain');
     15 
     16 function log(event) {
     17    const clone = new event.constructor(event.type, event);
     18    clone.state = rich.innerHTML;
     19    inputEventsLog.push(clone);
     20 }
     21 
     22 function resetRich() {
     23    inputEventsLog = [];
     24    rich.innerHTML = '';
     25 }
     26 
     27 function resetPlain() {
     28    inputEventsLog = [];
     29    plain.innerHTML = '';
     30 }
     31 
     32 rich.addEventListener('beforeinput', log);
     33 rich.addEventListener('input', log);
     34 
     35 promise_test(async function() {
     36    this.add_cleanup(resetRich);
     37    rich.focus();
     38    const message = 'Hello';
     39    await test_driver.send_keys(rich, message);
     40    // 10 events (5 beforeinput + 5 input events)
     41    assert_equals(inputEventsLog.length, 10);
     42    for (let i = 0; i < inputEventsLog.length; i += 2) {
     43        const beforeInputEvent = inputEventsLog[i];
     44        const inputEvent = inputEventsLog[i + 1];
     45        assert_equals(beforeInputEvent.type, 'beforeinput');
     46        assert_equals(inputEvent.type, 'input');
     47        assert_equals(beforeInputEvent.inputType, 'insertText');
     48        assert_equals(inputEvent.inputType, 'insertText');
     49        assert_equals(beforeInputEvent.data, inputEvent.data);
     50        assert_equals(inputEvent.data, message[i / 2]);
     51        assert_equals(beforeInputEvent.state + message[i / 2], inputEvent.state);
     52    }
     53 }, 'It triggers beforeinput and input events on text typing');
     54 
     55 promise_test(async function() {
     56    this.add_cleanup(resetRich);
     57    rich.focus();
     58    await test_driver.send_keys(rich, "\uE006"); // Return
     59 
     60    assert_equals(inputEventsLog.length, 2);
     61    const beforeInputEvent = inputEventsLog[0];
     62    const inputEvent = inputEventsLog[1];
     63    assert_equals(beforeInputEvent.type, 'beforeinput');
     64    assert_equals(inputEvent.type, 'input');
     65    assert_equals(beforeInputEvent.inputType, 'insertParagraph');
     66    assert_equals(inputEvent.inputType, 'insertParagraph');
     67    assert_equals(beforeInputEvent.data, inputEvent.data);
     68 }, 'It triggers beforeinput and input events on typing RETURN');
     69 
     70 promise_test(async function() {
     71    this.add_cleanup(resetPlain);
     72    const expectedResult = [
     73        // Pressing 'a'
     74        'insertText',
     75        // Return
     76        'insertLineBreak'
     77    ];
     78    const result = [];
     79 
     80    plain.addEventListener("input", (inputEvent) => {
     81      result.push(inputEvent.inputType);
     82    });
     83    await test_driver.click(plain);
     84    await test_driver.send_keys(plain,"a");
     85    await test_driver.send_keys(plain,'\uE006'); // Return
     86    assert_equals(result.length, expectedResult.length);
     87    expectedResult.forEach((er, index) => assert_equals(result[index], er));
     88 }, 'Newline character in plain text editing should get insertLinebreak input event');
     89 
     90 promise_test(async function() {
     91    this.add_cleanup(resetRich);
     92    rich.focus();
     93    await new test_driver.Actions()
     94        .keyDown('\uE008') // Shift
     95        .keyDown('\uE006') // Return
     96        .keyUp('\uE006')
     97        .keyUp('\uE008')
     98        .send();
     99 
    100    assert_equals(inputEventsLog.length, 2);
    101    const [beforeInputEvent, inputEvent] = inputEventsLog;
    102    assert_equals(beforeInputEvent.type, 'beforeinput');
    103    assert_equals(inputEvent.type, 'input');
    104    assert_equals(beforeInputEvent.inputType, 'insertLineBreak');
    105    assert_equals(inputEvent.inputType, 'insertLineBreak');
    106    assert_equals(beforeInputEvent.data, inputEvent.data);
    107 }, 'It triggers beforeinput and input events on typing Shift+RETURN');
    108 
    109 promise_test(async function() {
    110    this.add_cleanup(resetRich);
    111    rich.innerHTML = '<p>Preexisting <i id="caret">c</i>ontent</p>';
    112    const caret = document.querySelector('#caret');
    113    await test_driver.click(caret);
    114    await test_driver.send_keys(caret, "\uE017"); // Delete
    115 
    116    assert_equals(inputEventsLog.length, 2);
    117    const [beforeInputEvent, inputEvent] = inputEventsLog;
    118    assert_equals(beforeInputEvent.type, 'beforeinput');
    119    assert_equals(inputEvent.type, 'input');
    120    assert_equals(beforeInputEvent.inputType, 'deleteContentForward');
    121    assert_equals(inputEvent.inputType, 'deleteContentForward');
    122    assert_equals(beforeInputEvent.data, inputEvent.data);
    123 }, 'It triggers beforeinput and input events on typing DELETE with pre-existing content');
    124 
    125 promise_test(async function() {
    126    this.add_cleanup(resetRich);
    127    rich.focus();
    128    await test_driver.send_keys(rich, "\uE017"); // Delete
    129    assert_equals(inputEventsLog.length, 2);
    130    const [beforeInputEvent, inputEvent] = inputEventsLog;
    131    assert_equals(beforeInputEvent.type, 'beforeinput');
    132    assert_equals(inputEvent.type, 'input');
    133    assert_equals(beforeInputEvent.inputType, 'deleteContentForward');
    134    assert_equals(inputEvent.inputType, 'deleteContentForward');
    135    assert_equals(beforeInputEvent.data, inputEvent.data);
    136 }, 'It triggers beforeinput and input events on typing DELETE with no pre-existing content');
    137 
    138 promise_test(async function() {
    139    this.add_cleanup(resetRich);
    140    rich.innerHTML = '<p>Preexisting <i id="caret">c</i>ontent</p>';
    141 
    142    await test_driver.click(document.querySelector('#caret'));
    143    await test_driver.send_keys(rich, "\uE003"); // Back Space
    144 
    145    assert_equals(inputEventsLog.length, 2);
    146    const [beforeInputEvent, inputEvent] = inputEventsLog;
    147    assert_equals(beforeInputEvent.type, 'beforeinput');
    148    assert_equals(inputEvent.type, 'input');
    149    assert_equals(beforeInputEvent.inputType, 'deleteContentBackward');
    150    assert_equals(inputEvent.inputType, 'deleteContentBackward');
    151    assert_equals(beforeInputEvent.data, inputEvent.data);
    152 }, 'It triggers beforeinput and input events on typing BACK_SPACE with pre-existing content');
    153 
    154 promise_test(async function () {
    155    this.add_cleanup(resetRich);
    156    rich.innerHTML = '<p>Preexisting <i id="caret">C</i>ontent</p>';
    157 
    158    const expectedResult = [
    159        // Pressing 'a', 'b'
    160        'insertText',
    161        'insertText',
    162        // Delete twice
    163        'deleteContentForward',
    164        'deleteContentForward',
    165        // Pressing 'c', 'd'
    166        'insertText',
    167        'insertText',
    168        // Backspace
    169        'deleteContentBackward'
    170    ];
    171    const result = [];
    172 
    173    rich.addEventListener("input", (inputEvent) => {
    174      result.push(inputEvent.inputType);
    175    });
    176 
    177    await test_driver.click(document.querySelector('#caret')); // Preexisting |Content
    178    await test_driver.send_keys(rich, "a"); // Preexisting a|Content
    179    await test_driver.send_keys(rich, "b"); // Preexisting ab|Content
    180    // Delete
    181    await test_driver.send_keys(rich, "\uE017"); // Preexisting ab|ontent
    182    // Delete
    183    await test_driver.send_keys(rich, "\uE017"); // Preexisting ab|ntent
    184    await test_driver.send_keys(rich, "c"); // Preexisting abc|ntent
    185    await test_driver.send_keys(rich, "d"); // Preexisting abcd|ntent
    186    // Backspace
    187    await test_driver.send_keys(rich, "\uE003"); // Preexisting abc|ntent
    188 
    189    assert_equals(result.length, expectedResult.length);
    190    expectedResult.forEach((er, index) => assert_equals(result[index], er));
    191 }, 'Input events have correct inputType updated when different inputs are typed');
    192 
    193 promise_test(async function () {
    194    this.add_cleanup(resetRich);
    195    rich.innerHTML = '<p>Preexisting <i id="caret">c</i>ontent</p>';
    196 
    197    const expectedResult = [
    198        // Remove selected text with Backspace
    199        'deleteContentBackward',
    200        // Remove selected text with Delete
    201        'deleteContentForward'
    202    ];
    203    const result = [];
    204 
    205    rich.addEventListener("input", (inputEvent) => {
    206      result.push(inputEvent.inputType);
    207    });
    208 
    209    const modifierKey = navigator.platform === "MacIntel" ? '\u2318' : '\uE009';
    210 
    211    // Click before "content"
    212    await test_driver.click(document.querySelector('#caret')); // Preexisting |content
    213    // Select text to the left
    214    await new test_driver.Actions()
    215        .keyDown(modifierKey)
    216        .keyDown('\uE008') // Shift
    217        .keyDown('\uE012') // Arrow Left
    218        .keyUp('\uE012')
    219        .keyUp('\uE008')
    220        .keyUp(modifierKey)
    221        .send(); // |Preexisting ^content
    222    // Backspace
    223    await test_driver.send_keys(rich, "\uE003"); // |content
    224    // Select text to the right
    225    await new test_driver.Actions()
    226        .keyDown(modifierKey)
    227        .keyDown('\uE008') // Shift
    228        .keyDown('\uE014') // Arrow Right
    229        .keyUp('\uE012')
    230        .keyUp('\uE008')
    231        .keyUp(modifierKey)
    232        .send(); // ^content|
    233    // Delete
    234    await test_driver.send_keys(rich, "\uE017"); // |
    235 
    236    assert_equals(result.length, expectedResult.length);
    237    expectedResult.forEach((er, index) => assert_equals(result[index], er));
    238 }, 'Input events have correct inputType when selected text is removed with Backspace or Delete');
    239 
    240 promise_test(async function() {
    241    this.add_cleanup(resetRich);
    242    rich.focus();
    243    await test_driver.send_keys(rich, "\uE003"); // Back Space
    244 
    245    assert_equals(inputEventsLog.length, 2);
    246    const [beforeInputEvent, inputEvent] = inputEventsLog;
    247    assert_equals(beforeInputEvent.type, 'beforeinput');
    248    assert_equals(inputEvent.type, 'input');
    249    assert_equals(beforeInputEvent.inputType, 'deleteContentBackward');
    250    assert_equals(inputEvent.inputType, 'deleteContentBackward');
    251    assert_equals(beforeInputEvent.data, inputEvent.data);
    252 }, 'It triggers beforeinput and input events on typing BACK_SPACE with no pre-existing content');
    253 
    254 promise_test(async function() {
    255    this.add_cleanup(resetRich);
    256    rich.focus();
    257    await test_driver.send_keys(rich, "hello");
    258 
    259    // Decide whether to use  Key.COMMAND (mac) or Key.CONTROL (everything else)
    260    const modifierKey = navigator.platform === "MacIntel" ? '\u2318' : '\uE009';
    261 
    262    // Undo
    263    await new test_driver.Actions()
    264        .keyDown(modifierKey)
    265        .keyDown('z')
    266        .keyUp('z')
    267        .keyUp(modifierKey)
    268        .send();
    269    // Redo
    270    await new test_driver.Actions()
    271        .keyDown(modifierKey)
    272        .keyDown('\uE008') // Shift
    273        .keyDown('z')
    274        .keyUp('z')
    275        .keyUp('\uE008')
    276        .keyUp(modifierKey)
    277        .send();
    278 
    279    // Ignore the initial typing of 'hello'
    280    const historyInputEventsLog = inputEventsLog.slice(10);
    281 
    282    assert_equals(historyInputEventsLog.length, 4);
    283    const inputTypes = ['historyUndo', 'historyRedo'];
    284    for (let i = 0; i < historyInputEventsLog.length; i += 2) {
    285        // We are increaisng i by 2 as there should always be matching beforeinput and input events.
    286        const beforeInputEvent = historyInputEventsLog[i];
    287        const inputEvent = historyInputEventsLog[i + 1];
    288        assert_equals(beforeInputEvent.type, 'beforeinput');
    289        assert_equals(inputEvent.type, 'input');
    290        assert_equals(beforeInputEvent.inputType, inputTypes[i / 2]);
    291        assert_equals(inputEvent.inputType, inputTypes[i / 2]);
    292        assert_equals(beforeInputEvent.data, inputEvent.data);
    293    }
    294 }, 'It triggers beforeinput and input events on typing Undo and Redo key combinations with an existing history');
    295 
    296 promise_test(async function() {
    297    this.add_cleanup(resetRich);
    298    rich.focus();
    299    // Decide whether to use  Key.COMMAND (mac) or Key.CONTROL (everything else)
    300    const modifierKey = navigator.platform === "MacIntel" ? '\u2318' : '\uE009';
    301 
    302    // Undo
    303    await new test_driver.Actions()
    304        .keyDown(modifierKey)
    305        .keyDown('z')
    306        .keyUp('z')
    307        .keyUp(modifierKey)
    308        .send();
    309    // Redo
    310    await new test_driver.Actions()
    311        .keyDown(modifierKey)
    312        .keyDown('\uE008') // Shift
    313        .keyDown('z')
    314        .keyUp('z')
    315        .keyUp('\uE008')
    316        .keyUp(modifierKey)
    317        .send();
    318 
    319    assert_equals(inputEventsLog.length, 4);
    320    const inputTypes = ['historyUndo', 'historyRedo'];
    321    for (let i = 0; i < inputEventsLog.length; i += 2) {
    322        const beforeInputEvent = inputEventsLog[i];
    323        const inputEvent = inputEventsLog[i + 1];
    324        assert_equals(beforeInputEvent.type, 'beforeinput');
    325        assert_equals(inputEvent.type, 'input');
    326        assert_equals(beforeInputEvent.inputType, inputTypes[i / 2]);
    327        assert_equals(inputEvent.inputType, inputTypes[i / 2]);
    328        assert_equals(beforeInputEvent.data, inputEvent.data);
    329    }
    330 }, 'It triggers beforeinput and input events on typing Undo and Redo key combinations without an existing history');
    331 
    332 promise_test(async function() {
    333    this.add_cleanup(resetRich);
    334    const expectedResult = [
    335        // Pressing 'a'.
    336        'plain-keydown-a',
    337        'plain-keypress-a',
    338        'plain-beforeinput-a-null',
    339        'plain-input-a-null',
    340        'plain-keyup-a',
    341        // Pressing Shift-'b'.
    342        'plain-keydown-B',
    343        'plain-keypress-B',
    344        'plain-beforeinput-B-null',
    345        'plain-input-B-null',
    346        'plain-keyup-B',
    347        // Pressing 'c'.
    348        'rich-keydown-c',
    349        'rich-keypress-c',
    350        'rich-beforeinput-c-null',
    351        'rich-input-c-null',
    352        'rich-keyup-c',
    353        // Pressing Shift-'d'.
    354        'rich-keydown-D',
    355        'rich-keypress-D',
    356        'rich-beforeinput-D-null',
    357        'rich-input-D-null',
    358        'rich-keyup-D',
    359    ];
    360    const result = [];
    361 
    362    for (const eventType of ['beforeinput', 'input', 'keydown', 'keypress', 'keyup']) {
    363        const listener = event => {
    364            if (event.key === 'Shift') return;
    365            const eventInfo = [event.target.id, event.type, event.data || event.key];
    366            if (event instanceof InputEvent) eventInfo.push(String(event.dataTransfer));
    367            result.push(eventInfo.join('-'));
    368        }
    369        rich.addEventListener(eventType, listener);
    370        plain.addEventListener(eventType, listener);
    371    }
    372 
    373    plain.focus();
    374    await new test_driver.Actions()
    375        .keyDown('a')
    376        .keyUp('a')
    377        .keyDown('\uE008') // Shift
    378        .keyDown('b')
    379        .keyUp('b')
    380        .keyUp('\uE008')
    381        .send();
    382 
    383    rich.focus();
    384    await new test_driver.Actions()
    385        .keyDown('c')
    386        .keyUp('c')
    387        .keyDown('\uE008') // Shift
    388        .keyDown('d')
    389        .keyUp('d')
    390        .keyUp('\uE008')
    391        .send();
    392 
    393    assert_equals(result.length, expectedResult.length);
    394    expectedResult.forEach((er, index) => assert_equals(result[index], er));
    395 }, 'InputEvents have correct data/order when typing on textarea and contenteditable');
    396 </script>