mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-11-07 03:38:51 +02:00
Change how microsurveys are structured. This ensures that screen readers perceive a single logical collection, which contains all the radio buttons and is labeled by the question, which is no longer defined by subtitle but by tiles.label. This also changes how survey randomization works. Instead of randomizing the entire set, we randomize specific items. Any adjacent items with randomize will be randomized in-place. So if there are 4 items with randomize, followed by 1 nonrandom item, the 4 will be randomized but the 5th will stay at the bottom. Finally, this patch saves the randomized order so that it persists between back and forward navigation on about:welcome. That should avoid some jank if we show surveys in about:welcome. Differential Revision: https://phabricator.services.mozilla.com/D202513
221 lines
7.5 KiB
JavaScript
221 lines
7.5 KiB
JavaScript
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(<MultiSelect {...MULTISELECT_SCREEN_PROPS} />);
|
|
|
|
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(
|
|
<MultiSelect screenMultiSelects={{ map }} {...MULTISELECT_SCREEN_PROPS} />
|
|
);
|
|
|
|
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(<MultiSelect {...MULTISELECT_SCREEN_PROPS} />);
|
|
|
|
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(<MultiSelect {...MULTISELECT_SCREEN_PROPS} />);
|
|
|
|
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(<MultiSelect {...MULTISELECT_SCREEN_PROPS} />);
|
|
|
|
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(<MultiSelect {...MULTISELECT_SCREEN_PROPS} />);
|
|
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(<MultiSelect {...MULTISELECT_SCREEN_PROPS} />);
|
|
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(<MultiSelect {...SCREEN_PROPS} />);
|
|
|
|
// 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");
|
|
});
|
|
});
|