From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- .../tests/unit/MultiStageAboutWelcome.test.jsx | 859 +++++++++++++++++++++ 1 file changed, 859 insertions(+) create mode 100644 browser/components/aboutwelcome/tests/unit/MultiStageAboutWelcome.test.jsx (limited to 'browser/components/aboutwelcome/tests/unit/MultiStageAboutWelcome.test.jsx') diff --git a/browser/components/aboutwelcome/tests/unit/MultiStageAboutWelcome.test.jsx b/browser/components/aboutwelcome/tests/unit/MultiStageAboutWelcome.test.jsx new file mode 100644 index 0000000000..b4593a45f3 --- /dev/null +++ b/browser/components/aboutwelcome/tests/unit/MultiStageAboutWelcome.test.jsx @@ -0,0 +1,859 @@ +import { GlobalOverrider } from "newtab/test/unit/utils"; +import { + MultiStageAboutWelcome, + SecondaryCTA, + StepsIndicator, + ProgressBar, + WelcomeScreen, +} from "content-src/components/MultiStageAboutWelcome"; +import { Themes } from "content-src/components/Themes"; +import React from "react"; +import { shallow, mount } from "enzyme"; +import { AboutWelcomeDefaults } from "modules/AboutWelcomeDefaults.sys.mjs"; +import { AboutWelcomeUtils } from "content-src/lib/aboutwelcome-utils.mjs"; + +describe("MultiStageAboutWelcome module", () => { + let globals; + let sandbox; + + const DEFAULT_PROPS = { + defaultScreens: AboutWelcomeDefaults.getDefaults().screens, + metricsFlowUri: "http://localhost/", + message_id: "DEFAULT_ABOUTWELCOME", + utm_term: "default", + startScreen: 0, + }; + + beforeEach(async () => { + globals = new GlobalOverrider(); + globals.set({ + AWGetSelectedTheme: () => Promise.resolve("automatic"), + AWSendEventTelemetry: () => {}, + AWWaitForMigrationClose: () => Promise.resolve(), + AWSelectTheme: () => Promise.resolve(), + AWFinish: () => Promise.resolve(), + }); + sandbox = sinon.createSandbox(); + }); + + afterEach(() => { + sandbox.restore(); + globals.restore(); + }); + + describe("MultiStageAboutWelcome functional component", () => { + it("should render MultiStageAboutWelcome", () => { + const wrapper = shallow(); + + assert.ok(wrapper.exists()); + }); + + it("should pass activeTheme and initialTheme props to WelcomeScreen", async () => { + let wrapper = mount(); + // Spin the event loop to allow the useEffect hooks to execute, + // any promises to resolve, and re-rendering to happen after the + // promises have updated the state/props + await new Promise(resolve => setTimeout(resolve, 0)); + // sync up enzyme's representation with the real DOM + wrapper.update(); + + let welcomeScreenWrapper = wrapper.find(WelcomeScreen); + assert.strictEqual(welcomeScreenWrapper.prop("activeTheme"), "automatic"); + assert.strictEqual( + welcomeScreenWrapper.prop("initialTheme"), + "automatic" + ); + }); + + it("should handle primary Action", () => { + const screens = [ + { + content: { + title: "test title", + subtitle: "test subtitle", + primary_button: { + label: "Test button", + action: { + navigate: true, + }, + }, + }, + }, + ]; + + const PRIMARY_ACTION_PROPS = { + defaultScreens: screens, + metricsFlowUri: "http://localhost/", + message_id: "DEFAULT_ABOUTWELCOME", + utm_term: "default", + startScreen: 0, + }; + + const stub = sinon.stub(AboutWelcomeUtils, "sendActionTelemetry"); + let wrapper = mount(); + wrapper.update(); + + let welcomeScreenWrapper = wrapper.find(WelcomeScreen); + const btnPrimary = welcomeScreenWrapper.find(".primary"); + btnPrimary.simulate("click"); + assert.calledOnce(stub); + assert.equal( + stub.firstCall.args[0], + welcomeScreenWrapper.props().messageId + ); + assert.equal(stub.firstCall.args[1], "primary_button"); + stub.restore(); + }); + + it("should autoAdvance on last screen and send appropriate telemetry", () => { + let clock = sinon.useFakeTimers(); + const screens = [ + { + auto_advance: "primary_button", + content: { + title: "test title", + subtitle: "test subtitle", + primary_button: { + label: "Test Button", + action: { + navigate: true, + }, + }, + }, + }, + ]; + const AUTO_ADVANCE_PROPS = { + defaultScreens: screens, + metricsFlowUri: "http://localhost/", + message_id: "DEFAULT_ABOUTWELCOME", + utm_term: "default", + startScreen: 0, + }; + const wrapper = mount(); + wrapper.update(); + const finishStub = sandbox.stub(global, "AWFinish"); + const telemetryStub = sinon.stub( + AboutWelcomeUtils, + "sendActionTelemetry" + ); + + assert.notCalled(finishStub); + clock.tick(20001); + assert.calledOnce(finishStub); + assert.calledOnce(telemetryStub); + assert.equal(telemetryStub.lastCall.args[2], "AUTO_ADVANCE"); + clock.restore(); + finishStub.restore(); + telemetryStub.restore(); + }); + + it("should send telemetry ping on collectSelect", () => { + const screens = [ + { + id: "EASY_SETUP_TEST", + content: { + tiles: { + type: "multiselect", + data: [ + { + id: "checkbox-1", + defaultValue: true, + }, + ], + }, + primary_button: { + label: "Test Button", + action: { + collectSelect: true, + }, + }, + }, + }, + ]; + const EASY_SETUP_PROPS = { + defaultScreens: screens, + message_id: "DEFAULT_ABOUTWELCOME", + startScreen: 0, + }; + const stub = sinon.stub(AboutWelcomeUtils, "sendActionTelemetry"); + let wrapper = mount(); + wrapper.update(); + + let welcomeScreenWrapper = wrapper.find(WelcomeScreen); + const btnPrimary = welcomeScreenWrapper.find(".primary"); + btnPrimary.simulate("click"); + assert.calledTwice(stub); + assert.equal( + stub.firstCall.args[0], + welcomeScreenWrapper.props().messageId + ); + assert.equal(stub.firstCall.args[1], "primary_button"); + assert.equal( + stub.lastCall.args[0], + welcomeScreenWrapper.props().messageId + ); + assert.ok(stub.lastCall.args[1].includes("checkbox-1")); + assert.equal(stub.lastCall.args[2], "SELECT_CHECKBOX"); + stub.restore(); + }); + }); + + describe("WelcomeScreen component", () => { + describe("easy setup screen", () => { + const screen = AboutWelcomeDefaults.getDefaults().screens.find( + s => s.id === "AW_EASY_SETUP_NEEDS_DEFAULT_AND_PIN" + ); + let EASY_SETUP_SCREEN_PROPS; + + beforeEach(() => { + EASY_SETUP_SCREEN_PROPS = { + id: screen.id, + content: screen.content, + messageId: `${DEFAULT_PROPS.message_id}_${screen.id}`, + UTMTerm: DEFAULT_PROPS.utm_term, + flowParams: null, + totalNumberOfScreens: 1, + setScreenMultiSelects: sandbox.stub(), + setActiveMultiSelect: sandbox.stub(), + }; + }); + + it("should render Easy Setup screen", () => { + const wrapper = shallow(); + assert.ok(wrapper.exists()); + }); + + it("should render secondary.top button", () => { + let SCREEN_PROPS = { + content: { + title: "Step", + secondary_button_top: { + text: "test", + label: "test label", + }, + }, + position: "top", + }; + const wrapper = mount(); + assert.ok(wrapper.find("div.secondary-cta.top").exists()); + }); + + it("should render the arrow icon in the secondary button", () => { + let SCREEN_PROPS = { + content: { + title: "Step", + secondary_button: { + has_arrow_icon: true, + label: "test label", + }, + }, + }; + const wrapper = mount(); + assert.ok(wrapper.find("button.arrow-icon").exists()); + }); + + it("should render steps indicator", () => { + let PROPS = { totalNumberOfScreens: 1 }; + const wrapper = mount(); + assert.ok(wrapper.find("div.indicator").exists()); + }); + + it("should assign the total number of screens and current screen to the aria-valuemax and aria-valuenow labels", () => { + const EXTRA_PROPS = { totalNumberOfScreens: 3, order: 1 }; + const wrapper = mount( + + ); + + const steps = wrapper.find(`div.steps`); + assert.ok(steps.exists()); + const { attributes } = steps.getDOMNode(); + assert.equal( + parseInt(attributes.getNamedItem("aria-valuemax").value, 10), + EXTRA_PROPS.totalNumberOfScreens + ); + assert.equal( + parseInt(attributes.getNamedItem("aria-valuenow").value, 10), + EXTRA_PROPS.order + 1 + ); + }); + + it("should render progress bar", () => { + let SCREEN_PROPS = { + step: 1, + previousStep: 0, + totalNumberOfScreens: 2, + }; + const wrapper = mount(); + assert.ok(wrapper.find("div.indicator").exists()); + assert.propertyVal( + wrapper.find("div.indicator").prop("style"), + "--progress-bar-progress", + "50%" + ); + }); + + it("should have a primary, secondary and secondary.top button in the rendered input", () => { + const wrapper = mount(); + assert.ok(wrapper.find(".primary").exists()); + assert.ok( + wrapper + .find(".secondary-cta button.secondary[value='secondary_button']") + .exists() + ); + assert.ok( + wrapper + .find( + ".secondary-cta.top button.secondary[value='secondary_button_top']" + ) + .exists() + ); + }); + }); + + describe("theme screen", () => { + const THEME_SCREEN_PROPS = { + id: "test-theme-screen", + totalNumberOfScreens: 1, + content: { + title: "test title", + subtitle: "test subtitle", + tiles: { + type: "theme", + action: { + theme: "", + }, + data: [ + { + theme: "automatic", + label: "test-label", + tooltip: "test-tooltip", + description: "test-description", + }, + ], + }, + primary_button: { + action: {}, + label: "test button", + }, + }, + navigate: null, + messageId: `${DEFAULT_PROPS.message_id}_"test-theme-screen"`, + UTMTerm: DEFAULT_PROPS.utm_term, + flowParams: null, + activeTheme: "automatic", + }; + + it("should render WelcomeScreen", () => { + const wrapper = shallow(); + + assert.ok(wrapper.exists()); + }); + + it("should check this.props.activeTheme in the rendered input", () => { + const wrapper = shallow(); + + const selectedThemeInput = wrapper.find(".theme input[checked=true]"); + assert.strictEqual( + selectedThemeInput.prop("value"), + THEME_SCREEN_PROPS.activeTheme + ); + }); + }); + describe("import screen", () => { + const IMPORT_SCREEN_PROPS = { + content: { + title: "test title", + subtitle: "test subtitle", + }, + }; + it("should render ImportScreen", () => { + const wrapper = mount(); + assert.ok(wrapper.exists()); + }); + it("should not have a primary or secondary button", () => { + const wrapper = mount(); + assert.isFalse(wrapper.find(".primary").exists()); + assert.isFalse( + wrapper.find(".secondary button[value='secondary_button']").exists() + ); + assert.isFalse( + wrapper + .find(".secondary button[value='secondary_button_top']") + .exists() + ); + }); + }); + describe("#handleAction", () => { + let SCREEN_PROPS; + let TEST_ACTION; + beforeEach(() => { + SCREEN_PROPS = { + content: { + title: "test title", + subtitle: "test subtitle", + primary_button: { + action: {}, + label: "test button", + }, + }, + navigate: sandbox.stub(), + setActiveTheme: sandbox.stub(), + UTMTerm: "you_tee_emm", + }; + TEST_ACTION = SCREEN_PROPS.content.primary_button.action; + sandbox.stub(AboutWelcomeUtils, "handleUserAction").resolves(); + }); + it("should handle navigate", () => { + TEST_ACTION.navigate = true; + const wrapper = mount(); + + wrapper.find(".primary").simulate("click"); + + assert.calledOnce(SCREEN_PROPS.navigate); + }); + it("should handle theme", () => { + TEST_ACTION.theme = "test"; + const wrapper = mount(); + + wrapper.find(".primary").simulate("click"); + + assert.calledWith(SCREEN_PROPS.setActiveTheme, "test"); + }); + it("should handle dismiss", () => { + SCREEN_PROPS.content.dismiss_button = { + action: { dismiss: true }, + }; + const finishStub = sandbox.stub(global, "AWFinish"); + const wrapper = mount(); + + wrapper.find(".dismiss-button").simulate("click"); + + assert.calledOnce(finishStub); + }); + it("should handle SHOW_FIREFOX_ACCOUNTS", () => { + TEST_ACTION.type = "SHOW_FIREFOX_ACCOUNTS"; + const wrapper = mount(); + + wrapper.find(".primary").simulate("click"); + + assert.calledWith(AboutWelcomeUtils.handleUserAction, { + data: { + extraParams: { + utm_campaign: "firstrun", + utm_medium: "referral", + utm_source: "activity-stream", + utm_term: "you_tee_emm-screen", + }, + }, + type: "SHOW_FIREFOX_ACCOUNTS", + }); + }); + it("should handle OPEN_URL", () => { + TEST_ACTION.type = "OPEN_URL"; + TEST_ACTION.data = { + args: "https://example.com?utm_campaign=test-campaign", + }; + TEST_ACTION.addFlowParams = true; + let flowBeginTime = Date.now(); + const wrapper = mount( + + ); + + wrapper.find(".primary").simulate("click"); + + let [handledAction] = AboutWelcomeUtils.handleUserAction.firstCall.args; + assert.equal(handledAction.type, "OPEN_URL"); + let { searchParams } = new URL(handledAction.data.args); + assert.equal(searchParams.get("utm_campaign"), "test-campaign"); + assert.equal(searchParams.get("utm_medium"), "referral"); + assert.equal(searchParams.get("utm_source"), "activity-stream"); + assert.equal(searchParams.get("utm_term"), "you_tee_emm-screen"); + assert.equal(searchParams.get("device_id"), "test-device-id"); + assert.equal(searchParams.get("flow_id"), "test-flow-id"); + assert.equal( + searchParams.get("flow_begin_time"), + flowBeginTime.toString() + ); + }); + it("should handle SHOW_MIGRATION_WIZARD", () => { + TEST_ACTION.type = "SHOW_MIGRATION_WIZARD"; + const wrapper = mount(); + + wrapper.find(".primary").simulate("click"); + + assert.calledWith(AboutWelcomeUtils.handleUserAction, { + type: "SHOW_MIGRATION_WIZARD", + }); + }); + it("should handle SHOW_MIGRATION_WIZARD INSIDE MULTI_ACTION", async () => { + const migrationCloseStub = sandbox.stub( + global, + "AWWaitForMigrationClose" + ); + const MULTI_ACTION_SCREEN_PROPS = { + content: { + title: "test title", + subtitle: "test subtitle", + primary_button: { + action: { + type: "MULTI_ACTION", + navigate: true, + data: { + actions: [ + { + type: "PIN_FIREFOX_TO_TASKBAR", + }, + { + type: "SET_DEFAULT_BROWSER", + }, + { + type: "SHOW_MIGRATION_WIZARD", + data: {}, + }, + ], + }, + }, + label: "test button", + }, + }, + navigate: sandbox.stub(), + }; + const wrapper = mount(); + + wrapper.find(".primary").simulate("click"); + assert.calledWith(AboutWelcomeUtils.handleUserAction, { + type: "MULTI_ACTION", + navigate: true, + data: { + actions: [ + { + type: "PIN_FIREFOX_TO_TASKBAR", + }, + { + type: "SET_DEFAULT_BROWSER", + }, + { + type: "SHOW_MIGRATION_WIZARD", + data: {}, + }, + ], + }, + }); + // handleUserAction returns a Promise, so let's let the microtask queue + // flush so that anything waiting for the handleUserAction Promise to + // resolve can run. + await new Promise(resolve => queueMicrotask(resolve)); + assert.calledOnce(migrationCloseStub); + }); + + it("should handle SHOW_MIGRATION_WIZARD INSIDE NESTED MULTI_ACTION", async () => { + const migrationCloseStub = sandbox.stub( + global, + "AWWaitForMigrationClose" + ); + const MULTI_ACTION_SCREEN_PROPS = { + content: { + title: "test title", + subtitle: "test subtitle", + primary_button: { + action: { + type: "MULTI_ACTION", + navigate: true, + data: { + actions: [ + { + type: "PIN_FIREFOX_TO_TASKBAR", + }, + { + type: "SET_DEFAULT_BROWSER", + }, + { + type: "MULTI_ACTION", + data: { + actions: [ + { + type: "SET_PREF", + }, + { + type: "SHOW_MIGRATION_WIZARD", + data: {}, + }, + ], + }, + }, + ], + }, + }, + label: "test button", + }, + }, + navigate: sandbox.stub(), + }; + const wrapper = mount(); + + wrapper.find(".primary").simulate("click"); + assert.calledWith(AboutWelcomeUtils.handleUserAction, { + type: "MULTI_ACTION", + navigate: true, + data: { + actions: [ + { + type: "PIN_FIREFOX_TO_TASKBAR", + }, + { + type: "SET_DEFAULT_BROWSER", + }, + { + type: "MULTI_ACTION", + data: { + actions: [ + { + type: "SET_PREF", + }, + { + type: "SHOW_MIGRATION_WIZARD", + data: {}, + }, + ], + }, + }, + ], + }, + }); + // handleUserAction returns a Promise, so let's let the microtask queue + // flush so that anything waiting for the handleUserAction Promise to + // resolve can run. + await new Promise(resolve => queueMicrotask(resolve)); + assert.calledOnce(migrationCloseStub); + }); + it("should unset prefs from unchecked checkboxes", () => { + const PREF_SCREEN_PROPS = { + content: { + title: "Checkboxes", + tiles: { + type: "multiselect", + data: [ + { + id: "checkbox-1", + label: "checkbox 1", + checkedAction: { + type: "SET_PREF", + data: { + pref: { + name: "pref-a", + value: true, + }, + }, + }, + uncheckedAction: { + type: "SET_PREF", + data: { + pref: { + name: "pref-a", + }, + }, + }, + }, + { + id: "checkbox-2", + label: "checkbox 2", + checkedAction: { + type: "MULTI_ACTION", + data: { + actions: [ + { + type: "SET_PREF", + data: { + pref: { + name: "pref-b", + value: "pref-b", + }, + }, + }, + { + type: "SET_PREF", + data: { + pref: { + name: "pref-c", + value: 3, + }, + }, + }, + ], + }, + }, + uncheckedAction: { + type: "SET_PREF", + data: { + pref: { name: "pref-b" }, + }, + }, + }, + ], + }, + primary_button: { + label: "Set Prefs", + action: { + type: "MULTI_ACTION", + collectSelect: true, + isDynamic: true, + navigate: true, + data: { + actions: [], + }, + }, + }, + }, + navigate: sandbox.stub(), + setScreenMultiSelects: sandbox.stub(), + setActiveMultiSelect: sandbox.stub(), + }; + + // No checkboxes checked. All prefs will be unset and pref-c will not be + // reset. + { + const wrapper = mount( + + ); + wrapper.find(".primary").simulate("click"); + assert.calledWith(AboutWelcomeUtils.handleUserAction, { + type: "MULTI_ACTION", + collectSelect: true, + isDynamic: true, + navigate: true, + data: { + actions: [ + { type: "SET_PREF", data: { pref: { name: "pref-a" } } }, + { type: "SET_PREF", data: { pref: { name: "pref-b" } } }, + ], + }, + }); + + AboutWelcomeUtils.handleUserAction.resetHistory(); + } + + // The first checkbox is checked. Only pref-a will be set and pref-c + // will not be reset. + { + const wrapper = mount( + + ); + wrapper.find(".primary").simulate("click"); + assert.calledWith(AboutWelcomeUtils.handleUserAction, { + type: "MULTI_ACTION", + collectSelect: true, + isDynamic: true, + navigate: true, + data: { + actions: [ + { + type: "SET_PREF", + data: { + pref: { + name: "pref-a", + value: true, + }, + }, + }, + { type: "SET_PREF", data: { pref: { name: "pref-b" } } }, + ], + }, + }); + + AboutWelcomeUtils.handleUserAction.resetHistory(); + } + + // The second checkbox is checked. Prefs pref-b and pref-c will be set. + { + const wrapper = mount( + + ); + wrapper.find(".primary").simulate("click"); + assert.calledWith(AboutWelcomeUtils.handleUserAction, { + type: "MULTI_ACTION", + collectSelect: true, + isDynamic: true, + navigate: true, + data: { + actions: [ + { type: "SET_PREF", data: { pref: { name: "pref-a" } } }, + { + type: "MULTI_ACTION", + data: { + actions: [ + { + type: "SET_PREF", + data: { pref: { name: "pref-b", value: "pref-b" } }, + }, + { + type: "SET_PREF", + data: { pref: { name: "pref-c", value: 3 } }, + }, + ], + }, + }, + ], + }, + }); + + AboutWelcomeUtils.handleUserAction.resetHistory(); + } + + // // Both checkboxes are checked. All prefs will be set. + { + const wrapper = mount( + + ); + wrapper.find(".primary").simulate("click"); + assert.calledWith(AboutWelcomeUtils.handleUserAction, { + type: "MULTI_ACTION", + collectSelect: true, + isDynamic: true, + navigate: true, + data: { + actions: [ + { + type: "SET_PREF", + data: { pref: { name: "pref-a", value: true } }, + }, + { + type: "MULTI_ACTION", + data: { + actions: [ + { + type: "SET_PREF", + data: { pref: { name: "pref-b", value: "pref-b" } }, + }, + { + type: "SET_PREF", + data: { pref: { name: "pref-c", value: 3 } }, + }, + ], + }, + }, + ], + }, + }); + + AboutWelcomeUtils.handleUserAction.resetHistory(); + } + }); + }); + }); +}); -- cgit v1.2.3