summaryrefslogtreecommitdiffstats
path: root/browser/components/newtab/test/browser/browser_aboutwelcome_multistage_default.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/newtab/test/browser/browser_aboutwelcome_multistage_default.js')
-rw-r--r--browser/components/newtab/test/browser/browser_aboutwelcome_multistage_default.js736
1 files changed, 736 insertions, 0 deletions
diff --git a/browser/components/newtab/test/browser/browser_aboutwelcome_multistage_default.js b/browser/components/newtab/test/browser/browser_aboutwelcome_multistage_default.js
new file mode 100644
index 0000000000..9d578db93d
--- /dev/null
+++ b/browser/components/newtab/test/browser/browser_aboutwelcome_multistage_default.js
@@ -0,0 +1,736 @@
+"use strict";
+const { SpecialMessageActions } = ChromeUtils.importESModule(
+ "resource://messaging-system/lib/SpecialMessageActions.sys.mjs"
+);
+
+const DID_SEE_ABOUT_WELCOME_PREF = "trailhead.firstrun.didSeeAboutWelcome";
+
+const TEST_DEFAULT_CONTENT = [
+ {
+ id: "AW_STEP1",
+ content: {
+ position: "split",
+ title: "Step 1",
+ primary_button: {
+ label: "Next",
+ action: {
+ navigate: true,
+ },
+ },
+ secondary_button: {
+ label: "link",
+ },
+ secondary_button_top: {
+ label: "link top",
+ action: {
+ type: "SHOW_FIREFOX_ACCOUNTS",
+ data: { entrypoint: "test" },
+ },
+ },
+ help_text: {
+ text: "Here's some sample help text",
+ },
+ },
+ },
+ {
+ id: "AW_STEP2",
+ content: {
+ position: "center",
+ title: "Step 2",
+ primary_button: {
+ label: "Next",
+ action: {
+ navigate: true,
+ },
+ },
+ secondary_button: {
+ label: "link",
+ },
+ has_noodles: true,
+ },
+ },
+ {
+ id: "AW_STEP3",
+ content: {
+ title: "Step 3",
+ tiles: {
+ type: "theme",
+ action: {
+ theme: "<event>",
+ },
+ data: [
+ {
+ theme: "automatic",
+ label: "theme-1",
+ tooltip: "test-tooltip",
+ },
+ {
+ theme: "dark",
+ label: "theme-2",
+ },
+ ],
+ },
+ primary_button: {
+ label: "Next",
+ action: {
+ navigate: true,
+ },
+ },
+ secondary_button: {
+ label: "Import",
+ action: {
+ type: "SHOW_MIGRATION_WIZARD",
+ data: { source: "chrome" },
+ },
+ },
+ },
+ },
+ {
+ id: "AW_STEP4",
+ auto_advance: "primary_button",
+ content: {
+ title: "Step 4",
+ primary_button: {
+ label: "Next",
+ action: {
+ navigate: true,
+ },
+ },
+ secondary_button: {
+ label: "link",
+ },
+ },
+ },
+];
+
+const TEST_DEFAULT_JSON = JSON.stringify(TEST_DEFAULT_CONTENT);
+
+async function openAboutWelcome() {
+ await setAboutWelcomePref(true);
+ await setAboutWelcomeMultiStage(TEST_DEFAULT_JSON);
+
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "about:welcome",
+ true
+ );
+ registerCleanupFunction(() => {
+ BrowserTestUtils.removeTab(tab);
+ });
+ return tab.linkedBrowser;
+}
+
+/**
+ * Test the multistage welcome default UI
+ */
+add_task(async function test_multistage_aboutwelcome_default() {
+ const sandbox = sinon.createSandbox();
+ let browser = await openAboutWelcome();
+ let aboutWelcomeActor = await getAboutWelcomeParent(browser);
+ // Stub AboutWelcomeParent Content Message Handler
+ sandbox.spy(aboutWelcomeActor, "onContentMessage");
+ registerCleanupFunction(() => {
+ sandbox.restore();
+ });
+
+ await test_screen_content(
+ browser,
+ "multistage step 1",
+ // Expected selectors:
+ [
+ "main.AW_STEP1",
+ "div.onboardingContainer",
+ "div.section-secondary",
+ "span.attrib-text",
+ "div.secondary-cta.top",
+ "div.steps",
+ "div.indicator.current",
+ ],
+ // Unexpected selectors:
+ [
+ "main.AW_STEP2",
+ "main.AW_STEP3",
+ "main.dialog-initial",
+ "main.dialog-last",
+ ]
+ );
+
+ await onButtonClick(browser, "button.primary");
+
+ const { callCount } = aboutWelcomeActor.onContentMessage;
+ ok(callCount >= 1, `${callCount} Stub was called`);
+ let clickCall;
+ for (let i = 0; i < callCount; i++) {
+ const call = aboutWelcomeActor.onContentMessage.getCall(i);
+ info(`Call #${i}: ${call.args[0]} ${JSON.stringify(call.args[1])}`);
+ if (call.calledWithMatch("", { event: "CLICK_BUTTON" })) {
+ clickCall = call;
+ }
+ }
+
+ Assert.ok(
+ clickCall.args[1].message_id === "MR_WELCOME_DEFAULT_0_AW_STEP1",
+ "AboutWelcome MR message id joined with screen id"
+ );
+
+ await test_screen_content(
+ browser,
+ "multistage step 2",
+ // Expected selectors:
+ [
+ "main.AW_STEP2",
+ "div.onboardingContainer",
+ "div.section-main",
+ "div.steps",
+ "div.indicator.current",
+ "main.with-noodles",
+ ],
+ // Unexpected selectors:
+ [
+ "main.AW_STEP1",
+ "main.AW_STEP3",
+ "div.section-secondary",
+ "main.dialog-last",
+ ]
+ );
+
+ await onButtonClick(browser, "button.primary");
+
+ // No 3rd screen to go to for win7.
+ if (win7Content) return;
+
+ await test_screen_content(
+ browser,
+ "multistage step 3",
+ // Expected selectors:
+ [
+ "main.AW_STEP3",
+ "div.onboardingContainer",
+ "div.section-main",
+ "div.tiles-theme-container",
+ "div.steps",
+ "div.indicator.current",
+ ],
+ // Unexpected selectors:
+ [
+ "main.AW_STEP2",
+ "main.AW_STEP1",
+ "div.section-secondary",
+ "main.dialog-initial",
+ "main.with-noodles",
+ "main.dialog-last",
+ ]
+ );
+
+ await onButtonClick(browser, "button.primary");
+
+ await test_screen_content(
+ browser,
+ "multistage step 4",
+ // Expected selectors:
+ [
+ "main.AW_STEP4.screen-1",
+ "main.AW_STEP4.dialog-last",
+ "div.onboardingContainer",
+ ],
+ // Unexpected selectors:
+ [
+ "main.AW_STEP2",
+ "main.AW_STEP1",
+ "main.AW_STEP3",
+ "div.steps",
+ "main.dialog-initial",
+ "main.AW_STEP4.screen-0",
+ "main.AW_STEP4.screen-2",
+ "main.AW_STEP4.screen-3",
+ ]
+ );
+});
+
+/**
+ * Test navigating back/forward between screens
+ */
+add_task(async function test_Multistage_About_Welcome_navigation() {
+ let browser = await openAboutWelcome();
+
+ await onButtonClick(browser, "button.primary");
+ await TestUtils.waitForCondition(() => browser.canGoBack);
+ browser.goBack();
+
+ await test_screen_content(
+ browser,
+ "multistage step 1",
+ // Expected selectors:
+ [
+ "div.onboardingContainer",
+ "main.AW_STEP1",
+ "div.secondary-cta",
+ "div.secondary-cta.top",
+ "button[value='secondary_button']",
+ "button[value='secondary_button_top']",
+ ],
+ // Unexpected selectors:
+ ["main.AW_STEP2", "main.AW_STEP3"]
+ );
+
+ await document.getElementById("forward-button").click();
+});
+
+/**
+ * Test the multistage welcome UI primary button action
+ */
+add_task(async function test_AWMultistage_Primary_Action() {
+ let browser = await openAboutWelcome();
+ let aboutWelcomeActor = await getAboutWelcomeParent(browser);
+ const sandbox = sinon.createSandbox();
+ sandbox.spy(aboutWelcomeActor, "onContentMessage");
+ registerCleanupFunction(() => {
+ sandbox.restore();
+ });
+
+ await onButtonClick(browser, "button.primary");
+ const { callCount } = aboutWelcomeActor.onContentMessage;
+ ok(callCount >= 1, `${callCount} Stub was called`);
+
+ let clickCall;
+ let performanceCall;
+ for (let i = 0; i < callCount; i++) {
+ const call = aboutWelcomeActor.onContentMessage.getCall(i);
+ info(`Call #${i}: ${call.args[0]} ${JSON.stringify(call.args[1])}`);
+ if (call.calledWithMatch("", { event: "CLICK_BUTTON" })) {
+ clickCall = call;
+ } else if (
+ call.calledWithMatch("", {
+ event_context: { mountStart: sinon.match.number },
+ })
+ ) {
+ performanceCall = call;
+ }
+ }
+
+ // For some builds, we can stub fast enough to catch the performance
+ if (performanceCall) {
+ Assert.equal(
+ performanceCall.args[0],
+ "AWPage:TELEMETRY_EVENT",
+ "send telemetry event"
+ );
+ Assert.equal(
+ performanceCall.args[1].event,
+ "IMPRESSION",
+ "performance impression event recorded in telemetry"
+ );
+ Assert.equal(
+ typeof performanceCall.args[1].event_context.domComplete,
+ "number",
+ "numeric domComplete recorded in telemetry"
+ );
+ Assert.equal(
+ typeof performanceCall.args[1].event_context.domInteractive,
+ "number",
+ "numeric domInteractive recorded in telemetry"
+ );
+ Assert.equal(
+ typeof performanceCall.args[1].event_context.mountStart,
+ "number",
+ "numeric mountStart recorded in telemetry"
+ );
+ Assert.equal(
+ performanceCall.args[1].message_id,
+ "MR_WELCOME_DEFAULT",
+ "MessageId sent in performance event telemetry"
+ );
+ }
+
+ Assert.equal(
+ clickCall.args[0],
+ "AWPage:TELEMETRY_EVENT",
+ "send telemetry event"
+ );
+ Assert.equal(
+ clickCall.args[1].event,
+ "CLICK_BUTTON",
+ "click button event recorded in telemetry"
+ );
+ Assert.equal(
+ clickCall.args[1].event_context.source,
+ "primary_button",
+ "primary button click source recorded in telemetry"
+ );
+ Assert.equal(
+ clickCall.args[1].message_id,
+ "MR_WELCOME_DEFAULT_0_AW_STEP1",
+ "MessageId sent in click event telemetry"
+ );
+});
+
+add_task(async function test_AWMultistage_Secondary_Open_URL_Action() {
+ if (win7Content) return;
+ let browser = await openAboutWelcome();
+ let aboutWelcomeActor = await getAboutWelcomeParent(browser);
+ const sandbox = sinon.createSandbox();
+ // Stub AboutWelcomeParent Content Message Handler
+ sandbox.stub(aboutWelcomeActor, "onContentMessage").resolves(null);
+ registerCleanupFunction(() => {
+ sandbox.restore();
+ });
+
+ await onButtonClick(browser, "button[value='secondary_button_top']");
+ const { callCount } = aboutWelcomeActor.onContentMessage;
+ ok(
+ callCount >= 2,
+ `${callCount} Stub called twice to handle FxA open URL and Telemetry`
+ );
+
+ let actionCall;
+ let eventCall;
+ for (let i = 0; i < callCount; i++) {
+ const call = aboutWelcomeActor.onContentMessage.getCall(i);
+ info(`Call #${i}: ${call.args[0]} ${JSON.stringify(call.args[1])}`);
+ if (call.calledWithMatch("SPECIAL")) {
+ actionCall = call;
+ } else if (call.calledWithMatch("", { event: "CLICK_BUTTON" })) {
+ eventCall = call;
+ }
+ }
+
+ Assert.equal(
+ actionCall.args[0],
+ "AWPage:SPECIAL_ACTION",
+ "Got call to handle special action"
+ );
+ Assert.equal(
+ actionCall.args[1].type,
+ "SHOW_FIREFOX_ACCOUNTS",
+ "Special action SHOW_FIREFOX_ACCOUNTS event handled"
+ );
+ Assert.equal(
+ actionCall.args[1].data.extraParams.utm_term,
+ "aboutwelcome-default-screen",
+ "UTMTerm set in FxA URL"
+ );
+ Assert.equal(
+ actionCall.args[1].data.entrypoint,
+ "test",
+ "EntryPoint set in FxA URL"
+ );
+ Assert.equal(
+ eventCall.args[0],
+ "AWPage:TELEMETRY_EVENT",
+ "Got call to handle Telemetry event"
+ );
+ Assert.equal(
+ eventCall.args[1].event,
+ "CLICK_BUTTON",
+ "click button event recorded in Telemetry"
+ );
+ Assert.equal(
+ eventCall.args[1].event_context.source,
+ "secondary_button_top",
+ "secondary_top button click source recorded in Telemetry"
+ );
+});
+
+add_task(async function test_AWMultistage_Themes() {
+ // No theme screen to test for win7.
+ if (win7Content) return;
+
+ let browser = await openAboutWelcome();
+ let aboutWelcomeActor = await getAboutWelcomeParent(browser);
+
+ const sandbox = sinon.createSandbox();
+ sandbox.spy(aboutWelcomeActor, "onContentMessage");
+
+ registerCleanupFunction(() => {
+ sandbox.restore();
+ });
+ await onButtonClick(browser, "button.primary");
+
+ await test_screen_content(
+ browser,
+ "multistage proton step 2",
+ // Expected selectors:
+ ["main.AW_STEP2"],
+ // Unexpected selectors:
+ ["main.AW_STEP1"]
+ );
+ await onButtonClick(browser, "button.primary");
+
+ await ContentTask.spawn(browser, "Themes", async () => {
+ await ContentTaskUtils.waitForCondition(
+ () => content.document.querySelector("label.theme"),
+ "Theme Icons"
+ );
+ let themes = content.document.querySelectorAll("label.theme");
+ Assert.equal(themes.length, 2, "Two themes displayed");
+ });
+
+ await onButtonClick(browser, "input[value=automatic]");
+
+ const { callCount } = aboutWelcomeActor.onContentMessage;
+ ok(callCount >= 1, `${callCount} Stub was called`);
+
+ let actionCall;
+ let eventCall;
+ for (let i = 0; i < callCount; i++) {
+ const call = aboutWelcomeActor.onContentMessage.getCall(i);
+ info(`Call #${i}: ${call.args[0]} ${JSON.stringify(call.args[1])}`);
+ if (call.calledWithMatch("SELECT_THEME")) {
+ actionCall = call;
+ } else if (call.calledWithMatch("", { event: "CLICK_BUTTON" })) {
+ eventCall = call;
+ }
+ }
+
+ Assert.equal(
+ actionCall.args[0],
+ "AWPage:SELECT_THEME",
+ "Got call to handle select theme"
+ );
+ Assert.equal(
+ actionCall.args[1],
+ "AUTOMATIC",
+ "Theme value passed as AUTOMATIC"
+ );
+ Assert.equal(
+ eventCall.args[0],
+ "AWPage:TELEMETRY_EVENT",
+ "Got call to handle Telemetry event when theme tile clicked"
+ );
+ Assert.equal(
+ eventCall.args[1].event,
+ "CLICK_BUTTON",
+ "click button event recorded in Telemetry"
+ );
+ Assert.equal(
+ eventCall.args[1].event_context.source,
+ "automatic",
+ "automatic click source recorded in Telemetry"
+ );
+});
+
+add_task(async function test_AWMultistage_can_restore_theme() {
+ const { XPIProvider } = ChromeUtils.import(
+ "resource://gre/modules/addons/XPIProvider.jsm"
+ );
+ const sandbox = sinon.createSandbox();
+ registerCleanupFunction(() => sandbox.restore());
+
+ const fakeAddons = [];
+ class FakeAddon {
+ constructor({ id = "default-theme@mozilla.org", isActive = false } = {}) {
+ this.id = id;
+ this.isActive = isActive;
+ }
+ enable() {
+ for (let addon of fakeAddons) {
+ addon.isActive = false;
+ }
+ this.isActive = true;
+ }
+ }
+ fakeAddons.push(
+ new FakeAddon({ id: "fake-theme-1@mozilla.org", isActive: true }),
+ new FakeAddon({ id: "fake-theme-2@mozilla.org" })
+ );
+
+ let browser = await openAboutWelcome();
+ let aboutWelcomeActor = await getAboutWelcomeParent(browser);
+
+ sandbox.stub(XPIProvider, "getAddonsByTypes").resolves(fakeAddons);
+ sandbox
+ .stub(XPIProvider, "getAddonByID")
+ .callsFake(id => fakeAddons.find(addon => addon.id === id));
+ sandbox.spy(aboutWelcomeActor, "onContentMessage");
+
+ // Test that the active theme ID is stored in LIGHT_WEIGHT_THEMES
+ await aboutWelcomeActor.receiveMessage({
+ name: "AWPage:GET_SELECTED_THEME",
+ });
+ Assert.equal(
+ await aboutWelcomeActor.onContentMessage.lastCall.returnValue,
+ "automatic",
+ `Should return "automatic" for non-built-in theme`
+ );
+
+ await aboutWelcomeActor.receiveMessage({
+ name: "AWPage:SELECT_THEME",
+ data: "AUTOMATIC",
+ });
+ Assert.equal(
+ XPIProvider.getAddonByID.lastCall.args[0],
+ fakeAddons[0].id,
+ `LIGHT_WEIGHT_THEMES.AUTOMATIC should be ${fakeAddons[0].id}`
+ );
+
+ // Enable a different theme...
+ fakeAddons[1].enable();
+ // And test that AWGetSelectedTheme updates the active theme ID
+ await aboutWelcomeActor.receiveMessage({
+ name: "AWPage:GET_SELECTED_THEME",
+ });
+ await aboutWelcomeActor.receiveMessage({
+ name: "AWPage:SELECT_THEME",
+ data: "AUTOMATIC",
+ });
+ Assert.equal(
+ XPIProvider.getAddonByID.lastCall.args[0],
+ fakeAddons[1].id,
+ `LIGHT_WEIGHT_THEMES.AUTOMATIC should be ${fakeAddons[1].id}`
+ );
+});
+
+add_task(async function test_AWMultistage_Import() {
+ // No import screen to test for win7.
+ if (win7Content) return;
+ let browser = await openAboutWelcome();
+ let aboutWelcomeActor = await getAboutWelcomeParent(browser);
+
+ // Click twice to advance to screen 3
+ await onButtonClick(browser, "button.primary");
+ await test_screen_content(
+ browser,
+ "multistage proton step 2",
+ // Expected selectors:
+ ["main.AW_STEP2"],
+ // Unexpected selectors:
+ ["main.AW_STEP1"]
+ );
+ await onButtonClick(browser, "button.primary");
+
+ const sandbox = sinon.createSandbox();
+ sandbox.stub(SpecialMessageActions, "handleAction");
+ sandbox.spy(aboutWelcomeActor, "onContentMessage");
+
+ registerCleanupFunction(() => {
+ sandbox.restore();
+ });
+
+ await test_screen_content(
+ browser,
+ "multistage proton step 2",
+ // Expected selectors:
+ ["main.AW_STEP3"],
+ // Unexpected selectors:
+ ["main.AW_STEP2"]
+ );
+
+ await onButtonClick(browser, "button[value='secondary_button']");
+ const { callCount } = aboutWelcomeActor.onContentMessage;
+
+ let actionCall;
+ let eventCall;
+ for (let i = 0; i < callCount; i++) {
+ const call = aboutWelcomeActor.onContentMessage.getCall(i);
+ info(`Call #${i}: ${call.args[0]} ${JSON.stringify(call.args[1])}`);
+ if (call.calledWithMatch("SPECIAL")) {
+ actionCall = call;
+ } else if (call.calledWithMatch("", { event: "CLICK_BUTTON" })) {
+ eventCall = call;
+ }
+ }
+
+ Assert.equal(
+ actionCall.args[0],
+ "AWPage:SPECIAL_ACTION",
+ "Got call to handle special action"
+ );
+ Assert.equal(
+ actionCall.args[1].type,
+ "SHOW_MIGRATION_WIZARD",
+ "Special action SHOW_MIGRATION_WIZARD event handled"
+ );
+ Assert.equal(
+ actionCall.args[1].data.source,
+ "chrome",
+ "Source passed to event handler"
+ );
+ Assert.equal(
+ eventCall.args[0],
+ "AWPage:TELEMETRY_EVENT",
+ "Got call to handle Telemetry event"
+ );
+});
+
+add_task(async function test_updatesPrefOnAWOpen() {
+ Services.prefs.setBoolPref(DID_SEE_ABOUT_WELCOME_PREF, false);
+ await setAboutWelcomePref(true);
+
+ await openAboutWelcome();
+ await TestUtils.waitForCondition(
+ () =>
+ Services.prefs.getBoolPref(DID_SEE_ABOUT_WELCOME_PREF, false) === true,
+ "Updated pref to seen AW"
+ );
+ Services.prefs.clearUserPref(DID_SEE_ABOUT_WELCOME_PREF);
+});
+
+add_setup(async function () {
+ const sandbox = sinon.createSandbox();
+ // This needs to happen before any about:welcome page opens
+ sandbox.stub(FxAccounts.config, "promiseMetricsFlowURI").resolves("");
+ await setAboutWelcomeMultiStage("");
+
+ registerCleanupFunction(() => {
+ sandbox.restore();
+ });
+});
+
+add_task(async function test_FxA_metricsFlowURI() {
+ let browser = await openAboutWelcome();
+
+ await ContentTask.spawn(browser, {}, async () => {
+ Assert.ok(
+ await ContentTaskUtils.waitForCondition(
+ () => content.document.querySelector("div.onboardingContainer"),
+ "Wait for about:welcome to load"
+ ),
+ "about:welcome loaded"
+ );
+ });
+
+ Assert.ok(FxAccounts.config.promiseMetricsFlowURI.called, "Stub was called");
+ Assert.equal(
+ FxAccounts.config.promiseMetricsFlowURI.firstCall.args[0],
+ "aboutwelcome",
+ "Called by AboutWelcomeParent"
+ );
+
+ SpecialPowers.popPrefEnv();
+});
+
+add_task(async function test_send_aboutwelcome_as_page_in_event_telemetry() {
+ const sandbox = sinon.createSandbox();
+ let browser = await openAboutWelcome();
+ let aboutWelcomeActor = await getAboutWelcomeParent(browser);
+ sandbox.spy(aboutWelcomeActor, "onContentMessage");
+
+ await onButtonClick(browser, "button.primary");
+
+ const { callCount } = aboutWelcomeActor.onContentMessage;
+ ok(callCount >= 1, `${callCount} Stub was called`);
+
+ let eventCall;
+ for (let i = 0; i < callCount; i++) {
+ const call = aboutWelcomeActor.onContentMessage.getCall(i);
+ info(`Call #${i}: ${call.args[0]} ${JSON.stringify(call.args[1])}`);
+ if (call.calledWithMatch("", { event: "CLICK_BUTTON" })) {
+ eventCall = call;
+ }
+ }
+
+ Assert.equal(
+ eventCall.args[1].event,
+ "CLICK_BUTTON",
+ "Event telemetry sent on primary button press"
+ );
+ Assert.equal(
+ eventCall.args[1].event_context.page,
+ "about:welcome",
+ "Event context page set to 'about:welcome' in event telemetry"
+ );
+
+ registerCleanupFunction(() => {
+ sandbox.restore();
+ });
+});