/* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; const kForceOverflowWidthPx = 450; Services.scriptloader.loadSubScript( "chrome://mochitests/content/browser/browser/base/content/test/general/head.js", this ); const fxaDevices = [ { id: 1, name: "Foo", availableCommands: { "https://identity.mozilla.com/cmd/open-uri": "baz" }, lastAccessTime: Date.now(), }, { id: 2, name: "Bar", availableCommands: { "https://identity.mozilla.com/cmd/open-uri": "boo" }, lastAccessTime: Date.now() + 60000, // add 30min }, { id: 3, name: "Baz", clientRecord: "bar", lastAccessTime: Date.now() + 120000, // add 60min }, // Legacy send tab target (no availableCommands). { id: 4, name: "Homer" }, // Incompatible target. ]; let [testTab] = gBrowser.visibleTabs; function updateTabContextMenu(tab = gBrowser.selectedTab) { let menu = document.getElementById("tabContextMenu"); var evt = new Event(""); tab.dispatchEvent(evt); // The TabContextMenu initializes its strings only on a focus or mouseover event. // Calls focus event on the TabContextMenu early in the test gBrowser.selectedTab.focus(); menu.openPopup(tab, "end_after", 0, 0, true, false, evt); is( window.TabContextMenu.contextTab, tab, "TabContextMenu context is the expected tab" ); menu.hidePopup(); } add_setup(async function () { await promiseSyncReady(); await Services.search.init(); // gSync.init() is called in a requestIdleCallback. Force its initialization. gSync.init(); sinon .stub(Weave.Service.clientsEngine, "getClientByFxaDeviceId") .callsFake(fxaDeviceId => { let target = fxaDevices.find(c => c.id == fxaDeviceId); return target ? target.clientRecord : null; }); sinon.stub(Weave.Service.clientsEngine, "getClientType").returns("desktop"); await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:mozilla"); registerCleanupFunction(() => { gBrowser.removeCurrentTab(); }); is(gBrowser.visibleTabs.length, 2, "there are two visible tabs"); }); add_task(async function test_sendTabToDevice_callsFlushLogFile() { const sandbox = setupSendTabMocks({ fxaDevices }); updateTabContextMenu(testTab); await openTabContextMenu("context_sendTabToDevice"); let promiseObserved = promiseObserver("service:log-manager:flush-log-file"); await activateMenuItem(); await promiseObserved; ok(true, "Got flush-log-file observer message"); await closeConfirmationHint(); sandbox.restore(); }); async function checkForConfirmationHint(targetId) { const sandbox = setupSendTabMocks({ fxaDevices }); updateTabContextMenu(testTab); await openTabContextMenu("context_sendTabToDevice"); await activateMenuItem(); is( ConfirmationHint._panel.anchorNode.id, targetId, `Hint anchored to ${targetId}` ); await closeConfirmationHint(); sandbox.restore(); } add_task(async function test_sendTabToDevice_showsConfirmationHint_fxa() { // We need to change the fxastatus from "not_configured" to show the FxA button. is( document.documentElement.getAttribute("fxastatus"), "not_configured", "FxA button is hidden" ); document.documentElement.setAttribute("fxastatus", "foo"); await checkForConfirmationHint("fxa-toolbar-menu-button"); document.documentElement.setAttribute("fxastatus", "not_configured"); }); add_task( async function test_sendTabToDevice_showsConfirmationHint_onOverflowMenu() { // We need to change the fxastatus from "not_configured" to show the FxA button. is( document.documentElement.getAttribute("fxastatus"), "not_configured", "FxA button is hidden" ); document.documentElement.setAttribute("fxastatus", "foo"); let navbar = document.getElementById("nav-bar"); // Resize the window so that the account button is in the overflow menu. let originalWidth = window.outerWidth; window.resizeTo(kForceOverflowWidthPx, window.outerHeight); await TestUtils.waitForCondition(() => navbar.hasAttribute("overflowing")); await checkForConfirmationHint("PanelUI-menu-button"); document.documentElement.setAttribute("fxastatus", "not_configured"); window.resizeTo(originalWidth, window.outerHeight); await TestUtils.waitForCondition(() => !navbar.hasAttribute("overflowing")); CustomizableUI.reset(); } ); add_task(async function test_sendTabToDevice_showsConfirmationHint_appMenu() { // If fxastatus is "not_configured" then the FxA button is hidden, and we // should use the appMenu. is( document.documentElement.getAttribute("fxastatus"), "not_configured", "FxA button is hidden" ); await checkForConfirmationHint("PanelUI-menu-button"); }); add_task(async function test_tab_contextmenu() { const sandbox = setupSendTabMocks({ fxaDevices }); let expectation = sandbox .mock(gSync) .expects("sendTabToDevice") .once() .withExactArgs( "about:mozilla", [fxaDevices[1]], "The Book of Mozilla, 6:27" ) .returns(true); updateTabContextMenu(testTab); await openTabContextMenu("context_sendTabToDevice"); is( document.getElementById("context_sendTabToDevice").hidden, false, "Send tab to device is shown" ); is( document.getElementById("context_sendTabToDevice").disabled, false, "Send tab to device is enabled" ); await activateMenuItem(); await closeConfirmationHint(); expectation.verify(); sandbox.restore(); }); add_task(async function test_tab_contextmenu_unconfigured() { const sandbox = setupSendTabMocks({ state: UIState.STATUS_NOT_CONFIGURED }); updateTabContextMenu(testTab); is( document.getElementById("context_sendTabToDevice").hidden, true, "Send tab to device is hidden" ); is( document.getElementById("context_sendTabToDevice").disabled, false, "Send tab to device is enabled" ); sandbox.restore(); }); add_task(async function test_tab_contextmenu_not_sendable() { const sandbox = setupSendTabMocks({ fxaDevices, isSendableURI: false }); updateTabContextMenu(testTab); is( document.getElementById("context_sendTabToDevice").hidden, true, "Send tab to device is hidden" ); is( document.getElementById("context_sendTabToDevice").disabled, true, "Send tab to device is disabled" ); sandbox.restore(); }); add_task(async function test_tab_contextmenu_not_synced_yet() { const sandbox = setupSendTabMocks({ fxaDevices: null }); updateTabContextMenu(testTab); is( document.getElementById("context_sendTabToDevice").hidden, true, "Send tab to device is hidden" ); is( document.getElementById("context_sendTabToDevice").disabled, true, "Send tab to device is disabled" ); sandbox.restore(); }); add_task(async function test_tab_contextmenu_sync_not_ready_configured() { const sandbox = setupSendTabMocks({ syncReady: false }); updateTabContextMenu(testTab); is( document.getElementById("context_sendTabToDevice").hidden, true, "Send tab to device is hidden" ); is( document.getElementById("context_sendTabToDevice").disabled, true, "Send tab to device is disabled" ); sandbox.restore(); }); add_task(async function test_tab_contextmenu_sync_not_ready_other_state() { const sandbox = setupSendTabMocks({ syncReady: false, state: UIState.STATUS_NOT_VERIFIED, }); updateTabContextMenu(testTab); is( document.getElementById("context_sendTabToDevice").hidden, true, "Send tab to device is hidden" ); is( document.getElementById("context_sendTabToDevice").disabled, false, "Send tab to device is enabled" ); sandbox.restore(); }); add_task(async function test_tab_contextmenu_fxa_disabled() { const getter = sinon.stub(gSync, "FXA_ENABLED").get(() => false); // Simulate onFxaDisabled() being called on window open. gSync.onFxaDisabled(); updateTabContextMenu(testTab); is( document.getElementById("context_sendTabToDevice").hidden, true, "Send tab to device is hidden" ); getter.restore(); [...document.querySelectorAll(".sync-ui-item")].forEach( e => (e.hidden = false) ); }); add_task(async function teardown() { Weave.Service.clientsEngine.getClientByFxaDeviceId.restore(); Weave.Service.clientsEngine.getClientType.restore(); }); async function openTabContextMenu(openSubmenuId = null) { const contextMenu = document.getElementById("tabContextMenu"); is(contextMenu.state, "closed", "checking if popup is closed"); const awaitPopupShown = BrowserTestUtils.waitForEvent( contextMenu, "popupshown" ); EventUtils.synthesizeMouseAtCenter(gBrowser.selectedTab, { type: "contextmenu", button: 2, }); await awaitPopupShown; if (openSubmenuId) { const menuPopup = document.getElementById(openSubmenuId).menupopup; const menuPopupPromise = BrowserTestUtils.waitForEvent( menuPopup, "popupshown" ); menuPopup.openPopup(); await menuPopupPromise; } } function promiseObserver(topic) { return new Promise(resolve => { let obs = (aSubject, aTopic, aData) => { Services.obs.removeObserver(obs, aTopic); resolve(aSubject); }; Services.obs.addObserver(obs, topic); }); } function waitForConfirmationHint() { return BrowserTestUtils.waitForEvent(ConfirmationHint._panel, "popuphidden"); } async function activateMenuItem() { let popupHidden = BrowserTestUtils.waitForEvent( document.getElementById("tabContextMenu"), "popuphidden" ); let hintShown = BrowserTestUtils.waitForEvent( ConfirmationHint._panel, "popupshown" ); let menuitem = document .getElementById("context_sendTabToDevicePopupMenu") .querySelector("menuitem"); menuitem.closest("menupopup").activateItem(menuitem); await popupHidden; await hintShown; } async function closeConfirmationHint() { let hintHidden = BrowserTestUtils.waitForEvent( ConfirmationHint._panel, "popuphidden" ); ConfirmationHint._panel.hidePopup(); await hintHidden; }