@samitouri / QOS-React-1 / commits / 51c380d6ed

Use createRoot in ReactEmptyComponent-test (#28095)

Jack Pope committed Jan 26, 2024 at 11:37 UTC 51c380d6ed303a54dad7b55258dd7aa8c6bb3fe1
1 file changed +160 -132
packages/react-dom/src/__tests__/ReactEmptyComponent-test.js
+160 -132
@@ -11,10 +11,13 @@
11
12 let React;
13 let ReactDOM;
14 -let ReactTestUtils;
14 +let ReactDOMClient;
15 let TogglingComponent;
16 +let act;
17 +let Scheduler;
18 +let assertLog;
19
17 -let log;
20 +let container;
21
22 describe('ReactEmptyComponent', () => {
23 beforeEach(() => {
@@ -22,20 +25,24 @@ describe('ReactEmptyComponent', () => {
25
26 React = require('react');
27 ReactDOM = require('react-dom');
25 - ReactTestUtils = require('react-dom/test-utils');
28 + ReactDOMClient = require('react-dom/client');
29 + Scheduler = require('scheduler');
30 + const InternalTestUtils = require('internal-test-utils');
31 + act = InternalTestUtils.act;
32 + assertLog = InternalTestUtils.assertLog;
33
27 - log = jest.fn();
34 + container = document.createElement('div');
35
36 TogglingComponent = class extends React.Component {
37 state = {component: this.props.firstComponent};
38
39 componentDidMount() {
33 - log(ReactDOM.findDOMNode(this));
40 + Scheduler.log('mount ' + ReactDOM.findDOMNode(this)?.nodeName);
41 this.setState({component: this.props.secondComponent});
42 }
43
44 componentDidUpdate() {
38 - log(ReactDOM.findDOMNode(this));
45 + Scheduler.log('update ' + ReactDOM.findDOMNode(this)?.nodeName);
46 }
47
48 render() {
@@ -47,40 +54,44 @@ describe('ReactEmptyComponent', () => {
54
55 describe.each([null, undefined])('when %s', nullORUndefined => {
56 it('should not throw when rendering', () => {
50 - class Component extends React.Component {
51 - render() {
52 - return nullORUndefined;
53 - }
57 + function EmptyComponent() {
58 + return nullORUndefined;
59 }
60
56 - expect(function () {
57 - ReactTestUtils.renderIntoDocument(<Component />);
61 + const root = ReactDOMClient.createRoot(container);
62 +
63 + expect(() => {
64 + ReactDOM.flushSync(() => {
65 + root.render(<EmptyComponent />);
66 + });
67 }).not.toThrowError();
68 });
69
61 - it('should not produce child DOM nodes for nullish and false', () => {
62 - class Component1 extends React.Component {
63 - render() {
64 - return nullORUndefined;
65 - }
70 + it('should not produce child DOM nodes for nullish and false', async () => {
71 + function Component1() {
72 + return nullORUndefined;
73 }
74
68 - class Component2 extends React.Component {
69 - render() {
70 - return false;
71 - }
75 + function Component2() {
76 + return false;
77 }
78
79 const container1 = document.createElement('div');
75 - ReactDOM.render(<Component1 />, container1);
80 + const root1 = ReactDOMClient.createRoot(container1);
81 + await act(() => {
82 + root1.render(<Component1 />);
83 + });
84 expect(container1.children.length).toBe(0);
85
86 const container2 = document.createElement('div');
79 - ReactDOM.render(<Component2 />, container2);
87 + const root2 = ReactDOMClient.createRoot(container2);
88 + await act(() => {
89 + root2.render(<Component2 />);
90 + });
91 expect(container2.children.length).toBe(0);
92 });
93
83 - it('should be able to switch between rendering nullish and a normal tag', () => {
94 + it('should be able to switch between rendering nullish and a normal tag', async () => {
95 const instance1 = (
96 <TogglingComponent
97 firstComponent={nullORUndefined}
@@ -94,23 +105,26 @@ describe('ReactEmptyComponent', () => {
105 />
106 );
107
97 - ReactTestUtils.renderIntoDocument(instance1);
98 - ReactTestUtils.renderIntoDocument(instance2);
99 -
100 - expect(log).toHaveBeenCalledTimes(4);
101 - expect(log).toHaveBeenNthCalledWith(1, null);
102 - expect(log).toHaveBeenNthCalledWith(
103 - 2,
104 - expect.objectContaining({tagName: 'DIV'}),
105 - );
106 - expect(log).toHaveBeenNthCalledWith(
107 - 3,
108 - expect.objectContaining({tagName: 'DIV'}),
109 - );
110 - expect(log).toHaveBeenNthCalledWith(4, null);
108 + const container2 = document.createElement('div');
109 + const root1 = ReactDOMClient.createRoot(container);
110 + await act(() => {
111 + root1.render(instance1);
112 + });
113 +
114 + const root2 = ReactDOMClient.createRoot(container2);
115 + await act(() => {
116 + root2.render(instance2);
117 + });
118 +
119 + assertLog([
120 + 'mount undefined',
121 + 'update DIV',
122 + 'mount DIV',
123 + 'update undefined',
124 + ]);
125 });
126
113 - it('should be able to switch in a list of children', () => {
127 + it('should be able to switch in a list of children', async () => {
128 const instance1 = (
129 <TogglingComponent
130 firstComponent={nullORUndefined}
@@ -118,30 +132,25 @@ describe('ReactEmptyComponent', () => {
132 />
133 );
134
121 - ReactTestUtils.renderIntoDocument(
122 - <div>
123 - {instance1}
124 - {instance1}
125 - {instance1}
126 - </div>,
127 - );
128 -
129 - expect(log).toHaveBeenCalledTimes(6);
130 - expect(log).toHaveBeenNthCalledWith(1, null);
131 - expect(log).toHaveBeenNthCalledWith(2, null);
132 - expect(log).toHaveBeenNthCalledWith(3, null);
133 - expect(log).toHaveBeenNthCalledWith(
134 - 4,
135 - expect.objectContaining({tagName: 'DIV'}),
136 - );
137 - expect(log).toHaveBeenNthCalledWith(
138 - 5,
139 - expect.objectContaining({tagName: 'DIV'}),
140 - );
141 - expect(log).toHaveBeenNthCalledWith(
142 - 6,
143 - expect.objectContaining({tagName: 'DIV'}),
144 - );
135 + const root = ReactDOMClient.createRoot(container);
136 + await act(() => {
137 + root.render(
138 + <div>
139 + {instance1}
140 + {instance1}
141 + {instance1}
142 + </div>,
143 + );
144 + });
145 +
146 + assertLog([
147 + 'mount undefined',
148 + 'mount undefined',
149 + 'mount undefined',
150 + 'update DIV',
151 + 'update DIV',
152 + 'update DIV',
153 + ]);
154 });
155
156 it('should distinguish between a script placeholder and an actual script tag', () => {
@@ -158,40 +167,39 @@ describe('ReactEmptyComponent', () => {
167 />
168 );
169
161 - expect(function () {
162 - ReactTestUtils.renderIntoDocument(instance1);
170 + const root1 = ReactDOMClient.createRoot(container);
171 + expect(() => {
172 + ReactDOM.flushSync(() => {
173 + root1.render(instance1);
174 + });
175 }).not.toThrow();
164 - expect(function () {
165 - ReactTestUtils.renderIntoDocument(instance2);
176 +
177 + const container2 = document.createElement('div');
178 + const root2 = ReactDOMClient.createRoot(container2);
179 + expect(() => {
180 + ReactDOM.flushSync(() => {
181 + root2.render(instance2);
182 + });
183 }).not.toThrow();
184
168 - expect(log).toHaveBeenCalledTimes(4);
169 - expect(log).toHaveBeenNthCalledWith(1, null);
170 - expect(log).toHaveBeenNthCalledWith(
171 - 2,
172 - expect.objectContaining({tagName: 'SCRIPT'}),
173 - );
174 - expect(log).toHaveBeenNthCalledWith(
175 - 3,
176 - expect.objectContaining({tagName: 'SCRIPT'}),
177 - );
178 - expect(log).toHaveBeenNthCalledWith(4, null);
185 + assertLog([
186 + 'mount undefined',
187 + 'update SCRIPT',
188 + 'mount SCRIPT',
189 + 'update undefined',
190 + ]);
191 });
192
193 it(
194 'should have findDOMNode return null when multiple layers of composite ' +
195 'components render to the same nullish placeholder',
196 () => {
185 - class GrandChild extends React.Component {
186 - render() {
187 - return nullORUndefined;
188 - }
197 + function GrandChild() {
198 + return nullORUndefined;
199 }
200
191 - class Child extends React.Component {
192 - render() {
193 - return <GrandChild />;
194 - }
201 + function Child() {
202 + return <GrandChild />;
203 }
204
205 const instance1 = (
@@ -201,29 +209,32 @@ describe('ReactEmptyComponent', () => {
209 <TogglingComponent firstComponent={Child} secondComponent={'div'} />
210 );
211
204 - expect(function () {
205 - ReactTestUtils.renderIntoDocument(instance1);
212 + const root1 = ReactDOMClient.createRoot(container);
213 + expect(() => {
214 + ReactDOM.flushSync(() => {
215 + root1.render(instance1);
216 + });
217 }).not.toThrow();
207 - expect(function () {
208 - ReactTestUtils.renderIntoDocument(instance2);
218 +
219 + const container2 = document.createElement('div');
220 + const root2 = ReactDOMClient.createRoot(container2);
221 + expect(() => {
222 + ReactDOM.flushSync(() => {
223 + root2.render(instance2);
224 + });
225 }).not.toThrow();
226
211 - expect(log).toHaveBeenCalledTimes(4);
212 - expect(log).toHaveBeenNthCalledWith(
213 - 1,
214 - expect.objectContaining({tagName: 'DIV'}),
215 - );
216 - expect(log).toHaveBeenNthCalledWith(2, null);
217 - expect(log).toHaveBeenNthCalledWith(3, null);
218 - expect(log).toHaveBeenNthCalledWith(
219 - 4,
220 - expect.objectContaining({tagName: 'DIV'}),
221 - );
227 + assertLog([
228 + 'mount DIV',
229 + 'update undefined',
230 + 'mount undefined',
231 + 'update DIV',
232 + ]);
233 },
234 );
235
225 - it('works when switching components', () => {
226 - let assertions = 0;
236 + it('works when switching components', async () => {
237 + let innerRef;
238
239 class Inner extends React.Component {
240 render() {
@@ -234,44 +245,51 @@ describe('ReactEmptyComponent', () => {
245 // Make sure the DOM node resolves properly even if we're replacing a
246 // `null` component
247 expect(ReactDOM.findDOMNode(this)).not.toBe(null);
237 - assertions++;
248 }
249
250 componentWillUnmount() {
251 // Even though we're getting replaced by `null`, we haven't been
252 // replaced yet!
253 expect(ReactDOM.findDOMNode(this)).not.toBe(null);
244 - assertions++;
254 }
255 }
256
248 - class Wrapper extends React.Component {
249 - render() {
250 - return this.props.showInner ? <Inner /> : nullORUndefined;
251 - }
257 + function Wrapper({showInner}) {
258 + innerRef = React.createRef(null);
259 + return showInner ? <Inner ref={innerRef} /> : nullORUndefined;
260 }
261
262 const el = document.createElement('div');
255 - let component;
263
264 // Render the <Inner /> component...
258 - component = ReactDOM.render(<Wrapper showInner={true} />, el);
259 - expect(ReactDOM.findDOMNode(component)).not.toBe(null);
265 + const root = ReactDOMClient.createRoot(el);
266 + await act(() => {
267 + root.render(<Wrapper showInner={true} />);
268 + });
269 + expect(innerRef.current).not.toBe(null);
270
271 // Switch to null...
262 - component = ReactDOM.render(<Wrapper showInner={false} />, el);
263 - expect(ReactDOM.findDOMNode(component)).toBe(null);
272 + await act(() => {
273 + root.render(<Wrapper showInner={false} />);
274 + });
275 + expect(innerRef.current).toBe(null);
276
277 // ...then switch back.
266 - component = ReactDOM.render(<Wrapper showInner={true} />, el);
267 - expect(ReactDOM.findDOMNode(component)).not.toBe(null);
278 + await act(() => {
279 + root.render(<Wrapper showInner={true} />);
280 + });
281 + expect(innerRef.current).not.toBe(null);
282
269 - expect(assertions).toBe(3);
283 + expect.assertions(6);
284 });
285
272 - it('can render nullish at the top level', () => {
286 + it('can render nullish at the top level', async () => {
287 const div = document.createElement('div');
274 - ReactDOM.render(nullORUndefined, div);
288 + const root = ReactDOMClient.createRoot(div);
289 +
290 + await act(() => {
291 + root.render(nullORUndefined);
292 + });
293 expect(div.innerHTML).toBe('');
294 });
295
@@ -308,26 +326,30 @@ describe('ReactEmptyComponent', () => {
326 }
327 }
328
311 - expect(function () {
312 - ReactTestUtils.renderIntoDocument(<Parent />);
329 + const root = ReactDOMClient.createRoot(container);
330 + expect(() => {
331 + ReactDOM.flushSync(() => {
332 + root.render(<Parent />);
333 + });
334 }).not.toThrow();
335 });
336
316 - it('preserves the dom node during updates', () => {
317 - class Empty extends React.Component {
318 - render() {
319 - return nullORUndefined;
320 - }
337 + it('preserves the dom node during updates', async () => {
338 + function Empty() {
339 + return nullORUndefined;
340 }
341
323 - const container = document.createElement('div');
324 -
325 - ReactDOM.render(<Empty />, container);
342 + const root = ReactDOMClient.createRoot(container);
343 + await act(() => {
344 + root.render(<Empty />);
345 + });
346 const noscript1 = container.firstChild;
347 expect(noscript1).toBe(null);
348
349 // This update shouldn't create a DOM node
330 - ReactDOM.render(<Empty />, container);
350 + await act(() => {
351 + root.render(<Empty />);
352 + });
353 const noscript2 = container.firstChild;
354 expect(noscript2).toBe(null);
355 });
@@ -338,8 +360,11 @@ describe('ReactEmptyComponent', () => {
360 };
361 const EmptyForwardRef = React.forwardRef(Empty);
362
363 + const root = ReactDOMClient.createRoot(container);
364 expect(() => {
342 - ReactTestUtils.renderIntoDocument(<EmptyForwardRef />);
365 + ReactDOM.flushSync(() => {
366 + root.render(<EmptyForwardRef />);
367 + });
368 }).not.toThrowError();
369 });
370
@@ -349,8 +374,11 @@ describe('ReactEmptyComponent', () => {
374 };
375 const EmptyMemo = React.memo(Empty);
376
377 + const root = ReactDOMClient.createRoot(container);
378 expect(() => {
353 - ReactTestUtils.renderIntoDocument(<EmptyMemo />);
379 + ReactDOM.flushSync(() => {
380 + root.render(<EmptyMemo />);
381 + });
382 }).not.toThrowError();
383 });
384 });