{children || (
<>
diff --git a/src/DataTable/TableHeaderCell.jsx b/src/DataTable/TableHeaderCell.jsx
index d2426a573a..676ce865a8 100644
--- a/src/DataTable/TableHeaderCell.jsx
+++ b/src/DataTable/TableHeaderCell.jsx
@@ -6,14 +6,14 @@ import { ArrowDropDown, ArrowDropUp, ArrowDropUpDown } from '../../icons';
export function SortIndicator({ isSorted, isSortedDesc }) {
if (!isSorted) {
- return
;
+ return
;
}
if (isSortedDesc) {
- return
;
+ return
;
}
- return
;
+ return
;
}
SortIndicator.propTypes = {
diff --git a/src/DataTable/filters/tests/CheckboxFilter.test.jsx b/src/DataTable/filters/tests/CheckboxFilter.test.jsx
index 50b3d01c70..d593b7d706 100644
--- a/src/DataTable/filters/tests/CheckboxFilter.test.jsx
+++ b/src/DataTable/filters/tests/CheckboxFilter.test.jsx
@@ -1,8 +1,8 @@
import React from 'react';
-import { mount } from 'enzyme';
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import CheckboxFilter from '../CheckboxFilter';
-import Badge from '../../../Badge';
const setFilterMock = jest.fn();
const roan = { name: 'roan', number: 3, value: '10' };
@@ -25,49 +25,54 @@ describe('
', () => {
beforeEach(() => {
jest.resetAllMocks();
});
+
it('renders a list of checkboxes', () => {
- const wrapper = mount(
);
- const checkbox = wrapper.find({ type: 'checkbox' }).find('input');
- expect(checkbox.length).toEqual(3);
+ render(
);
+ const checkboxes = screen.getAllByRole('checkbox');
+ expect(checkboxes.length).toEqual(3);
});
+
it('renders a title', () => {
- const wrapper = mount(
);
- expect(wrapper.text()).toContain(props.column.Header);
+ render(
);
+ expect(screen.getByText(props.column.Header)).toBeInTheDocument();
});
- it('sets a filter - no initial filters', () => {
- const wrapper = mount(
);
- const checkbox = wrapper.find({ type: 'checkbox' }).find('input').at(1);
- checkbox.simulate('change');
+
+ it('sets a filter - no initial filters', async () => {
+ render(
);
+ const checkbox = screen.getByLabelText(palomino.name);
+ await userEvent.click(checkbox);
expect(setFilterMock).toHaveBeenCalledWith([palomino.value]);
});
- it('sets a filter - initial filters', () => {
- const wrapper = mount(
);
- const checkbox = wrapper.find({ type: 'checkbox' }).find('input').at(1);
- checkbox.simulate('change');
+
+ it('sets a filter - initial filters', async () => {
+ render(
);
+ const checkbox = screen.getByLabelText(palomino.name);
+ await userEvent.click(checkbox);
expect(setFilterMock).toHaveBeenCalledWith([roan.value, palomino.value]);
});
- it('removes a filter', () => {
- const wrapper = mount(
);
- const checkbox = wrapper.find({ type: 'checkbox' }).find('input').at(1);
- checkbox.simulate('change');
+
+ it('removes a filter', async () => {
+ render(
);
+ const checkbox = screen.getByLabelText(palomino.name);
+ await userEvent.click(checkbox);
expect(setFilterMock).toHaveBeenCalledWith([]);
});
+
it('renders checkbox label with filter name', () => {
- const wrapper = mount(
);
- const label = wrapper.find('.pgn__form-checkbox').at(0);
- expect(label.text()).toContain(roan.name);
+ render(
);
+ const checkbox = screen.getByText(roan.name);
+ expect(checkbox).toBeInTheDocument();
});
+
it('renders checkbox label with number', () => {
- const wrapper = mount(
);
- const label = wrapper.find('.pgn__form-checkbox').at(0);
- const badge = label.find(Badge);
- expect(badge).toHaveLength(1);
- expect(badge.text()).toEqual(String(roan.number));
+ render(
);
+ const badge = screen.getByText(String(roan.number));
+ expect(badge).toBeInTheDocument();
});
- it('renders checkbox label with number', () => {
- const wrapper = mount(
);
- const label = wrapper.find('.pgn__form-checkbox').at(1);
- const badge = label.find(Badge);
- expect(badge).toHaveLength(0);
+
+ it('does not render badge if number is not present', () => {
+ render(
);
+ const badge = screen.queryByText(String(palomino.number));
+ expect(badge).toBeNull();
});
});
diff --git a/src/DataTable/filters/tests/DropdownFilter.test.jsx b/src/DataTable/filters/tests/DropdownFilter.test.jsx
index 64fd50347d..95acb02ebb 100644
--- a/src/DataTable/filters/tests/DropdownFilter.test.jsx
+++ b/src/DataTable/filters/tests/DropdownFilter.test.jsx
@@ -1,5 +1,6 @@
import React from 'react';
-import { mount } from 'enzyme';
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import DropdownFilter from '../DropdownFilter';
@@ -24,32 +25,45 @@ describe('
', () => {
beforeEach(() => {
jest.resetAllMocks();
});
+
it('renders a select button', () => {
- const wrapper = mount(
);
- expect(wrapper.text()).toContain(props.column.Header);
+ render(
);
+ expect(screen.getByLabelText(props.column.Header)).toBeInTheDocument();
});
- it('sets a filter - no initial filters', () => {
- const wrapper = mount(
);
- wrapper.find('select').simulate('click');
- wrapper.find('option').at(2).simulate('change');
+
+ it('sets a filter - no initial filters', async () => {
+ render(
);
+ const select = screen.getByLabelText(props.column.Header);
+ await userEvent.click(select);
+ await userEvent.selectOptions(select, palomino.value);
expect(setFilterMock).toHaveBeenCalledWith(palomino.value);
});
- it('sets a filter - initial filters', () => {
- const wrapper = mount(
);
- wrapper.find('select').simulate('click');
- wrapper.find('option').at(2).simulate('change');
+
+ it('sets a filter - initial filters', async () => {
+ render(
+
,
+ );
+ const select = screen.getByLabelText(props.column.Header);
+ await userEvent.click(select);
+ await userEvent.selectOptions(select, palomino.value);
expect(setFilterMock).toHaveBeenCalledWith(palomino.value);
});
- it('removes filters when default option is clicked', () => {
- const wrapper = mount(
);
- wrapper.find('select').simulate('click');
- wrapper.find('option').at(0).simulate('change');
+
+ it('removes filters when default option is clicked', async () => {
+ render(
+
,
+ );
+ const select = screen.getByLabelText(props.column.Header);
+ await userEvent.click(select);
+ await userEvent.selectOptions(select, '');
expect(setFilterMock).toHaveBeenCalledWith(undefined);
});
- it('displays a number if a number is provided', () => {
- const wrapper = mount(
);
- wrapper.find('select').simulate('click');
- const option = wrapper.find('option').at(1);
- expect(option.text()).toEqual(`${roan.name} (${roan.number})`);
+
+ it('displays a number if a number is provided', async () => {
+ render(
);
+ const select = screen.getByLabelText(props.column.Header);
+ await userEvent.click(select);
+ const option = screen.getByText(`${roan.name} (${roan.number})`);
+ expect(option).toBeInTheDocument();
});
});
diff --git a/src/DataTable/filters/tests/MultiSelectDropdownFilter.test.jsx b/src/DataTable/filters/tests/MultiSelectDropdownFilter.test.jsx
index c8aa7e7b9c..a9ab8ff192 100644
--- a/src/DataTable/filters/tests/MultiSelectDropdownFilter.test.jsx
+++ b/src/DataTable/filters/tests/MultiSelectDropdownFilter.test.jsx
@@ -1,9 +1,8 @@
import React from 'react';
-import { mount } from 'enzyme';
-import { act } from 'react-dom/test-utils';
+import { render, screen, act } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import MultiSelectDropdownFilter from '../MultiSelectDropdownFilter';
-import Badge from '../../../Badge';
const setFilterMock = jest.fn();
const roan = { name: 'roan', number: 3, value: '10' };
@@ -27,71 +26,77 @@ describe('
', () => {
jest.resetAllMocks();
});
it('renders a list of checkboxes', async () => {
- const wrapper = mount(
);
- wrapper.find('button').simulate('click');
+ render(
);
await act(async () => {
- const checkbox = wrapper.find({ type: 'checkbox' }).find('input');
- expect(checkbox.length).toEqual(3);
+ await userEvent.click(screen.getByText(props.column.Header));
});
+ const checkboxes = screen.getAllByRole('checkbox');
+ expect(checkboxes.length).toEqual(3);
});
it('renders a title', () => {
- const wrapper = mount(
);
- expect(wrapper.text()).toContain(props.column.Header);
+ render(
);
+ expect(screen.getByText(props.column.Header)).toBeInTheDocument();
});
it('sets a filter - no initial filters', async () => {
- const wrapper = mount(
);
- wrapper.find('button').simulate('click');
- const checkbox = wrapper.find({ type: 'checkbox' }).find('input').at(1);
+ render(
);
await act(async () => {
- checkbox.simulate('change');
+ await userEvent.click(screen.getByText(props.column.Header));
+ });
+ const checkbox = screen.getAllByRole('checkbox')[1];
+ await act(async () => {
+ await userEvent.click(checkbox, undefined, { skipPointerEventsCheck: true });
});
expect(setFilterMock).toHaveBeenCalledWith([palomino.value]);
});
it('sets a filter - initial filters', async () => {
- const wrapper = mount(
);
- wrapper.find('button').simulate('click');
- const checkbox = wrapper.find({ type: 'checkbox' }).find('input').at(1);
+ render(
);
+ await act(async () => {
+ await userEvent.click(screen.getByText(props.column.Header));
+ });
+ const checkbox = screen.getAllByRole('checkbox')[1];
await act(async () => {
- checkbox.simulate('change');
+ await userEvent.click(checkbox, undefined, { skipPointerEventsCheck: true });
});
expect(setFilterMock).toHaveBeenCalledWith([roan.value, palomino.value]);
});
it('removes a filter', async () => {
- const wrapper = mount(
);
- wrapper.find('button').simulate('click');
- const checkbox = wrapper.find({ type: 'checkbox' }).find('input').at(1);
+ render(
);
await act(async () => {
- checkbox.simulate('change');
+ await userEvent.click(screen.getByText(props.column.Header));
+ });
+ const checkbox = screen.getAllByRole('checkbox')[1];
+ await act(async () => {
+ await userEvent.click(checkbox, undefined, { skipPointerEventsCheck: true });
});
expect(setFilterMock).toHaveBeenCalledWith([]);
});
it('renders checkbox label with filter name', async () => {
- const wrapper = mount(
);
- wrapper.find('button').simulate('click');
+ render(
);
await act(async () => {
- const label = wrapper.find('.pgn__form-label').at(0);
- expect(label.text()).toContain(roan.name);
+ await userEvent.click(screen.getByText(props.column.Header));
});
+ const label = screen.getByText(roan.name);
+ expect(label).toBeInTheDocument();
});
- it('renders checkbox label with number', async () => {
- const wrapper = mount(
);
- wrapper.find('button').simulate('click');
-
+ it('renders checkbox label with number - with badge', async () => {
+ render(
);
await act(async () => {
- const label = wrapper.find('.pgn__form-checkbox').at(0);
- const badge = label.find(Badge);
- expect(badge).toHaveLength(1);
- expect(badge.text()).toEqual(String(roan.number));
+ await userEvent.click(screen.getByText(props.column.Header));
});
+ const label = screen.getByText(roan.name);
+ expect(label).toBeInTheDocument();
+ const badge = screen.getByText(roan.number.toString());
+ expect(badge).toBeInTheDocument();
});
- it('renders checkbox label with number', async () => {
- const wrapper = mount(
);
- wrapper.find('button').simulate('click');
-
+ it('renders checkbox label with number - without badge', async () => {
+ render(
+
,
+ );
await act(async () => {
- const label = wrapper.find('.pgn__form-checkbox').at(1);
- const badge = label.find('.badge');
- expect(badge).toHaveLength(0);
+ await userEvent.click(screen.getByText(props.column.Header));
});
+ const label = screen.getByText(palomino.name);
+ expect(label).toBeInTheDocument();
+ expect(label.querySelector('.badge')).toBeNull();
});
});
diff --git a/src/DataTable/filters/tests/TextFilter.test.jsx b/src/DataTable/filters/tests/TextFilter.test.jsx
index 24a19889b9..0d6aef13ab 100644
--- a/src/DataTable/filters/tests/TextFilter.test.jsx
+++ b/src/DataTable/filters/tests/TextFilter.test.jsx
@@ -1,9 +1,10 @@
import React from 'react';
-import { mount } from 'enzyme';
+import { render } from '@testing-library/react';
+
import TextFilter from '../TextFilter';
describe('
', () => {
- it('Header as function', () => {
+ it('renders Header as function', () => {
const mockKey = () => 'key';
const props = {
column: {
@@ -13,11 +14,11 @@ describe('
', () => {
getHeaderProps: jest.fn().mockImplementation(() => ({ key: mockKey })),
},
};
- const wrapper = mount(
);
- expect(wrapper.text()).toContain('Search test');
+ const { getByText } = render(
);
+ expect(getByText('Search test')).toBeInTheDocument();
});
- it('Header as string', () => {
+ it('renders Header as string', () => {
const mockKey = () => 'key';
const props = {
column: {
@@ -27,11 +28,11 @@ describe('
', () => {
getHeaderProps: jest.fn().mockImplementation(() => ({ key: mockKey })),
},
};
- const wrapper = mount(
);
- expect(wrapper.text()).toContain('Search test');
+ const { getByText } = render(
);
+ expect(getByText('Search test')).toBeInTheDocument();
});
- it('Header as component', () => {
+ it('renders Header as component', () => {
const mockKey = () => 'key';
const props = {
column: {
@@ -41,7 +42,7 @@ describe('
', () => {
getHeaderProps: jest.fn().mockImplementation(() => ({ key: mockKey })),
},
};
- const wrapper = mount(
);
- expect(wrapper.text()).toContain('test_component');
+ const { getByText } = render(
);
+ expect(getByText('test_component')).toBeInTheDocument();
});
});
diff --git a/src/DataTable/selection/BaseSelectionStatus.jsx b/src/DataTable/selection/BaseSelectionStatus.jsx
index e4d9d551ea..80039b245c 100644
--- a/src/DataTable/selection/BaseSelectionStatus.jsx
+++ b/src/DataTable/selection/BaseSelectionStatus.jsx
@@ -57,11 +57,13 @@ function BaseSelectionStatus({
const intlSelectedText = selectedText || defaultSelectedText;
return (
-
-
{isAllRowsSelected ? intlAllSelectedText : intlSelectedText}
+
+
+ {isAllRowsSelected ? intlAllSelectedText : intlSelectedText}
+
{!isAllRowsSelected && !hasMaxSelectedRows && (
);
diff --git a/src/DataTable/selection/ControlledSelectHeader.jsx b/src/DataTable/selection/ControlledSelectHeader.jsx
index d858a5c4bd..e59e6e2a0f 100644
--- a/src/DataTable/selection/ControlledSelectHeader.jsx
+++ b/src/DataTable/selection/ControlledSelectHeader.jsx
@@ -9,7 +9,7 @@ import {
setSelectedRowsAction,
} from './data/actions';
-function ControlledSelectHeader({ rows }) {
+function ControlledSelectHeader({ rows, ...rest }) {
const {
itemCount,
controlledTableSelections: [, dispatch],
@@ -44,6 +44,7 @@ function ControlledSelectHeader({ rows }) {
);
diff --git a/src/DataTable/selection/ControlledSelectionStatus.jsx b/src/DataTable/selection/ControlledSelectionStatus.jsx
index 3078dbf52e..f7a49e6dc4 100644
--- a/src/DataTable/selection/ControlledSelectionStatus.jsx
+++ b/src/DataTable/selection/ControlledSelectionStatus.jsx
@@ -14,7 +14,7 @@ import {
getRowIds,
} from './data/helpers';
-function ControlledSelectionStatus({ className, clearSelectionText }) {
+function ControlledSelectionStatus({ className, clearSelectionText, ...rest }) {
const {
itemCount,
page,
@@ -44,6 +44,7 @@ function ControlledSelectionStatus({ className, clearSelectionText }) {
clearSelectionText,
onSelectAll: () => dispatch(setSelectAllRowsAllPagesAction()),
onClear: () => dispatch(clearSelectionAction()),
+ ...rest,
};
return
;
}
diff --git a/src/DataTable/selection/tests/ControlledSelect.test.jsx b/src/DataTable/selection/tests/ControlledSelect.test.jsx
index 463628d072..2daded3660 100644
--- a/src/DataTable/selection/tests/ControlledSelect.test.jsx
+++ b/src/DataTable/selection/tests/ControlledSelect.test.jsx
@@ -1,16 +1,16 @@
import React from 'react';
-import { mount } from 'enzyme';
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import ControlledSelect from '../ControlledSelect';
import DataTable from '../..';
import * as selectActions from '../data/actions';
-import { toggleCheckbox } from './utils';
// eslint-disable-next-line react/prop-types
-function ControlledSelectWrapper({ tableProps, selectProps }) {
+function ControlledSelectWrapper({ tableProps, selectProps, ...rest }) {
return (
-
+
);
}
@@ -30,29 +30,33 @@ describe('
', () => {
beforeEach(() => {
jest.resetAllMocks();
});
- it('correctly selects a row', () => {
+
+ it('correctly selects a row', async () => {
const isChecked = true;
mockGetToggleRowSelectedProps.mockReturnValue({ checked: isChecked });
const spy = jest.spyOn(selectActions, 'addSelectedRowAction');
const row = { ...baseRow, isSelected: false };
const selectProps = { row };
- const wrapper = mount(
-
,
- );
- toggleCheckbox({ isChecked, wrapper });
+ render(
);
+
+ const checkbox = screen.getByRole('checkbox');
+ await userEvent.click(checkbox);
+
expect(spy).toHaveBeenCalledTimes(1);
expect(spy).toHaveBeenCalledWith(row, tableProps.itemCount);
});
- it('correctly unselects a row', () => {
+
+ it('correctly unselects a row', async () => {
const isChecked = false;
mockGetToggleRowSelectedProps.mockReturnValue({ checked: isChecked });
const spy = jest.spyOn(selectActions, 'deleteSelectedRowAction');
const row = { ...baseRow, isSelected: true };
const selectProps = { row };
- const wrapper = mount(
-
,
- );
- toggleCheckbox({ isChecked, wrapper });
+ render(
);
+
+ const checkbox = screen.getByRole('checkbox');
+ await userEvent.click(checkbox);
+
expect(spy).toHaveBeenCalledWith(row.id);
});
});
diff --git a/src/DataTable/selection/tests/ControlledSelectHeader.test.jsx b/src/DataTable/selection/tests/ControlledSelectHeader.test.jsx
index f2bb6b6750..732c3cdb43 100644
--- a/src/DataTable/selection/tests/ControlledSelectHeader.test.jsx
+++ b/src/DataTable/selection/tests/ControlledSelectHeader.test.jsx
@@ -1,19 +1,18 @@
import React, { useContext } from 'react';
-import { mount } from 'enzyme';
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import ControlledSelectHeader from '../ControlledSelectHeader';
import DataTable from '../..';
-import { CheckboxControl } from '../../../Form';
import DataTableContext from '../../DataTableContext';
import * as selectActions from '../data/actions';
-import { toggleCheckbox } from './utils';
import { getRowIds } from '../data/helpers';
// eslint-disable-next-line react/prop-types
-function ControlledSelectHeaderWrapper({ tableProps, selectProps }) {
+function ControlledSelectHeaderWrapper({ tableProps, selectProps, ...rest }) {
return (
-
+
);
@@ -42,6 +41,7 @@ describe('
', () => {
beforeEach(() => {
jest.resetAllMocks();
});
+
it('correctly selects all page rows', () => {
const isChecked = true;
mockToggleAllPageRowsSelectedProps.mockReturnValue({
@@ -50,13 +50,15 @@ describe('
', () => {
});
const spy = jest.spyOn(selectActions, 'setSelectedRowsAction');
const selectProps = { rows };
- const wrapper = mount(
-
,
- );
- toggleCheckbox({ isChecked, wrapper });
+ render(
);
+
+ const checkbox = screen.getByRole('checkbox');
+ userEvent.click(checkbox);
+
expect(spy).toHaveBeenCalledTimes(1);
expect(spy).toHaveBeenCalledWith(rows, tableProps.itemCount);
});
+
it('correctly unselects all page rows', () => {
const spy = jest.spyOn(selectActions, 'clearPageSelectionAction');
mockToggleAllPageRowsSelectedProps.mockReturnValue({
@@ -73,14 +75,16 @@ describe('
', () => {
state: { selectedRowIds },
isAllPageRowsSelected: true,
};
- const wrapper = mount(
-
,
- );
- toggleCheckbox({ isChecked: false, wrapper });
+ render(
);
+
+ const checkbox = screen.getByRole('checkbox');
+ userEvent.click(checkbox);
+
expect(spy).toHaveBeenCalledTimes(1);
const rowIds = getRowIds(rows).map(id => id.toString());
expect(spy).toHaveBeenCalledWith(rowIds);
});
+
it('correctly shows indeterminate checkbox when some page rows (not all) are selected', () => {
const isIndeterminate = true;
mockToggleAllPageRowsSelectedProps.mockReturnValue({
@@ -95,11 +99,9 @@ describe('
', () => {
},
},
};
- const wrapper = mount(
-
,
- );
+ render(
);
- const actualIsIndeterminate = wrapper.find(CheckboxControl).prop('isIndeterminate');
- expect(actualIsIndeterminate).toEqual(isIndeterminate);
+ const checkbox = screen.getByRole('checkbox');
+ expect(checkbox.indeterminate).toEqual(isIndeterminate);
});
});
diff --git a/src/DataTable/selection/tests/ControlledSelectionStatus.test.jsx b/src/DataTable/selection/tests/ControlledSelectionStatus.test.jsx
index cd2bdf2022..e7850350ef 100644
--- a/src/DataTable/selection/tests/ControlledSelectionStatus.test.jsx
+++ b/src/DataTable/selection/tests/ControlledSelectionStatus.test.jsx
@@ -1,6 +1,7 @@
import React from 'react';
-import { mount } from 'enzyme';
+import { render, screen } from '@testing-library/react';
import { IntlProvider } from 'react-intl';
+import userEvent from '@testing-library/user-event';
import ControlledSelectionStatus from '../ControlledSelectionStatus';
import { clearSelectionAction, setSelectAllRowsAllPagesAction, setSelectedRowsAction } from '../data/actions';
@@ -24,11 +25,11 @@ const instance = {
};
// eslint-disable-next-line react/prop-types
-function ControlledSelectionStatusWrapper({ value, props = {} }) {
+function ControlledSelectionStatusWrapper({ value, props = {}, ...rest }) {
return (
-
+
);
@@ -37,13 +38,21 @@ function ControlledSelectionStatusWrapper({ value, props = {} }) {
describe('
', () => {
it('accepts a class name', () => {
const customClassName = 'classy';
- const wrapper = mount(
);
- expect(wrapper.find(ControlledSelectionStatus).props().className).toEqual(customClassName);
+ render(
+
,
+ );
+ const component = screen.getByTestId('selection-status-component');
+ expect(component).toHaveClass(customClassName);
});
+
describe('entire table selected', () => {
it('shows that entire table is selected', () => {
const selectedRows = Array(instance.itemCount).map((item, index) => ({ id: index + 1 }));
- const wrapper = mount(
+ render(
', () => {
}}
/>,
);
- expect(wrapper.text()).toContain(`All ${instance.itemCount}`);
+ expect(screen.getByText(`All ${instance.itemCount} selected`)).toBeInTheDocument();
});
+
it('does not show select all button if entire table is selected', () => {
const selectedRows = Array(instance.itemCount).map((item, index) => ({ id: index + 1 }));
- const wrapper = mount(
+ render(
', () => {
}}
/>,
);
- const button = wrapper.find(`button.${SELECT_ALL_TEST_ID}`);
- expect(button.length).toEqual(0);
+ const selectAllButton = screen.queryByTestId(SELECT_ALL_TEST_ID);
+ expect(selectAllButton).not.toBeInTheDocument();
});
+
it('selects any unselected page rows', () => {
const selectedRows = Array(instance.itemCount).map((item, index) => ({ id: index + 1 }));
const dispatchSpy = jest.fn();
- mount(
+ render(
', () => {
expect(dispatchSpy).toHaveBeenCalledWith(action);
});
});
+
describe('individual rows selected', () => {
it('shows the number of rows selected', () => {
- const wrapper = mount(
);
+ render(
);
const [selections] = instance.controlledTableSelections;
- expect(wrapper.text()).toContain(selections.selectedRows.length.toString());
+ expect(screen.getByText(`${selections.selectedRows.length.toString()} selected`)).toBeInTheDocument();
});
+
it('renders default selection text', () => {
- const wrapper = mount(
);
- expect(wrapper.text()).toContain(CLEAR_SELECTION_TEXT);
+ render(
);
+ expect(screen.getByText(CLEAR_SELECTION_TEXT)).toBeInTheDocument();
});
+
it('can accept clear selection text as a prop', () => {
const customText = 'CLEAR ME';
- const wrapper = mount((
-
- ));
- expect(wrapper.text()).toContain(customText);
- expect(wrapper.text()).not.toContain(CLEAR_SELECTION_TEXT);
+ render(
+
,
+ );
+ expect(screen.getByText(customText)).toBeInTheDocument();
+ expect(screen.queryByText(CLEAR_SELECTION_TEXT)).not.toBeInTheDocument();
});
- it('toggles select all on select all button click', () => {
+
+ it('toggles select all on select all button click', async () => {
const dispatchSpy = jest.fn();
- const wrapper = mount(
+ render(
', () => {
}}
/>,
);
- const button = wrapper.find(`button.${SELECT_ALL_TEST_ID}`);
- button.simulate('click');
+ const selectAllButton = screen.getByTestId(SELECT_ALL_TEST_ID);
+ await userEvent.click(selectAllButton);
expect(dispatchSpy).toHaveBeenCalledTimes(1);
const action = setSelectAllRowsAllPagesAction();
expect(dispatchSpy).toHaveBeenCalledWith(action);
});
- it('clears selection on clear selection button click', () => {
+
+ it('clears selection on clear selection button click', async () => {
const dispatchSpy = jest.fn();
- const wrapper = mount(
+ render(
', () => {
}}
/>,
);
- const button = wrapper.find(`button.${CLEAR_SELECTION_TEST_ID}`);
- button.simulate('click');
+ const clearSelectionButton = screen.getByTestId(CLEAR_SELECTION_TEST_ID);
+ await userEvent.click(clearSelectionButton);
expect(dispatchSpy).toHaveBeenCalledTimes(1);
const action = clearSelectionAction();
expect(dispatchSpy).toHaveBeenCalledWith(action);
});
});
+
describe('no rows selected', () => {
it('does not render the clear selection button', () => {
- const wrapper = mount(
+ render(
', () => {
}}
/>,
);
- expect(wrapper.find(CLEAR_SELECTION_TEST_ID).length).toEqual(0);
+ const clearSelectionButton = screen.queryByTestId(CLEAR_SELECTION_TEST_ID);
+ expect(clearSelectionButton).not.toBeInTheDocument();
});
});
});
diff --git a/src/DataTable/selection/tests/SelectionStatus.test.jsx b/src/DataTable/selection/tests/SelectionStatus.test.jsx
index f1b1a6e3ec..feb56ecf12 100644
--- a/src/DataTable/selection/tests/SelectionStatus.test.jsx
+++ b/src/DataTable/selection/tests/SelectionStatus.test.jsx
@@ -1,6 +1,7 @@
import React from 'react';
-import { mount } from 'enzyme';
+import { render, screen } from '@testing-library/react';
import { IntlProvider } from 'react-intl';
+import userEvent from '@testing-library/user-event';
import SelectionStatus from '../SelectionStatus';
import DataTableContext from '../../DataTableContext';
@@ -11,7 +12,6 @@ import {
} from '../data/constants';
const instance = {
- // represents how `react-table` passes the row data for the current page
page: [1, 2, 3, 4],
toggleAllRowsSelected: () => {},
itemCount: 101,
@@ -39,9 +39,10 @@ function SelectionStatusWrapper({ value, props = {} }) {
describe('
', () => {
it('Shows the number of rows selected', () => {
- const wrapper = mount(
);
- expect(wrapper.text()).toContain(instance.page.length.toString());
+ render(
);
+ expect(screen.getByTestId('selection-status')).toHaveTextContent(instance.page.length.toString());
});
+
it('Shows that all rows are selected', () => {
const selectedRowIds = {};
[...new Array(101)].forEach((_, index) => {
@@ -54,33 +55,39 @@ describe('
', () => {
selectedRowIds,
},
};
- const wrapper = mount(
+ render(
,
);
- expect(wrapper.text()).toContain('All 101');
- const button = wrapper.find(`button.${SELECT_ALL_TEST_ID}`);
- expect(button.length).toEqual(0);
+ expect(screen.getByText('All 101 selected')).toBeInTheDocument();
+ const selectAllButton = screen.queryByTestId(SELECT_ALL_TEST_ID);
+ expect(selectAllButton).not.toBeInTheDocument();
});
- it('toggles select all on select all button click', () => {
+
+ it('toggles select all on select all button click', async () => {
const toggleAllRowsSpy = jest.fn();
- const wrapper = mount(
-
,
+ render(
+
,
);
- const button = wrapper.find(`button.${SELECT_ALL_TEST_ID}`);
- button.simulate('click');
+ const selectAllButton = screen.getByTestId(SELECT_ALL_TEST_ID);
+ await userEvent.click(selectAllButton);
expect(toggleAllRowsSpy).toHaveBeenCalledTimes(1);
expect(toggleAllRowsSpy).toHaveBeenCalledWith(true);
});
+
it('updates select all button text after applying filters', () => {
- const wrapper = mount(
);
- const button = wrapper.find(`button.${SELECT_ALL_TEST_ID}`);
- expect(button.text()).toContain('Select all 27');
+ render(
);
+ const selectAllButton = screen.getByTestId(SELECT_ALL_TEST_ID);
+ expect(selectAllButton).toHaveTextContent('Select all 27');
});
+
it('updates select all text if filters value is empty', () => {
- const wrapper = mount(
);
- const button = wrapper.find(`button.${SELECT_ALL_TEST_ID}`);
- expect(button.text()).toContain('Select all 101');
+ render(
);
+ const selectAllButton = screen.getByTestId(SELECT_ALL_TEST_ID);
+ expect(selectAllButton).toHaveTextContent('Select all 101');
});
+
it('does not render the clear selection button if there are no selected rows', () => {
const value = {
...instance,
@@ -90,34 +97,39 @@ describe('
', () => {
selectedRowIds: {},
},
};
- const wrapper = mount(
+ render(
,
);
- expect(wrapper.find(CLEAR_SELECTION_TEST_ID).length).toEqual(0);
+ expect(screen.queryByTestId(CLEAR_SELECTION_TEST_ID)).not.toBeInTheDocument();
});
- it('toggles select all on clear all button click', () => {
+
+ it('toggles select all on clear all button click', async () => {
const toggleAllRowsSpy = jest.fn();
- const wrapper = mount(
+ render(
,
);
- const button = wrapper.find(`button.${CLEAR_SELECTION_TEST_ID}`);
- button.simulate('click');
+ const clearSelectionButton = screen.getByTestId(CLEAR_SELECTION_TEST_ID);
+ await userEvent.click(clearSelectionButton);
expect(toggleAllRowsSpy).toHaveBeenCalledTimes(1);
expect(toggleAllRowsSpy).toHaveBeenCalledWith(false);
});
+
it('renders default selection text', () => {
- const wrapper = mount(
);
- expect(wrapper.text()).toContain(CLEAR_SELECTION_TEXT);
+ render(
);
+ expect(screen.getByText(CLEAR_SELECTION_TEXT)).toBeInTheDocument();
});
+
it('can accept clear selection text as a prop', () => {
const customText = 'CLEAR ME';
- const wrapper = mount(
);
- expect(wrapper.text()).toContain(customText);
- expect(wrapper.text()).not.toContain(CLEAR_SELECTION_TEXT);
+ render(
);
+ expect(screen.getByText(customText)).toBeInTheDocument();
+ expect(screen.queryByText(CLEAR_SELECTION_TEXT)).not.toBeInTheDocument();
});
+
it('accepts a class name', () => {
const customClassName = 'classy';
- const wrapper = mount(
);
- expect(wrapper.find(SelectionStatus).props().className).toEqual(customClassName);
+ render(
);
+ const component = screen.getByTestId('selection-status-component');
+ expect(component).toHaveClass(customClassName);
});
});
diff --git a/src/DataTable/tests/ActionDisplay.test.jsx b/src/DataTable/tests/ActionDisplay.test.jsx
index e89489daac..68645a3c0d 100644
--- a/src/DataTable/tests/ActionDisplay.test.jsx
+++ b/src/DataTable/tests/ActionDisplay.test.jsx
@@ -1,11 +1,8 @@
import React from 'react';
-import { mount } from 'enzyme';
+import { render, screen } from '@testing-library/react';
-import { Button } from '../..';
import ActionDisplay from '../ActionDisplay';
import DataTableContext from '../DataTableContext';
-import BulkActions from '../BulkActions';
-import TableActions from '../TableActions';
const instance = {
selectedFlatRows: null,
@@ -39,52 +36,57 @@ function SecondAction({ as: Component }) {
}
// eslint-disable-next-line react/prop-types
-function ActionDisplayWrapper({ value = instance, props = {} }) {
- return
;
+function ActionDisplayWrapper({ value = instance, props = {}, ...rest }) {
+ return
;
}
describe('
', () => {
it('renders null if there are no actions', () => {
- const wrapper = mount(
);
- expect(wrapper.find(ActionDisplay).text()).toEqual('');
+ render(
);
+ expect(screen.queryByTestId('action-display')).not.toBeInTheDocument();
});
+
it('renders null if there are no rows', () => {
- const wrapper = mount(
+ render(
], bulkActions: [
],
}}
/>,
);
- const button = wrapper.find(Button);
- expect(button.length).toEqual(0);
+ expect(screen.queryByTestId('action-display')).not.toBeInTheDocument();
});
+
it('displays bulk actions when rows are selected', () => {
- const wrapper = mount(
+ render(
,
], selectedFlatRows: [{}, {}] }}
+ data-testid=""
/>,
);
- expect(wrapper.find(BulkActions)).toHaveLength(1);
+ expect(screen.queryByTestId('bulk-actions')).toBeInTheDocument();
});
+
it('does not display bulk actions when no rows are selected (no table actions)', () => {
- const wrapper = mount(
+ render(
,
], selectedFlatRows: [] }}
/>,
);
- expect(wrapper.find(ActionDisplay).text()).toEqual('');
+ expect(screen.queryByTestId('action-display')).not.toBeInTheDocument();
});
+
it('displays tableActions', () => {
- const wrapper = mount(
+ render(
,
], selectedFlatRows: [] }}
/>,
);
- expect(wrapper.find(TableActions)).toHaveLength(1);
+ expect(screen.queryByTestId('table-actions')).toBeInTheDocument();
});
+
it('displays table actions when both bulk actions and table actions are present - no selected rows', () => {
- const wrapper = mount(
+ render(
', () => {
}}
/>,
);
- expect(wrapper.find(TableActions)).toHaveLength(1);
+ expect(screen.queryByTestId('table-actions')).toBeInTheDocument();
});
+
it('displays table actions with rows selected and no bulk actions', () => {
// This is an edge case
- const wrapper = mount(
+ render(
,
], selectedFlatRows: [{}, {}] }}
/>,
);
- expect(wrapper.find(TableActions)).toHaveLength(1);
+ expect(screen.queryByTestId('table-actions')).toBeInTheDocument();
});
+
it('displays bulk actions instead of table actions when rows are selected', () => {
- const wrapper = mount(
+ render(
', () => {
}}
/>,
);
- expect(wrapper.find(BulkActions)).toHaveLength(1);
+ expect(screen.queryByTestId('bulk-actions')).toBeInTheDocument();
});
});
diff --git a/src/DataTable/tests/BulkActions.test.jsx b/src/DataTable/tests/BulkActions.test.jsx
index f6d2cf504b..bb207b22b8 100644
--- a/src/DataTable/tests/BulkActions.test.jsx
+++ b/src/DataTable/tests/BulkActions.test.jsx
@@ -1,37 +1,49 @@
import React from 'react';
-import { mount } from 'enzyme';
+import { screen, render } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+
import classNames from 'classnames';
import BulkActions from '../BulkActions';
import {
ACTION_OVERFLOW_BUTTON_TEXT, SMALL_SCREEN_ACTION_OVERFLOW_BUTTON_TEXT,
} from '../CollapsibleButtonGroup';
-import {
- useWindowSize,
- Button,
- IconButton,
- ModalPopup,
-} from '../..';
+import { useWindowSize, Button } from '../..';
import DataTableContext from '../DataTableContext';
import { waitForComponentToPaint } from './utils';
jest.mock('../../hooks/useWindowSize');
useWindowSize.mockReturnValue({ width: 800 });
-// eslint-disable-next-line react/prop-types
-function FirstAction({ as: Component, onClick, className }) {
+const FIRST_ACTION = 'First Action';
+const SECOND_ACTION = 'Second Action';
+
+function FirstAction({
+ // eslint-disable-next-line react/prop-types
+ as: Component, onClick, className, ...rest
+}) {
return (
-
- First Action
+
+ {FIRST_ACTION}
);
}
// eslint-disable-next-line react/prop-types
-function SecondAction({ onClick, className }) {
+function SecondAction({ onClick, className, ...rest }) {
return (
-
- Second Action
+
+ {SECOND_ACTION}
);
}
@@ -45,15 +57,13 @@ function ExtraAction({ text }) {
);
}
-const selectedFlatRows = [{ id: 1 }, { id: 2 }];
-
const twoActions = [
- ,
- ,
+ ,
+ ,
];
const instance = {
- selectedFlatRows,
+ selectedFlatRows: [{ id: 1 }, { id: 2 }],
controlledTableSelections: [
{
selectedRows: [],
@@ -82,46 +92,59 @@ describe('', () => {
describe('with functional rendering', () => {
it('renders the function', () => {
const myFunction = () => Some Button;
- const wrapper = mount();
- const button = wrapper.find(Button);
- expect(button.length).toEqual(1);
+ render();
+ const button = screen.getByText('Some Button');
+ expect(button).toBeInTheDocument();
});
});
+
describe('with one action', () => {
- it('displays the primary button as an brand button', () => {
- const wrapper = mount(] }} />);
- const button = wrapper.find(Button);
- expect(button.length).toEqual(1);
- expect(button.props().variant).toEqual('brand');
+ it('displays the primary button as a brand button', () => {
+ render(
+ ] }}
+ />,
+ );
+ const button = screen.getByTestId('brand');
+ expect(button).toBeInTheDocument();
});
});
+
describe('with two actions', () => {
- const wrapper = mount();
- it('displays the user\'s first button as an brand button', () => {
- const buttons = wrapper.find(Button);
- expect(buttons.length).toEqual(2);
- expect(buttons.get(1).props.variant).toEqual('brand');
+ beforeEach(() => {
+ render(
+ ,
+ );
+ });
+
+ it('displays the user\'s first button as a brand button', () => {
+ expect(screen.getAllByRole('button').length).toEqual(2);
+ expect(screen.getAllByRole('button')[1].textContent).toBe(FIRST_ACTION);
});
+
it('displays the user\'s second button as an outline button', () => {
- const buttons = wrapper.find(Button);
- expect(buttons.get(0).props.variant).toEqual('outline-primary');
+ expect(screen.getAllByRole('button')[0].textContent).toBe(SECOND_ACTION);
});
+
it('reverses the button order so that the primary button is on the right', () => {
- const buttons = wrapper.find(Button);
- expect(buttons.get(1).props.variant).toEqual('brand');
- expect(buttons.get(0).props.variant).toEqual('outline-primary');
+ expect(screen.getAllByRole('button')[1].textContent).toBe(FIRST_ACTION);
+ expect(screen.getAllByRole('button')[0].textContent).toBe(SECOND_ACTION);
});
});
+
describe('controlled table selections', () => {
it('passed correct number of selected rows', () => {
- const wrapper = mount(] }} />);
- const button = wrapper.find(Button);
- expect(button.length).toEqual(1);
- expect(button.text()).toEqual('First Action');
+ render(
+ ] }} />,
+ );
+ const button = screen.getByText(FIRST_ACTION);
+ expect(button).toBeInTheDocument();
});
- it('handles action on click with full table selection (all rows across all pages)', () => {
+ it('handles action on click with full table selection (all rows across all pages)', async () => {
const onClickSpy = jest.fn();
- const wrapper = mount(
+ render(
', () => {
}}
/>,
);
- const button = wrapper.find(Button).at(1);
- button.simulate('click');
+ const button = screen.getAllByRole('button')[1];
+ await userEvent.click(button);
expect(onClickSpy).toHaveBeenCalledTimes(1);
});
});
+
describe('two actions on click', () => {
- it('performs the primary button action on click', () => {
+ it('performs the primary button action on click', async () => {
const onClickSpy = jest.fn();
- const wrapper = mount(
+ render(
, ] }}
/>,
);
- const button = wrapper.find(Button).at(1);
- button.simulate('click');
+ const button = screen.getAllByRole('button')[1];
+ await userEvent.click(button);
expect(onClickSpy).toHaveBeenCalledTimes(1);
});
- it('performs the second button action on click', () => {
+ it('performs the second button action on click', async () => {
const onClickSpy = jest.fn();
- const wrapper = mount(
+ render(
, ] }}
/>,
);
- const button = wrapper.find(Button).at(0);
- button.simulate('click');
+ const button = screen.getAllByRole('button')[0];
+ await userEvent.click(button);
expect(onClickSpy).toHaveBeenCalledTimes(1);
});
});
+
describe('with more than two actions', () => {
- it('displays the user\'s first button as an brand button', () => {
- const wrapper = mount();
- waitForComponentToPaint(wrapper);
- const buttons = wrapper.find(Button);
+ it('displays the user\'s first button as a brand button', () => {
+ render();
+ const buttons = screen.getAllByTestId('action');
expect(buttons.length).toEqual(2);
- expect(buttons.get(1).props.variant).toEqual('brand');
+ expect(buttons[1].textContent).toBe(FIRST_ACTION);
});
+
it('displays the user\'s second button as an outline button', () => {
- const wrapper = mount();
- waitForComponentToPaint(wrapper);
- const buttons = wrapper.find(Button);
- expect(buttons.get(0).props.variant).toEqual('outline-primary');
+ const { container } = render();
+ waitForComponentToPaint(container);
+ const buttons = screen.getAllByTestId('action');
+ expect(buttons[0].textContent).toBe(SECOND_ACTION);
});
+
describe('overflow menu', () => {
const onClickSpy = jest.fn();
- const itemClassName = 'itemClickTest';
- let wrapper;
- let overflowButton;
- beforeEach(() => {
- wrapper = mount(
+ const itemTestId = 'itemTestId';
+ beforeEach(async () => {
+ render(
],
+ bulkActions: [...instance.bulkActions, ],
}}
/>,
);
- waitForComponentToPaint(wrapper);
// the overflow toggle button is the first button
- overflowButton = wrapper.find(IconButton);
- overflowButton.simulate('click');
+ await userEvent.click(screen.getByRole('button', { name: ACTION_OVERFLOW_BUTTON_TEXT }));
});
afterEach(() => {
onClickSpy.mockClear();
});
- it('displays additional actions in a ModalPopup', () => {
- const overflowToggle = wrapper.find(IconButton);
- expect(overflowToggle.props().alt).toEqual(ACTION_OVERFLOW_BUTTON_TEXT);
- const actionItems = wrapper.find(ModalPopup).find('button');
- // we subtract two for the two main buttons that aren't in the overflow menu
+ it('displays additional actions in a ModalPopup', async () => {
+ const actionItems = screen.getAllByRole('button');
+ // subtract two for the two main buttons that aren't in the overflow menu
expect(actionItems.length).toEqual(4);
});
- it('performs actions when overflow items are clicked', () => {
- wrapper.find(`button.${itemClassName}`).simulate('click');
+ it('performs actions when overflow items are clicked', async () => {
+ const item = screen.getByTestId(itemTestId);
+ await userEvent.click(item);
expect(onClickSpy).toHaveBeenCalledTimes(1);
});
it('passes the class names to the dropdown item', () => {
- const item = wrapper.find(`button.${itemClassName}`);
- expect(item.length).toEqual(1);
+ const item = screen.getByTestId(itemTestId);
+ expect(item).toBeInTheDocument();
});
});
});
describe('small screen', () => {
const actions = [[[]], [[, ]], [instance.bulkActions]];
- test.each(actions)('puts all actions in a dropdown %#', (testActions) => {
+ test.each(actions)('puts all actions in a dropdown %#', async (testActions) => {
useWindowSize.mockReturnValue({ width: 500 });
- const wrapper = mount();
- const button = wrapper.find(IconButton);
- expect(button.length).toEqual(1);
- expect(wrapper.text()).not.toContain('First Action');
- const buttons = wrapper.find('button');
- expect(buttons.length).toEqual(1);
- const dropdownButton = buttons.at(0);
- dropdownButton.simulate('click');
- wrapper.update();
- expect(wrapper.text().length).toBeGreaterThan(0);
+ const { container } = render();
+ const button = screen.getByRole('button', { name: SMALL_SCREEN_ACTION_OVERFLOW_BUTTON_TEXT });
+ expect(button).toBeInTheDocument();
+ expect(container.textContent).not.toContain(FIRST_ACTION);
+ await userEvent.click(button);
+ expect(container.textContent.length).toBeGreaterThan(0);
});
it('renders the correct alt text for the dropdown', () => {
useWindowSize.mockReturnValue({ width: 500 });
- const wrapper = mount();
- const overflowToggle = wrapper.find(IconButton);
- expect(overflowToggle.props().alt).toEqual(SMALL_SCREEN_ACTION_OVERFLOW_BUTTON_TEXT);
+ render();
+ const overflowToggle = screen.getByRole('button', { name: SMALL_SCREEN_ACTION_OVERFLOW_BUTTON_TEXT });
+ expect(overflowToggle).toBeInTheDocument();
});
});
});
diff --git a/src/DataTable/tests/CardView.test.jsx b/src/DataTable/tests/CardView.test.jsx
index f3b9dbb1f3..2ec9a878b2 100644
--- a/src/DataTable/tests/CardView.test.jsx
+++ b/src/DataTable/tests/CardView.test.jsx
@@ -1,6 +1,5 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
-import '@testing-library/jest-dom';
import { selectColumn } from '../utils/getVisibleColumns';
import CardView, { DEFAULT_SKELETON_CARD_COUNT } from '../CardView';
import DataTableContext from '../DataTableContext';
diff --git a/src/DataTable/tests/DataTable.test.jsx b/src/DataTable/tests/DataTable.test.jsx
index 5f55ab2d17..1bb332df1d 100644
--- a/src/DataTable/tests/DataTable.test.jsx
+++ b/src/DataTable/tests/DataTable.test.jsx
@@ -1,19 +1,11 @@
import React, { useContext } from 'react';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
-import { act } from 'react-dom/test-utils';
-import { mount } from 'enzyme';
import * as reactTable from 'react-table';
import { IntlProvider } from 'react-intl';
-import '@testing-library/jest-dom';
import DataTable from '..';
-import TableControlBar from '../TableControlBar';
-import EmptyTable from '../EmptyTable';
-import Table from '../Table';
-import TableFooter from '../TableFooter';
import DataTableContext from '../DataTableContext';
-import { addSelectedRowAction } from '../selection/data/actions';
const additionalColumns = [
{
@@ -107,7 +99,7 @@ function DataTableContextProviderChild({ children }) {
const contextValue = useContext(DataTableContext);
return (
<>
-
+
{children}
>
);
@@ -128,54 +120,57 @@ describe('', () => {
beforeEach(() => {
jest.clearAllMocks();
});
+
it('displays the empty table component if empty', () => {
- const wrapper = mount();
- expect(wrapper.find(EmptyTable).length).toEqual(1);
+ render();
+ expect(screen.getByText('No results found')).toBeInTheDocument();
});
+
it('accepts an empty table component', () => {
- const wrapper = mount();
- expect(wrapper.find(EmptyTable).length).toEqual(0);
- expect(wrapper.find(EmptyTest).length).toEqual(1);
+ render();
+ expect(screen.queryByText('No results found')).not.toBeInTheDocument();
+ expect(screen.getByText(emptyTestText)).toBeInTheDocument();
});
+
it('displays a control bar', () => {
- const wrapper = mount();
- const controlBar = wrapper.find(TableControlBar);
- expect(controlBar.length).toEqual(1);
- expect(controlBar.text()).toEqual('Showing 7 of 7.');
+ render();
+ expect(screen.getByTestId('table-control-bar')).toBeInTheDocument();
+ expect(screen.getAllByText('Showing 7 of 7.')[0]).toBeInTheDocument();
});
+
it('displays a table', () => {
- const wrapper = mount();
- const table = wrapper.find(Table);
- expect(table.length).toEqual(1);
- expect(table.find('th').length).toEqual(3);
- expect(table.find('tr').length).toEqual(8);
+ render();
+ expect(screen.getAllByRole('columnheader')).toHaveLength(props.columns.length);
+ expect(screen.getAllByRole('row')).toHaveLength(props.data.length + 1); // (need + 1 to include header row)
});
+
it('displays a table footer', () => {
- const wrapper = mount();
- expect(wrapper.find(TableFooter).length).toEqual(1);
+ render();
+ expect(screen.getByTestId('table-footer')).toBeInTheDocument();
});
+
it('adds a column when table is selectable', () => {
- const wrapper = mount();
- const tableHeaders = wrapper.find(Table).find('th');
- expect(tableHeaders.length).toEqual(props.columns.length + 1);
+ render();
+ expect(screen.getAllByRole('columnheader')).toHaveLength(props.columns.length + 1); // (need + 1 extra to include selection)
});
+
it('adds additional columns', () => {
- const wrapper = mount();
- const tableHeaders = wrapper.find(Table).find('th');
- expect(tableHeaders.length).toEqual(props.columns.length + additionalColumns.length);
- expect(wrapper.text()).toContain(additionalColumns[0].Header);
- expect(wrapper.text()).toContain(additionalColumns[1].Header);
+ render();
+ expect(screen.getAllByRole('columnheader')).toHaveLength(props.columns.length + 2); // (original + 2 additional)
+ expect(screen.getByText('Action')).toBeInTheDocument();
+ expect(screen.getByText('More')).toBeInTheDocument();
});
- test('calls useTable with the data and columns', () => {
+ it('calls useTable with the data and columns', () => {
const spy = jest.spyOn(reactTable, 'useTable');
- mount();
+ render();
+ expect(spy).toHaveBeenCalledTimes(1);
expect(spy).toHaveBeenCalledTimes(1);
expect(spy.mock.calls[0][0].columns).toEqual(props.columns);
expect(spy.mock.calls[0][0].data).toEqual(props.data);
expect(spy.mock.calls[0][0].initialState).toEqual({});
expect(spy.mock.calls[0]).toHaveLength(2);
});
- test.each([
+ it.each([
[{}, { manualFilters: false, manualPagination: false, manualSortBy: false }],
[{ manualFilters: true, pageCount: 1 }, { manualFilters: true, manualPagination: false, manualSortBy: false }],
[{ manualPagination: true, pageCount: 1 }, { manualFilters: false, manualPagination: true, manualSortBy: false }],
@@ -184,7 +179,7 @@ describe('', () => {
[{ manualSortBy: true, manualFilters: true, manualPagination: true, pageCount: 1 }, { manualFilters: true, manualPagination: true, manualSortBy: true }],
])('calls useTable with the correct manual settings %#', (additionalProps, expected) => {
const spy = jest.spyOn(reactTable, 'useTable');
- mount();
+ render();
expect(spy.mock.calls[0][0].manualFilters).toEqual(expected.manualFilters);
expect(spy.mock.calls[0][0].manualPagination).toEqual(expected.manualPagination);
expect(spy.mock.calls[0][0].manualSortBy).toEqual(expected.manualSortBy);
@@ -192,70 +187,33 @@ describe('', () => {
it('passes the initial state to useTable', () => {
const spy = jest.spyOn(reactTable, 'useTable');
const initialState = { foo: 'bar' };
- mount();
+ render();
expect(spy.mock.calls[0][0].initialState).toEqual(initialState);
});
it('displays loading state', () => {
- const wrapper = mount();
- const tableContainer = wrapper.find('.pgn__data-table-container');
- const spinner = wrapper.find('.pgn__data-table-spinner');
- expect(tableContainer.hasClass('is-loading')).toEqual(true);
- expect(spinner.exists()).toEqual(true);
- });
+ render();
+ const tableContainer = screen.getByTestId('data-table-container');
+ const spinner = screen.getByTestId('data-table-spinner');
+ expect(tableContainer).toBeTruthy();
- // TODO: test that useTable is called with the correct arguments when isPaginated, isFilterable, isSelectable are used
- // TODO: test that fetchData is called correctly
+ expect(spinner).toBeTruthy();
+ });
describe('[legacy] controlled table selections', () => {
- it('passes initial controlledTableSelections to context', () => {
- const wrapper = mount(
+ it('passes initial controlledTableSelections to context', async () => {
+ render(
,
);
- const contextValue = wrapper.find('div.context-value').prop('data-contextvalue');
- const { controlledTableSelections } = contextValue;
- expect(controlledTableSelections).toEqual([
- { selectedRows: [], isEntireTableSelected: false },
- expect.any(Function),
- ]);
- });
- it('passes appropriate selection props to context with active selections', () => {
- const wrapper = mount(
- ,
- );
+ const contextDiv = screen.getByTestId('context-value');
+ expect(contextDiv).toBeInTheDocument();
- // verify there are no current selections
- let contextValue = wrapper.find('div.context-value').prop('data-contextvalue');
+ const contextValue = JSON.parse(contextDiv.getAttribute('data-contextvalue'));
expect(contextValue.controlledTableSelections).toEqual([
{ selectedRows: [], isEntireTableSelected: false },
- expect.any(Function),
+ null,
]);
-
- // select one row
- const [, selectionsDispatch] = contextValue.controlledTableSelections;
- const selectedRow = { id: 1 };
- const itemCount = 5;
- const action = addSelectedRowAction(selectedRow, itemCount);
- act(() => {
- selectionsDispatch(action);
- });
- wrapper.update();
-
- // verify there is one active selection and appropriate selection props are passed
- contextValue = wrapper.find('div.context-value').prop('data-contextvalue');
- expect(contextValue.controlledTableSelections).toEqual([
- { selectedRows: [selectedRow], isEntireTableSelected: false },
- expect.any(Function),
- ]);
- expect(contextValue.state).toEqual(
- expect.objectContaining({
- selectedRowIds: {
- [selectedRow.id]: true,
- },
- }),
- );
- expect(contextValue.selectedFlatRows).toEqual([selectedRow]);
});
});
@@ -263,8 +221,7 @@ describe('', () => {
beforeEach(() => {
jest.clearAllMocks();
});
-
- it('calls onSelectedRowsChanged when selected rows are updated', () => {
+ it('calls onSelectedRowsChanged when selected rows are updated', async () => {
const mockOnSelectedRowsChange = jest.fn();
const propsWithSelection = {
...props,
@@ -272,18 +229,16 @@ describe('', () => {
onSelectedRowsChanged: mockOnSelectedRowsChange,
};
render();
-
// select first row
- userEvent.click(screen.getAllByTestId('datatable-select-column-checkbox-cell')[0]);
+ await userEvent.click(screen.getAllByTestId('datatable-select-column-checkbox-cell')[0]);
expect(mockOnSelectedRowsChange).toHaveBeenCalledTimes(1);
expect(mockOnSelectedRowsChange).toHaveBeenCalledWith(
expect.objectContaining({
1: true,
}),
);
-
// select third row
- userEvent.click(screen.getAllByTestId('datatable-select-column-checkbox-cell')[2]);
+ await userEvent.click(screen.getAllByTestId('datatable-select-column-checkbox-cell')[2]);
expect(mockOnSelectedRowsChange).toHaveBeenCalledTimes(2);
expect(mockOnSelectedRowsChange).toHaveBeenCalledWith(
expect.objectContaining({
@@ -291,9 +246,8 @@ describe('', () => {
3: true,
}),
);
-
// unselect third row
- userEvent.click(screen.getAllByTestId('datatable-select-column-checkbox-cell')[2]);
+ await userEvent.click(screen.getAllByTestId('datatable-select-column-checkbox-cell')[2]);
expect(mockOnSelectedRowsChange).toHaveBeenCalledTimes(3);
expect(mockOnSelectedRowsChange).toHaveBeenCalledWith(
expect.objectContaining({
@@ -301,8 +255,7 @@ describe('', () => {
}),
);
});
-
- it('deselects all rows across all pages when `toggleAllRowsSelected(false)` is called', () => {
+ it('deselects all rows across all pages when `toggleAllRowsSelected(false)` is called', async () => {
const mockOnSelectedRowsChange = jest.fn();
const propsWithSelection = {
...props,
@@ -323,7 +276,7 @@ describe('', () => {
},
};
render();
- userEvent.click(screen.getByText('Clear selection'));
+ await userEvent.click(screen.getByText('Clear selection'));
expect(mockOnSelectedRowsChange).toHaveBeenCalledTimes(1);
expect(mockOnSelectedRowsChange).toHaveBeenCalledWith({});
diff --git a/src/DataTable/tests/DataViewToggle.test.jsx b/src/DataTable/tests/DataViewToggle.test.jsx
index 677747345a..8c10e83ba4 100644
--- a/src/DataTable/tests/DataViewToggle.test.jsx
+++ b/src/DataTable/tests/DataViewToggle.test.jsx
@@ -1,7 +1,5 @@
import React from 'react';
-
import { render, screen } from '@testing-library/react';
-import '@testing-library/jest-dom/extend-expect';
import { act } from 'react-dom/test-utils';
import userEvent from '@testing-library/user-event';
diff --git a/src/DataTable/tests/DropdownFilters.test.jsx b/src/DataTable/tests/DropdownFilters.test.jsx
index b35dc2f6b9..b48e443bba 100644
--- a/src/DataTable/tests/DropdownFilters.test.jsx
+++ b/src/DataTable/tests/DropdownFilters.test.jsx
@@ -1,9 +1,9 @@
import React from 'react';
-import { mount } from 'enzyme';
-import { act } from 'react-dom/test-utils';
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import DropdownFilters from '../DropdownFilters';
-import { useWindowSize, DropdownButton } from '../..';
+import { useWindowSize } from '../..';
import DataTableContext from '../DataTableContext';
jest.mock('../../hooks/useWindowSize');
@@ -30,65 +30,68 @@ const instance = {
// eslint-disable-next-line react/prop-types
function DropdownFiltersWrapper({ value = instance, props }) {
- return ;
+ return (
+
+
+
+ );
}
describe('', () => {
- afterAll(() => {
+ afterEach(() => {
jest.restoreAllMocks();
});
+
describe('non-mobile site', () => {
it('renders a breakout filter', () => {
useWindowSize.mockReturnValue({ width: 800 });
- const wrapper = mount();
- expect(wrapper.text()).toContain('Bears filter');
+ render();
+ expect(screen.getByText('Bears filter')).toBeInTheDocument();
});
+
it('renders additional filters in a dropdown', async () => {
useWindowSize.mockReturnValue({ width: 800 });
- const wrapper = mount();
+ render();
// filter should be rendered in the dropdown, so should not be present before
// clicking the button.
- expect(wrapper.text()).not.toContain('Occupation filter');
- const filtersButton = wrapper.find(DropdownButton);
- expect(filtersButton).toHaveLength(1);
- await act(async () => {
- filtersButton.find('button').simulate('click');
- });
- expect(wrapper.text()).toContain('Occupation filter');
+ expect(screen.queryByText('Occupation filter')).toBeNull();
+ const filtersButton = screen.getByRole('button', { name: /Filters/i });
+ await userEvent.click(filtersButton);
+ expect(screen.getByText('Occupation filter')).toBeInTheDocument();
});
+
it('should not render filters for non-filterable rows', async () => {
useWindowSize.mockReturnValue({ width: 800 });
- const wrapper = mount();
- expect(wrapper.text()).not.toContain('DOB filter');
- const filtersButton = wrapper.find('button');
- await act(async () => {
- filtersButton.simulate('click');
- });
- expect(wrapper.text()).not.toContain('DOB filter');
+ render();
+ expect(screen.queryByText('DOB filter')).toBeNull();
+ const filtersButton = screen.getByRole('button', { name: /Filters/i });
+ await userEvent.click(filtersButton);
+ expect(screen.queryByText('DOB filter')).toBeNull();
});
+
it('does not render a dropdown if there is only one filter', () => {
useWindowSize.mockReturnValue({ width: 800 });
- const wrapper = mount();
- expect(wrapper.text()).toContain('Occupation filter');
- expect(wrapper.find(DropdownButton)).toHaveLength(0);
+ render();
+ expect(screen.getByText('Occupation filter')).toBeInTheDocument();
+ expect(screen.queryByRole('button', { name: /Filters/i })).toBeNull();
});
});
+
describe('on mobile', () => {
it('does not render a breakout filter', () => {
useWindowSize.mockReturnValue({ width: 500 });
- const wrapper = mount();
- expect(wrapper.text()).not.toContain('Bears filter');
+ render();
+ expect(screen.queryByText('Bears filter')).toBeNull();
});
+
it('renders all filters in the dropdown', async () => {
useWindowSize.mockReturnValue({ width: 500 });
- const wrapper = mount();
- const filtersButton = wrapper.find('button');
- await act(async () => {
- filtersButton.simulate('click');
- });
- expect(wrapper.text()).toContain('Bears filter');
- expect(wrapper.text()).toContain('Occupation filter');
+ render();
+ const filtersButton = screen.getByRole('button', { name: /Filters/i });
+ await userEvent.click(filtersButton);
+ expect(screen.getByText('Bears filter')).toBeInTheDocument();
+ expect(screen.getByText('Occupation filter')).toBeInTheDocument();
});
});
});
diff --git a/src/DataTable/tests/EmptyTable.test.jsx b/src/DataTable/tests/EmptyTable.test.jsx
index f43965c6a3..32024ef321 100644
--- a/src/DataTable/tests/EmptyTable.test.jsx
+++ b/src/DataTable/tests/EmptyTable.test.jsx
@@ -1,5 +1,5 @@
import React from 'react';
-import { mount } from 'enzyme';
+import { render } from '@testing-library/react';
import EmptyTableContent from '../EmptyTable';
import DataTableContext from '../DataTableContext';
@@ -10,32 +10,44 @@ const props = {
};
describe('', () => {
- const wrapper = mount(
-
-
- ,
- );
it('displays the content', () => {
- expect(wrapper.text()).toEqual(props.content);
+ const { getByText } = render(
+
+
+ ,
+ );
+
+ expect(getByText(props.content)).toBeInTheDocument();
});
+
it('adds props to the div', () => {
- const cell = wrapper.find('div');
- expect(cell.props().className).toEqual(`pgn__data-table-empty ${props.className}`);
+ const { getByTestId } = render(
+
+
+ ,
+ );
+
+ const divElement = getByTestId('test-div');
+ expect(divElement).toHaveClass(`pgn__data-table-empty ${props.className}`);
});
+
it('does not display if there are rows', () => {
- const nonEmptyWrapper = mount(
+ const { container } = render(
,
);
- expect(nonEmptyWrapper.text()).toEqual('');
+
+ expect(container.textContent).toBe('');
});
+
it('does not display if the table data is loading', () => {
- const loadingWrapper = mount(
+ const { container } = render(
,
);
- expect(loadingWrapper.text()).toEqual('');
+
+ expect(container.textContent).toBe('');
});
});
diff --git a/src/DataTable/tests/ExpandAll.test.jsx b/src/DataTable/tests/ExpandAll.test.jsx
index 3d2d383d88..68ab1f9503 100644
--- a/src/DataTable/tests/ExpandAll.test.jsx
+++ b/src/DataTable/tests/ExpandAll.test.jsx
@@ -1,6 +1,7 @@
import React from 'react';
-import { mount } from 'enzyme';
+import { render } from '@testing-library/react';
import { IntlProvider } from 'react-intl';
+
import ExpandAll from '../ExpandAll';
function ExpandAllWrapper(props) {
@@ -13,19 +14,22 @@ function ExpandAllWrapper(props) {
describe('', () => {
it('renders expand all element if not all rows are expanded', () => {
- const wrapper = mount( {}} isAllRowsExpanded={false} />);
- const labelWrapper = wrapper.find('span');
- expect(labelWrapper.exists()).toEqual(true);
- const collapseButton = wrapper.find('button');
- expect(collapseButton.exists()).toEqual(true);
- expect(collapseButton.text()).toContain('Expand all');
+ const { getByText, getByRole } = render(
+ {}} isAllRowsExpanded={false} />,
+ );
+ const labelWrapper = getByText('Expand all');
+ const collapseButton = getByRole('button', { name: 'Expand all' });
+ expect(labelWrapper).toBeInTheDocument();
+ expect(collapseButton).toBeInTheDocument();
});
+
it('renders collapse all element if all rows are expanded', () => {
- const wrapper = mount( {}} isAllRowsExpanded />);
- const labelWrapper = wrapper.find('span');
- expect(labelWrapper.exists()).toEqual(true);
- const collapseButton = wrapper.find('button');
- expect(collapseButton.exists()).toEqual(true);
- expect(collapseButton.text()).toContain('Collapse all');
+ const { getByText, getByRole } = render(
+ {}} isAllRowsExpanded />,
+ );
+ const labelWrapper = getByText('Collapse all');
+ const collapseButton = getByRole('button', { name: 'Collapse all' });
+ expect(labelWrapper).toBeInTheDocument();
+ expect(collapseButton).toBeInTheDocument();
});
});
diff --git a/src/DataTable/tests/ExpandRow.test.jsx b/src/DataTable/tests/ExpandRow.test.jsx
index cbe45c30e8..da17e520f6 100644
--- a/src/DataTable/tests/ExpandRow.test.jsx
+++ b/src/DataTable/tests/ExpandRow.test.jsx
@@ -1,8 +1,7 @@
import React from 'react';
-import { mount } from 'enzyme';
-import { ExpandLess, ExpandMore } from '../../../icons';
+import { render } from '@testing-library/react';
+
import ExpandRow from '../ExpandRow';
-import { IconButton } from '../..';
const row = {
isExpanded: false,
@@ -11,19 +10,23 @@ const row = {
describe('', () => {
it('renders expand row element if rows is not expanded', () => {
- const wrapper = mount();
- const labelWrapper = wrapper.find('span');
- expect(labelWrapper.exists()).toEqual(true);
- const iconButton = wrapper.find(IconButton);
- expect(iconButton.prop('src')).toEqual(ExpandMore);
- expect(iconButton.prop('alt')).toEqual('Expand row');
+ const { getByTestId, getByLabelText } = render();
+
+ const labelWrapper = getByTestId('span-expand-row');
+ expect(labelWrapper).toBeInTheDocument();
+
+ const iconButton = getByLabelText('Expand row');
+ expect(iconButton).toBeInTheDocument();
});
+
it('renders collapse row element if row is expanded', () => {
- const wrapper = mount();
- const labelWrapper = wrapper.find('span');
- expect(labelWrapper.exists()).toEqual(true);
- const iconButton = wrapper.find(IconButton);
- expect(iconButton.prop('src')).toEqual(ExpandLess);
- expect(iconButton.prop('alt')).toEqual('Collapse row');
+ const expandedRow = { ...row, isExpanded: true };
+ const { getByTestId, getByLabelText } = render();
+
+ const labelWrapper = getByTestId('span-collapse-row');
+ expect(labelWrapper).toBeInTheDocument();
+
+ const iconButton = getByLabelText('Collapse row');
+ expect(iconButton).toBeInTheDocument();
});
});
diff --git a/src/DataTable/tests/FilterStatus.test.jsx b/src/DataTable/tests/FilterStatus.test.jsx
index b584367dd6..5a0ef41f63 100644
--- a/src/DataTable/tests/FilterStatus.test.jsx
+++ b/src/DataTable/tests/FilterStatus.test.jsx
@@ -1,9 +1,9 @@
import React from 'react';
-import { mount } from 'enzyme';
+import { render, screen } from '@testing-library/react';
import { IntlProvider } from 'react-intl';
+import userEvent from '@testing-library/user-event';
import FilterStatus from '../FilterStatus';
-import { Button } from '../..';
import DataTableContext from '../DataTableContext';
const filterNames = ['color', 'breed', 'discipline'];
@@ -37,38 +37,35 @@ function FilterStatusWrapper({ value, props }) {
describe('', () => {
it('passes props to the button', () => {
- const wrapper = mount();
- const buttonProps = wrapper.find(Button).props();
- expect(buttonProps.className).toEqual(filterProps.buttonClassName);
- expect(buttonProps.variant).toEqual(filterProps.variant);
- expect(buttonProps.size).toEqual(filterProps.size);
+ render();
+ const button = screen.getByText(filterProps.clearFiltersText);
+ expect(button).toHaveClass(filterProps.buttonClassName);
});
- it('sets the button text', () => {
- const wrapper = mount();
- expect(wrapper.find(Button).text()).toEqual(filterProps.clearFiltersText);
- });
- it('clears the selection on click', () => {
+ it('clears the selection on click', async () => {
const clearSpy = jest.fn();
- const wrapper = mount();
- wrapper.find(Button).simulate('click');
+ render(
+ ,
+ );
+ const button = screen.getByText(filterProps.clearFiltersText);
+ await userEvent.click(button);
expect(clearSpy).toHaveBeenCalledTimes(1);
expect(clearSpy).toHaveBeenCalledWith([]);
});
it('displays the current filter names', () => {
- const wrapper = mount();
- expect(wrapper.text()).toContain(filterNames.join(', '));
+ render();
+ expect(screen.getByText(`Filtered by ${filterNames.join(', ')}`)).toBeInTheDocument();
});
it('sets class names on the parent', () => {
- const wrapper = mount();
- const statusDiv = wrapper.find('div');
- expect(statusDiv.props().className).toEqual(filterProps.className);
+ const { container } = render();
+ const statusDiv = container.firstChild;
+ expect(statusDiv).toHaveClass(filterProps.className);
});
it('returns null if setAllFilters is not present (table is not filterable)', () => {
- const wrapper = mount();
- expect(wrapper.text()).toEqual('');
+ const { container } = render();
+ expect(container.firstChild).toBeNull();
});
it('hides filter text', () => {
- const wrapper = mount();
- expect(wrapper.text()).toEqual('');
+ render();
+ expect(screen.queryByText(filterProps.clearFiltersText)).toBeNull();
});
});
diff --git a/src/DataTable/tests/RowStatus.test.jsx b/src/DataTable/tests/RowStatus.test.jsx
index e906b649bd..587b746e69 100644
--- a/src/DataTable/tests/RowStatus.test.jsx
+++ b/src/DataTable/tests/RowStatus.test.jsx
@@ -1,5 +1,5 @@
import React from 'react';
-import { mount } from 'enzyme';
+import { render } from '@testing-library/react';
import { IntlProvider } from 'react-intl';
import RowStatus from '../RowStatus';
@@ -26,21 +26,24 @@ function RowStatusWrapper({ value = instance, props = statusProps }) {
describe('', () => {
it('returns null if there is no pageSize', () => {
- const wrapper = mount();
- expect(wrapper.text()).toEqual('');
+ const { container } = render();
+ expect(container.firstChild).toBeNull();
});
it('displays the row status with pagination', () => {
const pageSize = 10;
- const wrapper = mount();
- expect(wrapper.text()).toEqual(`Showing ${pageSize} of ${instance.itemCount}.`);
+ const { getByText } = render();
+ const statusText = getByText(`Showing ${pageSize} of ${instance.itemCount}.`);
+ expect(statusText).toBeInTheDocument();
});
it('displays the row status without pagination', () => {
const pageSize = 10;
- const wrapper = mount();
- expect(wrapper.text()).toEqual(`Showing ${pageSize} of ${instance.itemCount}.`);
+ const { getByText } = render();
+ const statusText = getByText(`Showing ${pageSize} of ${instance.itemCount}.`);
+ expect(statusText).toBeInTheDocument();
});
it('sets class names on the parent', () => {
- const wrapper = mount();
- expect(wrapper.find('div').props().className).toEqual(statusProps.className);
+ const { container } = render();
+ const statusDiv = container.querySelector('div');
+ expect(statusDiv).toHaveClass(statusProps.className);
});
});
diff --git a/src/DataTable/tests/SmartStatus.test.jsx b/src/DataTable/tests/SmartStatus.test.jsx
index 368d74f519..85d4573cb4 100644
--- a/src/DataTable/tests/SmartStatus.test.jsx
+++ b/src/DataTable/tests/SmartStatus.test.jsx
@@ -1,5 +1,5 @@
import React from 'react';
-import { mount } from 'enzyme';
+import { render } from '@testing-library/react';
import { IntlProvider } from 'react-intl';
import SmartStatus from '../SmartStatus';
@@ -46,41 +46,37 @@ describe('', () => {
},
},
};
- const wrapper = mount(
+ const { getByTestId } = render(
,
);
- expect(wrapper.find(SelectionStatus)).toHaveLength(1);
+ expect(getByTestId('selection-status')).toBeInTheDocument();
});
it('Shows the filter state with selection turned off', () => {
- const wrapper = mount();
- const status = wrapper.find(SmartStatus);
- expect(status.text()).toContain(filterNames.join(', '));
+ const { getByText } = render();
+ expect(getByText(`Filtered by ${filterNames.join(', ')}`)).toBeInTheDocument();
});
it('Shows the filter state when there are no selected rows', () => {
- const wrapper = mount(
+ const { getByText } = render(
,
);
- const status = wrapper.find(SmartStatus);
- expect(status.text()).toContain(filterNames.join(', '));
+ expect(getByText(`Filtered by ${filterNames.join(', ')}`)).toBeInTheDocument();
});
- it('Shows the number of items on the page if the there are no selected rows and no filters', () => {
- const wrapper = mount(
+ it('Shows the number of items on the page if there are no selected rows and no filters', () => {
+ const { getByText } = render(
,
);
- const status = wrapper.find(SmartStatus);
- expect(status.text()).toContain(`Showing ${instance.page.length} of ${itemCount}`);
+ expect(getByText(`Showing ${instance.page.length} of ${itemCount}.`)).toBeInTheDocument();
});
it('Shows the number of items on the page if selection is off and there are no filters', () => {
- const wrapper = mount(
+ const { getByText } = render(
,
);
- const status = wrapper.find(SmartStatus);
- expect(status.text()).toContain(`Showing ${instance.page.length} of ${itemCount}`);
+ expect(getByText(`Showing ${instance.page.length} of ${itemCount}.`)).toBeInTheDocument();
});
it('shows an alternate selection status', () => {
const altStatusText = 'horses R cool';
function AltStatus() {
- return {altStatusText}
;
+ return {altStatusText}
;
}
const contextValue = {
...instance,
@@ -92,27 +88,27 @@ describe('', () => {
},
},
};
- const wrapper = mount();
- expect(wrapper.text()).toContain(altStatusText);
+ const { getByTestId } = render();
+ expect(getByTestId('alternate-status')).toBeInTheDocument();
});
it('shows an alternate row status', () => {
const altStatusText = 'horses R cool';
function AltStatus() {
- return {altStatusText}
;
+ return {altStatusText}
;
}
- const wrapper = mount();
- expect(wrapper.text()).toContain(altStatusText);
+ expect(getByTestId('alternate-status')).toBeInTheDocument();
});
it('shows an alternate filter status', () => {
const altStatusText = 'horses R cool';
function AltStatus() {
- return {altStatusText}
;
+ return {altStatusText}
;
}
- const wrapper = mount();
- expect(wrapper.text()).toContain(altStatusText);
+ expect(getByTestId('alternate-status')).toBeInTheDocument();
});
});
diff --git a/src/DataTable/tests/Table.test.jsx b/src/DataTable/tests/Table.test.jsx
index 4093b6ef43..99b84aa526 100644
--- a/src/DataTable/tests/Table.test.jsx
+++ b/src/DataTable/tests/Table.test.jsx
@@ -1,6 +1,6 @@
import React from 'react';
-import { mount } from 'enzyme';
-import TableHeaderRow from '../TableHeaderRow';
+import { render } from '@testing-library/react';
+
import Table from '../Table';
import DataTableContext from '../DataTableContext';
@@ -64,26 +64,29 @@ function TableWrapper({ value = instance, props }) {
describe('DataTable ', () => {
it('renders a table header', () => {
- const wrapper = mount();
- const row = wrapper.find(TableHeaderRow);
- expect(row.length).toEqual(1);
+ const { getByText } = render();
+ const headerText = getByText(header1Name);
+ expect(headerText).toBeInTheDocument();
});
+
it('renders rows', () => {
- const wrapper = mount();
- const row = wrapper.find('tbody tr');
- expect(row.length).toEqual(1);
+ const { getByText } = render();
+ const rowText = getByText('Fido');
+ expect(rowText).toBeInTheDocument();
});
+
it('adds table props', () => {
const tableProps = {
summary: 'It is a table',
};
const getTablePropsSpy = jest.fn();
getTablePropsSpy.mockReturnValue(tableProps);
- const wrapper = mount();
- const table = wrapper.find('table');
+ const { getByRole } = render();
+ const table = getByRole('table');
- expect(table.props().summary).toEqual(tableProps.summary);
+ expect(table).toHaveAttribute('summary', tableProps.summary);
});
+
it('adds table body props', () => {
const tableProps = {
foo: 'bar',
@@ -91,13 +94,14 @@ describe('DataTable ', () => {
};
const getTableBodyPropsSpy = jest.fn();
getTableBodyPropsSpy.mockReturnValue(tableProps);
- const wrapper = mount();
- const table = wrapper.find('tbody');
- expect(table.props().foo).toEqual(tableProps.foo);
- expect(table.props().baz).toEqual(tableProps.baz);
+ const { getByRole } = render();
+ const tableBody = getByRole('table').querySelector('tbody');
+ expect(tableBody).toHaveAttribute('foo', tableProps.foo);
+ expect(tableBody).toHaveAttribute('baz', tableProps.baz);
});
+
it('returns null if the instance does not exist', () => {
- const wrapper = mount();
- expect(wrapper.text()).toEqual('');
+ const { container } = render();
+ expect(container.firstChild).toBeNull();
});
});
diff --git a/src/DataTable/tests/TableActions.test.jsx b/src/DataTable/tests/TableActions.test.jsx
index ced6077b33..0edbc36c5f 100644
--- a/src/DataTable/tests/TableActions.test.jsx
+++ b/src/DataTable/tests/TableActions.test.jsx
@@ -1,7 +1,7 @@
import React from 'react';
-import { mount } from 'enzyme';
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import classNames from 'classnames';
-
import TableActions from '../TableActions';
import {
ACTION_OVERFLOW_BUTTON_TEXT, SMALL_SCREEN_ACTION_OVERFLOW_BUTTON_TEXT,
@@ -9,28 +9,37 @@ import {
import {
useWindowSize,
Button,
- IconButton,
- ModalPopup,
} from '../..';
import DataTableContext from '../DataTableContext';
-import { waitForComponentToPaint } from './utils';
jest.mock('../../hooks/useWindowSize');
useWindowSize.mockReturnValue({ width: 800 });
-// eslint-disable-next-line react/prop-types
-function FirstAction({ as: Component, onClick, className }) {
+function FirstAction({
+ // eslint-disable-next-line react/prop-types
+ as: Component, onClick, className, ...rest
+}) {
return (
-
+
First Action
);
}
// eslint-disable-next-line react/prop-types
-function SecondAction({ onClick, className }) {
+function SecondAction({ onClick, className, ...rest }) {
return (
-
+
Second Action
);
@@ -39,21 +48,19 @@ function SecondAction({ onClick, className }) {
// eslint-disable-next-line react/prop-types
function ExtraAction({ text }) {
return (
-
+
{`Extra Action ${text}`}
);
}
-const selectedFlatRows = [{ id: 1 }, { id: 2 }];
-
const twoActions = [
- ,
- ,
+ ,
+ ,
];
const instance = {
- selectedFlatRows,
+ selectedFlatRows: [{ id: 1 }, { id: 2 }],
controlledTableSelections: [
{
selectedRows: [],
@@ -82,132 +89,143 @@ describe('', () => {
describe('with functional rendering', () => {
it('renders the function', () => {
const myFunction = () => Some Button;
- const wrapper = mount();
- const button = wrapper.find(Button);
- expect(button.length).toEqual(1);
+ render();
+ const button = screen.getByText('Some Button');
+ expect(button).toBeInTheDocument();
});
});
+
describe('with one action', () => {
- it('performs the button action on click', () => {
+ it('performs the button action on click', async () => {
const onClickSpy = jest.fn();
const tableInstance = { ...instance, tableActions: [] };
- const wrapper = mount(
- ,
- );
- const button = wrapper.find('button');
- expect(button.length).toEqual(1);
- button.simulate('click');
+ render();
+ const button = screen.getByText('First Action');
+ await userEvent.click(button);
expect(onClickSpy).toHaveBeenCalledTimes(1);
});
});
+
describe('with two actions', () => {
- const wrapper = mount();
- it('displays the user\'s first button as an brand button', () => {
- const buttons = wrapper.find(Button);
+ it('displays the user\'s first button as a brand button', () => {
+ render();
+ const buttons = screen.getAllByRole('button');
expect(buttons.length).toEqual(2);
- expect(buttons.get(0).props.variant).toEqual('outline-primary');
+ expect(buttons[0]).toHaveClass('btn-outline-primary');
});
+
it('displays the user\'s second button as an outline button', () => {
- const buttons = wrapper.find(Button);
- expect(buttons.get(1).props.variant).toEqual('brand');
+ render();
+ const buttons = screen.getAllByRole('button');
+ expect(buttons[1]).toHaveClass('btn-brand');
});
+
it('reverses the button order so that the primary button is on the right', () => {
- const buttons = wrapper.find(Button);
- expect(buttons.get(1).props.variant).toEqual('brand');
- expect(buttons.get(0).props.variant).toEqual('outline-primary');
+ render();
+ const buttons = screen.getAllByRole('button');
+ expect(buttons[0]).toHaveClass('btn-outline-primary');
+ expect(buttons[1]).toHaveClass('btn-brand');
});
});
+
describe('two actions on click', () => {
- it('performs the primary button action on click', () => {
+ it('performs the primary button action on click', async () => {
const onClickSpy = jest.fn();
const tableInstance = { ...instance, tableActions: [, ] };
- const wrapper = mount();
- const button = wrapper.find(Button).at(1);
- button.simulate('click');
+ render();
+ const button = screen.getByText('First Action');
+ await userEvent.click(button);
expect(onClickSpy).toHaveBeenCalledTimes(1);
});
- it('performs the second button action on click', () => {
+
+ it('performs the second button action on click', async () => {
const onClickSpy = jest.fn();
const tableInstance = { ...instance, tableActions: [, ] };
- const wrapper = mount();
- const button = wrapper.find(Button).at(0);
- button.simulate('click');
+ render();
+ const button = screen.getByText('Second Action');
+ await userEvent.click(button);
expect(onClickSpy).toHaveBeenCalledTimes(1);
});
});
+
describe('with more than two actions', () => {
- it('displays the user\'s first button as an brand button', () => {
- const wrapper = mount();
- waitForComponentToPaint(wrapper);
- const buttons = wrapper.find(Button);
+ it('displays the user\'s first button as a brand button', () => {
+ render();
+ const buttons = screen.getAllByTestId('action-btn');
expect(buttons.length).toEqual(2);
- expect(buttons.get(1).props.variant).toEqual('brand');
+ expect(buttons[1]).toHaveClass('btn-brand');
});
+
it('displays the user\'s second button as an outline button', () => {
- const wrapper = mount();
- waitForComponentToPaint(wrapper);
- const buttons = wrapper.find(Button);
- expect(buttons.get(0).props.variant).toEqual('outline-primary');
+ render();
+ const buttons = screen.getAllByTestId('action-btn');
+ expect(buttons[0]).toHaveClass('btn-outline-primary');
});
+
describe('overflow menu', () => {
const onClickSpy = jest.fn();
const itemClassName = 'itemClickTest';
- let tableInstance;
- let wrapper;
- let overflowButton;
- beforeEach(() => {
- tableInstance = {
- ...instance,
- tableActions: [...instance.tableActions, ],
- };
- wrapper = mount(
- ,
- );
- waitForComponentToPaint(wrapper);
+ const tableInstance = {
+ ...instance,
+ tableActions: [
+ ...instance.tableActions,
+ ,
+ ],
+ };
+
+ beforeEach(async () => {
+ render();
// the overflow toggle button is the first button
- overflowButton = wrapper.find(IconButton);
- overflowButton.simulate('click');
+ const overflowButton = screen.getByRole('button', { name: ACTION_OVERFLOW_BUTTON_TEXT });
+ await userEvent.click(overflowButton);
});
+
afterEach(() => {
onClickSpy.mockClear();
});
+
it('displays additional actions in a ModalPopup', () => {
- const overflowToggle = wrapper.find(IconButton);
- expect(overflowToggle.props().alt).toEqual(ACTION_OVERFLOW_BUTTON_TEXT);
- const actionItems = wrapper.find(ModalPopup).find('button');
- // we subtract two for the two main buttons that aren't in the dropdown
+ const actionItems = screen.getAllByRole('button');
expect(actionItems.length).toEqual(4);
});
- it('performs actions when dropdown items are clicked', () => {
- wrapper.find(`button.${itemClassName}`).simulate('click');
+
+ it('performs actions when dropdown items are clicked', async () => {
+ const item = screen.getByTestId('extra-first-action');
+ await userEvent.click(item);
expect(onClickSpy).toHaveBeenCalledTimes(1);
});
+
it('passes the class names to the dropdown item', () => {
- const item = wrapper.find(`button.${itemClassName}`);
- expect(item.length).toEqual(1);
+ const item = screen.getByTestId('extra-first-action');
+ expect(item).toHaveClass(itemClassName);
});
});
});
+
describe('small screen', () => {
const actions = [[[]], [[, ]], [instance.tableActions]];
+
test.each(actions)('puts all actions in a dropdown %#', (testActions) => {
useWindowSize.mockReturnValue({ width: 500 });
- const wrapper = mount();
- const iconButton = wrapper.find(IconButton);
- expect(iconButton.length).toEqual(1);
- const wrapperText = wrapper.text();
- const buttons = wrapper.find('button');
- expect(buttons.length).toEqual(1);
- const dropdownButton = buttons.at(0);
- dropdownButton.simulate('click');
- wrapper.update();
- expect(wrapper.text()).not.toEqual(wrapperText);
+ render();
+ const overflowToggle = screen.getByRole('button', { name: SMALL_SCREEN_ACTION_OVERFLOW_BUTTON_TEXT });
+ expect(overflowToggle).toBeInTheDocument();
+
+ userEvent.click(overflowToggle);
+
+ const buttons = screen.getAllByRole('button');
+ expect(buttons.length).toBeGreaterThan(1);
});
+
it('renders the correct alt text for the dropdown', () => {
useWindowSize.mockReturnValue({ width: 500 });
- const wrapper = mount();
- const overflowToggle = wrapper.find(IconButton);
- expect(overflowToggle.props().alt).toEqual(SMALL_SCREEN_ACTION_OVERFLOW_BUTTON_TEXT);
+ render();
+ const overflowToggle = screen.getByRole('button', { name: SMALL_SCREEN_ACTION_OVERFLOW_BUTTON_TEXT });
+ expect(overflowToggle).toBeInTheDocument();
});
});
});
diff --git a/src/DataTable/tests/TableCell.test.jsx b/src/DataTable/tests/TableCell.test.jsx
index 82aae3dfff..104313c12a 100644
--- a/src/DataTable/tests/TableCell.test.jsx
+++ b/src/DataTable/tests/TableCell.test.jsx
@@ -1,5 +1,5 @@
import React from 'react';
-import { mount } from 'enzyme';
+import { render, screen } from '@testing-library/react';
import TableCell from '../TableCell';
@@ -10,27 +10,27 @@ const props = {
};
describe('', () => {
- let wrapper;
- beforeEach(() => {
- // Rendering a cell outside of a table causes a warning, so it is wrapped the appropriate html
- wrapper = mount();
- });
-
it('renders a table cell', () => {
- const cell = wrapper.find('td');
- expect(cell.length).toEqual(1);
+ render();
+ const cell = screen.getByRole('cell');
+ expect(cell).toBeInTheDocument();
});
+
it('adds props to the cell', () => {
- const cell = wrapper.find('td');
- expect(cell.props().className).toEqual('pgn__data-table-cell-wrap red');
+ render();
+ const cell = screen.getByRole('cell');
+ expect(cell).toHaveClass('red');
});
+
it('renders cell content', () => {
- const cell = wrapper.find('td');
- expect(cell.text()).toEqual('Cell data');
+ render();
+ const cell = screen.getByRole('cell');
+ expect(cell).toBeInTheDocument();
});
+
it('adds class names to the cell span', () => {
const addedClass = 'align-me';
- wrapper = mount(
+ render(
@@ -39,7 +39,7 @@ describe('', () => {
,
);
- const cell = wrapper.find('td');
- expect(cell.props().className).toContain(addedClass);
+ const cell = screen.getByRole('cell');
+ expect(cell).toHaveClass(addedClass);
});
});
diff --git a/src/DataTable/tests/TableFooter.test.jsx b/src/DataTable/tests/TableFooter.test.jsx
index e3fe43fc18..c2ec03f8db 100644
--- a/src/DataTable/tests/TableFooter.test.jsx
+++ b/src/DataTable/tests/TableFooter.test.jsx
@@ -1,8 +1,8 @@
import React from 'react';
-import { mount } from 'enzyme';
+import { render, screen } from '@testing-library/react';
+import { IntlProvider } from 'react-intl';
+
import TableFooter from '../TableFooter';
-import RowStatus from '../RowStatus';
-import TablePagination from '../TablePagination';
import DataTableContext from '../DataTableContext';
const footerInstance = {
@@ -14,39 +14,48 @@ const footerInstance = {
pageCount: 3,
itemCount: 30,
RowStatusComponent: undefined,
+ page: [1],
};
// eslint-disable-next-line react/prop-types
function TableFooterWrapper({ value = footerInstance, props = {}, children }) {
return (
-
- {children}
-
+
+
+ {children}
+
+
);
}
describe('', () => {
it('Renders the default footer', () => {
- const wrapper = mount();
- expect(wrapper.find(RowStatus)).toHaveLength(1);
- expect(wrapper.find(TablePagination)).toHaveLength(1);
+ render();
+ expect(screen.getByTestId('row-status')).toBeInTheDocument();
+ expect(screen.getByLabelText('table pagination')).toBeInTheDocument();
});
+
it('accepts a class name', () => {
const fakeClass = 'fancy-class';
- const wrapper = mount();
- expect(wrapper.find(TableFooter).props().className).toContain(fakeClass);
+ render();
+ const footer = screen.getByTestId('table-footer');
+ expect(footer).toHaveClass(fakeClass);
});
- it('renders a children', () => {
+
+ it('renders children', () => {
const leftText = "I'm on the left";
- const wrapper = mount({leftText}
);
- expect(wrapper.text()).toContain(leftText);
+ render({leftText}
);
+ const childDiv = screen.getByText(leftText);
+ expect(childDiv).toBeInTheDocument();
});
+
it('uses custom RowStatus component, if provided', () => {
const dataTableContextValue = {
...footerInstance,
RowStatusComponent: () => Hello world
,
};
- const wrapper = mount();
- expect(wrapper.text()).toContain('Hello world');
+ render();
+ const customRowStatus = screen.getByText('Hello world');
+ expect(customRowStatus).toBeInTheDocument();
});
});
diff --git a/src/DataTable/tests/TableHeaderCell.test.jsx b/src/DataTable/tests/TableHeaderCell.test.jsx
index 56bfa870e9..31542fd932 100644
--- a/src/DataTable/tests/TableHeaderCell.test.jsx
+++ b/src/DataTable/tests/TableHeaderCell.test.jsx
@@ -1,7 +1,7 @@
import React from 'react';
-import { mount } from 'enzyme';
+import { render, screen } from '@testing-library/react';
-import TableHeaderCell, { SortIndicator } from '../TableHeaderCell';
+import TableHeaderCell from '../TableHeaderCell';
const sortByToggleProps = { foo: 'bar' };
const props = {
@@ -15,61 +15,51 @@ const props = {
};
// eslint-disable-next-line react/prop-types
-function FakeTable({ children }) {
- return ;
+function FakeTable({ ...rest }) {
+ return ;
}
describe('', () => {
describe('unsorted', () => {
- const wrapper = mount();
+ render();
+ const cell = screen.getByRole('columnheader');
+ const innerCell = cell.firstChild;
+
it('renders a table header cell', () => {
- const cell = wrapper.find('th');
- expect(cell.length).toEqual(1);
+ expect(cell).toBeInTheDocument();
});
+
it('adds props to the cell', () => {
- const cell = wrapper.find('th');
- expect(cell.props().className).toEqual('red');
- });
- it('renders cell content', () => {
- const cell = wrapper.find('th');
- expect(cell.text()).toEqual('Title');
+ expect(cell.className).toBe('red');
});
+
it('adds the headerClassName to inner span', () => {
- const innerCell = wrapper.find('th span').at(0);
- expect(innerCell.props().className).toContain(props.headerClassName);
+ expect(innerCell.className).toContain(props.headerClassName);
});
});
+
describe('with sorting', () => {
it('renders a sortable indicator if sorting is available', () => {
- const wrapper = mount();
- expect(wrapper.find(SortIndicator).props().isSorted).toBe(false);
- expect(wrapper.find(SortIndicator).props().isSortedDesc).toBe(false);
+ render();
+ const sortIndicator = screen.getByTestId('arrow-drop-up-down');
+ expect(sortIndicator).toBeInTheDocument();
});
+
it('renders a sorted ascending indicator when sorted ascending', () => {
- const wrapper = mount(
-
-
- ,
- );
- expect(wrapper.find(SortIndicator).props().isSorted).toBe(true);
- expect(wrapper.find(SortIndicator).props().isSortedDesc).toBe(false);
+ render();
+ const sortIndicator = screen.getByTestId('arrow-drop-up');
+ expect(sortIndicator).toBeInTheDocument();
});
- it('renders a sorted descending indicator when sorted ascending', () => {
- const wrapper = mount(
-
-
- ,
- );
- expect(wrapper.find(SortIndicator).props().isSorted).toBe(true);
- expect(wrapper.find(SortIndicator).props().isSortedDesc).toBe(true);
+
+ it('renders a sorted descending indicator when sorted descending', () => {
+ render();
+ const sortIndicator = screen.getByTestId('arrow-drop-down');
+ expect(sortIndicator).toBeInTheDocument();
});
+
it('adds the toggle props to the header props if toggle props are available', () => {
const headerPropsSpy = jest.fn().mockReturnValueOnce({});
- mount(
-
-
- ,
- );
+ render();
expect(headerPropsSpy).toHaveBeenCalledWith(sortByToggleProps);
});
});
diff --git a/src/DataTable/tests/TableHeaderRow.test.jsx b/src/DataTable/tests/TableHeaderRow.test.jsx
index de9a847f4e..feccb65511 100644
--- a/src/DataTable/tests/TableHeaderRow.test.jsx
+++ b/src/DataTable/tests/TableHeaderRow.test.jsx
@@ -1,5 +1,5 @@
import React from 'react';
-import { mount } from 'enzyme';
+import { render, screen } from '@testing-library/react';
import TableHeaderRow from '../TableHeaderRow';
@@ -32,21 +32,23 @@ const props = {
};
describe('', () => {
- const wrapper = mount();
+ render();
+ const head = screen.getByRole('rowgroup');
+ const row = screen.getByRole('row');
+ const cells = screen.getAllByRole('columnheader');
+
it('renders a table head and row', () => {
- const head = wrapper.find('thead');
- expect(head.length).toEqual(1);
- const row = wrapper.find('tr');
- expect(row.length).toEqual(1);
+ expect(head).toBeInTheDocument();
+ expect(row).toBeInTheDocument();
});
+
it('adds props to the row', () => {
- const row = wrapper.find('tr');
- expect(row.props().className).toEqual('red');
+ expect(row.className).toEqual('red');
});
+
it('renders cells', () => {
- const cells = wrapper.find('th');
expect(cells.length).toEqual(2);
- expect(wrapper.text()).toContain(header1Name);
- expect(wrapper.text()).toContain(header2Name);
+ expect(cells[0]).toHaveTextContent(header1Name);
+ expect(cells[1]).toHaveTextContent(header2Name);
});
});
diff --git a/src/DataTable/tests/TablePagination.test.jsx b/src/DataTable/tests/TablePagination.test.jsx
index 1da0c87073..da03995281 100644
--- a/src/DataTable/tests/TablePagination.test.jsx
+++ b/src/DataTable/tests/TablePagination.test.jsx
@@ -1,40 +1,49 @@
import React from 'react';
-import { mount } from 'enzyme';
+import { render, act } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import TablePagination from '../TablePagination';
-import { Dropdown } from '../..';
import DataTableContext from '../DataTableContext';
const instance = {
state: { pageIndex: 1 },
pageCount: 3,
- gotoPage: () => {},
+ gotoPage: jest.fn(),
};
// eslint-disable-next-line react/prop-types
function PaginationWrapper({ value }) {
- return ;
+ return (
+
+
+
+ );
}
describe('', () => {
it('returns null if pagination is not possible', () => {
- const wrapper = mount();
- expect(wrapper.text()).toEqual('');
+ const { container } = render();
+ expect(container.textContent).toBe('');
});
- it('Shows dropdown button with the page count as label and performs actions when dropdown items are clicked', () => {
- const gotoPageSpy = jest.fn();
- const wrapper = mount();
- const dropdown = wrapper.find(Dropdown);
- expect(dropdown.text()).toContain(`${instance.state.pageIndex + 1} of ${instance.pageCount}`);
- const dropdownButton = wrapper.find('button');
- dropdownButton.simulate('click');
- const dropdownChoices = wrapper.find(Dropdown.Item);
- expect(dropdownChoices.length).toEqual(instance.pageCount);
+ it(
+ 'Shows dropdown button with the page count as label and performs actions when dropdown items are clicked',
+ async () => {
+ const { getAllByTestId, getByRole } = render();
+ const dropdownButton = getByRole('button', { name: /2 of 3/i });
+ expect(dropdownButton).toBeInTheDocument();
+ await act(async () => {
+ await userEvent.click(dropdownButton);
+ });
- const secondPageButton = dropdownChoices.at(1);
- secondPageButton.simulate('click');
- expect(gotoPageSpy).toHaveBeenCalledTimes(1);
- expect(gotoPageSpy).toHaveBeenCalledWith(1);
- });
+ const dropdownChoices = getAllByTestId('pagination-dropdown-item');
+ expect(dropdownChoices.length).toEqual(instance.pageCount);
+ await act(async () => {
+ await userEvent.click(dropdownChoices[1], undefined, { skipPointerEventsCheck: true });
+ });
+
+ expect(instance.gotoPage).toHaveBeenCalledTimes(1);
+ expect(instance.gotoPage).toHaveBeenCalledWith(1);
+ },
+ );
});
diff --git a/src/DataTable/tests/TableRow.test.jsx b/src/DataTable/tests/TableRow.test.jsx
index 46e64e4779..17a46e7c03 100644
--- a/src/DataTable/tests/TableRow.test.jsx
+++ b/src/DataTable/tests/TableRow.test.jsx
@@ -1,5 +1,5 @@
import React from 'react';
-import { mount } from 'enzyme';
+import { render, screen } from '@testing-library/react';
import TableRow from '../TableRow';
import DataTableContext from '../DataTableContext';
@@ -30,49 +30,61 @@ const props = {
};
const contextValue = {
- /* eslint-disable-next-line react/prop-types */
renderRowSubComponent: ({ row }) => {row.values.name}
,
visibleColumns: [...props.cells],
};
-/* eslint-disable-next-line react/prop-types */
+// eslint-disable-next-line react/prop-types
function TableRowWrapper({ value, row }) {
return (
-
+
);
}
describe('', () => {
- const wrapper = mount();
it('renders a table row', () => {
- const row = wrapper.find('tr');
- expect(row.length).toEqual(1);
+ render();
+ const row = screen.getByRole('row');
+ expect(row).toBeInTheDocument();
});
+
it('adds props to the row', () => {
- const row = wrapper.find('tr');
- expect(row.props().className).toEqual('red');
+ render();
+ const row = screen.getByRole('row');
+ expect(row).toHaveClass('red');
});
+
it('renders cells', () => {
- const cells = wrapper.find('td');
- expect(cells.length).toEqual(2);
- expect(wrapper.text()).toContain('Fido');
- expect(wrapper.text()).toContain('Bones');
+ render();
+ expect(screen.getByText('Fido')).toBeInTheDocument();
+ expect(screen.getByText('Bones')).toBeInTheDocument();
});
+
it('renders subcomponent if row is in expanded state and has a renderRowSubComponent function defined', () => {
- const tableWrapper = mount();
- const rows = tableWrapper.find('tr');
+ render(
+ ,
+ );
+ const rows = screen.getAllByRole('row');
expect(rows.length).toEqual(2);
- const subcomponentWrapper = rows.at(1);
- expect(subcomponentWrapper.find('div').exists()).toEqual(true);
- expect(subcomponentWrapper.text()).toContain('Fido');
- expect(subcomponentWrapper.find('td').props().colSpan).toEqual(2);
+ const subcomponentWrapper = rows[1];
+ expect(subcomponentWrapper.querySelector('div')).toBeInTheDocument();
+ expect(screen.getAllByText('Fido')[1]).toBeInTheDocument();
+ expect(subcomponentWrapper.querySelector('td')).toHaveAttribute('colSpan', '2');
});
+
it('does not render subcomponent if row is in expanded state and does not have renderRowSubComponent function defined', () => {
- const tableWrapper = mount();
- const rows = tableWrapper.find('tr');
+ const { container } = render();
+ const rows = screen.getAllByRole('row');
expect(rows.length).toEqual(1);
- expect(tableWrapper.find('div').exists()).toEqual(false);
+ expect(container.querySelector('div')).not.toBeInTheDocument();
});
});
diff --git a/src/Dropdown/Dropdown.test.jsx b/src/Dropdown/Dropdown.test.jsx
index bfb51e3406..b1747d12d6 100644
--- a/src/Dropdown/Dropdown.test.jsx
+++ b/src/Dropdown/Dropdown.test.jsx
@@ -1,10 +1,10 @@
import React from 'react';
-import { mount } from 'enzyme';
-import { render, screen, waitFor } from '@testing-library/react';
-import '@testing-library/jest-dom/extend-expect';
+import {
+ render, screen, waitFor, act,
+} from '@testing-library/react';
import userEvent from '@testing-library/user-event';
-import Dropdown from './index';
+import Dropdown from '.';
const mockOnToggle = jest.fn();
@@ -27,9 +27,10 @@ describe('', () => {
const props = {
...baseProps,
};
- const wrapper = mount();
- expect(wrapper.exists()).toEqual(true);
+ render();
+ expect(screen.getByTestId('dropdown')).toBeInTheDocument();
});
+
it('Dropdown functions without autoClose or show props', async () => {
render(
@@ -41,28 +42,39 @@ describe('', () => {
,
);
+
expect(screen.queryByText('foobar')).not.toBeInTheDocument();
expect(screen.getByText('Dropdown Button')).toBeInTheDocument();
// Open the dropdown
const button = screen.getByText('Dropdown Button');
- userEvent.click(button);
+ await act(async () => {
+ await userEvent.click(button);
+ });
+
// Expect the dropdown item to be visible
await waitFor(() => expect(screen.queryByText('foobar')).toBeVisible());
// Close the dropdown by clicking off the element
- userEvent.click(document.body);
+ await act(async () => {
+ userEvent.click(document.body);
+ });
await waitFor(() => expect(screen.queryByText('foobar')).not.toBeVisible());
// Reopen the dropdown
- userEvent.click(button);
+ await act(async () => {
+ userEvent.click(button);
+ });
await waitFor(() => expect(screen.queryByText('foobar')).toBeVisible());
- // Close the dropdown by clicking the item
+ // Close the dropdown by clicking the item
const dropdownItem = screen.getByText('foobar');
- userEvent.click(dropdownItem);
+ await act(async () => {
+ userEvent.click(dropdownItem);
+ });
await waitFor(() => expect(screen.queryByText('foobar')).not.toBeVisible());
});
+
it('Dropdown functions when autoClose is outside', async () => {
const props = {
...outsideAutoCloseProps,
@@ -77,28 +89,39 @@ describe('', () => {
,
);
+
expect(screen.queryByText('foobar')).not.toBeInTheDocument();
expect(screen.getByText('Dropdown Button')).toBeInTheDocument();
// Open the dropdown
const button = screen.getByText('Dropdown Button');
- userEvent.click(button);
+ await act(async () => {
+ userEvent.click(button);
+ });
+
// Expect the dropdown item to be visible
await waitFor(() => expect(screen.queryByText('foobar')).toBeVisible());
// Close the dropdown by clicking off the element
- userEvent.click(document.body);
+ await act(async () => {
+ userEvent.click(document.body);
+ });
await waitFor(() => expect(screen.queryByText('foobar')).not.toBeVisible());
// Reopen the dropdown
- userEvent.click(button);
+ await act(async () => {
+ userEvent.click(button);
+ });
await waitFor(() => expect(screen.queryByText('foobar')).toBeVisible());
// Assert the dropdown stays open when clicking the item
const dropdownItem = screen.getByText('foobar');
- userEvent.click(dropdownItem);
+ await act(async () => {
+ userEvent.click(dropdownItem);
+ });
await waitFor(() => expect(screen.queryByText('foobar')).toBeVisible());
});
+
it('Dropdown functions when autoClose is inside', async () => {
const props = {
...insideAutoCloseProps,
@@ -113,22 +136,30 @@ describe('', () => {
,
);
+
expect(screen.queryByText('foobar')).not.toBeInTheDocument();
expect(screen.getByText('Dropdown Button')).toBeInTheDocument();
// Open the dropdown
const button = screen.getByText('Dropdown Button');
- userEvent.click(button);
+ await act(async () => {
+ await userEvent.click(button);
+ });
+
// Expect the dropdown item to be visible
await waitFor(() => expect(screen.queryByText('foobar')).toBeVisible());
// Assert the dropdown stays open when clicking outside the dropdown
- userEvent.click(document.body);
+ await act(async () => {
+ await userEvent.click(document.body);
+ });
await waitFor(() => expect(screen.queryByText('foobar')).toBeVisible());
// Close the dropdown by clicking the element
const dropdownItem = screen.getByText('foobar');
- userEvent.click(dropdownItem);
+ await act(async () => {
+ userEvent.click(dropdownItem);
+ });
await waitFor(() => expect(screen.queryByText('foobar')).not.toBeVisible());
});
});
diff --git a/src/Dropdown/deprecated/Dropdown.test.jsx b/src/Dropdown/deprecated/Dropdown.test.jsx
index da4bb6d769..e95bbc1045 100644
--- a/src/Dropdown/deprecated/Dropdown.test.jsx
+++ b/src/Dropdown/deprecated/Dropdown.test.jsx
@@ -1,6 +1,7 @@
import React from 'react';
-import { mount } from 'enzyme';
+import { render } from '@testing-library/react';
import renderer from 'react-test-renderer';
+import userEvent from '@testing-library/user-event';
import Dropdown from './index';
import Icon from '../../Icon';
@@ -19,9 +20,8 @@ const menuContent = (
);
const menuOpen = (isOpen, wrapper) => {
- expect(wrapper.find('.dropdown').hasClass('show')).toEqual(isOpen);
- expect(wrapper.find('button').prop('aria-expanded')).toEqual(isOpen);
- expect(wrapper.find('[aria-hidden=false]').exists()).toEqual(isOpen);
+ expect(wrapper.container.querySelector('.dropdown').classList.contains('show')).toBe(isOpen);
+ expect(wrapper.getByRole('button', { name: 'Search Engines' })).toHaveAttribute('aria-expanded', isOpen ? 'true' : 'false');
};
describe('', () => {
@@ -55,160 +55,184 @@ describe('', () => {
});
describe('Mouse Interactions', () => {
- const app = document.createElement('div');
- document.body.appendChild(app);
+ let wrapper;
- const wrapper = mount({menuContent}, { attachTo: app });
- const menuTrigger = wrapper.find('button');
- const menuContainer = wrapper.find('.dropdown-menu');
- const menuItems = wrapper.find('.dropdown-menu a');
+ beforeEach(() => {
+ wrapper = render({menuContent});
+ });
- it('opens on trigger click', () => {
- menuTrigger.simulate('click'); // Open
+ it('opens on trigger click', async () => {
+ await userEvent.click(wrapper.getByRole('button', { name: 'Search Engines' }));
menuOpen(true, wrapper);
});
- it('should focus on the first item after opening', () => {
- expect(menuItems.first().is(':focus')).toBe(true);
+ it('should focus on the first item after opening', async () => {
+ await userEvent.click(wrapper.getByRole('button', { name: 'Search Engines' }));
+ expect(wrapper.getByText('Google')).toHaveFocus();
});
- it('does not close on click inside the menu', () => {
- menuContainer.simulate('click'); // Do nothing
+ it('does not close on click inside the menu', async () => {
+ await userEvent.click(wrapper.getByRole('button', { name: 'Search Engines' }));
+ await userEvent.click(wrapper.getByText('Google')); // Do nothing
menuOpen(true, wrapper);
});
- it('closes on trigger click', () => {
- menuTrigger.simulate('click'); // Close
+ it('closes on trigger click', async () => {
+ await userEvent.click(wrapper.getByRole('button', { name: 'Search Engines' }));
+ await userEvent.click(wrapper.getByRole('button', { name: 'Search Engines' })); // Close
menuOpen(false, wrapper);
});
- it('should focus on the trigger button after closing', () => {
- expect(menuTrigger.is(':focus')).toBe(true);
+ it('should focus on the trigger button after closing', async () => {
+ await userEvent.click(wrapper.getByRole('button', { name: 'Search Engines' }));
+ await userEvent.click(wrapper.getByRole('button', { name: 'Search Engines' })); // Close
+ expect(wrapper.getByRole('button', { name: 'Search Engines' })).toHaveFocus();
});
- it('closes on document click when open', () => {
- menuTrigger.simulate('click'); // Open
+ it('closes on document click when open', async () => {
+ await userEvent.click(wrapper.getByRole('button', { name: 'Search Engines' }));
menuOpen(true, wrapper);
document.dispatchEvent(new MouseEvent('click'));
- wrapper.update(); // Let react re-render
menuOpen(false, wrapper);
});
});
describe('Keyboard Interactions', () => {
- // Note: menuContent has three items
- const app = document.createElement('div');
- document.body.appendChild(app);
+ let wrapper;
+
+ beforeEach(() => {
+ wrapper = render({menuContent});
+ });
- const wrapper = mount({menuContent}, { attachTo: app });
- const menuTrigger = wrapper.find('button');
- const menuContainer = wrapper.find('.dropdown-menu');
- const menuItems = wrapper.find('.dropdown-menu a');
+ it('opens on Enter keyDown', async () => {
+ wrapper.getByRole('button', { name: 'Search Engines' }).focus();
+ await userEvent.keyboard('{Enter}');
+ menuOpen(true, wrapper);
+ });
- it('opens on click', () => {
- menuTrigger.simulate('click'); // Open
+ it('opens on Space keyDown', async () => {
+ wrapper.getByRole('button', { name: 'Search Engines' }).focus();
+ await userEvent.keyboard('{space}');
menuOpen(true, wrapper);
});
- it('should focus on the first item after opening', () => {
- expect(menuItems.first().is(':focus')).toBe(true);
+ it('should focus on the first item after opening', async () => {
+ wrapper.getByRole('button', { name: 'Search Engines' }).focus();
+ await userEvent.keyboard('{Enter}');
+ expect(wrapper.getByText('Google')).toHaveFocus();
});
- it('should focus the next item after ArrowDown keyDown', () => {
- menuContainer.simulate('keyDown', { key: 'ArrowDown' });
- expect(menuItems.at(1).is(':focus')).toBe(true);
+ it('should focus the next item after ArrowDown keyDown', async () => {
+ wrapper.getByRole('button', { name: 'Search Engines' }).focus();
+ await userEvent.keyboard('{Enter}');
+ await userEvent.keyboard('{arrowdown}');
+ expect(wrapper.getByText('DuckDuckGo')).toHaveFocus();
});
- it('should focus the next item after Tab keyDown', () => {
- menuContainer.simulate('keyDown', { key: 'Tab' });
- expect(menuItems.at(2).is(':focus')).toBe(true);
+ it('should focus the next item after Tab keyDown', async () => {
+ wrapper.getByRole('button', { name: 'Search Engines' }).focus();
+ await userEvent.keyboard('{Enter}');
+ await userEvent.tab();
+ expect(wrapper.getByText('DuckDuckGo')).toHaveFocus();
});
- it('should loop focus to the first item after Tab keyDown on last item', () => {
- menuContainer.simulate('keyDown', { key: 'Tab' });
- expect(menuItems.at(0).is(':focus')).toBe(true);
+ it('should loop focus to the first item after Tab keyDown on last item', async () => {
+ wrapper.getByRole('button', { name: 'Search Engines' }).focus();
+ await userEvent.keyboard('{Enter}');
+ wrapper.getByRole('link', { name: 'Yahoo' }).focus();
+ await userEvent.tab();
+ expect(wrapper.getByText('Google')).toHaveFocus();
});
- it('should loop focus to the last item after ArrowUp keyDown on first item', () => {
- menuContainer.simulate('keyDown', { key: 'ArrowUp' });
- expect(menuItems.at(2).is(':focus')).toBe(true);
+ it('should loop focus to the last item after ArrowUp keyDown on first item', async () => {
+ wrapper.getByRole('button', { name: 'Search Engines' }).focus();
+ await userEvent.keyboard('{Enter}');
+ wrapper.getByRole('link', { name: 'Google' }).focus();
+ await userEvent.keyboard('{arrowup}');
+ expect(wrapper.getByText('Yahoo')).toHaveFocus();
});
- it('should focus the previous item after Shift + Tab keyDown', () => {
- menuContainer.simulate('keyDown', { key: 'Tab', shiftKey: true });
- expect(menuItems.at(1).is(':focus')).toBe(true);
+ it('should focus the previous item after Shift + Tab keyDown', async () => {
+ wrapper.getByRole('button', { name: 'Search Engines' }).focus();
+ await userEvent.keyboard('{Enter}');
+ wrapper.getByRole('link', { name: 'Yahoo' }).focus();
+ await userEvent.keyboard('{Shift>}{Tab}');
+ expect(wrapper.getByText('DuckDuckGo')).toHaveFocus();
});
- it('should close the menu on Escape keyDown', () => {
- menuContainer.simulate('keyDown', { key: 'Escape' });
+ it('should close the menu on Escape keyDown', async () => {
+ wrapper.getByRole('button', { name: 'Search Engines' }).focus();
+ await userEvent.keyboard('{Enter}');
+ await userEvent.keyboard('{escape}');
menuOpen(false, wrapper);
});
- it('should focus on the trigger button after closing', () => {
- expect(menuTrigger.is(':focus')).toBe(true);
+ it('should focus on the trigger button after closing', async () => {
+ wrapper.getByRole('button', { name: 'Search Engines' }).focus();
+ await userEvent.keyboard('{Enter}');
+ await userEvent.keyboard('{escape}');
+ expect(wrapper.getByRole('button', { name: 'Search Engines' })).toHaveFocus();
});
});
- describe('Backwards compatibility', () => {
- it('renders the basic usage', () => {
- const tree = renderer.create((
-
- )).toJSON();
- expect(tree).toMatchSnapshot();
- });
+ it('renders the basic usage', () => {
+ const tree = renderer.create((
+
+ )).toJSON();
+ expect(tree).toMatchSnapshot();
+ });
- it('renders menu items as elements', () => {
- const tree = renderer.create((
- Google,
- DuckDuckGo,
- Yahoo,
- ]}
- />
- )).toJSON();
- expect(tree).toMatchSnapshot();
- });
+ it('renders menu items as elements', () => {
+ const tree = renderer.create((
+ Google,
+ DuckDuckGo,
+ Yahoo,
+ ]}
+ />
+ )).toJSON();
+ expect(tree).toMatchSnapshot();
+ });
- it('renders with icon element', () => {
- const tree = renderer.create((
- }
- menuItems={[
- {
- label: 'Google',
- href: 'https://google.com',
- },
- {
- label: 'DuckDuckGo',
- href: 'https://duckduckgo.com',
- },
- {
- label: 'Yahoo',
- href: 'https://yahoo.com',
- },
- ]}
- />
- )).toJSON();
- expect(tree).toMatchSnapshot();
- });
+ it('renders with icon element', () => {
+ const tree = renderer.create((
+ }
+ menuItems={[
+ {
+ label: 'Google',
+ href: 'https://google.com',
+ },
+ {
+ label: 'DuckDuckGo',
+ href: 'https://duckduckgo.com',
+ },
+ {
+ label: 'Yahoo',
+ href: 'https://yahoo.com',
+ },
+ ]}
+ />
+ )).toJSON();
+ expect(tree).toMatchSnapshot();
});
});
diff --git a/src/Dropdown/deprecated/__snapshots__/Dropdown.test.jsx.snap b/src/Dropdown/deprecated/__snapshots__/Dropdown.test.jsx.snap
index 0a859c5a74..1a3ebf5bab 100644
--- a/src/Dropdown/deprecated/__snapshots__/Dropdown.test.jsx.snap
+++ b/src/Dropdown/deprecated/__snapshots__/Dropdown.test.jsx.snap
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[` Backwards compatibility renders menu items as elements 1`] = `
+exports[` Rendering renders the happy path 1`] = `
@@ -8,7 +8,7 @@ exports[`
Backwards compatibility renders menu items as elements 1`]
aria-expanded={false}
aria-haspopup={true}
className="dropdown-toggle btn btn-light"
- id="pgn__dropdown-trigger-6"
+ id="pgn__dropdown-trigger-0"
onClick={[Function]}
type="button"
>
@@ -16,26 +16,26 @@ exports[`
Backwards compatibility renders menu items as elements 1`]
Google
DuckDuckGo
Yahoo
@@ -43,7 +43,7 @@ exports[`
Backwards compatibility renders menu items as elements 1`]
`;
-exports[`
Backwards compatibility renders the basic usage 1`] = `
+exports[`
Rendering renders when there is html content in the trigger button 1`] = `
@@ -51,7 +51,7 @@ exports[`
Backwards compatibility renders the basic usage 1`] = `
aria-expanded={false}
aria-haspopup={true}
className="dropdown-toggle btn btn-light"
- id="pgn__dropdown-trigger-5"
+ id="pgn__dropdown-trigger-1"
onClick={[Function]}
type="button"
>
@@ -59,7 +59,7 @@ exports[`
Backwards compatibility renders the basic usage 1`] = `
Backwards compatibility renders the basic usage 1`] = `
`;
-exports[`
Backwards compatibility renders with icon element 1`] = `
+exports[`
Rendering renders with custom menu content 1`] = `
+
+ Custom Content
+
+`;
+
+exports[`
renders menu items as elements 1`] = `
@@ -94,39 +102,34 @@ exports[`
Backwards compatibility renders with icon element 1`] = `
aria-expanded={false}
aria-haspopup={true}
className="dropdown-toggle btn btn-light"
- id="pgn__dropdown-trigger-7"
+ id="pgn__dropdown-trigger-20"
onClick={[Function]}
type="button"
>
-
Search Engines
Google
DuckDuckGo
Yahoo
@@ -134,7 +137,7 @@ exports[`
Backwards compatibility renders with icon element 1`] = `
`;
-exports[`
Rendering renders the happy path 1`] = `
+exports[`
renders the basic usage 1`] = `
@@ -142,7 +145,7 @@ exports[`
Rendering renders the happy path 1`] = `
aria-expanded={false}
aria-haspopup={true}
className="dropdown-toggle btn btn-light"
- id="pgn__dropdown-trigger-2"
+ id="pgn__dropdown-trigger-19"
onClick={[Function]}
type="button"
>
@@ -150,7 +153,7 @@ exports[`
Rendering renders the happy path 1`] = `
Rendering renders the happy path 1`] = `
`;
-exports[`
Rendering renders when there is html content in the trigger button 1`] = `
+exports[`
renders with icon element 1`] = `
@@ -185,15 +188,20 @@ exports[`
Rendering renders when there is html content in the trigge
aria-expanded={false}
aria-haspopup={true}
className="dropdown-toggle btn btn-light"
- id="pgn__dropdown-trigger-3"
+ id="pgn__dropdown-trigger-21"
onClick={[Function]}
type="button"
>
+
Search Engines
Rendering renders when there is html content in the trigge
`;
-
-exports[`
Rendering renders with custom menu content 1`] = `
-
- Custom Content
-
-`;
diff --git a/src/Dropzone/GenericError.jsx b/src/Dropzone/GenericError.jsx
index 6054c3c32d..23480fb410 100644
--- a/src/Dropzone/GenericError.jsx
+++ b/src/Dropzone/GenericError.jsx
@@ -3,12 +3,13 @@ import PropTypes from 'prop-types';
import Alert from '../Alert';
import { Info } from '../../icons';
-function GenericError({ errorMsgs }) {
+function GenericError({ errorMsgs, ...rest }) {
return (
{errorMsgs.map(msg => (
{msg}
diff --git a/src/Dropzone/UploadProgress.jsx b/src/Dropzone/UploadProgress.jsx
index 7b30e6efe3..5c6ab394b6 100644
--- a/src/Dropzone/UploadProgress.jsx
+++ b/src/Dropzone/UploadProgress.jsx
@@ -14,6 +14,7 @@ function UploadProgress({
animation="border"
aria-live="polite"
screenReaderText={`Uploading ${name}, ${percent}% done.`}
+ data-testid="upload-spinner"
/>
);
}
@@ -34,6 +35,7 @@ function UploadProgress({
label={`${percent}%`}
variant="success"
className="flex-grow-1"
+ data-testid="upload-progress-bar"
/>
', () => {
it('renders error message', () => {
- const wrapper = mount();
- const content = wrapper.find('div.pgn__dropzone-error-wrapper');
- expect(content.text()).toEqual('Drag error message');
+ const { getByText } = render();
+ const errorMessage = getByText('Drag error message');
+ expect(errorMessage).toBeInTheDocument();
});
});
diff --git a/src/Dropzone/tests/Dropzone.test.jsx b/src/Dropzone/tests/Dropzone.test.jsx
index d7ae071f2d..f53c5106d9 100644
--- a/src/Dropzone/tests/Dropzone.test.jsx
+++ b/src/Dropzone/tests/Dropzone.test.jsx
@@ -9,7 +9,6 @@ import {
fireEvent,
screen,
} from '@testing-library/react';
-import '@testing-library/jest-dom';
import { formatBytes } from '../utils';
import messages from '../messages';
diff --git a/src/Dropzone/tests/GenericError.test.jsx b/src/Dropzone/tests/GenericError.test.jsx
index 71bd933fdf..e66cb08cbb 100644
--- a/src/Dropzone/tests/GenericError.test.jsx
+++ b/src/Dropzone/tests/GenericError.test.jsx
@@ -1,20 +1,25 @@
import React from 'react';
-import { mount } from 'enzyme';
+import { render } from '@testing-library/react';
import { IntlProvider } from 'react-intl';
+
import GenericError from '../GenericError';
-import { Alert } from '../..';
describe('', () => {
it('renders Alert component with messages', () => {
const messages = ['First error message', 'Second error message'];
- const wrapper = mount();
- const alert = wrapper.find(Alert);
- expect(alert.exists()).toEqual(true);
- expect(alert.hasClass('pgn__dropzone-generic-alert')).toEqual(true);
- const contents = wrapper.find('span');
- expect(contents.length).toEqual(3);
- expect(contents.at(0).hasClass('pgn__icon')).toEqual(true);
- expect(contents.at(1).text()).toEqual('First error message');
- expect(contents.at(2).text()).toEqual('Second error message');
+ const { getByTestId, getAllByText } = render(
+
+
+ ,
+ );
+
+ const alert = getByTestId('generic-error-alert');
+ expect(alert).toBeInTheDocument();
+ expect(alert).toHaveClass('pgn__dropzone-generic-alert');
+
+ const contents = getAllByText(/error message/i);
+ expect(contents).toHaveLength(2);
+ expect(contents[0]).toHaveTextContent('First error message');
+ expect(contents[1]).toHaveTextContent('Second error message');
});
});
diff --git a/src/Dropzone/tests/UploadProgress.test.jsx b/src/Dropzone/tests/UploadProgress.test.jsx
index bc47f95bd6..7dbd3e72f0 100644
--- a/src/Dropzone/tests/UploadProgress.test.jsx
+++ b/src/Dropzone/tests/UploadProgress.test.jsx
@@ -1,8 +1,9 @@
import React from 'react';
-import { mount } from 'enzyme';
+import { render, screen } from '@testing-library/react';
import { IntlProvider } from 'react-intl';
+import userEvent from '@testing-library/user-event';
+
import UploadProgress from '../UploadProgress';
-import { Spinner, ProgressBar, Button } from '../..';
const onCancel = jest.fn();
@@ -26,20 +27,22 @@ function UploadProgressWrapper({ children, ...props }) {
describe('', () => {
it('renders spinner if receives "spinner" as a variant prop', () => {
- const wrapper = mount();
- const spinner = wrapper.find(Spinner);
- expect(spinner.exists()).toEqual(true);
- expect(spinner.props().screenReaderText).toEqual(`Uploading ${defaultProps.name}, ${defaultProps.percent}% done.`);
+ render();
+ const spinner = screen.getByTestId('upload-spinner');
+ expect(spinner).toBeInTheDocument();
+ expect(spinner).toHaveTextContent(`Uploading ${defaultProps.name}, ${defaultProps.percent}% done.`);
});
- it('renders progress bar if receives "bar" as a variant prop', () => {
- const wrapper = mount();
- const bar = wrapper.find(ProgressBar);
- expect(bar.exists()).toEqual(true);
- expect(bar.props().now).toEqual(defaultProps.percent);
- expect(bar.props().label).toEqual(`${defaultProps.percent}%`);
- const cancelButton = wrapper.find(Button);
- expect(cancelButton.exists()).toEqual(true);
- cancelButton.simulate('click');
+
+ it('renders progress bar if receives "bar" as a variant prop', async () => {
+ render();
+ const progressBar = screen.getByTestId('upload-progress-bar');
+ expect(progressBar).toBeInTheDocument();
+ expect(progressBar).toHaveTextContent(`${defaultProps.percent}%`);
+
+ const cancelButton = screen.getByText('Cancel');
+ expect(cancelButton).toBeInTheDocument();
+
+ await userEvent.click(cancelButton);
expect(onCancel).toHaveBeenCalledTimes(1);
});
});
diff --git a/src/Fieldset/Fieldset.test.jsx b/src/Fieldset/Fieldset.test.jsx
index d852268a00..2f1f459f0c 100644
--- a/src/Fieldset/Fieldset.test.jsx
+++ b/src/Fieldset/Fieldset.test.jsx
@@ -1,9 +1,7 @@
import React from 'react';
-import { mount } from 'enzyme';
+import { render } from '@testing-library/react';
-import Fieldset from './index';
-import newId from '../utils/newId';
-import ValidationMessage from '../ValidationMessage';
+import Fieldset from '.';
import Variant from '../utils/constants';
const dangerVariant = {
@@ -28,78 +26,76 @@ const baseProps = {
variantIconDescription,
};
-const mockedNextId = 'fieldset1';
-
-// Cannot reference variables inside of a jest mock function: https://github.com/facebook/jest/issues/2567
jest.mock('../utils/newId', () => jest.fn().mockReturnValue('fieldset1'));
describe('Fieldset', () => {
- let wrapper;
-
- beforeEach(() => {
- const props = {
- ...baseProps,
- };
- wrapper = mount();
- });
it('renders', () => {
- const fieldset = wrapper.find('fieldset.form-control');
- expect(fieldset.exists()).toEqual(true);
- expect(fieldset.hasClass('is-invalid-nodanger')).toEqual(true);
- expect(fieldset.prop('aria-describedby')).toEqual(`error-${id}`);
- const legendElem = fieldset.find('legend');
- expect(legendElem.text()).toEqual(legend);
- expect(fieldset.text()).toEqual(legend + children);
- const feedback = wrapper.find(ValidationMessage);
- expect(feedback.prop('id')).toEqual(`error-${id}`);
+ const { getByTestId, getByText } = render(
+ ,
+ );
+
+ const fieldset = getByTestId('fieldset');
+ expect(fieldset).toBeInTheDocument();
+ expect(fieldset).toHaveClass('is-invalid-nodanger');
+ expect(fieldset).toHaveAttribute('aria-describedby', `error-${id}`);
+
+ const legendElem = getByText(legend);
+ expect(legendElem).toBeInTheDocument();
+
+ const feedback = getByTestId('validation-message');
+ expect(feedback).toHaveAttribute('id', `error-${id}`);
});
+
it('renders with auto-generated id if not specified', () => {
const props = {
...baseProps,
id: undefined,
};
- wrapper = mount();
- const feedback = wrapper.find(ValidationMessage);
- expect(feedback.prop('id')).toEqual('error-fieldset1');
+
+ const { getByTestId } = render();
+ const feedback = getByTestId('validation-message');
+ expect(feedback).toHaveAttribute('id', 'error-fieldset1');
});
+
it('renders invalidMessage when isValid is false', () => {
- wrapper.setProps({ isValid: false });
- const feedback = wrapper.find(ValidationMessage);
- expect(feedback.prop('invalidMessage')).toEqual(invalidMessage);
+ const { getByTestId } = render(
+ ,
+ );
+ const feedback = getByTestId('validation-message');
+ expect(feedback).toHaveTextContent(invalidMessage);
});
+
it('renders with danger variant when isValid is false and variant is DANGER', () => {
- wrapper.setProps({ isValid: false, variant: dangerVariant });
- const feedback = wrapper.find(ValidationMessage);
- expect(feedback.hasClass('invalid-feedback-nodanger')).toEqual(false);
- expect(feedback.prop('variantIconDescription')).toEqual(variantIconDescription);
- expect(feedback.prop('invalidMessage')).toEqual(invalidMessage);
- expect(feedback.prop('variant')).toEqual(dangerVariant);
+ const { getByTestId } = render(
+ ,
+ );
+ const feedback = getByTestId('validation-message');
+ expect(feedback).not.toHaveClass('invalid-feedback-nodanger');
+ expect(feedback).toHaveTextContent(invalidMessage);
});
+
it('receives new id when a valid one is passed to props', () => {
const nextId = 'new-id';
- let fieldset = wrapper.find('fieldset.form-control');
- let feedback = wrapper.find(ValidationMessage);
- expect(fieldset.prop('aria-describedby')).toEqual(`error-${id}`);
- expect(feedback.prop('id')).toEqual(`error-${id}`);
-
- wrapper.setProps({ id: nextId });
- fieldset = wrapper.find('fieldset.form-control');
- feedback = wrapper.find(ValidationMessage);
- expect(fieldset.prop('aria-describedby')).toEqual(`error-${nextId}`);
- expect(feedback.prop('id')).toEqual(`error-${nextId}`);
+
+ const { getByTestId } = render();
+ const fieldset = getByTestId('fieldset');
+ const feedback = getByTestId('validation-message');
+
+ expect(fieldset).toHaveAttribute('aria-describedby', `error-${nextId}`);
+ expect(feedback).toHaveAttribute('id', `error-${nextId}`);
});
+
it('auto-generates new id when an invalid one is passed to props', () => {
const nextId = '';
- let fieldset = wrapper.find('fieldset.form-control');
- let feedback = wrapper.find(ValidationMessage);
- expect(fieldset.prop('aria-describedby')).toEqual(`error-${id}`);
- expect(feedback.prop('id')).toEqual(`error-${id}`);
-
- wrapper.setProps({ id: nextId });
- expect(newId).toHaveBeenCalledWith('fieldset');
- fieldset = wrapper.find('fieldset.form-control');
- feedback = wrapper.find(ValidationMessage);
- expect(fieldset.prop('aria-describedby')).toEqual(`error-${mockedNextId}`);
- expect(feedback.prop('id')).toEqual(`error-${mockedNextId}`);
+
+ const { getByTestId } = render();
+ const fieldset = getByTestId('fieldset');
+ const feedback = getByTestId('validation-message');
+
+ expect(fieldset).toHaveAttribute(
+ 'aria-describedby',
+ 'error-fieldset1',
+ );
+ expect(feedback).toHaveAttribute('id', 'error-fieldset1');
});
});
diff --git a/src/Fieldset/index.jsx b/src/Fieldset/index.jsx
index 56905a46d0..6cd7f8d942 100644
--- a/src/Fieldset/index.jsx
+++ b/src/Fieldset/index.jsx
@@ -84,6 +84,7 @@ class Fieldset extends React.Component {
className,
)}
aria-describedby={errorId}
+ data-testid="fieldset"
>
{children}
diff --git a/src/Form/tests/FormCheckbox.test.jsx b/src/Form/tests/FormCheckbox.test.jsx
index 2ca1904980..bcd133bc36 100644
--- a/src/Form/tests/FormCheckbox.test.jsx
+++ b/src/Form/tests/FormCheckbox.test.jsx
@@ -1,14 +1,17 @@
import React from 'react';
-import { mount } from 'enzyme';
+import { render, screen, waitFor } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+
import FormCheckbox from '../FormCheckbox';
import FormGroup from '../FormGroup';
import FormLabel from '../FormLabel';
-describe('FormCheckbox', () => {
- const handleChange = jest.fn();
- const handleFocus = jest.fn();
- const handleBlur = jest.fn();
- const wrapper = mount((
+const handleChange = jest.fn();
+const handleFocus = jest.fn();
+const handleBlur = jest.fn();
+
+function FormCheckboxComponent() {
+ return (
{
>
Green
- ));
- const inputNode = wrapper.find('input[value="green"]').first();
+ );
+}
+describe('FormCheckbox', () => {
it('renders an input with a name and value', () => {
- wrapper.exists('input[value="green"]');
- expect(inputNode.props().name).toBe('color');
- });
+ render(FormCheckboxComponent());
- 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');
+ const inputNode = screen.getByLabelText('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', () => {
+ render(FormCheckboxComponent());
+
+ const inputNode = screen.getByLabelText('Green');
+ const describerNode = screen.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',
- }),
- );
+ it('calls the change handler', async () => {
+ render(FormCheckboxComponent());
+
+ const inputNode = screen.getByLabelText('Green');
+ await userEvent.type(inputNode, 'green');
+
+ waitFor(() => {
+ expect(handleChange).toHaveBeenCalledWith(
+ expect.objectContaining({
+ target: expect.objectContaining({ value: 'green' }),
+ type: 'change',
+ }),
+ );
+ });
});
- it('calls the focus handler', () => {
- inputNode.simulate('focus');
+ it('calls the focus handler', async () => {
+ render(FormCheckboxComponent());
+
+ const inputNode = screen.getByLabelText('Green');
+ inputNode.focus();
+
expect(handleFocus).toHaveBeenCalledWith(
expect.objectContaining({
target: expect.objectContaining({ value: 'green' }),
@@ -61,8 +75,13 @@ describe('FormCheckbox', () => {
);
});
- it('calls the blur handler', () => {
- inputNode.simulate('blur');
+ it('calls the blur handler', async () => {
+ render(FormCheckboxComponent());
+
+ const inputNode = screen.getByLabelText('Green');
+ inputNode.focus();
+ await userEvent.tab();
+
expect(handleBlur).toHaveBeenCalledWith(
expect.objectContaining({
target: expect.objectContaining({ value: 'green' }),
@@ -73,32 +92,24 @@ describe('FormCheckbox', () => {
});
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', () => {
+ render(
+
+ Group Label
+
+ Green
+
+ ,
+ );
+
+ const groupNode = screen.getByText('Group Label');
+ expect(groupNode).toBeInTheDocument();
});
});
diff --git a/src/Form/tests/FormCheckboxSet.test.jsx b/src/Form/tests/FormCheckboxSet.test.jsx
index 6194ccf205..be5c0a9082 100644
--- a/src/Form/tests/FormCheckboxSet.test.jsx
+++ b/src/Form/tests/FormCheckboxSet.test.jsx
@@ -1,108 +1,116 @@
import React from 'react';
-import { mount } from 'enzyme';
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+
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
+
+ ,
+ );
+}
+
+const setValue = jest.fn();
+function renderFormWithoutLabel() {
+ return render(
+ {
+ setValue(e.target.value);
+ }}
+ >
+ red
+ green
+ blue
+ ,
+ );
+}
+
+const onChange = jest.fn();
+const onBlur = jest.fn();
+const onFocus = jest.fn();
+
+function renderFormWithHandlers() {
+ return render(
+
+ red
+ green
+ blue
+ ,
+ );
+}
+
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');
+ renderFormCheckbox();
+ const groupNode = screen.getByRole('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', () => {
+ renderFormCheckbox();
+ const labelNode = screen.getByLabelText('Which color?');
+ expect(labelNode).toBeInTheDocument();
+ const groupNode = screen.getByRole('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');
+ renderFormCheckbox();
+ const describerIds = screen.getByDisplayValue('blue');
+ expect(describerIds).toBeInTheDocument();
+ expect(describerIds.getAttribute('aria-describedby')).toBeTruthy();
+ const descriptionNode = screen.getByText('Blue description');
+ expect(descriptionNode.textContent).toBe('Blue description');
});
});
describe('controlled behavior', () => {
- const setValue = jest.fn();
- const wrapper = mount((
- {
- setValue(e.target.value);
- }}
- >
- red
- green
- blue
-
- ));
-
it('checks the right checkbox button', () => {
- const checkboxNode = wrapper.find('input[value="red"]').first();
- expect(checkboxNode.props().checked).toBe(true);
+ renderFormWithoutLabel();
+ const checkboxNode = screen.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 () => {
+ renderFormWithoutLabel();
+ const checkboxNode = screen.getByLabelText('green');
+ await userEvent.type(checkboxNode, 'green');
expect(setValue).toHaveBeenCalledWith('green');
});
});
describe('event handlers', () => {
- const onChange = jest.fn();
- const onBlur = jest.fn();
- const onFocus = jest.fn();
- const wrapper = mount((
-
- red
- green
- blue
-
- ));
-
- const checkboxNode = wrapper.find('input[value="green"]').first();
-
- it('calls the change handlers with the right value', () => {
- checkboxNode.simulate('change');
+ it('calls the change handlers with the right value', async () => {
+ renderFormWithHandlers();
+ const checkboxNode = screen.getByLabelText('green');
+ await userEvent.type(checkboxNode, 'green');
expect(onChange).toHaveBeenCalledWith(
expect.objectContaining({
target: expect.objectContaining({ value: 'green' }),
@@ -110,8 +118,12 @@ describe('FormCheckboxSet', () => {
}),
);
});
- it('calls the focus handler', () => {
- checkboxNode.simulate('focus');
+
+ it('calls the focus handler', async () => {
+ renderFormWithHandlers();
+ const checkboxNode = screen.getByLabelText('green');
+ checkboxNode.focus();
+ await userEvent.tab();
expect(onFocus).toHaveBeenCalledWith(
expect.objectContaining({
target: expect.objectContaining({ value: 'green' }),
@@ -119,8 +131,12 @@ describe('FormCheckboxSet', () => {
}),
);
});
- it('calls the blur handler', () => {
- checkboxNode.simulate('blur');
+
+ it('calls the blur handler', async () => {
+ renderFormWithHandlers();
+ const checkboxNode = screen.getByLabelText('green');
+ checkboxNode.focus();
+ await userEvent.tab();
expect(onBlur).toHaveBeenCalledWith(
expect.objectContaining({
target: expect.objectContaining({ value: 'green' }),
@@ -131,22 +147,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);
+ render(
+
+ red
+ green
+ blue
+ ,
+ );
+
+ const checkboxNode = screen.getByLabelText('red');
+ expect(checkboxNode.defaultChecked).toBe(true);
});
});
});
diff --git a/src/Form/tests/FormControl.test.jsx b/src/Form/tests/FormControl.test.jsx
index 9702464f7e..95044c3f75 100644
--- a/src/Form/tests/FormControl.test.jsx
+++ b/src/Form/tests/FormControl.test.jsx
@@ -1,5 +1,6 @@
import React from 'react';
-import { mount } from 'enzyme';
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
import FormControl from '../FormControl';
@@ -8,19 +9,26 @@ const ref = {
};
describe('FormControl', () => {
- it('textarea changes its height with autoResize prop', () => {
+ it('textarea changes its height with autoResize prop', async () => {
const useReferenceSpy = jest.spyOn(React, 'useRef').mockReturnValue(ref);
const onChangeFunc = jest.fn();
- const wrapper = mount((
-
- ));
+ const inputText = 'new text';
+ render(
+ ,
+ );
+
ref.scrollHeight = 180;
ref.offsetHeight = 90;
ref.clientHeight = 88;
+
+ const textarea = screen.getByTestId('form-control-textarea');
+
expect(useReferenceSpy).toHaveBeenCalledTimes(1);
expect(ref.current.style.height).toBe('0px');
- wrapper.find('textarea').simulate('change');
- expect(onChangeFunc).toHaveBeenCalledTimes(1);
- expect(ref.current.style.height).toEqual(`${ref.current.scrollHeight + ref.current.offsets}px`);
+
+ await userEvent.type(textarea, inputText);
+
+ expect(onChangeFunc).toHaveBeenCalledTimes(inputText.length);
+ 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 e3c1be3706..afc4518d03 100644
--- a/src/Form/tests/FormControlDecoratorGroup.test.jsx
+++ b/src/Form/tests/FormControlDecoratorGroup.test.jsx
@@ -1,55 +1,59 @@
import React from 'react';
-import { mount } from 'enzyme';
+import { render, screen } from '@testing-library/react';
import { FormGroupContext } from '../FormGroupContext';
import FormControlDecoratorGroup from '../FormControlDecoratorGroup';
import { FORM_CONTROL_SIZES } from '../constants';
-describe('FormFieldDecoratorGroup', () => {
+describe('FormControlDecoratorGroup', () => {
it('renders', () => {
- const wrapper = mount((
+ 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(screen.getByText('before')).toBeInTheDocument();
+ expect(screen.getByText('after')).toBeInTheDocument();
+ expect(screen.getByText('label')).toBeInTheDocument();
});
+
it('renders a size reflecting a context', () => {
- const wrapper = mount((
+ render(
Form control
-
- ));
- const groupNode = wrapper.find(FormControlDecoratorGroup).first().childAt(0);
- expect(groupNode.props().className).toContain('-lg');
+ ,
+ );
+ const groupNode = screen.getByTestId('form-control-decoration-group');
+ 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;
+ 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(screen.getByTestId('before-node')).toBeInTheDocument();
+ expect(screen.getByTestId('after-node')).toBeInTheDocument();
+ expect(screen.getByTestId('label-node')).toBeInTheDocument();
});
});
diff --git a/src/Form/tests/FormControlFeedback.test.jsx b/src/Form/tests/FormControlFeedback.test.jsx
index 8ef5d8d564..fefb57f08c 100644
--- a/src/Form/tests/FormControlFeedback.test.jsx
+++ b/src/Form/tests/FormControlFeedback.test.jsx
@@ -1,45 +1,47 @@
import React from 'react';
-import { mount } from 'enzyme';
+import { render, screen } from '@testing-library/react';
import FormControlFeedback from '../FormControlFeedback';
-import { FORM_TEXT_TYPES, FORM_TEXT_ICONS } from '../FormText';
+import { FORM_TEXT_TYPES } from '../FormText';
import { FormGroupContext } from '../FormGroupContext';
+const THIS_IS_FEEDBACK = 'This is feedback';
+
describe('FormControlFeedback', () => {
it('renders a form control with an id', () => {
const getDescriptorProps = jest.fn(() => ({ id: 'descriptor-id' }));
const contextValue = {
getDescriptorProps,
};
- const wrapper = mount((
+ render(
- This is feedback
+ {THIS_IS_FEEDBACK}
-
- ));
- expect(wrapper.exists('[children="This is feedback"]')).toBe(true);
- const FeedbackNode = wrapper.find(FormControlFeedback).first().childAt(0);
+ ,
+ );
+ const FeedbackNode = screen.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((
-
- This is feedback
-
- ));
- expect(wrapper.exists(FORM_TEXT_ICONS[FORM_TEXT_TYPES.VALID])).toBe(true);
+ render(
+
+ {THIS_IS_FEEDBACK}
+ ,
+ );
+ expect(screen.getByTestId(FORM_TEXT_TYPES.VALID)).toBeInTheDocument();
});
it('renders with a custom icon', () => {
const customIcon = !;
- const wrapper = mount((
+ render(
- This is feedback
-
- ));
- expect(wrapper.exists('custom-icon')).toBe(true);
+ {THIS_IS_FEEDBACK}
+ ,
+ );
+ expect(screen.getByText('!')).toBeInTheDocument();
});
});
diff --git a/src/Form/tests/FormGroup.test.jsx b/src/Form/tests/FormGroup.test.jsx
index 65af690677..6d4485f93f 100644
--- a/src/Form/tests/FormGroup.test.jsx
+++ b/src/Form/tests/FormGroup.test.jsx
@@ -1,5 +1,5 @@
import React from 'react';
-import { mount } from 'enzyme';
+import { render, screen } from '@testing-library/react';
import FormGroup from '../FormGroup';
import FormControl from '../FormControl';
@@ -18,58 +18,64 @@ jest.mock('react-bootstrap/FormControl', () => {
});
});
-describe('FormGroup', () => {
- describe('associate element ids and attributes', () => {
- const wrapper = mount((
+function renderFormGroup() {
+ return (
+ render(
My Field
-
+
Default help text
Second help text
-
- ));
+ ,
+ )
+ );
+}
+describe('FormGroup', () => {
+ describe('associate element ids and attributes', () => {
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');
+ renderFormGroup();
+ const formControlNode = screen.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');
+ renderFormGroup();
+ const labelNode = screen.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);
+ renderFormGroup();
+ const defaultHelpTextNode = screen.getByText('Default help text').parentElement;
+ const formControlNode = screen.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);
+ renderFormGroup();
+ const secondHelpTextNode = screen.getByText('Second help text').parentElement;
+ const formControlNode = screen.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((
+ render(
-
-
- ));
- expect(wrapper.exists('form-control')).toBe(true);
- const formControlNode = wrapper.find('form-control').first();
- expect(formControlNode.props().id).toBeTruthy();
+
+ ,
+ );
+ const formControlNode = screen.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 fce088541b..4d5a5d0a66 100644
--- a/src/Form/tests/FormRadioSet.test.jsx
+++ b/src/Form/tests/FormRadioSet.test.jsx
@@ -1,15 +1,17 @@
import React from 'react';
-import { mount } from 'enzyme';
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+
import FormGroup from '../FormGroup';
import FormRadioSet from '../FormRadioSet';
import FormRadio from '../FormRadio';
import FormLabel from '../FormLabel';
-describe('FormRadioSet', () => {
- describe('associate element ids and attributes', () => {
- const handleChange = jest.fn();
- const value = 'green';
- const wrapper = mount((
+function renderFormGroup() {
+ const handleChange = jest.fn();
+ const value = 'green';
+ return (
+ render(
Which color?
{
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');
- });
+ ,
+ )
+ );
+}
- 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);
- });
- });
-
- describe('controlled behavior', () => {
- const setValue = jest.fn();
- const wrapper = mount((
+const setValue = jest.fn();
+function renderFormRadioSet(isDefault) {
+ return (
+ render(
{
setValue(e.target.value);
@@ -54,52 +44,68 @@ describe('FormRadioSet', () => {
red
green
blue
-
- ));
+ ,
+ )
+ );
+}
+describe('FormRadioSet', () => {
+ describe('associate element ids and attributes', () => {
+ it('has a radiogroup div with the proper id', () => {
+ renderFormGroup();
+ const radioGroupNode = screen.getByRole('radiogroup', { name: /Which color\?/i });
+ expect(radioGroupNode).toBeInTheDocument();
+ expect(radioGroupNode).toHaveAttribute('id', 'my-field');
+ });
+
+ it('has an element labelling the radiogroup', () => {
+ renderFormGroup();
+ const labelNode = screen.getByText('Which color?');
+ const radioGroupNode = screen.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', () => {
it('checks the right radio button', () => {
- const radioNode = wrapper.find('input[value="red"]').first();
- expect(radioNode.props().checked).toBe(true);
+ renderFormRadioSet();
+ const redRadio = screen.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);
+ it('calls the change handlers with the right value', async () => {
+ renderFormRadioSet();
+ const greenRadio = screen.getByLabelText('green');
+ await userEvent.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);
+ renderFormRadioSet();
+ const redRadio = screen.getByLabelText('red');
+ expect(redRadio).toBeChecked();
});
});
it('renders radio controls without a context', () => {
- const wrapper = mount((
+ render(
<>
Evergreen
Deciduous
- >
- ));
+ >,
+ );
+
+ const evergreenRadio = screen.getByLabelText('Evergreen');
+ const deciduousRadio = screen.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 163b4c60bc..31caad4573 100644
--- a/src/Form/tests/FormSwitch.test.jsx
+++ b/src/Form/tests/FormSwitch.test.jsx
@@ -1,25 +1,24 @@
import React from 'react';
-import { mount } from 'enzyme';
-import FormSwitch from '../FormSwitch';
+import { render, screen } from '@testing-library/react';
-// 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');
+ render(
+
+ Green
+ ,
+ );
+ const switchInput = screen.getByRole('switch');
+ expect(switchInput).toBeInTheDocument();
+ expect(switchInput).toHaveAttribute('name', 'color');
+
+ const helperText = screen.getByText('Describe green');
+ expect(helperText).toBeInTheDocument();
});
});
diff --git a/src/Form/tests/FormText.test.jsx b/src/Form/tests/FormText.test.jsx
index f30a84d821..1d8c1594c0 100644
--- a/src/Form/tests/FormText.test.jsx
+++ b/src/Form/tests/FormText.test.jsx
@@ -1,26 +1,28 @@
import React from 'react';
-import { mount } from 'enzyme';
+import { render, screen } from '@testing-library/react';
-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((
-
+ render(
+
This is feedback
-
- ));
- expect(wrapper.exists(FORM_TEXT_ICONS[FORM_TEXT_TYPES.VALID])).toBe(true);
+ ,
+ );
+ const icon = screen.getByTestId(`${FORM_TEXT_TYPES.VALID}`);
+ expect(icon).toBeInTheDocument();
});
it('renders with a custom icon', () => {
const customIcon = !;
- const wrapper = mount((
-
+ render(
+
This is feedback
-
- ));
- expect(wrapper.exists('custom-icon')).toBe(true);
+ ,
+ );
+ const icon = screen.getByTestId('form-text-custom-icon');
+ expect(icon).toBeInTheDocument();
});
});
@@ -29,14 +31,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 02a3966611..03d5583ed9 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, screen, act } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
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', () => {
+ render(
+ ,
+ );
+ const idList = screen.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');
+ render(
+ ,
+ );
+ const idList = screen.getByTestId('id-list');
+ const renderedIds = idList.textContent.split(' ');
+ expect(renderedIds[1]).toBe('prefix-2');
});
it('registers an explicit id', () => {
+ render(
+ ,
+ );
+ const idList = screen.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', () => {
+ render();
+ const idList = screen.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,66 @@ 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);
+ render();
+ expect(screen.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 () => {
+ render();
+ const input = screen.getByTestId('input');
+ await act(async () => {
+ await userEvent.type(input, 'hello');
+ await userEvent.tab();
+ });
+
+ expect(screen.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', () => {
+ render(
+ ,
+ );
+ expect(screen.getByTestId('has-value')).toBeInTheDocument();
+ });
+ it('has no value when a target blur event has no value', async () => {
+ render(
+ ,
+ );
+ const input = screen.getByTestId('input');
+ await act(async () => {
+ input.focus();
+ await userEvent.tab();
});
+
+ expect(screen.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', () => {
+ render();
+ expect(screen.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 () => {
+ render();
+ await act(async () => {
+ await userEvent.tab();
+ });
+
+ expect(screen.getByTestId('has-value')).toBeInTheDocument();
+ },
+ );
});
diff --git a/src/Form/tests/useCheckboxSetValues.test.jsx b/src/Form/tests/useCheckboxSetValues.test.jsx
index 25eb41de22..ecb38cb98a 100644
--- a/src/Form/tests/useCheckboxSetValues.test.jsx
+++ b/src/Form/tests/useCheckboxSetValues.test.jsx
@@ -1,65 +1,71 @@
/* eslint-disable react/button-has-type */
import React from 'react';
-import { mount } from 'enzyme';
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+
import useCheckboxSetValues from '../useCheckboxSetValues';
+const VALUES = 'values';
+
function Example() {
const [values, {
add, remove, set, clear,
}] = useCheckboxSetValues(['cheddar']);
return (
<>
- {values.join(' ')}
- add('provolone')}>Add
- remove('provolone')}>Add
- set(['cheddar', 'swiss', 'provolone'])}>Add
- clear()}>Add
+ {values.join(' ')}
+ add('provolone')}>Add
+ remove('provolone')}>Remove
+ set(['cheddar', 'swiss', 'provolone'])}>Set
+ clear()}>Clear
>
);
}
describe('useCheckboxSetValues', () => {
- const wrapper = mount();
+ it('has a default value', () => {
+ render();
+ const values = screen.getByTestId(VALUES);
+ expect(values.textContent).toBe('cheddar');
+ });
- const getValues = () => {
- const valueStr = wrapper.find('#values').first().text();
- if (valueStr === '') {
- return [];
- }
- return valueStr.split(' ');
- };
+ it('can append a value', async () => {
+ render();
+ const addButton = screen.getByTestId('add');
+ const values = screen.getByTestId(VALUES);
- const addButton = wrapper.find('#add').first();
- const removeButton = wrapper.find('#remove').first();
- const setButton = wrapper.find('#set').first();
- const clearButton = wrapper.find('#clear').first();
+ await userEvent.click(addButton);
- it('has a default value', () => {
- const values = getValues();
- expect(values).toEqual(['cheddar']);
+ expect(values.textContent).toBe('cheddar provolone');
});
- it('can append a value', () => {
- addButton.simulate('click');
- const values = getValues();
- expect(values).toEqual(['cheddar', 'provolone']);
- });
+ it('can remove a value', async () => {
+ render();
+ const removeButton = screen.getByTestId('remove');
+ const values = screen.getByTestId(VALUES);
- it('can remove a value', () => {
- removeButton.simulate('click');
- const values = getValues();
- expect(values).toEqual(['cheddar']);
+ await userEvent.click(removeButton);
+
+ expect(values.textContent).toBe('cheddar');
});
- it('can replace all values', () => {
- setButton.simulate('click');
- const values = getValues();
- expect(values).toEqual(['cheddar', 'swiss', 'provolone']);
+ it('can replace all values', async () => {
+ render();
+ const setButton = screen.getByTestId('set');
+ const values = screen.getByTestId(VALUES);
+
+ await userEvent.click(setButton);
+
+ expect(values.textContent).toBe('cheddar swiss provolone');
});
- it('can clear all values', () => {
- clearButton.simulate('click');
- const values = getValues();
- expect(values).toEqual([]);
+ it('can clear all values', async () => {
+ render();
+ const clearButton = screen.getByTestId('clear');
+ const values = screen.getByTestId(VALUES);
+
+ await userEvent.click(clearButton);
+
+ expect(values.textContent).toBe('');
});
});
diff --git a/src/Hyperlink/Hyperlink.test.jsx b/src/Hyperlink/Hyperlink.test.jsx
index 9ec1f0d3fb..2d5ffd3c5e 100644
--- a/src/Hyperlink/Hyperlink.test.jsx
+++ b/src/Hyperlink/Hyperlink.test.jsx
@@ -1,18 +1,19 @@
import React from 'react';
-import { mount } from 'enzyme';
+import { render } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
-import Hyperlink from './index';
+import Hyperlink from '.';
const content = 'content';
const destination = 'destination';
-const onClick = () => {};
+const onClick = jest.fn();
const props = {
content,
destination,
onClick,
};
const externalLinkAlternativeText = 'externalLinkAlternativeText';
-const externalLinkTitle = 'externalLinktTitle';
+const externalLinkTitle = 'externalLinkTitle';
const externalLinkProps = {
target: '_blank',
externalLinkAlternativeText,
@@ -21,51 +22,52 @@ const externalLinkProps = {
};
describe('correct rendering', () => {
- it('renders Hyperlink', () => {
- const wrapper = mount().find('a');
- expect(wrapper).toHaveLength(1);
+ it('renders Hyperlink', async () => {
+ const { getByRole } = render();
+ const wrapper = getByRole('link');
+ expect(wrapper).toBeInTheDocument();
- expect(wrapper.prop('className')).toContain('pgn__hyperlink');
- expect(wrapper.prop('children')).toEqual([content, undefined]);
- expect(wrapper.prop('href')).toEqual(destination);
- expect(wrapper.prop('target')).toEqual('_self');
- expect(wrapper.prop('onClick')).toEqual(onClick);
+ expect(wrapper).toHaveClass('pgn__hyperlink');
+ expect(wrapper).toHaveTextContent(content);
+ expect(wrapper).toHaveAttribute('href', destination);
+ expect(wrapper).toHaveAttribute('target', '_self');
- expect(wrapper.find('span')).toHaveLength(0);
- expect(wrapper.find('i')).toHaveLength(0);
+ await userEvent.click(wrapper);
+ expect(onClick).toHaveBeenCalledTimes(1);
});
it('renders external Hyperlink', () => {
- const wrapper = mount();
+ const { getByRole, getByTestId } = render();
+ const wrapper = getByRole('link');
+ const icon = getByTestId('hyperlink-icon');
+ const iconSvg = icon.querySelector('svg');
- expect(wrapper.find('span')).toHaveLength(3);
+ expect(wrapper).toBeInTheDocument();
+ expect(icon).toBeInTheDocument();
- const icon = wrapper.find('span').at(1);
- const iconImage = icon.find('svg').at(0);
-
- expect(icon.prop('className')).toEqual('pgn__icon');
- expect(iconImage.prop('role')).toEqual('img');
- expect(iconImage.prop('width')).toEqual(24);
- expect(iconImage.prop('height')).toEqual(24);
+ expect(icon).toHaveClass('pgn__icon');
+ expect(iconSvg).toHaveAttribute('width', '24');
+ expect(iconSvg).toHaveAttribute('height', '24');
});
});
describe('security', () => {
it('prevents reverse tabnabbing for links with target="_blank"', () => {
- const wrapper = mount();
- expect(wrapper.find('a').prop('rel')).toEqual('noopener noreferrer');
+ const { getByRole } = render();
+ const wrapper = getByRole('link');
+ expect(wrapper).toHaveAttribute('rel', 'noopener noreferrer');
});
});
describe('event handlers are triggered correctly', () => {
let spy;
-
beforeEach(() => { spy = jest.fn(); });
- it('should fire onClick', () => {
- const wrapper = mount();
+ it('should fire onClick', async () => {
+ const { getByRole } = render();
+ const wrapper = getByRole('link');
expect(spy).toHaveBeenCalledTimes(0);
- wrapper.simulate('click');
+ await userEvent.click(wrapper);
expect(spy).toHaveBeenCalledTimes(1);
});
});
diff --git a/src/Hyperlink/index.jsx b/src/Hyperlink/index.jsx
index 1d4bc27838..cb21b3cc2c 100644
--- a/src/Hyperlink/index.jsx
+++ b/src/Hyperlink/index.jsx
@@ -53,6 +53,7 @@ const Hyperlink = React.forwardRef((props, ref) => {
src={Launch}
screenReaderText={externalLinkAlternativeText}
style={{ height: '1em', width: '1em' }}
+ data-testid="hyperlink-icon"
/>
);
diff --git a/src/Icon/Icon.test.jsx b/src/Icon/Icon.test.jsx
index 8183c3a6e8..a1fe6e251b 100644
--- a/src/Icon/Icon.test.jsx
+++ b/src/Icon/Icon.test.jsx
@@ -1,5 +1,5 @@
import React from 'react';
-import { mount } from 'enzyme';
+import { render } from '@testing-library/react';
import Icon from './index';
@@ -14,55 +14,57 @@ function BlankSrc() {
return ;
}
-let wrapper;
-
describe('', () => {
describe('props received correctly', () => {
it('receives required props', () => {
- wrapper = mount();
- const iconSpans = wrapper.find('span');
- const iconSpan = iconSpans.at(0);
+ const { container } = render();
+ const iconSpans = container.querySelectorAll('span');
+ const iconSpan = iconSpans[0];
- expect(iconSpan.prop('id')).toContain('Icon');
- expect(iconSpan.hasClass(classNames[0])).toEqual(true);
- expect(iconSpan.hasClass(classNames[1])).toEqual(true);
+ expect(iconSpan.getAttribute('id')).toContain('Icon');
+ expect(iconSpan.classList.contains(classNames[0])).toEqual(true);
+ expect(iconSpan.classList.contains(classNames[1])).toEqual(true);
});
+
it('handles null id properly', () => {
const nullId = null;
- wrapper = mount();
- const iconSpans = wrapper.find('span');
- const iconSpan = iconSpans.at(0);
+ const { container } = render();
+ const iconSpans = container.querySelectorAll('span');
+ const iconSpan = iconSpans[0];
- expect(iconSpan.prop('id')).toContain('Icon');
- expect(iconSpan.hasClass(classNames[0])).toEqual(true);
- expect(iconSpan.hasClass(classNames[1])).toEqual(true);
+ expect(iconSpan.getAttribute('id')).toContain('Icon');
+ expect(iconSpan.classList.contains(classNames[0])).toEqual(true);
+ expect(iconSpan.classList.contains(classNames[1])).toEqual(true);
});
+
it('generates unique ids when no id is provided', () => {
- wrapper = mount(<>>);
- const iconSpans = wrapper.find('span');
- const iconSpan1 = iconSpans.at(0);
- const iconSpan2 = iconSpans.at(1);
- const id1 = iconSpan1.prop('id');
- const id2 = iconSpan2.prop('id');
+ const { container } = render(<>>);
+ const iconSpans = container.querySelectorAll('span');
+ const iconSpan1 = iconSpans[0];
+ const iconSpan2 = iconSpans[1];
+ const id1 = iconSpan1.getAttribute('id');
+ const id2 = iconSpan2.getAttribute('id');
expect(id1).toContain('Icon');
expect(id2).toContain('Icon');
expect(id1).not.toEqual(id2);
});
+
it('handles screenReaderText correctly', () => {
- wrapper = mount();
- const iconSpans = wrapper.find('span');
+ const { container } = render();
+ const iconSpans = container.querySelectorAll('span');
expect(iconSpans.length).toEqual(2);
- expect(iconSpans.at(0).prop('id')).toEqual(testId);
- expect(iconSpans.at(1).hasClass('sr-only')).toEqual(true);
+ expect(iconSpans[0].getAttribute('id')).toEqual(testId);
+ expect(iconSpans[1].classList.contains('sr-only')).toEqual(true);
});
+
it('receives size prop correctly', () => {
- wrapper = mount();
- const iconSpans = wrapper.find('span');
- const iconSpan = iconSpans.at(0);
+ const { container } = render();
+ const iconSpans = container.querySelectorAll('span');
+ const iconSpan = iconSpans[0];
- expect(iconSpan.hasClass('pgn__icon__xs')).toEqual(true);
+ expect(iconSpan.classList.contains('pgn__icon__xs')).toEqual(true);
});
});
});
diff --git a/src/IconButton/IconButton.test.jsx b/src/IconButton/IconButton.test.jsx
index 3c3c96f067..9f098002ea 100644
--- a/src/IconButton/IconButton.test.jsx
+++ b/src/IconButton/IconButton.test.jsx
@@ -1,8 +1,10 @@
import React from 'react';
-import { shallow, mount } from 'enzyme';
+import { render } from '@testing-library/react';
import renderer from 'react-test-renderer';
+import userEvent from '@testing-library/user-event';
+
import { InfoOutline } from '../../icons';
-import IconButton from './index';
+import IconButton from '.';
import Icon from '../Icon';
describe('', () => {
@@ -28,55 +30,66 @@ describe('', () => {
expect(tree).toMatchSnapshot();
});
it('passes the alt text to the button aria-label', () => {
- const wrapper = shallow( {}} />);
- expect(wrapper.prop('aria-label')).toEqual(alt);
+ const { getByRole } = render( {}} />);
+ const button = getByRole('button');
+ expect(button).toHaveAttribute('aria-label', alt);
});
it('should render with active style if isActive is true', () => {
- const wrapper = shallow( {}} />);
- expect(wrapper.find(`.btn-icon-${variant}-active`)).toHaveLength(1);
+ const { container } = render( {}} />);
+ const button = container.querySelector(`.btn-icon-${variant}-active`);
+ expect(button).toBeInTheDocument();
});
it('should render with inverse active style if inverse and isActive is true', () => {
- const wrapper = shallow( {}} />);
- expect(wrapper.find(`.btn-icon-inverse-${variant}-active`)).toHaveLength(1);
+ const { container } = render( {}} />);
+ const button = container.querySelector(`.btn-icon-inverse-${variant}-active`);
+ expect(button).toBeInTheDocument();
});
it('should not render with inverse- class names if invertColors is false', () => {
- const wrapper = shallow( {}} />);
- expect(wrapper.find(`.btn-icon-inverse-${variant}`)).toHaveLength(0);
- expect(wrapper.find(`.btn-icon-inverse-${variant}`)).toHaveLength(0);
+ const { container } = render( {}} />);
+ const inverseButton = container.querySelector(`.btn-icon-inverse-${variant}`);
+ const inverseActiveButton = container.querySelector(`.btn-icon-inverse-${variant}-active`);
+ expect(inverseButton).not.toBeInTheDocument();
+ expect(inverseActiveButton).not.toBeInTheDocument();
});
it('should render with inverse- class names if invertColors is true', () => {
- const wrapper = shallow( {}} invertColors />);
- expect(wrapper.find(`.btn-icon-inverse-${variant}`)).toHaveLength(1);
- expect(wrapper.find(`.btn-icon-inverse-${variant}`)).toHaveLength(1);
+ const { container } = render( {}} invertColors />);
+ const inverseButton = container.querySelector(`.btn-icon-inverse-${variant}`);
+ expect(inverseButton).toBeInTheDocument();
});
it('should add the icon class names if it receives them', () => {
- const wrapper = shallow( {}} iconClassNames="foo bar" />);
- expect(wrapper.find('.foo')).toHaveLength(1);
- expect(wrapper.find('.bar')).toHaveLength(1);
+ const { container } = render( {}} iconClassNames="foo bar" />);
+ const iconWithFooClass = container.querySelector('.foo');
+ const iconWithBarClass = container.querySelector('.bar');
+ expect(iconWithFooClass).toBeInTheDocument();
+ expect(iconWithBarClass).toBeInTheDocument();
});
+
describe('onClick', () => {
- it('performs the onClick action when clicked', () => {
+ it('performs the onClick action when clicked', async () => {
const spy = jest.fn();
- const wrapper = mount(());
- expect(spy).toHaveBeenCalledTimes(0);
- wrapper.simulate('click');
+ const { getByRole } = render();
+ const button = getByRole('button');
+ await userEvent.click(button);
expect(spy).toHaveBeenCalledTimes(1);
});
- it('only clicks one icon at a time', () => {
+
+ it('only clicks one icon at a time', async () => {
const spy1 = jest.fn();
const spy2 = jest.fn();
- const wrapper = mount((
-
+ const { getAllByRole } = render(
+ <>
-
- ));
- const icon1 = wrapper.find(IconButton).at(0);
- icon1.simulate('click');
+ >,
+ );
+
+ const buttons = getAllByRole('button');
+
+ await userEvent.click(buttons[0]);
expect(spy1).toHaveBeenCalledTimes(1);
expect(spy2).toHaveBeenCalledTimes(0);
- const icon2 = wrapper.find(IconButton).at(1);
- icon2.simulate('click');
+
+ await userEvent.click(buttons[1]);
expect(spy1).toHaveBeenCalledTimes(1);
expect(spy2).toHaveBeenCalledTimes(1);
});
diff --git a/src/IconButtonToggle/IconButtonToggle.test.jsx b/src/IconButtonToggle/IconButtonToggle.test.jsx
index b62fa4ebc6..e7ef9fe6d1 100644
--- a/src/IconButtonToggle/IconButtonToggle.test.jsx
+++ b/src/IconButtonToggle/IconButtonToggle.test.jsx
@@ -1,8 +1,5 @@
import React from 'react';
import { render, screen, waitFor } from '@testing-library/react';
-
-// adds special assertions like toHaveTextContent
-import '@testing-library/jest-dom/extend-expect';
import userEvent from '@testing-library/user-event';
import { Info, Home } from '../../icons';
diff --git a/src/Input/input.test.jsx b/src/Input/input.test.jsx
index 8cd7a9fcaa..f45cbf8f76 100644
--- a/src/Input/input.test.jsx
+++ b/src/Input/input.test.jsx
@@ -1,8 +1,8 @@
import React from 'react';
-import { mount } from 'enzyme';
+import { render } from '@testing-library/react';
import renderer from 'react-test-renderer';
-import Input from './index';
+import Input from '.';
describe('', () => {
const label = 'label';
@@ -64,15 +64,16 @@ describe('', () => {
describe('rendering', () => {
it('should render with forwardRef', () => {
- const wrapper = mount();
- expect(wrapper.find(React.forwardRef)).toBeTruthy();
+ const refSpy = jest.fn();
+ render();
+ expect(refSpy).toHaveBeenCalled();
});
it('should render each input type', () => {
types.forEach((type) => {
- const wrapper = mount();
- const input = wrapper.find('Input.input');
- expect(input.prop('type')).toEqual(type);
+ const { container } = render();
+ const input = container.querySelector('.input');
+ expect(input).toBeInTheDocument();
});
});
diff --git a/src/InputText/InputText.test.jsx b/src/InputText/InputText.test.jsx
index 47720327a2..1d74824d62 100644
--- a/src/InputText/InputText.test.jsx
+++ b/src/InputText/InputText.test.jsx
@@ -1,7 +1,7 @@
import React from 'react';
-import { mount } from 'enzyme';
+import { render } from '@testing-library/react';
-import InputText from './index';
+import InputText from '.';
describe('', () => {
const label = 'label';
@@ -13,64 +13,62 @@ describe('', () => {
describe('rendering', () => {
it('should render with default type when type is not defined', () => {
- const wrapper = mount();
- expect(wrapper.find('input')).toHaveLength(1);
-
- const input = wrapper.find('input').at(0);
- expect(input.prop('type')).toEqual('text');
+ const { container } = render();
+ const input = container.querySelector('input');
+ expect(input).toBeInTheDocument();
+ expect(input.type).toEqual('text');
});
it('should render with default type when type is defined as undefined', () => {
- const wrapper = mount();
- expect(wrapper.find('input')).toHaveLength(1);
-
- const input = wrapper.find('input').at(0);
- expect(input.prop('type')).toEqual('text');
+ const { container } = render();
+ const input = container.querySelector('input');
+ expect(input).toBeInTheDocument();
+ expect(input.type).toEqual('text');
});
it('should render with default type when type is defined as null', () => {
- const wrapper = mount();
- expect(wrapper.find('input')).toHaveLength(1);
-
- const input = wrapper.find('input').at(0);
- expect(input.prop('type')).toEqual('text');
+ const { container } = render();
+ const input = container.querySelector('input');
+ expect(input).toBeInTheDocument();
+ expect(input.type).toEqual('text');
});
it('should render with specified type when type is defined', () => {
const type = 'foobar';
- const wrapper = mount();
- expect(wrapper.find('input')).toHaveLength(1);
-
- const input = wrapper.find('input').at(0);
- expect(input.prop('type')).toEqual(type);
+ const { getByRole } = render();
+ const input = getByRole('textbox', { type: 'foobar' });
+ expect(input).toBeInTheDocument();
+ expect(input.getAttribute('type')).toEqual(type);
});
- it('should render with the autocomplete property if set', () => {
- const wrapper = mount();
- expect(wrapper.find('input')).toHaveLength(1);
-
- const input = wrapper.find('input').at(0);
- expect(input.prop('autoComplete')).toEqual('off');
+ it('should render with the autoComplete property if set', () => {
+ const { container } = render();
+ const input = container.querySelector('input');
+ expect(input).toBeInTheDocument();
+ expect(input.getAttribute('autocomplete')).toEqual('off');
});
it('should render with custom classNames if set', () => {
- const wrapper = mount();
- expect(wrapper.find('input')).toHaveLength(1);
-
- const input = wrapper.find('input').at(0);
- expect(input.prop('type')).toEqual('text');
- expect(input.hasClass('first')).toEqual(true);
- expect(input.hasClass('last')).toEqual(true);
+ const { container } = render();
+ const input = container.querySelector('input');
+ expect(input).toBeInTheDocument();
+ expect(input.type).toEqual('text');
+ expect(input.classList.contains('first')).toBe(true);
+ expect(input.classList.contains('last')).toBe(true);
});
it('should not be readOnly if the readOnly property is not set', () => {
- const wrapper = mount();
- expect(wrapper.props().readOnly).toBeUndefined();
+ const { container } = render();
+ const input = container.querySelector('input');
+ expect(input).toBeInTheDocument();
+ expect(input.readOnly).toBe(false);
});
it('should render with the readOnly property if set', () => {
- const wrapper = mount();
- expect(wrapper.props().readOnly).toEqual(true);
+ const { container } = render();
+ const input = container.querySelector('input');
+ expect(input).toBeInTheDocument();
+ expect(input.readOnly).toBe(true);
});
});
});
diff --git a/src/Layout/Layout.test.jsx b/src/Layout/Layout.test.jsx
index ca32d11c1c..0997a1df7b 100644
--- a/src/Layout/Layout.test.jsx
+++ b/src/Layout/Layout.test.jsx
@@ -1,6 +1,6 @@
import React from 'react';
-import { mount } from 'enzyme';
-import Layout from './index';
+import { render } from '@testing-library/react';
+import Layout from '.';
function TestLayout(props) {
return (
@@ -22,22 +22,24 @@ function TestLayout(props) {
describe('', () => {
describe('correct rendering', () => {
it('renders correct number of children', () => {
- const wrapper = mount();
- const children = wrapper.find('.row div');
+ const { container } = render();
+ const children = container.querySelectorAll('.row div');
expect(children.length).toEqual(3);
});
- it('renders correct number of children', () => {
- const wrapper = mount();
- const children = wrapper.find('.row div');
- expect(children.at(0).hasClass(
- 'col-xl-3 col-lg-4 col-md-auto col-sm-8 col-4 offset-xl-0 offset-lg-0 offset-md-0 offset-sm-0 offset-0',
- )).toEqual(true);
+
+ it('renders children with correct class names', () => {
+ const { container } = render();
+ const children = container.querySelectorAll('.row div');
+
+ const classNames = 'col-xl-3 col-lg-4 col-md-auto col-sm-8 col-4 offset-xl-0 offset-lg-0 offset-md-0 offset-sm-0 offset-0';
+ expect(children[0].className).toEqual(classNames);
});
- it('renders although dimensions are incorrect', () => {
- const wrapper = mount(
+
+ it('renders correctly even with incorrect dimensions', () => {
+ const { container } = render(
,
);
- const children = wrapper.find('.row div');
+ const children = container.querySelectorAll('.row div');
expect(children.length).not.toEqual(0);
});
});
diff --git a/src/ListBox/ListBox.test.jsx b/src/ListBox/ListBox.test.jsx
index c5aa4911fa..19da147012 100644
--- a/src/ListBox/ListBox.test.jsx
+++ b/src/ListBox/ListBox.test.jsx
@@ -1,169 +1,161 @@
import React from 'react';
-import { shallow } from 'enzyme';
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
-import ListBox from './index';
+import ListBox from '.';
import ListBoxOption from '../ListBoxOption';
describe('ListBox', () => {
- const listBox = (
-
- test1
- test2
- test3
-
- );
-
- let wrapper;
-
- describe('rendering', () => {
- it('should have null aria-activedescendant attribute by default', () => {
- wrapper = shallow(listBox);
-
- expect(wrapper.prop('aria-activedescendant')).toEqual(null);
- });
+ beforeEach(() => {
+ render(
+
+ test1
+ test2
+ test3
+ ,
+ );
+ });
- it('should have correct aria-activedescendant attribute when selectedOptionIndex state is non-null', () => {
- wrapper = shallow(listBox);
+ it('should have null aria-activedescendant attribute by default', () => {
+ const listBoxElement = screen.getByRole('listbox');
+ expect(listBoxElement.getAttribute('aria-activedescendant')).toBeNull();
+ });
+ it(
+ 'should have correct aria-activedescendant attribute when selectedOptionIndex state is non-null',
+ async () => {
+ const listBoxElement = screen.getByRole('listbox');
const selectedOptionIndex = 1;
- wrapper.setState({
- selectedOptionIndex,
- });
+ listBoxElement.focus();
- expect(wrapper.prop('aria-activedescendant')).toEqual(`list-box-option-${selectedOptionIndex}`);
- });
+ await userEvent.keyboard('{arrowdown}');
- it('selectedOptionIndex prop should override selectedOptionIndex state', () => {
- wrapper = shallow(listBox);
+ expect(listBoxElement.getAttribute('aria-activedescendant')).toEqual(`list-box-option-${selectedOptionIndex}`);
+ },
+ );
- wrapper.setState({
- selectedOptionIndex: 1,
- });
+ it('selectedOptionIndex prop should override selectedOptionIndex state', async () => {
+ const listBoxElement = screen.getByRole('listbox');
+ const selectedOptionIndex = 2;
- const selectedOptionIndex = 2;
+ listBoxElement.focus();
- wrapper.setProps({
- selectedOptionIndex,
- });
+ await userEvent.keyboard('{arrowdown}');
+ await userEvent.keyboard('{arrowdown}');
- expect(wrapper.prop('aria-activedescendant')).toEqual(`list-box-option-${selectedOptionIndex}`);
- });
+ expect(listBoxElement.getAttribute('aria-activedescendant')).toEqual(`list-box-option-${selectedOptionIndex}`);
+ });
- it('should render a div by default', () => {
- wrapper = shallow(listBox);
- expect(wrapper.find('div')).toHaveLength(1);
- });
+ it('should render a div by default', () => {
+ const listBoxElement = screen.getByRole('listbox');
+ expect(listBoxElement.tagName.toLowerCase()).toBe('div');
+ });
- it('should render an HTML element when passed tag prop is an HTML element', () => {
- wrapper = shallow(listBox);
+ it('should render an HTML element when passed tag prop is an HTML element', () => {
+ const { container } = render(
+
+ test1
+ ,
+ );
+ const listBoxElement = container.querySelector('li');
+ expect(listBoxElement).toBeInTheDocument();
+ });
- wrapper.setProps({
- tag: 'li',
- });
+ it('should have correct default classNames', () => {
+ const listBoxElement = screen.getByRole('listbox');
+ expect(listBoxElement).toHaveClass('list-group');
+ });
- expect(wrapper.find('div')).toHaveLength(0);
- expect(wrapper.find('li')).toHaveLength(1);
- });
+ it('should have listbox role', () => {
+ const listBoxElement = screen.getByRole('listbox');
+ expect(listBoxElement).toHaveAttribute('role', 'listbox');
+ });
- it('should have correct default classNames', () => {
- wrapper = shallow(listBox);
+ it('should have 0 tabIndex', () => {
+ const listBoxElement = screen.getByRole('listbox');
+ expect(listBoxElement).toHaveAttribute('tabIndex', '0');
+ });
- expect(wrapper.prop('className')).toEqual(expect.stringContaining('list-group'));
- });
+ it('should select first ListBoxOption on focus if not ListBoxOption selected', async () => {
+ const listBoxElement = screen.getByRole('listbox');
- it('should have listbox role', () => {
- wrapper = shallow(listBox);
+ listBoxElement.focus();
- expect(wrapper.prop('role')).toEqual('listbox');
- });
+ expect(listBoxElement.getAttribute('aria-activedescendant')).toEqual('list-box-option-0');
+ });
- it('should have 0 tabIndex', () => {
- wrapper = shallow(listBox);
+ it('should not select first ListBoxOption on focus if ListBoxOption selected', async () => {
+ const listBoxElement = screen.getByRole('listbox');
- expect(wrapper.prop('tabIndex')).toEqual(0);
- });
- });
- describe('behavior', () => {
- it('should select first ListBoxOption on focus if not ListBoxOption selected', () => {
- wrapper = shallow(listBox);
+ listBoxElement.focus();
- wrapper.simulate('focus');
- expect(wrapper.state('selectedOptionIndex')).toEqual(0);
- });
+ await userEvent.keyboard('{arrowdown}');
- it('should not select first ListBoxOption on focus if ListBoxOption selected', () => {
- wrapper = shallow(listBox);
+ listBoxElement.focus();
- wrapper.setState({
- selectedOptionIndex: 1,
- });
+ expect(listBoxElement.getAttribute('aria-activedescendant')).toEqual('list-box-option-1');
+ });
- wrapper.simulate('focus');
- expect(wrapper.state('selectedOptionIndex')).toEqual(1);
- });
+ it('should select next ListBoxOption on down arrow key', async () => {
+ const listBoxElement = screen.getByRole('listbox');
- it('should select next ListBoxOption on down arrow key', () => {
- wrapper = shallow(listBox);
+ listBoxElement.focus();
- wrapper.simulate('focus');
- wrapper.simulate('keyDown', { key: 'ArrowDown', preventDefault() { } });
+ await userEvent.keyboard('{arrowdown}');
- expect(wrapper.state('selectedOptionIndex')).toEqual(1);
- });
+ expect(listBoxElement.getAttribute('aria-activedescendant')).toEqual('list-box-option-1');
+ });
- it('should not select next ListBoxOption on down arrow key if at end of list', () => {
- wrapper = shallow(listBox);
+ it('should not select next ListBoxOption on down arrow key if at end of list', async () => {
+ const listBoxElement = screen.getByRole('listbox');
- wrapper.simulate('focus');
+ listBoxElement.focus();
- wrapper.setState({
- selectedOptionIndex: 2,
- });
+ await userEvent.keyboard('{arrowdown}');
+ await userEvent.keyboard('{arrowdown}');
+ await userEvent.keyboard('{arrowdown}');
- wrapper.simulate('keyDown', { key: 'ArrowDown', preventDefault() { } });
+ expect(listBoxElement.getAttribute('aria-activedescendant')).toEqual('list-box-option-2');
+ });
- expect(wrapper.state('selectedOptionIndex')).toEqual(2);
- });
+ it('should select previous ListBoxOption on up arrow key', async () => {
+ const listBoxElement = screen.getByRole('listbox');
- it('should select previous ListBoxOption on up arrow key', () => {
- wrapper = shallow(listBox);
+ listBoxElement.focus();
- wrapper.simulate('focus');
+ await userEvent.keyboard('{arrowdown}');
+ await userEvent.keyboard('{arrowup}');
- wrapper.setState({
- selectedOptionIndex: 1,
- });
+ expect(listBoxElement.getAttribute('aria-activedescendant')).toEqual('list-box-option-0');
+ });
- wrapper.simulate('keyDown', { key: 'ArrowUp', preventDefault() { } });
+ it('should not select previous ListBoxOption on up arrow key if at start of list', async () => {
+ const listBoxElement = screen.getByRole('listbox');
- expect(wrapper.state('selectedOptionIndex')).toEqual(0);
- });
+ listBoxElement.focus();
- it('should not select previous ListBoxOption on up arrow key if at start of list', () => {
- wrapper = shallow(listBox);
+ await userEvent.keyboard('{arrowup}');
- wrapper.simulate('focus');
- wrapper.simulate('keyDown', { key: 'ArrowUp', preventDefault() { } });
+ expect(listBoxElement.getAttribute('aria-activedescendant')).toEqual('list-box-option-0');
+ });
- expect(wrapper.state('selectedOptionIndex')).toEqual(0);
- });
+ it('should not change ListBoxOption selection on non-supported key', async () => {
+ const listBoxElement = screen.getByRole('listbox');
- it('should not change ListBoxOption selection on non supported key', () => {
- wrapper = shallow(listBox);
+ listBoxElement.focus();
- wrapper.simulate('focus');
- wrapper.simulate('keyDown', { key: 'leftArrow', preventDefault() { } });
+ await userEvent.keyboard('{leftarrow}');
- expect(wrapper.state('selectedOptionIndex')).toEqual(0);
- });
+ expect(listBoxElement.getAttribute('aria-activedescendant')).toEqual('list-box-option-0');
+ });
- it('should update state when child\'s onSelect is called', () => {
- wrapper = shallow(listBox);
+ it('should update state when child\'s onSelect is called', async () => {
+ const listBoxElement = screen.getByRole('listbox');
+ const listBoxOption2 = screen.getAllByRole('option')[1];
- wrapper.find(ListBoxOption).at(1).dive().simulate('mouseDown');
+ await userEvent.click(listBoxOption2);
- expect(wrapper.state('selectedOptionIndex')).toEqual(1);
- });
+ expect(listBoxElement.getAttribute('aria-activedescendant')).toEqual('list-box-option-1');
});
});
diff --git a/src/ListBoxOption/ListBoxOption.test.jsx b/src/ListBoxOption/ListBoxOption.test.jsx
index 8dcbe6cb32..c192aa4305 100644
--- a/src/ListBoxOption/ListBoxOption.test.jsx
+++ b/src/ListBoxOption/ListBoxOption.test.jsx
@@ -1,133 +1,154 @@
import React from 'react';
-import { shallow } from 'enzyme';
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
-import ListBoxOption from './index';
+import ListBoxOption from '.';
describe('ListBoxOption', () => {
const listBoxOptionChild = 'test';
- const listBoxOption = (
- {listBoxOptionChild}
- );
-
- let wrapper;
describe('rendering', () => {
it('should have false aria-selected attribute by default', () => {
- wrapper = shallow(listBoxOption);
+ render(
+ {listBoxOptionChild},
+ );
- expect(wrapper.prop('aria-selected')).toEqual(false);
+ const listBoxOptionElement = screen.getByTestId('listbox-option');
+ expect(listBoxOptionElement.getAttribute('aria-selected')).toEqual('false');
});
it('should have false aria-selected attribute when isSelected prop is false', () => {
- wrapper = shallow(listBoxOption);
-
- wrapper.setProps({
- isSelected: false,
- });
+ const { rerender } = render(
+ {listBoxOptionChild},
+ );
- expect(wrapper.prop('aria-selected')).toEqual(false);
- });
+ const listBoxOptionElement = screen.getByTestId('listbox-option');
+ expect(listBoxOptionElement.getAttribute('aria-selected')).toEqual('false');
- it('should have true aria-selected attribute when isSelected prop is true', () => {
- wrapper = shallow(listBoxOption);
+ rerender(
+ {listBoxOptionChild},
+ );
- wrapper.setProps({
- isSelected: true,
- });
-
- expect(wrapper.prop('aria-selected')).toEqual(true);
+ expect(listBoxOptionElement.getAttribute('aria-selected')).toEqual('true');
});
it('should render a div by default', () => {
- wrapper = shallow(listBoxOption);
+ const { container } = render(
+ {listBoxOptionChild},
+ );
- expect(wrapper.find('div')).toHaveLength(1);
+ const listBoxOptionElement = container.querySelector('div');
+ expect(listBoxOptionElement).toBeInTheDocument();
});
it('should render an HTML element when tag prop is an HTML element', () => {
- wrapper = shallow(listBoxOption);
-
- wrapper.setProps({
- tag: 'li',
- });
+ const { container } = render(
+ {listBoxOptionChild},
+ );
- expect(wrapper.find('div')).toHaveLength(0);
- expect(wrapper.find('li')).toHaveLength(1);
+ const listBoxOptionElement = container.querySelector('li');
+ expect(listBoxOptionElement).toBeInTheDocument();
});
it('should have correct default classNames', () => {
- wrapper = shallow(listBoxOption);
+ render(
+ {listBoxOptionChild},
+ );
- expect(wrapper.prop('className')).toEqual(expect.stringContaining('list-group-item'));
- expect(wrapper.prop('className')).toEqual(expect.stringContaining('list-group-item-action'));
+ const listBoxOptionElement = screen.getByTestId('listbox-option');
+ expect(listBoxOptionElement).toHaveClass('list-group-item');
+ expect(listBoxOptionElement).toHaveClass('list-group-item-action');
});
it('should not have active className by default', () => {
- wrapper = shallow(listBoxOption);
+ render(
+ {listBoxOptionChild},
+ );
- expect(wrapper.prop('className')).not.toEqual(expect.stringContaining('active'));
+ const listBoxOptionElement = screen.getByTestId('listbox-option');
+ expect(listBoxOptionElement).not.toHaveClass('active');
});
it('should have correct default id', () => {
- wrapper = shallow(listBoxOption);
+ render(
+ {listBoxOptionChild},
+ );
- expect(wrapper.prop('id')).toBeNull();
+ const listBoxOptionElement = screen.getByTestId('listbox-option');
+ expect(listBoxOptionElement.getAttribute('id')).toBeNull();
});
it('should have correct id when index prop is a number', () => {
- wrapper = shallow(listBoxOption);
+ const { rerender } = render(
+ {listBoxOptionChild},
+ );
- const index = 1;
+ const listBoxOptionElement = screen.getByTestId('listbox-option');
+ expect(listBoxOptionElement.getAttribute('id')).toEqual('list-box-option-1');
- wrapper.setProps({
- index,
- });
+ rerender(
+ {listBoxOptionChild},
+ );
- expect(wrapper.prop('id')).toEqual(`list-box-option-${index}`);
+ expect(listBoxOptionElement.getAttribute('id')).toEqual('list-box-option-2');
});
it('should have option role', () => {
- wrapper = shallow(listBoxOption);
+ render(
+ {listBoxOptionChild},
+ );
- expect(wrapper.prop('role')).toEqual('option');
+ const listBoxOptionElement = screen.getByTestId('listbox-option');
+ expect(listBoxOptionElement.getAttribute('role')).toEqual('option');
});
it('should have active className when isSelected prop is true', () => {
- wrapper = shallow(listBoxOption);
+ const { rerender } = render(
+ {listBoxOptionChild},
+ );
- wrapper.setProps({
- isSelected: true,
- });
+ const listBoxOptionElement = screen.getByTestId('listbox-option');
+ expect(listBoxOptionElement).not.toHaveClass('active');
- expect(wrapper.prop('className')).toEqual(expect.stringContaining('active'));
+ rerender(
+ {listBoxOptionChild},
+ );
+
+ expect(listBoxOptionElement).toHaveClass('active');
});
});
+
describe('behavior', () => {
- it('should call onSelect on mouse down', () => {
- wrapper = shallow(listBoxOption);
+ it('should call onSelect on mouse down', async () => {
const onSelectSpy = jest.fn();
+ render(
+ {listBoxOptionChild},
+ );
+
+ const listBoxOptionElement = screen.getByTestId('listbox-option');
- wrapper.setProps({
- onSelect: onSelectSpy,
- });
+ await userEvent.click(listBoxOptionElement);
- wrapper.simulate('mouseDown');
expect(onSelectSpy).toHaveBeenCalledTimes(1);
});
- it('should call onSelect when receiving new isSelected prop', () => {
- wrapper = shallow(listBoxOption);
+ it('should call onSelect when receiving new isSelected prop', async () => {
const onSelectSpy = jest.fn();
+ const { rerender } = render(
+ {listBoxOptionChild},
+ );
- wrapper.setProps({
- onSelect: onSelectSpy,
- });
+ const listBoxOptionElement = screen.getByTestId('listbox-option');
- wrapper.setProps({
- isSelected: true,
- });
+ await userEvent.click(listBoxOptionElement);
- expect(onSelectSpy).toHaveBeenCalledTimes(1);
+ rerender(
+
+ {listBoxOptionChild}
+ ,
+ );
+
+ expect(onSelectSpy).toHaveBeenCalledTimes(2);
});
});
});
diff --git a/src/MailtoLink/MailtoLink.test.jsx b/src/MailtoLink/MailtoLink.test.jsx
index 41e4303cd1..f11f27066a 100644
--- a/src/MailtoLink/MailtoLink.test.jsx
+++ b/src/MailtoLink/MailtoLink.test.jsx
@@ -1,7 +1,7 @@
import React from 'react';
-import { mount } from 'enzyme';
+import { render } from '@testing-library/react';
-import MailtoLink from './index';
+import MailtoLink from '.';
const emailAddress = 'edx@example.com';
const emailAddresses = ['foo@example.com', 'bar@example.com', 'baz@example.com'];
@@ -21,10 +21,12 @@ describe('correct rendering', () => {
bcc={emailAddress}
/>
);
- const wrapper = mount(singleRecipientLink);
- expect(wrapper.find('.pgn_mailtolink')).toBeTruthy();
- const linkWrapper = wrapper.find('a');
- expect(linkWrapper.prop('href')).toEqual('mailto:edx@example.com?bcc=edx%40example.com&body=body&cc=edx%40example.com&subject=subject');
+ const { getByText } = render(singleRecipientLink);
+ const linkElement = getByText('content');
+ expect(linkElement).toBeTruthy();
+ expect(linkElement.getAttribute('href')).toEqual(
+ 'mailto:edx@example.com?bcc=edx%40example.com&body=body&cc=edx%40example.com&subject=subject',
+ );
});
it('renders mailtoLink with many to, cc, and bcc recipients', () => {
@@ -36,14 +38,16 @@ describe('correct rendering', () => {
bcc={emailAddresses}
/>
);
- const wrapper = mount(multiRecipientLink).find('a');
-
- expect(wrapper.prop('href')).toEqual('mailto:foo@example.com,bar@example.com,baz@example.com?bcc=foo%40example.com%2Cbar%40example.com%2Cbaz%40example.com&body=body&cc=foo%40example.com%2Cbar%40example.com%2Cbaz%40example.com&subject=subject');
+ const { getByText } = render(multiRecipientLink);
+ const linkElement = getByText('content');
+ expect(linkElement.getAttribute('href')).toEqual(
+ 'mailto:foo@example.com,bar@example.com,baz@example.com?bcc=foo%40example.com%2Cbar%40example.com%2Cbaz%40example.com&body=body&cc=foo%40example.com%2Cbar%40example.com%2Cbaz%40example.com&subject=subject',
+ );
});
it('renders empty mailtoLink', () => {
- const wrapper = mount().find('a');
-
- expect(wrapper.prop('href')).toEqual('mailto:');
+ const { getByText } = render();
+ const linkElement = getByText('content');
+ expect(linkElement.getAttribute('href')).toEqual('mailto:');
});
});
diff --git a/src/Menu/Menu.test.jsx b/src/Menu/Menu.test.jsx
index 133a1f770a..c2cdad5f98 100644
--- a/src/Menu/Menu.test.jsx
+++ b/src/Menu/Menu.test.jsx
@@ -1,6 +1,8 @@
import React from 'react';
-import { mount } from 'enzyme';
+import { render, screen } from '@testing-library/react';
import renderer from 'react-test-renderer';
+import userEvent from '@testing-library/user-event';
+
import { Add, Check } from '../../icons';
import { MenuItem } from '..';
import Menu from '.';
@@ -8,11 +10,14 @@ import Hyperlink from '../Hyperlink';
import Button from '../Button';
import Form from '../Form';
+const MENU_ITEM_TEXT = 'Cant Touch This';
+
describe('Menu Item renders correctly', () => {
- it('renders as just a div With empty usage', () => {
- const wrapper = mount();
- expect(wrapper.find('div').exists()).toEqual(true);
+ it('renders as just a div with empty usage', () => {
+ render();
+ expect(screen.getByTestId('menu')).toBeInTheDocument();
});
+
it('renders as expected with menu items', () => {
const tree = renderer.create((
,
+ );
+
+ const button = screen.getByText(MENU_ITEM_TEXT);
+ await userEvent.click(button);
expect(clickFn).toHaveBeenCalledTimes(0);
});
});
-describe('Keyoard Interactions', () => {
- const wrapper = mount(
-
-
-
-
- ,
- );
-
- const menuContainer = wrapper.find(Menu);
- const menuItems = wrapper.find(MenuItem);
-
- it('should focus on the first item after click', () => {
- menuItems.at(0).simulate('click');
- expect(menuItems.at(0) === document.activeElement);
+describe('Keyboard Interactions', () => {
+ beforeEach(() => {
+ render(
+
+
+
+
+ ,
+ );
+ });
+
+ it('should focus on the first item after click', async () => {
+ const defaultItem = screen.getByText('Default').parentElement;
+ await userEvent.click(defaultItem);
+ expect(defaultItem).toHaveFocus();
});
+
it('should focus the next item after ArrowDown keyDown', () => {
- menuContainer
- .simulate('keyDown', { key: 'ArrowDown' });
- expect(menuItems.at(1) === document.activeElement);
+ const defaultItem = screen.getByText('Default');
+ const cantTouchThisItem = screen.getByText(MENU_ITEM_TEXT).parentElement;
+
+ userEvent.type(defaultItem, '{arrowdown}');
+
+ expect(cantTouchThisItem).toHaveFocus();
});
+
it('should focus the next item after Tab keyDown', () => {
- menuContainer.simulate('keyDown', { key: 'Tab' });
- expect(menuItems.at(2) === document.activeElement);
+ const defaultItem = screen.getByText('Default').parentElement;
+ const cantTouchThisItem = screen.getByText(MENU_ITEM_TEXT).parentElement;
+ defaultItem.focus();
+ userEvent.tab();
+
+ expect(cantTouchThisItem).toHaveFocus();
});
it('should loop focus to the first item after Tab keyDown on last item', () => {
- menuContainer.simulate('keyDown', { key: 'Tab' });
- expect(menuItems.at(0) === document.activeElement);
+ const defaultItem = screen.getByText('Default').parentElement;
+ const iconBeforeItem = screen.getByText('Icon Before');
+ iconBeforeItem.focus();
+ userEvent.tab();
+
+ expect(defaultItem).toHaveFocus();
});
it('should loop focus to the last item after ArrowUp keyDown on first item', () => {
- menuContainer.simulate('keyDown', { key: 'ArrowUp' });
- expect(menuItems.at(2) === document.activeElement);
+ const defaultItem = screen.getByText('Default').parentElement;
+ const iconBeforeItem = screen.getByText('Icon Before').parentElement;
+ defaultItem.focus();
+ userEvent.type(defaultItem, '{arrowup}');
+
+ expect(iconBeforeItem).toHaveFocus();
});
it('should focus the previous item after Shift + Tab keyDown', () => {
- menuContainer.simulate('keyDown', { key: 'Tab', shiftKey: true });
- expect(menuItems.at(1) === document.activeElement);
+ const button1 = screen.getAllByRole('button')[0];
+ const button2 = screen.getAllByRole('button')[1];
+
+ button2.focus();
+ userEvent.tab({ shift: true });
+
+ expect(button1).toHaveFocus();
});
});
diff --git a/src/Menu/MenuItem.test.jsx b/src/Menu/MenuItem.test.jsx
index b242969ca9..dc59443549 100644
--- a/src/Menu/MenuItem.test.jsx
+++ b/src/Menu/MenuItem.test.jsx
@@ -1,6 +1,8 @@
import React from 'react';
-import { mount } from 'enzyme';
+import { render, screen } from '@testing-library/react';
import renderer from 'react-test-renderer';
+import userEvent from '@testing-library/user-event';
+
import { Add, Check } from '../../icons';
import { MenuItem } from '..';
import Button from '../Button';
@@ -14,28 +16,32 @@ describe('Menu Item', () => {
)).toJSON();
expect(tree).toMatchSnapshot();
});
- it('The Button can be clicked', () => {
+
+ it('The Button can be clicked', async () => {
const clickFn = jest.fn();
- const wrapper = mount((
-
- ));
- expect(clickFn).toHaveBeenCalledTimes(0);
- wrapper
- .find('button')
- .simulate('click');
+ render(
+ ,
+ );
+ const button = screen.getByRole('button');
+
+ await userEvent.click(button);
+
expect(clickFn).toHaveBeenCalledTimes(1);
});
- it('Disabled Button cant be clicked', () => {
+
+ it('Disabled Button can\'t be clicked', async () => {
const clickFn = jest.fn();
- const wrapper = mount(
+ render(
,
);
- expect(clickFn).toHaveBeenCalledTimes(0);
- wrapper
- .find('button')
- .simulate('click');
+ const button = screen.getByRole('button');
+
+ await userEvent.click(button);
+
expect(clickFn).toHaveBeenCalledTimes(0);
});
});
diff --git a/src/Menu/SelectMenu.test.jsx b/src/Menu/SelectMenu.test.jsx
index be22f4edcd..9d542da4e2 100644
--- a/src/Menu/SelectMenu.test.jsx
+++ b/src/Menu/SelectMenu.test.jsx
@@ -1,15 +1,21 @@
import React from 'react';
-import { mount } from 'enzyme';
+import { render, screen } from '@testing-library/react';
import renderer from 'react-test-renderer';
+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 ;
+}
+
+function defaultSelectMenu() {
+ return (
@@ -25,17 +31,8 @@ const selectMenu = mount(
- ), { attachTo: app },
-);
-
-function DefaultSelectMenu(props) {
- return ;
+ );
}
-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 +41,132 @@ 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);
+ it('opens on trigger click', async () => {
+ const { getByRole } = render(defaultSelectMenu());
+ const menuTrigger = getByRole('button', { expanded: false });
+ await 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);
+ it('should focus on the first item after opening', async () => {
+ const { getByRole, getAllByRole } = render(defaultSelectMenu());
+ const menuTrigger = getByRole('button', { expanded: false });
+ await userEvent.click(menuTrigger);
+ expect(menuTrigger).toHaveAttribute('aria-expanded', 'true');
+ const menuItem = getAllByRole('link')[0];
+ expect(menuItem).toHaveFocus();
});
- it('returns focus to trigger on close', () => {
- menuItems.at(7).simulate('click');
- expect(menuTrigger === document.activeElement);
+
+ it('returns focus to trigger on close', async () => {
+ const { getByRole, getAllByRole } = render(defaultSelectMenu());
+ const menuTrigger = getByRole('button', { expanded: false });
+ await userEvent.click(menuTrigger);
+ const menuItems = getAllByRole('link');
+ await userEvent.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);
- });
- 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);
+ it('should focus on the first item after opening', async () => {
+ const { getByRole, getAllByRole } = render(defaultSelectMenu());
+ const menuTrigger = getByRole('button', { expanded: false });
+ await userEvent.click(menuTrigger);
+ const menuItems = getAllByRole('link');
+ expect(menuItems[0]).toHaveFocus();
+ });
+
+ it('should focus the next item after ArrowDown keyDown', async () => {
+ const { getByRole, getAllByRole } = render(defaultSelectMenu());
+ const menuTrigger = getByRole('button', { expanded: false });
+ await userEvent.click(menuTrigger);
+ const menuItems = getAllByRole('link');
+ await userEvent.keyboard('[arrowdown]');
+ expect(menuItems[1]).toHaveFocus();
+ });
+
+ it('should focus the next item after ArrowRight keyDown', async () => {
+ const { getByRole, getAllByRole } = render(defaultSelectMenu());
+ const menuTrigger = getByRole('button', { expanded: false });
+ await userEvent.click(menuTrigger);
+ const menuItems = getAllByRole('link');
+ await userEvent.keyboard('[arrowright]');
+ expect(menuItems[1]).toHaveFocus();
+ });
+
+ it('should focus the previous item after ArrowUp keyDown', async () => {
+ const { getByRole, getAllByRole } = render(defaultSelectMenu());
+ const menuTrigger = getByRole('button', { expanded: false });
+ await userEvent.click(menuTrigger);
+ const menuItems = getAllByRole('link');
+ menuItems[1].focus();
+ await userEvent.keyboard('[arrowup]');
+ expect(menuItems[0]).toHaveFocus();
+ });
+
+ it('should focus the previous item after ArrowLeft keyDown', async () => {
+ const { getByRole, getAllByRole } = render(defaultSelectMenu());
+ const menuTrigger = getByRole('button', { expanded: false });
+ await userEvent.click(menuTrigger);
+ const menuItems = getAllByRole('link');
+ menuItems[1].focus();
+ await userEvent.keyboard('[arrowleft]');
+ expect(menuItems[0]).toHaveFocus();
+ });
+
+ it('edge behavior should loop start to end', async () => {
+ const { getByRole, getAllByRole } = render(defaultSelectMenu());
+ const menuTrigger = getByRole('button', { expanded: false });
+ await userEvent.click(menuTrigger);
+ const menuItems = getAllByRole('link');
+ await userEvent.keyboard('[arrowup]');
+ expect(menuItems[menuItems.length - 1]).toHaveFocus();
+ });
+ it('edge behavior should loop end to start', async () => {
+ const { getByRole, getAllByRole } = render(defaultSelectMenu());
+ const menuTrigger = getByRole('button', { expanded: false });
+ await userEvent.click(menuTrigger);
+ const menuItems = getAllByRole('link');
+ await userEvent.keyboard('[arrowup]');
+ await userEvent.keyboard('[arrowdown]');
+ expect(menuItems[0]).toHaveFocus();
+ });
+
+ it('should move focus to the last item when pressing the End key', async () => {
+ const { getByRole, getAllByRole } = render(defaultSelectMenu());
+ const menuTrigger = getByRole('button', { expanded: false });
+ await userEvent.click(menuTrigger);
+ const menuItems = getAllByRole('link');
+ await userEvent.keyboard('[end]');
+ expect(menuItems[menuItems.length - 1]).toHaveFocus();
+ });
+ it('should move focus to the first item when pressing the Home key', async () => {
+ const { getByRole, getAllByRole } = render(defaultSelectMenu());
+ const menuTrigger = getByRole('button', { name: 'Select...', expanded: false });
+ await userEvent.click(menuTrigger);
+ const menuItems = getAllByRole('link');
+ menuItems[menuItems.length - 1].focus();
+ await userEvent.keyboard('[home]');
+ expect(menuItems[0]).toHaveFocus();
});
});
diff --git a/src/Modal/AlertModal.jsx b/src/Modal/AlertModal.jsx
index 1b0ee88251..b0940a886b 100644
--- a/src/Modal/AlertModal.jsx
+++ b/src/Modal/AlertModal.jsx
@@ -19,7 +19,13 @@ function AlertModal({
>
- {icon && }
+ {icon && (
+
+ )}
{props.title}
diff --git a/src/Modal/Modal.test.jsx b/src/Modal/Modal.test.jsx
deleted file mode 100644
index 6f7c5499ec..0000000000
--- a/src/Modal/Modal.test.jsx
+++ /dev/null
@@ -1,277 +0,0 @@
-import React from 'react';
-import { mount, shallow } from 'enzyme';
-
-import Modal from './index';
-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 title = 'Modal title';
-const body = 'Modal body';
-const defaultProps = {
- title,
- body,
- open: true,
- onClose: () => { },
-};
-const closeText = 'GO AWAY!';
-
-let wrapper;
-
-describe('', () => {
- describe('correct rendering', () => {
- const buttons = [
- Blue button!,
- {
- label: 'Red button!',
- buttonType: 'danger',
- },
- Green button!,
- ];
-
- it('renders default buttons', () => {
- wrapper = mount();
- const modalTitle = wrapper.find('.modal-title');
- const modalBody = wrapper.find('.modal-body');
-
- expect(modalTitle.text()).toEqual(title);
- expect(modalBody.text()).toEqual(body);
- expect(wrapper.find('button')).toHaveLength(2);
- });
-
- it('renders custom buttons', () => {
- wrapper = mount();
- expect(wrapper.find('button')).toHaveLength(buttons.length + 2);
- });
-
- it('renders Warning Variant', () => {
- wrapper = mount();
-
- const modalBody = wrapper.find('.modal-body');
- expect(modalBody.childAt(0).hasClass('container-fluid')).toEqual(true);
- expect(modalBody.find('p').text()).toEqual(body);
-
- 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);
- });
-
- it('renders invalid Variant properly', () => {
- wrapper = mount();
- const modalTitle = wrapper.find('.modal-title');
- const modalBody = wrapper.find('.modal-body');
-
- expect(modalTitle.text()).toEqual(title);
- expect(modalBody.text()).toEqual(body);
- expect(wrapper.find('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');
-
- expect(modalHeader.find('button')).toHaveLength(0);
- expect(modalFooter.find('button')).toHaveLength(1);
- expect(wrapper.find('button')).toHaveLength(1);
- });
-
- it('render of the default footer close button is optional', () => {
- wrapper = mount();
- const modalHeader = wrapper.find('.modal-header');
- const modalFooter = wrapper.find('.modal-footer');
-
- expect(modalHeader.find('button')).toHaveLength(1);
- expect(modalFooter.find('button')).toHaveLength(0);
- expect(wrapper.find('button')).toHaveLength(1);
- });
-
- 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);
- });
-
- 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);
- });
-
- it('renders with IE11-specific styling when IE11 is detected', () => {
- const { MSInputMethodContext } = global;
- const { documentMode } = global.document;
-
- // mimic IE11
- global.MSInputMethodContext = true;
- global.document.documentMode = true;
- wrapper = mount();
- const modal = wrapper.find('.modal');
- expect(modal.hasClass('is-ie11')).toEqual(true);
-
- global.MSInputMethodContext = MSInputMethodContext;
- global.document.documentMode = documentMode;
- });
-
- it('renders without IE11-specific styling when IE11 is not detected', () => {
- const { MSInputMethodContext } = global;
- const { documentMode } = global.document;
-
- // 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);
-
- global.MSInputMethodContext = MSInputMethodContext;
- global.document.documentMode = documentMode;
- });
- });
-
- 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();
-
- modalOpen(false, wrapper);
- wrapper.setProps({ open: true });
- wrapper.update();
- modalOpen(true, wrapper);
- });
-
- it('component receives props and ignores prop change', () => {
- wrapper = mount();
-
- modalOpen(true, wrapper);
- wrapper.setProps({ title: 'Changed modal title' });
- wrapper.update();
- modalOpen(true, wrapper);
- });
-
- it('throws an error when an invalid parentSelector prop is passed', () => {
- expect(() => {
- wrapper = shallow();
- }).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);
- });
-
- it('closes when Close button pressed', () => {
- modalOpen(true, wrapper);
- wrapper.find('button').at(1).simulate('click');
- modalOpen(false, wrapper);
- });
-
- it('calls callback function on close', () => {
- const spy = jest.fn();
-
- wrapper = mount();
-
- expect(spy).toHaveBeenCalledTimes(0);
-
- // press X button
- wrapper.find('button').at(0).simulate('click');
- 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);
- });
- });
-
- 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);
- });
-
- 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);
- });
-
- it('has focus on input in modal body', () => {
- wrapper = mount((
- }
- />
- ));
- expect(wrapper.find('input').html()).toEqual(document.activeElement.outerHTML);
- });
-
- it('has focus on `.modal-content` when nothing else is tabbable', () => {
- wrapper = mount((
-
- ));
- expect(wrapper.find('.modal-content').html()).toEqual(document.activeElement.outerHTML);
- });
- });
-});
diff --git a/src/Modal/ModalCloseButton.test.jsx b/src/Modal/ModalCloseButton.test.jsx
deleted file mode 100644
index 3f0a978861..0000000000
--- a/src/Modal/ModalCloseButton.test.jsx
+++ /dev/null
@@ -1,29 +0,0 @@
-import React from 'react';
-import { mount } from 'enzyme';
-import { ModalContextProvider } from './ModalContext';
-import ModalCloseButton from './ModalCloseButton';
-
-describe('', () => {
- it('calls a modal context close function on click', () => {
- const mockClose = jest.fn();
- const wrapper = mount((
-
- Close
-
- ));
- wrapper.find(ModalCloseButton).simulate('click');
- expect(mockClose).toHaveBeenCalled();
- });
- it('calls both the modal context close function and an passed click handler', () => {
- const mockClose = jest.fn();
- const mockOnClick = jest.fn();
- const wrapper = mount((
-
- Close
-
- ));
- wrapper.find(ModalCloseButton).simulate('click');
- expect(mockClose).toHaveBeenCalled();
- expect(mockOnClick).toHaveBeenCalled();
- });
-});
diff --git a/src/Modal/ModalLayer.jsx b/src/Modal/ModalLayer.jsx
index ce32887536..61990a41f4 100644
--- a/src/Modal/ModalLayer.jsx
+++ b/src/Modal/ModalLayer.jsx
@@ -7,8 +7,15 @@ import { ModalContextProvider } from './ModalContext';
// istanbul ignore next
function ModalBackdrop({ onClick }) {
- // eslint-disable-next-line jsx-a11y/no-static-element-interactions
- return ;
+ return (
+ // eslint-disable-next-line jsx-a11y/no-static-element-interactions
+
+ );
}
ModalBackdrop.propTypes = {
diff --git a/src/Modal/ModalLayer.test.jsx b/src/Modal/ModalLayer.test.jsx
deleted file mode 100644
index 2995e4b532..0000000000
--- a/src/Modal/ModalLayer.test.jsx
+++ /dev/null
@@ -1,100 +0,0 @@
-import React from 'react';
-import { shallow } from 'enzyme';
-import { FocusOn } from 'react-focus-on';
-import ModalLayer, { ModalBackdrop } from './ModalLayer';
-import { ModalContextProvider } from './ModalContext';
-
-/* eslint-disable react/prop-types */
-jest.mock('./Portal', () => function PortalMock(props) {
- const { children, ...otherProps } = props;
- return (
-
- {children}
-
- );
-});
-
-jest.mock('react-focus-on', () => ({
- FocusOn: (props) => {
- const { children, ...otherProps } = props;
- return (
- {children}
- );
- },
-}));
-
-function Dialog() {
- return (
-
- A dialog.
-
- );
-}
-
-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);
- });
-
- it('renders a modal context provider', () => {
- const contextProvider = wrapper.find(ModalContextProvider);
- expect(contextProvider.props().isOpen).toBe(isOpen);
- expect(contextProvider.props().onClose).toBe(closeFn);
- });
-
- 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);
- });
- });
-
- test('when isOpen is false the dialog is not rendered', () => {
- const wrapper = shallow((
-
-
-
- ));
- const dialog = wrapper.find(Dialog);
- expect(dialog.length).toBe(0);
- });
-
- describe('Backdrop', () => {
- it('closes a non-blocking modal layer when clicked', () => {
- const closeFn = jest.fn();
- const wrapper = shallow((
-
-
-
- ));
-
- const backdrop = wrapper.find(ModalBackdrop);
- backdrop.simulate('click');
- expect(closeFn).toHaveBeenCalled();
- });
- it('does not close a blocking modal layer when clicked', () => {
- const closeFn = jest.fn();
- const wrapper = shallow((
-
-
-
- ));
-
- const backdrop = wrapper.find(ModalBackdrop);
- backdrop.simulate('click');
- expect(closeFn).not.toHaveBeenCalled();
- });
- });
-});
diff --git a/src/Modal/ModalPopup.jsx b/src/Modal/ModalPopup.jsx
index f92efb8ae7..ada4f0db22 100644
--- a/src/Modal/ModalPopup.jsx
+++ b/src/Modal/ModalPopup.jsx
@@ -55,6 +55,7 @@ function ModalPopup({
{hasArrow && (
diff --git a/src/Modal/ModalPopup.test.jsx b/src/Modal/ModalPopup.test.jsx
deleted file mode 100644
index 7749e414a2..0000000000
--- a/src/Modal/ModalPopup.test.jsx
+++ /dev/null
@@ -1,143 +0,0 @@
-import React from 'react';
-import { shallow } from 'enzyme';
-import { FocusOn } from 'react-focus-on';
-import ModalPopup from './ModalPopup';
-import { ModalContextProvider } from './ModalContext';
-import Portal from './Portal';
-
-/* eslint-disable react/prop-types */
-jest.mock('./Portal', () => function PortalMock(props) {
- const { children, ...otherProps } = props;
- return (
-
- {children}
-
- );
-});
-
-jest.mock('react-focus-on', () => ({
- FocusOn: (props) => {
- const { children, ...otherProps } = props;
- return (
- {children}
- );
- },
-}));
-
-function Dialog() {
- return (
-
- A dialog.
-
- );
-}
-
-const mockPositionRef = { current: null };
-
-const arrowPlacements = [
- 'auto',
- 'auto-start',
- 'auto-end',
- 'top',
- 'top-start',
- 'top-end',
- 'bottom',
- 'bottom-start',
- 'bottom-end',
- 'right',
- 'right-start',
- 'right-end',
- 'left',
- 'left-start',
- 'left-end',
-];
-
-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);
- });
-
- 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);
- });
- });
-
- it('when isOpen is false the dialog is not rendered', () => {
- const wrapper = shallow((
-
-
-
- ));
- const dialog = wrapper.find(Dialog);
- expect(dialog.length).toBe(0);
- });
-
- describe('withPortal', () => {
- it('renders no portal by default', () => {
- const wrapper = shallow((
-
-
-
- ));
- expect(wrapper.find(Portal).length).toBe(0);
- });
- it('renders with a portal if withPortal is true', () => {
- const wrapper = shallow((
-
-
-
- ));
- expect(wrapper.find(Portal).length).toBe(1);
- });
- });
- describe('withArrow', () => {
- const popupArrowModalClass = '.pgn__modal-popup__arrow';
- arrowPlacements.forEach((side) => {
- it(`renders with placement ${side}`, () => {
- const wrapperPopup = shallow((
-
-
- ));
- expect(wrapperPopup.exists(`${popupArrowModalClass}-${side}`)).toBe(true);
- });
- });
- it('renders without arrow', () => {
- const wrapperPopup = shallow((
-
-
- ));
- expect(wrapperPopup.exists(popupArrowModalClass)).toBe(false);
- });
- it('renders with arrow', () => {
- const wrapperPopup = shallow((
-
-
- ));
- expect(wrapperPopup.exists(popupArrowModalClass)).toBe(true);
- });
- });
-});
diff --git a/src/Modal/index.jsx b/src/Modal/index.jsx
index a12a7d6931..ee9c8c1fb8 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}