From 8236a5e2d2e2c049e1a40095073298e310f9fd3d Mon Sep 17 00:00:00 2001 From: Kyrylo Hudym-Levkovych Date: Wed, 6 Sep 2023 10:45:33 +0300 Subject: [PATCH] refactor: rewrite test using react testing library 2part --- src/Form/tests/FormCheckbox.test.jsx | 172 +++-- src/Form/tests/FormCheckboxSet.test.jsx | 165 ++--- src/Form/tests/FormControl.test.jsx | 18 +- .../tests/FormControlDecoratorGroup.test.jsx | 50 +- src/Form/tests/FormControlFeedback.test.jsx | 35 +- src/Form/tests/FormGroup.test.jsx | 92 ++- src/Form/tests/FormRadioSet.test.jsx | 171 +++-- src/Form/tests/FormSwitch.test.jsx | 36 +- src/Form/tests/FormText.test.jsx | 30 +- src/Form/tests/fieldUtils.test.jsx | 105 +-- src/Form/tests/useCheckboxSetValues.test.jsx | 72 ++- src/Menu/Menu.test.jsx | 51 +- src/Menu/SelectMenu.test.jsx | 181 ++++-- src/Modal/Modal.test.jsx | 260 ++++---- src/Modal/ModalLayer.test.jsx | 75 +-- src/Modal/ModalPopup.test.jsx | 117 ++-- src/Modal/index.jsx | 13 +- src/Pagination/Pagination.test.jsx | 236 +++---- .../RadioButtonGroup.test.jsx | 194 ++---- src/SearchField/SearchField.test.jsx | 194 +++--- src/SearchField/SearchFieldAdvanced.jsx | 2 + .../__snapshots__/SearchField.test.jsx.snap | 61 +- src/StatusAlert/StatusAlert.test.jsx | 63 +- src/Table/Table.test.jsx | 292 ++++----- src/Tabs/index.jsx | 1 + src/asInput/asInput.test.jsx | 597 +++++++++--------- src/asInput/index.jsx | 2 +- src/hooks/tests/useToggle.test.jsx | 100 +-- 28 files changed, 1777 insertions(+), 1608 deletions(-) diff --git a/src/Form/tests/FormCheckbox.test.jsx b/src/Form/tests/FormCheckbox.test.jsx index 2ca19049802..8951c9a04ca 100644 --- a/src/Form/tests/FormCheckbox.test.jsx +++ b/src/Form/tests/FormCheckbox.test.jsx @@ -1,5 +1,7 @@ import React from 'react'; -import { mount } from 'enzyme'; +import { render, fireEvent, waitFor } from '@testing-library/react'; +import '@testing-library/jest-dom/extend-expect'; + import FormCheckbox from '../FormCheckbox'; import FormGroup from '../FormGroup'; import FormLabel from '../FormLabel'; @@ -8,51 +10,92 @@ describe('FormCheckbox', () => { const handleChange = jest.fn(); const handleFocus = jest.fn(); const handleBlur = jest.fn(); - const wrapper = mount(( - - Green - - )); - const inputNode = wrapper.find('input[value="green"]').first(); it('renders an input with a name and value', () => { - wrapper.exists('input[value="green"]'); - expect(inputNode.props().name).toBe('color'); - }); + const { getByLabelText } = render( + + Green + , + ); + + const inputNode = getByLabelText('Green'); - it('has an associated label', () => { - const inputNodeId = inputNode.props().id; - wrapper.exists({ htmlFor: inputNodeId }); - const labelNode = wrapper.find({ htmlFor: inputNodeId }).hostNodes().first(); - expect(labelNode.text()).toBe('Green'); + expect(inputNode).toBeInTheDocument(); + expect(inputNode.getAttribute('name')).toBe('color'); }); - it('has an associated description', () => { - const describerIds = inputNode.props()['aria-describedby']; - const describerNode = wrapper.find({ id: describerIds }).hostNodes().first(); - wrapper.exists({ id: describerIds }); - expect(describerNode.text()).toBe('Describe green'); + it('has an associated label and description', () => { + const { getByLabelText, getByText } = render( + + Green + , + ); + + const inputNode = getByLabelText('Green'); + const describerNode = getByText('Describe green'); + + expect(inputNode).toBeInTheDocument(); + expect(describerNode).toBeInTheDocument(); }); it('calls the change handler', () => { - inputNode.simulate('change'); - expect(handleChange).toHaveBeenCalledWith( - expect.objectContaining({ - target: expect.objectContaining({ value: 'green' }), - type: 'change', - }), + const { getByLabelText } = render( + + Green + , ); + + const inputNode = getByLabelText('Green'); + fireEvent.change(inputNode, { target: { value: 'green' } }); + + waitFor(() => { + expect(handleChange).toHaveBeenCalledWith( + expect.objectContaining({ + target: expect.objectContaining({ value: 'green' }), + type: 'change', + }), + ); + }); }); it('calls the focus handler', () => { - inputNode.simulate('focus'); + const { getByLabelText } = render( + + Green + , + ); + + const inputNode = getByLabelText('Green'); + fireEvent.focus(inputNode); + expect(handleFocus).toHaveBeenCalledWith( expect.objectContaining({ target: expect.objectContaining({ value: 'green' }), @@ -62,7 +105,22 @@ describe('FormCheckbox', () => { }); it('calls the blur handler', () => { - inputNode.simulate('blur'); + const { getByLabelText } = render( + + Green + , + ); + + const inputNode = getByLabelText('Green'); + fireEvent.blur(inputNode); + expect(handleBlur).toHaveBeenCalledWith( expect.objectContaining({ target: expect.objectContaining({ value: 'green' }), @@ -76,29 +134,25 @@ describe('FormCheckbox with FormGroup', () => { const handleChange = jest.fn(); const handleFocus = jest.fn(); const handleBlur = jest.fn(); - const wrapper = mount(( - - Group Label - - Green - - - )); - - it('renders an a group with a label', () => { - expect(wrapper.exists('#group-id')).toBe(true); - const groupNode = wrapper.find('#group-id').first(); - const labelledById = groupNode.props()['aria-labelledby']; - expect(labelledById).toBeTruthy(); - expect(wrapper.exists(`#${labelledById}`)).toBe(true); - const labelNode = wrapper.find(`#${labelledById}`).first(); - expect(labelNode.text()).toBe('Group Label'); + + it('renders a group with a label', () => { + const { getByText } = render( + + Group Label + + Green + + , + ); + + const groupNode = getByText('Group Label'); + expect(groupNode).toBeInTheDocument(); }); }); diff --git a/src/Form/tests/FormCheckboxSet.test.jsx b/src/Form/tests/FormCheckboxSet.test.jsx index 6194ccf2056..b4666b9782d 100644 --- a/src/Form/tests/FormCheckboxSet.test.jsx +++ b/src/Form/tests/FormCheckboxSet.test.jsx @@ -1,60 +1,63 @@ import React from 'react'; -import { mount } from 'enzyme'; +import { render, fireEvent } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import '@testing-library/jest-dom/extend-expect'; + import FormGroup from '../FormGroup'; import FormCheckboxSet from '../FormCheckboxSet'; import FormCheckbox from '../FormCheckbox'; import FormLabel from '../FormLabel'; +function renderFormCheckbox() { + const handleChange = jest.fn(); + const value = ['green']; + return render( + + Which color? + + Red + Green + Blue + Cyan + + , + ); +} + describe('FormCheckboxSet', () => { describe('associate element ids and attributes', () => { - const handleChange = jest.fn(); - const value = ['green']; - const wrapper = mount(( - - Which color? - - Red - Green - Blue - Cyan - - - )); - it('has a group div with the proper id', () => { - expect(wrapper.exists('div[role="group"]')).toBe(true); - const groupNode = wrapper.find('div[role="group"]').first(); - expect(groupNode.props().id).toEqual('my-field'); + const wrapper = renderFormCheckbox(); + const groupNode = wrapper.container.querySelector('div[role="group"]'); + expect(groupNode).toBeInTheDocument(); + expect(groupNode.getAttribute('id')).toEqual('my-field'); }); - it('has an element labelling the group', () => { - expect(wrapper.exists('FormLabel')).toBe(true); - const labelNode = wrapper.find('FormLabel').first().childAt(0); - const labelNodeId = labelNode.props().id; - expect(labelNode.props().id).toBeTruthy(); - const groupNode = wrapper.find('div[role="group"]').first(); - expect(groupNode.props()['aria-labelledby']).toContain(labelNodeId); + it('has an element labeling the group', () => { + const wrapper = renderFormCheckbox(); + const labelNode = wrapper.getByLabelText('Which color?'); + expect(labelNode).toBeInTheDocument(); + const groupNode = wrapper.container.querySelector('div[role="group"]'); + expect(groupNode.getAttribute('aria-labelledby')).toContain(labelNode.getAttribute('id')); }); it('has a description for a checkbox value', () => { - expect(wrapper.exists({ children: 'Blue description' })).toBe(true); - const checkboxNode = wrapper.find('input[value="blue"]').first(); - const describerIds = checkboxNode.props()['aria-describedby']; - expect(describerIds).toBeTruthy(); - expect(wrapper.exists(`#${describerIds}`)).toBe(true); - const descriptionNode = wrapper.find(`#${describerIds}`).first(); - const descriptionNodeContent = descriptionNode.props().children; - expect(descriptionNodeContent).toBe('Blue description'); + const wrapper = renderFormCheckbox(); + const describerIds = wrapper.getByDisplayValue('blue'); + expect(describerIds).toBeInTheDocument(); + expect(describerIds.getAttribute('aria-describedby')).toBeTruthy(); + const descriptionNode = wrapper.getByText('Blue description'); + expect(descriptionNode.textContent).toBe('Blue description'); }); }); - describe('controlled behavior', () => { - const setValue = jest.fn(); - const wrapper = mount(( + const setValue = jest.fn(); + function renderFormWithoutLabel() { + return render( { red green blue - - )); + , + ); + } + describe('controlled behavior', () => { it('checks the right checkbox button', () => { - const checkboxNode = wrapper.find('input[value="red"]').first(); - expect(checkboxNode.props().checked).toBe(true); + const wrapper = renderFormWithoutLabel(); + const checkboxNode = wrapper.getByDisplayValue('red'); + expect(checkboxNode.checked).toBe(true); }); - it('calls the change handlers with the right value', () => { - const checkboxNode = wrapper.find('input[value="green"]').first(); - const eventData = { target: { value: 'green' } }; - checkboxNode.simulate('change', eventData); + it('calls the change handlers with the right value', async () => { + const wrapper = renderFormWithoutLabel(); + const checkboxNode = wrapper.getByLabelText('green'); + userEvent.type(checkboxNode, { target: { value: 'green' } }); expect(setValue).toHaveBeenCalledWith('green'); }); }); - describe('event handlers', () => { - const onChange = jest.fn(); - const onBlur = jest.fn(); - const onFocus = jest.fn(); - const wrapper = mount(( + const onChange = jest.fn(); + const onBlur = jest.fn(); + const onFocus = jest.fn(); + + function renderFormWithHandlers() { + return render( { red green blue - - )); - - const checkboxNode = wrapper.find('input[value="green"]').first(); + , + ); + } + describe('event handlers', () => { it('calls the change handlers with the right value', () => { - checkboxNode.simulate('change'); + const { getByLabelText } = renderFormWithHandlers(); + const checkboxNode = getByLabelText('green'); + userEvent.type(checkboxNode, { target: { value: 'green' } }); expect(onChange).toHaveBeenCalledWith( expect.objectContaining({ target: expect.objectContaining({ value: 'green' }), @@ -110,8 +119,11 @@ describe('FormCheckboxSet', () => { }), ); }); + it('calls the focus handler', () => { - checkboxNode.simulate('focus'); + const { getByLabelText } = renderFormWithHandlers(); + const checkboxNode = getByLabelText('green'); + fireEvent.focus(checkboxNode); expect(onFocus).toHaveBeenCalledWith( expect.objectContaining({ target: expect.objectContaining({ value: 'green' }), @@ -119,8 +131,11 @@ describe('FormCheckboxSet', () => { }), ); }); + it('calls the blur handler', () => { - checkboxNode.simulate('blur'); + const { getByLabelText } = renderFormWithHandlers(); + const checkboxNode = getByLabelText('green'); + fireEvent.blur(checkboxNode); expect(onBlur).toHaveBeenCalledWith( expect.objectContaining({ target: expect.objectContaining({ value: 'green' }), @@ -131,22 +146,22 @@ describe('FormCheckboxSet', () => { }); describe('uncontrolled behavior', () => { - const wrapper = mount(( - - red - green - blue - - )); - it('checks the right checkbox button', () => { - const checkboxNode = wrapper.find('input[value="red"]').first(); - expect(checkboxNode.props().defaultChecked).toBe(true); + const { getByLabelText } = render( + + red + green + blue + , + ); + + const checkboxNode = getByLabelText('red'); + expect(checkboxNode.defaultChecked).toBe(true); }); }); }); diff --git a/src/Form/tests/FormControl.test.jsx b/src/Form/tests/FormControl.test.jsx index 9702464f7e7..6fb49ee7f75 100644 --- a/src/Form/tests/FormControl.test.jsx +++ b/src/Form/tests/FormControl.test.jsx @@ -1,5 +1,5 @@ import React from 'react'; -import { mount } from 'enzyme'; +import { fireEvent, render } from '@testing-library/react'; import FormControl from '../FormControl'; @@ -11,16 +11,22 @@ describe('FormControl', () => { it('textarea changes its height with autoResize prop', () => { const useReferenceSpy = jest.spyOn(React, 'useRef').mockReturnValue(ref); const onChangeFunc = jest.fn(); - const wrapper = mount(( - - )); + const { getByTestId } = render( + , + ); + ref.scrollHeight = 180; ref.offsetHeight = 90; ref.clientHeight = 88; + + const textarea = getByTestId('textarea-id'); + expect(useReferenceSpy).toHaveBeenCalledTimes(1); expect(ref.current.style.height).toBe('0px'); - wrapper.find('textarea').simulate('change'); + + fireEvent.change(textarea, { target: { value: 'new text' } }); + expect(onChangeFunc).toHaveBeenCalledTimes(1); - expect(ref.current.style.height).toEqual(`${ref.current.scrollHeight + ref.current.offsets}px`); + expect(ref.current.style.height).toEqual(`${ref.current.scrollHeight + ref.current.offsetHeight}px`); }); }); diff --git a/src/Form/tests/FormControlDecoratorGroup.test.jsx b/src/Form/tests/FormControlDecoratorGroup.test.jsx index e3c1be37063..1474bf036e9 100644 --- a/src/Form/tests/FormControlDecoratorGroup.test.jsx +++ b/src/Form/tests/FormControlDecoratorGroup.test.jsx @@ -1,55 +1,61 @@ import React from 'react'; -import { mount } from 'enzyme'; +import { render } from '@testing-library/react'; +import '@testing-library/jest-dom/extend-expect'; import { FormGroupContext } from '../FormGroupContext'; import FormControlDecoratorGroup from '../FormControlDecoratorGroup'; import { FORM_CONTROL_SIZES } from '../constants'; -describe('FormFieldDecoratorGroup', () => { +describe('FormControlDecoratorGroup', () => { it('renders', () => { - const wrapper = mount(( + const { getByText } = render( Form control - - )); - expect(wrapper.exists('[children="before"]')).toBe(true); - expect(wrapper.exists('[children="after"]')).toBe(true); - expect(wrapper.exists('[children="label"]')).toBe(true); + , + ); + expect(getByText('before')).toBeInTheDocument(); + expect(getByText('after')).toBeInTheDocument(); + expect(getByText('label')).toBeInTheDocument(); }); + it('renders a size reflecting a context', () => { - const wrapper = mount(( + const { getByTestId } = render( Form control - - )); - const groupNode = wrapper.find(FormControlDecoratorGroup).first().childAt(0); - expect(groupNode.props().className).toContain('-lg'); + , + ); + const groupNode = getByTestId('decoration-id'); + expect(groupNode).toBeInTheDocument(); + expect(groupNode.classList).toContain('pgn__form-control-decorator-group-lg'); }); + it('renders nodes in leading, trailing, and floatLabel elements', () => { - const beforeNode = before; - const afterNode = after; - const labelNode = label; - const wrapper = mount(( + const beforeNode = before; + const afterNode = after; + const labelNode = label; + const { getByTestId } = render( Form control - - )); - expect(wrapper.exists('#before-node')).toBe(true); - expect(wrapper.exists('#after-node')).toBe(true); - expect(wrapper.exists('#label-node')).toBe(true); + , + ); + expect(getByTestId('before-node')).toBeInTheDocument(); + expect(getByTestId('after-node')).toBeInTheDocument(); + expect(getByTestId('label-node')).toBeInTheDocument(); }); }); diff --git a/src/Form/tests/FormControlFeedback.test.jsx b/src/Form/tests/FormControlFeedback.test.jsx index 8ef5d8d564e..07f1bb1607c 100644 --- a/src/Form/tests/FormControlFeedback.test.jsx +++ b/src/Form/tests/FormControlFeedback.test.jsx @@ -1,8 +1,9 @@ import React from 'react'; -import { mount } from 'enzyme'; +import { render } from '@testing-library/react'; +import '@testing-library/jest-dom/extend-expect'; import FormControlFeedback from '../FormControlFeedback'; -import { FORM_TEXT_TYPES, FORM_TEXT_ICONS } from '../FormText'; +import { FORM_TEXT_TYPES } from '../FormText'; import { FormGroupContext } from '../FormGroupContext'; describe('FormControlFeedback', () => { @@ -11,35 +12,35 @@ describe('FormControlFeedback', () => { const contextValue = { getDescriptorProps, }; - const wrapper = mount(( + const { getByText } = render( This is feedback - - )); - expect(wrapper.exists('[children="This is feedback"]')).toBe(true); - const FeedbackNode = wrapper.find(FormControlFeedback).first().childAt(0); + , + ); + const FeedbackNode = getByText('This is feedback'); + expect(FeedbackNode).toBeInTheDocument(); expect(getDescriptorProps).toHaveBeenCalled(); - expect(FeedbackNode.props().id).toContain('descriptor-id'); + expect(FeedbackNode.parentElement).toHaveAttribute('id', 'descriptor-id'); }); it('renders with a default icon for a variant', () => { - const wrapper = mount(( - + const { getByTestId } = render( + This is feedback - - )); - expect(wrapper.exists(FORM_TEXT_ICONS[FORM_TEXT_TYPES.VALID])).toBe(true); + , + ); + expect(getByTestId(FORM_TEXT_TYPES.VALID)).toBeInTheDocument(); }); it('renders with a custom icon', () => { const customIcon = !; - const wrapper = mount(( + const { getByText } = render( This is feedback - - )); - expect(wrapper.exists('custom-icon')).toBe(true); + , + ); + expect(getByText('!')).toBeInTheDocument(); }); }); diff --git a/src/Form/tests/FormGroup.test.jsx b/src/Form/tests/FormGroup.test.jsx index 65af6906773..232b1d00d24 100644 --- a/src/Form/tests/FormGroup.test.jsx +++ b/src/Form/tests/FormGroup.test.jsx @@ -1,5 +1,6 @@ import React from 'react'; -import { mount } from 'enzyme'; +import { render } from '@testing-library/react'; +import '@testing-library/jest-dom/extend-expect'; import FormGroup from '../FormGroup'; import FormControl from '../FormControl'; @@ -20,56 +21,77 @@ jest.mock('react-bootstrap/FormControl', () => { describe('FormGroup', () => { describe('associate element ids and attributes', () => { - const wrapper = mount(( - - My Field - - Default help text - Second help text - - )); - it('has a form control with the proper id', () => { - expect(wrapper.exists('form-control')).toBe(true); - const formControlNode = wrapper.find('form-control').first(); - expect(formControlNode.props().id).toEqual('my-field'); + const { getByTestId } = render( + + My Field + + Default help text + Second help text + , + ); + const formControlNode = getByTestId('form-control'); + expect(formControlNode).toBeInTheDocument(); + expect(formControlNode).toHaveAttribute('id', 'my-field'); }); it('has a label with the proper htmlFor value', () => { - expect(wrapper.exists('label')).toBe(true); - const labelNode = wrapper.find('label').first(); - expect(labelNode.props().htmlFor).toEqual('my-field'); + const { getByText } = render( + + My Field + + Default help text + Second help text + , + ); + const labelNode = getByText('My Field'); + expect(labelNode).toBeInTheDocument(); + expect(labelNode).toHaveAttribute('for', 'my-field'); }); it('has default description text with a generated id that appears on the form-control', () => { - const selector = '[children="Default help text"]'; - const node = wrapper.find(selector).first().childAt(0); - const { id } = node.props(); - const formControlNode = wrapper.find('form-control').first(); - expect(wrapper.exists(selector)).toBe(true); + const { getByText, getByTestId } = render( + + My Field + + Default help text + Second help text + , + ); + const defaultHelpTextNode = getByText('Default help text').parentElement; + const formControlNode = getByTestId('form-control'); + expect(defaultHelpTextNode).toBeInTheDocument(); + const id = defaultHelpTextNode.getAttribute('id'); expect(id).toBeTruthy(); - expect(formControlNode.props()['aria-describedby']).toContain(id); + expect(formControlNode).toHaveAttribute('aria-describedby', expect.stringContaining(id)); }); it('has another description text with a generated id that appears on the form-control', () => { - const selector = '[children="Second help text"]'; - const node = wrapper.find(selector).first().childAt(0); - const { id } = node.props(); - const formControlNode = wrapper.find('form-control').first(); - expect(wrapper.exists(selector)).toBe(true); + const { getByText, getByTestId } = render( + + My Field + + Default help text + Second help text + , + ); + const secondHelpTextNode = getByText('Second help text').parentElement; + const formControlNode = getByTestId('form-control'); + expect(secondHelpTextNode).toBeInTheDocument(); + const id = secondHelpTextNode.getAttribute('id'); expect(id).toBeTruthy(); - expect(formControlNode.props()['aria-describedby']).toContain(id); + expect(formControlNode).toHaveAttribute('aria-describedby', expect.stringContaining(id)); }); }); it('renders a form control with a generated id', () => { - const wrapper = mount(( + const { getByTestId } = render( - - - )); - expect(wrapper.exists('form-control')).toBe(true); - const formControlNode = wrapper.find('form-control').first(); - expect(formControlNode.props().id).toBeTruthy(); + + , + ); + const formControlNode = getByTestId('form-control'); + expect(formControlNode).toBeInTheDocument(); + expect(formControlNode).toHaveAttribute('id', expect.any(String)); }); }); diff --git a/src/Form/tests/FormRadioSet.test.jsx b/src/Form/tests/FormRadioSet.test.jsx index fce088541b4..8d4c3972964 100644 --- a/src/Form/tests/FormRadioSet.test.jsx +++ b/src/Form/tests/FormRadioSet.test.jsx @@ -1,5 +1,7 @@ import React from 'react'; -import { mount } from 'enzyme'; +import { render, fireEvent } from '@testing-library/react'; +import '@testing-library/jest-dom/extend-expect'; + import FormGroup from '../FormGroup'; import FormRadioSet from '../FormRadioSet'; import FormRadio from '../FormRadio'; @@ -7,99 +9,130 @@ import FormLabel from '../FormLabel'; describe('FormRadioSet', () => { describe('associate element ids and attributes', () => { - const handleChange = jest.fn(); - const value = 'green'; - const wrapper = mount(( - - Which color? - - Red - Green - Blue - Cyan - - - )); - it('has a radiogroup div with the proper id', () => { - expect(wrapper.exists('div[role="radiogroup"]')).toBe(true); - const radioGroupNode = wrapper.find('div[role="radiogroup"]').first(); - expect(radioGroupNode.props().id).toEqual('my-field'); + const handleChange = jest.fn(); + const value = 'green'; + const { getByRole } = render( + + Which color? + + Red + Green + Blue + Cyan + + , + ); + const radioGroupNode = getByRole('radiogroup', { name: /Which color\?/i }); + expect(radioGroupNode).toBeInTheDocument(); + expect(radioGroupNode).toHaveAttribute('id', 'my-field'); }); it('has an element labelling the radiogroup', () => { - expect(wrapper.exists('FormLabel')).toBe(true); - const labelNode = wrapper.find('FormLabel').first().childAt(0); - const labelNodeId = labelNode.props().id; - expect(labelNode.props().id).toBeTruthy(); - const radioGroupNode = wrapper.find('div[role="radiogroup"]').first(); - expect(radioGroupNode.props()['aria-labelledby']).toContain(labelNodeId); + const handleChange = jest.fn(); + const value = 'green'; + const { getByText, getByRole } = render( + + Which color? + + Red + Green + Blue + Cyan + + , + ); + const labelNode = getByText('Which color?'); + const radioGroupNode = getByRole('radiogroup', { name: /Which color\?/i }); + const labelNodeId = labelNode.getAttribute('id'); + expect(labelNode).toBeInTheDocument(); + expect(labelNodeId).toBeTruthy(); + expect(radioGroupNode).toHaveAttribute('aria-labelledby', expect.stringContaining(labelNodeId)); }); }); describe('controlled behavior', () => { - const setValue = jest.fn(); - const wrapper = mount(( - { - setValue(e.target.value); - }} - > - red - green - blue - - )); - it('checks the right radio button', () => { - const radioNode = wrapper.find('input[value="red"]').first(); - expect(radioNode.props().checked).toBe(true); + const setValue = jest.fn(); + const { getByLabelText } = render( + { + setValue(e.target.value); + }} + > + red + green + blue + , + ); + const redRadio = getByLabelText('red'); + expect(redRadio).toBeChecked(); }); it('calls the change handlers with the right value', () => { - const radioNode = wrapper.find('input[value="green"]').first(); - const eventData = { target: { value: 'green' } }; - radioNode.simulate('change', eventData); + const setValue = jest.fn(); + const { getByLabelText } = render( + { + setValue(e.target.value); + }} + > + red + green + blue + , + ); + const greenRadio = getByLabelText('green'); + fireEvent.click(greenRadio); expect(setValue).toHaveBeenCalledWith('green'); }); }); describe('uncontrolled behavior', () => { - const wrapper = mount(( - - red - green - blue - - )); - it('checks the right radio button', () => { - const radioNode = wrapper.find('input[value="red"]').first(); - expect(radioNode.props().defaultChecked).toBe(true); + const { getByLabelText } = render( + + red + green + blue + , + ); + const redRadio = getByLabelText('red'); + expect(redRadio).toBeChecked(); }); }); it('renders radio controls without a context', () => { - const wrapper = mount(( + const { getByLabelText } = render( <> Evergreen Deciduous - - )); + , + ); + + const evergreenRadio = getByLabelText('Evergreen'); + const deciduousRadio = getByLabelText('Deciduous'); - expect(wrapper.exists('input[type="radio"]')).toBe(true); - const radioNode = wrapper.find('input[type="radio"]').first(); - expect(radioNode.props().name).toBe('trees'); + expect(evergreenRadio).toBeInTheDocument(); + expect(deciduousRadio).toBeInTheDocument(); + expect(evergreenRadio).toHaveAttribute('name', 'trees'); + expect(deciduousRadio).toHaveAttribute('name', 'trees'); }); }); diff --git a/src/Form/tests/FormSwitch.test.jsx b/src/Form/tests/FormSwitch.test.jsx index 163b4c60bc8..f9632f7552d 100644 --- a/src/Form/tests/FormSwitch.test.jsx +++ b/src/Form/tests/FormSwitch.test.jsx @@ -1,25 +1,25 @@ import React from 'react'; -import { mount } from 'enzyme'; -import FormSwitch from '../FormSwitch'; +import { render } from '@testing-library/react'; +import '@testing-library/jest-dom/extend-expect'; -// A minimal test here since Switch depends on Checkbox +import FormSwitch from '../FormSwitch'; describe('FormSwitch', () => { - const wrapper = mount(( - - Green - - )); - const inputNode = wrapper.find('input[value="green"]').first(); - it('renders an input with a name and value and role=switch', () => { - expect(wrapper.exists('input[value="green"]')).toBe(true); - expect(wrapper.exists('.pgn__form-switch-helper-text')).toBe(true); - expect(inputNode.props().name).toBe('color'); - expect(inputNode.props().role).toBe('switch'); + const { getByRole, getByText } = render( + + Green + , + ); + const switchInput = getByRole('switch'); + expect(switchInput).toBeInTheDocument(); + expect(switchInput).toHaveAttribute('name', 'color'); + + const helperText = getByText('Describe green'); + expect(helperText).toBeInTheDocument(); }); }); diff --git a/src/Form/tests/FormText.test.jsx b/src/Form/tests/FormText.test.jsx index f30a84d821a..d9d9b6121ba 100644 --- a/src/Form/tests/FormText.test.jsx +++ b/src/Form/tests/FormText.test.jsx @@ -1,26 +1,29 @@ import React from 'react'; -import { mount } from 'enzyme'; +import { render } from '@testing-library/react'; +import '@testing-library/jest-dom/extend-expect'; -import FormText, { resolveTextType, FORM_TEXT_TYPES, FORM_TEXT_ICONS } from '../FormText'; +import FormText, { resolveTextType, FORM_TEXT_TYPES } from '../FormText'; describe('FormText', () => { it('renders with a default icon for a variant', () => { - const wrapper = mount(( - + const { getByTestId } = render( + This is feedback - - )); - expect(wrapper.exists(FORM_TEXT_ICONS[FORM_TEXT_TYPES.VALID])).toBe(true); + , + ); + const icon = getByTestId(`${FORM_TEXT_TYPES.VALID}`); + expect(icon).toBeInTheDocument(); }); it('renders with a custom icon', () => { const customIcon = !; - const wrapper = mount(( - + const { getByTestId } = render( + This is feedback - - )); - expect(wrapper.exists('custom-icon')).toBe(true); + , + ); + const icon = getByTestId('form-text-custom-icon'); + expect(icon).toBeInTheDocument(); }); }); @@ -29,14 +32,17 @@ describe('resolveTextType', () => { const result = resolveTextType({ isValid: true }); expect(result).toBe(FORM_TEXT_TYPES.VALID); }); + it('resolves to INVALID when is invalid is set', () => { const result = resolveTextType({ isInvalid: true }); expect(result).toBe(FORM_TEXT_TYPES.INVALID); }); + it('resolves to VALID when is valid and invalid are set', () => { const result = resolveTextType({ isValid: true, isInvalid: true }); expect(result).toBe(FORM_TEXT_TYPES.VALID); }); + it('resolves to DEFAULT when is valid and invalid are false', () => { const result = resolveTextType({ isValid: false, isInvalid: false }); expect(result).toBe(FORM_TEXT_TYPES.DEFAULT); diff --git a/src/Form/tests/fieldUtils.test.jsx b/src/Form/tests/fieldUtils.test.jsx index 02a39666112..5b4caca9918 100644 --- a/src/Form/tests/fieldUtils.test.jsx +++ b/src/Form/tests/fieldUtils.test.jsx @@ -1,5 +1,6 @@ import React from 'react'; -import { mount } from 'enzyme'; +import { render, fireEvent, waitFor } from '@testing-library/react'; +import '@testing-library/jest-dom/extend-expect'; import { callAllHandlers, @@ -54,39 +55,52 @@ function IdListExample({ prefix, initialList }) { const id2 = useRegisteredId('explicit-id'); return (
- {ids.join(' ')} - {id1} - {id2} + {ids.join(' ')} + {id1} + {id2}
); } + describe('useIdList', () => { describe('with default', () => { - const wrapper = mount(); - const idList = wrapper.find('#id-list').first(); - const renderedIds = idList.text().split(' '); it('starts with the default id', () => { + const { getByTestId } = render( + , + ); + const idList = getByTestId('id-list'); + const renderedIds = idList.textContent.split(' '); expect(renderedIds[0]).toBe('id-0'); }); it('generates a registered id', () => { - expect(renderedIds[1]).toBe('prefix-1'); + const { getByTestId } = render( + , + ); + const idList = getByTestId('id-list'); + const renderedIds = idList.textContent.split(' '); + expect(renderedIds[1]).toBe('prefix-2'); }); it('registers an explicit id', () => { + const { getByTestId } = render( + , + ); + const idList = getByTestId('id-list'); + const renderedIds = idList.textContent.split(' '); expect(renderedIds[2]).toBe('explicit-id'); }); }); describe('with no default', () => { - const wrapper = mount(); - const idList = wrapper.find('#id-list').first(); - const renderedIds = idList.text().split(' '); - it('only have the two ids', () => { + it('only has the two ids', () => { + const { getByTestId } = render(); + const idList = getByTestId('id-list'); + const renderedIds = idList.textContent.split(' '); expect(renderedIds.length).toBe(2); }); }); }); describe('mergeAttributeValues', () => { - it('joins not empty values with in a space delimited list', () => { + it('joins not empty values with a space-delimited list', () => { const output = mergeAttributeValues('one', 'two', undefined, null, 'four'); expect(output).toBe('one two four'); }); @@ -101,46 +115,59 @@ function HasValueExample({ defaultValue, value }) { const [hasValue, handleInputEvent] = useHasValue({ defaultValue, value }); return (
- {hasValue &&
Has value
} - + {hasValue &&
Has value
} +
); } describe('useHasValue', () => { describe('uncontrolled input with no default', () => { - const wrapper = mount(); - const input = wrapper.find('input').at(0); it('starts with the default id', () => { - expect(wrapper.exists('#has-value')).toBe(false); + const { queryByText } = render(); + expect(queryByText('Has value')).toBe(null); }); - it('has value when a targets blur event has a value', () => { - input.invoke('onBlur')({ target: { value: 'hello' } }); - expect(wrapper.exists('#has-value')).toBe(true); + it('has value when a target blur event has a value', async () => { + const { getByTestId } = render(); + const input = getByTestId('input'); + fireEvent.blur(input, { target: { value: 'hello' } }); + await waitFor(() => { + expect(getByTestId('has-value')).toBeInTheDocument(); + }); }); }); +}); - describe('uncontrolled input with a default value', () => { - const wrapper = mount(); - const input = wrapper.find('input').at(0); - it('starts with the default id', () => { - expect(wrapper.exists('#has-value')).toBe(true); - }); - it('has no value when a targets blur event has no value', () => { - input.invoke('onBlur')({ target: { value: '' } }); - expect(wrapper.exists('#has-value')).toBe(false); +describe('uncontrolled input with a default value', () => { + it('starts with the default id', () => { + const { getByTestId } = render( + , + ); + expect(getByTestId('has-value')).toBeInTheDocument(); + }); + it('has no value when a target blur event has no value', async () => { + const { getByTestId, queryByTestId } = render( + , + ); + const input = getByTestId('input'); + fireEvent.blur(input, { target: { value: '' } }); + await waitFor(() => { + expect(queryByTestId('has-value')).toBe(null); }); }); +}); - describe('controlled value', () => { - const wrapper = mount(); - const input = wrapper.find('input').at(0); - it('starts with the default id', () => { - expect(wrapper.exists('#has-value')).toBe(true); - }); - it('continues to have a value despite a blur event saying there is not one but props say there is', () => { - input.invoke('onBlur')({ target: { value: '' } }); - expect(wrapper.exists('#has-value')).toBe(true); +describe('controlled value', () => { + it('starts with the default id', () => { + const { getByTestId } = render(); + expect(getByTestId('has-value')).toBeInTheDocument(); + }); + it('continues to have a value despite a blur event saying there is not one but props say there is', async () => { + const { getByTestId } = render(); + const input = getByTestId('input'); + fireEvent.blur(input, { target: { value: '' } }); + await waitFor(() => { + expect(getByTestId('has-value')).toBeInTheDocument(); }); }); }); diff --git a/src/Form/tests/useCheckboxSetValues.test.jsx b/src/Form/tests/useCheckboxSetValues.test.jsx index 25eb41de227..442d07cff37 100644 --- a/src/Form/tests/useCheckboxSetValues.test.jsx +++ b/src/Form/tests/useCheckboxSetValues.test.jsx @@ -1,6 +1,6 @@ /* eslint-disable react/button-has-type */ import React from 'react'; -import { mount } from 'enzyme'; +import { render, fireEvent } from '@testing-library/react'; import useCheckboxSetValues from '../useCheckboxSetValues'; function Example() { @@ -9,57 +9,59 @@ function Example() { }] = useCheckboxSetValues(['cheddar']); return ( <> - {values.join(' ')} - - - - + {values.join(' ')} + + + + ); } describe('useCheckboxSetValues', () => { - const wrapper = mount(); - - const getValues = () => { - const valueStr = wrapper.find('#values').first().text(); - if (valueStr === '') { - return []; - } - return valueStr.split(' '); - }; - - const addButton = wrapper.find('#add').first(); - const removeButton = wrapper.find('#remove').first(); - const setButton = wrapper.find('#set').first(); - const clearButton = wrapper.find('#clear').first(); - it('has a default value', () => { - const values = getValues(); - expect(values).toEqual(['cheddar']); + const { getByTestId } = render(); + const values = getByTestId('values'); + expect(values.textContent).toBe('cheddar'); }); it('can append a value', () => { - addButton.simulate('click'); - const values = getValues(); - expect(values).toEqual(['cheddar', 'provolone']); + const { getByTestId } = render(); + const addButton = getByTestId('add'); + const values = getByTestId('values'); + + fireEvent.click(addButton); + + expect(values.textContent).toBe('cheddar provolone'); }); it('can remove a value', () => { - removeButton.simulate('click'); - const values = getValues(); - expect(values).toEqual(['cheddar']); + const { getByTestId } = render(); + const removeButton = getByTestId('remove'); + const values = getByTestId('values'); + + fireEvent.click(removeButton); + + expect(values.textContent).toBe('cheddar'); }); it('can replace all values', () => { - setButton.simulate('click'); - const values = getValues(); - expect(values).toEqual(['cheddar', 'swiss', 'provolone']); + const { getByTestId } = render(); + const setButton = getByTestId('set'); + const values = getByTestId('values'); + + fireEvent.click(setButton); + + expect(values.textContent).toBe('cheddar swiss provolone'); }); it('can clear all values', () => { - clearButton.simulate('click'); - const values = getValues(); - expect(values).toEqual([]); + const { getByTestId } = render(); + const clearButton = getByTestId('clear'); + const values = getByTestId('values'); + + fireEvent.click(clearButton); + + expect(values.textContent).toBe(''); }); }); diff --git a/src/Menu/Menu.test.jsx b/src/Menu/Menu.test.jsx index b7eb5d2d0d5..bf2096c15fc 100644 --- a/src/Menu/Menu.test.jsx +++ b/src/Menu/Menu.test.jsx @@ -2,6 +2,7 @@ import React from 'react'; import { render, fireEvent, screen } from '@testing-library/react'; import '@testing-library/jest-dom/extend-expect'; import renderer from 'react-test-renderer'; +import userEvent from '@testing-library/user-event'; import { Add, Check } from '../../icons'; import { MenuItem } from '..'; @@ -49,7 +50,7 @@ describe('Keyboard Interactions', () => { beforeEach(() => { render( - Default + Default Cant Touch This Icon Before , @@ -57,53 +58,55 @@ describe('Keyboard Interactions', () => { }); it('should focus on the first item after click', () => { - const defaultItem = screen.getByText('Default'); + const defaultItem = screen.getByText('Default').parentElement; fireEvent.click(defaultItem); - expect(defaultItem === document.activeElement); + userEvent.tab(); + expect(defaultItem).toHaveFocus(); }); it('should focus the next item after ArrowDown keyDown', () => { const defaultItem = screen.getByText('Default'); - const cantTouchThisItem = screen.getByText('Cant Touch This'); + const cantTouchThisItem = screen.getByText('Cant Touch This').parentElement; - fireEvent.keyDown(defaultItem, { key: 'ArrowDown' }); + userEvent.type(defaultItem, '{arrowdown}'); - expect(cantTouchThisItem === document.activeElement); + expect(cantTouchThisItem).toHaveFocus(); }); it('should focus the next item after Tab keyDown', () => { - const defaultItem = screen.getByText('Default'); - const cantTouchThisItem = screen.getByText('Cant Touch This'); - - fireEvent.keyDown(defaultItem, { key: 'Tab' }); + const defaultItem = screen.getByText('Default').parentElement; + const cantTouchThisItem = screen.getByText('Cant Touch This').parentElement; + defaultItem.focus(); + userEvent.tab(); - expect(cantTouchThisItem === document.activeElement); + expect(cantTouchThisItem).toHaveFocus(); }); it('should loop focus to the first item after Tab keyDown on last item', () => { - const defaultItem = screen.getByText('Default'); + const defaultItem = screen.getByText('Default').parentElement; const iconBeforeItem = screen.getByText('Icon Before'); + iconBeforeItem.focus(); + userEvent.tab(); - fireEvent.keyDown(iconBeforeItem, { key: 'Tab' }); - - expect(defaultItem === document.activeElement); + expect(defaultItem).toHaveFocus(); }); it('should loop focus to the last item after ArrowUp keyDown on first item', () => { - const defaultItem = screen.getByText('Default'); - const iconBeforeItem = screen.getByText('Icon Before'); + const defaultItem = screen.getByText('Default').parentElement; + const iconBeforeItem = screen.getByText('Icon Before').parentElement; + defaultItem.focus(); + userEvent.type(defaultItem, '{arrowup}'); - fireEvent.keyDown(defaultItem, { key: 'ArrowUp' }); - - expect(iconBeforeItem === document.activeElement); + expect(iconBeforeItem).toHaveFocus(); }); it('should focus the previous item after Shift + Tab keyDown', () => { - const cantTouchThisItem = screen.getByText('Cant Touch This'); - const iconBeforeItem = screen.getByText('Icon Before'); + const button1 = screen.getAllByRole('button')[0]; + const button2 = screen.getAllByRole('button')[1]; - fireEvent.keyDown(iconBeforeItem, { key: 'Tab', shiftKey: true }); + button2.focus(); + userEvent.tab({ shift: true }); - expect(cantTouchThisItem === document.activeElement); + expect(button1).toHaveFocus(); }); }); diff --git a/src/Menu/SelectMenu.test.jsx b/src/Menu/SelectMenu.test.jsx index be22f4edcd8..28db6b6745e 100644 --- a/src/Menu/SelectMenu.test.jsx +++ b/src/Menu/SelectMenu.test.jsx @@ -1,15 +1,22 @@ import React from 'react'; -import { mount } from 'enzyme'; +import { render, fireEvent, screen } from '@testing-library/react'; import renderer from 'react-test-renderer'; +import '@testing-library/jest-dom/extend-expect'; +import userEvent from '@testing-library/user-event'; + import { Add, Check } from '../../icons'; import { MenuItem, SelectMenu } from '..'; import Hyperlink from '../Hyperlink'; -import Button from '../Button'; const app = document.createElement('div'); document.body.appendChild(app); -const selectMenu = mount( - ( + +function DefaultSelectMenu(props) { + return A Menu Item; +} + +function defaultSelectMenu() { + return ( A Menu Item A Menu Item With an Icon Before @@ -25,17 +32,8 @@ const selectMenu = mount( Kainian M. Hortens - ), { attachTo: app }, -); - -function DefaultSelectMenu(props) { - return A Menu Item; + ); } -const menuTrigger = selectMenu.find(Button); - -const menuOpen = (isOpen, wrapper) => { - expect(wrapper.find(Button).prop('aria-expanded')).toEqual(isOpen); -}; describe('correct rendering', () => { it('renders as expected', () => { @@ -44,73 +42,134 @@ describe('correct rendering', () => { )).toJSON(); expect(tree).toMatchSnapshot(); }); + it('renders with a default message you set', () => { - const wrapper = mount(); - expect(wrapper.find(Button).text()).toEqual('Pick Me'); + render(DefaultSelectMenu({ defaultMessage: 'Pick Me' })); + expect(screen.getByText('Pick Me')).toBeInTheDocument(); }); + it('renders with a button as link', () => { - const wrapper = mount(); - expect(wrapper.find(Button).prop('variant')).toEqual('link'); + render(DefaultSelectMenu({ isLink: true })); + const button = screen.getByRole('button'); + expect(button).toHaveClass('btn-link'); }); - it('rendering with Brand button variant', () => { - const wrapper = mount(); - expect(wrapper.find(Button).prop('variant')).toEqual('brand'); - expect(wrapper.find('button').hasClass('btn-brand')).toEqual(true); + + it('renders with Brand button variant', () => { + render(DefaultSelectMenu({ variant: 'brand' })); + const button = screen.getByRole('button'); + expect(button).toHaveClass('btn-brand'); }); }); describe('mouse behavior & keyboard behavior', () => { - menuTrigger.simulate('click'); - const menuItems = selectMenu.find('.pgn__menu-item'); - it('opens on trigger click', () => { - menuTrigger.simulate('click'); // Open - menuOpen(true, selectMenu); + const { getByRole } = render(defaultSelectMenu()); + const menuTrigger = getByRole('button', { expanded: false }); + userEvent.click(menuTrigger); + expect(menuTrigger).toHaveAttribute('aria-expanded', 'true'); }); it('should focus on the first item after opening', () => { - expect(menuItems.first().is(':focus')).toBe(true); + const { getByRole, getAllByRole } = render(defaultSelectMenu()); + const menuTrigger = getByRole('button', { expanded: false }); + userEvent.click(menuTrigger); + expect(menuTrigger).toHaveAttribute('aria-expanded', 'true'); + const menuItem = getAllByRole('link')[0]; + userEvent.tab(); + expect(menuItem).toHaveFocus(); }); + it('returns focus to trigger on close', () => { - menuItems.at(7).simulate('click'); - expect(menuTrigger === document.activeElement); + const { getByRole, getAllByRole } = render(defaultSelectMenu()); + const menuTrigger = getByRole('button', { expanded: false }); + userEvent.click(menuTrigger); + const menuItems = getAllByRole('link'); + fireEvent.click(menuItems[7]); + expect(menuTrigger).toHaveFocus(); }); }); describe('keyboard Interactions', () => { - menuTrigger.simulate('click'); // Open - const menuItems = selectMenu.find('.pgn__menu-item'); - const menuContainer = selectMenu.find('.pgn__menu'); - it('should focus on the first item after opening', () => { - expect(menuItems.at(0) === document.activeElement); + const { getByRole, getAllByRole } = render(defaultSelectMenu()); + const menuTrigger = getByRole('button', { expanded: false }); + userEvent.click(menuTrigger); + const menuItems = getAllByRole('link'); + userEvent.tab(); + expect(menuItems[0]).toHaveFocus(); }); + it('should focus the next item after ArrowDown keyDown', () => { - menuContainer.simulate('keyDown', { key: 'ArrowDown' }); - expect(menuItems.at(1) === document.activeElement); - }); - it('should focus the next item after ArrowDown right', () => { - menuContainer.simulate('keyDown', { key: 'ArrowRight' }); - expect(menuItems.at(2) === document.activeElement); - }); - it('should focus the previous item after ArrowDown up', () => { - menuContainer.simulate('keyDown', { key: 'ArrowUp' }); - expect(menuItems.at(1) === document.activeElement); - }); - it('should focus the previous item after ArrowDown left', () => { - menuContainer.simulate('keyDown', { key: 'ArrowLeft' }); - expect(menuItems.at(0) === document.activeElement); - }); - it('edge behavior should loop', () => { - menuContainer.simulate('keyDown', { key: 'ArrowUp' }); - expect(menuItems.at(menuItems.length - 1) === document.activeElement); - menuContainer.simulate('keyDown', { key: 'ArrowDown' }); - expect(menuItems.at(0) === document.activeElement); - }); - it('home should go to first, End to last', () => { - menuContainer.simulate('keyDown', { key: 'End' }); - expect(menuItems.at(menuItems.length - 1) === document.activeElement); - menuContainer.simulate('keyDown', { key: 'Home' }); - expect(menuItems.at(0) === document.activeElement); + const { getByRole, getAllByRole } = render(defaultSelectMenu()); + const menuTrigger = getByRole('button', { expanded: false }); + userEvent.click(menuTrigger); + const menuItems = getAllByRole('link'); + userEvent.keyboard('[arrowdown]'); + expect(menuItems[1]).toHaveFocus(); + }); + + it('should focus the next item after ArrowRight keyDown', () => { + const { getByRole, getAllByRole } = render(defaultSelectMenu()); + const menuTrigger = getByRole('button', { expanded: false }); + userEvent.click(menuTrigger); + const menuItems = getAllByRole('link'); + userEvent.keyboard('[arrowright]'); + expect(menuItems[1]).toHaveFocus(); + }); + + it('should focus the previous item after ArrowUp keyDown', () => { + const { getByRole, getAllByRole } = render(defaultSelectMenu()); + const menuTrigger = getByRole('button', { expanded: false }); + userEvent.click(menuTrigger); + const menuItems = getAllByRole('link'); + menuItems[1].focus(); + userEvent.keyboard('[arrowup]'); + expect(menuItems[0]).toHaveFocus(); + }); + + it('should focus the previous item after ArrowLeft keyDown', () => { + const { getByRole, getAllByRole } = render(defaultSelectMenu()); + const menuTrigger = getByRole('button', { expanded: false }); + userEvent.click(menuTrigger); + const menuItems = getAllByRole('link'); + menuItems[1].focus(); + userEvent.keyboard('[arrowleft]'); + expect(menuItems[0]).toHaveFocus(); + }); + + it('edge behavior should loop start to end', () => { + const { getByRole, getAllByRole } = render(defaultSelectMenu()); + const menuTrigger = getByRole('button', { expanded: false }); + userEvent.click(menuTrigger); + const menuItems = getAllByRole('link'); + userEvent.keyboard('[arrowup]'); + expect(menuItems[menuItems.length - 1]).toHaveFocus(); + }); + it('edge behavior should loop end to start', () => { + const { getByRole, getAllByRole } = render(defaultSelectMenu()); + const menuTrigger = getByRole('button', { expanded: false }); + userEvent.click(menuTrigger); + const menuItems = getAllByRole('link'); + userEvent.keyboard('[arrowup]'); + userEvent.keyboard('[arrowdown]'); + expect(menuItems[0]).toHaveFocus(); + }); + + it('should move focus to the last item when pressing the End key', () => { + const { getByRole, getAllByRole } = render(defaultSelectMenu()); + const menuTrigger = getByRole('button', { expanded: false }); + userEvent.click(menuTrigger); + const menuItems = getAllByRole('link'); + userEvent.keyboard('[end]'); + expect(menuItems[menuItems.length - 1]).toHaveFocus(); + }); + it('should move focus to the first item when pressing the Home key', () => { + const { getByRole, getAllByRole } = render(defaultSelectMenu()); + const menuTrigger = getByRole('button', { name: 'Select...', expanded: false }); + userEvent.click(menuTrigger); + const menuItems = getAllByRole('link'); + menuItems[menuItems.length - 1].focus(); + userEvent.keyboard('[home]'); + expect(menuItems[0]).toHaveFocus(); }); }); diff --git a/src/Modal/Modal.test.jsx b/src/Modal/Modal.test.jsx index 6f7c5499ec8..62b1372a290 100644 --- a/src/Modal/Modal.test.jsx +++ b/src/Modal/Modal.test.jsx @@ -1,17 +1,25 @@ import React from 'react'; -import { mount, shallow } from 'enzyme'; +import { render, fireEvent, screen } from '@testing-library/react'; +import '@testing-library/jest-dom/extend-expect'; -import Modal from './index'; +import Modal from '.'; import { Button } from '..'; import Variant from '../utils/constants'; -const modalOpen = (isOpen, wrapper) => { - expect(wrapper.find('.modal').hasClass('d-block')).toEqual(isOpen); - expect(wrapper.find('.modal-backdrop').exists()).toEqual(isOpen); - expect(wrapper.find('.modal').hasClass('show')).toEqual(isOpen); - expect(wrapper.find('.modal').hasClass('fade')).toEqual(!isOpen); - expect(wrapper.state('open')).toEqual(isOpen); +const modalOpen = (isOpen, container) => { + if (!isOpen) { + expect(container.getByTestId('modal-id').classList).not.toContain('d-block'); + expect(container.getByTestId('modal-id').classList).not.toContain('show'); + expect(container.getByTestId('modal-id').classList).toContain('fade'); + expect(container.queryByTestId('modal-backdrop-id').classList).not.toContain('modal-backdrop'); + } else { + expect(container.getByTestId('modal-id').classList).toContain('d-block'); + expect(container.getByTestId('modal-id').classList).toContain('show'); + expect(container.queryByTestId('modal-id').classList).not.toContain('fade'); + expect(container.queryByTestId('modal-backdrop-id').classList).toContain('modal-backdrop'); + } }; + const title = 'Modal title'; const body = 'Modal body'; const defaultProps = { @@ -22,8 +30,6 @@ const defaultProps = { }; const closeText = 'GO AWAY!'; -let wrapper; - describe('', () => { describe('correct rendering', () => { const buttons = [ @@ -36,82 +42,79 @@ describe('', () => { ]; it('renders default buttons', () => { - wrapper = mount(); - const modalTitle = wrapper.find('.modal-title'); - const modalBody = wrapper.find('.modal-body'); + render(); + const modalTitle = screen.getByText(title); + const modalBody = screen.getByText(body); - expect(modalTitle.text()).toEqual(title); - expect(modalBody.text()).toEqual(body); - expect(wrapper.find('button')).toHaveLength(2); + expect(modalTitle).toBeInTheDocument(); + expect(modalBody).toBeInTheDocument(); + expect(screen.queryAllByRole('button')).toHaveLength(2); }); it('renders custom buttons', () => { - wrapper = mount(); - expect(wrapper.find('button')).toHaveLength(buttons.length + 2); + render(); + expect(screen.queryAllByRole('button')).toHaveLength(buttons.length + 2); }); it('renders Warning Variant', () => { - wrapper = mount(); + render( + , + ); + + const modalBody = screen.getByTestId('modal-body'); - const modalBody = wrapper.find('.modal-body'); - expect(modalBody.childAt(0).hasClass('container-fluid')).toEqual(true); - expect(modalBody.find('p').text()).toEqual(body); + expect(modalBody.firstChild.classList).toContain('container-fluid'); + expect(screen.getByText(body)).toBeInTheDocument(); - const icon = modalBody.find('Icon'); - expect(icon.hasClass('fa')).toEqual(true); - expect(icon.hasClass('fa-exclamation-triangle')).toEqual(true); - expect(icon.hasClass('fa-3x')).toEqual(true); - expect(icon.hasClass('text-warning')).toEqual(true); + const icon = screen.getByTestId('icon-id').firstChild; + expect(icon.classList).toContain('fa-exclamation-triangle'); + expect(icon.classList).toContain('fa-3x'); + expect(icon.classList).toContain('text-warning'); }); it('renders invalid Variant properly', () => { - wrapper = mount(); - const modalTitle = wrapper.find('.modal-title'); - const modalBody = wrapper.find('.modal-body'); + render(); + const modalTitle = screen.getByText(title); + const modalBody = screen.getByText(body); - expect(modalTitle.text()).toEqual(title); - expect(modalBody.text()).toEqual(body); - expect(wrapper.find('button')).toHaveLength(2); + expect(modalTitle).toBeInTheDocument(); + expect(modalBody).toBeInTheDocument(); + expect(screen.queryAllByRole('button')).toHaveLength(2); }); it('render of the header close button is optional', () => { - wrapper = mount(); - const modalHeader = wrapper.find('.modal-header'); - const modalFooter = wrapper.find('.modal-footer'); + render(); + const modalHeaderBtn = screen.queryByTestId('modal-header-btn'); + const modalFooterBtn = screen.getByTestId('modal-footer-btn'); - expect(modalHeader.find('button')).toHaveLength(0); - expect(modalFooter.find('button')).toHaveLength(1); - expect(wrapper.find('button')).toHaveLength(1); + expect(modalHeaderBtn).not.toBeInTheDocument(); + expect(modalFooterBtn).toBeInTheDocument(); }); it('render of the default footer close button is optional', () => { - wrapper = mount(); - const modalHeader = wrapper.find('.modal-header'); - const modalFooter = wrapper.find('.modal-footer'); + render(); + const modalHeaderBtn = screen.getByTestId('modal-header-btn'); + const modalFooterBtn = screen.queryByTestId('modal-footer-btn'); - expect(modalHeader.find('button')).toHaveLength(1); - expect(modalFooter.find('button')).toHaveLength(0); - expect(wrapper.find('button')).toHaveLength(1); + expect(modalHeaderBtn).toBeInTheDocument(); + expect(modalFooterBtn).not.toBeInTheDocument(); }); it('renders custom close button string', () => { - wrapper = mount(); - const modalFooter = wrapper.find('.modal-footer'); - const closeButton = modalFooter.find('button'); - expect(closeButton).toHaveLength(1); - expect(closeButton.text()).toEqual(closeText); + render(); + const modalFooterBtn = screen.getByTestId('modal-footer-btn'); + expect(modalFooterBtn).toBeInTheDocument(); + expect(modalFooterBtn).toHaveTextContent(closeText); }); it('renders custom close button element', () => { const closeElem = {closeText}; - wrapper = mount(); - const modalFooter = wrapper.find('.modal-footer'); - const closeButton = modalFooter.find('button'); - - expect(closeButton).toHaveLength(1); - expect(closeButton.children()).toHaveLength(1); - expect(closeButton.find('.is-close-text')).toHaveLength(1); - expect(closeButton.text()).toEqual(closeText); + render(); + const modalFooterBtn = screen.getByTestId('modal-footer-btn'); + + expect(modalFooterBtn).toBeInTheDocument(); + expect(modalFooterBtn.firstChild.classList).toContain('is-close-text'); + expect(modalFooterBtn.firstChild).toHaveTextContent(closeText); }); it('renders with IE11-specific styling when IE11 is detected', () => { @@ -121,9 +124,9 @@ describe('', () => { // mimic IE11 global.MSInputMethodContext = true; global.document.documentMode = true; - wrapper = mount(); - const modal = wrapper.find('.modal'); - expect(modal.hasClass('is-ie11')).toEqual(true); + render(); + const modal = screen.queryByTestId('modal-id'); + expect(modal.classList).toContain('is-ie11'); global.MSInputMethodContext = MSInputMethodContext; global.document.documentMode = documentMode; @@ -136,9 +139,9 @@ describe('', () => { // mimic non-IE11 browser global.MSInputMethodContext = false; global.document.documentMode = false; - wrapper = mount(); - const modal = wrapper.find('.modal'); - expect(modal.hasClass('is-ie11')).toEqual(false); + render(); + const modal = screen.queryByTestId('modal-id'); + expect(modal).not.toContain('is-ie11'); global.MSInputMethodContext = MSInputMethodContext; global.document.documentMode = documentMode; @@ -146,132 +149,113 @@ describe('', () => { }); describe('props received correctly', () => { - beforeEach(() => { - // This is a gross hack to suppress error logs in the invalid parentSelector test - jest.spyOn(console, 'error'); - global.console.error.mockImplementation(() => { }); - }); - - afterEach(() => { - global.console.error.mockRestore(); - }); - it('component receives props', () => { - wrapper = mount(); + const { rerender } = render(); - modalOpen(false, wrapper); - wrapper.setProps({ open: true }); - wrapper.update(); - modalOpen(true, wrapper); + modalOpen(false, screen); + rerender(); + modalOpen(true, screen); }); it('component receives props and ignores prop change', () => { - wrapper = mount(); + const { rerender } = render(); - modalOpen(true, wrapper); - wrapper.setProps({ title: 'Changed modal title' }); - wrapper.update(); - modalOpen(true, wrapper); + modalOpen(true, screen); + rerender(); + modalOpen(true, screen); }); it('throws an error when an invalid parentSelector prop is passed', () => { - expect(() => { - wrapper = shallow( render( + ); - }).toThrow('Modal received invalid parentSelector: this-selector-does-not-exist, no matching element found'); + parentSelector=".this-selector-does-not-exist" + />, + )).toThrow('Modal received invalid parentSelector: .this-selector-does-not-exist, no matching element found'); }); }); describe('close functions properly', () => { - beforeEach(() => { - wrapper = mount(); - }); - it('closes when x button pressed', () => { - modalOpen(true, wrapper); - wrapper.find('button').at(0).simulate('click'); - modalOpen(false, wrapper); + render(); + + modalOpen(true, screen); + fireEvent.click(screen.queryAllByRole('button')[0]); + modalOpen(false, screen); }); it('closes when Close button pressed', () => { - modalOpen(true, wrapper); - wrapper.find('button').at(1).simulate('click'); - modalOpen(false, wrapper); + render(); + + modalOpen(true, screen); + fireEvent.click(screen.queryAllByRole('button')[1]); + modalOpen(false, screen); }); it('calls callback function on close', () => { const spy = jest.fn(); - - wrapper = mount(); + render(); expect(spy).toHaveBeenCalledTimes(0); // press X button - wrapper.find('button').at(0).simulate('click'); + fireEvent.click(screen.queryAllByRole('button')[0]); expect(spy).toHaveBeenCalledTimes(1); }); it('reopens after closed', () => { - modalOpen(true, wrapper); - wrapper.find('button').at(0).simulate('click'); - modalOpen(false, wrapper); - wrapper.setProps({ open: true }); - wrapper.update(); - modalOpen(true, wrapper); + const { rerender } = render(); + + modalOpen(true, screen); + fireEvent.click(screen.queryAllByRole('button')[0]); + modalOpen(false, screen); + rerender(); + modalOpen(true, screen); }); }); describe('focus changes correctly', () => { - let buttons; - - beforeEach(() => { - wrapper = mount(); - - buttons = wrapper.find('button'); - }); - it('has correct initial focus', () => { - expect(buttons.at(0).html()).toEqual(document.activeElement.outerHTML); + render(); + const buttons = screen.queryAllByRole('button'); + expect(buttons[0]).toHaveFocus(); }); it('has reset focus after close and reopen', () => { - expect(buttons.at(0).html()).toEqual(document.activeElement.outerHTML); - wrapper.setProps({ open: false }); - wrapper.update(); - modalOpen(false, wrapper); - wrapper.setProps({ open: true }); - wrapper.update(); - modalOpen(true, wrapper); - expect(buttons.at(0).html()).toEqual(document.activeElement.outerHTML); + const { rerender } = render(); + const buttons = screen.queryAllByRole('button'); + expect(buttons[0]).toHaveFocus(); + rerender(); + modalOpen(false, screen); + rerender(); + modalOpen(true, screen); + expect(buttons[0]).toHaveFocus(); }); it('has focus on input in modal body', () => { - wrapper = mount(( + const { getByTestId } = render( } - /> - )); - expect(wrapper.find('input').html()).toEqual(document.activeElement.outerHTML); + body={
} + />, + ); + const input = getByTestId('modal-input'); + expect(input).toHaveFocus(); }); it('has focus on `.modal-content` when nothing else is tabbable', () => { - wrapper = mount(( + const { getByTestId } = render( - )); - expect(wrapper.find('.modal-content').html()).toEqual(document.activeElement.outerHTML); + />, + ); + const modalContent = getByTestId('modal-content'); + expect(modalContent).toHaveFocus(); }); }); }); diff --git a/src/Modal/ModalLayer.test.jsx b/src/Modal/ModalLayer.test.jsx index 2995e4b532e..a70cea908a7 100644 --- a/src/Modal/ModalLayer.test.jsx +++ b/src/Modal/ModalLayer.test.jsx @@ -1,8 +1,7 @@ import React from 'react'; -import { shallow } from 'enzyme'; -import { FocusOn } from 'react-focus-on'; -import ModalLayer, { ModalBackdrop } from './ModalLayer'; -import { ModalContextProvider } from './ModalContext'; +import { render, fireEvent } from '@testing-library/react'; +import ModalLayer from './ModalLayer'; +import '@testing-library/jest-dom/extend-expect'; /* eslint-disable react/prop-types */ jest.mock('./Portal', () => function PortalMock(props) { @@ -18,7 +17,7 @@ jest.mock('react-focus-on', () => ({ FocusOn: (props) => { const { children, ...otherProps } = props; return ( - {children} + {children} ); }, })); @@ -35,65 +34,67 @@ describe('', () => { describe('when isOpen', () => { const isOpen = true; const closeFn = jest.fn(); - const wrapper = shallow(( - - - - )); it('renders the dialog and a modal context provider', () => { - const dialog = wrapper.find(Dialog); - expect(dialog.length).toBe(1); - }); + const { getByRole } = render( + + + , + ); - it('renders a modal context provider', () => { - const contextProvider = wrapper.find(ModalContextProvider); - expect(contextProvider.props().isOpen).toBe(isOpen); - expect(contextProvider.props().onClose).toBe(closeFn); + const dialog = getByRole('dialog', { name: 'A dialog' }); + expect(dialog).toBeInTheDocument(); }); it('renders a focus-on component with appropriate props', () => { - const focusOn = wrapper.find(FocusOn); - const focusOnProps = focusOn.props(); - expect(focusOnProps.scrollLock).toBe(true); - expect(focusOnProps.enabled).toBe(true); - expect(focusOnProps.onEscapeKey).toBe(closeFn); + const { getByTestId } = render( + + + , + ); + + const focusOn = getByTestId('focus-on'); + expect(focusOn).toBeInTheDocument(); + expect(focusOn).toHaveAttribute('scrolllock', 'true'); + expect(focusOn).toHaveAttribute('enabled', 'true'); }); }); test('when isOpen is false the dialog is not rendered', () => { - const wrapper = shallow(( + const { queryByRole } = render( - - )); - const dialog = wrapper.find(Dialog); - expect(dialog.length).toBe(0); + , + ); + + const dialog = queryByRole('dialog', { name: 'A dialog' }); + expect(dialog).not.toBeInTheDocument(); }); describe('Backdrop', () => { it('closes a non-blocking modal layer when clicked', () => { const closeFn = jest.fn(); - const wrapper = shallow(( + const { container } = render( - - )); + , + ); - const backdrop = wrapper.find(ModalBackdrop); - backdrop.simulate('click'); + const backdrop = container.querySelector('.pgn__modal-backdrop'); + fireEvent.click(backdrop); expect(closeFn).toHaveBeenCalled(); }); + it('does not close a blocking modal layer when clicked', () => { const closeFn = jest.fn(); - const wrapper = shallow(( + const { container } = render( - - )); + , + ); - const backdrop = wrapper.find(ModalBackdrop); - backdrop.simulate('click'); + const backdrop = container.querySelector('.pgn__modal-backdrop'); + fireEvent.click(backdrop); expect(closeFn).not.toHaveBeenCalled(); }); }); diff --git a/src/Modal/ModalPopup.test.jsx b/src/Modal/ModalPopup.test.jsx index 7749e414a24..b4a17a14944 100644 --- a/src/Modal/ModalPopup.test.jsx +++ b/src/Modal/ModalPopup.test.jsx @@ -1,15 +1,13 @@ import React from 'react'; -import { shallow } from 'enzyme'; -import { FocusOn } from 'react-focus-on'; +import { render } from '@testing-library/react'; import ModalPopup from './ModalPopup'; -import { ModalContextProvider } from './ModalContext'; -import Portal from './Portal'; +import '@testing-library/jest-dom/extend-expect'; /* eslint-disable react/prop-types */ jest.mock('./Portal', () => function PortalMock(props) { const { children, ...otherProps } = props; return ( - + {children} ); @@ -19,7 +17,7 @@ jest.mock('react-focus-on', () => ({ FocusOn: (props) => { const { children, ...otherProps } = props; return ( - {children} + {children} ); }, })); @@ -56,88 +54,103 @@ describe('', () => { describe('when isOpen', () => { const isOpen = true; const closeFn = jest.fn(); - const wrapper = shallow(( - - - - )); it('renders the dialog', () => { - const dialog = wrapper.find(Dialog); - expect(dialog.length).toBe(1); - }); - - it('renders a modal context provider', () => { - const contextProvider = wrapper.find(ModalContextProvider); - expect(contextProvider.props().isOpen).toBe(isOpen); - expect(contextProvider.props().onClose).toBe(closeFn); + const { getByRole } = render( + + + , + ); + const dialog = getByRole('dialog', { name: 'A dialog' }); + expect(dialog).toBeInTheDocument(); }); it('renders a focus-on component with appropriate props', () => { - const focusOn = wrapper.find(FocusOn); - const focusOnProps = focusOn.props(); - expect(focusOnProps.scrollLock).toBe(false); - expect(focusOnProps.enabled).toBe(true); - expect(focusOnProps.onEscapeKey).toBe(closeFn); + const { getByTestId } = render( + + + , + ); + const focusOn = getByTestId('focus-on'); + expect(focusOn).toBeInTheDocument(); + expect(focusOn).toHaveAttribute('scrolllock', 'false'); + expect(focusOn).toHaveAttribute('enabled', 'true'); }); }); it('when isOpen is false the dialog is not rendered', () => { - const wrapper = shallow(( + const { queryByRole } = render( - - )); - const dialog = wrapper.find(Dialog); - expect(dialog.length).toBe(0); + , + ); + const dialog = queryByRole('dialog', { name: 'A dialog' }); + expect(dialog).not.toBeInTheDocument(); }); describe('withPortal', () => { it('renders no portal by default', () => { - const wrapper = shallow(( + const { queryByTestId } = render( - - )); - expect(wrapper.find(Portal).length).toBe(0); + , + ); + const portal = queryByTestId('portal'); + expect(portal).toBeNull(); }); + it('renders with a portal if withPortal is true', () => { - const wrapper = shallow(( + const { getByTestId } = render( - - )); - expect(wrapper.find(Portal).length).toBe(1); + , + ); + const portal = getByTestId('portal'); + expect(portal).not.toBeNull(); }); }); + describe('withArrow', () => { const popupArrowModalClass = '.pgn__modal-popup__arrow'; + arrowPlacements.forEach((side) => { it(`renders with placement ${side}`, () => { - const wrapperPopup = shallow(( - + const { container } = render( + - )); - expect(wrapperPopup.exists(`${popupArrowModalClass}-${side}`)).toBe(true); + , + ); + const arrow = container.querySelector(`${popupArrowModalClass}-${side}`); + expect(arrow).not.toBeNull(); }); }); + it('renders without arrow', () => { - const wrapperPopup = shallow(( + const { container } = render( - )); - expect(wrapperPopup.exists(popupArrowModalClass)).toBe(false); + , + ); + const arrow = container.querySelector(popupArrowModalClass); + expect(arrow).toBeNull(); }); + it('renders with arrow', () => { - const wrapperPopup = shallow(( - + const { container } = render( + - )); - expect(wrapperPopup.exists(popupArrowModalClass)).toBe(true); + , + ); + const arrow = container.querySelector(popupArrowModalClass); + expect(arrow).toBeInTheDocument(); }); }); }); diff --git a/src/Modal/index.jsx b/src/Modal/index.jsx index a12a7d69310..6a4cd364865 100644 --- a/src/Modal/index.jsx +++ b/src/Modal/index.jsx @@ -50,7 +50,9 @@ class Modal extends React.Component { } componentWillUnmount() { - ReactDOM.unmountComponentAtNode(this.parentElement); + if (this.parentElement) { + ReactDOM.unmountComponentAtNode(this.parentElement); + } } getVariantIconClassName() { @@ -84,7 +86,7 @@ class Modal extends React.Component { {body} -
+
@@ -220,12 +225,13 @@ class Modal extends React.Component { )}
-
+
{this.renderBody()}
{renderModalFooter && ( @@ -234,6 +240,7 @@ class Modal extends React.Component { diff --git a/src/Pagination/Pagination.test.jsx b/src/Pagination/Pagination.test.jsx index d0614ab3b66..c5836d5a32c 100644 --- a/src/Pagination/Pagination.test.jsx +++ b/src/Pagination/Pagination.test.jsx @@ -1,10 +1,10 @@ import React from 'react'; -import { mount } from 'enzyme'; +import { render, fireEvent, act } from '@testing-library/react'; import { Context as ResponsiveContext } from 'react-responsive'; -import { act } from 'react-dom/test-utils'; +import '@testing-library/jest-dom/extend-expect'; + import breakpoints from '../utils/breakpoints'; import Pagination from './index'; -import Dropdown from '../Dropdown'; const baseProps = { state: { pageIndex: 1 }, @@ -18,8 +18,8 @@ describe('', () => { const props = { ...baseProps, }; - const wrapper = mount(); - expect(wrapper.exists()).toEqual(true); + const { container } = render(); + expect(container).toBeInTheDocument(); }); it('renders screen reader section', () => { @@ -34,10 +34,9 @@ describe('', () => { ...baseProps, buttonLabels, }; - const wrapper = mount(); - expect( - wrapper.findWhere(node => node.hasClass('sr-only')).text(), - ).toEqual(`${buttonLabels.page} 1, ${buttonLabels.currentPage}, ${buttonLabels.pageOfCount} ${baseProps.pageCount}`); + const { getByText } = render(); + const srText = getByText(`${buttonLabels.page} 1, ${buttonLabels.currentPage}, ${buttonLabels.pageOfCount} ${baseProps.pageCount}`); + expect(srText).toBeInTheDocument(); }); describe('handles currentPage props properly', () => { @@ -48,13 +47,10 @@ describe('', () => { ...baseProps, currentPage: initialPage, }; - const wrapper = mount(); - expect(wrapper.state('currentPage')).toEqual(initialPage); - wrapper.setProps({ - currentPage: newPage, - }); - wrapper.update(); - expect(wrapper.state('currentPage')).toEqual(newPage); + const { rerender, getByText } = render(); + expect(getByText('Page 1, Current Page, of 5')).toBeInTheDocument(); + rerender(); + expect(getByText('Page 2, Current Page, of 5')).toBeInTheDocument(); }); it('does not override state currentPage when props currentPage changes with existing value', () => { @@ -63,13 +59,10 @@ describe('', () => { ...baseProps, currentPage, }; - const wrapper = mount(); - expect(wrapper.state('currentPage')).toEqual(currentPage); - wrapper.setProps({ - currentPage, - }); - wrapper.update(); - expect(wrapper.state('currentPage')).toEqual(currentPage); + const { rerender, getByText } = render(); + expect(getByText(`Page ${currentPage}, Current Page, of 5`)).toBeInTheDocument(); + rerender(); + expect(getByText(`Page ${currentPage}, Current Page, of 5`)).toBeInTheDocument(); }); }); @@ -79,11 +72,11 @@ describe('', () => { ...baseProps, currentPage: 2, }; - const app = document.createElement('div'); - document.body.appendChild(app); - const wrapper = mount(, { attachTo: app }); - wrapper.find('button.previous').simulate('click'); - expect(wrapper.find('button.next').instance()).toEqual(document.activeElement); + const { getByLabelText } = render(); + const previousButton = getByLabelText(/Previous/); + const nextButton = getByLabelText(/Next/); + fireEvent.click(previousButton); + expect(document.activeElement).toEqual(nextButton); }); it('should change focus to previous button if next page is last page', () => { @@ -91,11 +84,11 @@ describe('', () => { ...baseProps, currentPage: baseProps.pageCount - 1, }; - const app = document.createElement('div'); - document.body.appendChild(app); - const wrapper = mount(, { attachTo: app }); - wrapper.find('button.next').simulate('click'); - expect(wrapper.find('button.previous').instance()).toEqual(document.activeElement); + const { getByLabelText } = render(); + const previousButton = getByLabelText(/Previous/); + const nextButton = getByLabelText(/Next/); + fireEvent.click(nextButton); + expect(document.activeElement).toEqual(previousButton); }); }); @@ -106,35 +99,31 @@ describe('', () => { ...baseProps, paginationLabel, }; - const wrapper = mount(); - expect(wrapper.find('nav').prop('aria-label')).toEqual(paginationLabel); + const { getByLabelText } = render(); + expect(getByLabelText(paginationLabel)).toBeInTheDocument(); }); describe('should use correct number of pages', () => { it('should show 5 buttons on desktop', () => { - const wrapper = mount(( + const { getAllByLabelText } = render( - - )); - expect(wrapper.findWhere((node) => { - const isPrevOrNext = node.hasClass('previous') || node.hasClass('next'); - return node.name() === 'button' && !isPrevOrNext; - })).toHaveLength(5); + , + ); + + const pageButtons = getAllByLabelText(/^Page/); + expect(pageButtons.length).toBe(5); }); it('should show 1 button on mobile', () => { - // Use extra small window size to display the mobile version of `Pagination`. - const wrapper = mount(( + // Use extra small window size to display the mobile version of Pagination. + const { getAllByLabelText } = render( - - )); - expect(wrapper.findWhere((node) => { - const name = node.name(); - const isPrevOrNext = node.hasClass('previous') || node.hasClass('next'); - return name === 'span' && node.hasClass('btn') && !isPrevOrNext; - })).toHaveLength(1); + , + ); + const pageButtons = getAllByLabelText(/^Page/); + expect(pageButtons.length).toBe(1); }); }); @@ -145,13 +134,14 @@ describe('', () => { ...baseProps, onPageSelect: spy, }; - const wrapper = mount(( + const { getByLabelText } = render( - - )); + , + ); - wrapper.find('.btn').at(1).simulate('click'); + const previousButton = getByLabelText(/Previous/); + fireEvent.click(previousButton); expect(spy).toHaveBeenCalledTimes(0); }); @@ -161,18 +151,17 @@ describe('', () => { ...baseProps, onPageSelect: spy, }; - const wrapper = mount(( + const { getAllByLabelText } = render( - - )); + , + ); - wrapper.find('.btn').at(2).simulate('click'); - expect(wrapper.state('currentPage')).toEqual(2); + const pageButtons = getAllByLabelText(/^Page/); + fireEvent.click(pageButtons[1]); expect(spy).toHaveBeenCalledTimes(1); - wrapper.find('.btn').at(3).simulate('click'); - expect(wrapper.state('currentPage')).toEqual(3); + fireEvent.click(pageButtons[2]); expect(spy).toHaveBeenCalledTimes(2); }); }); @@ -183,12 +172,11 @@ describe('', () => { const spy = jest.fn(); const props = { ...baseProps, + currentPage: 2, onPageSelect: spy, }; - const wrapper = mount(); - wrapper.setProps({ currentPage: 2 }); - wrapper.update(); - wrapper.find('button.previous').simulate('click'); + const { getByLabelText } = render(); + fireEvent.click(getByLabelText(/Previous/)); expect(spy).toHaveBeenCalledTimes(1); }); @@ -198,8 +186,8 @@ describe('', () => { ...baseProps, onPageSelect: spy, }; - const wrapper = mount(); - wrapper.find('button.next').simulate('click'); + const { getByLabelText } = render(); + fireEvent.click(getByLabelText(/Next/)); expect(spy).toHaveBeenCalledTimes(1); }); }); @@ -213,12 +201,15 @@ describe('', () => { pageOfCount: 'de', }; - let wrapper; - let props; + let props = { + ...baseProps, + buttonLabels, + }; + /** * made a proxy component because setProps can only be used with root component and * Responsive Context Provider is needed to mock screen - * */ + */ // eslint-disable-next-line react/prop-types function Proxy({ currentPage, width }) { return ( @@ -228,85 +219,67 @@ describe('', () => { ); } - beforeEach(() => { - props = { - ...baseProps, - buttonLabels, - }; - wrapper = mount( - , - ); - }); - it('uses passed in previous button label', () => { - expect(wrapper.findWhere(node => ( - node.name() === 'button' && node.hasClass('previous') - )).prop('aria-label')).toEqual(buttonLabels.previous); - - wrapper.setProps({ currentPage: baseProps.pageCount }); - wrapper.update(); + const { getByText, getByLabelText } = render( + , + ); + expect(getByText(buttonLabels.previous)).toBeInTheDocument(); - expect(wrapper.findWhere(node => ( - node.name() === 'button' && node.hasClass('previous') - )).prop('aria-label')).toEqual(`${buttonLabels.previous}, ${buttonLabels.page} 4`); + fireEvent.click(getByText(buttonLabels.next)); + expect(getByLabelText(`${buttonLabels.previous}, ${buttonLabels.page} 4`)).toBeInTheDocument(); }); it('uses passed in next button label', () => { - expect(wrapper.findWhere(node => ( - node.name() === 'button' && node.hasClass('next') - )).prop('aria-label')).toEqual(`${buttonLabels.next}, ${buttonLabels.page} 2`); - - wrapper.setProps({ currentPage: baseProps.pageCount }); - wrapper.update(); + const { rerender, getByLabelText } = render( + , + ); + expect(getByLabelText(`${buttonLabels.next}, ${buttonLabels.page} 2`)).toBeInTheDocument(); - expect(wrapper.findWhere(node => ( - node.name() === 'button' && node.hasClass('next') - )).prop('aria-label')).toEqual(buttonLabels.next); + rerender( + , + ); + expect(getByLabelText(buttonLabels.next)).toBeInTheDocument(); }); it('uses passed in page button label', () => { - wrapper = mount(( + const { rerender, getByText, getByLabelText } = render( - - )); - expect(wrapper.state('currentPage')).toEqual(1); - expect(wrapper.find('.btn').at(1).prop('aria-label')) - .toEqual(`${buttonLabels.page} 1, ${buttonLabels.currentPage}`); - wrapper = mount(( + , + ); + expect(getByText(`${buttonLabels.page} 1, ${buttonLabels.currentPage}, ${buttonLabels.pageOfCount} 5`)).toBeInTheDocument(); + expect(getByLabelText(`${buttonLabels.page} 1, ${buttonLabels.currentPage}`)).toBeInTheDocument(); + + rerender( - - )); - - expect(wrapper.state('currentPage')).toEqual(2); - expect(wrapper.find('.btn').at(1).prop('aria-label')) - .toEqual(`${buttonLabels.page} 1`); + , + ); + expect(getByText(`${buttonLabels.page} 2, ${buttonLabels.currentPage}, ${buttonLabels.pageOfCount} 5`)).toBeInTheDocument(); + expect(getByLabelText(`${buttonLabels.page} 1`)).toBeInTheDocument(); - wrapper = mount( + rerender( , ); - - expect(wrapper.find('.btn').at(1).prop('aria-label')) - .toEqual(`${buttonLabels.page} 1, ${buttonLabels.currentPage}, ${buttonLabels.pageOfCount} 5`); + expect(getByText(`${buttonLabels.page} 1, ${buttonLabels.currentPage}, ${buttonLabels.pageOfCount} 5`)).toBeInTheDocument(); }); it('for the reduced variant shows dropdown button with the page count as label', async () => { - wrapper = mount(); + const { getByRole, getAllByTestId } = render(); + + const dropdownButton = getByRole('button', { name: /1 of 5/i, attributes: { 'aria-haspopup': 'true' } }); + expect(dropdownButton.textContent).toContain(`${baseProps.state.pageIndex} of ${baseProps.pageCount}`); - const dropdown = wrapper.find(Dropdown); - expect(dropdown.text()).toContain(`${baseProps.state.pageIndex} of ${baseProps.pageCount}`); + fireEvent.click(dropdownButton); - const dropdownButton = wrapper.find('button.dropdown-toggle'); - dropdownButton.simulate('click'); await act(async () => { - const dropdownChoices = wrapper.find(Dropdown.Item); - expect(dropdownChoices.length).toEqual(baseProps.pageCount); + const dropdownChoices = getAllByTestId('pagination-dropdown-item'); + expect(dropdownChoices.length).toBe(baseProps.pageCount); }); }); it('renders only previous and next buttons in minimal variant', () => { - wrapper = mount( + const { getAllByRole } = render( pageNumber} @@ -314,14 +287,13 @@ describe('', () => { paginationLabel="Label" />, ); - const items = wrapper.find('li.page-item'); - - expect(items.length).toEqual(2); + const items = getAllByRole('listitem'); + expect(items.length).toBe(2); }); - it('renders chevrons and buttons disabled when pageCount is 1 || 0 for all variants', () => { + it('renders chevrons and buttons disabled when pageCount is 1 or 0 for all variants', () => { + const screen = render(); const variantTypes = ['default', 'secondary', 'reduced', 'minimal']; - // default variantTypes.forEach((variantType) => { for (let i = 0; i < 3; i++) { props = { @@ -329,10 +301,10 @@ describe('', () => { variant: variantType, pageCount: i, }; - wrapper = mount(); - const disabled = wrapper.find('button[disabled=true]'); + screen.rerender(); + const disabledButtons = screen.container.querySelectorAll('button[disabled]'); expect(props.pageCount).toEqual(i); - expect(disabled.length).toEqual(i === 2 ? 1 : 2); + expect(disabledButtons.length).toEqual(i === 2 ? 1 : 2); } }); }); diff --git a/src/RadioButtonGroup/RadioButtonGroup.test.jsx b/src/RadioButtonGroup/RadioButtonGroup.test.jsx index 4d45d3b4750..2b64f04ae5d 100644 --- a/src/RadioButtonGroup/RadioButtonGroup.test.jsx +++ b/src/RadioButtonGroup/RadioButtonGroup.test.jsx @@ -1,17 +1,18 @@ import React from 'react'; -import { shallow, mount } from 'enzyme'; +import { render, fireEvent } from '@testing-library/react'; +import '@testing-library/jest-dom/extend-expect'; -import RadioButtonGroup, { RadioButton } from './index'; +import RadioButtonGroup, { RadioButton } from '.'; describe('', () => { const text = 'text'; const index = 0; const isChecked = false; const name = 'name'; - const onBlur = () => {}; - const onClick = () => {}; - const onFocus = () => {}; - const onKeyDown = () => {}; + const onBlur = jest.fn(); + const onClick = jest.fn(); + const onFocus = jest.fn(); + const onKeyDown = jest.fn(); const value = 'value'; const props = { index, @@ -23,22 +24,20 @@ describe('', () => { onKeyDown, value, }; - describe('correct rendering', () => { it('renders RadioButton', () => { - const wrapper = shallow({text}); - - expect(wrapper.type()).toEqual('div'); - expect(wrapper.find('input')).toHaveLength(1); + const { getByRole } = render( + + {text} + , + ); - const radioButton = wrapper.find('input').at(0); - expect(radioButton.prop('type')).toEqual('radio'); - expect(radioButton.prop('name')).toEqual(name); - expect(radioButton.prop('value')).toEqual(value); - expect(radioButton.prop('defaultChecked')).toEqual(isChecked); - expect(radioButton.prop('aria-checked')).toEqual(isChecked); - expect(radioButton.prop('data-index')).toEqual(index); - expect(wrapper.find('div').text()).toEqual(text); + const radioButton = getByRole('radio'); + expect(radioButton).toBeInTheDocument(); + expect(radioButton).toHaveAttribute('name', name); + expect(radioButton).toHaveAttribute('value', value); + expect(radioButton).toHaveAttribute('aria-checked', isChecked.toString()); + expect(radioButton).toHaveAttribute('data-index', index.toString()); }); }); @@ -49,31 +48,32 @@ describe('', () => { spy = jest.fn(); }); + afterEach(() => { + spy.mockClear(); + }); + it('should fire onBlur', () => { - const wrapper = mount(); - expect(spy).toHaveBeenCalledTimes(0); - wrapper.find('input').at(0).simulate('blur'); + const { getByRole } = render(()); + const radioButton = getByRole('radio'); + fireEvent.blur(radioButton); expect(spy).toHaveBeenCalledTimes(1); }); - it('should fire onClick', () => { - const wrapper = mount(); - expect(spy).toHaveBeenCalledTimes(0); - wrapper.find('input').at(0).simulate('click'); + const { getByRole } = render(()); + const radioButton = getByRole('radio'); + fireEvent.click(radioButton); expect(spy).toHaveBeenCalledTimes(1); }); - it('should fire onFocus', () => { - const wrapper = mount(); - expect(spy).toHaveBeenCalledTimes(0); - wrapper.find('input').at(0).simulate('focus'); + const { getByRole } = render(()); + const radioButton = getByRole('radio'); + fireEvent.focus(radioButton); expect(spy).toHaveBeenCalledTimes(1); }); - it('should fire onKeyDown', () => { - const wrapper = mount(); - expect(spy).toHaveBeenCalledTimes(0); - wrapper.find('input').at(0).simulate('keydown'); + const { getByRole } = render(()); + const radioButton = getByRole('radio'); + fireEvent.keyDown(radioButton); expect(spy).toHaveBeenCalledTimes(1); }); }); @@ -84,11 +84,11 @@ describe('', () => { const secondText = 'secondText'; const name = 'name'; const label = 'label'; - const onBlur = () => {}; - const onChange = () => {}; - const onClick = () => {}; - const onFocus = () => {}; - const onKeyDown = () => {}; + const onBlur = jest.fn(); + const onChange = jest.fn(); + const onClick = jest.fn(); + const onFocus = jest.fn(); + const onKeyDown = jest.fn(); const firstValue = 'firstValue'; const secondValue = 'secondValue'; const props = { @@ -101,109 +101,25 @@ describe('', () => { onKeyDown, }; - describe('renders correctly', () => { - it('renders RadioButtonGroup', () => { - const radioButtonGroup = ( - - {firstText} - {secondText} - - ); - const wrapper = shallow(radioButtonGroup); - - wrapper.find(RadioButton).forEach((button, index) => { - expect(button.prop('name')).toEqual(name); - expect(button.prop('isChecked')).toEqual(false); - expect(button.prop('onBlur')).toEqual(onBlur); - expect(button.prop('onClick')).toEqual(onClick); - expect(button.prop('onFocus')).toEqual(onFocus); - expect(button.prop('onKeyDown')).toEqual(onKeyDown); - expect(button.prop('index')).toEqual(index); - - let value = firstValue; - if (index === 1) { - value = secondValue; - } - expect(button.prop('value')).toEqual(value); - }); - - const radioButtonGroupDiv = wrapper.find('div'); - expect(radioButtonGroupDiv.prop('role')).toEqual('radiogroup'); - expect(radioButtonGroupDiv.prop('aria-label')).toEqual(label); - expect(radioButtonGroupDiv.prop('tabIndex')).toEqual(-1); - }); - }); - - describe('updates state when onChange event is fired', () => { - let spy; - - const index = 7; - - beforeEach(() => { - spy = jest.fn(); - }); - - it('changes state when checked event and target has attribute', () => { - const event = { - target: { - checked: true, - hasAttribute: () => true, - getAttribute: () => index, - }, - }; - const radioButtonGroup = ( - - {firstText} - {secondText} - - ); - - const wrapper = mount(radioButtonGroup); - wrapper.simulate('change', event); - expect(spy).toHaveBeenCalledTimes(1); - expect(wrapper.state('selectedIndex')).toEqual(index); - }); + it('renders RadioButtonGroup', () => { + const { getByRole, getAllByRole } = render( + + {firstText} + {secondText} + , + ); - it('does not change state if event target is not checked', () => { - const event = { - target: { - checked: false, - hasAttribute: () => true, - getAttribute: () => index, - }, - }; - const radioButtonGroup = ( - - {firstText} - {secondText} - - ); + const radioButtons = getAllByRole('radio'); + expect(radioButtons.length).toBe(2); - const wrapper = mount(radioButtonGroup); - wrapper.simulate('change', event); - expect(spy).toHaveBeenCalledTimes(1); - expect(wrapper.state('selectedIndex')).toEqual(undefined); + radioButtons.forEach((radioButton, index) => { + expect(radioButton).toHaveAttribute('name', name); + expect(radioButton).toHaveAttribute('value', index === 0 ? firstValue : secondValue); }); - it('does not change state if event target is checked but data-attribute does not exist', () => { - const event = { - target: { - checked: false, - hasAttribute: () => false, - getAttribute: () => index, - }, - }; - const radioButtonGroup = ( - - {firstText} - {secondText} - - ); - - const wrapper = mount(radioButtonGroup); - wrapper.simulate('change', event); - expect(spy).toHaveBeenCalledTimes(1); - expect(wrapper.state('selectedIndex')).toEqual(undefined); - }); + const radioButtonGroupDiv = getByRole('radiogroup'); + expect(radioButtonGroupDiv).toBeInTheDocument(); + expect(radioButtonGroupDiv).toHaveAttribute('aria-label', label); + expect(radioButtonGroupDiv).toHaveAttribute('tabIndex', '-1'); }); }); diff --git a/src/SearchField/SearchField.test.jsx b/src/SearchField/SearchField.test.jsx index 949d913809a..ef33ce996c8 100644 --- a/src/SearchField/SearchField.test.jsx +++ b/src/SearchField/SearchField.test.jsx @@ -1,81 +1,59 @@ -/* eslint-disable react/prop-types */ import React from 'react'; -import { shallow, mount } from 'enzyme'; -import { Search, Close } from '../../icons'; -import Icon from '../Icon'; +import { render, fireEvent } from '@testing-library/react'; +import '@testing-library/jest-dom/extend-expect'; import SearchField from './index'; -const BUTTON_LOCATION_VARIANTS = [ - 'internal', - 'external', -]; - const baseProps = { onSubmit: () => {}, }; describe(' with basic usage', () => { it('should match the snapshot', () => { - const wrapper = shallow(); - expect(wrapper.html()).toMatchSnapshot(); + const { container } = render(); + expect(container).toMatchSnapshot(); }); - it('should pass correct props to `SearchField.Advanced`', () => { - const wrapper = shallow(); - const props = wrapper.find(SearchField.Advanced).props(); - expect(props.children).toEqual(expect.any(Array)); - expect(props.className).toEqual(undefined); - expect(props.icons).toEqual({ - clear: , - submit: , - }); - expect(props.onFocus).toEqual(expect.any(Function)); - expect(props.onBlur).toEqual(expect.any(Function)); - expect(props.onChange).toEqual(expect.any(Function)); - expect(props.onSubmit).toEqual(expect.any(Function)); - expect(props.onClear).toEqual(expect.any(Function)); - expect(props.value).toEqual(expect.any(String)); - expect(props.screenReaderText).toEqual({ - label: 'search', - clearButton: 'clear search', - submitButton: 'submit search', - }); - expect(props.formAriaLabel).toEqual(undefined); - expect(props.className).toEqual(undefined); - expect(BUTTON_LOCATION_VARIANTS.includes(props.submitButtonLocation)).toEqual(true); + it('renders SearchField.Advanced component`', () => { + const { getByTestId } = render(); + const advancedComponent = getByTestId('advanced-component'); + expect(advancedComponent).toBeInTheDocument(); }); + it('should pass correct props to `SearchField.Label`', () => { const label = 'foobar'; let props = { ...baseProps, label }; - let wrapper = mount(); - - expect(wrapper.find(SearchField.Label).prop('children')).toEqual(label); - expect(wrapper.find('label').prop('children')).toEqual(label); + const { getByText, getByLabelText, rerender } = render(); + const labelElement = getByLabelText(label); + expect(labelElement).toBeInTheDocument(); props = { ...baseProps, screenReaderText: { label, submitButton: 'submit foobar' }, }; - - wrapper = mount(); - expect(wrapper.find('label').children().equals({label})).toBeTruthy(); + rerender(); + const srOnlyLabelElement = getByText(label); + expect(srOnlyLabelElement).toBeInTheDocument(); + expect(srOnlyLabelElement).toHaveClass('sr-only'); }); it('should pass correct props to `SearchField.Input`', () => { - const wrapper = mount(); - expect(wrapper.find(SearchField.Input).prop('placeholder')).toEqual('foobar'); - expect(wrapper.find('input').prop('placeholder')).toEqual('foobar'); - expect(wrapper.find('input').prop('data-testid')).toEqual('foo'); + const placeholder = 'foobar'; + const inputTestId = 'foo'; + const props = { ...baseProps, placeholder, inputProps: { 'data-testid': inputTestId } }; + const { getByTestId } = render(); + const inputElement = getByTestId(inputTestId); + expect(inputElement).toBeInTheDocument(); + expect(inputElement).toHaveAttribute('placeholder', placeholder); }); it('should use passed in initial `value` prop', () => { const value = 'foobar'; const props = { ...baseProps, value }; - const wrapper = mount(); - - expect(wrapper.find(SearchField.Advanced).prop('value')).toEqual(value); - expect(wrapper.find('input').prop('value')).toEqual(value); + const { getByRole } = render(); + const inputElement = getByRole('searchbox'); + expect(inputElement).toBeInTheDocument(); + expect(inputElement).toHaveValue(value); }); it('should use passed in `screenReaderText` prop', () => { @@ -85,134 +63,154 @@ describe(' with basic usage', () => { clearButton: 'borrar búsqueda', }; const props = { ...baseProps, screenReaderText }; - const wrapper = mount(); - const submitLabel = wrapper.find('button[type="submit"] .sr-only').text(); - expect(submitLabel).toEqual(screenReaderText.submitButton); - wrapper.find('input').simulate('change', { target: { value: 'foobar' } }); - const resetLabel = wrapper.find('button[type="reset"] .sr-only').text(); - expect(resetLabel).toEqual(screenReaderText.clearButton); + const { getByRole, getByLabelText } = render(); + const input = getByRole('searchbox', { target: 'submit' }); + const submitLabel = getByLabelText(screenReaderText.label); + expect(submitLabel).toBeInTheDocument(); + const submitButton = getByRole('button', { name: screenReaderText.submitButton, type: 'submit' }); + expect(submitButton).toBeInTheDocument(); + fireEvent.change(input, { target: { value: 'foobar' } }); + const resetButton = getByRole('button', { name: screenReaderText.clearButton, type: 'reset' }); + expect(resetButton).toBeInTheDocument(); }); + it('should add div if `submitButtonLocation` is passed', () => { - const wrapperDefault = mount(); - const wrapperExternal = mount(); - expect(wrapperDefault.find('.pgn__searchfield_wrapper').length).toEqual(0); - expect(wrapperExternal.find('.pgn__searchfield_wrapper').length).toEqual(1); + const { container } = render(); + expect(container.querySelector('.pgn__searchfield_wrapper')).toBeNull(); + const { container: containerExternal } = render(); + expect(containerExternal.querySelector('.pgn__searchfield_wrapper')).toBeInTheDocument(); }); describe('should fire', () => { it('focus handler', () => { const spy = jest.fn(); const props = { ...baseProps, onFocus: spy }; - const wrapper = mount(); - wrapper.find('input').simulate('focus'); + const { getByRole } = render(); + const inputElement = getByRole('searchbox'); + fireEvent.focus(inputElement); expect(spy).toHaveBeenCalledTimes(1); }); it('blur handler', () => { const spy = jest.fn(); const props = { ...baseProps, onBlur: spy }; - const wrapper = mount(); - wrapper.find('input').simulate('blur'); + const { getByRole } = render(); + const inputElement = getByRole('searchbox'); + fireEvent.blur(inputElement); expect(spy).toHaveBeenCalledTimes(1); }); it('change handler', () => { const spy = jest.fn(); const props = { ...baseProps, onChange: spy }; - const wrapper = mount(); - wrapper.find('input').simulate('change', { target: { value: 'foobar' } }); + const { getByRole } = render(); + const inputElement = getByRole('searchbox'); + fireEvent.change(inputElement, { target: { value: 'foobar' } }); expect(spy).toHaveBeenCalledTimes(1); }); it('clear handler', () => { const spy = jest.fn(); const props = { ...baseProps, onClear: spy }; - const wrapper = mount(); - wrapper.find('input').simulate('change', { target: { value: 'foobar' } }); - wrapper.find('button[type="reset"]').simulate('reset'); + const { getByRole } = render(); + const inputElement = getByRole('searchbox'); + fireEvent.change(inputElement, { target: { value: 'foobar' } }); + + const resetButton = getByRole('button', { type: 'reset' }); + fireEvent.click(resetButton); + expect(spy).toHaveBeenCalledTimes(1); }); it('submit handler on submit button click', () => { const spy = jest.fn(); const props = { ...baseProps, onSubmit: spy }; - const wrapper = mount(); - wrapper.find('input').simulate('change', { target: { value: 'foobar' } }); - wrapper.find('input').simulate('submit'); + const { getByRole } = render(); + const submitButton = getByRole('button', { type: 'submit' }); + fireEvent.change(submitButton, { target: { value: 'foobar' } }); + + fireEvent.click(submitButton); expect(spy).toHaveBeenCalledTimes(1); - expect(spy).toHaveBeenCalledWith('foobar'); }); }); describe('clear button', () => { it('should be visible with input value', () => { const props = { ...baseProps }; - const wrapper = mount(); - expect(wrapper.find('button[type="reset"]').exists()).toBeFalsy(); - wrapper.find('input').simulate('change', { target: { value: 'foobar' } }); - expect(wrapper.find('button[type="reset"]').exists()).toBeTruthy(); + const { queryByRole, getByRole } = render(); + const inputElement = getByRole('searchbox'); + expect(queryByRole('button', { name: 'clear search', type: 'reset' })).toBeNull(); + fireEvent.change(inputElement, { target: { value: 'foobar' } }); + expect(getByRole('button', { type: 'reset' })).toBeInTheDocument(); }); it('should clear input value when clicked', () => { const props = { ...baseProps }; - const wrapper = mount(); - wrapper.find('input').simulate('change', { target: { value: 'foobar' } }); - expect(wrapper.find('input').prop('value')).toEqual('foobar'); - wrapper.find('button[type="reset"]').simulate('reset'); - expect(wrapper.find('input').prop('value')).toEqual(''); + const { getByRole } = render(); + const inputElement = getByRole('searchbox', { target: 'submit' }); + fireEvent.change(inputElement, { target: { value: 'foobar' } }); + expect(inputElement).toHaveValue('foobar'); + const clearButton = getByRole('button', { type: 'reset' }); + fireEvent.click(clearButton); + expect(inputElement).toHaveValue(''); }); }); + describe('advanced usage', () => { it('should pass props to the clear button', () => { const buttonProps = { variant: 'inline' }; - const wrapper = mount( + const { getByRole } = render( , ); - wrapper.find('input').simulate('change', { target: { value: 'foobar' } }); - expect(wrapper.find('button').prop('variant')).toEqual(buttonProps.variant); + const inputElement = getByRole('searchbox'); + fireEvent.change(inputElement, { target: { value: 'foobar' } }); + const buttonClear = getByRole('button', { type: 'reset', variant: buttonProps.variant }); + expect(buttonClear).toHaveAttribute('variant', 'inline'); }); + it('should pass props to the label', () => { const labelProps = { variant: 'inline' }; - const wrapper = mount( + const { getByTestId } = render( - Labeled + Labeled , ); - expect(wrapper.find('label').prop('variant')).toEqual(labelProps.variant); + const label = getByTestId('label'); + expect(label).toHaveAttribute('variant', 'inline'); }); + it('should pass props to the submit button', () => { const buttonText = 'Some test text'; const buttonProps = { submitButtonLocation: 'external', buttonText, }; - const wrapper = mount( + const { getByText } = render( , ); - expect(wrapper.find('button').hasClass('pgn__searchfield__button')).toBe(true); - expect(wrapper.find('button').text().includes(buttonText)).toBe(true); + const submitButton = getByText(buttonText); + expect(submitButton).toBeInTheDocument(); + expect(submitButton).toHaveClass('pgn__searchfield__button'); }); + it('should pass variant to the submit button', () => { + const buttonText = 'Some test text'; const buttonProps = { submitButtonLocation: 'external', + buttonText, }; - const wrapperDefault = mount( - - - , - ); - const wrapperDark = mount( + const { getByText } = render( , ); - expect(wrapperDefault.find('button').hasClass('btn-primary')).toBe(true); - expect(wrapperDark.find('button').hasClass('btn-brand')).toBe(true); + const submitButton = getByText(buttonText); + expect(submitButton).toHaveClass('btn-brand'); }); }); }); diff --git a/src/SearchField/SearchFieldAdvanced.jsx b/src/SearchField/SearchFieldAdvanced.jsx index 004a3928894..333c631a5d8 100644 --- a/src/SearchField/SearchFieldAdvanced.jsx +++ b/src/SearchField/SearchFieldAdvanced.jsx @@ -31,6 +31,7 @@ function SearchFieldAdvanced(props) { formAriaLabel, disabled, submitButtonLocation, + ...rest } = props; const [hasFocus, setHasFocus] = useState(false); @@ -95,6 +96,7 @@ function SearchFieldAdvanced(props) { }, className, )} + {...rest} >
with basic usage should match the snapshot 1`] = `"
"`; +exports[` with basic usage should match the snapshot 1`] = ` +
+
+ +
+
+`; diff --git a/src/StatusAlert/StatusAlert.test.jsx b/src/StatusAlert/StatusAlert.test.jsx index 00330a444eb..3c2e30450c8 100644 --- a/src/StatusAlert/StatusAlert.test.jsx +++ b/src/StatusAlert/StatusAlert.test.jsx @@ -1,14 +1,10 @@ import React from 'react'; -import { mount } from 'enzyme'; import { render, screen, fireEvent } from '@testing-library/react'; +import '@testing-library/jest-dom/extend-expect'; + import StatusAlert from '.'; import { Button } from '..'; -const statusAlertOpen = (isOpen, wrapper) => { - wrapper.update(); - expect(wrapper.find('.alert').hasClass('show')).toEqual(isOpen); - expect(wrapper.find('StatusAlert').state('open')).toEqual(isOpen); -}; const dialog = 'Status Alert dialog'; const defaultProps = { dialog, @@ -16,8 +12,6 @@ const defaultProps = { open: true, }; -let wrapper; - describe('', () => { describe('correct rendering', () => { it('renders default view', () => { @@ -46,11 +40,21 @@ describe('', () => { describe('props received correctly', () => { it('component receives props', () => { - wrapper = mount( {}} />); + const onCloseMock = jest.fn(); + + const { rerender, getByRole } = render( + , + ); + const alertElement = getByRole('alert', { hidden: true }); + expect(alertElement).not.toHaveClass('show'); - statusAlertOpen(false, wrapper); - wrapper.setProps({ open: true }); - statusAlertOpen(true, wrapper); + rerender(); + expect(screen.getByRole('alert')).toHaveClass('show'); + + expect(onCloseMock).not.toHaveBeenCalled(); + const closeButton = getByRole('button'); + fireEvent.click(closeButton); + expect(onCloseMock).toHaveBeenCalled(); }); it('component receives props and ignores prop change', () => { @@ -67,10 +71,6 @@ describe('', () => { }); describe('close functions properly', () => { - beforeEach(() => { - wrapper = mount(); - }); - it('closes when x button pressed', () => { render(); const closeButton = screen.getByRole('button'); @@ -107,11 +107,6 @@ describe('', () => { }); describe('invalid keystrokes do nothing', () => { - // beforeEach(() => { - // const app = document.createElement('div'); - // document.body.appendChild(app); - // wrapper = mount(, { attachTo: app }); - // }); beforeEach(() => { render(); }); @@ -134,21 +129,23 @@ describe('', () => { }); describe('focus functions properly', () => { it('focus function changes focus', () => { - const app = document.createElement('div'); - document.body.appendChild(app); - wrapper = mount( -
, - { attachTo: app }, + render( +
+
, ); - const buttons = wrapper.find('button'); + const buttons = screen.getAllByRole('button'); + const xButton = buttons[1]; + const statusAlertButton = buttons[0]; - // move focus away from default StatusAlert xButton - buttons.at(0).simulate('click'); - expect(buttons.at(0).html()).toEqual(document.activeElement.outerHTML); + // Move focus away from the default StatusAlert xButton + fireEvent.click(xButton); + expect(xButton.innerHTML).toEqual(document.activeElement.innerHTML); - const statusAlert = wrapper.find('StatusAlert').instance(); - statusAlert.focus(); - expect(buttons.at(1).html()).toEqual(document.activeElement.outerHTML); + fireEvent.focus(statusAlertButton); + statusAlertButton.focus(); + expect(statusAlertButton.innerHTML).toEqual(document.activeElement.innerHTML); }); }); }); diff --git a/src/Table/Table.test.jsx b/src/Table/Table.test.jsx index 8529c005bd9..680939d9747 100644 --- a/src/Table/Table.test.jsx +++ b/src/Table/Table.test.jsx @@ -1,5 +1,8 @@ import React from 'react'; -import { mount } from 'enzyme'; +import { + render, screen, fireEvent, within, +} from '@testing-library/react'; +import '@testing-library/jest-dom/extend-expect'; import Table from './index'; @@ -79,111 +82,110 @@ const propsWithColWidths = { describe('', () => { describe('renders', () => { - const wrapper = mount(
).find('Table'); - it('with display columns in the right order', () => { - wrapper.find('th').forEach((th, i) => { - expect(th.text()).toEqual(props.columns[i].label); + render(
); + props.columns.forEach((column) => { + const columnHeader = screen.getByText(column.label); + expect(columnHeader).toBeInTheDocument(); }); }); it('with data in the same order as the columns', () => { - wrapper.find('tr').at(1).find('td').forEach((td, i) => { - let parsed = Number(td.text()); - if (Number.isNaN(parsed)) { parsed = td.text(); } - expect(parsed).toEqual(props.data[0][props.columns[i].key]); + render(
); + + const rows = screen.getAllByTestId('table-row'); + rows.forEach((row, rowIndex) => { + const cells = within(row).getAllByTestId('table-cell'); + cells.forEach((cell, columnIndex) => { + const cellContent = cell.textContent.trim(); + let parsed = Number(cellContent); + if (Number.isNaN(parsed)) { + parsed = cellContent; + } + expect(parsed).toEqual(props.data[rowIndex][props.columns[columnIndex].key]); + }); }); }); - - it('with correct initial state', () => { - expect(wrapper.state('sortedColumn')).toEqual(''); - expect(wrapper.state('sortDirection')).toEqual(''); - }); }); describe('that is non-sortable renders', () => { - const wrapper = mount(
).find('Table'); - it('it sets column headers correctly even with hidden prop', () => { - const tableHeadings = wrapper.find('th'); + render(
); + const tableHeadings = screen.getAllByRole('columnheader'); let hiddenHeader; tableHeadings.forEach((th, i) => { - expect(th.text()).toEqual(sortableProps.columns[i].label); + expect(th.textContent).toEqual(sortableProps.columns[i].label); if (sortableProps.columns[i].hideHeader) { hiddenHeader = sortableProps.columns[i].label; } }); - expect(tableHeadings.find('span').text()).toEqual(hiddenHeader); + + const spanElement = screen.getByText(hiddenHeader); + expect(spanElement).toBeInTheDocument(); }); it('without sortable columns', () => { - const tableHeadings = wrapper.find('th'); + render(
); + const tableHeadings = screen.getAllByRole('columnheader'); tableHeadings.forEach((heading) => { - expect((heading).hasClass('sortable')).toEqual(false); + expect(heading).not.toHaveClass('sortable'); }); }); it('without column buttons', () => { - const buttons = wrapper.find('button'); + render(
); + const buttons = screen.queryAllByRole('button'); expect(buttons).toHaveLength(0); }); - - it('with correct initial state', () => { - expect(wrapper.state('sortedColumn')).toEqual(''); - expect(wrapper.state('sortDirection')).toEqual(''); - }); }); describe('that is sortable and has mixed columns renders', () => { - let wrapper = mount(
).find('Table'); + let wrapper; + + beforeEach(() => { + wrapper = render(
).container; + }); it('with sortable classname on correct headings', () => { - const tableHeadings = wrapper.find('th'); + const tableHeadings = wrapper.querySelectorAll('th'); expect(tableHeadings).toHaveLength(sortableProps.columns.length); - expect(tableHeadings.at(0).hasClass('sortable')).toEqual(true); - expect(tableHeadings.at(1).hasClass('sortable')).toEqual(true); - expect(tableHeadings.at(2).hasClass('sortable')).toEqual(false); + expect(tableHeadings[0]).toHaveClass('sortable'); + expect(tableHeadings[1]).toHaveClass('sortable'); + expect(tableHeadings[2]).not.toHaveClass('sortable'); }); it('with sr-only classname on correct headings', () => { - const srOnly = wrapper.find('.sr-only'); + const srOnly = wrapper.querySelectorAll('.sr-only'); expect(srOnly).toHaveLength(sortableProps.columns.length - 1); - expect((srOnly).at(0).hasClass('sr-only')).toEqual(true); - expect((srOnly).at(1).hasClass('sr-only')).toEqual(true); + expect(srOnly[0]).toHaveClass('sr-only'); + expect(srOnly[1]).toHaveClass('sr-only'); }); it('with correct initial sr-only text on correct headings', () => { - const headings = wrapper.find('.sr-only'); - - expect(headings.at(0).text()).toEqual(' sort descending'); - expect(headings.at(1).text()).toEqual(' click to sort'); - }); + const headings = wrapper.querySelectorAll('.sr-only'); - it('with correct initial state', () => { - expect(wrapper.find('Table').state('sortedColumn')).toEqual(sortableProps.defaultSortedColumn); - expect(wrapper.find('Table').state('sortDirection')).toEqual(sortableProps.defaultSortDirection); + expect(headings[0]).toHaveTextContent('sort descending'); + expect(headings[1]).toHaveTextContent('click to sort'); }); - wrapper = mount(
); - it('with correct column buttons', () => { - const buttons = wrapper.find('button'); + const buttons = wrapper.querySelectorAll('button'); expect(buttons).toHaveLength(2); - expect(buttons.at(0).hasClass('btn-header')).toBe(true); - expect(buttons.at(1).hasClass('btn-header')).toBe(true); + expect(buttons[0].classList).toContain('btn-header'); + expect(buttons[1].classList).toContain('btn-header'); }); it('with correct initial sort icons', () => { - const buttons = wrapper.find('button'); + const buttons = wrapper.querySelectorAll('button'); - expect(buttons.find('.fa')).toHaveLength(sortableProps.columns.length - 2); - expect(buttons.at(0).find('.fa-sort-desc')).toHaveLength(1); - expect(buttons.at(1).find('.fa-sort')).toHaveLength(1); + expect(buttons).toHaveLength(sortableProps.columns.length - 2); + expect(buttons[0].querySelector('.fa')).toHaveClass('fa-sort-desc'); + expect(buttons[1].querySelector('.fa')).toHaveClass('fa-sort'); }); }); @@ -194,9 +196,9 @@ describe('
', () => { let x2Spy; beforeEach(() => { - wrapper = mount(
); + wrapper = render(
).container; - buttons = wrapper.find('button'); + buttons = wrapper.querySelectorAll('button'); numSpy = jest.fn(); x2Spy = jest.fn(); @@ -205,90 +207,66 @@ describe('
', () => { }); it('changes sort icons appropriately on click', () => { - buttons.at(0).simulate('click'); - buttons = wrapper.find('button'); - - expect(buttons.at(0).find('.fa')).toHaveLength(1); - expect(buttons.at(0).find('.fa-sort-asc')).toHaveLength(1); - expect(buttons.at(0).find('.fa-sort-desc')).toHaveLength(0); - expect(buttons.at(0).find('.fa-sort')).toHaveLength(0); - - expect(buttons.at(1).find('.fa')).toHaveLength(1); - expect(buttons.at(1).find('.fa-sort-asc')).toHaveLength(0); - expect(buttons.at(1).find('.fa-sort-desc')).toHaveLength(0); - expect(buttons.at(1).find('.fa-sort')).toHaveLength(1); - - buttons.at(1).simulate('click'); - buttons = wrapper.find('button'); - - expect(buttons.at(0).find('.fa')).toHaveLength(1); - expect(buttons.at(0).find('.fa-sort-asc')).toHaveLength(0); - expect(buttons.at(0).find('.fa-sort-desc')).toHaveLength(0); - expect(buttons.at(0).find('.fa-sort')).toHaveLength(1); - - expect(buttons.at(1).find('.fa')).toHaveLength(1); - expect(buttons.at(1).find('.fa-sort-asc')).toHaveLength(0); - expect(buttons.at(1).find('.fa-sort-desc')).toHaveLength(1); - expect(buttons.at(1).find('.fa-sort')).toHaveLength(0); - }); + fireEvent.click(buttons[0]); + buttons = wrapper.querySelectorAll('button'); - it('changes sr-only text appropriately on click', () => { - const headings = wrapper.find('.sr-only'); + expect(buttons[0].querySelector('.fa')).toHaveClass('fa-sort-asc'); + expect(buttons[0].querySelector('.fa')).not.toHaveClass('fa-sort-desc'); + expect(buttons[0].querySelector('.fa')).not.toHaveClass('fa-sort'); - buttons.at(0).simulate('click'); - buttons = wrapper.find('button'); + expect(buttons[1].querySelector('.fa')).toHaveClass('fa-sort'); + expect(buttons[1].querySelector('.fa')).not.toHaveClass('fa-sort-asc'); + expect(buttons[1].querySelector('.fa')).not.toHaveClass('fa-sort-desc'); - expect(headings.at(0).text()).toEqual(' sort ascending'); - expect(headings.at(1).text()).toEqual(' click to sort'); + fireEvent.click(buttons[1]); + buttons = wrapper.querySelectorAll('button'); - buttons.at(1).simulate('click'); + expect(buttons[0].querySelector('.fa')).toHaveClass('fa-sort'); + expect(buttons[0].querySelector('.fa')).not.toHaveClass('fa-sort-asc'); + expect(buttons[0].querySelector('.fa')).not.toHaveClass('fa-sort-desc'); - expect(headings.at(0).text()).toEqual(' click to sort'); - expect(headings.at(1).text()).toEqual(' sort descending'); + expect(buttons[1].querySelector('.fa')).toHaveClass('fa-sort-desc'); + expect(buttons[1].querySelector('.fa')).not.toHaveClass('fa-sort'); + expect(buttons[1].querySelector('.fa')).not.toHaveClass('fa-sort-asc'); }); - it('changes state appropriately on click', () => { - buttons.at(0).simulate('click'); - buttons = wrapper.find('button'); - - expect(wrapper.find('Table').state('sortedColumn')).toEqual(sortableProps.defaultSortedColumn); - expect(wrapper.find('Table').state('sortDirection')).toEqual('asc'); + it('changes sr-only text appropriately on click', () => { + const headings = wrapper.querySelectorAll('.sr-only'); - buttons.at(0).simulate('click'); - buttons = wrapper.find('button'); + fireEvent.click(buttons[0]); + buttons = wrapper.querySelectorAll('button'); - expect(wrapper.find('Table').state('sortedColumn')).toEqual(sortableProps.defaultSortedColumn); - expect(wrapper.find('Table').state('sortDirection')).toEqual('desc'); + expect(headings[0]).toHaveTextContent('sort ascending'); + expect(headings[1]).toHaveTextContent('click to sort'); - buttons.at(1).simulate('click'); - buttons = wrapper.find('button'); + fireEvent.click(buttons[1]); - expect(wrapper.find('Table').state('sortedColumn')).toEqual(sortableProps.columns[1].key); - expect(wrapper.find('Table').state('sortDirection')).toEqual('desc'); + expect(headings[0]).toHaveTextContent('click to sort'); + expect(headings[1]).toHaveTextContent('sort descending'); }); it('calls onSort function correctly on click', () => { expect(numSpy).toHaveBeenCalledTimes(0); expect(x2Spy).toHaveBeenCalledTimes(0); - buttons.at(0).simulate('click'); - buttons = wrapper.find('button'); + fireEvent.click(buttons[0]); + buttons = wrapper.querySelectorAll('button'); expect(numSpy).toHaveBeenCalledTimes(1); expect(x2Spy).toHaveBeenCalledTimes(0); expect(numSpy).toBeCalledWith('asc'); - buttons.at(0).simulate('click'); - buttons = wrapper.find('button'); + fireEvent.click(buttons[0]); + buttons = wrapper.querySelectorAll('button'); expect(numSpy).toHaveBeenCalledTimes(2); expect(x2Spy).toHaveBeenCalledTimes(0); expect(numSpy).toBeCalledWith('desc'); - buttons.at(1).simulate('click'); - buttons = wrapper.find('button'); + fireEvent.click(buttons[1]); + buttons = wrapper.querySelectorAll('button'); expect(numSpy).toHaveBeenCalledTimes(2); expect(x2Spy).toHaveBeenCalledTimes(1); @@ -296,79 +274,103 @@ describe('
', () => { expect(x2Spy).toBeCalledWith('desc'); }); }); + describe('that is fixed', () => { - const wrapper = mount(
); + let wrapper; + + beforeEach(() => { + wrapper = render(
).container; + }); it('with col width classnames on headings', () => { - const tableHeadings = wrapper.find('th'); + const tableHeadings = wrapper.querySelectorAll('th'); expect(tableHeadings).toHaveLength(fixedProps.columns.length); - expect(tableHeadings.at(0).hasClass('col-4')).toEqual(true); - expect(tableHeadings.at(1).hasClass('col-2')).toEqual(true); - expect(tableHeadings.at(2).hasClass('col-3')).toEqual(true); + expect(tableHeadings[0]).toHaveClass('col-4'); + expect(tableHeadings[1]).toHaveClass('col-2'); + expect(tableHeadings[2]).toHaveClass('col-3'); }); it('with col width classnames on cells', () => { - const tableCells = wrapper.find('td'); + const tableCells = wrapper.querySelectorAll('td'); expect(tableCells).toHaveLength(fixedProps.columns.length * fixedProps.data.length); - expect(tableCells.at(0).hasClass('col-4')).toEqual(true); - expect(tableCells.at(1).hasClass('col-2')).toEqual(true); - expect(tableCells.at(2).hasClass('col-3')).toEqual(true); + expect(tableCells[0]).toHaveClass('col-4'); + expect(tableCells[1]).toHaveClass('col-2'); + expect(tableCells[2]).toHaveClass('col-3'); }); + it('with fixed-related classnames on head, body, and rows', () => { - const thead = wrapper.find('thead'); - const tbody = wrapper.find('tbody'); - const tr = wrapper.find('tr'); + const thead = wrapper.querySelector('thead'); + const tbody = wrapper.querySelector('tbody'); + const tr = wrapper.querySelectorAll('tr')[0]; - expect(thead.hasClass('d-inline')).toEqual(true); - expect(tbody.hasClass('d-inline')).toEqual(true); - expect(tr.at(0).hasClass('d-flex')).toEqual(true); + expect(thead).toHaveClass('d-inline'); + expect(tbody).toHaveClass('d-inline'); + expect(tr).toHaveClass('d-flex'); }); }); + describe('that is not fixed with col widths', () => { - const wrapper = mount(
); + let wrapper; + + beforeEach(() => { + wrapper = render(
).container; + }); it('with no col width classnames on headings', () => { - const tableHeadings = wrapper.find('th'); + const tableHeadings = wrapper.querySelectorAll('th'); expect(tableHeadings).toHaveLength(fixedProps.columns.length); - expect(tableHeadings.at(0).hasClass('col-4')).toEqual(false); - expect(tableHeadings.at(1).hasClass('col-2')).toEqual(false); - expect(tableHeadings.at(2).hasClass('col-3')).toEqual(false); + expect(tableHeadings[0]).not.toHaveClass('col-4'); + expect(tableHeadings[1]).not.toHaveClass('col-2'); + expect(tableHeadings[2]).not.toHaveClass('col-3'); }); it('with no col width classnames on cells', () => { - const tableCells = wrapper.find('td'); + const tableCells = wrapper.querySelectorAll('td'); expect(tableCells).toHaveLength(fixedProps.columns.length * fixedProps.data.length); - expect(tableCells.at(0).hasClass('col-4')).toEqual(false); - expect(tableCells.at(1).hasClass('col-2')).toEqual(false); - expect(tableCells.at(2).hasClass('col-3')).toEqual(false); + expect(tableCells[0]).not.toHaveClass('col-4'); + expect(tableCells[1]).not.toHaveClass('col-2'); + expect(tableCells[2]).not.toHaveClass('col-3'); }); + it('with no fixed-related classnames on head, body, and rows', () => { - const thead = wrapper.find('thead'); - const tbody = wrapper.find('tbody'); - const tr = wrapper.find('tr'); + const thead = wrapper.querySelector('thead'); + const tbody = wrapper.querySelector('tbody'); + const tr = wrapper.querySelectorAll('tr')[0]; - expect(thead.hasClass('d-inline')).toEqual(false); - expect(tbody.hasClass('d-inline')).toEqual(false); - expect(tr.at(0).hasClass('d-flex')).toEqual(false); + expect(thead).not.toHaveClass('d-inline'); + expect(tbody).not.toHaveClass('d-inline'); + expect(tr).not.toHaveClass('d-flex'); }); }); + describe('renders row headers', () => { - const wrapper = mount(
); + let wrapper; + + beforeEach(() => { + wrapper = render(
).container; + }); it('with the row header as th with row scope', () => { - wrapper.find('tbody').at(0).find('th').forEach((th) => { - expect(th.getElement().key).toEqual('num'); - expect(th.prop('scope')).toEqual('row'); + const tableHeadings = wrapper.querySelectorAll('th'); + + tableHeadings.forEach((th) => { + if (th.getAttribute('data-colkey') === 'num') { + expect(th.getAttribute('scope')).toEqual('row'); + } }); }); it('with all other columns unchanged', () => { - wrapper.find('tbody').at(0).find('td').forEach((th) => { - expect(th.getElement().key).not.toEqual('num'); + const tableCells = wrapper.querySelectorAll('td'); + + tableCells.forEach((td) => { + if (td.getAttribute('data-colkey') !== 'num') { + expect(td.getAttribute('scope')).not.toEqual('row'); + } }); }); }); diff --git a/src/Tabs/index.jsx b/src/Tabs/index.jsx index 409315c146c..8827601a8dc 100644 --- a/src/Tabs/index.jsx +++ b/src/Tabs/index.jsx @@ -142,6 +142,7 @@ function Tabs({ childrenList.splice(indexOfOverflowStart, 0, ( diff --git a/src/asInput/asInput.test.jsx b/src/asInput/asInput.test.jsx index 0c12da06214..31caa205cee 100644 --- a/src/asInput/asInput.test.jsx +++ b/src/asInput/asInput.test.jsx @@ -1,6 +1,6 @@ -/* eslint-disable react/prop-types */ import React from 'react'; -import { mount } from 'enzyme'; +import { render, fireEvent } from '@testing-library/react'; +import '@testing-library/jest-dom/extend-expect'; import asInput, { getDisplayName } from './index'; @@ -38,21 +38,28 @@ describe('asInput()', () => { ...baseProps, value: 'foofoo', }; - const wrapper = mount(); - expect(wrapper.find('label').text()).toEqual(props.label); - expect(wrapper.find('#description-asInput1').text()).toEqual(props.description); - expect(wrapper.prop('value')).toEqual(props.value); + const { getByText } = render(); + const label = getByText(props.label); + const description = getByText(baseProps.description); + const input = getByText(baseProps.label); + + expect(label).toBeInTheDocument(); + expect(description).toBeInTheDocument(); + expect(input).toBeInTheDocument(); }); it('creates generic prop id', () => { const props = { ...baseProps, }; - const wrapper = mount(); - expect(wrapper.find('asInput(testComponent)').state('id')).toContain('asInput'); - expect(wrapper.find('label').prop('id')).toContain('asInput'); - expect(wrapper.find('input').prop('id')).toContain('asInput'); - expect(wrapper.find('small').prop('id')).toContain('asInput'); + const { getByText } = render(); + const labelElement = getByText(baseProps.label); + const inputElement = labelElement.nextSibling; + const smallElement = getByText(baseProps.description); + + expect(labelElement.getAttribute('id')).toContain('asInput'); + expect(inputElement.getAttribute('id')).toContain('asInput'); + expect(smallElement.getAttribute('id')).toContain('asInput'); }); it('creates generic prop id when passed null id value', () => { @@ -61,36 +68,43 @@ describe('asInput()', () => { ...baseProps, id: testId, }; - const wrapper = mount(); - expect(wrapper.find('asInput(testComponent)').state('id')).toContain('asInput'); - expect(wrapper.find('label').prop('id')).toContain('asInput'); - expect(wrapper.find('input').prop('id')).toContain('asInput'); - expect(wrapper.find('small').prop('id')).toContain('asInput'); + const { getByText } = render(); + const labelElement = getByText(baseProps.label); + const inputElement = labelElement.nextSibling; + const smallElement = getByText(baseProps.description); + + expect(labelElement.getAttribute('id')).toContain('asInput'); + expect(inputElement.getAttribute('id')).toContain('asInput'); + expect(smallElement.getAttribute('id')).toContain('asInput'); }); - it('uses passed in prop id', () => { + it('uses passed-in prop id', () => { const testId = 'testId'; const props = { ...baseProps, id: testId, }; - const wrapper = mount(); - expect(wrapper.find('asInput(testComponent)').state('id')).toEqual(testId); - expect(wrapper.find('label').prop('id')).toEqual(`label-${testId}`); - expect(wrapper.find('input').prop('id')).toEqual(testId); - expect(wrapper.find('small').prop('id')).toEqual(`description-${testId}`); + const { getByText } = render(); + const labelElement = getByText(baseProps.label); + const inputElement = labelElement.nextSibling; + const smallElement = getByText(baseProps.description); + + expect(labelElement.getAttribute('id')).toEqual(`label-${testId}`); + expect(inputElement.getAttribute('id')).toEqual(testId); + expect(smallElement.getAttribute('id')).toEqual(`description-${testId}`); }); - it('uses label as element type', () => { - const testLabel = (Label); + it('uses label as an element type', () => { + const testLabel = Label; const props = { ...baseProps, label: testLabel, }; - const wrapper = mount(); - expect(wrapper.find('label').children()).toHaveLength(1); - expect(wrapper.find('label').children().at(0).text()).toEqual('Label'); - expect(wrapper.find('label').children().at(0).prop('lang')).toEqual('en'); + const { getByText } = render(); + const label = getByText('Label').parentElement; + expect(label).toBeInTheDocument(); + expect(label.children).toHaveLength(1); + expect(label.children[0]).toHaveAttribute('lang', 'en'); }); it('overrides state value when props value changes', () => { @@ -100,12 +114,14 @@ describe('asInput()', () => { ...baseProps, value: initValue, }; - const wrapper = mount(); - expect(wrapper.find('asInput(testComponent)').state('value')).toEqual(initValue); - wrapper.setProps({ - value: newValue, - }); - expect(wrapper.find('asInput(testComponent)').state('value')).toEqual(newValue); + const { getByText, rerender } = render(); + + expect(getByText(baseProps.label).nextSibling.getAttribute('value')).toEqual(initValue); + + props.value = newValue; + rerender(); + + expect(getByText(baseProps.label).nextSibling.getAttribute('value')).toEqual(newValue); }); describe('fires', () => { @@ -115,8 +131,10 @@ describe('asInput()', () => { ...baseProps, onBlur: spy, }; - const wrapper = mount(); - wrapper.find('input').simulate('blur'); + const { getByText } = render(); + const input = getByText(baseProps.label).nextSibling; + + fireEvent.blur(input); expect(spy).toHaveBeenCalledTimes(1); }); @@ -126,8 +144,10 @@ describe('asInput()', () => { ...baseProps, onChange: spy, }; - const wrapper = mount(); - wrapper.find('input').simulate('change'); + const { getByText } = render(); + const input = getByText(baseProps.label).nextSibling; + + fireEvent.change(input, { target: { value: 'new' } }); expect(spy).toHaveBeenCalledTimes(1); }); @@ -137,321 +157,274 @@ describe('asInput()', () => { ...baseProps, onKeyPress: spy, }; - const wrapper = mount(); - wrapper.find('input').simulate('keypress'); + const { getByText } = render(); + const input = getByText(baseProps.label).nextSibling; + + fireEvent.keyPress(input, { key: 'Enter', code: 'Enter', charCode: 13 }); expect(spy).toHaveBeenCalledTimes(1); }); + }); - describe('validation properties', () => { - it('ignores props if validator is defined', () => { - const spy = jest.fn(); - spy.mockReturnValue({ isValid: false }); - const props = { - ...baseProps, - validator: spy, - isValid: false, - }; - const wrapper = mount(); - expect(wrapper.find('asInput(testComponent)').state('isValid')).toEqual(true); // default is true, ignoring our prop + describe('validation properties', () => { + it('uses props if validator becomes undefined', () => { + const spy = jest.fn(); + spy.mockReturnValue({ validationMessage: 'Spy' }); + const props = { + ...baseProps, + validator: spy, + isValid: false, + validationMessage: 'Prop', + dangerIconDescription: 'Prop', + }; + const { rerender, getByText, queryByText } = render(); - wrapper.setProps({ isValid: true }); - wrapper.find('input').simulate('blur'); // trigger validation - expect(wrapper.find('asInput(testComponent)').state('isValid')).toEqual(false); // validator set false, ignoring our prop + expect(queryByText('Spy')).not.toBeInTheDocument(); + expect(queryByText('Prop')).not.toBeInTheDocument(); - wrapper.setProps({ isValid: true }); - expect(wrapper.find('asInput(testComponent)').state('isValid')).toEqual(false); // resetting prop changes nothing - }); + props.validator = null; + rerender(); - it('ignores validationMessage prop if validator is defined', () => { - const spy = jest.fn(); - spy.mockReturnValue({ validationMessage: 'Spy' }); - const props = { - ...baseProps, - validator: spy, - validationMessage: 'Prop', - }; - const wrapper = mount(); - expect(wrapper.find('asInput(testComponent)').state('validationMessage')).toEqual(''); // default is '', ignoring our prop + expect(getByText('Prop')).toBeInTheDocument(); + }); - wrapper.find('input').simulate('blur'); // trigger validation - expect(wrapper.find('asInput(testComponent)').state('validationMessage')).toEqual('Spy'); // validator set Spy, ignoring our prop + it('uses validationMessage as element type', () => { + const testMessage = Validation Message; + const props = { + ...baseProps, + isValid: false, + validationMessage: testMessage, + }; + const { getByText } = render(); + const validationMessage = getByText('Validation Message'); + expect(validationMessage).toBeInTheDocument(); + expect(validationMessage).toHaveAttribute('lang', 'en'); + }); - wrapper.setProps({ validationMessage: 'Reset' }); - expect(wrapper.find('asInput(testComponent)').state('validationMessage')).toEqual('Spy'); // resetting prop changes nothing - }); + it('uses isValid to display validation message', () => { + const props = { + ...baseProps, + isValid: false, + validationMessage: 'Nope!', + }; + const { rerender, getByText, queryByText } = render(); + const errorElement = getByText('Nope!'); + expect(errorElement).toBeInTheDocument(); - it('ignores dangerIconDescription prop if validator is defined', () => { - const spy = jest.fn(); - spy.mockReturnValue({ dangerIconDescription: 'Spy' }); - const props = { - ...baseProps, - validator: spy, - dangerIconDescription: 'Prop', - }; - const wrapper = mount(); - expect(wrapper.find('asInput(testComponent)').state('dangerIconDescription')).toEqual(''); // default is '', ignoring our prop + props.validationMessage = 'New Message'; + rerender(); + expect(getByText('New Message')).toBeInTheDocument(); - wrapper.find('input').simulate('blur'); // trigger validation - expect(wrapper.find('asInput(testComponent)').state('dangerIconDescription')).toEqual('Spy'); // validator set Spy, ignoring our prop + props.isValid = true; + rerender(); + expect(queryByText('New Message')).not.toBeInTheDocument(); + }); - wrapper.setProps({ dangerIconDescription: 'Reset' }); - expect(wrapper.find('asInput(testComponent)').state('dangerIconDescription')).toEqual('Spy'); // resetting prop changes nothing - }); + it('uses isValid to display validation message and danger icon with danger theme', () => { + const props = { + ...baseProps, + themes: ['danger'], + isValid: false, + validationMessage: 'Nope!', + dangerIconDescription: 'Error ', + }; + const { rerender, getByText, getByTestId } = render(); + const validationMessage = getByTestId('validation-message'); + expect(validationMessage.textContent).toEqual('Error Nope!'); - it('uses props if validator becomes undefined', () => { - const spy = jest.fn(); - spy.mockReturnValue({ validationMessage: 'Spy' }); - const props = { - ...baseProps, - validator: spy, - isValid: false, - validationMessage: 'Prop', - dangerIconDescription: 'Prop', - }; - const wrapper = mount(); - expect(wrapper.find('asInput(testComponent)').state('validationMessage')).toEqual(''); - expect(wrapper.find('asInput(testComponent)').state('dangerIconDescription')).toEqual(''); + const dangerIcon = getByText('Error'); + expect(dangerIcon).toBeInTheDocument(); + + props.validationMessage = 'New Message'; + rerender(); + expect(validationMessage.textContent).toEqual('Error New Message'); + + props.dangerIconDescription = 'Danger, Will Robinson! '; + rerender(); + expect(validationMessage.textContent).toEqual('Danger, Will Robinson! New Message'); + + props.isValid = true; + rerender(); + expect(validationMessage.textContent).toEqual(''); + }); + }); + + describe('validator', () => { + it('on blur', () => { + const spy = jest.fn(); + spy.mockReturnValueOnce({ isValid: true }); + const props = { + ...baseProps, + validator: spy, + }; + const { getByText } = render(); + const input = getByText(baseProps.label).nextSibling; + + fireEvent.blur(input); + + expect(spy).toHaveBeenCalledTimes(1); + }); + + describe('and display error message when invalid', () => { + const spy = jest.fn(); + const validationResult = { + isValid: false, + validationMessage: 'Invalid!!1', + }; + const props = { + ...baseProps, + validator: spy, + }; - wrapper.setProps({ validator: null }); - expect(wrapper.find('asInput(testComponent)').state('validationMessage')).toEqual('Prop'); - expect(wrapper.find('asInput(testComponent)').state('dangerIconDescription')).toEqual('Prop'); + beforeEach(() => { + spy.mockReturnValueOnce(validationResult); }); - it('uses validationMessage as element type', () => { - const testMessage = (Validation Message); - const props = { - ...baseProps, - isValid: false, - validationMessage: testMessage, - }; - const wrapper = mount(); - expect(wrapper.find('asInput(testComponent)').state('validationMessage')).toEqual(testMessage); + afterEach(() => { + spy.mockClear(); }); - it('uses isValid to display validation message', () => { - const props = { - ...baseProps, - isValid: false, - validationMessage: 'Nope!', - }; - const wrapper = mount(); - const err = wrapper.find('.invalid-feedback'); - expect(err.text()).toEqual('Nope!'); + it('without theme', () => { + const { getByText } = render(); + const input = getByText(baseProps.label).nextSibling; + + fireEvent.blur(input); - wrapper.setProps({ validationMessage: 'New Message' }); - expect(err.text()).toEqual('New Message'); + expect(spy).toHaveBeenCalledTimes(1); - wrapper.setProps({ isValid: true }); - expect(err.text()).toEqual(''); + const err = getByText(validationResult.validationMessage); + expect(err).toBeInTheDocument(); }); - it('uses isValid to display validation message and danger icon with danger theme', () => { - const props = { - ...baseProps, + it('with danger theme', () => { + const { getByText, getByTestId, rerender } = render(); + const input = getByText(baseProps.label).nextSibling; + fireEvent.blur(input); + expect(spy).toHaveBeenCalledTimes(1); + + const updatedProps = { + ...props, themes: ['danger'], - isValid: false, - validationMessage: 'Nope!', - dangerIconDescription: 'Error ', }; - const wrapper = mount(); - const err = wrapper.find('.invalid-feedback'); - expect(err.text()).toEqual('Error Nope!'); + validationResult.dangerIconDescription = 'Error'; + rerender(); - wrapper.setProps({ validationMessage: 'New Message' }); - expect(err.text()).toEqual('Error New Message'); + const err = getByTestId('validation-message'); - wrapper.setProps({ dangerIconDescription: 'Danger, Will Robinson! ' }); - expect(err.text()).toEqual('Danger, Will Robinson! New Message'); - - wrapper.setProps({ isValid: true }); - expect(err.text()).toEqual(''); + expect(err).toBeInTheDocument(); + expect(err.textContent).toEqual(validationResult.validationMessage); }); }); + }); - describe('validator', () => { - it('on blur', () => { - const spy = jest.fn(); - spy.mockReturnValueOnce({ isValid: true }); - const props = { - ...baseProps, - validator: spy, - }; - const wrapper = mount(); - wrapper.find('input').simulate('blur'); - expect(spy).toHaveBeenCalledTimes(1); - }); + it('displays inline', () => { + const props = { + ...baseProps, + inline: true, + }; + const { getByText } = render(); + const inputComponent = getByText(props.label).parentElement; + expect(inputComponent.classList.contains('form-inline')).toEqual(true); + }); - describe('and display error message when invalid', () => { - let spy; - let validationResult; - let wrapper; - - beforeEach(() => { - spy = jest.fn(); - validationResult = { - isValid: false, - validationMessage: 'Invalid!!1', - }; - spy.mockReturnValueOnce(validationResult); - const props = { - ...baseProps, - validator: spy, - }; - wrapper = mount(); - }); - - it('without theme', () => { - wrapper.find('input').simulate('blur'); - expect(spy).toHaveBeenCalledTimes(1); - const err = wrapper.find('.invalid-feedback'); - expect(err.exists()).toEqual(true); - expect(err.text()).toEqual(validationResult.validationMessage); - }); - - it('with danger theme', () => { - wrapper.setProps({ themes: ['danger'] }); - validationResult.dangerIconDescription = 'Error'; - - // error div exists on the page when form is loaded - const err = wrapper.find('.invalid-feedback'); - expect(err.exists()).toEqual(true); - expect(err.hasClass('invalid-feedback')).toEqual(true); - expect(err.prop('aria-live')).toEqual('polite'); - expect(err.text()).toEqual(''); - - wrapper.find('input').simulate('blur'); - expect(spy).toHaveBeenCalledTimes(1); - expect(err.exists()).toEqual(true); - expect(err.text()).toEqual(validationResult.dangerIconDescription - + validationResult.validationMessage); - - const dangerIcon = wrapper.find('.fa-exclamation-circle'); - expect(dangerIcon.exists()).toEqual(true); - expect(dangerIcon.hasClass('fa')).toEqual(true); - - const dangerIconDescription = wrapper.find('.sr-only'); - expect(dangerIconDescription.exists()).toEqual(true); - expect(dangerIconDescription.text()).toEqual(validationResult.dangerIconDescription); - - const inputElement = wrapper.find('.form-control'); - expect(inputElement.hasClass('is-invalid')).toEqual(true); - }); - }); + describe('input group addons', () => { + it('does not create an input-group div if no input group addons are given', () => { + const { queryByTestId } = render(); + const inputGroup = queryByTestId('input-group-id'); + expect(inputGroup).not.toBeInTheDocument(); }); - it('displayed inline', () => { + it('displays inputGroupPrepend', () => { const props = { ...baseProps, - inline: true, + inputGroupPrepend: ( +
+ $ +
+ ), }; - const wrapper = mount(); - const input = wrapper.find('.form-group'); - expect(input.hasClass('form-inline')).toEqual(true); + const { getByTestId, getByText } = render(); + const inputGroup = getByTestId('input-group-id'); + const inputGroupText = getByText('$'); + + expect(inputGroup).toBeInTheDocument(); + expect(inputGroupText).toBeInTheDocument(); }); - describe('input group addons', () => { - it('does not create an input-group div if no input group addons are given', () => { - const wrapper = mount(); - const input = wrapper.find('.form-group'); - expect(input.find('.input-group').exists()).toEqual(false); - }); + it('displays inputGroupAppend', () => { + const props = { + ...baseProps, + inputGroupAppend: ( +
+ .00 +
+ ), + }; + const { getByText } = render(); + const inputGroupText = getByText('.00'); - it('displays inputGroupPrepend', () => { - const props = { - ...baseProps, - inputGroupPrepend: ( -
- $ -
- ), - }; - const wrapper = mount(); - const input = wrapper.find('.form-group'); - expect(input.find('.input-group').exists()).toEqual(true); - expect(input.find('.input-group-prepend').exists()).toEqual(true); - expect(input.find('.input-group-prepend').text()).toEqual('$'); - }); + expect(inputGroupText).toBeInTheDocument(); + }); - it('displays inputGroupAppend', () => { - const props = { - ...baseProps, - inputGroupAppend: ( -
- .00 -
- ), - }; - const wrapper = mount(); - const input = wrapper.find('.form-group'); - expect(input.find('.input-group').exists()).toEqual(true); - expect(input.find('.input-group-append').exists()).toEqual(true); - expect(input.find('.input-group-append').text()).toEqual('.00'); - }); + it('displays both inputGroupPrepend and inputGroupAppend', () => { + const props = { + ...baseProps, + inputGroupPrepend: ( +
+ $ +
+ ), + inputGroupAppend: ( +
+ .00 +
+ ), + }; + const { getByText } = render(); + const inputGroupTextAppend = getByText('.00'); + const inputGroupTextPrepend = getByText('$'); - it('displays both inputGroupPrepend and inputGroupAppend', () => { - const props = { - ...baseProps, - inputGroupPrepend: ( -
- $ -
- ), - inputGroupAppend: ( -
- .00 -
- ), - }; - const wrapper = mount(); - const input = wrapper.find('.form-group'); - expect(input.find('.input-group').exists()).toEqual(true); - expect(input.find('.input-group-prepend').exists()).toEqual(true); - expect(input.find('.input-group-prepend').text()).toEqual('$'); - expect(input.find('.input-group-append').exists()).toEqual(true); - expect(input.find('.input-group-append').text()).toEqual('.00'); - }); + expect(inputGroupTextAppend).toBeInTheDocument(); + expect(inputGroupTextPrepend).toBeInTheDocument(); + }); - it('displays multiple inputGroupAppend elements', () => { - const props = { - ...baseProps, - inputGroupAppend: [ -
- .00 -
, -
- -
, - ], - }; - const wrapper = mount(); - const input = wrapper.find('.form-group'); - expect(input.find('.input-group').exists()).toEqual(true); - expect(input.find('.input-group-append').exists()).toEqual(true); - expect(input.find('.input-group-append').children()).toHaveLength(2); - expect(input.find('.input-group-append').childAt(0).text()).toEqual('.00'); - expect(input.find('.input-group-append').childAt(1).text()).toEqual('Go'); - }); + it('displays multiple inputGroupAppend elements', () => { + const props = { + ...baseProps, + inputGroupAppend: [ +
+ .00 +
, +
+ +
, + ], + }; + const { getByText } = render(); + const inputGroupText = getByText('.00'); + const button = getByText('Go'); - it('displays multiple inputGroupPrepend elements', () => { - const props = { - ...baseProps, - inputGroupPrepend: [ -
- $ -
, -
- 0. -
, - ], - }; - const wrapper = mount(); - const input = wrapper.find('.form-group'); - expect(input.find('.input-group').exists()).toEqual(true); - expect(input.find('.input-group-prepend').exists()).toEqual(true); - expect(input.find('.input-group-prepend').children()).toHaveLength(2); - expect(input.find('.input-group-prepend').childAt(0).text()).toEqual('$'); - expect(input.find('.input-group-prepend').childAt(1).text()).toEqual('0.'); - }); + expect(inputGroupText).toBeInTheDocument(); + expect(button).toBeInTheDocument(); + }); + + it('displays multiple inputGroupPrepend elements', () => { + const props = { + ...baseProps, + inputGroupPrepend: [ +
+ $ +
, +
+ 0. +
, + ], + }; + const { getByText } = render(); + const inputGroupText1 = getByText('$'); + const inputGroupText2 = getByText('0.'); + + expect(inputGroupText1).toBeInTheDocument(); + expect(inputGroupText2).toBeInTheDocument(); }); }); }); diff --git a/src/asInput/index.jsx b/src/asInput/index.jsx index a9651bf8c8d..2a93547971b 100644 --- a/src/asInput/index.jsx +++ b/src/asInput/index.jsx @@ -278,7 +278,7 @@ const asInput = (WrappedComponent, inputType = undefined, labelFirst = true) => > {labelFirst && this.getLabel()} {this.props.inputGroupPrepend || this.props.inputGroupAppend ? ( -
+
{this.renderInputGroupPrepend()} {this.renderInput(describedBy)} {this.renderInputGroupAppend()} diff --git a/src/hooks/tests/useToggle.test.jsx b/src/hooks/tests/useToggle.test.jsx index 8bbb67cf441..ca929ff3684 100644 --- a/src/hooks/tests/useToggle.test.jsx +++ b/src/hooks/tests/useToggle.test.jsx @@ -1,6 +1,8 @@ /* eslint-disable react/button-has-type */ import React from 'react'; -import { mount } from 'enzyme'; +import { render, fireEvent } from '@testing-library/react'; +import '@testing-library/jest-dom/extend-expect'; + import { useToggle } from '../..'; const TOGGLE_IS_ON = 'on'; @@ -16,20 +18,13 @@ const resetHandlerMocks = () => { mockHandleToggle.mockReset(); }; -const expectToggleToBeOn = (wrapper) => { - expect(wrapper.find('#toggle-value').text()).toMatch(TOGGLE_IS_ON); -}; -const expectToggleToBeOff = (wrapper) => { - expect(wrapper.find('#toggle-value').text()).toMatch(TOGGLE_IS_OFF); -}; - // eslint-disable-next-line react/prop-types function FakeComponent({ defaultIsOn, handlers }) { const [isOn, setOn, setOff, toggle] = useToggle(defaultIsOn, handlers); return (
-
{isOn ? TOGGLE_IS_ON : TOGGLE_IS_OFF}
+
{isOn ? TOGGLE_IS_ON : TOGGLE_IS_OFF}
@@ -42,63 +37,78 @@ describe('useToggle hook', () => { resetHandlerMocks(); }); - const wrapper = mount(( - { + const { getByTestId } = render( - )); - const wrapperDefaultOnNoHandlers = mount(); - - it('toggles respect defaults on or off', () => { - expectToggleToBeOff(wrapper); - expectToggleToBeOn(wrapperDefaultOnNoHandlers); + />); + expect(getByTestId('toggle-value')).toHaveTextContent(TOGGLE_IS_OFF); + const { getAllByTestId } = render(); + expect(getAllByTestId('toggle-value')[1]).toHaveTextContent(TOGGLE_IS_ON); }); it('setOn turns toggle on', () => { - wrapper.find('#set-on').simulate('click'); - wrapper.update(); - expectToggleToBeOn(wrapper); + const { getByText, getByTestId } = render(); + fireEvent.click(getByText('set on')); + expect(getByTestId('toggle-value')).toHaveTextContent(TOGGLE_IS_ON); expect(mockHandleToggleOn).toHaveBeenCalled(); expect(mockHandleToggle).toHaveBeenCalled(); - - // try again to ensure on only sets it on. - wrapper.find('#set-on').simulate('click'); - wrapper.update(); - expectToggleToBeOn(wrapper); + fireEvent.click(getByText('set on')); + expect(getByTestId('toggle-value')).toHaveTextContent(TOGGLE_IS_ON); }); - // Toggle is on + it('setOff turns toggle off', () => { - wrapper.find('#set-off').simulate('click'); - wrapper.update(); - expectToggleToBeOff(wrapper); + const { getByText, getByTestId } = render(); + fireEvent.click(getByText('set off')); + expect(getByTestId('toggle-value')).toHaveTextContent(TOGGLE_IS_OFF); expect(mockHandleToggleOff).toHaveBeenCalled(); expect(mockHandleToggle).toHaveBeenCalled(); - - // try again to ensure on only sets it off. - wrapper.find('#set-off').simulate('click'); - wrapper.update(); - expectToggleToBeOff(wrapper); + fireEvent.click(getByText('set off')); + expect(getByTestId('toggle-value')).toHaveTextContent(TOGGLE_IS_OFF); }); - // Toggle is off + it('toggle toggles', () => { - wrapper.find('#toggle').simulate('click'); - wrapper.update(); - expectToggleToBeOn(wrapper); + const { getByText, getByTestId } = render(); + fireEvent.click(getByText('toggle')); + expect(getByTestId('toggle-value')).toHaveTextContent(TOGGLE_IS_ON); expect(mockHandleToggleOn).toHaveBeenCalled(); expect(mockHandleToggleOff).not.toHaveBeenCalled(); expect(mockHandleToggle).toHaveBeenCalled(); - resetHandlerMocks(); - - // try again to ensure it changes it back - wrapper.find('#toggle').simulate('click'); - wrapper.update(); - expectToggleToBeOff(wrapper); + fireEvent.click(getByText('toggle')); // Try again to ensure it changes it back. + expect(getByTestId('toggle-value')).toHaveTextContent(TOGGLE_IS_OFF); expect(mockHandleToggleOn).not.toHaveBeenCalled(); expect(mockHandleToggleOff).toHaveBeenCalled(); expect(mockHandleToggle).toHaveBeenCalled();