browser_search_within_preferences_2.js (8171B)
1 "use strict"; 2 /** 3 * This file contains tests for the Preferences search bar. 4 */ 5 6 /** 7 * Test that we only search the selected child of a XUL deck. 8 * When we search "Remove Account", 9 * it should not show the "Remove Account" button if the Firefox account is not logged in yet. 10 */ 11 add_task(async function () { 12 await openPreferencesViaOpenPreferencesAPI("paneSync", { leaveOpen: true }); 13 14 let weavePrefsDeck = 15 gBrowser.contentDocument.getElementById("weavePrefsDeck"); 16 is( 17 weavePrefsDeck.selectedIndex, 18 0, 19 "Should select the #noFxaAccount child node" 20 ); 21 22 // Performs search. 23 let searchInput = gBrowser.contentDocument.getElementById("searchInput"); 24 25 is( 26 searchInput, 27 gBrowser.contentDocument.activeElement.closest("#searchInput"), 28 "Search input should be focused when visiting preferences" 29 ); 30 31 let query = "Sync"; 32 let searchCompletedPromise = BrowserTestUtils.waitForEvent( 33 gBrowser.contentWindow, 34 "PreferencesSearchCompleted", 35 evt => evt.detail == query 36 ); 37 EventUtils.sendString(query); 38 await searchCompletedPromise; 39 40 let mainPrefTag = gBrowser.contentDocument.getElementById("mainPrefPane"); 41 for (let i = 0; i < mainPrefTag.childElementCount; i++) { 42 let child = mainPrefTag.children[i]; 43 if (child.id == "header-searchResults" || child.id == "weavePrefsDeck") { 44 is_element_visible(child, "Should be in search results"); 45 } else if (child.id) { 46 is_element_hidden(child, "Should not be in search results"); 47 } 48 } 49 50 // Ensure the "Remove Account" button exists in the hidden child of the <xul:deck>. 51 let unlinkFxaAccount = weavePrefsDeck.children[1].querySelector( 52 "#unverifiedUnlinkFxaAccount" 53 ); 54 is( 55 unlinkFxaAccount.label, 56 "Remove Account", 57 "The Remove Account button should exist" 58 ); 59 60 // Performs search. 61 searchInput.focus(); 62 query = "Remove Account"; 63 searchCompletedPromise = BrowserTestUtils.waitForEvent( 64 gBrowser.contentWindow, 65 "PreferencesSearchCompleted", 66 evt => evt.detail == query 67 ); 68 EventUtils.sendString(query); 69 await searchCompletedPromise; 70 71 let noResultsEl = gBrowser.contentDocument.querySelector( 72 "#no-results-message" 73 ); 74 is_element_visible(noResultsEl, "Should be reporting no results"); 75 76 BrowserTestUtils.removeTab(gBrowser.selectedTab); 77 }); 78 79 /** 80 * Test that we search using `search-l10n-ids`. 81 * 82 * The test uses element `showUpdateHistory` and 83 * l10n id `language-and-appearance-header` and expects the element 84 * to be matched on the first word from the l10n id value ("Language" in en-US). 85 */ 86 add_task(async function () { 87 let l10nId = "language-and-appearance-header"; 88 89 await openPreferencesViaOpenPreferencesAPI("paneGeneral", { 90 leaveOpen: true, 91 }); 92 93 // First, lets make sure that the element is not matched without 94 // `search-l10n-ids`. 95 { 96 let searchInput = gBrowser.contentDocument.getElementById("searchInput"); 97 let suhElem = gBrowser.contentDocument.getElementById("showUpdateHistory"); 98 99 is( 100 searchInput, 101 gBrowser.contentDocument.activeElement.closest("#searchInput"), 102 "Search input should be focused when visiting preferences" 103 ); 104 105 ok( 106 !suhElem.getAttribute("search-l10n-ids").includes(l10nId), 107 "showUpdateHistory element should not contain the l10n id here." 108 ); 109 110 let query = "Language"; 111 let searchCompletedPromise = BrowserTestUtils.waitForEvent( 112 gBrowser.contentWindow, 113 "PreferencesSearchCompleted", 114 evt => evt.detail == query 115 ); 116 EventUtils.sendString(query); 117 await searchCompletedPromise; 118 119 is_element_hidden( 120 suhElem, 121 "showUpdateHistory should not be in search results" 122 ); 123 } 124 125 await BrowserTestUtils.removeTab(gBrowser.selectedTab); 126 127 // Now, let's add the l10n id to the element and perform the same search again. 128 129 await openPreferencesViaOpenPreferencesAPI("paneGeneral", { 130 leaveOpen: true, 131 }); 132 133 { 134 let searchInput = gBrowser.contentDocument.getElementById("searchInput"); 135 136 is( 137 searchInput, 138 gBrowser.contentDocument.activeElement.closest("#searchInput"), 139 "Search input should be focused when visiting preferences" 140 ); 141 142 let suhElem = gBrowser.contentDocument.getElementById("showUpdateHistory"); 143 suhElem.setAttribute("search-l10n-ids", l10nId); 144 145 let query = "Language"; 146 let searchCompletedPromise = BrowserTestUtils.waitForEvent( 147 gBrowser.contentWindow, 148 "PreferencesSearchCompleted", 149 evt => evt.detail == query 150 ); 151 EventUtils.sendString(query); 152 await searchCompletedPromise; 153 154 if ( 155 AppConstants.platform === "win" && 156 Services.sysinfo.getProperty("hasWinPackageId") 157 ) { 158 is_element_hidden( 159 suhElem, 160 "showUpdateHistory should not be in search results" 161 ); 162 } else { 163 is_element_visible( 164 suhElem, 165 "showUpdateHistory should be in search results" 166 ); 167 } 168 } 169 170 await BrowserTestUtils.removeTab(gBrowser.selectedTab); 171 }); 172 173 /** 174 * Test that search works as expected for custom elements that utilize both 175 * slots and shadow DOM. We should be able to find text the shadow DOM. 176 */ 177 add_task(async function testSearchShadowDOM() { 178 await openPreferencesViaOpenPreferencesAPI("paneGeneral", { 179 leaveOpen: true, 180 }); 181 182 // Create the toggle. 183 let { mozElements, SHADOW_DOM_TEXT } = createMozCustomElements(gBrowser); 184 185 mozElements.forEach(el => { 186 ok( 187 !BrowserTestUtils.isVisible(el), 188 `${el.localName} is not visible prior to search.` 189 ); 190 }); 191 192 // Perform search with text found in moz-toggle's shadow DOM. 193 let query = SHADOW_DOM_TEXT; 194 let searchCompletedPromise = BrowserTestUtils.waitForEvent( 195 gBrowser.contentWindow, 196 "PreferencesSearchCompleted", 197 evt => evt.detail == query 198 ); 199 EventUtils.sendString(query); 200 await searchCompletedPromise; 201 mozElements.forEach(el => { 202 ok( 203 BrowserTestUtils.isVisible(el), 204 `${el.localName} is visible after searching for string in the shadow DOM.` 205 ); 206 }); 207 208 BrowserTestUtils.removeTab(gBrowser.selectedTab); 209 }); 210 211 /** 212 * Test that search works as expected for custom elements that utilize both 213 * slots and shadow DOM. We should be able to find text the light DOM. 214 */ 215 add_task(async function testSearchLightDOM() { 216 await openPreferencesViaOpenPreferencesAPI("paneGeneral", { 217 leaveOpen: true, 218 }); 219 220 // Create the toggle. 221 let { mozElements, LIGHT_DOM_TEXT } = createMozCustomElements(gBrowser, [ 222 "moz-toggle", 223 ]); 224 let toggle = mozElements[0]; 225 226 // Perform search with text found in moz-toggle's slotted content. 227 let query = LIGHT_DOM_TEXT; 228 let searchCompletedPromise = BrowserTestUtils.waitForEvent( 229 gBrowser.contentWindow, 230 "PreferencesSearchCompleted", 231 evt => evt.detail == query 232 ); 233 EventUtils.sendString(query); 234 await searchCompletedPromise; 235 ok( 236 BrowserTestUtils.isVisible(toggle), 237 "Toggle is visible again after searching for text found in slotted content." 238 ); 239 240 BrowserTestUtils.removeTab(gBrowser.selectedTab); 241 }); 242 243 const MOZ_CUSTOM_ELEMENTS = [ 244 "moz-toggle", 245 "moz-radio-group", 246 "moz-radio", 247 "moz-checkbox", 248 ]; 249 250 // Create multiple moz- custom elements with the same label. 251 function createMozCustomElements(gBrowser, elements = MOZ_CUSTOM_ELEMENTS) { 252 const SHADOW_DOM_TEXT = "This text lives in the shadow DOM"; 253 const LIGHT_DOM_TEXT = "This text lives in the light DOM"; 254 255 let doc = gBrowser.contentDocument; 256 let mozElements = elements.map(tag => { 257 let el = doc.createElement(tag); 258 el.label = SHADOW_DOM_TEXT; 259 return el; 260 }); 261 let [toggle, radioGroup, radioButton, ...rest] = mozElements; 262 let protectionsGroup = doc.getElementById("trackingGroup"); 263 264 if (toggle) { 265 let link = doc.createElement("a"); 266 link.href = "https://mozilla.org/"; 267 link.textContent = LIGHT_DOM_TEXT; 268 toggle.append(link); 269 link.slot = "support-link"; 270 protectionsGroup.append(toggle); 271 } 272 273 if (radioGroup && radioButton) { 274 radioGroup.appendChild(radioButton); 275 protectionsGroup.append(radioGroup); 276 } 277 278 protectionsGroup.append(...rest); 279 280 return { SHADOW_DOM_TEXT, LIGHT_DOM_TEXT, mozElements }; 281 }