ReactJs MaterialUI - How do I test change of a select component (non-native)?

Viewed 122

I have this simple component in my app:

import {Select, MenuItem} from '@material-ui/core';
import {useEffect, useState} from 'react';

export const CountrySelection = ({
  countries,
  selectedCountry,
  onChange,
  id,
  className = ''
})=>{

    const [_selectedCountry, _setSelectedCountry] = useState(null);

    useEffect(()=>{
        _setSelectedCountry(selectedCountry);
    }, [selectedCountry]);

    return(
        <Select 
            value={(_selectedCountry && _selectedCountry.label) || ''}
            onChange={onChange}
            id={id}
            className={'selection ' + className}
            inputProps={{
                'data-testid':'country-selection'
            }}
        >
            {countries.map((c)=><MenuItem 
                key={c.id} 
                value={c.label}
              >{c.label}</MenuItem>
            )}
        </Select>
    )
}

This is my test attempt. I want to test that component keeps correct value/visual state when I change to a different option in the Select component:

afterEach(cleanup);

const setup = () => {

    const countries = [
        {label: "Austria", id: 0, code: 'at'},
        {label: "Denmark", id: 1, code: 'dk'},
        {label: "Germany", id: 2, code: 'de'}
    ];
    const defaultCountry = countries[0];

    const utils = render(
        <CountrySelection 
            countries={countries}
            selectedCountry={defaultCountry}
            id="country-selection"
            onChange={()=>{}}
        />
    );

    return {
        ...utils,
    }
}

test('country selection has correct number of options', async()=>{
    const {getAllByRole, getByText, getByTestId} = setup();

    const selectEl = document.querySelector('#country-selection');
    fireEvent.mouseDown(selectEl);
    const options = getAllByRole('option');
    expect(options.length).toBe(3);
    
    const choice2 = getByText('Denmark');
    expect(choice2.innerHTML).toBe('Denmark<span class="MuiTouchRipple-root"></span>');

    const choice3 = getByText('Germany');
    expect(choice3.innerHTML).toBe('Germany<span class="MuiTouchRipple-root"></span>');

    // how to change the value and query it?
    fireEvent.click(choice3);
    const selection = getByTestId('country-selection');
    expect(selection.value).toBe('Germany'); // doesn't work, value is still "Austria"
})

How do I do this with Select component? I cannot use native={true} prop.

(I am typing here just so that Stack overflow posting validation is happy with ratio of code to other text. I will type as long as it doesn't let me post my question. Sorry, humans.)

1 Answers

If you just want to check if a country has been selected correctly you can use userEvent to select the option like a real user would:

import { screen } from '@testing-library/react';

test('country selection has correct number of options', async()=>{
    // Check that the correct number of options are displayed
    const options = screen.getAllByRole('option');
    expect(options.length).toBe(3);

    // Select Denmark
    userEvent.selectOptions(screen.getByRole('combobox'), 'dk');
 
    // Check that Denmark is the selected country
    expect((screen.getByText('Denmark') as HTMLOptionElement).selected).toBe(true);

    // Select Germany
    userEvent.selectOptions(screen.getByRole('combobox'), 'de');
 
    // Check that Germany is now the selected country
    expect((screen.getByText('Denmark') as HTMLOptionElement).selected).toBe(true);

})
Related