Bug 1828508 - Clean up Firefox View Feature Tour + Notification r=omc-reviewers,fluent-reviewers,fxview-reviewers,nsharpley,bolsson,aminomancer

Differential Revision: https://phabricator.services.mozilla.com/D209404
This commit is contained in:
hanna alemu
2024-06-11 18:24:35 +00:00
parent c0a6598182
commit d7c67f44bd
13 changed files with 497 additions and 2057 deletions

View File

@@ -10,21 +10,6 @@ prefs = [
["browser_entrypoint_management.js"]
["browser_feature_callout.js"]
skip-if = ["true"] # Bug 1869605 and # Bug 1870296
["browser_feature_callout_position.js"]
skip-if = ["true"] # Bug 1869605 and # Bug 1870296
["browser_feature_callout_resize.js"]
skip-if = ["true"] # Bug 1869605 and # Bug 1870296
["browser_feature_callout_targeting.js"]
skip-if = ["true"] # Bug 1869605 and # Bug 1870296
["browser_feature_callout_theme.js"]
skip-if = ["true"] # Bug 1869605 and # Bug 1870296
["browser_firefoxview.js"]
["browser_firefoxview_dragDrop_pinned_tab.js"]

View File

@@ -1,746 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const { MessageLoaderUtils } = ChromeUtils.importESModule(
"resource:///modules/asrouter/ASRouter.sys.mjs"
);
const { BuiltInThemes } = ChromeUtils.importESModule(
"resource:///modules/BuiltInThemes.sys.mjs"
);
const defaultPrefValue = getPrefValueByScreen(1);
add_setup(async function () {
requestLongerTimeout(3);
registerCleanupFunction(() => ASRouter.resetMessageState());
});
add_task(async function feature_callout_renders_in_firefox_view() {
await SpecialPowers.pushPrefEnv({
set: [[featureTourPref, defaultPrefValue]],
});
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: "about:firefoxview",
},
async browser => {
const { document } = browser.contentWindow;
launchFeatureTourIn(browser.contentWindow);
await waitForCalloutScreen(document, "FEATURE_CALLOUT_1");
ok(
document.querySelector(calloutSelector),
"Feature Callout element exists"
);
}
);
});
add_task(async function feature_callout_is_not_shown_twice() {
// Third comma-separated value of the pref is set to a string value once a user completes the tour
await SpecialPowers.pushPrefEnv({
set: [[featureTourPref, '{"screen":"","complete":true}']],
});
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: "about:firefoxview",
},
async browser => {
const { document } = browser.contentWindow;
launchFeatureTourIn(browser.contentWindow);
ok(
!document.querySelector(calloutSelector),
"Feature Callout tour does not render if the user finished it previously"
);
}
);
Services.prefs.clearUserPref(featureTourPref);
});
add_task(async function feature_callout_syncs_across_visits_and_tabs() {
// Second comma-separated value of the pref is the id
// of the last viewed screen of the feature tour
await SpecialPowers.pushPrefEnv({
set: [[featureTourPref, '{"screen":"FEATURE_CALLOUT_1","complete":false}']],
});
// Open an about:firefoxview tab
let tab1 = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
"about:firefoxview"
);
let tab1Doc = tab1.linkedBrowser.contentWindow.document;
launchFeatureTourIn(tab1.linkedBrowser.contentWindow);
await waitForCalloutScreen(tab1Doc, "FEATURE_CALLOUT_1");
ok(
tab1Doc.querySelector(".FEATURE_CALLOUT_1"),
"First tab's Feature Callout shows the tour screen saved in the user pref"
);
// Open a second about:firefoxview tab
let tab2 = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
"about:firefoxview"
);
let tab2Doc = tab2.linkedBrowser.contentWindow.document;
launchFeatureTourIn(tab2.linkedBrowser.contentWindow);
await waitForCalloutScreen(tab2Doc, "FEATURE_CALLOUT_1");
ok(
tab2Doc.querySelector(".FEATURE_CALLOUT_1"),
"Second tab's Feature Callout shows the tour screen saved in the user pref"
);
await clickCTA(tab2Doc);
await waitForCalloutScreen(tab2Doc, "FEATURE_CALLOUT_2");
gBrowser.selectedTab = tab1;
tab1.focus();
await waitForCalloutScreen(tab1Doc, "FEATURE_CALLOUT_2");
ok(
tab1Doc.querySelector(".FEATURE_CALLOUT_2"),
"First tab's Feature Callout advances to the next screen when the tour is advanced in second tab"
);
await clickCTA(tab1Doc);
gBrowser.selectedTab = tab1;
await waitForCalloutRemoved(tab1Doc);
ok(
!tab1Doc.body.querySelector(calloutSelector),
"Feature Callout is removed in first tab after being dismissed in first tab"
);
gBrowser.selectedTab = tab2;
tab2.focus();
await waitForCalloutRemoved(tab2Doc);
ok(
!tab2Doc.body.querySelector(calloutSelector),
"Feature Callout is removed in second tab after tour was dismissed in first tab"
);
BrowserTestUtils.removeTab(tab1);
BrowserTestUtils.removeTab(tab2);
Services.prefs.clearUserPref(featureTourPref);
});
add_task(async function feature_callout_closes_on_dismiss() {
await SpecialPowers.pushPrefEnv({
set: [[featureTourPref, '{"screen":"FEATURE_CALLOUT_2","complete":false}']],
});
const testMessage = getCalloutMessageById("FIREFOX_VIEW_FEATURE_TOUR");
const sandbox = createSandboxWithCalloutTriggerStub(testMessage);
const spy = new TelemetrySpy(sandbox);
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: "about:firefoxview",
},
async browser => {
const { document } = browser.contentWindow;
launchFeatureTourIn(browser.contentWindow);
await waitForCalloutScreen(document, "FEATURE_CALLOUT_2");
document.querySelector(".dismiss-button").click();
await waitForCalloutRemoved(document);
ok(
!document.querySelector(calloutSelector),
"Callout is removed from screen on dismiss"
);
let tourComplete = JSON.parse(
Services.prefs.getStringPref(featureTourPref)
).complete;
ok(
tourComplete,
`Tour is recorded as complete in ${featureTourPref} preference value`
);
// Test that appropriate telemetry is sent
spy.assertCalledWith({
event: "CLICK_BUTTON",
event_context: {
source: "dismiss_button",
page: "about:firefoxview",
},
message_id: sinon.match("FEATURE_CALLOUT_2"),
});
spy.assertCalledWith({
event: "DISMISS",
event_context: {
source: "dismiss_button",
page: "about:firefoxview",
},
message_id: sinon.match("FEATURE_CALLOUT_2"),
});
}
);
sandbox.restore();
});
add_task(async function feature_callout_arrow_position_attribute_exists() {
const testMessage = getCalloutMessageById("FIREFOX_VIEW_FEATURE_TOUR");
const sandbox = createSandboxWithCalloutTriggerStub(testMessage);
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: "about:firefoxview",
},
async browser => {
const { document } = browser.contentWindow;
launchFeatureTourIn(browser.contentWindow);
const callout = await BrowserTestUtils.waitForCondition(
() =>
document.querySelector(`${calloutSelector}[arrow-position="top"]`),
"Waiting for callout to render"
);
is(
callout.getAttribute("arrow-position"),
"top",
"Arrow position attribute exists on parent container"
);
}
);
sandbox.restore();
});
add_task(async function feature_callout_arrow_is_not_flipped_on_ltr() {
const testMessage = getCalloutMessageById("FIREFOX_VIEW_FEATURE_TOUR");
testMessage.message.content.screens[0].anchors[0].arrow_position = "start";
testMessage.message.content.screens[0].anchors[0].selector =
"span.brand-feature-name";
const sandbox = createSandboxWithCalloutTriggerStub(testMessage);
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: "about:firefoxview",
},
async browser => {
const { document } = browser.contentWindow;
launchFeatureTourIn(browser.contentWindow);
const callout = await BrowserTestUtils.waitForCondition(
() =>
document.querySelector(
`${calloutSelector}[arrow-position="inline-start"]:not(.hidden)`
),
"Waiting for callout to render"
);
is(
callout.getAttribute("arrow-position"),
"inline-start",
"Feature callout has inline-start arrow position when arrow_position is set to 'start'"
);
ok(
!callout.classList.contains("hidden"),
"Feature Callout is not hidden"
);
}
);
sandbox.restore();
});
add_task(async function feature_callout_respects_cfr_features_pref() {
async function toggleCFRFeaturesPref(value) {
await SpecialPowers.pushPrefEnv({
set: [
[
"browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features",
value,
],
],
});
}
await SpecialPowers.pushPrefEnv({
set: [[featureTourPref, defaultPrefValue]],
});
await toggleCFRFeaturesPref(true);
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: "about:firefoxview",
},
async browser => {
const { document } = browser.contentWindow;
launchFeatureTourIn(browser.contentWindow);
await waitForCalloutScreen(document, "FEATURE_CALLOUT_1");
ok(
document.querySelector(calloutSelector),
"Feature Callout element exists"
);
}
);
await SpecialPowers.popPrefEnv();
await toggleCFRFeaturesPref(false);
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: "about:firefoxview",
},
async browser => {
const { document } = browser.contentWindow;
launchFeatureTourIn(browser.contentWindow);
ok(
!document.querySelector(calloutSelector),
"Feature Callout element was not created because CFR pref was disabled"
);
}
);
await SpecialPowers.popPrefEnv();
});
add_task(
async function feature_callout_tab_pickup_reminder_primary_click_elm() {
Services.prefs.setBoolPref("identity.fxaccounts.enabled", false);
const testMessage = getCalloutMessageById(
"FIREFOX_VIEW_TAB_PICKUP_REMINDER"
);
const sandbox = createSandboxWithCalloutTriggerStub(testMessage);
const expectedUrl =
await fxAccounts.constructor.config.promiseConnectAccountURI("fx-view");
info(`Expected FxA URL: ${expectedUrl}`);
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: "about:firefoxview",
},
async browser => {
const { document } = browser.contentWindow;
launchFeatureTourIn(browser.contentWindow);
let tabOpened = new Promise(resolve => {
gBrowser.tabContainer.addEventListener(
"TabOpen",
event => {
let newTab = event.target;
let newBrowser = newTab.linkedBrowser;
let result = newTab;
BrowserTestUtils.waitForDocLoadAndStopIt(
expectedUrl,
newBrowser
).then(() => resolve(result));
},
{ once: true }
);
});
info("Waiting for callout to render");
await waitForCalloutScreen(
document,
"FIREFOX_VIEW_TAB_PICKUP_REMINDER"
);
info("Clicking primary button");
let calloutRemoved = waitForCalloutRemoved(document);
await clickCTA(document);
let openedTab = await tabOpened;
ok(openedTab, "FxA sign in page opened");
// The callout should be removed when primary CTA is clicked
await calloutRemoved;
BrowserTestUtils.removeTab(openedTab);
}
);
Services.prefs.clearUserPref("identity.fxaccounts.enabled");
sandbox.restore();
}
);
add_task(async function feature_callout_dismiss_on_timeout() {
await SpecialPowers.pushPrefEnv({
set: [[featureTourPref, `{"screen":"","complete":true}`]],
});
const screenId = "FIREFOX_VIEW_TAB_PICKUP_REMINDER";
let testMessage = getCalloutMessageById(screenId);
// Configure message with a dismiss action on tab container click
testMessage.message.content.screens[0].content.page_event_listeners = [
{
params: { type: "timeout", options: { once: true, interval: 5000 } },
action: { dismiss: true, type: "CANCEL" },
},
];
const sandbox = createSandboxWithCalloutTriggerStub(testMessage);
const telemetrySpy = new TelemetrySpy(sandbox);
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: "about:firefoxview",
},
async browser => {
const { document } = browser.contentWindow;
let onInterval;
let startedInterval = new Promise(resolve => {
sandbox
.stub(browser.contentWindow, "setInterval")
.callsFake((fn, ms) => {
Assert.strictEqual(
ms,
5000,
"setInterval called with 5 second interval"
);
onInterval = fn;
resolve();
return 1;
});
});
launchFeatureTourIn(browser.contentWindow);
info("Waiting for callout to render");
await startedInterval;
await waitForCalloutScreen(document, screenId);
info("Ending timeout");
onInterval();
await waitForCalloutRemoved(document);
// Test that appropriate telemetry is sent
telemetrySpy.assertCalledWith({
event: "PAGE_EVENT",
event_context: {
action: "CANCEL",
reason: "TIMEOUT",
source: "timeout",
page: "about:firefoxview",
},
message_id: screenId,
});
telemetrySpy.assertCalledWith({
event: "DISMISS",
event_context: {
source: "PAGE_EVENT:timeout",
page: "about:firefoxview",
},
message_id: screenId,
});
}
);
Services.prefs.clearUserPref("browser.firefox-view.view-count");
sandbox.restore();
ASRouter.resetMessageState();
});
add_task(async function feature_callout_advance_tour_on_page_click() {
await SpecialPowers.pushPrefEnv({
set: [
[
featureTourPref,
JSON.stringify({
screen: "FEATURE_CALLOUT_1",
complete: false,
}),
],
],
});
// Add page action listeners to the built-in messages.
let testMessage = getCalloutMessageById("FIREFOX_VIEW_FEATURE_TOUR");
// Configure message with a dismiss action on tab container click
testMessage.message.content.screens.forEach(screen => {
screen.content.page_event_listeners = [
{
params: { type: "click", selectors: ".brand-logo" },
action: JSON.parse(
JSON.stringify(screen.content.primary_button.action)
),
},
];
});
const sandbox = createSandboxWithCalloutTriggerStub(testMessage);
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: "about:firefoxview",
},
async browser => {
const { document } = browser.contentWindow;
launchFeatureTourIn(browser.contentWindow);
await waitForCalloutScreen(document, "FEATURE_CALLOUT_1");
info("Clicking page container");
// We intentionally turn off a11y_checks, because the following click
// is send to dismiss the feature callout using an alternative way of
// the callout dismissal, where other ways are accessible, therefore
// this test can be ignored.
AccessibilityUtils.setEnv({
mustHaveAccessibleRule: false,
});
document.querySelector(".brand-logo").click();
AccessibilityUtils.resetEnv();
await waitForCalloutScreen(document, "FEATURE_CALLOUT_2");
info("Clicking page container");
// We intentionally turn off a11y_checks, because the following click
// is send to dismiss the feature callout using an alternative way of
// the callout dismissal, where other ways are accessible, therefore
// this test can be ignored.
AccessibilityUtils.setEnv({
mustHaveAccessibleRule: false,
});
document.querySelector(".brand-logo").click();
AccessibilityUtils.resetEnv();
await waitForCalloutRemoved(document);
let tourComplete = JSON.parse(
Services.prefs.getStringPref(featureTourPref)
).complete;
ok(
tourComplete,
`Tour is recorded as complete in ${featureTourPref} preference value`
);
}
);
sandbox.restore();
ASRouter.resetMessageState();
});
add_task(async function feature_callout_dismiss_on_escape() {
await SpecialPowers.pushPrefEnv({
set: [[featureTourPref, `{"screen":"","complete":true}`]],
});
const screenId = "FIREFOX_VIEW_TAB_PICKUP_REMINDER";
let testMessage = getCalloutMessageById(screenId);
const sandbox = createSandboxWithCalloutTriggerStub(testMessage);
const spy = new TelemetrySpy(sandbox);
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: "about:firefoxview",
},
async browser => {
const { document } = browser.contentWindow;
launchFeatureTourIn(browser.contentWindow);
info("Waiting for callout to render");
await waitForCalloutScreen(document, screenId);
info("Pressing escape");
// Press Escape to close
EventUtils.synthesizeKey("KEY_Escape", {}, browser.contentWindow);
await waitForCalloutRemoved(document);
// Test that appropriate telemetry is sent
spy.assertCalledWith({
event: "DISMISS",
event_context: {
source: "KEY_Escape",
page: "about:firefoxview",
},
message_id: screenId,
});
}
);
Services.prefs.clearUserPref("browser.firefox-view.view-count");
sandbox.restore();
ASRouter.resetMessageState();
});
add_task(async function test_firefox_view_spotlight_promo() {
// Prevent attempts to fetch CFR messages remotely.
const sandbox = sinon.createSandbox();
let remoteSettingsStub = sandbox.stub(
MessageLoaderUtils,
"_remoteSettingsLoader"
);
remoteSettingsStub.resolves([]);
await SpecialPowers.pushPrefEnv({
clear: [
[featureTourPref],
["browser.newtabpage.activity-stream.asrouter.providers.cfr"],
],
});
ASRouter.resetMessageState();
let dialogOpenPromise = BrowserTestUtils.promiseAlertDialogOpen(
null,
"chrome://browser/content/spotlight.html",
{ isSubDialog: true }
);
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: "about:firefoxview",
},
async browser => {
launchFeatureTourIn(browser.contentWindow);
info("Waiting for the Fx View Spotlight promo to open");
let dialogBrowser = await dialogOpenPromise;
let primaryBtnSelector = ".action-buttons button.primary";
await TestUtils.waitForCondition(
() => dialogBrowser.document.querySelector("main.DEFAULT_MODAL_UI"),
`Should render main.DEFAULT_MODAL_UI`
);
dialogBrowser.document.querySelector(primaryBtnSelector).click();
info("Fx View Spotlight promo clicked");
const { document } = browser.contentWindow;
await waitForCalloutScreen(document, "FEATURE_CALLOUT_1");
ok(
document.querySelector(calloutSelector),
"Feature Callout element exists"
);
info("Feature tour started");
await clickCTA(document);
}
);
ok(remoteSettingsStub.called, "Tried to load CFR messages");
sandbox.restore();
await SpecialPowers.popPrefEnv();
ASRouter.resetMessageState();
});
add_task(async function feature_callout_returns_default_fxview_focus_to_top() {
await SpecialPowers.pushPrefEnv({
set: [[featureTourPref, defaultPrefValue]],
});
const testMessage = getCalloutMessageById("FIREFOX_VIEW_FEATURE_TOUR");
const sandbox = createSandboxWithCalloutTriggerStub(testMessage);
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: "about:firefoxview",
},
async browser => {
const { document } = browser.contentWindow;
launchFeatureTourIn(browser.contentWindow);
await waitForCalloutScreen(document, "FEATURE_CALLOUT_1");
ok(
document.querySelector(calloutSelector),
"Feature Callout element exists"
);
document.querySelector(".dismiss-button").click();
await waitForCalloutRemoved(document);
Assert.strictEqual(
document.activeElement.localName,
"body",
"by default focus returns to the document body after callout closes"
);
}
);
sandbox.restore();
await SpecialPowers.popPrefEnv();
ASRouter.resetMessageState();
});
add_task(
async function feature_callout_returns_moved_fxview_focus_to_previous() {
const testMessage = getCalloutMessageById(
"FIREFOX_VIEW_TAB_PICKUP_REMINDER"
);
const sandbox = createSandboxWithCalloutTriggerStub(testMessage);
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: "about:firefoxview",
},
async browser => {
const { document } = browser.contentWindow;
launchFeatureTourIn(browser.contentWindow);
await waitForCalloutScreen(
document,
"FIREFOX_VIEW_TAB_PICKUP_REMINDER"
);
// change focus to recently-closed-tabs-container
let recentlyClosedHeaderSection = document.querySelector(
"#recently-closed-tabs-header-section"
);
recentlyClosedHeaderSection.focus();
// close the callout dialog
document.querySelector(".dismiss-button").click();
await waitForCalloutRemoved(document);
// verify that the focus landed in the right place
Assert.strictEqual(
document.activeElement.id,
"recently-closed-tabs-header-section",
"when focus changes away from callout it reverts after callout closes"
);
}
);
sandbox.restore();
}
);
add_task(async function feature_callout_does_not_display_arrow_if_hidden() {
await SpecialPowers.pushPrefEnv({
set: [[featureTourPref, defaultPrefValue]],
});
const testMessage = getCalloutMessageById("FIREFOX_VIEW_FEATURE_TOUR");
testMessage.message.content.screens[0].anchors[0].hide_arrow = true;
const sandbox = createSandboxWithCalloutTriggerStub(testMessage);
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: "about:firefoxview",
},
async browser => {
const { document } = browser.contentWindow;
launchFeatureTourIn(browser.contentWindow);
await waitForCalloutScreen(document, "FEATURE_CALLOUT_1");
is(
getComputedStyle(
document.querySelector(`${calloutSelector} .arrow-box`)
).getPropertyValue("display"),
"none",
"callout arrow is not visible"
);
}
);
sandbox.restore();
});

