@samitouri / QOS-React-1 / commits / 313e4d4129

Convert ReactDOMTextComponent to createRoot (#28141)

Sebastian Silbermann committed Jan 29, 2024 at 09:11 UTC 313e4d4129088b3ed99ab64d15dd8b6de175261b
1 file changed +224 -174
packages/react-dom/src/__tests__/ReactDOMTextComponent-test.js
+224 -174
@@ -10,94 +10,99 @@
10 'use strict';
11
12 let React;
13 -let ReactDOM;
13 +let ReactDOMClient;
14 let ReactDOMServer;
15 -
16 -// In standard React, TextComponent keeps track of different Text templates
17 -// using comments. However, in React Fiber, those comments are not outputted due
18 -// to the way Fiber keeps track of the templates.
19 -// This function "Normalizes" childNodes lists to avoid the presence of comments
20 -// and make the child list identical in standard React and Fiber
21 -function filterOutComments(nodeList) {
22 - return [].slice.call(nodeList).filter(node => !(node instanceof Comment));
23 -}
15 +let act;
16
17 describe('ReactDOMTextComponent', () => {
18 beforeEach(() => {
19 React = require('react');
28 - ReactDOM = require('react-dom');
20 + ReactDOMClient = require('react-dom/client');
21 ReactDOMServer = require('react-dom/server');
22 + act = require('internal-test-utils').act;
23 });
24
32 - it('updates a mounted text component in place', () => {
33 - const el = document.createElement('div');
34 - let inst = ReactDOM.render(
35 - <div>
36 - <span />
37 - {'foo'}
38 - {'bar'}
39 - </div>,
40 - el,
41 - );
42 - let nodes = filterOutComments(inst.childNodes);
25 + it('updates a mounted text component in place', async () => {
26 + const container = document.createElement('div');
27 + const root = ReactDOMClient.createRoot(container);
28 + await act(() => {
29 + root.render(
30 + <div>
31 + <span />
32 + {'foo'}
33 + {'bar'}
34 + </div>,
35 + );
36 + });
37 + let inst = container.firstChild;
38 + let nodes = inst.childNodes;
39
40 const foo = nodes[1];
41 const bar = nodes[2];
42 expect(foo.data).toBe('foo');
43 expect(bar.data).toBe('bar');
44
49 - inst = ReactDOM.render(
50 - <div>
51 - <span />
52 - {'baz'}
53 - {'qux'}
54 - </div>,
55 - el,
56 - );
45 + await act(() => {
46 + root.render(
47 + <div>
48 + <span />
49 + {'baz'}
50 + {'qux'}
51 + </div>,
52 + );
53 + });
54 + inst = container.firstChild;
55 // After the update, the text nodes should have stayed in place (as opposed
56 // to getting unmounted and remounted)
59 - nodes = filterOutComments(inst.childNodes);
57 + nodes = inst.childNodes;
58 expect(nodes[1]).toBe(foo);
59 expect(nodes[2]).toBe(bar);
60 expect(foo.data).toBe('baz');
61 expect(bar.data).toBe('qux');
62 });
63
66 - it('can be toggled in and out of the markup', () => {
67 - const el = document.createElement('div');
68 - let inst = ReactDOM.render(
69 - <div>
70 - {'foo'}
71 - <div />
72 - {'bar'}
73 - </div>,
74 - el,
75 - );
64 + it('can be toggled in and out of the markup', async () => {
65 + const container = document.createElement('div');
66 + const root = ReactDOMClient.createRoot(container);
67 + await act(() => {
68 + root.render(
69 + <div>
70 + {'foo'}
71 + <div />
72 + {'bar'}
73 + </div>,
74 + );
75 + });
76 + let inst = container.firstChild;
77
77 - let childNodes = filterOutComments(inst.childNodes);
78 + let childNodes = inst.childNodes;
79 const childDiv = childNodes[1];
80
80 - inst = ReactDOM.render(
81 - <div>
82 - {null}
83 - <div />
84 - {null}
85 - </div>,
86 - el,
87 - );
88 - childNodes = filterOutComments(inst.childNodes);
81 + await act(() => {
82 + root.render(
83 + <div>
84 + {null}
85 + <div />
86 + {null}
87 + </div>,
88 + );
89 + });
90 + inst = container.firstChild;
91 + childNodes = inst.childNodes;
92 expect(childNodes.length).toBe(1);
93 expect(childNodes[0]).toBe(childDiv);
94
92 - inst = ReactDOM.render(
93 - <div>
94 - {'foo'}
95 - <div />
96 - {'bar'}
97 - </div>,
98 - el,
99 - );
100 - childNodes = filterOutComments(inst.childNodes);
95 + await act(() => {
96 + root.render(
97 + <div>
98 + {'foo'}
99 + <div />
100 + {'bar'}
101 + </div>,
102 + );
103 + });
104 + inst = container.firstChild;
105 + childNodes = inst.childNodes;
106 expect(childNodes.length).toBe(3);
107 expect(childNodes[0].data).toBe('foo');
108 expect(childNodes[1]).toBe(childDiv);
@@ -106,101 +111,125 @@ describe('ReactDOMTextComponent', () => {
111
112 /**
113 * The following Node.normalize() tests are intentionally failing.
109 - * See #9836 tracking whether we'll need to fix this or if it's unnecessary.
114 + * See https://github.com/facebook/react/issues/9836 tracking whether we'll need to fix this or if it's unnecessary.
115 */
116 + // @gate TODO
117 + it('can reconcile text merged by Node.normalize() alongside other elements', async () => {
118 + const container = document.createElement('div');
119 + const root = ReactDOMClient.createRoot(container);
120 + await act(() => {
121 + root.render(
122 + <div>
123 + {'foo'}
124 + {'bar'}
125 + {'baz'}
126 + <span />
127 + {'qux'}
128 + </div>,
129 + );
130 + });
131
112 - xit('can reconcile text merged by Node.normalize() alongside other elements', () => {
113 - const el = document.createElement('div');
114 - let inst = ReactDOM.render(
115 - <div>
116 - {'foo'}
117 - {'bar'}
118 - {'baz'}
119 - <span />
120 - {'qux'}
121 - </div>,
122 - el,
123 - );
132 + const inst = container.firstChild;
133
134 inst.normalize();
135
127 - inst = ReactDOM.render(
128 - <div>
129 - {'bar'}
130 - {'baz'}
131 - {'qux'}
132 - <span />
133 - {'foo'}
134 - </div>,
135 - el,
136 - );
136 + await act(() => {
137 + root.render(
138 + <div>
139 + {'bar'}
140 + {'baz'}
141 + {'qux'}
142 + <span />
143 + {'foo'}
144 + </div>,
145 + container,
146 + );
147 + });
148 expect(inst.textContent).toBe('barbazquxfoo');
149 });
150
140 - xit('can reconcile text merged by Node.normalize()', () => {
141 - const el = document.createElement('div');
142 - let inst = ReactDOM.render(
143 - <div>
144 - {'foo'}
145 - {'bar'}
146 - {'baz'}
147 - </div>,
148 - el,
149 - );
151 + // @gate TODO
152 + it('can reconcile text merged by Node.normalize()', async () => {
153 + const container = document.createElement('div');
154 + const root = ReactDOMClient.createRoot(container);
155 + await act(() => {
156 + root.render(
157 + <div>
158 + {'foo'}
159 + {'bar'}
160 + {'baz'}
161 + </div>,
162 + );
163 + });
164 + let inst = container.firstChild;
165
166 inst.normalize();
167
153 - inst = ReactDOM.render(
154 - <div>
155 - {'bar'}
156 - {'baz'}
157 - {'qux'}
158 - </div>,
159 - el,
160 - );
168 + await act(() => {
169 + root.render(
170 + <div>
171 + {'bar'}
172 + {'baz'}
173 + {'qux'}
174 + </div>,
175 + container,
176 + );
177 + });
178 + inst = container.firstChild;
179 expect(inst.textContent).toBe('barbazqux');
180 });
181
164 - it('can reconcile text from pre-rendered markup', () => {
165 - const el = document.createElement('div');
166 - let reactEl = (
182 + it('can reconcile text from pre-rendered markup', async () => {
183 + const container = document.createElement('div');
184 + let children = (
185 <div>
186 {'foo'}
187 {'bar'}
188 {'baz'}
189 </div>
190 );
173 - el.innerHTML = ReactDOMServer.renderToString(reactEl);
191 + container.innerHTML = ReactDOMServer.renderToString(children);
192
175 - ReactDOM.hydrate(reactEl, el);
176 - expect(el.textContent).toBe('foobarbaz');
193 + const root = await act(() => {
194 + return ReactDOMClient.hydrateRoot(container, children);
195 + });
196 + expect(container.textContent).toBe('foobarbaz');
197
178 - ReactDOM.unmountComponentAtNode(el);
198 + await act(() => {
199 + root.unmount();
200 + });
201
180 - reactEl = (
202 + children = (
203 <div>
204 {''}
205 {''}
206 {''}
207 </div>
208 );
187 - el.innerHTML = ReactDOMServer.renderToString(reactEl);
209 + container.innerHTML = ReactDOMServer.renderToString(children);
210
189 - ReactDOM.hydrate(reactEl, el);
190 - expect(el.textContent).toBe('');
211 + await act(() => {
212 + ReactDOMClient.hydrateRoot(container, children);
213 + });
214 + expect(container.textContent).toBe('');
215 });
216
193 - xit('can reconcile text arbitrarily split into multiple nodes', () => {
194 - const el = document.createElement('div');
195 - let inst = ReactDOM.render(
196 - <div>
197 - <span />
198 - {'foobarbaz'}
199 - </div>,
200 - el,
201 - );
217 + // @gate TODO
218 + it('can reconcile text arbitrarily split into multiple nodes', async () => {
219 + const container = document.createElement('div');
220 + const root = ReactDOMClient.createRoot(container);
221
203 - const childNodes = filterOutComments(inst.childNodes);
222 + await act(() => {
223 + root.render(
224 + <div>
225 + <span />
226 + {'foobarbaz'}
227 + </div>,
228 + );
229 + });
230 + let inst = container.firstChild;
231 +
232 + const childNodes = inst.childNodes;
233 const textNode = childNodes[1];
234 textNode.textContent = 'foo';
235 inst.insertBefore(
@@ -212,32 +241,40 @@ describe('ReactDOMTextComponent', () => {
241 childNodes[1].nextSibling,
242 );
243
215 - inst = ReactDOM.render(
216 - <div>
217 - <span />
218 - {'barbazqux'}
219 - </div>,
220 - el,
221 - );
244 + await act(() => {
245 + root.render(
246 + <div>
247 + <span />
248 + {'barbazqux'}
249 + </div>,
250 + container,
251 + );
252 + });
253 + inst = container.firstChild;
254 expect(inst.textContent).toBe('barbazqux');
255 });
256
225 - xit('can reconcile text arbitrarily split into multiple nodes on some substitutions only', () => {
226 - const el = document.createElement('div');
227 - let inst = ReactDOM.render(
228 - <div>
229 - <span />
230 - {'bar'}
231 - <span />
232 - {'foobarbaz'}
233 - {'foo'}
234 - {'barfoo'}
235 - <span />
236 - </div>,
237 - el,
238 - );
257 + // @gate TODO
258 + it('can reconcile text arbitrarily split into multiple nodes on some substitutions only', async () => {
259 + const container = document.createElement('div');
260 + const root = ReactDOMClient.createRoot(container);
261 + await act(() => {
262 + root.render(
263 + <div>
264 + <span />
265 + {'bar'}
266 + <span />
267 + {'foobarbaz'}
268 + {'foo'}
269 + {'barfoo'}
270 + <span />
271 + </div>,
272 + );
273 + });
274
240 - const childNodes = filterOutComments(inst.childNodes);
275 + let inst = container.firstChild;
276 +
277 + const childNodes = inst.childNodes;
278 const textNode = childNodes[3];
279 textNode.textContent = 'foo';
280 inst.insertBefore(
@@ -255,38 +292,48 @@ describe('ReactDOMTextComponent', () => {
292 childNodes[5].nextSibling,
293 );
294
258 - inst = ReactDOM.render(
259 - <div>
260 - <span />
261 - {'baz'}
262 - <span />
263 - {'barbazqux'}
264 - {'bar'}
265 - {'bazbar'}
266 - <span />
267 - </div>,
268 - el,
269 - );
295 + await act(() => {
296 + root.render(
297 + <div>
298 + <span />
299 + {'baz'}
300 + <span />
301 + {'barbazqux'}
302 + {'bar'}
303 + {'bazbar'}
304 + <span />
305 + </div>,
306 + container,
307 + );
308 + });
309 + inst = container.firstChild;
310 expect(inst.textContent).toBe('bazbarbazquxbarbazbar');
311 });
312
273 - xit('can unmount normalized text nodes', () => {
274 - const el = document.createElement('div');
275 - ReactDOM.render(
276 - <div>
277 - {''}
278 - {'foo'}
279 - {'bar'}
280 - </div>,
281 - el,
282 - );
283 - el.normalize();
284 - ReactDOM.render(<div />, el);
285 - expect(el.innerHTML).toBe('<div></div>');
313 + // @gate TODO
314 + it('can unmount normalized text nodes', async () => {
315 + const container = document.createElement('div');
316 + const root = ReactDOMClient.createRoot(container);
317 + await act(() => {
318 + root.render(
319 + <div>
320 + {''}
321 + {'foo'}
322 + {'bar'}
323 + </div>,
324 + );
325 + });
326 +
327 + container.normalize();
328 + await act(() => {
329 + root.render(<div />);
330 + });
331 +
332 + expect(container.innerHTML).toBe('<div></div>');
333 });
334
288 - it('throws for Temporal-like text nodes', () => {
289 - const el = document.createElement('div');
335 + it('throws for Temporal-like text nodes', async () => {
336 + const container = document.createElement('div');
337 class TemporalLike {
338 valueOf() {
339 // Throwing here is the behavior of ECMAScript "Temporal" date/time API.
@@ -297,9 +344,12 @@ describe('ReactDOMTextComponent', () => {
344 return '2020-01-01';
345 }
346 }
300 - expect(() =>
301 - ReactDOM.render(<div>{new TemporalLike()}</div>, el),
302 - ).toThrowError(
347 + const root = ReactDOMClient.createRoot(container);
348 + await expect(
349 + act(() => {
350 + root.render(<div>{new TemporalLike()}</div>);
351 + }),
352 + ).rejects.toThrowError(
353 new Error(
354 'Objects are not valid as a React child (found: object with keys {}).' +
355 ' If you meant to render a collection of children, use an array instead.',