Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions packages/react-dom/src/__tests__/ReactDOM-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -372,4 +372,45 @@ describe('ReactDOM', () => {
delete global.__REACT_DEVTOOLS_GLOBAL_HOOK__;
}
});

it('warns in DEV if jsdom is destroyed by the time setState() is called', () => {
spyOnDev(console, 'error');
class App extends React.Component {
state = {x: 1};
render() {
return <div />;
}
}
const container = document.createElement('div');
const instance = ReactDOM.render(<App />, container);
const documentDescriptor = Object.getOwnPropertyDescriptor(
global,
'document',
);
try {
// Emulate jsdom environment cleanup.
// This is rouhly what happens if the test finished and then

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Roughly

// an asynchronous callback tried to setState() after this.
delete global.document;
const fn = () => instance.setState({x: 2});
if (__DEV__) {
expect(fn).toThrow('document is not defined');
expect(console.error.calls.count()).toBe(1);
expect(console.error.calls.argsFor(0)[0]).toContain(
'The `document` global was defined when React was initialized, but is not ' +
'defined anymore. This can happen in a test environment if a component ' +
'schedules an update from an asynchronous callback, but the test has already ' +
'finished running. To solve this, you can either unmount the component at ' +
'the end of your test (and ensure that any asynchronous operations get ' +
'canceled in `componentWillUnmount`), or you can change the test itself ' +
'to be asynchronous.',
);
} else {
expect(fn).not.toThrow();
}
} finally {
// Don't break other tests.
Object.defineProperty(global, 'document', documentDescriptor);
}
});
});
11 changes: 11 additions & 0 deletions packages/shared/ReactErrorUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
*/

import invariant from 'fbjs/lib/invariant';
import warning from 'fbjs/lib/warning';

const ReactErrorUtils = {
// Used by Fiber to simulate a try-catch.
Expand Down Expand Up @@ -167,6 +168,16 @@ if (__DEV__) {
e,
f,
) {
warning(
typeof document !== 'undefined',
'The `document` global was defined when React was initialized, but is not ' +
'defined anymore. This can happen in a test environment if a component ' +
'schedules an update from an asynchronous callback, but the test has already ' +
'finished running. To solve this, you can either unmount the component at ' +
'the end of your test (and ensure that any asynchronous operations get ' +
'canceled in `componentWillUnmount`), or you can change the test itself ' +
'to be asynchronous.',
);
// Keeps track of whether the user-provided callback threw an error. We
// set this to true at the beginning, then set it to false right after
// calling the function. If the function errors, `didError` will never be
Expand Down