diff --git a/packages/react-dom/src/__tests__/ReactTestUtils-test.js b/packages/react-dom/src/__tests__/ReactTestUtils-test.js index 15de9b708779a..0d2c8574d8df9 100644 --- a/packages/react-dom/src/__tests__/ReactTestUtils-test.js +++ b/packages/react-dom/src/__tests__/ReactTestUtils-test.js @@ -9,8 +9,9 @@ 'use strict'; +import {act} from 'internal-test-utils'; import * as React from 'react'; -import * as ReactDOM from 'react-dom'; +import * as ReactDOMClient from 'react-dom/client'; import * as ReactDOMServer from 'react-dom/server'; import * as ReactTestUtils from 'react-dom/test-utils'; @@ -30,7 +31,7 @@ describe('ReactTestUtils', () => { expect(Object.keys(ReactTestUtils.Simulate).sort()).toMatchSnapshot(); }); - it('gives Jest mocks a passthrough implementation with mockComponent()', () => { + it('gives Jest mocks a passthrough implementation with mockComponent()', async () => { class MockedComponent extends React.Component { render() { throw new Error('Should not get here.'); @@ -51,11 +52,15 @@ describe('ReactTestUtils', () => { ReactTestUtils.mockComponent(MockedComponent); const container = document.createElement('div'); - ReactDOM.render(Hello, container); + const root = ReactDOMClient.createRoot(container); + await act(() => { + root.render(Hello); + }); + expect(container.textContent).toBe('Hello'); }); - it('can scryRenderedComponentsWithType', () => { + it('can scryRenderedComponentsWithType', async () => { class Child extends React.Component { render() { return null; @@ -70,7 +75,12 @@ describe('ReactTestUtils', () => { ); } } - const renderedComponent = ReactTestUtils.renderIntoDocument(); + const container = document.createElement('div'); + const root = ReactDOMClient.createRoot(container); + let renderedComponent; + await act(() => { + root.render( (renderedComponent = current)} />); + }); const scryResults = ReactTestUtils.scryRenderedComponentsWithType( renderedComponent, Child, @@ -78,7 +88,7 @@ describe('ReactTestUtils', () => { expect(scryResults.length).toBe(1); }); - it('can scryRenderedDOMComponentsWithClass with TextComponent', () => { + it('can scryRenderedDOMComponentsWithClass with TextComponent', async () => { class Wrapper extends React.Component { render() { return ( @@ -89,7 +99,12 @@ describe('ReactTestUtils', () => { } } - const renderedComponent = ReactTestUtils.renderIntoDocument(); + const container = document.createElement('div'); + const root = ReactDOMClient.createRoot(container); + let renderedComponent; + await act(() => { + root.render( (renderedComponent = current)} />); + }); const scryResults = ReactTestUtils.scryRenderedDOMComponentsWithClass( renderedComponent, 'NonExistentClass', @@ -97,7 +112,7 @@ describe('ReactTestUtils', () => { expect(scryResults.length).toBe(0); }); - it('can scryRenderedDOMComponentsWithClass with className contains \\n', () => { + it('can scryRenderedDOMComponentsWithClass with className contains \\n', async () => { class Wrapper extends React.Component { render() { return ( @@ -108,7 +123,12 @@ describe('ReactTestUtils', () => { } } - const renderedComponent = ReactTestUtils.renderIntoDocument(); + const container = document.createElement('div'); + const root = ReactDOMClient.createRoot(container); + let renderedComponent; + await act(() => { + root.render( (renderedComponent = current)} />); + }); const scryResults = ReactTestUtils.scryRenderedDOMComponentsWithClass( renderedComponent, 'x', @@ -116,7 +136,7 @@ describe('ReactTestUtils', () => { expect(scryResults.length).toBe(1); }); - it('can scryRenderedDOMComponentsWithClass with multiple classes', () => { + it('can scryRenderedDOMComponentsWithClass with multiple classes', async () => { class Wrapper extends React.Component { render() { return ( @@ -127,7 +147,12 @@ describe('ReactTestUtils', () => { } } - const renderedComponent = ReactTestUtils.renderIntoDocument(); + const container = document.createElement('div'); + const root = ReactDOMClient.createRoot(container); + let renderedComponent; + await act(() => { + root.render( (renderedComponent = current)} />); + }); const scryResults1 = ReactTestUtils.scryRenderedDOMComponentsWithClass( renderedComponent, 'x y', @@ -162,7 +187,7 @@ describe('ReactTestUtils', () => { expect(scryResults5.length).toBe(0); }); - it('traverses children in the correct order', () => { + it('traverses children in the correct order', async () => { class Wrapper extends React.Component { render() { return
{this.props.children}
; @@ -170,25 +195,29 @@ describe('ReactTestUtils', () => { } const container = document.createElement('div'); - ReactDOM.render( - - {null} -
purple
-
, - container, - ); - const tree = ReactDOM.render( - -
orange
-
purple
-
, - container, - ); + const root = ReactDOMClient.createRoot(container); + await act(() => { + root.render( + + {null} +
purple
+
, + ); + }); + let tree; + await act(() => { + root.render( + (tree = current)}> +
orange
+
purple
+
, + ); + }); const log = []; ReactTestUtils.findAllInRenderedTree(tree, function (child) { if (ReactTestUtils.isDOMComponent(child)) { - log.push(ReactDOM.findDOMNode(child).textContent); + log.push(child.textContent); } }); @@ -196,7 +225,7 @@ describe('ReactTestUtils', () => { expect(log).toEqual(['orangepurple', 'orange', 'purple']); }); - it('should support injected wrapper components as DOM components', () => { + it('should support injected wrapper components as DOM components', async () => { const injectedDOMComponents = [ 'button', 'form', @@ -208,13 +237,22 @@ describe('ReactTestUtils', () => { 'textarea', ]; - injectedDOMComponents.forEach(function (type) { - const testComponent = ReactTestUtils.renderIntoDocument( - React.createElement(type), - ); + // eslint-disable-next-line no-for-of-loops/no-for-of-loops + for (const type of injectedDOMComponents) { + const container = document.createElement('div'); + const root = ReactDOMClient.createRoot(container); + let testComponent; + await act(() => { + root.render( + React.createElement(type, { + ref: current => (testComponent = current), + }), + ); + }); + expect(testComponent.tagName).toBe(type.toUpperCase()); expect(ReactTestUtils.isDOMComponent(testComponent)).toBe(true); - }); + } // Full-page components (html, head, body) can't be rendered into a div // directly... @@ -237,7 +275,13 @@ describe('ReactTestUtils', () => { const markup = ReactDOMServer.renderToString(); const testDocument = getTestDocument(markup); - const component = ReactDOM.hydrate(, testDocument); + let component; + await act(() => { + ReactDOMClient.hydrateRoot( + testDocument, + (component = current)} />, + ); + }); expect(component.htmlRef.current.tagName).toBe('HTML'); expect(component.headRef.current.tagName).toBe('HEAD'); @@ -247,7 +291,7 @@ describe('ReactTestUtils', () => { expect(ReactTestUtils.isDOMComponent(component.bodyRef.current)).toBe(true); }); - it('can scry with stateless components involved', () => { + it('can scry with stateless components involved', async () => { const Function = () => (

@@ -265,7 +309,13 @@ describe('ReactTestUtils', () => { } } - const inst = ReactTestUtils.renderIntoDocument(); + const container = document.createElement('div'); + const root = ReactDOMClient.createRoot(container); + let inst; + await act(() => { + root.render( (inst = current)} />); + }); + const hrs = ReactTestUtils.scryRenderedDOMComponentsWithTag(inst, 'hr'); expect(hrs.length).toBe(2); }); @@ -327,7 +377,7 @@ describe('ReactTestUtils', () => { }); describe('Simulate', () => { - it('should change the value of an input field', () => { + it('should change the value of an input field', async () => { const obj = { handler: function (e) { e.persist(); @@ -335,10 +385,11 @@ describe('ReactTestUtils', () => { }; spyOnDevAndProd(obj, 'handler'); const container = document.createElement('div'); - const node = ReactDOM.render( - , - container, - ); + const root = ReactDOMClient.createRoot(container); + await act(() => { + root.render(); + }); + const node = container.firstChild; node.value = 'giraffe'; ReactTestUtils.Simulate.change(node); @@ -348,7 +399,7 @@ describe('ReactTestUtils', () => { ); }); - it('should change the value of an input field in a component', () => { + it('should change the value of an input field in a component', async () => { class SomeComponent extends React.Component { inputRef = React.createRef(); render() { @@ -371,10 +422,16 @@ describe('ReactTestUtils', () => { }; spyOnDevAndProd(obj, 'handler'); const container = document.createElement('div'); - const instance = ReactDOM.render( - , - container, - ); + const root = ReactDOMClient.createRoot(container); + let instance; + await act(() => { + root.render( + (instance = current)} + />, + ); + }); const node = instance.inputRef.current; node.value = 'zebra'; @@ -385,27 +442,33 @@ describe('ReactTestUtils', () => { ); }); - it('should not warn when used with extra properties', () => { + it('should not warn when used with extra properties', async () => { const CLIENT_X = 100; class Component extends React.Component { + childRef = React.createRef(); handleClick = e => { expect(e.clientX).toBe(CLIENT_X); }; render() { - return
; + return
; } } const element = document.createElement('div'); - const instance = ReactDOM.render(, element); - ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(instance), { + const root = ReactDOMClient.createRoot(element); + let instance; + await act(() => { + root.render( (instance = current)} />); + }); + + ReactTestUtils.Simulate.click(instance.childRef.current, { clientX: CLIENT_X, }); }); - it('should set the type of the event', () => { + it('should set the type of the event', async () => { let event; const stub = jest.fn().mockImplementation(e => { e.persist(); @@ -413,8 +476,11 @@ describe('ReactTestUtils', () => { }); const container = document.createElement('div'); - const instance = ReactDOM.render(
, container); - const node = ReactDOM.findDOMNode(instance); + const root = ReactDOMClient.createRoot(container); + let node; + await act(() => { + root.render(
(node = current)} />); + }); ReactTestUtils.Simulate.keyDown(node); @@ -422,7 +488,7 @@ describe('ReactTestUtils', () => { expect(event.nativeEvent.type).toBe('keydown'); }); - it('should work with renderIntoDocument', () => { + it('should work with renderIntoDocument', async () => { const onChange = jest.fn(); class MyComponent extends React.Component { @@ -435,7 +501,13 @@ describe('ReactTestUtils', () => { } } - const instance = ReactTestUtils.renderIntoDocument(); + const container = document.createElement('div'); + const root = ReactDOMClient.createRoot(container); + let instance; + await act(() => { + root.render( (instance = current)} />); + }); + const input = ReactTestUtils.findRenderedDOMComponentWithTag( instance, 'input', @@ -449,7 +521,7 @@ describe('ReactTestUtils', () => { }); }); - it('should call setState callback with no arguments', () => { + it('should call setState callback with no arguments', async () => { let mockArgs; class Component extends React.Component { componentDidMount() { @@ -460,17 +532,28 @@ describe('ReactTestUtils', () => { } } - ReactTestUtils.renderIntoDocument(); + const container = document.createElement('div'); + const root = ReactDOMClient.createRoot(container); + await act(() => { + root.render(); + }); + expect(mockArgs.length).toEqual(0); }); - it('should find rendered component with type in document', () => { + it('should find rendered component with type in document', async () => { class MyComponent extends React.Component { render() { return true; } } - const instance = ReactTestUtils.renderIntoDocument(); + const container = document.createElement('div'); + const root = ReactDOMClient.createRoot(container); + let instance; + await act(() => { + root.render( (instance = current)} />); + }); + const renderedComponentType = ReactTestUtils.findRenderedComponentWithType( instance, MyComponent,