Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jest master doesn't seem to catch errors originating from React #4764

Closed
gaearon opened this issue Oct 26, 2017 · 14 comments · Fixed by #4767
Closed

Jest master doesn't seem to catch errors originating from React #4764

gaearon opened this issue Oct 26, 2017 · 14 comments · Fixed by #4767

Comments

@gaearon
Copy link
Contributor

gaearon commented Oct 26, 2017

As reported in #4761 (comment), I tried 21.3.0-beta.3 on React codebase, and tests like

    test('wat', () => {
      const container = document.createElement('div');
      function App() {
        throw new Error('hi');
      }
      expect(() => {
        ReactDOM.render(<App />, container);
      }).toThrow();
    });

now fail. (The error blows up the test despite bring caught.)

However, they pass with 21.2.1.

To repro, you can take the test above, put it into ReactDOMComponent-test on React master, update Jest packages to 21.3.0-beta.3 and run yarn test ReactDOMComponent-test.

I haven't had time to verify if it's isolated to React Jest setup or not. But if it affects us, could affect someone else (or everyone) too.

The only unusual thing we do in React wrt error handling is that we wrap our code into a browser event so that browsers report unhandled (but caught and rethrown by us) errors as unhandled. This used to work with Jest too. You can disable this logic by changing __DEV__ to false here and then you'll see it goes back to normal (test passes).

So something related to how errors are handled in jsdom browser event loop has changed, and will likely break any tests relying on throwing (and intentionally catching) errors with React 16. Maybe jsdom was updated or something like this?

Note that if this got broken, it will affect anyone using React, not just us.

Here is the diff of lockfile: https://gist.github.com/gaearon/fc30d89381e6b4c6a2494b66f04455cf. Something here causes the issue.

@SimenB
Copy link
Member

SimenB commented Oct 26, 2017

@gaearon there is #4669 which feels like it might be related. Could you try reverting that changeset and see if the error is gone? (On mobile, so can't test it myself ATM)

@gaearon
Copy link
Contributor Author

gaearon commented Oct 26, 2017

Pretty sure this is related. Will test in the office.

@SimenB
Copy link
Member

SimenB commented Oct 26, 2017

Yeah, based on your explanation of how React emits browser errors, it looks like a smoking gun.

We can make the behaviour opt-out to accommodate React's use case, but I don't think it should be reverted.
Normally I guess we'd ask developers to create their own custom test environment, but seeing as it's React's test suite we're breaking, I'm guessing we will adapt the environment which comes out of the box 🙂

@gaearon
Copy link
Contributor Author

gaearon commented Oct 26, 2017

If this is working as designed, what is the suggested way to test catching errors inside React components? It currently seems impossible.

(To be clear, it doesn't affect just React test suite, it affects any project using React.)

@gaearon
Copy link
Contributor Author

gaearon commented Oct 26, 2017

Would it make sense to somehow track if we’re inside toThrow() and in that case, not treat it as an unhandled error?

@cpojer
Copy link
Member

cpojer commented Oct 26, 2017 via email

@SimenB
Copy link
Member

SimenB commented Oct 26, 2017

What about instead of emitting uncaughtException we just rethrow? Not sure if it'll be caught in the try-catch of toThrow or if it happens in a later tick so it's too late

@gaearon
Copy link
Contributor Author

gaearon commented Oct 26, 2017

Here's an integration test for ya. #4767

@gaearon
Copy link
Contributor Author

gaearon commented Oct 26, 2017

What about instead of emitting uncaughtException we just rethrow

Seems like this fails the test from #4669. It never finishes.

@gaearon
Copy link
Contributor Author

gaearon commented Oct 26, 2017

Maybe we could override global.addEventListener and count error listeners. If there is more than zero when the error is caught, assume user is running their own (or React's) error handling mechanism and don't report it as unhandled.

@SimenB
Copy link
Member

SimenB commented Oct 26, 2017

That sounds interesting. Something like

const originalAddListener = this.global.addEventListener;

this.global.addEventListener = (name, ...args) => {
  if (name === 'error') {
    this.global.removeEventListener('error', this.errorEventListener);
  }

  return originalAddListener(name, ...args);
};

?

@SimenB
Copy link
Member

SimenB commented Oct 26, 2017

Seems to work. I'll push it to Dan's branch to trigger CI

@ooade
Copy link

ooade commented Feb 4, 2019

@gaearon something seems strange with this process. Although jest now catches the error because the test passes, yet it still displays them to the console.

jest-error

@github-actions
Copy link

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
Please note this issue tracker is not a help forum. We recommend using StackOverflow or our discord channel for questions.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators May 12, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants