import React from "react"; import { mount } from "enzyme"; import { MultiSelect } from "content-src/components/MultiSelect"; describe("MultiSelect component", () => { let sandbox; let MULTISELECT_SCREEN_PROPS; let setScreenMultiSelects; let setActiveMultiSelect; beforeEach(() => { sandbox = sinon.createSandbox(); setScreenMultiSelects = sandbox.stub(); setActiveMultiSelect = sandbox.stub(); MULTISELECT_SCREEN_PROPS = { id: "multiselect-screen", content: { position: "split", split_narrow_bkg_position: "-60px", image_alt_text: { string_id: "mr2022-onboarding-default-image-alt", }, background: "url('chrome://activity-stream/content/data/content/assets/mr-settodefault.svg') var(--mr-secondary-position) no-repeat var(--mr-screen-background-color)", progress_bar: true, logo: {}, title: "Test Title", tiles: { type: "multiselect", label: "Test Subtitle", data: [ { id: "checkbox-1", defaultValue: true, label: { string_id: "mr2022-onboarding-set-default-primary-button-label", }, action: { type: "SET_DEFAULT_BROWSER", }, }, { id: "checkbox-2", defaultValue: true, label: "Test Checkbox 2", action: { type: "SHOW_MIGRATION_WIZARD", data: {}, }, }, { id: "checkbox-3", defaultValue: false, label: "Test Checkbox 3", action: { type: "SHOW_MIGRATION_WIZARD", data: {}, }, }, ], }, primary_button: { label: "Save and Continue", action: { type: "MULTI_ACTION", collectSelect: true, navigate: true, data: { actions: [] }, }, }, secondary_button: { label: "Skip", action: { navigate: true, }, has_arrow_icon: true, }, }, setScreenMultiSelects, setActiveMultiSelect, }; }); afterEach(() => { sandbox.restore(); }); it("should call setScreenMultiSelects with all ids of checkboxes", () => { mount(); assert.calledOnce(setScreenMultiSelects); assert.calledWith(setScreenMultiSelects, [ "checkbox-1", "checkbox-2", "checkbox-3", ]); }); it("should not call setScreenMultiSelects if it's already set", () => { let map = sandbox .stub() .returns(MULTISELECT_SCREEN_PROPS.content.tiles.data); mount( ); assert.notCalled(setScreenMultiSelects); assert.calledOnce(map); assert.calledWith(map, sinon.match.func); }); it("should call setActiveMultiSelect with ids of checkboxes with defaultValue true", () => { const wrapper = mount(); wrapper.setProps({ activeMultiSelect: null }); assert.calledOnce(setActiveMultiSelect); assert.calledWith(setActiveMultiSelect, ["checkbox-1", "checkbox-2"]); }); it("should use activeMultiSelect ids to set checked state for respective checkbox", () => { const wrapper = mount(); wrapper.setProps({ activeMultiSelect: ["checkbox-1", "checkbox-2"] }); const checkBoxes = wrapper.find(".checkbox-container input"); assert.strictEqual(checkBoxes.length, 3); assert.strictEqual(checkBoxes.first().props().checked, true); assert.strictEqual(checkBoxes.at(1).props().checked, true); assert.strictEqual(checkBoxes.last().props().checked, false); }); it("cover the randomize property", async () => { MULTISELECT_SCREEN_PROPS.content.tiles.data.forEach( item => (item.randomize = true) ); const wrapper = mount(); const checkBoxes = wrapper.find(".checkbox-container input"); assert.strictEqual(checkBoxes.length, 3); // We don't want to actually test the randomization, just that it doesn't // throw. We _could_ render the component until we get a different order, // and that should work the vast majority of the time, but it's // theoretically possible that we get the same order over and over again // until we hit the 2 second timeout. That would be an extremely low failure // rate, but we already know Math.random() works, so we don't really need to // test it anyway. It's not worth the added risk of false failures. }); it("should filter out id when checkbox is unchecked", () => { const wrapper = mount(); wrapper.setProps({ activeMultiSelect: ["checkbox-1", "checkbox-2"] }); const ckbx1 = wrapper.find(".checkbox-container input").at(0); assert.strictEqual(ckbx1.prop("value"), "checkbox-1"); ckbx1.getDOMNode().checked = false; ckbx1.simulate("change"); assert.calledWith(setActiveMultiSelect, ["checkbox-2"]); }); it("should add id when checkbox is checked", () => { const wrapper = mount(); wrapper.setProps({ activeMultiSelect: ["checkbox-1", "checkbox-2"] }); const ckbx3 = wrapper.find(".checkbox-container input").at(2); assert.strictEqual(ckbx3.prop("value"), "checkbox-3"); ckbx3.getDOMNode().checked = true; ckbx3.simulate("change"); assert.calledWith(setActiveMultiSelect, [ "checkbox-1", "checkbox-2", "checkbox-3", ]); }); it("should render radios and checkboxes with correct styles", async () => { const SCREEN_PROPS = { ...MULTISELECT_SCREEN_PROPS }; SCREEN_PROPS.content.tiles.style = { flexDirection: "row", gap: "24px" }; SCREEN_PROPS.content.tiles.data = [ { id: "checkbox-1", defaultValue: true, label: { raw: "Test1" }, action: { type: "OPEN_PROTECTION_REPORT" }, style: { color: "red" }, icon: { style: { color: "blue" } }, }, { id: "radio-1", type: "radio", group: "radios", defaultValue: true, label: { raw: "Test3" }, action: { type: "OPEN_PROTECTION_REPORT" }, style: { color: "purple" }, icon: { style: { color: "yellow" } }, }, ]; const wrapper = mount(); // wait for effect hook await new Promise(resolve => queueMicrotask(resolve)); // activeMultiSelect was called on effect hook with default values assert.calledWith(setActiveMultiSelect, ["checkbox-1", "radio-1"]); const container = wrapper.find(".multi-select-container"); assert.strictEqual(container.prop("style").flexDirection, "row"); assert.strictEqual(container.prop("style").gap, "24px"); // checkboxes/radios are rendered with correct styles const checkBoxes = wrapper.find(".checkbox-container"); assert.strictEqual(checkBoxes.length, 2); assert.strictEqual(checkBoxes.first().prop("style").color, "red"); assert.strictEqual(checkBoxes.at(1).prop("style").color, "purple"); const checks = wrapper.find(".checkbox-container input"); assert.strictEqual(checks.length, 2); assert.strictEqual(checks.first().prop("style").color, "blue"); assert.strictEqual(checks.at(1).prop("style").color, "yellow"); }); });