'Jest Cannot read property 'createEvent' of null

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?



Solution 1:[1]

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.

Solution 2:[2]

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.

Solution 3:[3]

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();

Solution 4:[4]

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.

Solution 5:[5]

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'
    })
  });
})

Solution 6:[6]

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 ...

})

Solution 7:[7]

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.

Solution 8:[8]

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)

Solution 9:[9]

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

Solution 10:[10]

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

Solution 11:[11]

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.

Solution 12:[12]

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.