View File

@@ -1,445 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
requestLongerTimeout(2);
const defaultPrefValue = getPrefValueByScreen(1);
const squareWidth = 24;
const arrowWidth = Math.hypot(squareWidth, squareWidth);
const arrowHeight = arrowWidth / 2;
let overlap = 5 - arrowHeight;
add_task(
async function feature_callout_first_screen_positioned_below_element() {
await SpecialPowers.pushPrefEnv({
set: [[featureTourPref, defaultPrefValue]],
});
const testMessage = getCalloutMessageById("FIREFOX_VIEW_FEATURE_TOUR");
const sandbox = createSandboxWithCalloutTriggerStub(testMessage);
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: "about:firefoxview",
},
async browser => {
const { document } = browser.contentWindow;
launchFeatureTourIn(browser.contentWindow);
await waitForCalloutScreen(document, "FEATURE_CALLOUT_1");
let parentBottom = document
.querySelector("#tab-pickup-container")
.getBoundingClientRect().bottom;
let containerTop = document
.querySelector(calloutSelector)
.getBoundingClientRect().top;
isfuzzy(
parentBottom - containerTop,
overlap,
1, // add 1px fuzziness to account for possible subpixel rounding
"Feature Callout is positioned below parent element with the arrow overlapping by 5px"
);
}
);
sandbox.restore();
}
);
add_task(
async function feature_callout_second_screen_positioned_right_of_element() {
await SpecialPowers.pushPrefEnv({
set: [[featureTourPref, getPrefValueByScreen(2)]],
});
const testMessage = getCalloutMessageById("FIREFOX_VIEW_FEATURE_TOUR");
testMessage.message.content.screens[1].anchors = [
{ selector: ".brand-logo", arrow_position: "start" },
];
const sandbox = createSandboxWithCalloutTriggerStub(testMessage);
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: "about:firefoxview",
},
async browser => {
const { document } = browser.contentWindow;
launchFeatureTourIn(browser.contentWindow);
await waitForCalloutScreen(document, "FEATURE_CALLOUT_2");
let parentRight = document
.querySelector(".brand-logo")
.getBoundingClientRect().right;
let containerLeft = document
.querySelector(calloutSelector)
.getBoundingClientRect().left;
isfuzzy(
parentRight - containerLeft,
overlap,
1,
"Feature Callout is positioned right of parent element with the arrow overlapping by 5px"
);
}
);
sandbox.restore();
}
);
add_task(
async function feature_callout_second_screen_positioned_above_element() {
await SpecialPowers.pushPrefEnv({
set: [[featureTourPref, getPrefValueByScreen(2)]],
});
const testMessage = getCalloutMessageById("FIREFOX_VIEW_FEATURE_TOUR");
const sandbox = createSandboxWithCalloutTriggerStub(testMessage);
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: "about:firefoxview",
},
async browser => {
const { document } = browser.contentWindow;
launchFeatureTourIn(browser.contentWindow);
await waitForCalloutScreen(document, "FEATURE_CALLOUT_2");
let parentTop = document
.querySelector("#recently-closed-tabs-container")
.getBoundingClientRect().top;
let containerBottom = document
.querySelector(calloutSelector)
.getBoundingClientRect().bottom;
Assert.greaterOrEqual(
parentTop,
containerBottom - 5 - 1,
"Feature Callout is positioned above parent element with 5px overlap"
);
}
);
sandbox.restore();
}
);
add_task(
async function feature_callout_third_screen_position_respects_RTL_layouts() {
await SpecialPowers.pushPrefEnv({
set: [
// Set layout direction to right to left
["intl.l10n.pseudo", "bidi"],
[featureTourPref, getPrefValueByScreen(2)],
],
});
const testMessage = getCalloutMessageById("FIREFOX_VIEW_FEATURE_TOUR");
const sandbox = createSandboxWithCalloutTriggerStub(testMessage);
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: "about:firefoxview",
},
async browser => {
const { document } = browser.contentWindow;
launchFeatureTourIn(browser.contentWindow);
const parent = document.querySelector(
"#recently-closed-tabs-container"
);
parent.style.gridArea = "1/2";
await waitForCalloutScreen(document, "FEATURE_CALLOUT_2");
let parentRight = parent.getBoundingClientRect().right;
let containerLeft = document
.querySelector(calloutSelector)
.getBoundingClientRect().left;
Assert.lessOrEqual(
parentRight,
containerLeft + 5 + 1,
"Feature Callout is positioned right of parent element when callout is set to 'end' in RTL layouts"
);
}
);
await SpecialPowers.popPrefEnv();
sandbox.restore();
}
);
add_task(
async function feature_callout_is_repositioned_if_parent_container_is_toggled() {
await SpecialPowers.pushPrefEnv({
set: [[featureTourPref, defaultPrefValue]],
});
const testMessage = getCalloutMessageById("FIREFOX_VIEW_FEATURE_TOUR");
const sandbox = createSandboxWithCalloutTriggerStub(testMessage);
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: "about:firefoxview",
},
async browser => {
const { document } = browser.contentWindow;
launchFeatureTourIn(browser.contentWindow);
await waitForCalloutScreen(document, "FEATURE_CALLOUT_1");
const parentEl = document.querySelector("#tab-pickup-container");
const calloutStartingTopPosition =
document.querySelector(calloutSelector).style.top;
//container has been toggled/minimized
parentEl.removeAttribute("open", "");
await BrowserTestUtils.waitForMutationCondition(
document.querySelector(calloutSelector),
{ attributes: true },
() =>
document.querySelector(calloutSelector).style.top !=
calloutStartingTopPosition
);
isnot(
document.querySelector(calloutSelector).style.top,
calloutStartingTopPosition,
"Feature Callout position is recalculated when parent element is toggled"
);
await closeCallout(document);
}
);
sandbox.restore();
}
);
// This test should be moved into a surface agnostic test suite with bug 1793656.
add_task(async function feature_callout_top_end_positioning() {
await SpecialPowers.pushPrefEnv({
set: [[featureTourPref, defaultPrefValue]],
});
const testMessage = getCalloutMessageById("FIREFOX_VIEW_FEATURE_TOUR");
testMessage.message.content.screens[0].anchors[0].arrow_position = "top-end";
const sandbox = createSandboxWithCalloutTriggerStub(testMessage);
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: "about:firefoxview",
},
async browser => {
const { document } = browser.contentWindow;
launchFeatureTourIn(browser.contentWindow);
await waitForCalloutScreen(document, "FEATURE_CALLOUT_1");
let parent = document.querySelector("#tab-pickup-container");
let container = document.querySelector(calloutSelector);
let parentLeft = parent.getBoundingClientRect().left;
let containerLeft = container.getBoundingClientRect().left;
is(
container.getAttribute("arrow-position"),
"top-end",
"Feature Callout container has the expected top-end arrow-position attribute"
);
isfuzzy(
containerLeft - parent.clientWidth + container.offsetWidth,
parentLeft,
1, // Display scaling can cause up to 1px difference in layout
"Feature Callout's right edge is approximately aligned with parent element's right edge"
);
await closeCallout(document);
}
);
sandbox.restore();
});
// This test should be moved into a surface agnostic test suite with bug 1793656.
add_task(async function feature_callout_top_start_positioning() {
await SpecialPowers.pushPrefEnv({
set: [[featureTourPref, defaultPrefValue]],
});
const testMessage = getCalloutMessageById("FIREFOX_VIEW_FEATURE_TOUR");
testMessage.message.content.screens[0].anchors[0].arrow_position =
"top-start";
const sandbox = createSandboxWithCalloutTriggerStub(testMessage);
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: "about:firefoxview",
},
async browser => {
const { document } = browser.contentWindow;
launchFeatureTourIn(browser.contentWindow);
await waitForCalloutScreen(document, "FEATURE_CALLOUT_1");
let parent = document.querySelector("#tab-pickup-container");
let container = document.querySelector(calloutSelector);
let parentLeft = parent.getBoundingClientRect().left;
let containerLeft = container.getBoundingClientRect().left;
is(
container.getAttribute("arrow-position"),
"top-start",
"Feature Callout container has the expected top-start arrow-position attribute"
);
isfuzzy(
containerLeft,
parentLeft,
1, // Display scaling can cause up to 1px difference in layout
"Feature Callout's left edge is approximately aligned with parent element's left edge"
);
await closeCallout(document);
}
);
sandbox.restore();
});
// This test should be moved into a surface agnostic test suite with bug 1793656.
add_task(
async function feature_callout_top_end_position_respects_RTL_layouts() {
await SpecialPowers.pushPrefEnv({
set: [
// Set layout direction to right to left
["intl.l10n.pseudo", "bidi"],
[featureTourPref, defaultPrefValue],
],
});
const testMessage = getCalloutMessageById("FIREFOX_VIEW_FEATURE_TOUR");
testMessage.message.content.screens[0].anchors[0].arrow_position =
"top-end";
const sandbox = createSandboxWithCalloutTriggerStub(testMessage);
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: "about:firefoxview",
},
async browser => {
const { document } = browser.contentWindow;
launchFeatureTourIn(browser.contentWindow);
await waitForCalloutScreen(document, "FEATURE_CALLOUT_1");
let parent = document.querySelector("#tab-pickup-container");
let container = document.querySelector(calloutSelector);
let parentLeft = parent.getBoundingClientRect().left;
let containerLeft = container.getBoundingClientRect().left;
is(
container.getAttribute("arrow-position"),
"top-start",
"In RTL mode, the feature callout container has the expected top-start arrow-position attribute"
);
is(
containerLeft,
parentLeft,
"In RTL mode, the feature Callout's left edge is aligned with parent element's left edge"
);
await closeCallout(document);
}
);
await SpecialPowers.popPrefEnv();
sandbox.restore();
}
);
add_task(async function feature_callout_is_larger_than_its_parent() {
let testMessage = {
message: {
id: "FIREFOX_VIEW_FEATURE_TOUR",
template: "feature_callout",
content: {
id: "FIREFOX_VIEW_FEATURE_TOUR",
transitions: false,
disableHistoryUpdates: true,
screens: [
{
id: "FEATURE_CALLOUT_1",
anchors: [
{ selector: ".brand-feature-name", arrow_position: "end" },
],
content: {
position: "callout",
title: "callout-firefox-view-tab-pickup-title",
subtitle: {
string_id: "callout-firefox-view-tab-pickup-subtitle",
},
logo: {
imageURL: "chrome://browser/content/callout-tab-pickup.svg",
darkModeImageURL:
"chrome://browser/content/callout-tab-pickup-dark.svg",
height: "128px", // .brand-feature-name has a height of 32px
},
dismiss_button: {
action: {
navigate: true,
},
},
},
},
],
},
},
};
const sandbox = createSandboxWithCalloutTriggerStub(testMessage);
await SpecialPowers.pushPrefEnv({
set: [[featureTourPref, defaultPrefValue]],
});
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: "about:firefoxview",
},
async browser => {
const { document } = browser.contentWindow;
launchFeatureTourIn(browser.contentWindow);
await waitForCalloutScreen(document, "FEATURE_CALLOUT_1");
let parent = document.querySelector(".brand-feature-name");
let container = document.querySelector(calloutSelector);
let parentHeight = parent.offsetHeight;
let containerHeight = container.offsetHeight;
let parentPositionTop =
parent.getBoundingClientRect().top + window.scrollY;
let containerPositionTop =
container.getBoundingClientRect().top + window.scrollY;
Assert.greater(
containerHeight,
parentHeight,
"Feature Callout is height is larger than parent element when callout is configured at end of callout"
);
Assert.less(
containerPositionTop,
parentPositionTop,
"Feature Callout is positioned higher that parent element when callout is configured at end of callout"
);
isfuzzy(
containerHeight / 2 + containerPositionTop,
parentHeight / 2 + parentPositionTop,
1, // Display scaling can cause up to 1px difference in layout
"Feature Callout is centered equally to parent element when callout is configured at end of callout"
);
await ASRouter.resetMessageState();
}
);
sandbox.restore();
});

