summaryrefslogtreecommitdiffstats
path: root/browser/base/content/test/sync/browser_sync.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/base/content/test/sync/browser_sync.js')
-rw-r--r--browser/base/content/test/sync/browser_sync.js720
1 files changed, 720 insertions, 0 deletions
diff --git a/browser/base/content/test/sync/browser_sync.js b/browser/base/content/test/sync/browser_sync.js
new file mode 100644
index 0000000000..1143a94df8
--- /dev/null
+++ b/browser/base/content/test/sync/browser_sync.js
@@ -0,0 +1,720 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { CustomizableUITestUtils } = ChromeUtils.import(
+ "resource://testing-common/CustomizableUITestUtils.jsm"
+);
+
+let gCUITestUtils = new CustomizableUITestUtils(window);
+
+add_setup(async function() {
+ // gSync.init() is called in a requestIdleCallback. Force its initialization.
+ gSync.init();
+ // This preference gets set the very first time that the FxA menu gets opened,
+ // which can cause a state write to occur, which can confuse this test, since
+ // when in the signed-out state, we need to set the state _before_ opening
+ // the FxA menu (since the panel cannot be opened) in the signed out state.
+ await SpecialPowers.pushPrefEnv({
+ set: [["identity.fxaccounts.toolbar.accessed", true]],
+ });
+});
+
+add_task(async function test_ui_state_notification_calls_updateAllUI() {
+ let called = false;
+ let updateAllUI = gSync.updateAllUI;
+ gSync.updateAllUI = () => {
+ called = true;
+ };
+
+ Services.obs.notifyObservers(null, UIState.ON_UPDATE);
+ ok(called);
+
+ gSync.updateAllUI = updateAllUI;
+});
+
+add_task(async function test_navBar_button_visibility() {
+ const button = document.getElementById("fxa-toolbar-menu-button");
+ ok(button.closest("#nav-bar"), "button is in the #nav-bar");
+
+ const state = {
+ status: UIState.STATUS_NOT_CONFIGURED,
+ syncEnabled: true,
+ };
+ gSync.updateAllUI(state);
+ ok(
+ BrowserTestUtils.is_hidden(button),
+ "Button should be hidden with STATUS_NOT_CONFIGURED"
+ );
+
+ state.status = UIState.STATUS_NOT_VERIFIED;
+ gSync.updateAllUI(state);
+ ok(
+ BrowserTestUtils.is_visible(button),
+ "Check button visibility with STATUS_NOT_VERIFIED"
+ );
+
+ state.status = UIState.STATUS_LOGIN_FAILED;
+ gSync.updateAllUI(state);
+ ok(
+ BrowserTestUtils.is_visible(button),
+ "Check button visibility with STATUS_LOGIN_FAILED"
+ );
+
+ state.status = UIState.STATUS_SIGNED_IN;
+ gSync.updateAllUI(state);
+ ok(
+ BrowserTestUtils.is_visible(button),
+ "Check button visibility with STATUS_SIGNED_IN"
+ );
+
+ state.syncEnabled = false;
+ gSync.updateAllUI(state);
+ is(
+ BrowserTestUtils.is_visible(button),
+ true,
+ "Check button visibility when signed in, but sync disabled"
+ );
+});
+
+add_task(async function test_overflow_navBar_button_visibility() {
+ const button = document.getElementById("fxa-toolbar-menu-button");
+
+ let overflowPanel = document.getElementById("widget-overflow");
+ overflowPanel.setAttribute("animate", "false");
+ let navbar = document.getElementById(CustomizableUI.AREA_NAVBAR);
+ let originalWindowWidth = window.outerWidth;
+
+ registerCleanupFunction(function() {
+ overflowPanel.removeAttribute("animate");
+ window.resizeTo(originalWindowWidth, window.outerHeight);
+ return TestUtils.waitForCondition(
+ () => !navbar.hasAttribute("overflowing")
+ );
+ });
+
+ window.resizeTo(450, window.outerHeight);
+
+ await TestUtils.waitForCondition(() => navbar.hasAttribute("overflowing"));
+ ok(navbar.hasAttribute("overflowing"), "Should have an overflowing toolbar.");
+
+ let chevron = document.getElementById("nav-bar-overflow-button");
+ let shownPanelPromise = BrowserTestUtils.waitForEvent(
+ overflowPanel,
+ "popupshown"
+ );
+ chevron.click();
+ await shownPanelPromise;
+
+ ok(button, "fxa-toolbar-menu-button was found");
+
+ const state = {
+ status: UIState.STATUS_NOT_CONFIGURED,
+ syncEnabled: true,
+ };
+ gSync.updateAllUI(state);
+
+ ok(
+ BrowserTestUtils.is_hidden(button),
+ "Button should be hidden with STATUS_NOT_CONFIGURED"
+ );
+
+ let hidePanelPromise = BrowserTestUtils.waitForEvent(
+ overflowPanel,
+ "popuphidden"
+ );
+ chevron.click();
+ await hidePanelPromise;
+});
+
+add_task(async function setupForPanelTests() {
+ /* Proton hides the FxA toolbar button when in the nav-bar and unconfigured.
+ To test the panel in all states, we move it to the tabstrip toolbar where
+ it will always be visible.
+ */
+ CustomizableUI.addWidgetToArea(
+ "fxa-toolbar-menu-button",
+ CustomizableUI.AREA_TABSTRIP
+ );
+
+ // make sure it gets put back at the end of the tests
+ registerCleanupFunction(() => {
+ CustomizableUI.addWidgetToArea(
+ "fxa-toolbar-menu-button",
+ CustomizableUI.AREA_NAVBAR
+ );
+ });
+});
+
+add_task(async function test_ui_state_signedin() {
+ await BrowserTestUtils.openNewForegroundTab(gBrowser, "https://example.com/");
+
+ const relativeDateAnchor = new Date();
+ let state = {
+ status: UIState.STATUS_SIGNED_IN,
+ syncEnabled: true,
+ email: "foo@bar.com",
+ displayName: "Foo Bar",
+ avatarURL: "https://foo.bar",
+ lastSync: new Date(),
+ syncing: false,
+ };
+
+ const origRelativeTimeFormat = gSync.relativeTimeFormat;
+ gSync.relativeTimeFormat = {
+ formatBestUnit(date) {
+ return origRelativeTimeFormat.formatBestUnit(date, {
+ now: relativeDateAnchor,
+ });
+ },
+ };
+
+ gSync.updateAllUI(state);
+
+ await openFxaPanel();
+
+ checkMenuBarItem("sync-syncnowitem");
+ checkPanelHeader();
+ checkFxaToolbarButtonPanel({
+ headerTitle: "Manage account",
+ headerDescription: "foo@bar.com",
+ enabledItems: [
+ "PanelUI-fxa-menu-sendtab-button",
+ "PanelUI-fxa-menu-connect-device-button",
+ "PanelUI-fxa-menu-syncnow-button",
+ "PanelUI-fxa-menu-sync-prefs-button",
+ "PanelUI-fxa-menu-account-signout-button",
+ ],
+ disabledItems: [],
+ hiddenItems: ["PanelUI-fxa-menu-setup-sync-button"],
+ });
+ checkFxAAvatar("signedin");
+ gSync.relativeTimeFormat = origRelativeTimeFormat;
+ await closeFxaPanel();
+
+ await openMainPanel();
+
+ checkPanelUIStatusBar({
+ description: "foo@bar.com",
+ titleHidden: true,
+ hideFxAText: true,
+ });
+
+ await closeTabAndMainPanel();
+});
+
+add_task(async function test_ui_state_syncing_panel_closed() {
+ let state = {
+ status: UIState.STATUS_SIGNED_IN,
+ syncEnabled: true,
+ email: "foo@bar.com",
+ displayName: "Foo Bar",
+ avatarURL: "https://foo.bar",
+ lastSync: new Date(),
+ syncing: true,
+ };
+
+ gSync.updateAllUI(state);
+
+ checkSyncNowButtons(true);
+
+ // Be good citizens and remove the "syncing" state.
+ gSync.updateAllUI({
+ status: UIState.STATUS_SIGNED_IN,
+ syncEnabled: true,
+ email: "foo@bar.com",
+ lastSync: new Date(),
+ syncing: false,
+ });
+ // Because we switch from syncing to non-syncing, and there's a timeout involved.
+ await promiseObserver("test:browser-sync:activity-stop");
+});
+
+add_task(async function test_ui_state_syncing_panel_open() {
+ await BrowserTestUtils.openNewForegroundTab(gBrowser, "https://example.com/");
+
+ let state = {
+ status: UIState.STATUS_SIGNED_IN,
+ syncEnabled: true,
+ email: "foo@bar.com",
+ displayName: "Foo Bar",
+ avatarURL: "https://foo.bar",
+ lastSync: new Date(),
+ syncing: false,
+ };
+
+ gSync.updateAllUI(state);
+
+ await openFxaPanel();
+
+ checkSyncNowButtons(false);
+
+ state = {
+ status: UIState.STATUS_SIGNED_IN,
+ syncEnabled: true,
+ email: "foo@bar.com",
+ displayName: "Foo Bar",
+ avatarURL: "https://foo.bar",
+ lastSync: new Date(),
+ syncing: true,
+ };
+
+ gSync.updateAllUI(state);
+
+ checkSyncNowButtons(true);
+
+ // Be good citizens and remove the "syncing" state.
+ gSync.updateAllUI({
+ status: UIState.STATUS_SIGNED_IN,
+ syncEnabled: true,
+ email: "foo@bar.com",
+ lastSync: new Date(),
+ syncing: false,
+ });
+ // Because we switch from syncing to non-syncing, and there's a timeout involved.
+ await promiseObserver("test:browser-sync:activity-stop");
+
+ await closeFxaPanel();
+ BrowserTestUtils.removeTab(gBrowser.selectedTab);
+});
+
+add_task(async function test_ui_state_panel_open_after_syncing() {
+ await BrowserTestUtils.openNewForegroundTab(gBrowser, "https://example.com/");
+
+ let state = {
+ status: UIState.STATUS_SIGNED_IN,
+ syncEnabled: true,
+ email: "foo@bar.com",
+ displayName: "Foo Bar",
+ avatarURL: "https://foo.bar",
+ lastSync: new Date(),
+ syncing: true,
+ };
+
+ gSync.updateAllUI(state);
+
+ await openFxaPanel();
+
+ checkSyncNowButtons(true);
+
+ // Be good citizens and remove the "syncing" state.
+ gSync.updateAllUI({
+ status: UIState.STATUS_SIGNED_IN,
+ syncEnabled: true,
+ email: "foo@bar.com",
+ lastSync: new Date(),
+ syncing: false,
+ });
+ // Because we switch from syncing to non-syncing, and there's a timeout involved.
+ await promiseObserver("test:browser-sync:activity-stop");
+
+ await closeFxaPanel();
+ BrowserTestUtils.removeTab(gBrowser.selectedTab);
+});
+
+add_task(async function test_ui_state_unconfigured() {
+ await BrowserTestUtils.openNewForegroundTab(gBrowser, "https://example.com/");
+
+ let state = {
+ status: UIState.STATUS_NOT_CONFIGURED,
+ };
+
+ gSync.updateAllUI(state);
+
+ checkMenuBarItem("sync-setup");
+
+ checkFxAAvatar("not_configured");
+
+ let signedOffLabel = gSync.fluentStrings.formatValueSync(
+ "appmenu-fxa-signed-in-label"
+ );
+
+ await openMainPanel();
+
+ checkPanelUIStatusBar({
+ description: signedOffLabel,
+ titleHidden: true,
+ hideFxAText: false,
+ });
+ await closeTabAndMainPanel();
+});
+
+add_task(async function test_ui_state_syncdisabled() {
+ await BrowserTestUtils.openNewForegroundTab(gBrowser, "https://example.com/");
+
+ let state = {
+ status: UIState.STATUS_SIGNED_IN,
+ syncEnabled: false,
+ email: "foo@bar.com",
+ displayName: "Foo Bar",
+ avatarURL: "https://foo.bar",
+ };
+
+ gSync.updateAllUI(state);
+
+ await openFxaPanel();
+
+ checkMenuBarItem("sync-enable");
+ checkPanelHeader();
+ checkFxaToolbarButtonPanel({
+ headerTitle: "Manage account",
+ headerDescription: "foo@bar.com",
+ enabledItems: [
+ "PanelUI-fxa-menu-sendtab-button",
+ "PanelUI-fxa-menu-connect-device-button",
+ "PanelUI-fxa-menu-setup-sync-button",
+ "PanelUI-fxa-menu-account-signout-button",
+ ],
+ disabledItems: [],
+ hiddenItems: [
+ "PanelUI-fxa-menu-syncnow-button",
+ "PanelUI-fxa-menu-sync-prefs-button",
+ ],
+ });
+ checkFxAAvatar("signedin");
+ await closeFxaPanel();
+
+ await openMainPanel();
+
+ checkPanelUIStatusBar({
+ description: "foo@bar.com",
+ titleHidden: true,
+ hideFxAText: true,
+ });
+
+ await closeTabAndMainPanel();
+});
+
+add_task(async function test_ui_state_unverified() {
+ await BrowserTestUtils.openNewForegroundTab(gBrowser, "https://example.com/");
+
+ let state = {
+ status: UIState.STATUS_NOT_VERIFIED,
+ email: "foo@bar.com",
+ syncing: false,
+ };
+
+ gSync.updateAllUI(state);
+
+ await openFxaPanel();
+
+ const expectedLabel = gSync.fluentStrings.formatValueSync(
+ "account-finish-account-setup"
+ );
+
+ checkMenuBarItem("sync-unverifieditem");
+ checkPanelHeader();
+ checkFxaToolbarButtonPanel({
+ headerTitle: expectedLabel,
+ headerDescription: state.email,
+ enabledItems: [
+ "PanelUI-fxa-menu-sendtab-button",
+ "PanelUI-fxa-menu-setup-sync-button",
+ "PanelUI-fxa-menu-account-signout-button",
+ ],
+ disabledItems: ["PanelUI-fxa-menu-connect-device-button"],
+ hiddenItems: [
+ "PanelUI-fxa-menu-syncnow-button",
+ "PanelUI-fxa-menu-sync-prefs-button",
+ ],
+ });
+ checkFxAAvatar("unverified");
+ await closeFxaPanel();
+ await openMainPanel();
+
+ checkPanelUIStatusBar({
+ description: state.email,
+ title: expectedLabel,
+ titleHidden: false,
+ hideFxAText: true,
+ });
+
+ await closeTabAndMainPanel();
+});
+
+add_task(async function test_ui_state_loginFailed() {
+ await BrowserTestUtils.openNewForegroundTab(gBrowser, "https://example.com/");
+
+ let state = {
+ status: UIState.STATUS_LOGIN_FAILED,
+ email: "foo@bar.com",
+ };
+
+ gSync.updateAllUI(state);
+
+ await openFxaPanel();
+
+ const expectedLabel = gSync.fluentStrings.formatValueSync(
+ "account-disconnected2"
+ );
+
+ checkMenuBarItem("sync-reauthitem");
+ checkPanelHeader();
+ checkFxaToolbarButtonPanel({
+ headerTitle: expectedLabel,
+ headerDescription: state.email,
+ enabledItems: [
+ "PanelUI-fxa-menu-sendtab-button",
+ "PanelUI-fxa-menu-setup-sync-button",
+ "PanelUI-fxa-menu-account-signout-button",
+ ],
+ disabledItems: ["PanelUI-fxa-menu-connect-device-button"],
+ hiddenItems: [
+ "PanelUI-fxa-menu-syncnow-button",
+ "PanelUI-fxa-menu-sync-prefs-button",
+ ],
+ });
+ checkFxAAvatar("login-failed");
+ await closeFxaPanel();
+ await openMainPanel();
+
+ checkPanelUIStatusBar({
+ description: state.email,
+ title: expectedLabel,
+ titleHidden: false,
+ hideFxAText: true,
+ });
+
+ await closeTabAndMainPanel();
+});
+
+add_task(async function test_app_menu_fxa_disabled() {
+ const newWin = await BrowserTestUtils.openNewBrowserWindow();
+
+ Services.prefs.setBoolPref("identity.fxaccounts.enabled", true);
+ newWin.gSync.onFxaDisabled();
+
+ let menuButton = newWin.document.getElementById("PanelUI-menu-button");
+ menuButton.click();
+ await BrowserTestUtils.waitForEvent(newWin.PanelUI.mainView, "ViewShown");
+
+ [...newWin.document.querySelectorAll(".sync-ui-item")].forEach(
+ e => (e.hidden = false)
+ );
+
+ let hidden = BrowserTestUtils.waitForEvent(
+ newWin.document,
+ "popuphidden",
+ true
+ );
+ newWin.PanelUI.hide();
+ await hidden;
+ await BrowserTestUtils.closeWindow(newWin);
+});
+
+function checkPanelUIStatusBar({
+ description,
+ title,
+ titleHidden,
+ hideFxAText,
+}) {
+ checkAppMenuFxAText(hideFxAText);
+ let appMenuHeaderTitle = PanelMultiView.getViewNode(
+ document,
+ "appMenu-header-title"
+ );
+ let appMenuHeaderDescription = PanelMultiView.getViewNode(
+ document,
+ "appMenu-header-description"
+ );
+ is(
+ appMenuHeaderDescription.value,
+ description,
+ "app menu description has correct value"
+ );
+ is(appMenuHeaderTitle.hidden, titleHidden, "title has correct hidden status");
+ if (!titleHidden) {
+ is(appMenuHeaderTitle.value, title, "title has correct value");
+ }
+}
+
+function checkMenuBarItem(expectedShownItemId) {
+ checkItemsVisibilities(
+ [
+ "sync-setup",
+ "sync-enable",
+ "sync-syncnowitem",
+ "sync-reauthitem",
+ "sync-unverifieditem",
+ ],
+ expectedShownItemId
+ );
+}
+
+function checkPanelHeader() {
+ let fxaPanelView = PanelMultiView.getViewNode(document, "PanelUI-fxa");
+ is(
+ fxaPanelView.getAttribute("title"),
+ gSync.fluentStrings.formatValueSync("appmenu-fxa-header2"),
+ "Panel title is correct"
+ );
+}
+
+function checkSyncNowButtons(syncing, tooltip = null) {
+ const syncButtons = document.querySelectorAll(".syncNowBtn");
+
+ for (const syncButton of syncButtons) {
+ is(
+ syncButton.getAttribute("syncstatus"),
+ syncing ? "active" : "",
+ "button active has the right value"
+ );
+ if (tooltip) {
+ is(
+ syncButton.getAttribute("tooltiptext"),
+ tooltip,
+ "button tooltiptext is set to the right value"
+ );
+ }
+ }
+
+ const syncLabels = document.querySelectorAll(".syncnow-label");
+
+ for (const syncLabel of syncLabels) {
+ if (syncing) {
+ is(
+ syncLabel.getAttribute("data-l10n-id"),
+ syncLabel.getAttribute("syncing-data-l10n-id"),
+ "label is set to the right value"
+ );
+ } else {
+ is(
+ syncLabel.getAttribute("data-l10n-id"),
+ syncLabel.getAttribute("sync-now-data-l10n-id"),
+ "label is set to the right value"
+ );
+ }
+ }
+}
+
+async function checkFxaToolbarButtonPanel({
+ headerTitle,
+ headerDescription,
+ enabledItems,
+ disabledItems,
+ hiddenItems,
+}) {
+ is(
+ document.getElementById("fxa-menu-header-title").value,
+ headerTitle,
+ "has correct title"
+ );
+ is(
+ document.getElementById("fxa-menu-header-description").value,
+ headerDescription,
+ "has correct description"
+ );
+
+ for (const id of enabledItems) {
+ const el = document.getElementById(id);
+ is(el.hasAttribute("disabled"), false, id + " is enabled");
+ }
+
+ for (const id of disabledItems) {
+ const el = document.getElementById(id);
+ is(el.getAttribute("disabled"), "true", id + " is disabled");
+ }
+
+ for (const id of hiddenItems) {
+ const el = document.getElementById(id);
+ let elShown = window.getComputedStyle(el).display == "none";
+ is(elShown, true, id + " is hidden");
+ }
+}
+
+async function checkFxABadged() {
+ const button = document.getElementById("fxa-toolbar-menu-button");
+ await BrowserTestUtils.waitForCondition(() => {
+ return button.querySelector("label.feature-callout");
+ });
+ const badge = button.querySelector("label.feature-callout");
+ ok(badge, "expected feature-callout style badge");
+ ok(BrowserTestUtils.is_visible(badge), "expected the badge to be visible");
+}
+
+// fxaStatus is one of 'not_configured', 'unverified', 'login-failed', or 'signedin'.
+function checkFxAAvatar(fxaStatus) {
+ // Unhide the panel so computed styles can be read
+ document.querySelector("#appMenu-popup").hidden = false;
+
+ const avatarContainers = [document.getElementById("fxa-avatar-image")];
+ for (const avatar of avatarContainers) {
+ const avatarURL = getComputedStyle(avatar).listStyleImage;
+ const expected = {
+ not_configured: 'url("chrome://browser/skin/fxa/avatar-empty.svg")',
+ unverified: 'url("chrome://browser/skin/fxa/avatar.svg")',
+ signedin: 'url("chrome://browser/skin/fxa/avatar.svg")',
+ "login-failed": 'url("chrome://browser/skin/fxa/avatar.svg")',
+ };
+ ok(
+ avatarURL == expected[fxaStatus],
+ `expected avatar URL to be ${expected[fxaStatus]}, got ${avatarURL}`
+ );
+ }
+}
+
+function checkAppMenuFxAText(hideStatus) {
+ let fxaText = document.getElementById("appMenu-fxa-text");
+ let isHidden = fxaText.hidden || fxaText.style.visibility == "collapse";
+ ok(isHidden == hideStatus, "FxA text has correct hidden state");
+}
+
+// Only one item visible at a time.
+function checkItemsVisibilities(itemsIds, expectedShownItemId) {
+ for (let id of itemsIds) {
+ if (id == expectedShownItemId) {
+ ok(
+ !document.getElementById(id).hidden,
+ "menuitem " + id + " should be visible"
+ );
+ } else {
+ ok(
+ document.getElementById(id).hidden,
+ "menuitem " + id + " should be hidden"
+ );
+ }
+ }
+}
+
+function promiseObserver(topic) {
+ return new Promise(resolve => {
+ let obs = (aSubject, aTopic, aData) => {
+ Services.obs.removeObserver(obs, aTopic);
+ resolve(aSubject);
+ };
+ Services.obs.addObserver(obs, topic);
+ });
+}
+
+async function openTabAndFxaPanel() {
+ await BrowserTestUtils.openNewForegroundTab(gBrowser, "https://example.com/");
+ await openFxaPanel();
+}
+
+async function openFxaPanel() {
+ let fxaButton = document.getElementById("fxa-toolbar-menu-button");
+ fxaButton.click();
+
+ let fxaView = PanelMultiView.getViewNode(document, "PanelUI-fxa");
+ await BrowserTestUtils.waitForEvent(fxaView, "ViewShown");
+}
+
+async function closeFxaPanel() {
+ let fxaView = PanelMultiView.getViewNode(document, "PanelUI-fxa");
+ let hidden = BrowserTestUtils.waitForEvent(document, "popuphidden", true);
+ fxaView.closest("panel").hidePopup();
+ await hidden;
+}
+
+async function openMainPanel() {
+ let menuButton = document.getElementById("PanelUI-menu-button");
+ menuButton.click();
+ await BrowserTestUtils.waitForEvent(window.PanelUI.mainView, "ViewShown");
+}
+
+async function closeTabAndMainPanel() {
+ await gCUITestUtils.hideMainMenu();
+
+ BrowserTestUtils.removeTab(gBrowser.selectedTab);
+}