Neil Girardi
Neil Girardi

Reputation: 4923

React Testing Library: Unable to Select Material UI DatePicker Button

I have a React component which is a thin wrapper around Material UI's KeyboardDatePicker. I am trying to write tests for it using React Testing Library. The issue that I am running into is that I am unable to select the button used to open the calendar. I have tried several different queries. I have also used debug() to confirm that the button is indeed rendering.

Here is the component:

import React from 'react'
import MomentUtils from '@date-io/moment'
import {
  KeyboardDatePicker,
  MuiPickersUtilsProvider,
} from '@material-ui/pickers'
import { StyledDatePickerWrap } from './style'

const DatePicker = (props) => {
  const {
    handleDateChange,
    label,
    selectedDate,
    disableFuture,
    disablePast,
    variant,
  } = props

  return (
    <StyledDatePickerWrap>
      <MuiPickersUtilsProvider utils={MomentUtils}>
        <KeyboardDatePicker
          disableFuture={disableFuture}
          disablePast={disablePast}
          label={label}
          value={selectedDate}
          variant={variant}
          onChange={handleDateChange}
          InputProps={{
            disableUnderline: true,
          }}
          KeyboardButtonProps={{
            'aria-label': 'change date',
            'data-testid': 'changeDate',
          }}
        />
      </MuiPickersUtilsProvider>
    </StyledDatePickerWrap>
  )
}

DatePicker.defaultProps = {
  variant: 'dialog',
  disableFuture: true,
  disablePast: false,
}

export default DatePicker

Here is the test spec:

import React from 'react'
import { render, fireEvent } from 'Utils/test-utils'
import moment from 'moment'
import DatePicker from './DatePicker'

describe('DatePicker', () => {
  const handleDateChange = jest.fn()
  const selectedDate = moment('2020-05-21T19:25:00.000Z')

  const props = {
    handleDateChange,
    selectedDate,
  }

  const { debug, queryByDisplayValue, getByTestId, queryByText } = render(
    <DatePicker {...props} />
  )

  debug()

  it('should render the correct Date', () => {
    expect(queryByDisplayValue(/May 21st/)).not.toBeNull()
  })

  it('should render the "change date" button', () => {
    // why can't I select this button?
    // I've also tried by  getByLabelText('change date') and even container.querySelector('button')
    const button = getByTestId('changeDate')
    fireEvent.click(button)
    expect(queryByText('Thu, May 21')).not.toBeNull()
  })
})

Here is the test error:

 ● DatePicker › should render the "change date" button

    TestingLibraryElementError: Unable to find an element by: [data-testid="changeDate"]

    <body />

      26 |     // why can't I select this button?
      27 |     // I've also tried by  getByLabelText('change date') and even container.querySelector('button')
    > 28 |     const button = getByTestId('changeDate')
         |                    ^
      29 |     fireEvent.click(button)
      30 |     expect(queryByText('Thu, May 21')).not.toBeNull()
      31 |   })

      at Object.getElementError (node_modules/@testing-library/dom/dist/config.js:32:19)
      at node_modules/@testing-library/dom/dist/query-helpers.js:76:38
      at getByTestId (node_modules/@testing-library/dom/dist/query-helpers.js:59:17)
      at Object.<anonymous> (src/Components/CaseManagement/DatePicker/DatePicker.spec.js:28:20)

Here is the console output of the debug call:

 ● Console

    console.log node_modules/@testing-library/react/dist/pure.js:94
      <body>
        <div>
          <div
            class="sc-bdVaJa eYAdNP"
          >
            <div
              class="MuiFormControl-root MuiTextField-root"
            >
              <div
                class="MuiInputBase-root MuiInput-root MuiInputBase-formControl MuiInput-formControl MuiInputBase-adornedEnd"
              >
                <input
                  aria-invalid="false"
                  class="MuiInputBase-input MuiInput-input MuiInputBase-inputAdornedEnd"
                  type="text"
                  value="May 21st"
                />
                <div
                  class="MuiInputAdornment-root MuiInputAdornment-positionEnd"
                >
                  <button
                    aria-label="change date"
                    class="MuiButtonBase-root MuiIconButton-root"
                    data-testid="changeDate"
                    tabindex="0"
                    type="button"
                  >
                    <span
                      class="MuiIconButton-label"
                    >
                      <svg
                        aria-hidden="true"
                        class="MuiSvgIcon-root"
                        focusable="false"
                        viewBox="0 0 24 24"
                      >
                        <path
                          d="M17 12h-5v5h5v-5zM16 1v2H8V1H6v2H5c-1.11 0-1.99.9-1.99 2L3 19c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2h-1V1h-2zm3 18H5V8h14v11z"
                        />
                        <path
                          d="M0 0h24v24H0z"
                          fill="none"
                        />
                      </svg>
                    </span>
                    <span
                      class="MuiTouchRipple-root"
                    />
                  </button>
                </div>
              </div>

            </div>
          </div>
        </div>
      </body>

Note the following in regards to the debug() output:

  1. The button does appear and it has both a data-testid and an aria-label, neither of which have worked as selectors

  2. Note the odd formatting with the line breaks on > and /> in HTML tags. Is that normal? Perhaps this is a symptom of the larger issue?

As always, thank you kindly in advance!

Upvotes: 4

Views: 10657

Answers (2)

Jose A
Jose A

Reputation: 11077

You can try what @aquinq posted. I'm using a similar code to yours, and it's working:

This is in TypeScript: We set the data-testid inside the KeyboardButtonProps as shown below:

<KeyboardDatePicker
          {...props}
          className={cls(styles.root, props.className)}
          label="Fecha"
          // https://github.com/mui-org/material-ui-pickers/issues/1651#issuecomment-614315006
          autoOk={true}
          variant="inline"
          format="dd/MM/yyyy"
          margin="normal"
          id="date-picker-inline"
          value={selectedDate}
          name={name}
          onChange={handleDateChange}
          disablePast={disablePast}
          disableFuture={disableFuture}
          shouldDisableDate={shouldDisableDate}
          KeyboardButtonProps={{
            "aria-label": "change date",
            ...({ ["data-testid"]: "js-datepicker" } as any),
          }}
          InputLabelProps={{ shrink: true }}
        />

This is how I'm testing it:

describe("Tests the Anonymous version of the triage", () => {
  it("Happy Path of the Triage will be completed successfully", async () => {
    // The Triage Component has the <KeyboardDatePicker rendered inside it.
    const renderer = await render(<Triage variant="ANONYMOUS" />);
    const calendar = await waitFor(async () => renderer.getByTestId(FECHA));

    await fireEvent.click(calendar);
    // We get the 22nd day of the month:
    const day = await waitFor(async () => renderer.getByText("22"));

    // I'm wrapping 
    await act(async () => {
      await fireEvent.click(day);
      await fireEvent.click(continueBtn);
    });
  });
});

Upvotes: 2

aquinq
aquinq

Reputation: 1448

Have you tried to wait for the element ?

const button = await waitForElement(() => getByTestId('changeDate'))

Upvotes: 3

Related Questions