How to test ant design date picker using @testing-library/react?
Asked Answered
I

9

10

I have two ant design date pickers in a component. I am trying to change and test the value of date picker but not able to find the calendar input in the test environment dom.

Date Pickers Component

import { DatePicker, Form } from "antd";
import React, { Component } from "react";

import moment from "moment";

class DatePickers extends Component {
  constructor(props) {
    super(props);
    this.state = {
      startDate: moment().subtract(1, "month"),
      startDateError: "",
      endDate: moment(),
      endDateError: "",
    };
  }

  onStartDateChange = (date) => {
    this.setState({
      startDate: date,
      startDateError: date ? "" : "Please select start date",
    });
  };

  onEndDateChange = (date) => {
    this.setState({
      endDate: date,
      endDateError: date ? "" : "Please select end date",
    });
  };

  render() {
    const { endDate, endDateError, startDate, startDateError } = this.state;

    return (
      <React.Fragment>
        <Form.Item
          validateStatus={startDateError ? "error" : ""}
          help={startDateError}
          htmlFor="startDate"
          label="Date From"
        >
          <DatePicker
            id="startDate"
            placeholder="Select Start Date"
            allowClear={false}
            onChange={this.onStartDateChange}
            defaultPickerValue={startDate}
            defaultValue={startDate}
            format="DD-MM-YYYY"
          />
        </Form.Item>
        <Form.Item
          validateStatus={endDateError ? "error" : ""}
          help={endDateError}
          htmlFor="endDate"
          label="To"
        >
          <DatePicker
            id="endDate"
            placeholder="Select End Date"
            allowClear={false}
            onChange={this.onEndDateChange}
            defaultPickerValue={endDate}
            defaultValue={endDate}
            format="DD-MM-YYYY"
          />
        </Form.Item>
      </React.Fragment>
    );
  }
}


export default DatePickers;

Test case using @testing-library/react

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

test("date input value validation", async () => {
  const { container } = render(<DatePickers />);

  const startDateNode = screen.getByPlaceholderText(/select start date/i);
  const endDateNode = screen.getByPlaceholderText(/select end date/i);

  userEvent.click(startDateNode);

  const calendarInput = container.querySelector("ant-calendar-input");
  await userEvent.type(calendarInput, "");

  // assert
  expect(screen.getByText("Please select by start date")).toBeInTheDocument();
});

The test case fails and throws an error

console.error node_modules/jest-environment-jsdom-fourteen/node_modules/jsdom/lib/jsdom/virtual-console.js:29
      Error: Not implemented: navigation (except hash changes)

Versions:

"react" : "16.10.1",
"antd": "3.25.1",
"@testing-library/jest-dom": "5.7.0",
"@testing-library/react": "10.0.4",
"@testing-library/user-event": "10.1.2"

Is there any solution to assert this?

Illustrative answered 22/5, 2020 at 6:58 Comment(0)
G
16

I had the exact same issue with antd v4. The above solution is apt and its working. I had a bit of trouble with multiple date pickers in Form.Item and if we have a custom validation to Datepicker make sure date formats are set properly.

    const startDate = screen.getByTestId("start-date");
    fireEvent.mouseDown(startDate);
    fireEvent.change(startDate, { target: { value: "10-12-2020" } });
    fireEvent.click(document.querySelectorAll(".ant-picker-cell-selected")[0]);

When the date is set make sure the value is correct format, according to your validation and also make sure if not using moment all the date strings are set to whatever library you are using. I was using dayjs and had a moment object messing with one of the date. Which literally took several hours for me to figure out. Hope it helps.

    const endDate = screen.getByTestId("end-date");
    fireEvent.mouseDown(endDate);
    fireEvent.change(endDate, { target: { value: "10-25-2020" } });
    fireEvent.click(document.querySelectorAll(".ant-picker-cell-selected")[1]);

Goofy answered 8/7, 2020 at 22:24 Comment(1)
For me, for whatever reason, using fireEvent.mouseDown() and fireEvent.change() were important. I had been trying to use userEvent.click() and userEvent.type() to no avail. Maybe that's helpful to someone.Nemesis
N
5

This error seems to be unrelated to a date picker test problem : https://github.com/jsdom/jsdom/issues/2112

Anyway, this is how I test Ant Design date pickers with RTL :

Code below works with antd v4

Component

<Form.Item colon={false} label={t('searchFields.creationDate.label')} name="creationDateRange">
  <RangePicker data-testid="creationDate" />
</Form.Item>

Test

[...]
rtl = render(...);
[...]

// grab the input
const creationDateFromInput = rtl.getByTestId('creationDate').querySelectorAll('input')[0];
// mouseDown opens the calendar popup, set focus on the input
fireEvent.mouseDown(creationDateFromInput);
// type date in input
fireEvent.change(creationDateFromInput, { target: { value: '2020-01-15' } });
// now calendar popup is opened and 2020-01-15 cell is selected, just click on it
fireEvent.click(document.querySelector('.ant-picker-cell-selected'));

// now form field is correclty set

I tried to make this more readable and avoid the document.querySelector part which I'm not a big fan of, but I could not find another solution. This could certainly be improved.

Good luck

Nanceynanchang answered 23/5, 2020 at 15:7 Comment(2)
I tried to integrate your solution with DatePicker but it didn't work. Also, instead of .ant-picker-cell-selected, I tried to find .ant-calendar-selected-date.Illustrative
I realize you are using antd 3, I use antd 4, so you may have to adapt the selectors a bit indeed.Nanceynanchang
C
2

