Jest Cannot read property 'createEvent' of null
Asked Answered
I

14

54

I was trying to mock rejected value and got this error. It's weird that this construction works in the case of "success" addUser.mockImplementation(value => jest.fn().mockResolvedValue(value)), but when I'm trying to do the same trick with rejecting, it doesn't work and says 'Cannot read property 'createEvent' of null'

Here is my test case

it('receives invalid value and throws an error', async () => {
  addUser.mockImplementation(() =>
    jest.fn().mockRejectedValue(new Error('Sample error'))
  )

  const enqueueSnackbar = jest.fn()
  useSnackbar.mockReturnValue({ enqueueSnackbar })

  const { emailInput, form, submitButton } = setup()

  await act(async () => {
    fillIn(emailInput, '[email protected]')
  })

  expect(emailInput.value).toBe('[email protected]')
  expect(submitButton).toHaveProperty('disabled', false)

  await act(async () => {
    fireEvent.submit(form)
  })

  expect(enqueueSnackbar).toHaveBeenCalledTimes(1)
  expect(enqueueSnackbar).toHaveBeenCalledWith(`Sample error`, {
    variant: 'error'
})})

Does anyone know how to make it work?

Impower answered 3/3, 2020 at 10:5 Comment(4)
What does the real addUser function look like? It looks like maybe you want adduser.mockImplementation(() => Promise.reject(new Error('Sample error'))) which means "when add user is called, return a rejected promise with the sample error" whereas the current test code means "when add user is called, return a function, that, when it is called, returns a rejected promise with the sample error". I'm guessing that you want the first, but it's hard to know without seeing the addUser function.Proverbs
Or addUser.mockRejectedValue(new Error('Sample error')) which is the same as addUser.mockImplementation(() => Promise.reject(new Error('Sample error')))Proverbs
I'm having the same issue and I think that the error is thrown due the act function is repeated. If I leave my test with just one act the it doesn't crash (the problem is that I need to use act two times). Is it possible?Gorgeous
My issue was using a custom event, i.e. myElement.dispatchEvent(new CustomEvent('click', { bubbles: true })), which I was doing because of some weird code outside my control that ignore click events. Guess that's not gonna work either. :/ just commenting here for the tiny chance that someone else tries the same thing and sees this errorLankford
G
118

This seems to be the #1 question that is found when someone Googles "Cannot read property 'createEvent' of null", so leaving this answer here for those readers:

For me this error came in the midst of a test.

When executing a series of tests, some test or the other used to fail with this error, with no indication of where things went wrong. But the answer turned out to be not the test but the component itself:

It was an unmocked API call.

There was an API call being made in a hook and that hook was used in the component with the failing tests. Obviously Jest cleaned up everything after completing its test, and when the call returned, it found nothing so it errored out.

Mocking the hook solved the issue.

If someone comes across such an error, make sure to mock any asynchronous logic you have, especially if it interacts with the DOM when it returns.

Glyptodont answered 2/2, 2021 at 11:22 Comment(7)
To add to this. I had a test with no asynchronous fetches happening and was still getting this error. My issue was I had a test just barely over the timeout interval of 5s. So between the time I clicked on a button and the event handling cycle finished, I was getting this error, surprisingly consistently in the same spot. The test was then failing on timeout, tearing down the DOM, but then giving this error instead of the timeout error in my test.Darmstadt
In my case I only had forgotten to add some 'await'Indoctrinate
@mrvisser, if only I had noticed your comment days ago! This is the issue we are facing. Had plenty of async, but was confident we had squashed any leaks and it was only happening in our CI suites, which are far less powerful than my development machine. I reduced the CPUs locally and was able to reproduce the error. Increased the jest timeout and it passed in CI. Optimizing the test would be better ofc, but we are testing a multi-step onboarding flow which by nature is long. If I could upvote twice, I would!Jordanna
This is probably long running issue, which can be caused by many circumstances. There is related bug report in jest repo: github.com/facebook/jest/issues/12670 and here practical reproduction test code: github.com/testing-library/react-testing-library/issues/1027Piano
In my case I was using a hook. A waitFor wrapping the hook event did the trickStereopticon
they should've probably implement an error that says "you forgot to add await in front of findBy query"Lamkin
In my case the issue was that the test case in question took over 5000 seconds. Increasing the timeout with jest.setTimeout fixed my issue.Kew
G
37

