Hacker0x01 / react-datepicker

A simple and reusable datepicker component for React
https://reactdatepicker.com/
MIT License
7.95k stars 2.24k forks source link

Jest test fails when interacting with Datepicker #3690

Open BasePhil opened 2 years ago

BasePhil commented 2 years ago

Hello everyone! :)

I am currently using the react-datepicker lib in a project and we're now going to write some tests with jest. All tests seems to work fine, at least until we simulate a click in the datepicker. We somehow get different errors depending on what "solution" we currently use to fix the problem...

The test environment is jest-environment-jsdom-fourteen.

1st Error

node:internal/process/promises:246
          triggerUncaughtException(err, true /* fromPromise */);
          ^

[UnhandledPromiseRejection: This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason "TypeError: document.createRange is not a function".] {
  code: 'ERR_UNHANDLED_REJECTION'
}

Fix We added this one above the describe:

document.createRange = () => ({
    setStart: () => {},
    setEnd: () => {},
    commonAncestorContainer: {
      nodeName: 'BODY',
      ownerDocument: document,
    },
  })

taken from this repo and wrapped our events in an

await waitFor(() => {
  event1
  event2
})

Now that one error is gone, the second one kicks in...

2nd Error

Warning: `NaN` is an invalid value for the `top` css style property.
        at div
        at div
        at Mt (apps/react/.yarn/$$virtual/react-datepicker-virtual-4f7b4e4d5c/0/cache/react-datepicker-npm-3.3.0-999e4c88e3-fe268a1c66.zip/node_modules/react-datepicker/dist/index.js:1:42081)
        at div
        at o (apps/react/.yarn/$$virtual/react-datepicker-virtual-4f7b4e4d5c/0/cache/react-datepicker-npm-3.3.0-999e4c88e3-fe268a1c66.zip/node_modules/react-datepicker/dist/index.js:1:42460)
        at onClickOutside (apps/react/.yarn/$$virtual/react-onclickoutside-virtual-ffa8b0e7fc/0/cache/react-onclickoutside-npm-6.9.0-8a676ccbab-3694b8a283.zip/node_modules/react-onclickoutside/dist/react-onclickoutside.cjs.js:168:26)
        at div
        at div
        at n (apps/react/.yarn/$$virtual/react-datepicker-virtual-4f7b4e4d5c/0/cache/react-datepicker-npm-3.3.0-999e4c88e3-fe268a1c66.zip/node_modules/react-datepicker/dist/index.js:1:57902)
        at InnerPopper (apps/react/.yarn/$$virtual/react-popper-virtual-3c7e3b032f/0/cache/react-popper-npm-1.3.7-db7a7cf94e-09ef58054b.zip/node_modules/react-popper/lib/cjs/Popper.js:54:35)
        at Popper (apps/react/.yarn/$$virtual/react-popper-virtual-3c7e3b032f/0/cache/react-popper-npm-1.3.7-db7a7cf94e-09ef58054b.zip/node_modules/react-popper/lib/cjs/Popper.js:203:31)
        at div
        at inputContainer (apps/react/src/components/Form/DateInput/DateInput.jsx:118:29)
        at Manager (apps/react/.yarn/$$virtual/react-popper-virtual-3c7e3b032f/0/cache/react-popper-npm-1.3.7-db7a7cf94e-09ef58054b.zip/node_modules/react-popper/lib/cjs/Manager.js:39:35)
        at o (apps/react/.yarn/$$virtual/react-datepicker-virtual-4f7b4e4d5c/0/cache/react-datepicker-npm-3.3.0-999e4c88e3-fe268a1c66.zip/node_modules/react-datepicker/dist/index.js:1:59542)
        at a (apps/react/.yarn/$$virtual/react-datepicker-virtual-4f7b4e4d5c/0/cache/react-datepicker-npm-3.3.0-999e4c88e3-fe268a1c66.zip/node_modules/react-datepicker/dist/index.js:1:60830)
        at Controller (apps/react/.yarn/$$virtual/react-hook-form-virtual-c28c7cbf4b/0/cache/react-hook-form-npm-6.15.6-bc2006515a-4bddefeb01.zip/node_modules/react-hook-form/src/controller.tsx:13:11)

Fix Also put this one above describe:

Object.defineProperty(window, 'getComputedStyle', {
    value: () => ({
      paddingLeft: 0,
      paddingRight: 0,
      paddingTop: 0,
      paddingBottom: 0,
      marginLeft: 0,
      marginRight: 0,
      marginTop: 0,
      marginBottom: 0,
      borderBottomWidth: 0,
      borderTopWidth: 0,
      borderRightWidth: 0,
      borderLeftWidth: 0,
    }),
  })

taken from this repo

I am totally aware that those issues are not exactly about react-datepicker, but ultimately it all comes down to popper messing things up (at least after Fix2) and i could not get it mocked, even when i installed the peer-dependency from react-datepicker to make it mockable.

Now the errors are gone and things should just be fine. But they still aren't. When i use a selector like screen.getByRole('button', { name: /button text here/i }) things will error again, this time with style.getPropertyValue is not a function