View File

@@ -1,178 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
function getArrowPosition(doc) {
let callout = doc.querySelector(calloutSelector);
return callout.getAttribute("arrow-position");
}
add_setup(async function setup() {
let originalWidth = window.outerWidth;
let originalHeight = window.outerHeight;
registerCleanupFunction(async () => {
await BrowserTestUtils.withNewTab(
{ gBrowser, url: "about:firefoxview" },
async browser => window.FullZoom.reset(browser)
);
window.resizeTo(originalWidth, originalHeight);
});
await BrowserTestUtils.withNewTab(
{ gBrowser, url: "about:firefoxview" },
async browser => window.FullZoom.setZoom(0.5, browser)
);
});
add_task(async function feature_callout_is_repositioned_if_it_does_not_fit() {
await SpecialPowers.pushPrefEnv({
set: [["browser.sessionstore.max_tabs_undo", 1]],
});
const testMessage = getCalloutMessageById("FIREFOX_VIEW_FEATURE_TOUR");
const sandbox = createSandboxWithCalloutTriggerStub(testMessage);
await BrowserTestUtils.withNewTab(
{ gBrowser, url: "about:firefoxview" },
async browser => {
const { document } = browser.contentWindow;
launchFeatureTourIn(browser.contentWindow);
browser.contentWindow.resizeTo(1550, 1000);
await waitForCalloutScreen(document, "FEATURE_CALLOUT_1");
await BrowserTestUtils.waitForCondition(() => {
if (getArrowPosition(document) === "top") {
return true;
}
browser.contentWindow.resizeTo(1550, 1000);
return false;
});
is(
getArrowPosition(document),
"top",
"On first screen at 1550x1000, the callout is positioned below the parent element"
);
let startingTop = document.querySelector(calloutSelector).style.top;
browser.contentWindow.resizeTo(1800, 400);
// Wait for callout to be repositioned
await BrowserTestUtils.waitForMutationCondition(
document.querySelector(calloutSelector),
{ attributeFilter: ["style"], attributes: true },
() => document.querySelector(calloutSelector).style.top != startingTop
);
await BrowserTestUtils.waitForCondition(() => {
if (getArrowPosition(document) === "inline-start") {
return true;
}
browser.contentWindow.resizeTo(1800, 400);
return false;
});
is(
getArrowPosition(document),
"inline-start",
"On first screen at 1800x400, the callout is positioned to the right of the parent element"
);
startingTop = document.querySelector(calloutSelector).style.top;
browser.contentWindow.resizeTo(1100, 600);
await BrowserTestUtils.waitForMutationCondition(
document.querySelector(calloutSelector),
{ attributeFilter: ["style"], attributes: true },
() => document.querySelector(calloutSelector).style.top != startingTop
);
await BrowserTestUtils.waitForCondition(() => {
if (getArrowPosition(document) === "top") {
return true;
}
browser.contentWindow.resizeTo(1100, 600);
return false;
});
is(
getArrowPosition(document),
"top",
"On first screen at 1100x600, the callout is positioned below the parent element"
);
}
);
sandbox.restore();
});
add_task(async function feature_callout_is_repositioned_rtl() {
await SpecialPowers.pushPrefEnv({
set: [
// Set layout direction to right to left
["intl.l10n.pseudo", "bidi"],
["browser.sessionstore.max_tabs_undo", 1],
],
});
const testMessage = getCalloutMessageById("FIREFOX_VIEW_FEATURE_TOUR");
const sandbox = createSandboxWithCalloutTriggerStub(testMessage);
await BrowserTestUtils.withNewTab(
{ gBrowser, url: "about:firefoxview" },
async browser => {
const { document } = browser.contentWindow;
launchFeatureTourIn(browser.contentWindow);
browser.contentWindow.resizeTo(1550, 1000);
await waitForCalloutScreen(document, "FEATURE_CALLOUT_1");
await BrowserTestUtils.waitForCondition(() => {
if (getArrowPosition(document) === "top") {
return true;
}
browser.contentWindow.resizeTo(1550, 1000);
return false;
});
is(
getArrowPosition(document),
"top",
"On first screen at 1550x1000, the callout is positioned below the parent element"
);
let startingTop = document.querySelector(calloutSelector).style.top;
browser.contentWindow.resizeTo(1800, 400);
// Wait for callout to be repositioned
await BrowserTestUtils.waitForMutationCondition(
document.querySelector(calloutSelector),
{ attributeFilter: ["style"], attributes: true },
() => document.querySelector(calloutSelector).style.top != startingTop
);
await BrowserTestUtils.waitForCondition(() => {
if (getArrowPosition(document) === "inline-end") {
return true;
}
browser.contentWindow.resizeTo(1800, 400);
return false;
});
is(
getArrowPosition(document),
"inline-end",
"On first screen at 1800x400, the callout is positioned to the right of the parent element"
);
startingTop = document.querySelector(calloutSelector).style.top;
browser.contentWindow.resizeTo(1100, 600);
await BrowserTestUtils.waitForMutationCondition(
document.querySelector(calloutSelector),
{ attributeFilter: ["style"], attributes: true },
() => document.querySelector(calloutSelector).style.top != startingTop
);
await BrowserTestUtils.waitForCondition(() => {
if (getArrowPosition(document) === "top") {
return true;
}
browser.contentWindow.resizeTo(1100, 600);
return false;
});
is(
getArrowPosition(document),
"top",
"On first screen at 1100x600, the callout is positioned below the parent element"
);
}
);
sandbox.restore();
});

