select-events-2.optional.html (8868B)
1 <!DOCTYPE html> 2 <title>HTMLselectElement Test: events</title> 3 <script src="/resources/testharness.js"></script> 4 <script src="/resources/testharnessreport.js"></script> 5 <script src="/resources/testdriver.js"></script> 6 <script src="/resources/testdriver-actions.js"></script> 7 <script src="/resources/testdriver-vendor.js"></script> 8 9 <!-- This test is optional because the HTML spec does not require that specific 10 behaviors are mapped to specific keyboard buttons. --> 11 12 <select id="select0"> 13 <button id="select0-button"> 14 <selectedcontent></selectedcontent> 15 select0-button 16 </button> 17 <option>one</option> 18 <option>two</option> 19 <option>three</option> 20 </select> 21 22 <select id="select1"> 23 <option>one</option> 24 <option> 25 two 26 <button id="select1-button">select1-button</button> 27 </option> 28 <option>three</option> 29 </select> 30 31 <select id="select2"> 32 <option>one</option> 33 <option>two</option> 34 <option>three</option> 35 </select> 36 37 <select id="select3"> 38 <option>same</option> 39 <option>same</option> 40 </select> 41 42 <select id="select4"> 43 <option>one</option> 44 <option id="select4-option2">two</option> 45 </select> 46 47 <select id="select5WithTabIndex" tabindex="1"> 48 <option>one</option> 49 <option>two</option> 50 </select> 51 52 <input id="input6"/> 53 <select id="select7"> 54 <button id="select7-button"> 55 select7-button 56 </button> 57 <option>one</option> 58 <option>two</option> 59 </select> 60 61 <style> 62 select, ::picker(select) { 63 appearance: base-select; 64 } 65 </style> 66 67 <script> 68 69 function clickOn(element) { 70 const actions = new test_driver.Actions(); 71 return actions.pointerMove(0, 0, {origin: element}) 72 .pointerDown({button: actions.ButtonType.LEFT}) 73 .pointerUp({button: actions.ButtonType.LEFT}) 74 .send(); 75 } 76 77 promise_test(async (t) => { 78 const select = document.getElementById("select0"); 79 assert_false(select.matches(':open')); 80 81 const selectButtonWatcher = new EventWatcher(t, select, ["mousedown"]); 82 const selectButtonPromise = selectButtonWatcher.wait_for("mousedown").then((e) => { 83 assert_false(select.matches(':open'), "Listbox shouldn't have opened yet"); 84 // PreventDefaulting the event here should prevent UA controller code 85 // on the button part from opening the listbox. 86 e.preventDefault(); 87 }); 88 89 const selectWatcher = new EventWatcher(t, select, ["mousedown"]); 90 const selectPromise = selectWatcher.wait_for("mousedown").then((e) => { 91 assert_true(e.defaultPrevented, "Event should have been defaultPrevented by select mousedown handler"); 92 assert_false(select.matches(':open'), "Listbox shouldn't have opened, because mousedown event was defaultPrevented."); 93 }); 94 95 return Promise.all([selectButtonPromise, selectPromise, clickOn(select)]); 96 }, "Button controller code should not run if the mousedown event is preventDefaulted."); 97 98 // See https://w3c.github.io/webdriver/#keyboard-actions 99 const KEY_CODE_MAP = { 100 'Tab': '\uE004', 101 'Enter': '\uE007', 102 'Space': '\uE00D', 103 'ArrowUp': '\uE013', 104 'ArrowDown': '\uE015', 105 }; 106 107 promise_test(async (t) => { 108 const select = document.getElementById("select1"); 109 await clickOn(select); 110 assert_true(select.matches(':open')); 111 112 const selectButtonWatcher = new EventWatcher(t, select, ["mousedown"]); 113 const selectButtonPromise = selectButtonWatcher.wait_for("mousedown").then((e) => { 114 assert_true(select.matches(':open'), "Listbox shouldn't have closed yet"); 115 // PreventDefaulting the event here should prevent UA controller code 116 // on the listbox part from selecting the option and closing the listbox. 117 e.preventDefault(); 118 }); 119 120 const selectWatcher = new EventWatcher(t, select, ["mousedown"]); 121 const selectPromise = selectWatcher.wait_for("mousedown").then((e) => { 122 assert_true(e.defaultPrevented, "Event should have been defaultPrevented by select mousedown handler"); 123 assert_true(select.matches(':open'), "Listbox shouldn't have closed, because keydown event was defaultPrevented."); 124 assert_equals(select.value, "one", "<select> shouldn't have changed value, because keydown event was defaultPrevented."); 125 }); 126 127 return Promise.all([selectButtonPromise, selectPromise, clickOn(select)]); 128 }, "Listbox controller code should not run if the mousedown event is preventDefaulted."); 129 130 promise_test(async (t) => { 131 const select = document.getElementById("select2"); 132 const events = []; 133 134 select.addEventListener("input", t.step_func((e) => { 135 assert_true(e.composed, "input event should be composed."); 136 events.push('input'); 137 })); 138 select.addEventListener("change", t.step_func((e) => { 139 assert_false(e.composed, "change event should not be composed."); 140 events.push('change'); 141 })); 142 143 await clickOn(select); 144 assert_true(select.matches(':open')); 145 await test_driver.send_keys(document.activeElement, KEY_CODE_MAP.Enter); 146 assert_false(select.matches(':open')); 147 assert_equals(select.value, "one"); 148 assert_array_equals(events, [], "input and change shouldn't fire if value wansn't changed."); 149 150 await clickOn(select); 151 assert_true(select.matches(':open')); 152 await test_driver.send_keys(document.activeElement, KEY_CODE_MAP.ArrowDown); 153 assert_equals(select.value, "one", "value shouldn't change when user switches options with arrow key."); 154 assert_array_equals(events, [], "input event should not fire when user switches options with arrow key."); 155 156 await test_driver.send_keys(document.activeElement, KEY_CODE_MAP.Enter); 157 assert_equals(select.value, "two"); 158 assert_array_equals(events, ['input', 'change'], "input and change should fire after pressing enter."); 159 }, "<select> should fire input and change events when new option is selected."); 160 161 promise_test(async (t) => { 162 const select = document.getElementById("select3"); 163 const events = []; 164 165 select.addEventListener("input", t.step_func((e) => { 166 assert_true(e.composed, "input event should be composed."); 167 events.push('input'); 168 })); 169 select.addEventListener("change", t.step_func((e) => { 170 assert_false(e.composed, "change event should not be composed."); 171 events.push('change'); 172 })); 173 174 await clickOn(select); 175 assert_true(select.matches(':open')); 176 await test_driver.send_keys(select, KEY_CODE_MAP.ArrowDown); 177 assert_array_equals(events, [], "input event not should have fired after ArrowDown."); 178 await test_driver.send_keys(select, KEY_CODE_MAP.Enter); 179 assert_array_equals(events, [], "input and change should not fire after pressing Enter."); 180 }, "<select> should not fire input and change events when new selected option has the same value as the old."); 181 182 promise_test(async (t) => { 183 const select = document.getElementById("select4"); 184 const selectOption2 = document.getElementById("select4-option2"); 185 let input_event_count = 0; 186 let change_event_count = 0; 187 188 select.addEventListener("input", t.step_func((e) => { 189 assert_true(e.composed, "input event should be composed"); 190 assert_equals(input_event_count, 0, "input event should not fire twice"); 191 assert_equals(change_event_count, 0, "input event should not fire before change"); 192 input_event_count++; 193 })); 194 195 select.addEventListener("change", t.step_func((e) => { 196 assert_false(e.composed, "change event should not be composed"); 197 assert_equals(input_event_count, 1, "change event should fire after input"); 198 assert_equals(change_event_count, 0, "change event should not fire twice"); 199 change_event_count++; 200 })); 201 202 await clickOn(select); 203 assert_true(select.matches(':open')); 204 await clickOn(selectOption2); 205 assert_equals(input_event_count, 1, "input event shouldn't fire when selected option didn't change"); 206 assert_equals(change_event_count, 1, "change event shouldn't fire when selected option didn't change"); 207 }, "<select> should fire input and change events when option in listbox is clicked"); 208 209 promise_test(async() => { 210 const select = document.getElementById("select2"); 211 await test_driver.send_keys(select, " "); 212 assert_true(select.matches(':open'), "<Space> should open select"); 213 await test_driver.send_keys(document.activeElement, KEY_CODE_MAP.Enter); 214 assert_false(select.matches(':open'), "<Enter> should close select"); 215 }, "Check that <Space> opens <select>."); 216 217 promise_test(async() => { 218 const select = document.getElementById("select5WithTabIndex"); 219 await test_driver.send_keys(select, " "); 220 assert_true(select.matches(':open'), "<Space> should open select"); 221 await test_driver.send_keys(document.activeElement, KEY_CODE_MAP.Enter); 222 assert_false(select.matches(':open'), "<Enter> should close select"); 223 }, "Check that <Space> opens <select> when <select> specifies tabindex"); 224 </script>