test_input_event.html (18467B)
1 <!DOCTYPE HTML> 2 <html> 3 <!-- 4 https://bugzilla.mozilla.org/show_bug.cgi?id=851780 5 --> 6 <head> 7 <title>Test for input event</title> 8 <script src="/tests/SimpleTest/SimpleTest.js"></script> 9 <script src="/tests/SimpleTest/EventUtils.js"></script> 10 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> 11 </head> 12 <body> 13 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=851780">Mozilla Bug 851780</a> 14 <p id="display"></p> 15 <div id="content"></div> 16 <pre id="test"> 17 <script class="testbody" type="text/javascript"> 18 19 /** Test for input event. This is highly based on test_change_event.html */ 20 21 const isDesktop = !/Mobile|Tablet/.test(navigator.userAgent); 22 23 let expectedInputType = ""; 24 let expectedData = null; 25 let expectedBeforeInputCancelable = false; 26 function checkBeforeInputEvent(aEvent, aDescription) { 27 ok(aEvent instanceof InputEvent, 28 `"beforeinput" event should be dispatched with InputEvent interface ${aDescription}`); 29 is(aEvent.inputType, expectedInputType, 30 `inputType of "beforeinput" event should be "${expectedInputType}" ${aDescription}`); 31 is(aEvent.data, expectedData, 32 `data of "beforeinput" event should be ${expectedData} ${aDescription}`); 33 is(aEvent.dataTransfer, null, 34 `dataTransfer of "beforeinput" event should be null ${aDescription}`); 35 is(aEvent.getTargetRanges().length, 0, 36 `getTargetRanges() of "beforeinput" event should return empty array ${aDescription}`); 37 is(aEvent.cancelable, expectedBeforeInputCancelable, 38 `"beforeinput" event for "${expectedInputType}" should ${expectedBeforeInputCancelable ? "be" : "not be"} cancelable ${aDescription}`); 39 is(aEvent.bubbles, true, 40 `"beforeinput" event should always bubble ${aDescription}`); 41 } 42 43 let skipExpectedDataCheck = false; 44 function checkIfInputIsInputEvent(aEvent, aDescription) { 45 ok(aEvent instanceof InputEvent, 46 `"input" event should be dispatched with InputEvent interface ${aDescription}`); 47 is(aEvent.inputType, expectedInputType, 48 `inputType should be "${expectedInputType}" ${aDescription}`); 49 if (!skipExpectedDataCheck) 50 is(aEvent.data, expectedData, `data should be ${expectedData} ${aDescription}`); 51 else 52 info(`data is ${aEvent.data} ${aDescription}`); 53 is(aEvent.dataTransfer, null, 54 `dataTransfer should be null ${aDescription}`); 55 is(aEvent.cancelable, false, 56 `"input" event should be never cancelable ${aDescription}`); 57 is(aEvent.bubbles, true, 58 `"input" event should always bubble ${aDescription}`); 59 } 60 61 function checkIfInputIsEvent(aEvent, aDescription) { 62 ok(aEvent instanceof Event && !(aEvent instanceof UIEvent), 63 `"input" event should be dispatched with InputEvent interface ${aDescription}`); 64 is(aEvent.cancelable, false, 65 `"input" event should be never cancelable ${aDescription}`); 66 is(aEvent.bubbles, true, 67 `"input" event should always bubble ${aDescription}`); 68 } 69 70 let textareaInput = 0, textareaBeforeInput = 0; 71 let textTypes = ["text", "email", "search", "tel", "url", "password"]; 72 let textBeforeInput = [0, 0, 0, 0, 0, 0]; 73 let textInput = [0, 0, 0, 0, 0, 0]; 74 let nonTextTypes = ["button", "submit", "image", "reset", "radio", "checkbox"]; 75 let nonTextBeforeInput = [0, 0, 0, 0, 0, 0]; 76 let nonTextInput = [0, 0, 0, 0, 0, 0]; 77 let rangeInput = 0, rangeBeforeInput = 0; 78 let numberInput = 0, numberBeforeInput = 0; 79 80 // Don't create elements whose event listener attributes are required before enabling `beforeinput` event. 81 function init() { 82 document.getElementById("content").innerHTML = 83 `<input type="file" id="fileInput"> 84 <textarea id="textarea"></textarea> 85 <input type="text" id="input_text"> 86 <input type="email" id="input_email"> 87 <input type="search" id="input_search"> 88 <input type="tel" id="input_tel"> 89 <input type="url" id="input_url"> 90 <input type="password" id="input_password"> 91 92 <!-- "Non-text" inputs--> 93 <input type="button" id="input_button"> 94 <input type="submit" id="input_submit"> 95 <input type="image" id="input_image"> 96 <input type="reset" id="input_reset"> 97 <input type="radio" id="input_radio"> 98 <input type="checkbox" id="input_checkbox"> 99 <input type="range" id="input_range"> 100 <input type="number" id="input_number">`; 101 102 document.getElementById("textarea").addEventListener("beforeinput", (aEvent) => { 103 ++textareaBeforeInput; 104 checkBeforeInputEvent(aEvent, "on textarea element"); 105 }); 106 document.getElementById("textarea").addEventListener("input", (aEvent) => { 107 ++textareaInput; 108 checkIfInputIsInputEvent(aEvent, "on textarea element"); 109 }); 110 111 // These are the type were the input event apply. 112 for (let id of ["input_text", "input_email", "input_search", "input_tel", "input_url", "input_password"]) { 113 document.getElementById(id).addEventListener("beforeinput", (aEvent) => { 114 ++textBeforeInput[textTypes.indexOf(aEvent.target.type)]; 115 checkBeforeInputEvent(aEvent, `on input element whose type is ${aEvent.target.type}`); 116 }); 117 document.getElementById(id).addEventListener("input", (aEvent) => { 118 ++textInput[textTypes.indexOf(aEvent.target.type)]; 119 checkIfInputIsInputEvent(aEvent, `on input element whose type is ${aEvent.target.type}`); 120 }); 121 } 122 123 // These are the type were the input event does not apply. 124 for (let id of ["input_button", "input_submit", "input_image", "input_reset", "input_radio", "input_checkbox"]) { 125 document.getElementById(id).addEventListener("beforeinput", (aEvent) => { 126 ++nonTextBeforeInput[nonTextTypes.indexOf(aEvent.target.type)]; 127 }); 128 document.getElementById(id).addEventListener("input", (aEvent) => { 129 ++nonTextInput[nonTextTypes.indexOf(aEvent.target.type)]; 130 checkIfInputIsEvent(aEvent, `on input element whose type is ${aEvent.target.type}`); 131 }); 132 } 133 134 document.getElementById("input_range").addEventListener("beforeinput", (aEvent) => { 135 ++rangeBeforeInput; 136 }); 137 document.getElementById("input_range").addEventListener("input", (aEvent) => { 138 ++rangeInput; 139 checkIfInputIsEvent(aEvent, "on input element whose type is range"); 140 }); 141 142 document.getElementById("input_number").addEventListener("beforeinput", (aEvent) => { 143 ++numberBeforeInput; 144 }); 145 document.getElementById("input_number").addEventListener("input", (aEvent) => { 146 ++numberInput; 147 checkIfInputIsInputEvent(aEvent, "on input element whose type is number"); 148 }); 149 } 150 151 var MockFilePicker = SpecialPowers.MockFilePicker; 152 MockFilePicker.init(SpecialPowers.wrap(window).browsingContext); 153 154 function testUserInput() { 155 // Simulating an OK click and with a file name return. 156 MockFilePicker.useBlobFile(); 157 MockFilePicker.returnValue = MockFilePicker.returnOK; 158 var input = document.getElementById('fileInput'); 159 input.focus(); 160 161 input.addEventListener("beforeinput", function (aEvent) { 162 ok(false, "beforeinput event shouldn't be dispatched on file input."); 163 }); 164 input.addEventListener("input", function (aEvent) { 165 ok(true, "input event should've been dispatched on file input."); 166 checkIfInputIsEvent(aEvent, "on file input"); 167 }); 168 169 input.click(); 170 SimpleTest.executeSoon(testUserInput2); 171 } 172 173 function testUserInput2() { 174 // Some generic checks for types that support the input event. 175 for (var i = 0; i < textTypes.length; ++i) { 176 input = document.getElementById("input_" + textTypes[i]); 177 input.focus(); 178 expectedInputType = "insertLineBreak"; 179 expectedData = null; 180 expectedBeforeInputCancelable = true; 181 synthesizeKey("KEY_Enter"); 182 is(textBeforeInput[i], 1, "beforeinput event should've been dispatched on " + textTypes[i] + " input element"); 183 is(textInput[i], 0, "input event shouldn't be dispatched on " + textTypes[i] + " input element"); 184 185 expectedInputType = "insertText"; 186 expectedData = "m"; 187 expectedBeforeInputCancelable = true; 188 sendString("m"); 189 is(textBeforeInput[i], 2, textTypes[i] + " input element should've been dispatched beforeinput event."); 190 is(textInput[i], 1, textTypes[i] + " input element should've been dispatched input event."); 191 expectedInputType = "insertLineBreak"; 192 expectedData = null; 193 expectedBeforeInputCancelable = true; 194 synthesizeKey("KEY_Enter", {shiftKey: true}); 195 is(textBeforeInput[i], 3, "input event should've been dispatched on " + textTypes[i] + " input element"); 196 is(textInput[i], 1, "input event shouldn't be dispatched on " + textTypes[i] + " input element"); 197 198 expectedInputType = "deleteContentBackward"; 199 expectedData = null; 200 expectedBeforeInputCancelable = true; 201 synthesizeKey("KEY_Backspace"); 202 is(textBeforeInput[i], 4, textTypes[i] + " input element should've been dispatched beforeinput event."); 203 is(textInput[i], 2, textTypes[i] + " input element should've been dispatched input event."); 204 } 205 206 // Some scenarios of value changing from script and from user input. 207 input = document.getElementById("input_text"); 208 input.focus(); 209 expectedInputType = "insertText"; 210 expectedData = "f"; 211 expectedBeforeInputCancelable = true; 212 sendString("f"); 213 is(textBeforeInput[0], 5, "beforeinput event should've been dispatched"); 214 is(textInput[0], 3, "input event should've been dispatched"); 215 input.blur(); 216 is(textBeforeInput[0], 5, "input event should not have been dispatched"); 217 is(textInput[0], 3, "input event should not have been dispatched"); 218 219 input.focus(); 220 input.value = 'foo'; 221 is(textBeforeInput[0], 5, "beforeinput event should not have been dispatched"); 222 is(textInput[0], 3, "input event should not have been dispatched"); 223 input.blur(); 224 is(textBeforeInput[0], 5, "beforeinput event should not have been dispatched"); 225 is(textInput[0], 3, "input event should not have been dispatched"); 226 227 input.focus(); 228 expectedInputType = "insertText"; 229 expectedData = "f"; 230 expectedBeforeInputCancelable = true; 231 sendString("f"); 232 is(textBeforeInput[0], 6, "beforeinput event should've been dispatched"); 233 is(textInput[0], 4, "input event should've been dispatched"); 234 input.value = 'bar'; 235 is(textBeforeInput[0], 6, "beforeinput event should not have been dispatched"); 236 is(textInput[0], 4, "input event should not have been dispatched"); 237 input.blur(); 238 is(textBeforeInput[0], 6, "beforeinput event should not have been dispatched"); 239 is(textInput[0], 4, "input event should not have been dispatched"); 240 241 // Same for textarea. 242 var textarea = document.getElementById("textarea"); 243 textarea.focus(); 244 expectedInputType = "insertText"; 245 expectedData = "f"; 246 expectedBeforeInputCancelable = true; 247 sendString("f"); 248 is(textareaBeforeInput, 1, "beforeinput event should've been dispatched"); 249 is(textareaInput, 1, "input event should've been dispatched"); 250 textarea.blur(); 251 is(textareaBeforeInput, 1, "beforeinput event should not have been dispatched"); 252 is(textareaInput, 1, "input event should not have been dispatched"); 253 254 textarea.focus(); 255 textarea.value = 'foo'; 256 is(textareaBeforeInput, 1, "beforeinput event should not have been dispatched"); 257 is(textareaInput, 1, "input event should not have been dispatched"); 258 textarea.blur(); 259 is(textareaBeforeInput, 1, "beforeinput event should not have been dispatched"); 260 is(textareaInput, 1, "input event should not have been dispatched"); 261 262 textarea.focus(); 263 expectedInputType = "insertText"; 264 expectedData = "f"; 265 expectedBeforeInputCancelable = true; 266 sendString("f"); 267 is(textareaBeforeInput, 2, "beforeinput event should've been dispatched"); 268 is(textareaInput, 2, "input event should've been dispatched"); 269 textarea.value = 'bar'; 270 is(textareaBeforeInput, 2, "beforeinput event should not have been dispatched"); 271 is(textareaInput, 2, "input event should not have been dispatched"); 272 expectedInputType = "deleteContentBackward"; 273 expectedData = null; 274 expectedBeforeInputCancelable = true; 275 synthesizeKey("KEY_Backspace"); 276 is(textareaBeforeInput, 3, "beforeinput event should've been dispatched"); 277 is(textareaInput, 3, "input event should've been dispatched"); 278 textarea.blur(); 279 is(textareaBeforeInput, 3, "beforeinput event should not have been dispatched"); 280 is(textareaInput, 3, "input event should not have been dispatched"); 281 282 // Non-text input tests: 283 for (var i = 0; i < nonTextTypes.length; ++i) { 284 // Button, submit, image and reset input type tests. 285 if (i < 4) { 286 input = document.getElementById("input_" + nonTextTypes[i]); 287 input.focus(); 288 input.click(); 289 is(nonTextBeforeInput[i], 0, "beforeinput event doesn't apply"); 290 is(nonTextInput[i], 0, "input event doesn't apply"); 291 input.blur(); 292 is(nonTextBeforeInput[i], 0, "beforeinput event doesn't apply"); 293 is(nonTextInput[i], 0, "input event doesn't apply"); 294 } 295 // For radio and checkboxes, input event should be dispatched. 296 else { 297 input = document.getElementById("input_" + nonTextTypes[i]); 298 input.focus(); 299 input.click(); 300 is(nonTextBeforeInput[i], 0, "beforeinput event should not have been dispatched"); 301 is(nonTextInput[i], 1, "input event should've been dispatched"); 302 input.blur(); 303 is(nonTextBeforeInput[i], 0, "beforeinput event should not have been dispatched"); 304 is(nonTextInput[i], 1, "input event should not have been dispatched"); 305 306 // Test that input event is not dispatched if click event is cancelled. 307 function preventDefault(e) { 308 e.preventDefault(); 309 } 310 input.addEventListener("click", preventDefault); 311 input.click(); 312 is(nonTextBeforeInput[i], 0, "beforeinput event shouldn't be dispatched if click event is cancelled"); 313 is(nonTextInput[i], 1, "input event shouldn't be dispatched if click event is cancelled"); 314 input.removeEventListener("click", preventDefault); 315 } 316 } 317 318 // Type changes. 319 var input = document.createElement('input'); 320 input.type = 'text'; 321 input.value = 'foo'; 322 input.onbeforeinput = function () { 323 ok(false, "we shouldn't get a beforeinput event when the type changes"); 324 }; 325 input.oninput = function() { 326 ok(false, "we shouldn't get an input event when the type changes"); 327 }; 328 input.type = 'range'; 329 isnot(input.value, 'foo'); 330 331 // Tests for type='range'. 332 var range = document.getElementById("input_range"); 333 334 range.focus(); 335 sendString("a"); 336 range.blur(); 337 is(rangeBeforeInput, 0, "beforeinput event shouldn't be dispatched on range input " + 338 "element for key changes that don't change its value"); 339 is(rangeInput, 0, "input event shouldn't be dispatched on range input " + 340 "element for key changes that don't change its value"); 341 342 range.focus(); 343 synthesizeKey("KEY_Home"); 344 is(rangeBeforeInput, 0, "beforeinput event shouldn't be dispatched even for key changes"); 345 is(rangeInput, 1, "input event should be dispatched for key changes"); 346 range.blur(); 347 is(rangeBeforeInput, 0, "beforeinput event shouldn't be dispatched on blur"); 348 is(rangeInput, 1, "input event shouldn't be dispatched on blur"); 349 350 range.focus(); 351 var bcr = range.getBoundingClientRect(); 352 var centerOfRangeX = bcr.width / 2; 353 var centerOfRangeY = bcr.height / 2; 354 synthesizeMouse(range, centerOfRangeX - 10, centerOfRangeY, { type: "mousedown" }); 355 is(rangeBeforeInput, 0, "beforeinput event shouldn't be dispatched on mousedown if the value changes"); 356 is(rangeInput, 2, "Input event should be dispatched on mousedown if the value changes"); 357 synthesizeMouse(range, centerOfRangeX - 5, centerOfRangeY, { type: "mousemove" }); 358 is(rangeBeforeInput, 0, "beforeinput event shouldn't be dispatched during a drag"); 359 is(rangeInput, 3, "Input event should be dispatched during a drag"); 360 synthesizeMouse(range, centerOfRangeX, centerOfRangeY, { type: "mouseup" }); 361 is(rangeBeforeInput, 0, "beforeinput event shouldn't be dispatched at the end of a drag"); 362 is(rangeInput, 4, "Input event should be dispatched at the end of a drag"); 363 364 // Tests for type='number'. 365 // We only test key events here since input events for mouse event changes 366 // are tested in test_input_number_mouse_events.html 367 var number = document.getElementById("input_number"); 368 369 if (isDesktop) { // up/down arrow keys not supported on android 370 number.value = ""; 371 number.focus(); 372 // <input type="number">'s inputType value hasn't been decided, see 373 // https://github.com/w3c/input-events/issues/88 374 expectedInputType = "insertReplacementText"; 375 expectedData = "1"; 376 expectedBeforeInputCancelable = false; 377 synthesizeKey("KEY_ArrowUp"); 378 is(numberBeforeInput, 1, "beforeinput event should be dispatched for up/down arrow key keypress"); 379 is(numberInput, 1, "input event should be dispatched for up/down arrow key keypress"); 380 is(number.value, "1", "sanity check value of number control after keypress"); 381 382 // `data` will be the value of the input, but we can't change 383 // `expectedData` and use {repeat: 3} at the same time. 384 skipExpectedDataCheck = true; 385 synthesizeKey("KEY_ArrowDown", {repeat: 3}); 386 is(numberBeforeInput, 4, "beforeinput event should be dispatched for each up/down arrow key keypress event, even when rapidly repeated"); 387 is(numberInput, 4, "input event should be dispatched for each up/down arrow key keypress event, even when rapidly repeated"); 388 is(number.value, "-2", "sanity check value of number control after multiple keydown events"); 389 skipExpectedDataCheck = false; 390 391 number.blur(); 392 is(numberBeforeInput, 4, "beforeinput event shouldn't be dispatched on blur"); 393 is(numberInput, 4, "input event shouldn't be dispatched on blur"); 394 } 395 396 MockFilePicker.cleanup(); 397 SimpleTest.finish(); 398 } 399 400 SimpleTest.waitForExplicitFinish(); 401 document.addEventListener("DOMContentLoaded", () => { 402 init(); 403 SimpleTest.waitForFocus(testUserInput); 404 }, {once: true}); 405 406 </script> 407 </pre> 408 </body> 409 </html>