User-event: DateRangePicker onChange not triggering during testing

Created on 7 Aug 2020  路  4Comments  路  Source: testing-library/user-event

  • @testing-library/user-event version: v12.0.11
  • Testing Framework and version:
  • jest version: v24.9.0
  • DOM Environment:
  • @testing-library/jest-dom version: v5.11.0


node version: v13.8.0

What you did:
Writing test for DateRangePicker from Material-UI to assert the changes to the component which needs the date range information.

What happened:
The onChange of the DateRangePicker doesn't get triggered.
However, it works in the browser.
This simply does nothing:

await userEvent.type(screen.getByLabelText(/start date/i), '05082020');

However:
Using the delay option throws an unexpected error:

await userEvent.type(screen.getByLabelText(/start date/i), '05082020', {delay: 1});

TypeError: the current element is of type DIV and doesn't have a valid value

This error is not very clear, since when debugging the element returned by the query it returns the correct one.

screen.debug(screen.getByLabelText(/start date/i));
...
<input
      aria-invalid="false"
      class="MuiInputBase-input MuiInput-input"
      id="startDate"
      placeholder="dd/mm/yyyy"
      type="tel"
      value="06/08/2020"
    />

Reproduction repository:
I've created a reproduction in a sandbox, but couldn't get the tests feedback. Not sure why.
https://codesandbox.io/s/daterangepicker-onchange-not-triggering-022cl

Problem description:
The behaviour should be as expected when running tests or testing manually in a browser. Meaning, the onChange event should be triggered disregarding if the method of triggering it was userEvent.type() or manually via browser.

Suggested solution:
I'm actually not sure if this is a fault of the DateRangePicker library, or userEvents library, and because of that I've created a bug ticket there as well.
https://github.com/mui-org/material-ui-pickers/issues/2073

help wanted needs investigation

Most helpful comment

Glad you worked that out! I think we're safe to close this here.

All 4 comments

Hi @mnajdova thanks for opening the issue :).

I suggest you to use getByRole (https://testing-playground.com/gist/e5bf2f963c4be4ebe69e08e9563e08f9/b288e31dbfab91329ed0e6828dbaa59037780f3f).

The problem here is that clicking on the input field only this events are fired

1) pointerdown
2) mousedown
3) focus
4) focusin

while on user-event are fired

1) pointerdown
2) mousedown
3) focus
4) focusin
5) pointerup
6) mouseup
7) click

This is causing a change of the focus from the input to the calendar div. This is wired 馃

@marcosvega91, thanks for your feedback.
We are currently investigating the bug and will come back with more info soon, hopefully.

So the problem in our (i.e. @mnemanja and mine) tests was that the date range picker is rendered in mobile mode by default.

The picker library (@material-ui/pickers) uses media queries to determine if you are using a desktop environment. We were running our tests with jest and jsdom 16. It does not implement window.matchMedia. Defining matchMedia made our tests work:

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

beforeEach(() => {
  // add window.matchMedia
  // this is necessary for the date picker to be rendered in desktop mode.
  // if this is not provided, the mobile mode is rendered, which might lead to unexpected behavior
  Object.defineProperty(window, 'matchMedia', {
    writable: true,
    value: (query: string): MediaQueryList => ({
      media: query,
      // this is the media query that @material-ui/pickers uses to determine if a device is a desktop device
      matches: query === '(pointer: fine)',
      onchange: () => {},
      addEventListener: () => {},
      removeEventListener: () => {},
      addListener: () => {},
      removeListener: () => {},
      dispatchEvent: () => false,
    }),
  });
});

afterEach(() => {
  delete window.matchMedia;
});

it('should be possible to set a start and end date', async () => {
  render(<MyComponent />);

  const startDateInput = await screen.findByRole('textbox', { name: /start date/i });
  const endDateInput = screen.getByRole('textbox', { name: /end date/i });

  userEvent.clear(startDateInput);
  // this has to be typed one number at a time for the formatting to work
  await userEvent.type(startDateInput, '20200106', { delay: 1 });

  userEvent.clear(endDateInput);
  await userEvent.type(endDateInput, '20200109', { delay: 1 });

  expect(screen.getByRole('textbox', { name: /start date/i })).toHaveValue('2020-01-06');
  expect(screen.getByRole('textbox', { name: /end date/i })).toHaveValue('2020-01-09');
});

So this was more a documentation issue for @material-ui/pickers.

Glad you worked that out! I think we're safe to close this here.

Was this page helpful?
0 / 5 - 0 ratings