1

The React Testing Library docs specify that userEvent is to be preferred to fireEvent. However, I can't figure out a userEvent way to click to a particular value on a slider. With fireEvent, we can do:

fireEvent.change(screen.getByRole('slider'), { target: { value: '3000' } })

Is this possible with userEvent? Or are there some capabilities that fireEvent has that userEvent does not?

2 Answers 2

1

It turns out that this is not currently possible - userEvent cannot test clicking a particular value on a slider, and using fireEvent is the correct approach. See this issue for details.

Sign up to request clarification or add additional context in comments.

Comments

0

This is how I have personally achieved that:

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

describe('My Slider', () => {
  const mockOnRangeChange = jest.fn()

  let user

  beforeEach(() => {
    jest
      .spyOn(window.HTMLElement.prototype, 'getBoundingClientRect')
      .mockImplementation(() => ({
        width: 140,
        left: 0,
        right: 140,
        height: 28,
        top: 0,
        bottom: 28
      }))

    user = userEvent.setup()

    render(
      <MySlider
        min={2}
        max={16}
        defaultRange={[6, 8]}
        onRangeChange={mockOnRangeChange}
      />
    )
  })

  afterEach(() => {
    jest.restoreAllMocks()
  })

  it('renders a range slider with the given limits and initial values', () => {
    const sliderHandles = screen.getAllByRole('slider')
    expect(sliderHandles).toHaveLength(2)

    sliderHandles.forEach((slider) => {
      expect(slider).toHaveAttribute('aria-valuemin', '2')
      expect(slider).toHaveAttribute('aria-valuemax', '16')
    })

    expect(sliderHandles[0]).toHaveAttribute('aria-valuenow', '6')
    expect(sliderHandles[0]).toHaveAttribute('aria-label', 'My slider - Minimum')
    expect(sliderHandles[1]).toHaveAttribute('aria-valuenow', '8')
    expect(sliderHandles[1]).toHaveAttribute('aria-label', 'My Slider - maximum')
  })

  describe('when the range changes', () => {
    it("calls 'onRangeChange' callback with the current range", async () => {
      const sliderHandles = screen.getAllByRole('slider')

      await act(async () => {
        await user.pointer([
          { target: sliderHandles[0], keys: '[MouseLeft>]' },
          { coords: { pageX: 10 } },
          { keys: '[/MouseLeft]' }
        ])
      })

      expect(mockOnRangeChange).toHaveBeenLastCalledWith([7, 8])

      await act(async () => {
        await user.pointer([
          { target: sliderHandles[1], keys: '[MouseLeft>]' },
          { coords: { pageX: 40 } },
          { keys: '[/MouseLeft]' }
        ])
      })

      expect(mockOnRangeChange).toHaveBeenLastCalledWith([7, 11])
    })
  })
})

Basically the getBoundingClientRect function must be globally mocked and then restored at the end of the test. This is necessary in JSDOM, as rendered components don't have real dimensions and, therefore, dragging an handle in any direction cannot trigger any meaningful change of value (apart from selecting the minimum or the maximum value).

I am using the slider widget from react-component/slider in JSDOM.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.