From 0a56dc46cebf2f9fb9c998e2ffbb924f9d48ca7d Mon Sep 17 00:00:00 2001 From: Sebastian Silbermann Date: Tue, 20 Feb 2024 16:52:30 +0100 Subject: [PATCH] Remove ReactTestUtils from ReactDOMComponent (#28334) --- .../src/__tests__/ReactDOMComponent-test.js | 993 ++++++++++++------ 1 file changed, 675 insertions(+), 318 deletions(-) diff --git a/packages/react-dom/src/__tests__/ReactDOMComponent-test.js b/packages/react-dom/src/__tests__/ReactDOMComponent-test.js index 006b29fd97ab3..65c6dfa182a3a 100644 --- a/packages/react-dom/src/__tests__/ReactDOMComponent-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMComponent-test.js @@ -11,7 +11,6 @@ describe('ReactDOMComponent', () => { let React; - let ReactTestUtils; let ReactDOM; let ReactDOMClient; let ReactDOMServer; @@ -25,7 +24,6 @@ describe('ReactDOMComponent', () => { ReactDOM = require('react-dom'); ReactDOMClient = require('react-dom/client'); ReactDOMServer = require('react-dom/server'); - ReactTestUtils = require('react-dom/test-utils'); act = require('internal-test-utils').act; }); @@ -162,7 +160,7 @@ describe('ReactDOMComponent', () => { expect(stubStyle.lineHeight).toBe(''); }); - it('should throw when mutating style objects', () => { + it('should throw when mutating style objects', async () => { const style = {border: '1px solid black'}; class App extends React.Component { @@ -173,7 +171,12 @@ describe('ReactDOMComponent', () => { } } - ReactTestUtils.renderIntoDocument(); + const container = document.createElement('div'); + const root = ReactDOMClient.createRoot(container); + await act(() => { + root.render(); + }); + if (__DEV__) { expect(() => (style.position = 'absolute')).toThrow(); } @@ -298,14 +301,18 @@ describe('ReactDOMComponent', () => { expect(container.firstChild.getAttribute('CHILDREN')).toBe('5'); }); - it('should not warn for "0" as a unitless style value', () => { + it('should not warn for "0" as a unitless style value', async () => { class Component extends React.Component { render() { return
; } } - ReactTestUtils.renderIntoDocument(); + const container = document.createElement('div'); + const root = ReactDOMClient.createRoot(container); + await act(() => { + root.render(); + }); }); it('should warn nicely about NaN in style', async () => { @@ -906,28 +913,36 @@ describe('ReactDOMComponent', () => { ]); }); - it('should reject attribute key injection attack on mount for regular DOM', () => { - expect(() => { + it('should reject attribute key injection attack on mount for regular DOM', async () => { + await expect(async () => { for (let i = 0; i < 3; i++) { const container = document.createElement('div'); - ReactDOM.render( - React.createElement( - 'div', - {'blah" onclick="beevil" noise="hi': 'selected'}, - null, - ), - container, - ); + let root = ReactDOMClient.createRoot(container); + await act(() => { + root.render( + React.createElement( + 'div', + {'blah" onclick="beevil" noise="hi': 'selected'}, + null, + ), + ); + }); + expect(container.firstChild.attributes.length).toBe(0); - ReactDOM.unmountComponentAtNode(container); - ReactDOM.render( - React.createElement( - 'div', - {'>
': 'selected'}, - null, - ), - container, - ); + await act(() => { + root.unmount(); + }); + root = ReactDOMClient.createRoot(container); + await act(() => { + root.render( + React.createElement( + 'div', + {'>': 'selected'}, + null, + ), + ); + }); + expect(container.firstChild.attributes.length).toBe(0); } }).toErrorDev([ @@ -936,28 +951,37 @@ describe('ReactDOMComponent', () => { ]); }); - it('should reject attribute key injection attack on mount for custom elements', () => { - expect(() => { + it('should reject attribute key injection attack on mount for custom elements', async () => { + await expect(async () => { for (let i = 0; i < 3; i++) { const container = document.createElement('div'); - ReactDOM.render( - React.createElement( - 'x-foo-component', - {'blah" onclick="beevil" noise="hi': 'selected'}, - null, - ), - container, - ); + let root = ReactDOMClient.createRoot(container); + + await act(() => { + root.render( + React.createElement( + 'x-foo-component', + {'blah" onclick="beevil" noise="hi': 'selected'}, + null, + ), + ); + }); + expect(container.firstChild.attributes.length).toBe(0); - ReactDOM.unmountComponentAtNode(container); - ReactDOM.render( - React.createElement( - 'x-foo-component', - {'>': 'selected'}, - null, - ), - container, - ); + await act(() => { + root.unmount(); + }); + root = ReactDOMClient.createRoot(container); + await act(() => { + root.render( + React.createElement( + 'x-foo-component', + {'>': 'selected'}, + null, + ), + ); + }); + expect(container.firstChild.attributes.length).toBe(0); } }).toErrorDev([ @@ -966,29 +990,36 @@ describe('ReactDOMComponent', () => { ]); }); - it('should reject attribute key injection attack on update for regular DOM', () => { - expect(() => { + it('should reject attribute key injection attack on update for regular DOM', async () => { + await expect(async () => { for (let i = 0; i < 3; i++) { const container = document.createElement('div'); const beforeUpdate = React.createElement('div', {}, null); - ReactDOM.render(beforeUpdate, container); - ReactDOM.render( - React.createElement( - 'div', - {'blah" onclick="beevil" noise="hi': 'selected'}, - null, - ), - container, - ); + const root = ReactDOMClient.createRoot(container); + await act(() => { + root.render(beforeUpdate); + }); + await act(() => { + root.render( + React.createElement( + 'div', + {'blah" onclick="beevil" noise="hi': 'selected'}, + null, + ), + ); + }); + expect(container.firstChild.attributes.length).toBe(0); - ReactDOM.render( - React.createElement( - 'div', - {'>': 'selected'}, - null, - ), - container, - ); + await act(() => { + root.render( + React.createElement( + 'div', + {'>': 'selected'}, + null, + ), + ); + }); + expect(container.firstChild.attributes.length).toBe(0); } }).toErrorDev([ @@ -997,29 +1028,36 @@ describe('ReactDOMComponent', () => { ]); }); - it('should reject attribute key injection attack on update for custom elements', () => { - expect(() => { + it('should reject attribute key injection attack on update for custom elements', async () => { + await expect(async () => { for (let i = 0; i < 3; i++) { const container = document.createElement('div'); const beforeUpdate = React.createElement('x-foo-component', {}, null); - ReactDOM.render(beforeUpdate, container); - ReactDOM.render( - React.createElement( - 'x-foo-component', - {'blah" onclick="beevil" noise="hi': 'selected'}, - null, - ), - container, - ); + const root = ReactDOMClient.createRoot(container); + await act(() => { + root.render(beforeUpdate); + }); + await act(() => { + root.render( + React.createElement( + 'x-foo-component', + {'blah" onclick="beevil" noise="hi': 'selected'}, + null, + ), + ); + }); + expect(container.firstChild.attributes.length).toBe(0); - ReactDOM.render( - React.createElement( - 'x-foo-component', - {'>': 'selected'}, - null, - ), - container, - ); + await act(() => { + root.render( + React.createElement( + 'x-foo-component', + {'>': 'selected'}, + null, + ), + ); + }); + expect(container.firstChild.attributes.length).toBe(0); } }).toErrorDev([ @@ -1615,31 +1653,50 @@ describe('ReactDOMComponent', () => { expect(returnedValue).toContain('
'); }); - it('should warn on upper case HTML tags, not SVG nor custom tags', () => { - ReactTestUtils.renderIntoDocument( - React.createElement('svg', null, React.createElement('PATH')), - ); - ReactTestUtils.renderIntoDocument(React.createElement('CUSTOM-TAG')); + it('should warn on upper case HTML tags, not SVG nor custom tags', async () => { + let container = document.createElement('div'); + let root = ReactDOMClient.createRoot(container); + await act(() => { + root.render( + React.createElement('svg', null, React.createElement('PATH')), + ); + }); - expect(() => - ReactTestUtils.renderIntoDocument(React.createElement('IMG')), - ).toErrorDev( + container = document.createElement('div'); + root = ReactDOMClient.createRoot(container); + await act(() => { + root.render(React.createElement('CUSTOM-TAG')); + }); + + await expect(async () => { + container = document.createElement('div'); + root = ReactDOMClient.createRoot(container); + + await act(() => { + root.render(React.createElement('IMG')); + }); + }).toErrorDev( ' is using incorrect casing. ' + 'Use PascalCase for React components, ' + 'or lowercase for HTML elements.', ); }); - it('should warn on props reserved for future use', () => { - expect(() => - ReactTestUtils.renderIntoDocument(
), - ).toErrorDev( + it('should warn on props reserved for future use', async () => { + await expect(async () => { + const container = document.createElement('div'); + const root = ReactDOMClient.createRoot(container); + + await act(() => { + root.render(
); + }); + }).toErrorDev( 'The `aria` attribute is reserved for future use in React. ' + 'Pass individual `aria-` attributes instead.', ); }); - it('should warn if the tag is unrecognized', () => { + it('should warn if the tag is unrecognized', async () => { let realToString; try { realToString = Object.prototype.toString; @@ -1652,19 +1709,44 @@ describe('ReactDOMComponent', () => { }; Object.prototype.toString = wrappedToString; // eslint-disable-line no-extend-native - expect(() => ReactTestUtils.renderIntoDocument()).toErrorDev( - 'The tag is unrecognized in this browser', - ); + await expect(async () => { + const container = document.createElement('div'); + const root = ReactDOMClient.createRoot(container); + + await act(() => { + root.render(); + }); + }).toErrorDev('The tag is unrecognized in this browser'); // Test deduplication - expect(() => ReactTestUtils.renderIntoDocument()).toErrorDev( - 'The tag is unrecognized in this browser', - ); - ReactTestUtils.renderIntoDocument(); - ReactTestUtils.renderIntoDocument(