Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

/* Any copyright is dedicated to the Public Domain.
"use strict";
const { sinon } = ChromeUtils.importESModule(
);
const { UIState } = ChromeUtils.importESModule(
);
const ENTRY_POINT = "bookmarks-app-menu";
const IMAGE = "chrome://browser/skin/fxa/sync-promo-bookmarks.svg";
const VARIANTS = {
signin: {
heading: "appmenu-sync-promo-signin",
cta: "appmenu-sync-promo-signin-cta",
pref: "browser.promo.syncPromo.bookmarks.signin.dismissed",
},
turnonsync: {
heading: "appmenu-bookmarks-sync-promo-turnonsync",
cta: "appmenu-sync-promo-turnonsync-cta",
pref: "browser.promo.syncPromo.bookmarks.turnonsync.dismissed",
},
connectdevice: {
heading: "appmenu-bookmarks-sync-promo-connectdevice",
cta: "appmenu-sync-promo-connectdevice-cta",
pref: "browser.promo.syncPromo.bookmarks.connectdevice.dismissed",
},
};
function synthesizeClick(el) {
el.scrollIntoView({ block: "center" });
EventUtils.synthesizeMouseAtCenter(el, {}, el.ownerGlobal);
}
async function openBookmarksView() {
await gCUITestUtils.openMainMenu();
let bookmarksView = PanelMultiView.getViewNode(document, "PanelUI-bookmarks");
synthesizeClick(document.getElementById("appMenu-bookmarks-button"));
await BrowserTestUtils.waitForEvent(bookmarksView, "ViewShown");
return bookmarksView;
}
async function getPromo(bookmarksView) {
let promo = bookmarksView.querySelector("#PanelUI-bookmarks-sync-promo");
await customElements.whenDefined("sync-promo");
await customElements.whenDefined("moz-promo");
await customElements.whenDefined("moz-button");
await promo.updateComplete;
return promo;
}
function clearDismissals() {
for (let { pref } of Object.values(VARIANTS)) {
Services.prefs.clearUserPref(pref);
}
}
registerCleanupFunction(clearDismissals);
add_task(async function test_variants() {
let sandbox = sinon.createSandbox();
let promoState = sandbox.stub(gSync, "getSyncPromoState");
let handleAction = sandbox.stub(gSync, "handleSyncPromoAction");
for (let [state, expected] of Object.entries(VARIANTS)) {
clearDismissals();
promoState.returns(state);
let bookmarksView = await openBookmarksView();
let promo = await getPromo(bookmarksView);
let manageBookmarks = bookmarksView.querySelector(
"#panelMenu_showAllBookmarks"
);
let mozPromo = promo.shadowRoot.querySelector("moz-promo");
let cta = promo.shadowRoot.querySelector("a[slot='support-link']");
await mozPromo.updateComplete;
ok(!promo.hidden, `${state}: promo is shown`);
Assert.deepEqual(
promoState.lastCall.args,
[],
`${state}: eligibility is not gated on any individual engine`
);
is(
manageBookmarks.nextElementSibling,
promo,
`${state}: promo is below Manage bookmarks`
);
is(
mozPromo.getAttribute("data-l10n-id"),
expected.heading,
`${state}: heading matches variant`
);
is(
cta.getAttribute("data-l10n-id"),
expected.cta,
`${state}: CTA matches variant`
);
is(
mozPromo.getAttribute("imagesrc"),
IMAGE,
`${state}: every variant shares the bookmarks illustration`
);
handleAction.resetHistory();
// The CTA closes the containing panel.
let popupHidden = BrowserTestUtils.waitForEvent(
document.getElementById("appMenu-popup"),
"popuphidden"
);
cta.click();
ok(handleAction.calledOnce, `${state}: CTA triggers the promo action`);
Assert.deepEqual(
handleAction.firstCall.args,
[state, ENTRY_POINT],
`${state}: action receives the variant and app menu entry point`
);
ok(
!Services.prefs.getBoolPref(expected.pref, false),
`${state}: CTA does not dismiss the promo`
);
await popupHidden;
}
sandbox.restore();
clearDismissals();
});
add_task(async function test_per_variant_dismissal() {
let sandbox = sinon.createSandbox();
let promoState = sandbox.stub(gSync, "getSyncPromoState");
sandbox.stub(gSync, "handleSyncPromoAction");
clearDismissals();
promoState.returns("signin");
let bookmarksView = await openBookmarksView();
let promo = await getPromo(bookmarksView);
let mozPromo = promo.shadowRoot.querySelector("moz-promo");
synthesizeClick(mozPromo.closeButton);
await promo.updateComplete;
ok(promo.hidden, "Dismissing hides the promo");
ok(
Services.prefs.getBoolPref(VARIANTS.signin.pref, false),
"Dismissal is persisted for the signin variant"
);
is(
document.getElementById("appMenu-popup").state,
"open",
"Dismissing the promo leaves the panel open"
);
await gCUITestUtils.hideMainMenu();
bookmarksView = await openBookmarksView();
promo = await getPromo(bookmarksView);
ok(promo.hidden, "Dismissed signin variant stays hidden");
promoState.returns("connectdevice");
Services.obs.notifyObservers(null, "sync-ui-state:update");
await promo.updateComplete;
mozPromo = promo.shadowRoot.querySelector("moz-promo");
ok(!promo.hidden, "A different variant is not suppressed by the dismissal");
is(
mozPromo.getAttribute("data-l10n-id"),
VARIANTS.connectdevice.heading,
"The connectdevice variant is shown after dismissing signin"
);
await gCUITestUtils.hideMainMenu();
sandbox.restore();
clearDismissals();
});
add_task(async function test_state_transitions() {
let sandbox = sinon.createSandbox();
let promoState = sandbox.stub(gSync, "getSyncPromoState");
sandbox.stub(gSync, "handleSyncPromoAction");
clearDismissals();
promoState.returns("signin");
let bookmarksView = await openBookmarksView();
let promo = await getPromo(bookmarksView);
ok(!promo.hidden, "Promo shown for eligible user");
promoState.returns("turnonsync");
Services.obs.notifyObservers(null, "sync-ui-state:update");
await promo.updateComplete;
is(
promo.shadowRoot.querySelector("moz-promo").getAttribute("data-l10n-id"),
VARIANTS.turnonsync.heading,
"Promo updates to the new eligible variant"
);
promoState.returns(null);
Services.obs.notifyObservers(null, "sync-ui-state:update");
await promo.updateComplete;
ok(promo.hidden, "Promo disappears when the user becomes ineligible");
await gCUITestUtils.hideMainMenu();
sandbox.restore();
clearDismissals();
});
add_task(async function test_sync_eligibility_ignores_engine() {
let sandbox = sinon.createSandbox();
let syncEnabled = true;
sandbox.stub(UIState, "get").callsFake(() => ({
status: UIState.STATUS_SIGNED_IN,
syncEnabled,
}));
sandbox
.stub(fxAccounts.device, "recentDeviceList")
.get(() => [{ isCurrentDevice: true }, { isCurrentDevice: false }]);
// Stub this to prevent error spam on tests trying to fetch attached clients
sandbox.stub(gSync, "updateAllUI");
clearDismissals();
await SpecialPowers.pushPrefEnv({
set: [["services.sync.engine.bookmarks", false]],
});
let bookmarksView = await openBookmarksView();
let promo = await getPromo(bookmarksView);
ok(promo.hidden, "Promo hidden when only Bookmarks syncing is disabled");
syncEnabled = false;
Services.obs.notifyObservers(null, "sync-ui-state:update");
await promo.updateComplete;
ok(!promo.hidden, "Promo shown while Sync is disabled");
is(
promo.shadowRoot.querySelector("moz-promo").getAttribute("data-l10n-id"),
VARIANTS.turnonsync.heading,
"Disabled Sync shows the turnonsync variant"
);
syncEnabled = true;
Services.obs.notifyObservers(null, "sync-ui-state:update");
await promo.updateComplete;
ok(promo.hidden, "Promo removed once Sync is enabled");
await SpecialPowers.popPrefEnv();
await gCUITestUtils.hideMainMenu();
sandbox.restore();
clearDismissals();
});
add_task(async function test_menubar_action_dispatch() {
let sandbox = sinon.createSandbox();
let handleAction = sandbox.stub(gSync, "handleSyncPromoAction");
let promo = document.getElementById("bookmarksRemoteTabsPromo");
promo.dataset.action = "signin";
try {
promo.dispatchEvent(new CustomEvent("command", { bubbles: true }));
ok(
handleAction.calledOnceWith("signin", "bookmarks-top-menu"),
"The menu-bar promo dispatches its action with the menu-bar entry point"
);
} finally {
delete promo.dataset.action;
sandbox.restore();
}
});