Another solution to manipulate the Ant Design Date Picker. This works with Antd 4 and uses React Testing Library's recommended User Event approach (user-event@14).

// set the user
const user = userEvent.setup();

// find date input
const dateInput = await screen.findByTestId('my-custom-test-id');

// select the input to open the date picker
await user.click(dateInput);

// clear previous value. In my case, I had a default value set
await user.clear(dateInput);

// enter new value
await user.type(dateInput, '01/01/2000');

// tab to the next form item to set the value. This tab (or mouse out) is needed to actually set the value
await user.tab();

// assert
expect(dateInput).toHaveValue('01/01/2000');
Cointreau answered 26/5, 2022 at 22:13 Comment(1)
the findByTestId should be used as a last resort when trying to access elements on the page. Using this method means you're leaving random attributes on your elements in production for the sole purpose of testing. There are lots of better ways to go about selecting elements such as getByRole or getByLabelTextSteatopygia
D
0

An example that perfectly works for me:

[email protected]

import React from 'react';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { DatePicker } from 'antd';
import '@testing-library/jest-dom/extend-expect';

describe('RangePicker', () => {
  it('should be possible to set a start and end date', async () => {
    const { RangePicker } = DatePicker;
    render(<RangePicker />);

    const startDateInput = screen.getByPlaceholderText('Start date');
    const endDateInput = screen.getByPlaceholderText('End date');

    await userEvent.type(startDateInput, '1970-01-01');

    await userEvent.type(endDateInput, '1970-01-01');

    expect(startDateInput).toHaveValue('1970-01-01');
    expect(endDateInput).toHaveValue('1970-01-01');
  });
});
Deoxyribose answered 5/7, 2022 at 19:16 Comment(1)
I suspect that inputs are indeed have values, but if you wrap Datepicker with Form.Item, then you won't pass a required validation with thatUraninite
R
0

You can try this solution, this perfectly worked for me

    const startDate = await container.find('.ant-picker-input input').first();
    await startDate.simulate('mousedown');

    await waitFor(async () => {
       await container.update();
       const dateChart = await container.find('.ant-picker-today-btn');
       await dateChart.simulate('click');
    });

Ruhl answered 14/10, 2022 at 14:14 Comment(0)
G
0

After Trying many solution, and trying and error.

I found a solution that work perfectly fine for me.

describe('Date Picker Test', () => {
  let wrapper;
  beforeEach(() => {
    wrapper = mount(
      <Provider store={store}>
        <Router>
          <DatePikerFunction />
        </Router>
      </Provider>,
    );
  });
  it('Date Picker Change', async () => {
    const datePicker = await wrapper.find('.ant-picker-input input').first();
    await datePicker.simulate('mousedown');

    await waitFor(async () => {
      await wrapper.update();
      const today = await wrapper.find('.ant-picker-cell-today'); // put classname that is used to select the perticular date
      const next = today.nextElementSibling;
      await next.click();
    });
  });
});

Here I have find the today's date and then selected tomorrow date. You can find base on class or any thing you want and use it.

Hope It will work for you too. Thanks

Gaffney answered 17/10, 2022 at 7:5 Comment(0)
H
0

Updated answer is painfully simple (and works for date-range picker):

const startDate = screen.getByPlaceholderText('Start date')
userEvent.click(startDate) // select the datepicker
userEvent.keyboard('2023-06-015') // keyboard type in your date
userEvent.tab() // you need 2 tab presses to register
userEvent.tab()
userEvent.keyboard('2023-07-015') // repeat if date-range picker
userEvent.tab()
userEvent.tab()

Nothing else worked for me (antd 4.18.5), but this does. Hopefully that helps future readers

Heather answered 15/6, 2023 at 20:43 Comment(0)
G
0

I am using React testing library. I struggled when I have two datepickers, but finally the code below works.

<AntdDatePicker data-testid="start-date-input" ...>
<AntdDatePicker data-testid="end-date-input" ...>
    const startDateInput = screen.getByTestId('start-date-input')
    const endDateInput = screen.getByTestId('end-date-input')

    await user.click(startDateInput)
    await user.clear(startDateInput)
    await user.type(startDateInput, '2023-07-14')
    await user.tab()

    await user.click(endDateInput)
    await user.clear(endDateInput)
    await user.type(endDateInput, '2023-07-15')
    await user.tab()
Gallows answered 14/7, 2023 at 2:1 Comment(0)
S
0

None of the answers here were working for me in late 2023, so I tried a bunch of variations until I could get the RangePicker to properly update and trigger the onChange handler for my tests. The solution I found is more succinct than the other answers - but it is dependent upon the version of the @testing-library you are using.

I am using antd: ^4.21.7 with @testing-library/user-event: ^14.4.3

      const onChange = jest.fn();
      const user = userEvent.setup();
      await act(async () => {
        render(...);

        const afterInput = screen.getByPlaceholderText("Ordered After");
        const beforeInput = screen.getByPlaceholderText("Ordered Before");

        await user.type(beforeInput, "2020-01-01{tab}");
        expect(beforeInput.value).toEqual("2020-01-01");
      });

      await waitFor(() => {
        expect(onChange).toHaveBeenCalledWith(...);
      });

NOTE: Using {enter} did NOT work for me - it had to be {tab}.

Shrader answered 18/8, 2023 at 15:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.