James Bender
James Bender

Reputation: 1232

Need help unit testing Select from shadcn/ui

I have a React component that I'm using shadcn/ui in, specifically in this case, the Select:

<div className='flex-1'>
  <FormField
    control={form.control}
    name='clientType'
    render={({ field }) => (
      <FormItem>
        <FormLabel>
          Client Type <span className='text-red-500 text-xs'>*</span>
        </FormLabel>
        <Select onValueChange={field.onChange} value={field.value}>
          <FormControl>
            <SelectTrigger className='w-full px-3 py-2 border rounded shadow-sm focus:outline-none focus:ring-1 focus:ring-indigo-500'>
              <SelectValue placeholder='Select a Client Type' />
            </SelectTrigger>
          </FormControl>
          <SelectContent>
            <SelectItem value='Direct'>Direct</SelectItem>
            <SelectItem value='VMS'>VMS</SelectItem>
            <SelectItem value='Lm'>Lm</SelectItem>
          </SelectContent>
        </Select>
        <FormMessage />
      </FormItem>
    )}
  />
</div>

I'm writing unit tests (Jest, React Testing Library), but I haven't been able to change the value of this select from my test. I've tried getting the reference as a combo box and a button and tried userEvent and fireEvent. Nothing seems to work.

I'm also using react-hook-forms, just fyi.

Has anyone done this, and can you show me the technique you used?

Thanks, James

Upvotes: 3

Views: 1937

Answers (1)

Jasperan
Jasperan

Reputation: 3806

You need to use fireEvent.pointerDown to open up the select options in your unit test. You'll also need to mock a few things. I'm using vitest but you can use jest as well.

// required mocks to open Shadcn Select component
export class MockPointerEvent extends Event {
  button: number | undefined;
  ctrlKey: boolean | undefined;

  constructor(type: string, props: PointerEventInit | undefined) {
    super(type, props);
    if (props) {
      if (props.button != null) {
        this.button = props.button;
      }
      if (props.ctrlKey != null) {
        this.ctrlKey = props.ctrlKey;
      }
    }
  }
}
window.PointerEvent = MockPointerEvent as any;
window.HTMLElement.prototype.scrollIntoView = vi.fn();
window.HTMLElement.prototype.hasPointerCapture = vi.fn();
window.HTMLElement.prototype.releasePointerCapture = vi.fn();

Then in your test code, use

fireEvent.pointerDown(
  selectElementTrigger,
  new MockPointerEvent('pointerdown', {
    ctrlKey: false,
    button: 0,
  })
);

And finally, to select the option from you Select component, do:

const selectedOption = await waitFor(() => findByText("You_option_here"));
fireEvent.click(selectedOption);

Update - Using Next.js

When using Next.js instead of React Native, I found that you need to use fireEvent.click() instead of fireEvent.pointerDown().

const selectElement = await screen.findByTestId("color-select");
fireEvent.click(selectElement);

const greenOption = await waitFor(() => screen.findByText("Green"));
fireEvent.click(greenOption);

Upvotes: 2

Related Questions