View File

@@ -1,175 +0,0 @@
"use strict";
add_task(
async function test_firefox_view_tab_pick_up_not_signed_in_targeting() {
ASRouter.resetMessageState();
await SpecialPowers.pushPrefEnv({
set: [
["browser.firefox-view.feature-tour", `{"screen":"","complete":true}`],
],
});
await SpecialPowers.pushPrefEnv({
set: [["browser.firefox-view.view-count", 3]],
});
await SpecialPowers.pushPrefEnv({
set: [["identity.fxaccounts.enabled", false]],
});
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: "about:firefoxview",
},
async browser => {
const { document } = browser.contentWindow;
launchFeatureTourIn(browser.contentWindow);
await waitForCalloutScreen(
document,
"FIREFOX_VIEW_TAB_PICKUP_REMINDER"
);
ok(
document.querySelector(".featureCallout"),
"Firefox:View Tab Pickup should be displayed."
);
SpecialPowers.popPrefEnv();
SpecialPowers.popPrefEnv();
SpecialPowers.popPrefEnv();
}
);
}
);
add_task(
async function test_firefox_view_tab_pick_up_sync_not_enabled_targeting() {
ASRouter.resetMessageState();
await SpecialPowers.pushPrefEnv({
set: [
["browser.firefox-view.feature-tour", `{"screen":"","complete":true}`],
],
});
await SpecialPowers.pushPrefEnv({
set: [["browser.firefox-view.view-count", 3]],
});
await SpecialPowers.pushPrefEnv({
set: [["identity.fxaccounts.enabled", true]],
});
await SpecialPowers.pushPrefEnv({
set: [["services.sync.engine.tabs", false]],
});
await SpecialPowers.pushPrefEnv({
set: [["services.sync.username", false]],
});
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: "about:firefoxview",
},
async browser => {
const { document } = browser.contentWindow;
launchFeatureTourIn(browser.contentWindow);
await waitForCalloutScreen(
document,
"FIREFOX_VIEW_TAB_PICKUP_REMINDER"
);
ok(
document.querySelector(".featureCallout"),
"Firefox:View Tab Pickup should be displayed."
);
SpecialPowers.popPrefEnv();
SpecialPowers.popPrefEnv();
SpecialPowers.popPrefEnv();
SpecialPowers.popPrefEnv();
SpecialPowers.popPrefEnv();
}
);
}
);
add_task(
async function test_firefox_view_tab_pick_up_wait_24_hours_after_spotlight() {
const TWENTY_FIVE_HOURS_IN_MS = 25 * 60 * 60 * 1000;
ASRouter.resetMessageState();
await SpecialPowers.pushPrefEnv({
set: [
["browser.firefox-view.feature-tour", `{"screen":"","complete":true}`],
],
});
await SpecialPowers.pushPrefEnv({
set: [["browser.firefox-view.view-count", 3]],
});
await SpecialPowers.pushPrefEnv({
set: [["identity.fxaccounts.enabled", false]],
});
ASRouter.setState({
messageImpressions: { FIREFOX_VIEW_SPOTLIGHT: [Date.now()] },
});
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: "about:firefoxview",
},
async browser => {
const { document } = browser.contentWindow;
launchFeatureTourIn(browser.contentWindow);
ok(
!document.querySelector(".featureCallout"),
"Tab Pickup reminder should not be displayed when the Spotlight message introducing the tour was viewed less than 24 hours ago."
);
}
);
ASRouter.setState({
messageImpressions: {
FIREFOX_VIEW_SPOTLIGHT: [Date.now() - TWENTY_FIVE_HOURS_IN_MS],
},
});
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: "about:firefoxview",
},
async browser => {
const { document } = browser.contentWindow;
launchFeatureTourIn(browser.contentWindow);
await waitForCalloutScreen(
document,
"FIREFOX_VIEW_TAB_PICKUP_REMINDER"
);
ok(
document.querySelector(".featureCallout"),
"Tab Pickup reminder can be displayed when the Spotlight message introducing the tour was viewed over 24 hours ago."
);
SpecialPowers.popPrefEnv();
SpecialPowers.popPrefEnv();
SpecialPowers.popPrefEnv();
}
);
}
);

