5

I'm new to react testing library so this may be easy, but I currently have a dropdown that I'm importing from fluent UI, and currently getting a "The element does not have a value setter" error when I try to test it using React Testing library. Here's my render function.

import { Dropdown, IDropdownStyles, IDropdownOption } from 'office-ui-fabric-react/lib/Dropdown';
import { Stack } from 'office-ui-fabric-react/lib/Stack';


render() 
  {
    return (
      <div className="LandingPage">
        <h3>Here are some options</h3>
        <form onSubmit={this.handleSubmit}>
          <Stack>
            <Stack.Item align="center">
              <Dropdown
              placeholder="Select an option"
              label="Select an option"
              options={this.options}
              styles={this.dropdownStyles}
              onChange={this.handleChange}
              data-testid="myId"
            />
            </Stack.Item>
          </Stack>
        </form>
      </div>
    );
  }
}


Here is the line that is failing:

  const dropdownOption: IDropdownOption = {
    key: "0",
    text: "dropdownOption"
  }

fireEvent.click(queryByTestId("myId"), { target: { value: 'B' }, option: dropdownOption })
The given element does not have a value setter

    > 44 |   fireEvent.click(queryByTestId("myId"), { target: { value: 'B' }, option: dropdownOption })
         |             ^

skyboyer
  • 22,209
  • 7
  • 57
  • 64
avenmia
  • 2,015
  • 4
  • 25
  • 49

3 Answers3

3

The problem is the FluentUI implementation of the Dropdown component. If you inspect the generated HTML you will find that the <Dropdown> is just styled <div> and that there are no <option> elements as child nodes.

To inspect, what is currently rendered when testing, you can add this line to your test and it will display "whole" HTML (if it's small enough, other wise increment second parameter by some zeros).

screen.debug(undefined, 10000000);

To be able to see these <option> elements you must first click on the <Dropdown> component. After the click new span elements with role option will be rendered and you can access them.

To click on the dropdown, you can use fireEvent or userEvent from react-testing-library.

To select one of the options you must also click on it. There I found out that for some reason userEvent doesn't work and you must use fireEvent.

So the whole example could look like this:

import { act, fireEvent, render, screen } from '@testing-library/react';

test('select value from FluentUI Dropdown', async () => {
    render(<YourComponentWithDropdown/>);

    // Get the dropdown HTML element
    const dropdown = await screen.findByRole('combobox');
    expect(dropdown).toBeDefined();

    // Click on the dropdown
    userEvent.click(dropdown);

    // Find the option you want to select
    const option = (await screen.findByRole('option', { name: 'Bannana bread' })) as HTMLButtonElement;
    expect(option).toBeDefined();

    // Click on the option
    fireEvent.click(option);

    // Test what should follow after selection
});
Matan Bobi
  • 2,693
  • 1
  • 15
  • 27
onex941
  • 145
  • 8
1

you can refrer to this answer : how to test react-select with react-testing-library, not with fluent UI but quite similar. don't use click or change ,use keyDown + click

const DOWN_ARROW = { keyCode: 40 };
fireEvent.keyDown(screen.getByLabelText('myId'), DOWN_ARROW);
fireEvent.click(screen.getByText('...your text'));  

and don't use data-testid, use aria-label instead.

jjzjx118_2
  • 419
  • 7
  • 23
  • Thank you for the suggestion! When I was doing this, I was just able to get the dropdown to select a value with just the `keyDown` operation as suggested. There wasn't a need to do the `click()`. – Daniel Gonzalez Mar 29 '21 at 20:48
0

I found that you didn't even need to use fireEvent.keyDown, as if you look in the jest snapshot of a Fluent UI dropdown component, all of the dropdown options are selectable buttons that are directly accessible.

This is what worked for me:

const option = screen.getAllByText("myDropdownOption")[0];
fireEvent.click(option);

I couldn't figure out what selector to use here, as selecting by just the text returns multiple elements, and a "button" selector didn't seem to work either, so I settled for using getAllByText()[0] to grab the first element and click that one. It's not a very pretty solution, I so abstracted it away into a helper function, until the correct selector is figured out.

edag94
  • 11
  • 1