diff --git a/packages/react-dom/src/__tests__/InvalidEventListeners-test.js b/packages/react-dom/src/__tests__/InvalidEventListeners-test.js index dc5f0e694fe1..32cecb4640ec 100644 --- a/packages/react-dom/src/__tests__/InvalidEventListeners-test.js +++ b/packages/react-dom/src/__tests__/InvalidEventListeners-test.js @@ -13,32 +13,71 @@ jest.mock('../events/isEventSupported'); describe('InvalidEventListeners', () => { let React; - let ReactTestUtils; + let ReactDOM; + let container; beforeEach(() => { jest.resetModules(); React = require('react'); - ReactTestUtils = require('react-dom/test-utils'); + ReactDOM = require('react-dom'); + + container = document.createElement('div'); + document.body.appendChild(container); + }); + + afterEach(() => { + document.body.removeChild(container); + container = null; }); it('should prevent non-function listeners, at dispatch', () => { let node; expect(() => { - node = ReactTestUtils.renderIntoDocument( -
, - ); + node = ReactDOM.render(
, container); }).toErrorDev( 'Expected `onClick` listener to be a function, instead got a value of `string` type.', ); - expect(() => ReactTestUtils.SimulateNative.click(node)).toThrowError( - 'Expected `onClick` listener to be a function, instead got a value of `string` type.', + + spyOnProd(console, 'error'); + + const uncaughtErrors = []; + function handleWindowError(e) { + uncaughtErrors.push(e.error); + } + window.addEventListener('error', handleWindowError); + try { + node.dispatchEvent( + new MouseEvent('click', { + bubbles: true, + }), + ); + } finally { + window.removeEventListener('error', handleWindowError); + } + expect(uncaughtErrors.length).toBe(1); + expect(uncaughtErrors[0]).toEqual( + expect.objectContaining({ + message: + 'Expected `onClick` listener to be a function, ' + + 'instead got a value of `string` type.', + }), ); + + if (!__DEV__) { + expect(console.error).toHaveBeenCalledTimes(1); + expect(console.error.calls.argsFor(0)[0]).toMatch( + 'Expected `onClick` listener to be a function, ' + + 'instead got a value of `string` type.', + ); + } }); it('should not prevent null listeners, at dispatch', () => { - const node = ReactTestUtils.renderIntoDocument(
); - expect(function() { - ReactTestUtils.SimulateNative.click(node); - }).not.toThrow(); + const node = ReactDOM.render(
, container); + node.dispatchEvent( + new MouseEvent('click', { + bubbles: true, + }), + ); }); }); diff --git a/packages/react-dom/src/__tests__/ReactTestUtils-test.js b/packages/react-dom/src/__tests__/ReactTestUtils-test.js index b0ac326e9573..d926e85641e2 100644 --- a/packages/react-dom/src/__tests__/ReactTestUtils-test.js +++ b/packages/react-dom/src/__tests__/ReactTestUtils-test.js @@ -35,6 +35,22 @@ describe('ReactTestUtils', () => { expect(Object.keys(ReactTestUtils.SimulateNative).sort()).toMatchSnapshot(); }); + it('SimulateNative should warn about deprecation', () => { + const container = document.createElement('div'); + const node = ReactDOM.render(
, container); + expect(() => + ReactTestUtils.SimulateNative.click(node), + ).toWarnDev( + 'ReactTestUtils.SimulateNative is an undocumented API that does not match ' + + 'how the browser dispatches events, and will be removed in a future major ' + + 'version of React. If you rely on it for testing, consider attaching the root ' + + 'DOM container to the document during the test, and then dispatching native browser ' + + 'events by calling `node.dispatchEvent()` on the DOM nodes. Make sure to set ' + + 'the `bubbles` flag to `true` when creating the native browser event.', + {withoutStack: true}, + ); + }); + it('gives Jest mocks a passthrough implementation with mockComponent()', () => { class MockedComponent extends React.Component { render() { diff --git a/packages/react-dom/src/test-utils/ReactTestUtils.js b/packages/react-dom/src/test-utils/ReactTestUtils.js index 2ce109de9f51..aae4703b561b 100644 --- a/packages/react-dom/src/test-utils/ReactTestUtils.js +++ b/packages/react-dom/src/test-utils/ReactTestUtils.js @@ -48,6 +48,7 @@ const [ function Event(suffix) {} let hasWarnedAboutDeprecatedMockComponent = false; +let didWarnSimulateNativeDeprecated = false; /** * @class ReactTestUtils @@ -622,6 +623,20 @@ buildSimulators(); function makeNativeSimulator(eventType, topLevelType) { return function(domComponentOrNode, nativeEventData) { + if (__DEV__) { + if (!didWarnSimulateNativeDeprecated) { + didWarnSimulateNativeDeprecated = true; + console.warn( + 'ReactTestUtils.SimulateNative is an undocumented API that does not match ' + + 'how the browser dispatches events, and will be removed in a future major ' + + 'version of React. If you rely on it for testing, consider attaching the root ' + + 'DOM container to the document during the test, and then dispatching native browser ' + + 'events by calling `node.dispatchEvent()` on the DOM nodes. Make sure to set ' + + 'the `bubbles` flag to `true` when creating the native browser event.', + ); + } + } + const fakeNativeEvent = new Event(eventType); Object.assign(fakeNativeEvent, nativeEventData); if (isDOMComponent(domComponentOrNode)) {