summaryrefslogtreecommitdiffstats
path: root/browser/components/newtab/test/unit/asrouter/templates/SubmitFormSnippet.test.jsx
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/newtab/test/unit/asrouter/templates/SubmitFormSnippet.test.jsx')
-rw-r--r--browser/components/newtab/test/unit/asrouter/templates/SubmitFormSnippet.test.jsx354
1 files changed, 354 insertions, 0 deletions
diff --git a/browser/components/newtab/test/unit/asrouter/templates/SubmitFormSnippet.test.jsx b/browser/components/newtab/test/unit/asrouter/templates/SubmitFormSnippet.test.jsx
new file mode 100644
index 0000000000..12e4f96863
--- /dev/null
+++ b/browser/components/newtab/test/unit/asrouter/templates/SubmitFormSnippet.test.jsx
@@ -0,0 +1,354 @@
+import { mount } from "enzyme";
+import React from "react";
+import { FluentBundle, FluentResource } from "@fluent/bundle";
+import { LocalizationProvider, ReactLocalization } from "@fluent/react";
+import { RichText } from "content-src/asrouter/components/RichText/RichText.jsx";
+import schema from "content-src/asrouter/templates/SubmitFormSnippet/SubmitFormSnippet.schema.json";
+import { SubmitFormSnippet } from "content-src/asrouter/templates/SubmitFormSnippet/SubmitFormSnippet.jsx";
+
+const DEFAULT_CONTENT = {
+ scene1_text: "foo",
+ scene2_text: "bar",
+ scene1_button_label: "Sign Up",
+ retry_button_label: "Try again",
+ form_action: "foo.com",
+ hidden_inputs: { foo: "foo" },
+ error_text: "error",
+ success_text: "success",
+};
+
+describe("SubmitFormSnippet", () => {
+ let sandbox;
+ let onBlockStub;
+
+ function mockL10nWrapper(content) {
+ const bundle = new FluentBundle("en-US");
+ for (const [id, value] of Object.entries(content)) {
+ if (typeof value === "string") {
+ bundle.addResource(new FluentResource(`${id} = ${value}`));
+ }
+ }
+ const l10n = new ReactLocalization([bundle]);
+ return {
+ wrappingComponent: LocalizationProvider,
+ wrappingComponentProps: { l10n },
+ };
+ }
+
+ /**
+ * mountAndCheckProps - Mounts a SubmitFormSnippet with DEFAULT_CONTENT extended with any props
+ * passed in the content param and validates props against the schema.
+ * @param {obj} content Object containing custom message content (e.g. {text, icon, title})
+ * @returns enzyme wrapper for SubmitFormSnippet
+ */
+ function mountAndCheckProps(content = {}) {
+ const props = {
+ content: Object.assign({}, DEFAULT_CONTENT, content),
+ onBlock: onBlockStub,
+ onDismiss: sandbox.stub(),
+ sendUserActionTelemetry: sandbox.stub(),
+ onAction: sandbox.stub(),
+ form_method: "POST",
+ };
+ assert.jsonSchema(props.content, schema);
+ return mount(
+ <SubmitFormSnippet {...props} />,
+ mockL10nWrapper(props.content)
+ );
+ }
+
+ beforeEach(() => {
+ sandbox = sinon.createSandbox();
+ onBlockStub = sandbox.stub();
+ });
+
+ afterEach(() => {
+ sandbox.restore();
+ });
+
+ it("should render .text", () => {
+ const wrapper = mountAndCheckProps({ scene1_text: "bar" });
+ assert.equal(wrapper.find(".body").text(), "bar");
+ });
+ it("should not render title element if no .title prop is supplied", () => {
+ const wrapper = mountAndCheckProps();
+ assert.lengthOf(wrapper.find(".title"), 0);
+ });
+ it("should render .title", () => {
+ const wrapper = mountAndCheckProps({ scene1_title: "Foo" });
+ assert.equal(wrapper.find(".title").text().trim(), "Foo");
+ });
+ it("should render light-theme .icon", () => {
+ const wrapper = mountAndCheckProps({
+ scene1_icon: "data:image/gif;base64,R0lGODl",
+ });
+ assert.equal(
+ wrapper.find(".icon-light-theme").prop("src"),
+ "data:image/gif;base64,R0lGODl"
+ );
+ });
+ it("should render dark-theme .icon", () => {
+ const wrapper = mountAndCheckProps({
+ scene1_icon_dark_theme: "data:image/gif;base64,R0lGODl",
+ });
+ assert.equal(
+ wrapper.find(".icon-dark-theme").prop("src"),
+ "data:image/gif;base64,R0lGODl"
+ );
+ });
+ it("should render .button_label and default className", () => {
+ const wrapper = mountAndCheckProps({ scene1_button_label: "Click here" });
+
+ const button = wrapper.find("button.ASRouterButton");
+ assert.equal(button.text(), "Click here");
+ assert.equal(button.prop("className"), "ASRouterButton secondary");
+ });
+
+ describe("#SignupView", () => {
+ let wrapper;
+ const fetchOk = { json: () => Promise.resolve({ status: "ok" }) };
+ const fetchFail = { json: () => Promise.resolve({ status: "fail" }) };
+
+ beforeEach(() => {
+ wrapper = mountAndCheckProps({
+ scene1_text: "bar",
+ scene2_email_placeholder_text: "Email",
+ scene2_text: "signup",
+ });
+ });
+
+ it("should set the input type if provided through props.inputType", () => {
+ wrapper.setProps({ inputType: "number" });
+ wrapper.setState({ expanded: true });
+ assert.equal(wrapper.find(".mainInput").instance().type, "number");
+ });
+
+ it("should validate via props.validateInput if provided", () => {
+ function validateInput(value, content) {
+ if (content.country === "CA" && value === "poutine") {
+ return "";
+ }
+ return "Must be poutine";
+ }
+ const setCustomValidity = sandbox.stub();
+ wrapper.setProps({
+ validateInput,
+ content: { ...DEFAULT_CONTENT, country: "CA" },
+ });
+ wrapper.setState({ expanded: true });
+ const input = wrapper.find(".mainInput");
+ input.instance().value = "poutine";
+ input.simulate("change", {
+ target: { value: "poutine", setCustomValidity },
+ });
+ assert.calledWith(setCustomValidity, "");
+
+ input.instance().value = "fried chicken";
+ input.simulate("change", {
+ target: { value: "fried chicken", setCustomValidity },
+ });
+ assert.calledWith(setCustomValidity, "Must be poutine");
+ });
+
+ it("should show the signup form if state.expanded is true", () => {
+ wrapper.setState({ expanded: true });
+
+ assert.isTrue(wrapper.find("form").exists());
+ });
+ it("should dismiss the snippet", () => {
+ wrapper.setState({ expanded: true });
+
+ wrapper.find(".ASRouterButton.secondary").simulate("click");
+
+ assert.calledOnce(wrapper.props().onDismiss);
+ });
+ it("should send a DISMISS event ping", () => {
+ wrapper.setState({ expanded: true });
+
+ wrapper.find(".ASRouterButton.secondary").simulate("click");
+
+ assert.equal(
+ wrapper.props().sendUserActionTelemetry.firstCall.args[0].event,
+ "DISMISS"
+ );
+ });
+ it("should render hidden inputs + email input", () => {
+ wrapper.setState({ expanded: true });
+
+ assert.lengthOf(wrapper.find("input[type='hidden']"), 1);
+ });
+ it("should open the SignupView when the action button is clicked", () => {
+ assert.isFalse(wrapper.find("form").exists());
+
+ wrapper.find(".ASRouterButton").simulate("click");
+
+ assert.isTrue(wrapper.state().expanded);
+ assert.isTrue(wrapper.find("form").exists());
+ });
+ it("should submit telemetry when the action button is clicked", () => {
+ assert.isFalse(wrapper.find("form").exists());
+
+ wrapper.find(".ASRouterButton").simulate("click");
+
+ assert.equal(
+ wrapper.props().sendUserActionTelemetry.firstCall.args[0].event_context,
+ "scene1-button-learn-more"
+ );
+ });
+ it("should submit form data when submitted", () => {
+ sandbox.stub(window, "fetch").resolves(fetchOk);
+ wrapper.setState({ expanded: true });
+
+ wrapper.find("form").simulate("submit");
+ assert.calledOnce(window.fetch);
+ });
+ it("should send user telemetry when submitted", () => {
+ wrapper.setState({ expanded: true });
+
+ wrapper.find("form").simulate("submit");
+
+ assert.equal(
+ wrapper.props().sendUserActionTelemetry.firstCall.args[0].event_context,
+ "conversion-subscribe-activation"
+ );
+ });
+ it("should set signupSuccess when submission status is ok", async () => {
+ sandbox.stub(window, "fetch").resolves(fetchOk);
+ wrapper.setState({ expanded: true });
+ await wrapper.instance().handleSubmit({ preventDefault: sandbox.stub() });
+
+ assert.equal(wrapper.state().signupSuccess, true);
+ assert.equal(wrapper.state().signupSubmitted, true);
+ assert.calledOnce(onBlockStub);
+ assert.calledWithExactly(onBlockStub, { preventDismiss: true });
+ });
+ it("should send user telemetry when submission status is ok", async () => {
+ sandbox.stub(window, "fetch").resolves(fetchOk);
+ wrapper.setState({ expanded: true });
+ await wrapper.instance().handleSubmit({ preventDefault: sandbox.stub() });
+
+ assert.equal(
+ wrapper.props().sendUserActionTelemetry.secondCall.args[0]
+ .event_context,
+ "subscribe-success"
+ );
+ });
+ it("should not block the snippet if submission failed", async () => {
+ sandbox.stub(window, "fetch").resolves(fetchFail);
+ wrapper.setState({ expanded: true });
+ await wrapper.instance().handleSubmit({ preventDefault: sandbox.stub() });
+
+ assert.equal(wrapper.state().signupSuccess, false);
+ assert.equal(wrapper.state().signupSubmitted, true);
+ assert.notCalled(onBlockStub);
+ });
+ it("should not block if do_not_autoblock is true", async () => {
+ sandbox.stub(window, "fetch").resolves(fetchOk);
+ wrapper = mountAndCheckProps({
+ scene1_text: "bar",
+ scene2_email_placeholder_text: "Email",
+ scene2_text: "signup",
+ do_not_autoblock: true,
+ });
+ wrapper.setState({ expanded: true });
+ await wrapper.instance().handleSubmit({ preventDefault: sandbox.stub() });
+
+ assert.equal(wrapper.state().signupSuccess, true);
+ assert.equal(wrapper.state().signupSubmitted, true);
+ assert.notCalled(onBlockStub);
+ });
+ it("should send user telemetry if submission failed", async () => {
+ sandbox.stub(window, "fetch").resolves(fetchFail);
+ wrapper.setState({ expanded: true });
+ await wrapper.instance().handleSubmit({ preventDefault: sandbox.stub() });
+
+ assert.equal(
+ wrapper.props().sendUserActionTelemetry.secondCall.args[0]
+ .event_context,
+ "subscribe-error"
+ );
+ });
+ it("should render the signup success message", () => {
+ wrapper.setProps({ content: { success_text: "success" } });
+ wrapper.setState({ signupSuccess: true, signupSubmitted: true });
+
+ assert.isTrue(wrapper.find(".submissionStatus").exists());
+ assert.propertyVal(
+ wrapper.find(RichText).props(),
+ "localization_id",
+ "success_text"
+ );
+ assert.propertyVal(
+ wrapper.find(RichText).props(),
+ "success_text",
+ "success"
+ );
+ assert.isFalse(wrapper.find(".ASRouterButton").exists());
+ });
+ it("should render the signup error message", () => {
+ wrapper.setProps({ content: { error_text: "trouble" } });
+ wrapper.setState({ signupSuccess: false, signupSubmitted: true });
+
+ assert.isTrue(wrapper.find(".submissionStatus").exists());
+ assert.propertyVal(
+ wrapper.find(RichText).props(),
+ "localization_id",
+ "error_text"
+ );
+ assert.propertyVal(
+ wrapper.find(RichText).props(),
+ "error_text",
+ "trouble"
+ );
+ assert.isTrue(wrapper.find(".ASRouterButton").exists());
+ });
+ it("should render the button to return to the signup form if there was an error", () => {
+ wrapper.setState({ signupSubmitted: true, signupSuccess: false });
+
+ const button = wrapper.find("button.ASRouterButton");
+ assert.equal(button.text(), "Try again");
+ wrapper.find(".ASRouterButton").simulate("click");
+
+ assert.equal(wrapper.state().signupSubmitted, false);
+ });
+ it("should not render the privacy notice checkbox if prop is missing", () => {
+ wrapper.setState({ expanded: true });
+
+ assert.isFalse(wrapper.find(".privacyNotice").exists());
+ });
+ it("should render the privacy notice checkbox if prop is provided", () => {
+ wrapper.setProps({
+ content: { ...DEFAULT_CONTENT, scene2_privacy_html: "privacy notice" },
+ });
+ wrapper.setState({ expanded: true });
+
+ assert.isTrue(wrapper.find(".privacyNotice").exists());
+ });
+ it("should not call fetch if form_method is GET", async () => {
+ sandbox.stub(window, "fetch").resolves(fetchOk);
+ wrapper.setProps({ form_method: "GET" });
+ wrapper.setState({ expanded: true });
+
+ await wrapper.instance().handleSubmit({ preventDefault: sandbox.stub() });
+
+ assert.notCalled(window.fetch);
+ });
+ it("should block the snippet when form_method is GET", () => {
+ wrapper.setProps({ form_method: "GET" });
+ wrapper.setState({ expanded: true });
+
+ wrapper.instance().handleSubmit({ preventDefault: sandbox.stub() });
+
+ assert.calledOnce(onBlockStub);
+ assert.calledWithExactly(onBlockStub, { preventDismiss: true });
+ });
+ it("should return to scene 2 alt when clicking the retry button", async () => {
+ wrapper.setState({ signupSubmitted: true });
+ wrapper.setProps({ expandedAlt: true });
+
+ wrapper.find(".ASRouterButton").simulate("click");
+
+ assert.isTrue(wrapper.find(".scene2Alt").exists());
+ });
+ });
+});