Similar to what @alexandre_anicio stated. I was getting this error when using the findAllByText query. expect(screen.findAllByText('word'))...

When I switched to the getAllByText the error went away and the test passed. expect(screen.getAllByText('word'))...

If I used expect(await screen.findAllByText('word'))... I noticed the test passed as well.

Digging deeper, this is because findBy tests return a promise so the await is needed. https://testing-library.com/docs/guide-disappearance/#1-using-findby-queries

It would have been nice for the library to throw a better error however.

Gymnasium answered 23/9, 2021 at 16:34 Comment(3)
I normally use getBy but this time I let my IDE autocomplete to: findBy and just went crazy trying to set up testing on this project 😭 I cannot believe the lack of results when searching for this explicit error online.Griffin
Through my research I found: eslint-plugin-testing-library, it can at least warn you about this problem through your linter. Make sure to follow its setup.Griffin
findBy* is always supposed to be used with await because it's used specifically in cases where your component is being rendered in the result of asynchronous code (e.g. an API call). getBy* is used in cases where the component is instantly available. queryBy* is used when it can be null and the test is still required to pass.Glyptodont
B
11

This seems to work for me but I can't explain it. Try removing your act() wrapper, and use await immediately after calling the fireEvent function.

  fireEvent.submit(form);
  await wait();
Beare answered 19/4, 2020 at 12:28 Comment(2)
Would be very pleased if someone could explain this. Also, in the react-native-testing-library docs, they've said that wait is deprecated in favour of waitFor. I'm just gonna waitFor someone to explain why 😄. See what i did thereBeare
Check this out: kentcdodds.com/blog/write-fewer-longer-tests. Kent uses this kind of code in the very end of the article. He says that it's best to put your expect()s in the waitFor's callback (or that's what I think).Glyptodont
A
8

When I encountered this same error message, I discovered I had forgotten to declare my test function as async after I updated the expectation to include await.

Allegedly answered 29/4, 2021 at 9:5 Comment(0)
C
7

waitFor already uses act under the hood so there's no need to use the act blocks there.

I recognize the error you mentioned but the way I replicate it is using waitFor without await, something like this:

it('works', async() => {
  render(<SomeComponent />);
  // (some setup here)

  waitFor(() => { // notice that we are not awaiting this promise
    expect(someChange).toBeInTheDocument();
  });
});

Could you try

it('receives invalid value and throws an error', async () => {
  addUser.mockImplementation(() =>
    jest.fn().mockRejectedValue(new Error('Sample error'))
  )

  const enqueueSnackbar = jest.fn()
  useSnackbar.mockReturnValue({ enqueueSnackbar })

  const { emailInput, form, submitButton } = setup()

  fillIn(emailInput, '[email protected]') // This is using some fireEvent under the hood right?

  await waitFor(() => {
    expect(emailInput.value).toBe('[email protected]')
    expect(submitButton).toHaveProperty('disabled', false)
  });
  fireEvent.submit(form)

  await waitFor(() => {
    expect(enqueueSnackbar).toHaveBeenCalledTimes(1)
    expect(enqueueSnackbar).toHaveBeenCalledWith(`Sample error`, {
      variant: 'error'
    })
  });
})
Carmella answered 9/2, 2021 at 3:18 Comment(4)
I had the same problem, this worked for me but then I realized it was a false positive. Try changing the expectations to something that should fail.Smallclothes
this worked perfectly didnt know waitFor uses act under the hood :)Printable
This was the fix for me ^Ballade
waitFor does not use act under the hood. If it used act under the hood then you would not be able to test intermediate states because act flushes the task queue. Instead they use a hack where set the global that controls whether the act warning will complain to false. Source: I read the source code and worked on the React team when the act feature was rolled out by AndrewKew
R
7

Similar issue and error messages, adding await before userEvent did the trick

Before

userEvent.upload(screen.getByRole('button'), ...)

userEvent.upload(screen.getByLabelText('Upload'), FILE)

After

await userEvent.upload(screen.getByRole('button'), ...)

await userEvent.upload(screen.getByLabelText('Upload'), FILE)

Reunion answered 25/4, 2022 at 12:28 Comment(0)
C
1

I had some problems using mockImplementation(() => Promise) (returning some promise) and the await waitFor(()=> ...) at the same time.

