Manoj Kumar Mohan
Manoj Kumar Mohan

Reputation: 51

MUI V5 React unit test not triggering the Date Picker handler

I just upgraded my React project to MUI V5 where the KeyboardDatePicker component has been migrated to DatePicker as per the MUI docs. For some reason the React Library test is not able to trigger the mock handler function of the date picker component.

My Component

import React from "react"
import AdapterDateFns from '@mui/lab/AdapterDateFns';
import moment from "moment"
import LocalizationProvider from '@mui/lab/LocalizationProvider';
import DatePicker from '@mui/lab/DatePicker';
import { TextField } from "@mui/material"
// Required for Material UI datepicker since it is timezone sensitive
export const formatDate = date =>
    date ? new Date(moment(date).year(), moment(date).month(), moment(date).date()) : null

export default function IndependentDateRangePicker({
    handleStartDateChange,
    handleEndDateChange,
    startDateValue,
    endDateValue,
    disableDate
}) {
    return (
        <LocalizationProvider dateAdapter={AdapterDateFns}>
            <DatePicker
                inputFormat="MM/dd/yyyy"
                aria-label="change start date" 
                disabled={disableDate}
                value={formatDate(startDateValue)}
                onChange={handleStartDateChange}
                maxDate={endDateValue ? formatDate(endDateValue) : ""}
                InputProps={{ "data-testid": "start-date-picker" }}
                renderInput={(props) => <TextField  {...props} label="Start Date" variant="standard"/>}
            />
            <DatePicker
                style={{ marginTop: 5 }}
                inputFormat="MM/dd/yyyy"
                aria-label="change start date"
                disabled={disableDate}
                value={formatDate(endDateValue)}
                onChange={handleEndDateChange}
                minDate={startDateValue ? formatDate(startDateValue) : ""}
                InputProps={{ "data-testid": "end-date-picker" }}
                renderInput={(props) => <TextField {...props} variant="standard" label="End Date" />}
            />
        </LocalizationProvider>
    )
}

My React Test file

import React from "react"
import { render, fireEvent } from "@testing-library/react"
import IndependentDateRangePicker, { formatDate } from "../components/IndependentDateRangePicker"

describe("<IndependentDateRangePicker />", () => {
    let c, handleEndDateChangeMock, handleStartDateChangeMock

    beforeEach(() => {
        handleEndDateChangeMock = jest.fn()
        handleStartDateChangeMock = jest.fn()
    })
    


    describe("When no dates are passed as props", () => {
        beforeEach(() => {
            c = render(
                <IndependentDateRangePicker
                    handleStartDateChange={handleStartDateChangeMock}
                    handleEndDateChange={handleEndDateChangeMock}
                    startDateValue={""}
                    endDateValue={""}
                />
            )
        })

        it("should not call handlers when dates are empty", () => {
            fireEvent.change(c.getByTestId("start-date-picker").querySelector('input'), {
                target: { value: "" }
            })
            fireEvent.change(c.getByTestId("end-date-picker").querySelector('input'), {
                target: { value: "" }
            })
            expect(handleStartDateChangeMock).not.toHaveBeenCalled()
            expect(handleEndDateChangeMock).not.toHaveBeenCalled()
        })

        it("should call handler when start date is updated", async () => {
            fireEvent.change(c.getByTestId("start-date-picker").querySelector('input'), {
                target: { value: "01/03/2000" }
            })
            expect(handleStartDateChangeMock).toHaveBeenCalledWith(expect.any(Date), "01/03/2000")
        })

        it("should call handler when end date is updated", () => {
            fireEvent.change(c.getByTestId("end-date-picker").querySelector('input'), {
                target: { value: "01/04/2000" }
            })
            expect(handleEndDateChangeMock).toHaveBeenCalledWith(expect.any(Date), "01/04/2000")
        })
    })

})

Test Error Message

 ● <IndependentDateRangePicker /> › When no dates are passed as props › should call handler when end date is updated

    expect(jest.fn()).toHaveBeenCalledWith(...expected)

    Expected: Any<Date>, "01/04/2000"

    Number of calls: 0

      55 |              target: { value: "01/04/2000" }
      56 |          })
    > 57 |          expect(handleEndDateChangeMock).toHaveBeenCalledWith(expect.any(Date), "01/04/2000")
         |                                          ^
      58 |      })
      59 |  })
      60 |

      at Object.<anonymous> (src/__tests__/IndependentDateRangePicker.test.js:57:36)

you can see there is some problem in triggering the handleEndDateChangeMock function.

Please help me with this. TIA.

Upvotes: 5

Views: 7075

Answers (2)

hannanel gershinsky
hannanel gershinsky

Reputation: 21

I was struggling with a similiar issue, with the newer version of DatePicker from the @mui/x-date-pickers/DatePicker package, i was using the component as following:

<DatePicker
              label={'Date of birth'}
              value={dateOfBirth}
              openTo="year"
              views={['year', 'month', 'day']}
              onChange={setDateOfBirth}
              disableFuture
              desktopModeMediaQuery={theme.breakpoints.up('sm')}
              renderInput={params => (
                <TextField
                  {...params}
                  sx={{
                    width: '100%',
                  }}
                />
              )}
              name="dateOfBirth"
 />

so i made a little change to the previous answer and added this to my test:

beforeAll(() => {
    // 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 => ({
        media: query,
        // this is the media query that @material-ui/pickers uses to determine if a device is a desktop device
        matches: query === '(min-width:600px)',
        onchange: () => {},
        addEventListener: () => {},
        removeEventListener: () => {},
        addListener: () => {},
        removeListener: () => {},
        dispatchEvent: () => false,
      }),
    })
  })

  afterAll(() => {
    delete window.matchMedia
  })

This solved the problem.

You can checkout a full reproducible example here https://codesandbox.io/s/datepicker-usjhlm?file=/src/index.test.js:1847-2634

Upvotes: 1

RafikNayLee
RafikNayLee

Reputation: 101

The problem is the DatePicker is defaulting to mobile mode in tests, you should add the following code before your tests and they will pass:

beforeAll(() => {
  // 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) => ({
      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,
    }),
  });
}

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

Source : https://github.com/mui-org/material-ui-pickers/issues/2073

Upvotes: 10

Related Questions