From 2905ef877827934365a1188116da134dd111fea9 Mon Sep 17 00:00:00 2001 From: nuria1110 Date: Mon, 16 Sep 2024 09:51:37 +0100 Subject: [PATCH] refactor(radio-button-mapper): convert enzyme tests to RTL --- .../radio-button-mapper.component.tsx | 1 - .../radio-button-mapper.spec.tsx | 320 ------------------ .../radio-button-mapper.test.tsx | 163 +++++++++ 3 files changed, 163 insertions(+), 321 deletions(-) delete mode 100644 src/__internal__/radio-button-mapper/radio-button-mapper.spec.tsx create mode 100644 src/__internal__/radio-button-mapper/radio-button-mapper.test.tsx diff --git a/src/__internal__/radio-button-mapper/radio-button-mapper.component.tsx b/src/__internal__/radio-button-mapper/radio-button-mapper.component.tsx index 33aac65a55..7306191342 100644 --- a/src/__internal__/radio-button-mapper/radio-button-mapper.component.tsx +++ b/src/__internal__/radio-button-mapper/radio-button-mapper.component.tsx @@ -57,7 +57,6 @@ const RadioButtonMapper = ({ const onChangeProp = useCallback( (event) => { onChange?.(event); - /* istanbul ignore else */ if (!isControlled) { setCheckedValue(event.target.value); } diff --git a/src/__internal__/radio-button-mapper/radio-button-mapper.spec.tsx b/src/__internal__/radio-button-mapper/radio-button-mapper.spec.tsx deleted file mode 100644 index eb2f707e66..0000000000 --- a/src/__internal__/radio-button-mapper/radio-button-mapper.spec.tsx +++ /dev/null @@ -1,320 +0,0 @@ -import React, { useState } from "react"; -import { ThemeProvider } from "styled-components"; -import { shallow, mount, ReactWrapper } from "enzyme"; - -import sageTheme from "../../style/themes/sage"; -import RadioButtonMapper, { - MappedChildProps, - RadioButtonMapperProps, -} from "./radio-button-mapper.component"; -import { RadioButton } from "../../components/radio-button"; -import Button from "../../components/button"; - -const buttonValues = ["test-1", "test-2"]; -const name = "test-group"; - -const Controller = ({ - groupProps, - secondRadioProps, -}: { - groupProps: Partial; - secondRadioProps: Partial; -}) => { - const [value, setValue] = useState(undefined); - return ( - <> - { - setValue(e.target.value); - }} - onChange={(e) => { - setValue(e.target.value); - }} - value={value} - {...groupProps} - > - - - - - - - - ); -}; - -function render(props: Partial, theme = sageTheme) { - const children = buttonValues.map((value, index) => ( - - )); - - return mount( - - - {children} - - - ); -} - -function renderControlled(groupProps = {}, secondRadioProps = {}) { - return mount(); -} - -function renderUncontrolled( - groupProps: Partial = {}, - secondRadioProps: Partial = {} -) { - return mount( - - - - - - ); -} - -function getRadioButtons(wrapper: ReactWrapper) { - return wrapper.find(RadioButton); -} - -function getButtons(wrapper: ReactWrapper) { - return wrapper.find(Button); -} - -describe("RadioButtonMapper", () => { - it("accepts empty children", () => { - expect(() => { - mount( - - {null} - {false} - {undefined} - - ); - }).not.toThrow(); - }); - - describe("child RadioButton prop / key mapping", () => { - const wrapper = render({}); - const buttons = getRadioButtons(wrapper); - const buttonArray = buttons.getElements(); - - describe.each(buttonArray)("buttons[%#]", (button) => { - const index = buttonArray.indexOf(button); - - describe("key / value (both derived from value prop)", () => { - const expectedValue = buttonValues[index]; - const expectedKey = `.$radio-key-${expectedValue}`; - - it(`sets the value to ${expectedValue}`, () => { - expect(button.props.value).toEqual(expectedValue); - }); - - it(`sets the key to ${expectedKey}`, () => { - expect(button.key).toEqual(expectedKey); - }); - }); - - describe("name", () => { - it("is set using the RadioButtonMapper name prop", () => { - const buttonWrapper = buttons.at(buttonArray.indexOf(button)); - const input = buttonWrapper.find("input").getDOMNode(); - - expect(input.getAttribute("name")).toEqual(name); - }); - }); - }); - - describe("selected button state", () => { - describe("initial", () => { - it("sets checked to false for both buttons", () => { - buttonArray.forEach((button) => { - expect(button.props.checked).toBe(false); - }); - }); - }); - - describe("defaultChecked", () => { - it("sets a child radio button to checked when the prop is set programatically", () => { - const radioGroup = shallow( - - - - ); - - const button = radioGroup.find(RadioButton); - expect(button.props().checked).toBe(true); - }); - }); - - describe.each(buttonArray)("when buttons[%#] has changed", (button) => { - const index = buttonArray.indexOf(button); - const otherIndex = index ? 0 : 1; - let buttonWrapper = buttons.at(index); - let otherButtonWrapper = buttons.at(otherIndex); - const inputWrapper = buttonWrapper.find("input"); - const target = inputWrapper.instance(); - - inputWrapper.simulate("change", { target }); - wrapper.update(); - - buttonWrapper = getRadioButtons(wrapper).at(index); - otherButtonWrapper = getRadioButtons(wrapper).at(otherIndex); - - it("sets checked === true when it is changed", () => { - expect(buttonWrapper.props().checked).toBe(true); - expect(otherButtonWrapper.props().checked).toBe(false); - }); - - it("sets checked === false when the other button is selected", () => { - const otherInputWrapper = otherButtonWrapper.find("input"); - const otherTarget = otherInputWrapper.instance(); - - otherInputWrapper.simulate("change", { target: otherTarget }); - wrapper.update(); - - buttonWrapper = getRadioButtons(wrapper).at(index); - otherButtonWrapper = getRadioButtons(wrapper).at(otherIndex); - - expect(buttonWrapper.props().checked).toBe(false); - expect(otherButtonWrapper.props().checked).toBe(true); - }); - }); - }); - }); - - describe("defaultChecked", () => { - it("sets a child radio button to checked when the prop is set programatically", () => { - const radioGroup = mount( - - - - - ); - - const button = getRadioButtons(radioGroup).at(0); - expect(button.prop("checked")).toBe(true); - }); - }); - - describe.each([ - ["controlled", renderControlled], - ["uncontrolled", renderUncontrolled], - ])("%s", (type, renderer) => { - it("none of the radio buttons are checked by default", () => { - const wrapper = renderer(); - const radio = getRadioButtons(wrapper); - expect(radio.at(0).prop("checked")).toBe(false); - expect(radio.at(1).prop("checked")).toBe(false); - expect(radio.at(2).prop("checked")).toBe(false); - }); - - describe("onBlur function", () => { - const onBlur = jest.fn(); - const wrapper = renderer({ onBlur }); - const radioButtons = getRadioButtons(wrapper); - - it.each(radioButtons.getElements())( - "is passed down to RadioButton[%#]", - (radioButton) => { - expect(radioButton.props.onBlur).toBe(onBlur); - } - ); - }); - }); - - describe("controlled", () => { - it("changing the value checks the appropraite radio button", () => { - const wrapper = renderControlled(); - const buttons = getButtons(wrapper); - - buttons.at(0).simulate("click"); - - const radio = getRadioButtons(wrapper); - expect(radio.at(0).prop("checked")).toBe(true); - expect(radio.at(1).prop("checked")).toBe(false); - expect(radio.at(2).prop("checked")).toBe(false); - }); - }); - - describe("uncontrolled", () => { - it("clicking a value checks the appropraite radio button", () => { - const wrapper = renderUncontrolled(); - let radio = getRadioButtons(wrapper); - - radio.at(0).find("input").simulate("change"); - - radio = getRadioButtons(wrapper); - expect(radio.at(0).prop("checked")).toBe(true); - expect(radio.at(1).prop("checked")).toBe(false); - expect(radio.at(2).prop("checked")).toBe(false); - }); - - describe("when onChange not passed in", () => { - it("clicking a value checks the appropraite radio button", () => { - const wrapper = mount( - - - - - - ); - let radio = getRadioButtons(wrapper); - - radio.at(0).find("input").simulate("change"); - - radio = getRadioButtons(wrapper); - expect(radio.at(0).prop("checked")).toBe(true); - expect(radio.at(1).prop("checked")).toBe(false); - expect(radio.at(2).prop("checked")).toBe(false); - }); - }); - - describe("when children are passed in an array", () => { - it("should render the list correctly", () => { - const radioGroup = mount( - - {[ - , - null, - undefined, - "foo", - , - ]} - - ); - - expect(radioGroup.find("input").at(0).props().checked).toBe(true); - expect(radioGroup.find("input").at(1).props().checked).toBe(false); - }); - }); - }); -}); diff --git a/src/__internal__/radio-button-mapper/radio-button-mapper.test.tsx b/src/__internal__/radio-button-mapper/radio-button-mapper.test.tsx new file mode 100644 index 0000000000..efeb086c04 --- /dev/null +++ b/src/__internal__/radio-button-mapper/radio-button-mapper.test.tsx @@ -0,0 +1,163 @@ +import React, { useState } from "react"; +import { render, screen } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import RadioButtonMapper from "."; +import RadioButton from "../../components/radio-button"; + +const ControlledRadioButtons = () => { + const [value, setValue] = useState(undefined); + + return ( + { + setValue(e.target.value); + }} + onChange={(e) => { + setValue(e.target.value); + }} + > + + + + ); +}; + +test("renders RadioButton children with expected `name` prop", () => { + render( + + + + + ); + + const radioButtons = screen.getAllByRole("radio"); + + expect(radioButtons[0]).toHaveAttribute("name", "test"); + expect(radioButtons[1]).toHaveAttribute("name", "test"); +}); + +test("checks and unchecks correct RadioButtons when inputs are controlled", async () => { + const user = userEvent.setup(); + render(); + + const radio1 = screen.getByRole("radio", { name: "One" }); + const radio2 = screen.getByRole("radio", { name: "Two" }); + + expect(radio1).not.toBeChecked(); + expect(radio2).not.toBeChecked(); + + await user.click(radio1); + + expect(radio1).toBeChecked(); + expect(radio2).not.toBeChecked(); + + await user.click(radio2); + + expect(radio1).not.toBeChecked(); + expect(radio2).toBeChecked(); +}); + +test("checks and unchecks correct RadioButtons when inputs are uncontrolled", async () => { + const user = userEvent.setup(); + render( + + + + + ); + + const radio1 = screen.getByRole("radio", { name: "One" }); + const radio2 = screen.getByRole("radio", { name: "Two" }); + + expect(radio1).not.toBeChecked(); + expect(radio2).not.toBeChecked(); + + await user.click(radio1); + + expect(radio1).toBeChecked(); + expect(radio2).not.toBeChecked(); + + await user.click(radio2); + + expect(radio1).not.toBeChecked(); + expect(radio2).toBeChecked(); +}); + +test("checks and unchecks correct RadioButtons when `defaultChecked` is set", async () => { + const user = userEvent.setup(); + render( + + + + + ); + + const radio1 = screen.getByRole("radio", { name: "One" }); + const radio2 = screen.getByRole("radio", { name: "Two" }); + + expect(radio1).toBeChecked(); + expect(radio2).not.toBeChecked(); + + await user.click(radio2); + + expect(radio1).not.toBeChecked(); + expect(radio2).toBeChecked(); +}); + +test("calls `onChange` callback when a RadioButton child is clicked", async () => { + const user = userEvent.setup(); + const onChange = jest.fn(); + render( + + + + + ); + + const radio1 = screen.getByRole("radio", { name: "One" }); + await user.click(radio1); + + expect(onChange).toHaveBeenCalledTimes(1); + expect(onChange).toHaveBeenCalledWith( + expect.objectContaining({ target: radio1 }) + ); +}); + +test("calls `onBlur` callback when a RadioButton child is blurred", async () => { + const user = userEvent.setup(); + const onBlur = jest.fn(); + render( + + + + + ); + + const radio1 = screen.getByRole("radio", { name: "One" }); + const radio2 = screen.getByRole("radio", { name: "Two" }); + await user.click(radio1); + await user.click(radio2); + + expect(onBlur).toHaveBeenCalledTimes(1); + expect(onBlur).toHaveBeenCalledWith( + expect.objectContaining({ target: radio1 }) + ); +}); + +test("accepts non-RadioButton children", () => { + expect(() => { + render( + + {[ + , + "foo", + undefined, + null, + false, + ]} + + ); + }).not.toThrow(); +});