If you are using react-testing-library, you can work around this problem using findBy query, that are a combination of getBy queries and waitFor. The only downside is that you have to find something visual (a text, data-test-id, label, etc...) that can tell you that the mock function have been called. On your code you can try something like this:

it('receives invalid value and throws an error', async () => {
  addUser.mockImplementation(() =>
    jest.fn().mockRejectedValue(new Error('Sample error'))
  )

await screen.findByText('Sample Error message reflected in your component')

... rest of your tests ...

})

Carnet answered 11/3, 2021 at 15:43 Comment(0)
R
1
1. await waitFor(() => expect(history.location.pathname).toBe('/auth'))
2. await waitFor(() => expect(history.location.pathname)).toBe('/auth')

It's about something else but the same error. Spent 2 hours so you don't have to :) The second one with the parenthesis in the wrong place was the culprit

Repulsive answered 6/1, 2022 at 16:39 Comment(0)
G
1

I was getting

/…/node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:3905
      var evt = document.createEvent('Event');
                         ^

TypeError: Cannot read property 'createEvent' of null
    at Object.invokeGuardedCallbackDev (/…/node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:3905:26)
    at invokeGuardedCallback (/…/node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:4056:31)
    at flushPassiveEffectsImpl (/…/node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:23543:11)
    at unstable_runWithPriority (/…/node_modules/.pnpm/[email protected]/node_modules/scheduler/cjs/scheduler.development.js:468:12)
    at runWithPriority$1 (/…/node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:11276:10)
    at flushPassiveEffects (/…/node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom.development.js:23447:14)
    at Object.<anonymous>.flushWork (/…/node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom-test-utils.development.js:992:10)
    at Immediate.<anonymous> (/…/node_modules/.pnpm/[email protected][email protected]/node_modules/react-dom/cjs/react-dom-test-utils.development.js:1003:11)
    at processImmediate (internal/timers.js:461:21)

Tracked down to an import statement.

I was able to "fix" it by changing:

import { AddCircle, RemoveCircle } from '@mui/icons-material';

to

import AddCircle from '@mui/icons-material/AddCircle';
import RemoveCircle from '@mui/icons-material/RemoveCircle';

Crazy.

Gershwin answered 1/4, 2022 at 6:44 Comment(2)
same for me :) I even add this issue to readme of my project :D // bad import { QuestionCircleOutlined, CheckOutlined, CloseOutlined, } from '@ant-design/icons' lang-js // good import CheckOutlined from '@ant-design/icons/CheckOutlined' import CloseOutlined from '@ant-design/icons/CloseOutlined' import QuestionCircleOutlined from '@ant-design/icons/QuestionCircleOutlined' Mankind
Much much more likely is that the error was produced sporadically and making the change above caused the watcher to re-run the tests in a very slightly different state not producing the same effect again. That it only inadvertently suppressed the error temporarily.Amoy
H
0

If the error is because of wrong usage of jest's findBy* instead of async/await you can also return promise:

it('test', () => {
    expect.assertions(1);
    return screen
      .findByTestId('iiError')
      .then(elem =>
        expect(elem).toHaveTextContent(
          "This is error message"
        )
      );
});

Do not forget about expect.assertions and return!

Ref: https://jestjs.io/docs/tutorial-async

Hickok answered 30/11, 2021 at 14:1 Comment(0)
I
0

I had the same issue, the culprit in my case was that the rendered React component was unmounted with .unmount(). A running API call triggered a callback and React tried to update the DOM, that was already unmounted.

Ideologist answered 17/2, 2022 at 8:9 Comment(0)
D
0

Since this is the top result on Google for this issue, I'll add my own answer. For me, this issue was happening on Circle CI when my tests were running. The Circle CI server ran the tests more slowly because it's CPU is limited, so a long-running test with lots of userEvent.types in it was exceeding the default timeout of 5 seconds.

I don't know why it didn't give an error about exceeding the timeout, but this thread helped me track it down.

All I had to do was increase the timeout on the long-running test.

Dowden answered 27/4, 2022 at 14:55 Comment(0)
O
0

Error occurred for me because I had work scheduled from useEffect that resolved after the rest was torn down.

The solution is to await Promise.sleep() after each test.

Ouse answered 20/7, 2022 at 14:52 Comment(0)
A
0

I was facing the same issue It had to something with the async function not completing before the test case completes I solved this using await flushMicrotasksQueue() in my code

Arvonio answered 31/7, 2022 at 10:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.