browser_contextmenu_sendtab.js (11465B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 Services.scriptloader.loadSubScript( 7 "chrome://mochitests/content/browser/browser/base/content/test/general/head.js", 8 this 9 ); 10 Services.scriptloader.loadSubScript( 11 "chrome://mochitests/content/browser/browser/components/customizableui/test/head.js", 12 this 13 ); 14 15 const fxaDevices = [ 16 { 17 id: 1, 18 name: "Foo", 19 availableCommands: { "https://identity.mozilla.com/cmd/open-uri": "baz" }, 20 lastAccessTime: Date.now(), 21 }, 22 { 23 id: 2, 24 name: "Bar", 25 availableCommands: { "https://identity.mozilla.com/cmd/open-uri": "boo" }, 26 lastAccessTime: Date.now() + 60000, // add 30min 27 }, 28 { 29 id: 3, 30 name: "Baz", 31 clientRecord: "bar", 32 lastAccessTime: Date.now() + 120000, // add 60min 33 }, // Legacy send tab target (no availableCommands). 34 { id: 4, name: "Homer" }, // Incompatible target. 35 ]; 36 37 let [testTab] = gBrowser.visibleTabs; 38 39 function updateTabContextMenu(tab = gBrowser.selectedTab) { 40 let menu = document.getElementById("tabContextMenu"); 41 var evt = new Event(""); 42 tab.dispatchEvent(evt); 43 // The TabContextMenu initializes its strings only on a focus or mouseover event. 44 // Calls focus event on the TabContextMenu early in the test 45 gBrowser.selectedTab.focus(); 46 menu.openPopup(tab, "end_after", 0, 0, true, false, evt); 47 is( 48 window.TabContextMenu.contextTab, 49 tab, 50 "TabContextMenu context is the expected tab" 51 ); 52 menu.hidePopup(); 53 } 54 55 add_setup(async function () { 56 await SpecialPowers.pushPrefEnv({ 57 set: [ 58 ["browser.urlbar.trustPanel.featureGate", false], 59 ["test.wait300msAfterTabSwitch", true], 60 ], 61 }); 62 63 await promiseSyncReady(); 64 await Services.search.init(); 65 // gSync.init() is called in a requestIdleCallback. Force its initialization. 66 gSync.init(); 67 sinon 68 .stub(Weave.Service.clientsEngine, "getClientByFxaDeviceId") 69 .callsFake(fxaDeviceId => { 70 let target = fxaDevices.find(c => c.id == fxaDeviceId); 71 return target ? target.clientRecord : null; 72 }); 73 sinon.stub(Weave.Service.clientsEngine, "getClientType").returns("desktop"); 74 await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:mozilla"); 75 registerCleanupFunction(() => { 76 gBrowser.removeCurrentTab(); 77 }); 78 is(gBrowser.visibleTabs.length, 2, "there are two visible tabs"); 79 }); 80 81 add_task(async function test_sendTabToDevice_callsFlushLogFile() { 82 const sandbox = setupSendTabMocks({ fxaDevices }); 83 updateTabContextMenu(testTab); 84 await openTabContextMenu("context_sendTabToDevice"); 85 let promiseObserved = promiseObserver("service:log-manager:flush-log-file"); 86 87 await activateMenuItem(); 88 await promiseObserved; 89 ok(true, "Got flush-log-file observer message"); 90 91 await closeConfirmationHint(); 92 sandbox.restore(); 93 }); 94 95 async function checkForConfirmationHint(targetId) { 96 const sandbox = setupSendTabMocks({ fxaDevices }); 97 updateTabContextMenu(testTab); 98 99 await openTabContextMenu("context_sendTabToDevice"); 100 await activateMenuItem(); 101 is( 102 ConfirmationHint._panel.anchorNode.id, 103 targetId, 104 `Hint anchored to ${targetId}` 105 ); 106 await closeConfirmationHint(); 107 sandbox.restore(); 108 } 109 110 add_task(async function test_sendTabToDevice_showsConfirmationHint_fxa() { 111 // We need to change the fxastatus from "not_configured" to show the FxA button. 112 is( 113 document.documentElement.getAttribute("fxastatus"), 114 "not_configured", 115 "FxA button is hidden" 116 ); 117 document.documentElement.setAttribute("fxastatus", "foo"); 118 await checkForConfirmationHint("fxa-toolbar-menu-button"); 119 document.documentElement.setAttribute("fxastatus", "not_configured"); 120 }); 121 122 add_task( 123 async function test_sendTabToDevice_showsConfirmationHint_onOverflowMenu() { 124 // We need to change the fxastatus from "not_configured" to show the FxA button. 125 is( 126 document.documentElement.getAttribute("fxastatus"), 127 "not_configured", 128 "FxA button is hidden" 129 ); 130 document.documentElement.setAttribute("fxastatus", "foo"); 131 132 let navbar = document.getElementById("nav-bar"); 133 134 // Resize the window so that the account button is in the overflow menu. 135 // As of bug 1960002, overflowing the navbar also requires adding extra 136 // buttons. 137 let originalWidth = ensureToolbarOverflow(window, false); 138 139 await TestUtils.waitForCondition(() => navbar.hasAttribute("overflowing")); 140 141 await checkForConfirmationHint("PanelUI-menu-button"); 142 document.documentElement.setAttribute("fxastatus", "not_configured"); 143 144 unensureToolbarOverflow(window, originalWidth); 145 await TestUtils.waitForCondition(() => !navbar.hasAttribute("overflowing")); 146 CustomizableUI.reset(); 147 } 148 ); 149 150 add_task(async function test_sendTabToDevice_showsConfirmationHint_appMenu() { 151 ensureToolbarOverflow(window); 152 153 // If fxastatus is "not_configured" then the FxA button is hidden, and we 154 // should use the appMenu. 155 is( 156 document.documentElement.getAttribute("fxastatus"), 157 "not_configured", 158 "FxA button is hidden" 159 ); 160 await checkForConfirmationHint("PanelUI-menu-button"); 161 }); 162 163 add_task(async function test_tab_contextmenu() { 164 const sandbox = setupSendTabMocks({ fxaDevices }); 165 let expectation = sandbox 166 .mock(gSync) 167 .expects("sendTabToDevice") 168 .once() 169 .withExactArgs( 170 "about:mozilla", 171 [fxaDevices[1]], 172 "The Book of Mozilla, 6:27" 173 ) 174 .returns(true); 175 176 updateTabContextMenu(testTab); 177 await openTabContextMenu("context_sendTabToDevice"); 178 is( 179 document.getElementById("context_sendTabToDevice").hidden, 180 false, 181 "Send tab to device is shown" 182 ); 183 is( 184 document.getElementById("context_sendTabToDeviceSeparator").hidden, 185 false, 186 "Send tab to device separator is shown" 187 ); 188 is( 189 document.getElementById("context_sendTabToDevice").disabled, 190 false, 191 "Send tab to device is enabled" 192 ); 193 194 await activateMenuItem(); 195 await closeConfirmationHint(); 196 197 expectation.verify(); 198 sandbox.restore(); 199 }); 200 201 add_task(async function test_tab_contextmenu_unconfigured() { 202 const sandbox = setupSendTabMocks({ state: UIState.STATUS_NOT_CONFIGURED }); 203 204 updateTabContextMenu(testTab); 205 is( 206 document.getElementById("context_sendTabToDevice").hidden, 207 true, 208 "Send tab to device is hidden" 209 ); 210 is( 211 document.getElementById("context_sendTabToDeviceSeparator").hidden, 212 true, 213 "Send tab to device separator is hidden" 214 ); 215 is( 216 document.getElementById("context_sendTabToDevice").disabled, 217 false, 218 "Send tab to device is enabled" 219 ); 220 221 sandbox.restore(); 222 }); 223 224 add_task(async function test_tab_contextmenu_not_sendable() { 225 const sandbox = setupSendTabMocks({ fxaDevices, isSendableURI: false }); 226 227 updateTabContextMenu(testTab); 228 is( 229 document.getElementById("context_sendTabToDevice").hidden, 230 true, 231 "Send tab to device is hidden" 232 ); 233 is( 234 document.getElementById("context_sendTabToDeviceSeparator").hidden, 235 true, 236 "Send tab to device is hidden" 237 ); 238 is( 239 document.getElementById("context_sendTabToDevice").disabled, 240 true, 241 "Send tab to device is disabled" 242 ); 243 244 sandbox.restore(); 245 }); 246 247 add_task(async function test_tab_contextmenu_not_synced_yet() { 248 const sandbox = setupSendTabMocks({ fxaDevices: null }); 249 250 updateTabContextMenu(testTab); 251 is( 252 document.getElementById("context_sendTabToDevice").hidden, 253 true, 254 "Send tab to device is hidden" 255 ); 256 is( 257 document.getElementById("context_sendTabToDeviceSeparator").hidden, 258 true, 259 "Send tab to device separator is hidden" 260 ); 261 is( 262 document.getElementById("context_sendTabToDevice").disabled, 263 true, 264 "Send tab to device is disabled" 265 ); 266 267 sandbox.restore(); 268 }); 269 270 add_task(async function test_tab_contextmenu_sync_not_ready_configured() { 271 const sandbox = setupSendTabMocks({ syncReady: false }); 272 273 updateTabContextMenu(testTab); 274 is( 275 document.getElementById("context_sendTabToDevice").hidden, 276 true, 277 "Send tab to device is hidden" 278 ); 279 is( 280 document.getElementById("context_sendTabToDeviceSeparator").hidden, 281 true, 282 "Send tab to device is hidden" 283 ); 284 is( 285 document.getElementById("context_sendTabToDevice").disabled, 286 true, 287 "Send tab to device is disabled" 288 ); 289 290 sandbox.restore(); 291 }); 292 293 add_task(async function test_tab_contextmenu_sync_not_ready_other_state() { 294 const sandbox = setupSendTabMocks({ 295 syncReady: false, 296 state: UIState.STATUS_NOT_VERIFIED, 297 }); 298 299 updateTabContextMenu(testTab); 300 is( 301 document.getElementById("context_sendTabToDevice").hidden, 302 true, 303 "Send tab to device is hidden" 304 ); 305 is( 306 document.getElementById("context_sendTabToDeviceSeparator").hidden, 307 true, 308 "Send tab to device separator is hidden" 309 ); 310 is( 311 document.getElementById("context_sendTabToDevice").disabled, 312 false, 313 "Send tab to device is enabled" 314 ); 315 316 sandbox.restore(); 317 }); 318 319 add_task(async function test_tab_contextmenu_fxa_disabled() { 320 const getter = sinon.stub(gSync, "FXA_ENABLED").get(() => false); 321 // Simulate onFxaDisabled() being called on window open. 322 gSync.onFxaDisabled(); 323 324 updateTabContextMenu(testTab); 325 is( 326 document.getElementById("context_sendTabToDevice").hidden, 327 true, 328 "Send tab to device is hidden" 329 ); 330 updateTabContextMenu(testTab); 331 is( 332 document.getElementById("context_sendTabToDeviceSeparator").hidden, 333 true, 334 "Send tab to device separator is hidden" 335 ); 336 337 getter.restore(); 338 [...document.querySelectorAll(".sync-ui-item")].forEach( 339 e => (e.hidden = false) 340 ); 341 }); 342 343 add_task(async function teardown() { 344 Weave.Service.clientsEngine.getClientByFxaDeviceId.restore(); 345 Weave.Service.clientsEngine.getClientType.restore(); 346 }); 347 348 async function openTabContextMenu(openSubmenuId = null) { 349 const contextMenu = document.getElementById("tabContextMenu"); 350 is(contextMenu.state, "closed", "checking if popup is closed"); 351 352 const awaitPopupShown = BrowserTestUtils.waitForEvent( 353 contextMenu, 354 "popupshown" 355 ); 356 EventUtils.synthesizeMouseAtCenter(gBrowser.selectedTab, { 357 type: "contextmenu", 358 button: 2, 359 }); 360 await awaitPopupShown; 361 362 if (openSubmenuId) { 363 const menuPopup = document.getElementById(openSubmenuId).menupopup; 364 const menuPopupPromise = BrowserTestUtils.waitForEvent( 365 menuPopup, 366 "popupshown" 367 ); 368 menuPopup.openPopup(); 369 await menuPopupPromise; 370 } 371 } 372 373 function promiseObserver(topic) { 374 return new Promise(resolve => { 375 let obs = (aSubject, aTopic) => { 376 Services.obs.removeObserver(obs, aTopic); 377 resolve(aSubject); 378 }; 379 Services.obs.addObserver(obs, topic); 380 }); 381 } 382 383 function waitForConfirmationHint() { 384 return BrowserTestUtils.waitForEvent(ConfirmationHint._panel, "popuphidden"); 385 } 386 387 async function activateMenuItem() { 388 let popupHidden = BrowserTestUtils.waitForEvent( 389 document.getElementById("tabContextMenu"), 390 "popuphidden" 391 ); 392 let hintShown = BrowserTestUtils.waitForEvent( 393 ConfirmationHint._panel, 394 "popupshown" 395 ); 396 let menuitem = document 397 .getElementById("context_sendTabToDevicePopupMenu") 398 .querySelector("menuitem"); 399 menuitem.closest("menupopup").activateItem(menuitem); 400 await popupHidden; 401 await hintShown; 402 } 403 404 async function closeConfirmationHint() { 405 let hintHidden = BrowserTestUtils.waitForEvent( 406 ConfirmationHint._panel, 407 "popuphidden" 408 ); 409 ConfirmationHint._panel.hidePopup(); 410 await hintHidden; 411 }