main
js 454 lines 13.5 KB
Raw
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 const ReactDOMServerIntegrationUtils = require('./utils/ReactDOMServerIntegrationTestUtils');
13
14 let React;
15 let ReactDOMClient;
16 let ReactDOMServer;
17
18 describe('ReactDOMServerIntegration', () => {
19 function initModules() {
20 // Reset warning cache.
21 jest.resetModules();
22
23 React = require('react');
24 ReactDOMClient = require('react-dom/client');
25 ReactDOMServer = require('react-dom/server');
26
27 // Make them available to the helpers.
28 return {
29 ReactDOMClient,
30 ReactDOMServer,
31 };
32 }
33
34 const {resetModules, expectMarkupMismatch, expectMarkupMatch} =
35 ReactDOMServerIntegrationUtils(initModules);
36 beforeEach(() => {
37 resetModules();
38 });
39
40 describe('reconnecting to server markup', function () {
41 let EmptyComponent;
42 beforeEach(() => {
43 EmptyComponent = class extends React.Component {
44 render() {
45 return null;
46 }
47 };
48 });
49
50 describe('elements', function () {
51 describe('reconnecting different component implementations', function () {
52 let ES6ClassComponent, PureComponent, bareElement;
53 beforeEach(() => {
54 // try each type of component on client and server.
55 ES6ClassComponent = class extends React.Component {
56 render() {
57 return <div id={this.props.id} />;
58 }
59 };
60 PureComponent = props => <div id={props.id} />;
61 bareElement = <div id="foobarbaz" />;
62 });
63
64 it('should reconnect ES6 Class to ES6 Class', () =>
65 expectMarkupMatch(
66 <ES6ClassComponent id="foobarbaz" />,
67 <ES6ClassComponent id="foobarbaz" />,
68 ));
69
70 it('should reconnect Pure Component to ES6 Class', () =>
71 expectMarkupMatch(
72 <ES6ClassComponent id="foobarbaz" />,
73 <PureComponent id="foobarbaz" />,
74 ));
75
76 it('should reconnect Bare Element to ES6 Class', () =>
77 expectMarkupMatch(<ES6ClassComponent id="foobarbaz" />, bareElement));
78
79 it('should reconnect ES6 Class to Pure Component', () =>
80 expectMarkupMatch(
81 <PureComponent id="foobarbaz" />,
82 <ES6ClassComponent id="foobarbaz" />,
83 ));
84
85 it('should reconnect Pure Component to Pure Component', () =>
86 expectMarkupMatch(
87 <PureComponent id="foobarbaz" />,
88 <PureComponent id="foobarbaz" />,
89 ));
90
91 it('should reconnect Bare Element to Pure Component', () =>
92 expectMarkupMatch(<PureComponent id="foobarbaz" />, bareElement));
93
94 it('should reconnect ES6 Class to Bare Element', () =>
95 expectMarkupMatch(bareElement, <ES6ClassComponent id="foobarbaz" />));
96
97 it('should reconnect Pure Component to Bare Element', () =>
98 expectMarkupMatch(bareElement, <PureComponent id="foobarbaz" />));
99
100 it('should reconnect Bare Element to Bare Element', () =>
101 expectMarkupMatch(bareElement, bareElement));
102 });
103
104 it('should error reconnecting different element types', () =>
105 expectMarkupMismatch(<div />, <span />));
106
107 it('should error reconnecting fewer root children', () =>
108 expectMarkupMismatch(<span key="a" />, [
109 <span key="a" />,
110 <span key="b" />,
111 ]));
112
113 it('should error reconnecting missing attributes', () =>
114 expectMarkupMismatch(<div id="foo" />, <div />));
115
116 it('should error reconnecting added attributes', () =>
117 expectMarkupMismatch(<div />, <div id="foo" />));
118
119 it('should error reconnecting different attribute values', () =>
120 expectMarkupMismatch(<div id="foo" />, <div id="bar" />));
121
122 it('should error reconnecting different element types of children', () =>
123 expectMarkupMismatch(
124 <div>
125 <div />
126 </div>,
127 <div suppressHydrationWarning={true}>
128 <span />
129 </div>,
130 ));
131
132 it('can explicitly ignore errors reconnecting missing attributes', () =>
133 expectMarkupMatch(
134 <div id="foo" />,
135 <div suppressHydrationWarning={true} />,
136 ));
137
138 it('can explicitly ignore errors reconnecting added attributes', () =>
139 expectMarkupMatch(
140 <div />,
141 <div id="foo" suppressHydrationWarning={true} />,
142 ));
143
144 it('can explicitly ignore errors reconnecting different attribute values', () =>
145 expectMarkupMatch(
146 <div id="foo" />,
147 <div id="bar" suppressHydrationWarning={true} />,
148 ));
149
150 it('can not deeply ignore errors reconnecting different attribute values', () =>
151 expectMarkupMismatch(
152 <div>
153 <div id="foo" />
154 </div>,
155 <div suppressHydrationWarning={true}>
156 <div id="bar" />
157 </div>,
158 ));
159 });
160
161 describe('inline styles', function () {
162 it('should error reconnecting missing style attribute', () =>
163 expectMarkupMismatch(<div style={{width: '1px'}} />, <div />));
164
165 it('should error reconnecting added style attribute', () =>
166 expectMarkupMismatch(<div />, <div style={{width: '1px'}} />));
167
168 it('should error reconnecting empty style attribute', () =>
169 expectMarkupMismatch(
170 <div style={{width: '1px'}} />,
171 <div style={{}} />,
172 ));
173
174 it('should error reconnecting added style values', () =>
175 expectMarkupMismatch(
176 <div style={{}} />,
177 <div style={{width: '1px'}} />,
178 ));
179
180 it('should error reconnecting different style values', () =>
181 expectMarkupMismatch(
182 <div style={{width: '1px'}} />,
183 <div style={{width: '2px'}} />,
184 ));
185
186 it('should reconnect number and string versions of a number', () =>
187 expectMarkupMatch(
188 <div style={{width: '1px', height: 2}} />,
189 <div style={{width: 1, height: '2px'}} />,
190 ));
191
192 it('should error reconnecting reordered style values', () =>
193 expectMarkupMismatch(
194 <div style={{width: '1px', fontSize: '2px'}} />,
195 <div style={{fontSize: '2px', width: '1px'}} />,
196 ));
197
198 it('can explicitly ignore errors reconnecting added style values', () =>
199 expectMarkupMatch(
200 <div style={{}} />,
201 <div style={{width: '1px'}} suppressHydrationWarning={true} />,
202 ));
203
204 it('can explicitly ignore reconnecting different style values', () =>
205 expectMarkupMatch(
206 <div style={{width: '1px'}} />,
207 <div style={{width: '2px'}} suppressHydrationWarning={true} />,
208 ));
209 });
210
211 describe('text nodes', function () {
212 it('should error reconnecting different text', () =>
213 expectMarkupMismatch(<div>Text</div>, <div>Other Text</div>));
214
215 it('should reconnect a div with a number and string version of number', () =>
216 expectMarkupMatch(<div>{2}</div>, <div>2</div>));
217
218 it('should error reconnecting different numbers', () =>
219 expectMarkupMismatch(<div>{2}</div>, <div>{3}</div>));
220
221 it('should error reconnecting different number from text', () =>
222 expectMarkupMismatch(<div>{2}</div>, <div>3</div>));
223
224 it('should error reconnecting different text in two code blocks', () =>
225 expectMarkupMismatch(
226 <div>
227 {'Text1'}
228 {'Text2'}
229 </div>,
230 <div>
231 {'Text1'}
232 {'Text3'}
233 </div>,
234 ));
235
236 it('can explicitly ignore reconnecting different text', () =>
237 expectMarkupMatch(
238 <div>Text</div>,
239 <div suppressHydrationWarning={true}>Other Text</div>,
240 ));
241
242 it('can explicitly ignore reconnecting different text in two code blocks', () =>
243 expectMarkupMatch(
244 <div suppressHydrationWarning={true}>
245 {'Text1'}
246 {'Text2'}
247 </div>,
248 <div suppressHydrationWarning={true}>
249 {'Text1'}
250 {'Text3'}
251 </div>,
252 ));
253 });
254
255 describe('element trees and children', function () {
256 it('should error reconnecting missing children', () =>
257 expectMarkupMismatch(
258 <div>
259 <div />
260 </div>,
261 <div />,
262 ));
263
264 it('should error reconnecting added children', () =>
265 expectMarkupMismatch(
266 <div />,
267 <div>
268 <div />
269 </div>,
270 ));
271
272 it('should error reconnecting more children', () =>
273 expectMarkupMismatch(
274 <div>
275 <div />
276 </div>,
277 <div>
278 <div />
279 <div />
280 </div>,
281 ));
282
283 it('should error reconnecting fewer children', () =>
284 expectMarkupMismatch(
285 <div>
286 <div />
287 <div />
288 </div>,
289 <div>
290 <div />
291 </div>,
292 ));
293
294 it('should error reconnecting reordered children', () =>
295 expectMarkupMismatch(
296 <div>
297 <div />
298 <span />
299 </div>,
300 <div>
301 <span />
302 <div />
303 </div>,
304 ));
305
306 it('should error reconnecting a div with children separated by whitespace on the client', () =>
307 expectMarkupMismatch(
308 <div id="parent">
309 <div id="child1" />
310 <div id="child2" />
311 </div>,
312 // prettier-ignore
313 <div id="parent"><div id="child1" /> <div id="child2" /></div>,
314 ));
315
316 it('should error reconnecting a div with children separated by different whitespace on the server', () =>
317 expectMarkupMismatch(
318 // prettier-ignore
319 <div id="parent"><div id="child1" /> <div id="child2" /></div>,
320 <div id="parent">
321 <div id="child1" />
322 <div id="child2" />
323 </div>,
324 ));
325
326 it('should error reconnecting a div with children separated by different whitespace', () =>
327 expectMarkupMismatch(
328 <div id="parent">
329 <div id="child1" /> <div id="child2" />
330 </div>,
331 // prettier-ignore
332 <div id="parent"><div id="child1" /> <div id="child2" /></div>,
333 ));
334
335 it('can distinguish an empty component from a dom node', () =>
336 expectMarkupMismatch(
337 <div>
338 <span />
339 </div>,
340 <div>
341 <EmptyComponent />
342 </div>,
343 ));
344
345 it('can distinguish an empty component from an empty text component', () =>
346 expectMarkupMatch(
347 <div>
348 <EmptyComponent />
349 </div>,
350 <div>{''}</div>,
351 ));
352
353 it('can not ignore reconnecting more children', () =>
354 expectMarkupMismatch(
355 <div>
356 <div />
357 </div>,
358 <div suppressHydrationWarning={true}>
359 <div />
360 <div />
361 </div>,
362 ));
363
364 it('can not ignore reconnecting fewer children', () =>
365 expectMarkupMismatch(
366 <div>
367 <div />
368 <div />
369 </div>,
370 <div suppressHydrationWarning={true}>
371 <div />
372 </div>,
373 ));
374
375 it('can not ignore reconnecting reordered children', () =>
376 expectMarkupMismatch(
377 <div suppressHydrationWarning={true}>
378 <div />
379 <span />
380 </div>,
381 <div suppressHydrationWarning={true}>
382 <span />
383 <div />
384 </div>,
385 ));
386
387 it('can not deeply ignore reconnecting reordered children', () =>
388 expectMarkupMismatch(
389 <div suppressHydrationWarning={true}>
390 <div>
391 <div />
392 <span />
393 </div>
394 </div>,
395 <div suppressHydrationWarning={true}>
396 <div>
397 <span />
398 <div />
399 </div>
400 </div>,
401 ));
402 });
403
404 // Markup Mismatches: misc
405 it('should error reconnecting a div with different dangerouslySetInnerHTML', () =>
406 expectMarkupMismatch(
407 <div dangerouslySetInnerHTML={{__html: "<span id='child1'/>"}} />,
408 <div dangerouslySetInnerHTML={{__html: "<span id='child2'/>"}} />,
409 ));
410
411 it('should error reconnecting a div with different text dangerouslySetInnerHTML', () =>
412 expectMarkupMismatch(
413 <div dangerouslySetInnerHTML={{__html: 'foo'}} />,
414 <div dangerouslySetInnerHTML={{__html: 'bar'}} />,
415 ));
416
417 it('should error reconnecting a div with different number dangerouslySetInnerHTML', () =>
418 expectMarkupMismatch(
419 <div dangerouslySetInnerHTML={{__html: 10}} />,
420 <div dangerouslySetInnerHTML={{__html: 20}} />,
421 ));
422
423 it('should error reconnecting a div with different object dangerouslySetInnerHTML', () =>
424 expectMarkupMismatch(
425 <div
426 dangerouslySetInnerHTML={{
427 __html: {
428 toString() {
429 return 'hi';
430 },
431 },
432 }}
433 />,
434 <div
435 dangerouslySetInnerHTML={{
436 __html: {
437 toString() {
438 return 'bye';
439 },
440 },
441 }}
442 />,
443 ));
444
445 it('can explicitly ignore reconnecting a div with different dangerouslySetInnerHTML', () =>
446 expectMarkupMatch(
447 <div dangerouslySetInnerHTML={{__html: "<span id='child1'/>"}} />,
448 <div
449 dangerouslySetInnerHTML={{__html: "<span id='child2'/>"}}
450 suppressHydrationWarning={true}
451 />,
452 ));
453 });
454 });