From 5f53e12d038a97cd0b506cc577944b919083d646 Mon Sep 17 00:00:00 2001 From: Jack Pope Date: Fri, 26 Jan 2024 11:25:52 -0500 Subject: [PATCH] Use createRoot in ReactDOMComponentTree-test --- .../__tests__/ReactDOMComponentTree-test.js | 185 ++++++++---------- ...actDOMLegacyComponentTree-test.internal.js | 58 ++++++ 2 files changed, 145 insertions(+), 98 deletions(-) create mode 100644 packages/react-dom/src/__tests__/ReactDOMLegacyComponentTree-test.internal.js diff --git a/packages/react-dom/src/__tests__/ReactDOMComponentTree-test.js b/packages/react-dom/src/__tests__/ReactDOMComponentTree-test.js index 12d34a34a4ea3..ffaae4f699a60 100644 --- a/packages/react-dom/src/__tests__/ReactDOMComponentTree-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMComponentTree-test.js @@ -11,12 +11,15 @@ describe('ReactDOMComponentTree', () => { let React; - let ReactDOM; + let ReactDOMClient; + let act; let container; beforeEach(() => { React = require('react'); - ReactDOM = require('react-dom'); + ReactDOMClient = require('react-dom/client'); + act = require('internal-test-utils').act; + container = document.createElement('div'); document.body.appendChild(container); }); @@ -26,7 +29,7 @@ describe('ReactDOMComponentTree', () => { container = null; }); - it('finds nodes for instances on events', () => { + it('finds nodes for instances on events', async () => { const mouseOverID = 'mouseOverID'; const clickID = 'clickID'; let currentTargetID = null; @@ -34,17 +37,16 @@ describe('ReactDOMComponentTree', () => { // when an event is dispatched so we can test behavior by invoking // events on elements in the tree and confirming the expected node is // set as the current target - class Component extends React.Component { - handler = e => { + function Component() { + const handler = e => { currentTargetID = e.currentTarget.id; }; - render() { - return ( -
-
-
- ); - } + + return ( +
+
+
+ ); } function simulateMouseEvent(elem, type) { @@ -54,8 +56,10 @@ describe('ReactDOMComponentTree', () => { elem.dispatchEvent(event); } - const component = ; - ReactDOM.render(component, container); + const root = ReactDOMClient.createRoot(container); + await act(() => { + root.render(); + }); expect(currentTargetID).toBe(null); simulateMouseEvent(document.getElementById(mouseOverID), 'mouseover'); expect(currentTargetID).toBe(mouseOverID); @@ -63,25 +67,24 @@ describe('ReactDOMComponentTree', () => { expect(currentTargetID).toBe(clickID); }); - it('finds closest instance for node when an event happens', () => { + it('finds closest instance for node when an event happens', async () => { const nonReactElemID = 'aID'; const innerHTML = {__html: `
`}; const closestInstanceID = 'closestInstance'; let currentTargetID = null; - class ClosestInstance extends React.Component { - _onClick = e => { + function ClosestInstance() { + const onClick = e => { currentTargetID = e.currentTarget.id; }; - render() { - return ( -
- ); - } + + return ( +
+ ); } function simulateClick(elem) { @@ -91,16 +94,22 @@ describe('ReactDOMComponentTree', () => { elem.dispatchEvent(event); } - const component = ; - ReactDOM.render(
{component}
, container); + const root = ReactDOMClient.createRoot(container); + await act(() => { + root.render( +
+ +
, + ); + }); expect(currentTargetID).toBe(null); simulateClick(document.getElementById(nonReactElemID)); expect(currentTargetID).toBe(closestInstanceID); }); - it('updates event handlers from fiber props', () => { + it('updates event handlers from fiber props', async () => { let action = ''; - let instance; + let flip; const handlerA = () => (action = 'A'); const handlerB = () => (action = 'B'); @@ -111,55 +120,56 @@ describe('ReactDOMComponentTree', () => { target.dispatchEvent(event); } - class HandlerFlipper extends React.Component { - state = {flip: false}; - flip() { - this.setState({flip: true}); - } - render() { - return ( -
- ); - } + function HandlerFlipper() { + const [flipVal, setFlipVal] = React.useState(false); + flip = () => setFlipVal(true); + + return
; } - ReactDOM.render( - (instance = n)} />, - container, - ); + const root = ReactDOMClient.createRoot(container); + await act(() => { + root.render(); + }); const node = container.firstChild; - simulateMouseOver(node); + + await act(() => { + simulateMouseOver(node); + }); expect(action).toEqual('A'); action = ''; + // Render with the other event handler. - instance.flip(); - simulateMouseOver(node); + await act(() => { + flip(); + }); + await act(() => { + simulateMouseOver(node); + }); expect(action).toEqual('B'); }); - it('finds a controlled instance from node and gets its current fiber props', () => { + it('finds a controlled instance from node and gets its current fiber props', async () => { + let inputRef; const inputID = 'inputID'; const startValue = undefined; const finishValue = 'finish'; - class Controlled extends React.Component { - state = {value: startValue}; - a = null; - _onChange = e => this.setState({value: e.currentTarget.value}); - render() { - return ( - (this.a = n)} - value={this.state.value} - onChange={this._onChange} - /> - ); - } + function Controlled() { + const [state, setState] = React.useState(startValue); + const ref = React.useRef(); + inputRef = ref; + const onChange = e => setState(e.currentTarget.value); + + return ( + + ); } const setUntrackedInputValue = Object.getOwnPropertyDescriptor( @@ -175,9 +185,17 @@ describe('ReactDOMComponentTree', () => { elem.dispatchEvent(inputEvent); } - const component = ; - const instance = ReactDOM.render(component, container); - expect(() => simulateInput(instance.a, finishValue)).toErrorDev( + const root = ReactDOMClient.createRoot(container); + await act(() => { + root.render(); + }); + + await expect( + async () => + await act(() => { + simulateInput(inputRef.current, finishValue); + }), + ).toErrorDev( 'Warning: A component is changing an uncontrolled input to be controlled. ' + 'This is likely caused by the value changing from undefined to ' + 'a defined value, which should not happen. ' + @@ -186,33 +204,4 @@ describe('ReactDOMComponentTree', () => { 'https://reactjs.org/link/controlled-components', ); }); - - it('finds instance of node that is attempted to be unmounted', () => { - const component =
; - const node = ReactDOM.render(
{component}
, container); - expect(() => ReactDOM.unmountComponentAtNode(node)).toErrorDev( - "unmountComponentAtNode(): The node you're attempting to unmount " + - 'was rendered by React and is not a top-level container. You may ' + - 'have accidentally passed in a React root node instead of its ' + - 'container.', - {withoutStack: true}, - ); - }); - - it('finds instance from node to stop rendering over other react rendered components', () => { - const component = ( -
- Hello -
- ); - const anotherComponent =
; - const instance = ReactDOM.render(component, container); - expect(() => ReactDOM.render(anotherComponent, instance)).toErrorDev( - 'render(...): Replacing React-rendered children with a new root ' + - 'component. If you intended to update the children of this node, ' + - 'you should instead have the existing children update their state ' + - 'and render the new components instead of calling ReactDOM.render.', - {withoutStack: true}, - ); - }); }); diff --git a/packages/react-dom/src/__tests__/ReactDOMLegacyComponentTree-test.internal.js b/packages/react-dom/src/__tests__/ReactDOMLegacyComponentTree-test.internal.js new file mode 100644 index 0000000000000..de9f79ff23529 --- /dev/null +++ b/packages/react-dom/src/__tests__/ReactDOMLegacyComponentTree-test.internal.js @@ -0,0 +1,58 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @emails react-core + */ + +'use strict'; + +describe('ReactDOMComponentTree', () => { + let React; + let ReactDOM; + let container; + + beforeEach(() => { + React = require('react'); + ReactDOM = require('react-dom'); + + container = document.createElement('div'); + document.body.appendChild(container); + }); + + afterEach(() => { + document.body.removeChild(container); + container = null; + }); + + it('finds instance of node that is attempted to be unmounted', () => { + const component =
; + const node = ReactDOM.render(
{component}
, container); + expect(() => ReactDOM.unmountComponentAtNode(node)).toErrorDev( + "unmountComponentAtNode(): The node you're attempting to unmount " + + 'was rendered by React and is not a top-level container. You may ' + + 'have accidentally passed in a React root node instead of its ' + + 'container.', + {withoutStack: true}, + ); + }); + + it('finds instance from node to stop rendering over other react rendered components', () => { + const component = ( +
+ Hello +
+ ); + const anotherComponent =
; + const instance = ReactDOM.render(component, container); + expect(() => ReactDOM.render(anotherComponent, instance)).toErrorDev( + 'render(...): Replacing React-rendered children with a new root ' + + 'component. If you intended to update the children of this node, ' + + 'you should instead have the existing children update their state ' + + 'and render the new components instead of calling ReactDOM.render.', + {withoutStack: true}, + ); + }); +});