View File

@@ -1,80 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const { FeatureCallout } = ChromeUtils.importESModule(
"resource:///modules/asrouter/FeatureCallout.sys.mjs"
);
async function testCallout(config) {
const featureCallout = new FeatureCallout(config);
const testMessage = getCalloutMessageById("FIREFOX_VIEW_FEATURE_TOUR");
const screen = testMessage.message.content.screens[1];
screen.anchors[0].selector = "body";
testMessage.message.content.screens = [screen];
featureCallout.showFeatureCallout(testMessage.message);
await waitForCalloutScreen(config.win.document, screen.id);
testStyles(config);
return { featureCallout };
}
function testStyles({ win, theme }) {
const calloutEl = win.document.querySelector(calloutSelector);
const calloutStyle = win.getComputedStyle(calloutEl);
for (const type of ["light", "dark", "hcm"]) {
const appliedTheme = Object.assign(
{},
FeatureCallout.themePresets[theme.preset],
theme
);
const scheme = appliedTheme[type];
for (const name of FeatureCallout.themePropNames) {
Assert.equal(
!!calloutStyle.getPropertyValue(`--fc-${name}-${type}`),
!!(scheme?.[name] || appliedTheme.all?.[name]),
`Theme property --fc-${name}-${type} is set`
);
}
}
}
add_task(async function feature_callout_chrome_theme() {
const win = await BrowserTestUtils.openNewBrowserWindow();
await testCallout({
win,
location: "chrome",
context: "chrome",
browser: win.gBrowser.selectedBrowser,
theme: { preset: "chrome" },
});
await BrowserTestUtils.closeWindow(win);
});
add_task(async function feature_callout_pdfjs_theme() {
const win = await BrowserTestUtils.openNewBrowserWindow();
await testCallout({
win,
location: "pdfjs",
context: "chrome",
browser: win.gBrowser.selectedBrowser,
theme: { preset: "pdfjs", simulateContent: true },
});
await BrowserTestUtils.closeWindow(win);
});
add_task(async function feature_callout_content_theme() {
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: "about:firefoxview",
},
browser =>
testCallout({
win: browser.contentWindow,
location: "about:firefoxview",
context: "content",
theme: { preset: "themed-content" },
})
);
});

