test_searchbox-with-autocomplete.html (8928B)
1 <!-- This Source Code Form is subject to the terms of the Mozilla Public 2 - License, v. 2.0. If a copy of the MPL was not distributed with this 3 - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> 4 <!DOCTYPE html> 5 <html> 6 <!-- 7 Test the searchbox and autocomplete-popup components 8 --> 9 <head> 10 <meta charset="utf-8"> 11 <title>SearchBox component test</title> 12 <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> 13 <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> 14 <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> 15 </head> 16 <body> 17 <script src="head.js"></script> 18 <script> 19 "use strict"; 20 window.onload = async function () { 21 /** 22 * Takes a DOMNode with its children as list items, 23 * Typically UL > LI and each item's text value is 24 * compared with the reference item's value as a test 25 * 26 * @param {Node} list - Node to be compared 27 * @param {Array} reference - Reference array for comparison. The selected index is 28 * highlighted as a single element array ie. ["[abc]", "ab", "abcPQR"], 29 * Here the element "abc" is highlighted 30 */ 31 function compareAutocompleteList(list, reference) { 32 const delimiter = " - "; 33 const observedList = [...list.children].map(el => { 34 return el.classList.contains("autocomplete-selected") 35 ? `[${el.textContent}]` 36 : el.textContent 37 }); 38 is(observedList.join(delimiter), reference.join(delimiter), 39 "Autocomplete items are rendered as expected"); 40 } 41 42 function compareCursorPosition(initialElement) { 43 const initialPosition = initialElement.selectionStart; 44 return (element) => { 45 is(element.selectionStart, initialPosition, "Input cursor position is not changed"); 46 } 47 } 48 49 const React = browserRequire("devtools/client/shared/vendor/react"); 50 const SearchBox = React.createFactory( 51 browserRequire("devtools/client/shared/components/SearchBox") 52 ); 53 const { component, $ } = await createComponentTest(SearchBox, { 54 type: "search", 55 autocompleteProvider: (filter) => { 56 const baseList = [ 57 "foo", 58 "BAR", 59 "baZ", 60 "abc", 61 "pqr", 62 "xyz", 63 "ABC", 64 "a1", 65 "a2", 66 "a3", 67 "a4", 68 "a5", 69 ]; 70 if (!filter) { 71 return []; 72 } 73 74 const tokens = filter.split(/\s+/g); 75 const lastToken = tokens[tokens.length - 1]; 76 const previousTokens = tokens.slice(0, tokens.length - 1); 77 78 if (!lastToken) { 79 return []; 80 } 81 82 return baseList 83 .filter((item) => { 84 return item.toLowerCase().startsWith(lastToken.toLowerCase()) 85 && item.toLowerCase() !== lastToken.toLowerCase(); 86 }) 87 .sort() 88 .map(item => ({ 89 value: [...previousTokens, item].join(" "), 90 displayValue: item, 91 })); 92 }, 93 onChange: () => null, 94 }); 95 96 async function testSearchBoxWithAutocomplete() { 97 ok(!$(".devtools-autocomplete-popup"), "Autocomplete list not visible"); 98 99 $(".devtools-searchinput").focus(); 100 await forceRender(component); // Wait for state update 101 ok(!$(".devtools-autocomplete-popup"), "Autocomplete list not visible"); 102 103 sendString("a"); 104 await forceRender(component); 105 106 compareAutocompleteList($(".devtools-autocomplete-listbox"), [ 107 "[ABC]", 108 "a1", 109 "a2", 110 "a3", 111 "a4", 112 "a5", 113 "abc", 114 ]); 115 116 // Blur event 117 $(".devtools-searchinput").blur(); 118 await forceRender(component); 119 ok(!component.state.focused, "focused state was properly set"); 120 ok(!$(".devtools-autocomplete-popup"), "Autocomplete list removed from DOM"); 121 } 122 123 async function testKeyEventsWithAutocomplete() { 124 // Clear the initial input 125 $(".devtools-searchinput").focus(); 126 const cursorPositionIsNotChanged = compareCursorPosition($(".devtools-searchinput")); 127 128 // ArrowDown 129 synthesizeKey("KEY_ArrowDown"); 130 await forceRender(component); 131 compareAutocompleteList($(".devtools-autocomplete-listbox"), [ 132 "ABC", 133 "[a1]", 134 "a2", 135 "a3", 136 "a4", 137 "a5", 138 "abc", 139 ]); 140 ok($(".devtools-autocomplete-listbox .autocomplete-item:nth-child(2)") 141 .className.includes("autocomplete-selected"), 142 "Selection class applied"); 143 144 // A double ArrowUp should roll back to the bottom of the list 145 synthesizeKey("KEY_ArrowUp"); 146 synthesizeKey("KEY_ArrowUp"); 147 await forceRender(component); 148 compareAutocompleteList($(".devtools-autocomplete-listbox"), [ 149 "ABC", 150 "a1", 151 "a2", 152 "a3", 153 "a4", 154 "a5", 155 "[abc]", 156 ]); 157 cursorPositionIsNotChanged($(".devtools-searchinput")); 158 159 // PageDown should take -5 places up 160 synthesizeKey("KEY_PageUp"); 161 await forceRender(component); 162 compareAutocompleteList($(".devtools-autocomplete-listbox"), [ 163 "ABC", 164 "[a1]", 165 "a2", 166 "a3", 167 "a4", 168 "a5", 169 "abc", 170 ]); 171 cursorPositionIsNotChanged($(".devtools-searchinput")); 172 173 // PageDown should take +5 places down 174 synthesizeKey("KEY_PageDown"); 175 await forceRender(component); 176 compareAutocompleteList($(".devtools-autocomplete-listbox"), [ 177 "ABC", 178 "a1", 179 "a2", 180 "a3", 181 "a4", 182 "a5", 183 "[abc]", 184 ]); 185 cursorPositionIsNotChanged($(".devtools-searchinput")); 186 187 // Home should take to the top of the list 188 synthesizeKey("KEY_Home"); 189 await forceRender(component); 190 compareAutocompleteList($(".devtools-autocomplete-listbox"), [ 191 "[ABC]", 192 "a1", 193 "a2", 194 "a3", 195 "a4", 196 "a5", 197 "abc", 198 ]); 199 cursorPositionIsNotChanged($(".devtools-searchinput")); 200 201 // End should take to the bottom of the list 202 synthesizeKey("KEY_End"); 203 await forceRender(component); 204 compareAutocompleteList($(".devtools-autocomplete-listbox"), [ 205 "ABC", 206 "a1", 207 "a2", 208 "a3", 209 "a4", 210 "a5", 211 "[abc]", 212 ]); 213 cursorPositionIsNotChanged($(".devtools-searchinput")); 214 215 // Key down in existing state should rollover to the top 216 synthesizeKey("KEY_ArrowDown"); 217 await forceRender(component); 218 // Tab should select the component and hide popup 219 synthesizeKey("KEY_Tab"); 220 await forceRender(component); 221 is(component.state.value, "ABC", "Tab hit selects the item"); 222 ok(!$(".devtools-autocomplete-popup"), "Tab hit hides the popup"); 223 224 // Activate popup by removing a key 225 synthesizeKey("KEY_Backspace"); 226 await forceRender(component); 227 ok($(".devtools-autocomplete-popup"), "Popup is up"); 228 compareAutocompleteList($(".devtools-autocomplete-listbox"), [ 229 "[ABC]", 230 "abc" 231 ]); 232 233 // Enter key selection 234 synthesizeKey("KEY_ArrowUp"); 235 await forceRender(component); 236 synthesizeKey("KEY_Enter"); 237 is(component.state.value, "abc", "Enter selection"); 238 ok(!$(".devtools-autocomplete-popup"), "Enter/Return hides the popup"); 239 240 // Escape should remove the autocomplete component 241 synthesizeKey("KEY_Backspace"); 242 await forceRender(component); 243 synthesizeKey("KEY_Escape"); 244 await forceRender(component); 245 ok(!$(".devtools-autocomplete-popup"), 246 "Autocomplete list removed from DOM on Escape"); 247 } 248 249 async function testMouseEventsWithAutocomplete() { 250 $(".devtools-searchinput").focus(); 251 await setState(component, { 252 value: "", 253 focused: true, 254 }); 255 await forceRender(component); 256 257 // ArrowDown 258 synthesizeKey("KEY_ArrowDown"); 259 await forceRender(component); 260 synthesizeMouseAtCenter($(".devtools-searchinput"), {}, window); 261 await forceRender(component); 262 is(component.state.focused, true, "Component should now be focused"); 263 264 sendString("pq"); 265 await forceRender(component); 266 synthesizeMouseAtCenter( 267 $(".devtools-autocomplete-listbox .autocomplete-item:nth-child(1)"), 268 {}, window 269 ); 270 await forceRender(component); 271 is(component.state.value, "pqr", "Mouse click selects the item."); 272 ok(!$(".devtools-autocomplete-popup"), "Mouse click on item hides the popup"); 273 } 274 275 async function testTokenizedAutocomplete() { 276 // Test for string "pqr ab" which should show list of ABC, abc 277 sendString(" ab"); 278 await forceRender(component); 279 compareAutocompleteList($(".devtools-autocomplete-listbox"), [ 280 "[ABC]", 281 "abc" 282 ]); 283 284 // Select the first element, value now should be "pqr ABC" 285 synthesizeMouseAtCenter( 286 $(".devtools-autocomplete-listbox .autocomplete-item:nth-child(1)"), 287 {}, window 288 ); 289 is(component.state.value, "pqr ABC", "Post Tokenization value selection"); 290 } 291 292 add_task(async function () { 293 await testSearchBoxWithAutocomplete(); 294 await testKeyEventsWithAutocomplete(); 295 await testMouseEventsWithAutocomplete(); 296 await testTokenizedAutocomplete(); 297 }); 298 }; 299 </script> 300 </body> 301 </html>