Testing React Select component Testing React Select component reactjs reactjs

Testing React Select component


From https://github.com/JedWatson/react-select/issues/1357

Only solution I found was to simulate a selection through key-down events:

wrapper.find('.Select-control').simulate('keyDown', { keyCode: 40 });// you can use 'input' instead of '.Select-control'wrapper.find('.Select-control').simulate('keyDown', { keyCode: 13 });expect(size).to.eql('your first value in the list')


I've tried both answers listed above, and still no luck.

What did work for me was:

  1. Add classNamePrefix prop - i.e list (as mentioned in the other answers) :

    <Select   classNamePrefix='list'   options={[     { label: 'one', value: 'one' },     { label: 'two', value: 'two' }]}/>
  2. select the dropdown indicator & simulate a mouseDown => opened dropdown:

    wrapper  .find('.list__dropdown-indicator')  .simulate('mouseDown', {    button: 0   });
  3. expect things to happen i.e. in my case I was checking for the number of dropdown options

    expect(wrapper.find('.list__option').length).toEqual(2);

    if you have control over the props being sent, you can add a menuIsOpen prop to always have the menu open (aka step 2 in the list).

To select a value from the dropdown, after opening the dropdown:

wrapper.find('.list__option').last().simulate('click', null);

then you can test either:

expect(wrapper.find('.list__value-container').text()).toEqual('two');

or

expect(wrapper.find('.list__single-value').text()).toEqual('two');


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.

enter image description here

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.