import { GlobalOverrider } from "test/unit/utils";
import {
MultiStageAboutWelcome,
SecondaryCTA,
StepsIndicator,
ProgressBar,
WelcomeScreen,
} from "content-src/aboutwelcome/components/MultiStageAboutWelcome";
import { Themes } from "content-src/aboutwelcome/components/Themes";
import React from "react";
import { shallow, mount } from "enzyme";
import { AboutWelcomeDefaults } from "aboutwelcome/lib/AboutWelcomeDefaults.jsm";
import { AboutWelcomeUtils } from "content-src/lib/aboutwelcome-utils";
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("get started screen", () => {
const screen = AboutWelcomeDefaults.getDefaults().screens.find(
s => s.id === "AW_PIN_FIREFOX"
);
const GET_STARTED_SCREEN_PROPS = {
id: screen.id,
totalNumberOfScreens: 1,
content: screen.content,
messageId: `${DEFAULT_PROPS.message_id}_${screen.id}`,
UTMTerm: DEFAULT_PROPS.utm_term,
flowParams: null,
};
it("should render GetStarted 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",
help_text: {
text: "test help text",
position: "default",
},
},
};
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 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(),
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();
}
});
});
});
});