View File

@@ -52,10 +52,6 @@ ChromeUtils.defineESModuleGetters(this, {
TabStateFlusher: "resource:///modules/sessionstore/TabStateFlusher.sys.mjs",
});
const calloutId = "feature-callout";
const calloutSelector = `#${calloutId}.featureCallout`;
const CTASelector = `#${calloutId} :is(.primary, .secondary)`;
/**
* URLs used for browser_recently_closed_tabs_keyboard and
* browser_firefoxview_accessibility
@@ -337,174 +333,6 @@ async function tearDown(sandbox) {
Services.prefs.clearUserPref("services.sync.lastTabFetch");
}
const featureTourPref = "browser.firefox-view.feature-tour";
const launchFeatureTourIn = win => {
const { FeatureCallout } = ChromeUtils.importESModule(
"resource:///modules/asrouter/FeatureCallout.sys.mjs"
);
let callout = new FeatureCallout({
win,
pref: { name: featureTourPref },
location: "about:firefoxview",
context: "content",
theme: { preset: "themed-content" },
});
callout.showFeatureCallout();
return callout;
};
/**
* Returns a value that can be used to set
* `browser.firefox-view.feature-tour` to change the feature tour's
* UI state.
*
* @see FeatureCalloutMessages.sys.mjs for valid values of "screen"
*
* @param {number} screen The full ID of the feature callout screen
* @returns {string} JSON string used to set
* `browser.firefox-view.feature-tour`
*/
const getPrefValueByScreen = screen => {
return JSON.stringify({
screen: `FEATURE_CALLOUT_${screen}`,
complete: false,
});
};
/**
* Wait for a feature callout screen of given parameters to be shown
*
* @param {Document} doc the document where the callout appears.
* @param {string} screenPostfix The full ID of the feature callout screen.
*/
const waitForCalloutScreen = async (doc, screenPostfix) => {
await BrowserTestUtils.waitForCondition(() =>
doc.querySelector(`${calloutSelector}:not(.hidden) .${screenPostfix}`)
);
};
/**
* Waits for the feature callout screen to be removed.
*
* @param {Document} doc The document where the callout appears.
*/
const waitForCalloutRemoved = async doc => {
await BrowserTestUtils.waitForCondition(() => {
return !doc.body.querySelector(calloutSelector);
});
};
/**
* NOTE: Should be replaced with synthesizeMouseAtCenter for
* simulating user input. See Bug 1798322
*
* Clicks the primary button in the feature callout dialog
*
* @param {document} doc Firefox View document
*/
const clickCTA = async doc => {
doc.querySelector(CTASelector).click();
};
/**
* Closes a feature callout via a click to the dismiss button.
*
* @param {Document} doc The document where the callout appears.
*/
const closeCallout = async doc => {
// close the callout dialog
const dismissBtn = doc.querySelector(`${calloutSelector} .dismiss-button`);
if (!dismissBtn) {
return;
}
doc.querySelector(`${calloutSelector} .dismiss-button`).click();
await BrowserTestUtils.waitForCondition(() => {
return !document.querySelector(calloutSelector);
});
};
/**
* Get a Feature Callout message by id.
*
* @param {string} id
* The message id.
*/
const getCalloutMessageById = id => {
return {
message: FeatureCalloutMessages.getMessages().find(m => m.id === id),
};
};
/**
* Create a sinon sandbox with `sendTriggerMessage` stubbed
* to return a specified test message for featureCalloutCheck.
*
* @param {object} testMessage
* @param {string} [source="about:firefoxview"]
*/
const createSandboxWithCalloutTriggerStub = (
testMessage,
source = "about:firefoxview"
) => {
const firefoxViewMatch = sinon.match({
id: "featureCalloutCheck",
context: { source },
});
const sandbox = sinon.createSandbox();
const sendTriggerStub = sandbox.stub(ASRouter, "sendTriggerMessage");
sendTriggerStub.withArgs(firefoxViewMatch).resolves(testMessage);
sendTriggerStub.callThrough();
return sandbox;
};
/**
* A helper to check that correct telemetry was sent by AWSendEventTelemetry.
* This is a wrapper around sinon's spy functionality.
*
* @example
* let spy = new TelemetrySpy();
* element.click();
* spy.assertCalledWith({ event: "CLICK" });
* spy.restore();
*/
class TelemetrySpy {
/**
* @param {object} [sandbox] A pre-existing sinon sandbox to build the spy in.
* If not provided, a new sandbox will be created.
*/
constructor(sandbox = sinon.createSandbox()) {
this.sandbox = sandbox;
this.spy = this.sandbox
.spy(AboutWelcomeParent.prototype, "onContentMessage")
.withArgs("AWPage:TELEMETRY_EVENT");
registerCleanupFunction(() => this.restore());
}
/**
* Assert that AWSendEventTelemetry sent the expected telemetry object.
*
* @param {object} expectedData
*/
assertCalledWith(expectedData) {
let match = this.spy.calledWith("AWPage:TELEMETRY_EVENT", expectedData);
if (match) {
ok(true, "Expected telemetry sent");
} else if (this.spy.called) {
ok(
false,
"Wrong telemetry sent: " + JSON.stringify(this.spy.lastCall.args)
);
} else {
ok(false, "No telemetry sent");
}
}
reset() {
this.spy.resetHistory();
}
restore() {
this.sandbox.restore();
}
}
/**
* Helper function to open and close a tab so the recently
* closed tabs list can have data.