@samitouri / QOS-React-2 / commits / 4c41c09ccc

Use createRoot in ReactDOMComponentTree-test (#28112)

Jack Pope committed Jan 26, 2024 at 17:18 UTC 4c41c09ccc28e5cf85cbb706908b740c95ff5a97
2 files changed +145 -98
packages/react-dom/src/__tests__/ReactDOMComponentTree-test.js
+87 -98
@@ -11,12 +11,15 @@
11
12 describe('ReactDOMComponentTree', () => {
13 let React;
14 - let ReactDOM;
14 + let ReactDOMClient;
15 + let act;
16 let container;
17
18 beforeEach(() => {
19 React = require('react');
19 - ReactDOM = require('react-dom');
20 + ReactDOMClient = require('react-dom/client');
21 + act = require('internal-test-utils').act;
22 +
23 container = document.createElement('div');
24 document.body.appendChild(container);
25 });
@@ -26,7 +29,7 @@ describe('ReactDOMComponentTree', () => {
29 container = null;
30 });
31
29 - it('finds nodes for instances on events', () => {
32 + it('finds nodes for instances on events', async () => {
33 const mouseOverID = 'mouseOverID';
34 const clickID = 'clickID';
35 let currentTargetID = null;
@@ -34,17 +37,16 @@ describe('ReactDOMComponentTree', () => {
37 // when an event is dispatched so we can test behavior by invoking
38 // events on elements in the tree and confirming the expected node is
39 // set as the current target
37 - class Component extends React.Component {
38 - handler = e => {
40 + function Component() {
41 + const handler = e => {
42 currentTargetID = e.currentTarget.id;
43 };
41 - render() {
42 - return (
43 - <div id={mouseOverID} onMouseOver={this.handler}>
44 - <div id={clickID} onClick={this.handler} />
45 - </div>
46 - );
47 - }
44 +
45 + return (
46 + <div id={mouseOverID} onMouseOver={handler}>
47 + <div id={clickID} onClick={handler} />
48 + </div>
49 + );
50 }
51
52 function simulateMouseEvent(elem, type) {
@@ -54,8 +56,10 @@ describe('ReactDOMComponentTree', () => {
56 elem.dispatchEvent(event);
57 }
58
57 - const component = <Component />;
58 - ReactDOM.render(component, container);
59 + const root = ReactDOMClient.createRoot(container);
60 + await act(() => {
61 + root.render(<Component />);
62 + });
63 expect(currentTargetID).toBe(null);
64 simulateMouseEvent(document.getElementById(mouseOverID), 'mouseover');
65 expect(currentTargetID).toBe(mouseOverID);
@@ -63,25 +67,24 @@ describe('ReactDOMComponentTree', () => {
67 expect(currentTargetID).toBe(clickID);
68 });
69
66 - it('finds closest instance for node when an event happens', () => {
70 + it('finds closest instance for node when an event happens', async () => {
71 const nonReactElemID = 'aID';
72 const innerHTML = {__html: `<div id="${nonReactElemID}"></div>`};
73 const closestInstanceID = 'closestInstance';
74 let currentTargetID = null;
75
72 - class ClosestInstance extends React.Component {
73 - _onClick = e => {
76 + function ClosestInstance() {
77 + const onClick = e => {
78 currentTargetID = e.currentTarget.id;
79 };
76 - render() {
77 - return (
78 - <div
79 - id={closestInstanceID}
80 - onClick={this._onClick}
81 - dangerouslySetInnerHTML={innerHTML}
82 - />
83 - );
84 - }
80 +
81 + return (
82 + <div
83 + id={closestInstanceID}
84 + onClick={onClick}
85 + dangerouslySetInnerHTML={innerHTML}
86 + />
87 + );
88 }
89
90 function simulateClick(elem) {
@@ -91,16 +94,22 @@ describe('ReactDOMComponentTree', () => {
94 elem.dispatchEvent(event);
95 }
96
94 - const component = <ClosestInstance />;
95 - ReactDOM.render(<section>{component}</section>, container);
97 + const root = ReactDOMClient.createRoot(container);
98 + await act(() => {
99 + root.render(
100 + <section>
101 + <ClosestInstance />
102 + </section>,
103 + );
104 + });
105 expect(currentTargetID).toBe(null);
106 simulateClick(document.getElementById(nonReactElemID));
107 expect(currentTargetID).toBe(closestInstanceID);
108 });
109
101 - it('updates event handlers from fiber props', () => {
110 + it('updates event handlers from fiber props', async () => {
111 let action = '';
103 - let instance;
112 + let flip;
113 const handlerA = () => (action = 'A');
114 const handlerB = () => (action = 'B');
115
@@ -111,55 +120,56 @@ describe('ReactDOMComponentTree', () => {
120 target.dispatchEvent(event);
121 }
122
114 - class HandlerFlipper extends React.Component {
115 - state = {flip: false};
116 - flip() {
117 - this.setState({flip: true});
118 - }
119 - render() {
120 - return (
121 - <div
122 - id="update"
123 - onMouseOver={this.state.flip ? handlerB : handlerA}
124 - />
125 - );
126 - }
123 + function HandlerFlipper() {
124 + const [flipVal, setFlipVal] = React.useState(false);
125 + flip = () => setFlipVal(true);
126 +
127 + return <div id="update" onMouseOver={flipVal ? handlerB : handlerA} />;
128 }
129
129 - ReactDOM.render(
130 - <HandlerFlipper key="1" ref={n => (instance = n)} />,
131 - container,
132 - );
130 + const root = ReactDOMClient.createRoot(container);
131 + await act(() => {
132 + root.render(<HandlerFlipper key="1" />);
133 + });
134 const node = container.firstChild;
134 - simulateMouseOver(node);
135 +
136 + await act(() => {
137 + simulateMouseOver(node);
138 + });
139 expect(action).toEqual('A');
140 action = '';
141 +
142 // Render with the other event handler.
138 - instance.flip();
139 - simulateMouseOver(node);
143 + await act(() => {
144 + flip();
145 + });
146 + await act(() => {
147 + simulateMouseOver(node);
148 + });
149 expect(action).toEqual('B');
150 });
151
143 - it('finds a controlled instance from node and gets its current fiber props', () => {
152 + it('finds a controlled instance from node and gets its current fiber props', async () => {
153 + let inputRef;
154 const inputID = 'inputID';
155 const startValue = undefined;
156 const finishValue = 'finish';
157
148 - class Controlled extends React.Component {
149 - state = {value: startValue};
150 - a = null;
151 - _onChange = e => this.setState({value: e.currentTarget.value});
152 - render() {
153 - return (
154 - <input
155 - id={inputID}
156 - type="text"
157 - ref={n => (this.a = n)}
158 - value={this.state.value}
159 - onChange={this._onChange}
160 - />
161 - );
162 - }
158 + function Controlled() {
159 + const [state, setState] = React.useState(startValue);
160 + const ref = React.useRef();
161 + inputRef = ref;
162 + const onChange = e => setState(e.currentTarget.value);
163 +
164 + return (
165 + <input
166 + id={inputID}
167 + type="text"
168 + ref={ref}
169 + value={state}
170 + onChange={onChange}
171 + />
172 + );
173 }
174
175 const setUntrackedInputValue = Object.getOwnPropertyDescriptor(
@@ -175,9 +185,17 @@ describe('ReactDOMComponentTree', () => {
185 elem.dispatchEvent(inputEvent);
186 }
187
178 - const component = <Controlled />;
179 - const instance = ReactDOM.render(component, container);
180 - expect(() => simulateInput(instance.a, finishValue)).toErrorDev(
188 + const root = ReactDOMClient.createRoot(container);
189 + await act(() => {
190 + root.render(<Controlled />);
191 + });
192 +
193 + await expect(
194 + async () =>
195 + await act(() => {
196 + simulateInput(inputRef.current, finishValue);
197 + }),
198 + ).toErrorDev(
199 'Warning: A component is changing an uncontrolled input to be controlled. ' +
200 'This is likely caused by the value changing from undefined to ' +
201 'a defined value, which should not happen. ' +
@@ -186,33 +204,4 @@ describe('ReactDOMComponentTree', () => {
204 'https://reactjs.org/link/controlled-components',
205 );
206 });
189 -
190 - it('finds instance of node that is attempted to be unmounted', () => {
191 - const component = <div />;
192 - const node = ReactDOM.render(<div>{component}</div>, container);
193 - expect(() => ReactDOM.unmountComponentAtNode(node)).toErrorDev(
194 - "unmountComponentAtNode(): The node you're attempting to unmount " +
195 - 'was rendered by React and is not a top-level container. You may ' +
196 - 'have accidentally passed in a React root node instead of its ' +
197 - 'container.',
198 - {withoutStack: true},
199 - );
200 - });
201 -
202 - it('finds instance from node to stop rendering over other react rendered components', () => {
203 - const component = (
204 - <div>
205 - <span>Hello</span>
206 - </div>
207 - );
208 - const anotherComponent = <div />;
209 - const instance = ReactDOM.render(component, container);
210 - expect(() => ReactDOM.render(anotherComponent, instance)).toErrorDev(
211 - 'render(...): Replacing React-rendered children with a new root ' +
212 - 'component. If you intended to update the children of this node, ' +
213 - 'you should instead have the existing children update their state ' +
214 - 'and render the new components instead of calling ReactDOM.render.',
215 - {withoutStack: true},
216 - );
217 - });
207 });
packages/react-dom/src/__tests__/ReactDOMLegacyComponentTree-test.internal.js new
+58
@@ -0,0 +1,58 @@
1 +/**
2 + * Copyright (c) Meta Platforms, Inc. and affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @emails react-core
8 + */
9 +
10 +'use strict';
11 +
12 +describe('ReactDOMComponentTree', () => {
13 + let React;
14 + let ReactDOM;
15 + let container;
16 +
17 + beforeEach(() => {
18 + React = require('react');
19 + ReactDOM = require('react-dom');
20 +
21 + container = document.createElement('div');
22 + document.body.appendChild(container);
23 + });
24 +
25 + afterEach(() => {
26 + document.body.removeChild(container);
27 + container = null;
28 + });
29 +
30 + it('finds instance of node that is attempted to be unmounted', () => {
31 + const component = <div />;
32 + const node = ReactDOM.render(<div>{component}</div>, container);
33 + expect(() => ReactDOM.unmountComponentAtNode(node)).toErrorDev(
34 + "unmountComponentAtNode(): The node you're attempting to unmount " +
35 + 'was rendered by React and is not a top-level container. You may ' +
36 + 'have accidentally passed in a React root node instead of its ' +
37 + 'container.',
38 + {withoutStack: true},
39 + );
40 + });
41 +
42 + it('finds instance from node to stop rendering over other react rendered components', () => {
43 + const component = (
44 + <div>
45 + <span>Hello</span>
46 + </div>
47 + );
48 + const anotherComponent = <div />;
49 + const instance = ReactDOM.render(component, container);
50 + expect(() => ReactDOM.render(anotherComponent, instance)).toErrorDev(
51 + 'render(...): Replacing React-rendered children with a new root ' +
52 + 'component. If you intended to update the children of this node, ' +
53 + 'you should instead have the existing children update their state ' +
54 + 'and render the new components instead of calling ReactDOM.render.',
55 + {withoutStack: true},
56 + );
57 + });
58 +});