2

I'm writing unit tests for a component that renders a Material UI Select. But userEvent.selectOption does not seem to be working. In the example below, both getByRole calls seem to be selecting the correct components, but the validation with screen.getByText fails to find the text, and the test output shows that the placeholder option is still selected. Testing manually in Storybook shows my component works as expected. I've tried some variations of using fireEvent but no luck with that either.

TestSelect.tsx:

import { Box, Select } from '@mui/material';
import { useState } from 'react';

export function TestSelect() {
  const [thing, setThing] = useState('');

  return (
    <Box>
      <Select
        placeholder="hi"
        onChange={(evt) => {
          setThing(evt.target.value);
        }}
        value={thing}
      >
        <option key="" value="">hi</option>
        <option value="One">One</option>
        <option value="Two">Two</option>
      </Select>
      <div>{thing && `thing: ${thing}`}</div>
    </Box>
  );
}

TestSelect.test.tsx:

import userEvent from '@testing-library/user-event';
import { render, screen } from '@testing-library/react';
import { TestSelect } from './TestSelect';

it('select option', async () => {
  const { click, selectOptions } = userEvent.setup();

  render(<TestSelect />);
  await click(screen.getByRole('button'));
  await selectOptions(
    screen.getByRole('listbox'),
    screen.getByRole('option', { name: 'One' })
  );

  // next line throws "Unable to find an element with the text: thing: One."
  expect(screen.getByText('thing: One')).toBeInTheDocument();
});

according to the test output, here is the relevant section of the DOM at the time the assertion happens (after the selectOptions promise has resolved):

    <ul
      class="MuiList-root MuiList-padding MuiMenu-list css-6hp17o-MuiList-root-MuiMenu-list"
      role="listbox"
      tabindex="-1"
    >
      <option
        aria-selected="true"
        data-value=""
        role="option"
        tabindex="0"
      >
        hi
      </option>
      <option
        aria-selected="false"
        data-value="One"
        role="option"
      >
        One
      </option>
      <option
        aria-selected="false"
        data-value="Two"
        role="option"
      >
        Two
      </option>
    </ul>

package versions:

"@mui/material": "5.10.5",
"@testing-library/react": "13.4.0",
"@testing-library/user-event": "14.4.3",
"vitest": "0.23.4"
Tom Faber
  • 131
  • 1
  • 6

1 Answers1

1

Generally, after you perform an action, you want to wait for it to complete. The best way to do this is to use screen.findBy which uses waitFor() under the hood. So something like this:

expect(await screen.findByText('thing: One')).toBeInTheDocument();

Also, another assertion could be to check if the option is selected:

expect(await screen.findByRole('option', { name: 'One' }).selected).toBe(true)

Cathal Mac Donnacha
  • 1,285
  • 1
  • 16
  • 23
  • 1
    Thanks! But sadly this did not work. Using the code you suggested, the same error happened as before. My understanding of `user-event` is that because I *await*ed the `selectOptions` response, the action should already be complete by the time the assertion happens. Asserting that the option is selected fails, because the option is not selected (test output confirms this). – Tom Faber Nov 29 '22 at 00:03
  • I also tried `await act(() => selectOptions(` etc. That led to the same result. This all makes me think that `selectOptions` is not selecting the option from the MUI select, I just can't figure out why. – Tom Faber Nov 29 '22 at 00:10
  • Can you try putting together a simple CodeSandbox or StackBlitz example? – Cathal Mac Donnacha Nov 29 '22 at 08:39