This is a recurrent question. I’m sharing my own code with 100% passing tests which cover 100% of my source code.
My component looks like this
MySelectComponent({ options, onChange }) {
return <div data-testid="my-select-component">
<Select
className="basic-single"
classNamePrefix="select"
name="myOptions"
placeholder="Select an option"
options={options}
onChange={e => onChange(e)}
/>
</div>;
}
The reason I’m adding a wrapper on my Select
with data-testid="my-select-component"
is that the rendered options element will be available on it otherwise I can’t check if a text option exist (you’ll understand better when you’ll see my tests).
This is a live running example and when rendering it’ll show a select component with 10 options.
Test 1 : should render without errors
-
I render the component.
-
I search for the placeholder to be present.
Test 2 : should call onChange when the first option is selected
-
I render the component.
-
I check if my
mockedOnChange
is not yet called. -
Simulate an
ArrowDown
event. -
Click on the first option.
-
I check if
mockedOnChange
is called 1 time with the 1st option label and value.
Test 3 : should call onChange when the first option is selected then second option then the 9th one
-
I render the component.
-
I simulate a select of the first option.
-
I simulate a select of the 2nd option.
-
I simulate a select of the 9th option.
-
I check if the
mockedOnChange
is called 3 times with the 9th option bale and value.
Test 4 : should call onChange when filtering by input value
-
I render the component.
-
I simulate a change on the input field by typing “option 1”.
-
I know, based on my
mockedOptions
that the filtered result will be “Mocked option 1” and “Mocked option 10”. -
I simulate 2
ArrowDown
events. -
I check that the
mockedOnChange
is called with the 2nd filtered option with right label and value.
Complete test file
import React from 'react';
import { render, fireEvent, cleanup, waitForElement } from '@testing-library/react';
import MySelectComponent from './MySelectComponent';
afterEach(cleanup);
describe ('Test react-select component', () => {
const mockedOptions = [
{label: 'Mocked option 1', value: 'mocked-option-1'},
{label: 'Mocked option 2', value: 'mocked-option-2'},
{label: 'Mocked option 3', value: 'mocked-option-3'},
{label: 'Mocked option 4', value: 'mocked-option-4'},
{label: 'Mocked option 5', value: 'mocked-option-5'},
{label: 'Mocked option 6', value: 'mocked-option-6'},
{label: 'Mocked option 7', value: 'mocked-option-7'},
{label: 'Mocked option 8', value: 'mocked-option-8'},
{label: 'Mocked option 9', value: 'mocked-option-9'},
{label: 'Mocked option 10', value: 'mocked-option-10'},
];
it('should render without errors', async () => {
const mockedOnChange = jest.fn();
const { getByText } = render(<MySelectComponent
options={mockedOptions}
onChange={mockedOnChange} />);
const placeholder = getByText('Select an option');
expect(placeholder).toBeTruthy();
});
it('should call onChange when the first option is selected', async () => {
const mockedOnChange = jest.fn();
const { getByText, queryByTestId } = render(<MySelectComponent
options={mockedOptions}
onChange={mockedOnChange} />);
const mySelectComponent = queryByTestId('my-select-component');
expect(mySelectComponent).toBeDefined();
expect(mySelectComponent).not.toBeNull();
expect(mockedOnChange).toHaveBeenCalledTimes(0);
fireEvent.keyDown(mySelectComponent.firstChild, { key: 'ArrowDown' });
await waitForElement(() => getByText('Mocked option 1'));
fireEvent.click(getByText('Mocked option 1'));
expect(mockedOnChange).toHaveBeenCalledTimes(1);
expect(mockedOnChange).toHaveBeenCalledWith({label: 'Mocked option 1', value: 'mocked-option-1'});
});
it('should call onChange when the first option is selected then second option then the 9th one', async () => {
const mockedOnChange = jest.fn();
const { getByText, queryByTestId } = render(<MySelectComponent
options={mockedOptions}
onChange={mockedOnChange} />);
const mySelectComponent = queryByTestId('my-select-component');
expect(mySelectComponent).toBeDefined();
expect(mySelectComponent).not.toBeNull();
expect(mockedOnChange).toHaveBeenCalledTimes(0);
fireEvent.keyDown(mySelectComponent.firstChild, { key: 'ArrowDown' });
await waitForElement(() => getByText('Mocked option 1'));
fireEvent.click(getByText('Mocked option 1'));
fireEvent.keyDown(mySelectComponent.firstChild, { key: 'ArrowDown' });
await waitForElement(() => getByText('Mocked option 2'));
fireEvent.click(getByText('Mocked option 2'));
fireEvent.keyDown(mySelectComponent.firstChild, { key: 'ArrowDown' });
await waitForElement(() => getByText('Mocked option 9'));
fireEvent.click(getByText('Mocked option 9'));
expect(mockedOnChange).toHaveBeenCalledTimes(3);
expect(mockedOnChange).toHaveBeenCalledWith({label: 'Mocked option 9', value: 'mocked-option-9'});
});
it('should call onChange when filtering by input value', async () => {
const mockedOnChange = jest.fn();
const { getByText, queryByTestId, container } = render(<MySelectComponent
options={mockedOptions}
onChange={mockedOnChange} />);
const mySelectComponent = queryByTestId('my-select-component');
fireEvent.change(container.querySelector('input'), {
target: { value: 'option 1' },
});
// select Mocked option 1
fireEvent.keyDown(mySelectComponent.firstChild, { key: 'ArrowDown' });
// select Mocked option 10
fireEvent.keyDown(mySelectComponent.firstChild, { key: 'ArrowDown' });
await waitForElement(() => getByText('Mocked option 10'));
fireEvent.click(getByText('Mocked option 10'));
expect(mockedOnChange).toHaveBeenCalledTimes(1);
expect(mockedOnChange).toHaveBeenCalledWith({label: 'Mocked option 10', value: 'mocked-option-10'});
});
});
I hope that this help.