By leaving out the above fixes, the selector with the name option works just fine, but datepicker, more specific popper, doesn't. By keeping them in the test, the datepicker works just fine but the name option breaks things - it still works without using the name option (screen.getByRole('button')). But i need this option pretty badly and i don't want to use the casual container to query for elements, though i could.

This is our DateInput component, controlled by react-hook-form's Controller (which shouldn't bother in this thread):

<DatePicker
                ref={dpRef}
                onCalendarClose={() => handleOnBlur(onBlur)}
                customInput={TriggerElement(shiftGerToLocal(value))}
                selected={shiftGerToLocal(value)}
                onBlur={() => handleOnBlur(onBlur)}
                onChange={(e) => handleOnChange(e, onChange, onBlur)}
                placeholderText={placeholder}
                className={`DateInput Input ${
                  rest?.disabled ? ' disabled' : ''
                }`}
                popperContainer={inputContainer}
                popperClassName={
                  field?.mouseFlowExcluded && 'mouseflow-excluded'
                }
                disabled={rest?.disabled}
                dateFormat={
                  size === 'large' || !isMobile ? 'dd.MM.yyyy' : 'yyyy-MM-dd'
                }
                locale="de"
                shouldCloseOnSelect={false}
                showMonthDropdown
                showYearDropdown
                popperPlacement="left"
                {...(portalId && { portalId: portalId })}
                popperModifiers={{
                  offset: {
                    enabled: true,
                    offset: '-20px, -10px',
                  },
                  preventOverflow: {
                    enabled: true,
                    mainAxis: true,
                    padding: 40,
                  },
                }}
                {...dp_options}
>

Check the screenshot appended to see whats happening after using those 2 fixes described above. The radio is definitely not hidden at all (tested with screen.logTestingPlaygroundURL() and by logging the element itself)

To Reproduce Steps to reproduce the behavior:

  1. Create a component using react-datepicker
  2. Give it some props (i think they shouldn't matter, except for you prevent the datepicker from being opened)
  3. Apply the two "fixes" mentioned above
  4. Try querying an element byRole and set the name option

Expected behavior Datepicker should work in a test without additional configuration or workarounds.

Screenshots Latest error:

image

Desktop (please complete the following information):

Additional context Jest config in package.json

"jest": {
    "verbose": true,
    "roots": [
      "<rootDir>/src"
    ],
    "collectCoverageFrom": [
      "src/**/*.{js,jsx,ts,tsx}",
      "!src/**/*.d.ts"
    ],
    "setupFiles": [
      "react-app-polyfill/jsdom"
    ],
    "setupFilesAfterEnv": [
      "<rootDir>/src/setupTests.js"
    ],
    "testMatch": [
      "<rootDir>/src/**/__tests__/**/*.{js,jsx,ts,tsx}",
      "<rootDir>/src/**/*.{spec,test}.{js,jsx,ts,tsx}"
    ],
    "testEnvironment": "jest-environment-jsdom-fourteen",
    "transform": {
      "^.+\\.(js|jsx|ts|tsx)$": "babel-jest",
      "^.+\\.css$": "<rootDir>/config/jest/cssTransform.js",
      "^(?!.*\\.(js|jsx|ts|tsx|css|json)$)": "<rootDir>/config/jest/fileTransform.js"
    },
    "transformIgnorePatterns": [
      "[/\\\\]node_modules[/\\\\].+\\.(js|jsx|ts|tsx)$",
      "^.+\\.module\\.(css|sass|scss)$"
    ],
    "modulePaths": [],
    "moduleNameMapper": {
      "^react-native$": "react-native-web",
      "^.+\\.module\\.(css|sass|scss)$": "identity-obj-proxy",
      "^config/(.*)": [
        "<rootDir>/config/$1"
      ],
      "^dist/(.*)": [
        "<rootDir>/dist/$1"
      ],
      "^tools/(.*)": [
        "<rootDir>/tools/$1"
      ],
      "^storybook/(.*)": [
        "<rootDir>/.storybook/$1"
      ],
      "^components/(.*)": [
        "<rootDir>/src/components/$1"
      ],
      "^contexts/(.*)": [
        "<rootDir>/src/contexts/$1"
      ],
      "^static/(.*)": [
        "<rootDir>/src/assets/static/$1"
      ],
      "^images/(.*)": [
        "<rootDir>/src/assets/images/$1"
      ],
      "^utils/(.*)": [
        "<rootDir>/src/utils/$1"
      ],
      "^sassUtils/(.*)": [
        "<rootDir>/src/utils/sass/$1"
      ],
      "^source/(.*)": [
        "<rootDir>/src/$1"
      ]
    },
    "moduleFileExtensions": [
      "web.js",
      "js",
      "web.ts",
      "ts",
      "web.tsx",
      "tsx",
      "json",
      "web.jsx",
      "jsx",
      "node"
    ],
    "watchPlugins": [
      "jest-watch-typeahead/filename",
      "jest-watch-typeahead/testname"
    ]
  }

Hope someone's able to help me out on this one, appreciate all answers leading me towards the right direction to handling those errors/problems.

Thank you for the great work and effort you put into the lib! :)

If you miss any additional information on the topic just let me know and i will provide you with it.

Cheers, Phil

github-actions[bot] commented 2 months ago

This issue is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 10 days.