main
js 1,716 lines 63 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 * @jest-environment ./scripts/jest/ReactDOMServerIntegrationEnvironment
8 */
9
10 'use strict';
11
12 let React;
13 let ReactDOMClient;
14 let ReactDOMServer;
15 let act;
16
17 const util = require('util');
18 const realConsoleError = console.error;
19
20 function errorHandler() {
21 // forward to console.error but don't fail the tests
22 }
23
24 describe('ReactDOMServerHydration', () => {
25 let container;
26 let ownerStacks;
27
28 beforeEach(() => {
29 jest.resetModules();
30 React = require('react');
31 ReactDOMClient = require('react-dom/client');
32 ReactDOMServer = require('react-dom/server');
33 act = React.act;
34
35 window.addEventListener('error', errorHandler);
36 ownerStacks = [];
37 console.error = jest.fn(() => {
38 const ownerStack = React.captureOwnerStack();
39 if (typeof ownerStack === 'string') {
40 ownerStacks.push(ownerStack === '' ? ' <empty>' : ownerStack);
41 } else {
42 ownerStacks.push(' ' + String(ownerStack));
43 }
44 });
45 container = document.createElement('div');
46 document.body.appendChild(container);
47 });
48
49 afterEach(() => {
50 jest.restoreAllMocks();
51 window.removeEventListener('error', errorHandler);
52 document.body.removeChild(container);
53 console.error = realConsoleError;
54 });
55
56 function normalizeCodeLocInfo(str) {
57 return typeof str === 'string'
58 ? str.replace(/\n +(?:at|in) ([\S]+)[^\n]*/g, function (m, name) {
59 return '\n in ' + name + ' (at **)';
60 })
61 : str;
62 }
63
64 function formatMessage(args, index) {
65 const ownerStack = ownerStacks[index];
66
67 if (ownerStack === undefined) {
68 throw new Error(
69 'Expected an owner stack for message ' +
70 index +
71 ':\n' +
72 util.format(...args),
73 );
74 }
75
76 const [format, ...rest] = args;
77 if (format instanceof Error) {
78 if (format.cause instanceof Error) {
79 return (
80 'Caught [' +
81 format.message +
82 ']\n Cause [' +
83 format.cause.message +
84 ']\n Owner Stack:' +
85 normalizeCodeLocInfo(ownerStack)
86 );
87 }
88 return (
89 'Caught [' +
90 format.message +
91 ']\n Owner Stack:' +
92 normalizeCodeLocInfo(ownerStack)
93 );
94 }
95 rest[rest.length - 1] = normalizeCodeLocInfo(rest[rest.length - 1]);
96 return (
97 util.format(format, ...rest) +
98 '\n Owner Stack:' +
99 normalizeCodeLocInfo(ownerStack)
100 );
101 }
102
103 function formatConsoleErrors() {
104 return console.error.mock.calls.map(formatMessage).filter(Boolean);
105 }
106
107 function testMismatch(Mismatch) {
108 const htmlString = ReactDOMServer.renderToString(
109 <Mismatch isClient={false} />,
110 );
111 container.innerHTML = htmlString;
112 act(() => {
113 ReactDOMClient.hydrateRoot(container, <Mismatch isClient={true} />);
114 });
115 return formatConsoleErrors();
116 }
117
118 describe('text mismatch', () => {
119 // @gate __DEV__
120 it('warns when client and server render different text', () => {
121 function Mismatch({isClient}) {
122 return (
123 <div className="parent">
124 <main className="child">{isClient ? 'client' : 'server'}</main>
125 </div>
126 );
127 }
128 expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
129 [
130 "Caught [Hydration failed because the server rendered text didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used:
131
132 - A server/client branch \`if (typeof window !== 'undefined')\`.
133 - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called.
134 - Date formatting in a user's locale which doesn't match the server.
135 - External changing data without sending a snapshot of it along with the HTML.
136 - Invalid HTML tag nesting.
137
138 It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
139
140 https://react.dev/link/hydration-mismatch
141
142 <Mismatch isClient={true}>
143 <div className="parent">
144 <main className="child">
145 + client
146 - server
147 ]
148 Owner Stack:
149 in main (at **)
150 in Mismatch (at **)",
151 ]
152 `);
153 });
154
155 // @gate __DEV__
156 it('warns when escaping on a checksum mismatch', () => {
157 function Mismatch({isClient}) {
158 if (isClient) {
159 return (
160 <div>This markup contains an nbsp entity: &nbsp; client text</div>
161 );
162 }
163 return (
164 <div>This markup contains an nbsp entity: &nbsp; server text</div>
165 );
166 }
167
168 /* eslint-disable no-irregular-whitespace */
169 expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
170 [
171 "Caught [Hydration failed because the server rendered text didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used:
172
173 - A server/client branch \`if (typeof window !== 'undefined')\`.
174 - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called.
175 - Date formatting in a user's locale which doesn't match the server.
176 - External changing data without sending a snapshot of it along with the HTML.
177 - Invalid HTML tag nesting.
178
179 It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
180
181 https://react.dev/link/hydration-mismatch
182
183 <Mismatch isClient={true}>
184 <div>
185 + This markup contains an nbsp entity:   client text
186 - This markup contains an nbsp entity:   server text
187 ]
188 Owner Stack:
189 in div (at **)
190 in Mismatch (at **)",
191 ]
192 `);
193 /* eslint-enable no-irregular-whitespace */
194 });
195
196 // @gate __DEV__
197 it('warns when client and server render different html', () => {
198 function Mismatch({isClient}) {
199 return (
200 <div className="parent">
201 <main
202 className="child"
203 dangerouslySetInnerHTML={{
204 __html: isClient
205 ? '<span>client</span>'
206 : '<span>server</span>',
207 }}
208 />
209 </div>
210 );
211 }
212 expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
213 [
214 "A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used:
215
216 - A server/client branch \`if (typeof window !== 'undefined')\`.
217 - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called.
218 - Date formatting in a user's locale which doesn't match the server.
219 - External changing data without sending a snapshot of it along with the HTML.
220 - Invalid HTML tag nesting.
221
222 It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
223
224 https://react.dev/link/hydration-mismatch
225
226 <Mismatch isClient={true}>
227 <div className="parent">
228 <main
229 className="child"
230 dangerouslySetInnerHTML={{
231 + __html: "<span>client</span>"
232 - __html: "<span>server</span>"
233 }}
234 >
235
236 Owner Stack:
237 in main (at **)
238 in Mismatch (at **)",
239 ]
240 `);
241 });
242 });
243
244 describe('attribute mismatch', () => {
245 // @gate __DEV__
246 it('warns when client and server render different attributes', () => {
247 function Mismatch({isClient}) {
248 return (
249 <div className="parent">
250 <main
251 className={isClient ? 'child client' : 'child server'}
252 dir={isClient ? 'ltr' : 'rtl'}
253 />
254 </div>
255 );
256 }
257 expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
258 [
259 "A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used:
260
261 - A server/client branch \`if (typeof window !== 'undefined')\`.
262 - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called.
263 - Date formatting in a user's locale which doesn't match the server.
264 - External changing data without sending a snapshot of it along with the HTML.
265 - Invalid HTML tag nesting.
266
267 It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
268
269 https://react.dev/link/hydration-mismatch
270
271 <Mismatch isClient={true}>
272 <div className="parent">
273 <main
274 + className="child client"
275 - className="child server"
276 + dir="ltr"
277 - dir="rtl"
278 >
279
280 Owner Stack:
281 in main (at **)
282 in Mismatch (at **)",
283 ]
284 `);
285 });
286
287 // @gate __DEV__
288 it('warns when client renders extra attributes', () => {
289 function Mismatch({isClient}) {
290 return (
291 <div className="parent">
292 <main
293 className="child"
294 tabIndex={isClient ? 1 : null}
295 dir={isClient ? 'ltr' : null}
296 />
297 </div>
298 );
299 }
300 expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
301 [
302 "A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used:
303
304 - A server/client branch \`if (typeof window !== 'undefined')\`.
305 - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called.
306 - Date formatting in a user's locale which doesn't match the server.
307 - External changing data without sending a snapshot of it along with the HTML.
308 - Invalid HTML tag nesting.
309
310 It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
311
312 https://react.dev/link/hydration-mismatch
313
314 <Mismatch isClient={true}>
315 <div className="parent">
316 <main
317 className="child"
318 + tabIndex={1}
319 - tabIndex={null}
320 + dir="ltr"
321 - dir={null}
322 >
323
324 Owner Stack:
325 in main (at **)
326 in Mismatch (at **)",
327 ]
328 `);
329 });
330
331 // @gate __DEV__
332 it('warns when server renders extra attributes', () => {
333 function Mismatch({isClient}) {
334 return (
335 <div className="parent">
336 <main
337 className="child"
338 tabIndex={isClient ? null : 1}
339 dir={isClient ? null : 'rtl'}
340 />
341 </div>
342 );
343 }
344 expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
345 [
346 "A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used:
347
348 - A server/client branch \`if (typeof window !== 'undefined')\`.
349 - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called.
350 - Date formatting in a user's locale which doesn't match the server.
351 - External changing data without sending a snapshot of it along with the HTML.
352 - Invalid HTML tag nesting.
353
354 It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
355
356 https://react.dev/link/hydration-mismatch
357
358 <Mismatch isClient={true}>
359 <div className="parent">
360 <main
361 className="child"
362 + tabIndex={null}
363 - tabIndex="1"
364 + dir={null}
365 - dir="rtl"
366 >
367
368 Owner Stack:
369 in main (at **)
370 in Mismatch (at **)",
371 ]
372 `);
373 });
374
375 // @gate __DEV__
376 it('warns when both client and server render extra attributes', () => {
377 function Mismatch({isClient}) {
378 return (
379 <div className="parent">
380 <main
381 className="child"
382 tabIndex={isClient ? 1 : null}
383 dir={isClient ? null : 'rtl'}
384 />
385 </div>
386 );
387 }
388 expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
389 [
390 "A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used:
391
392 - A server/client branch \`if (typeof window !== 'undefined')\`.
393 - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called.
394 - Date formatting in a user's locale which doesn't match the server.
395 - External changing data without sending a snapshot of it along with the HTML.
396 - Invalid HTML tag nesting.
397
398 It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
399
400 https://react.dev/link/hydration-mismatch
401
402 <Mismatch isClient={true}>
403 <div className="parent">
404 <main
405 className="child"
406 + tabIndex={1}
407 - tabIndex={null}
408 + dir={null}
409 - dir="rtl"
410 >
411
412 Owner Stack:
413 in main (at **)
414 in Mismatch (at **)",
415 ]
416 `);
417 });
418
419 // @gate __DEV__
420 it('warns when client and server render different styles', () => {
421 function Mismatch({isClient}) {
422 return (
423 <div className="parent">
424 <main
425 className="child"
426 style={{
427 opacity: isClient ? 1 : 0,
428 }}
429 />
430 </div>
431 );
432 }
433 expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
434 [
435 "A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used:
436
437 - A server/client branch \`if (typeof window !== 'undefined')\`.
438 - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called.
439 - Date formatting in a user's locale which doesn't match the server.
440 - External changing data without sending a snapshot of it along with the HTML.
441 - Invalid HTML tag nesting.
442
443 It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
444
445 https://react.dev/link/hydration-mismatch
446
447 <Mismatch isClient={true}>
448 <div className="parent">
449 <main
450 className="child"
451 + style={{opacity:1}}
452 - style={{opacity:"0"}}
453 >
454
455 Owner Stack:
456 in main (at **)
457 in Mismatch (at **)",
458 ]
459 `);
460 });
461
462 // @gate __DEV__
463 it('picks the DFS-first Fiber as the error Owner', () => {
464 function LeftMismatch({isClient}) {
465 return <div className={isClient ? 'client' : 'server'} />;
466 }
467
468 function LeftIndirection({isClient}) {
469 return <LeftMismatch isClient={isClient} />;
470 }
471
472 function MiddleMismatch({isClient}) {
473 return <span className={isClient ? 'client' : 'server'} />;
474 }
475
476 function RightMisMatch({isClient}) {
477 return <p className={isClient ? 'client' : 'server'} />;
478 }
479
480 function App({isClient}) {
481 return (
482 <>
483 <LeftIndirection isClient={isClient} />
484 <MiddleMismatch isClient={isClient} />
485 <RightMisMatch isClient={isClient} />
486 </>
487 );
488 }
489 expect(testMismatch(App)).toMatchInlineSnapshot(`
490 [
491 "A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used:
492
493 - A server/client branch \`if (typeof window !== 'undefined')\`.
494 - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called.
495 - Date formatting in a user's locale which doesn't match the server.
496 - External changing data without sending a snapshot of it along with the HTML.
497 - Invalid HTML tag nesting.
498
499 It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
500
501 https://react.dev/link/hydration-mismatch
502
503 <App isClient={true}>
504 <LeftIndirection isClient={true}>
505 <LeftMismatch isClient={true}>
506 <div
507 + className="client"
508 - className="server"
509 >
510 <MiddleMismatch isClient={true}>
511 <span
512 + className="client"
513 - className="server"
514 >
515 <RightMisMatch isClient={true}>
516 <p
517 + className="client"
518 - className="server"
519 >
520
521 Owner Stack:
522 in div (at **)
523 in LeftMismatch (at **)
524 in LeftIndirection (at **)
525 in App (at **)",
526 ]
527 `);
528 });
529
530 describe('nonce', () => {
531 // Nonce is on HTMLOrSVGElement, so cover a few host tags that hydrate
532 // attributes through getValueForAttribute.
533 function App() {
534 return (
535 <div>
536 <script nonce="r4nd0m" src="https://example.com/script.js" />
537 <style nonce="r4nd0m">{`body { background-color: red; }`}</style>
538 <link
539 rel="stylesheet"
540 nonce="r4nd0m"
541 href="https://example.com/style.css"
542 />
543 <img nonce="r4nd0m" src="https://example.com/image.png" />
544 <video nonce="r4nd0m" src="https://example.com/video.mp4" />
545 <audio nonce="r4nd0m" src="https://example.com/audio.mp3" />
546 <iframe nonce="r4nd0m" src="https://example.com/iframe.html" />
547 <form nonce="r4nd0m">
548 <input type="text" nonce="r4nd0m" />
549 </form>
550 <my-element nonce="r4nd0m" />
551 </div>
552 );
553 }
554
555 // @gate __DEV__
556 it('does not warn when nonce matches and CSP hides getAttribute', () => {
557 // When CSP is enabled, browsers hide the nonce content attribute
558 // (getAttribute("nonce") returns "") while .nonce remains readable.
559 // JSDOM does not implement this, so mock it for this case.
560 // https://html.spec.whatwg.org/multipage/urls-and-fetching.html#cryptographicnonce
561 const originalGetAttribute = window.Element.prototype.getAttribute;
562 spyOnDevAndProd(
563 window.Element.prototype,
564 'getAttribute',
565 ).mockImplementation(function (name) {
566 if (typeof name === 'string' && name.toLowerCase() === 'nonce') {
567 return '';
568 }
569 return originalGetAttribute.call(this, name);
570 });
571
572 const htmlString = ReactDOMServer.renderToString(<App />);
573 container.innerHTML = htmlString;
574
575 // validate that the nonce attribute is hidden by getAttribute
576 // mimicking the behavior of browsers when CSP is enabled
577 const script = container.querySelector('script');
578 expect(script.getAttribute('nonce')).toBe('');
579 expect(script.nonce).toBe('r4nd0m');
580
581 expect(testMismatch(App)).toEqual([]);
582 });
583
584 // @gate __DEV__
585 it('does not warn when nonce matches without CSP hiding', () => {
586 const htmlString = ReactDOMServer.renderToString(<App />);
587 container.innerHTML = htmlString;
588
589 // validate that the nonce attribute is visible via getAttribute
590 // when CSP is disabled
591 const script = container.querySelector('script');
592 expect(script.getAttribute('nonce')).toBe('r4nd0m');
593 expect(script.nonce).toBe('r4nd0m');
594 expect(testMismatch(App)).toEqual([]);
595 });
596 });
597 });
598
599 describe('extra nodes on the client', () => {
600 describe('extra elements on the client', () => {
601 // @gate __DEV__
602 it('warns when client renders an extra element as only child', () => {
603 function Mismatch({isClient}) {
604 return (
605 <div className="parent">
606 {isClient && <main className="only" />}
607 </div>
608 );
609 }
610 expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
611 [
612 "Caught [Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used:
613
614 - A server/client branch \`if (typeof window !== 'undefined')\`.
615 - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called.
616 - Date formatting in a user's locale which doesn't match the server.
617 - External changing data without sending a snapshot of it along with the HTML.
618 - Invalid HTML tag nesting.
619
620 It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
621
622 https://react.dev/link/hydration-mismatch
623
624 <Mismatch isClient={true}>
625 <div className="parent">
626 + <main className="only">
627 ]
628 Owner Stack:
629 in main (at **)
630 in Mismatch (at **)",
631 ]
632 `);
633 });
634
635 // @gate __DEV__
636 it('warns when client renders an extra element in the beginning', () => {
637 function Mismatch({isClient}) {
638 return (
639 <div className="parent">
640 {isClient && <header className="1" />}
641 <main className="2" />
642 <footer className="3" />
643 </div>
644 );
645 }
646 expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
647 [
648 "Caught [Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used:
649
650 - A server/client branch \`if (typeof window !== 'undefined')\`.
651 - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called.
652 - Date formatting in a user's locale which doesn't match the server.
653 - External changing data without sending a snapshot of it along with the HTML.
654 - Invalid HTML tag nesting.
655
656 It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
657
658 https://react.dev/link/hydration-mismatch
659
660 <Mismatch isClient={true}>
661 <div className="parent">
662 + <header className="1">
663 - <main className="2">
664 ...
665 ]
666 Owner Stack:
667 in header (at **)
668 in Mismatch (at **)",
669 ]
670 `);
671 });
672
673 // @gate __DEV__
674 it('warns when client renders an extra element in the middle', () => {
675 function Mismatch({isClient}) {
676 return (
677 <div className="parent">
678 <header className="1" />
679 {isClient && <main className="2" />}
680 <footer className="3" />
681 </div>
682 );
683 }
684 expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
685 [
686 "Caught [Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used:
687
688 - A server/client branch \`if (typeof window !== 'undefined')\`.
689 - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called.
690 - Date formatting in a user's locale which doesn't match the server.
691 - External changing data without sending a snapshot of it along with the HTML.
692 - Invalid HTML tag nesting.
693
694 It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
695
696 https://react.dev/link/hydration-mismatch
697
698 <Mismatch isClient={true}>
699 <div className="parent">
700 <header>
701 + <main className="2">
702 - <footer className="3">
703 ...
704 ]
705 Owner Stack:
706 in main (at **)
707 in Mismatch (at **)",
708 ]
709 `);
710 });
711
712 // @gate __DEV__
713 it('warns when client renders an extra element in the end', () => {
714 function Mismatch({isClient}) {
715 return (
716 <div className="parent">
717 <header className="1" />
718 <main className="2" />
719 {isClient && <footer className="3" />}
720 </div>
721 );
722 }
723 expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
724 [
725 "Caught [Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used:
726
727 - A server/client branch \`if (typeof window !== 'undefined')\`.
728 - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called.
729 - Date formatting in a user's locale which doesn't match the server.
730 - External changing data without sending a snapshot of it along with the HTML.
731 - Invalid HTML tag nesting.
732
733 It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
734
735 https://react.dev/link/hydration-mismatch
736
737 <Mismatch isClient={true}>
738 <div className="parent">
739 <header>
740 <main>
741 + <footer className="3">
742 ]
743 Owner Stack:
744 in footer (at **)
745 in Mismatch (at **)",
746 ]
747 `);
748 });
749 });
750
751 describe('extra text nodes on the client', () => {
752 // @gate __DEV__
753 it('warns when client renders an extra text node as only child', () => {
754 function Mismatch({isClient}) {
755 return <div className="parent">{isClient && 'only'}</div>;
756 }
757 expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
758 [
759 "Caught [Hydration failed because the server rendered text didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used:
760
761 - A server/client branch \`if (typeof window !== 'undefined')\`.
762 - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called.
763 - Date formatting in a user's locale which doesn't match the server.
764 - External changing data without sending a snapshot of it along with the HTML.
765 - Invalid HTML tag nesting.
766
767 It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
768
769 https://react.dev/link/hydration-mismatch
770
771 <Mismatch isClient={true}>
772 <div className="parent">
773 + only
774 -
775 ]
776 Owner Stack:
777 in div (at **)
778 in Mismatch (at **)",
779 ]
780 `);
781 });
782
783 // @gate __DEV__
784 it('warns when client renders an extra text node in the beginning', () => {
785 function Mismatch({isClient}) {
786 return (
787 <div className="parent">
788 <header className="1" />
789 {isClient && 'second'}
790 <footer className="3" />
791 </div>
792 );
793 }
794 expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
795 [
796 "Caught [Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used:
797
798 - A server/client branch \`if (typeof window !== 'undefined')\`.
799 - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called.
800 - Date formatting in a user's locale which doesn't match the server.
801 - External changing data without sending a snapshot of it along with the HTML.
802 - Invalid HTML tag nesting.
803
804 It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
805
806 https://react.dev/link/hydration-mismatch
807
808 <Mismatch isClient={true}>
809 <div className="parent">
810 <header>
811 + second
812 - <footer className="3">
813 ...
814 ]
815 Owner Stack:
816 in div (at **)
817 in Mismatch (at **)",
818 ]
819 `);
820 });
821
822 // @gate __DEV__
823 it('warns when client renders an extra text node in the middle', () => {
824 function Mismatch({isClient}) {
825 return (
826 <div className="parent">
827 {isClient && 'first'}
828 <main className="2" />
829 <footer className="3" />
830 </div>
831 );
832 }
833 expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
834 [
835 "Caught [Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used:
836
837 - A server/client branch \`if (typeof window !== 'undefined')\`.
838 - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called.
839 - Date formatting in a user's locale which doesn't match the server.
840 - External changing data without sending a snapshot of it along with the HTML.
841 - Invalid HTML tag nesting.
842
843 It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
844
845 https://react.dev/link/hydration-mismatch
846
847 <Mismatch isClient={true}>
848 <div className="parent">
849 + first
850 - <main className="2">
851 ...
852 ]
853 Owner Stack:
854 in div (at **)
855 in Mismatch (at **)",
856 ]
857 `);
858 });
859
860 // @gate __DEV__
861 it('warns when client renders an extra text node in the end', () => {
862 function Mismatch({isClient}) {
863 return (
864 <div className="parent">
865 <header className="1" />
866 <main className="2" />
867 {isClient && 'third'}
868 </div>
869 );
870 }
871 expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
872 [
873 "Caught [Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used:
874
875 - A server/client branch \`if (typeof window !== 'undefined')\`.
876 - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called.
877 - Date formatting in a user's locale which doesn't match the server.
878 - External changing data without sending a snapshot of it along with the HTML.
879 - Invalid HTML tag nesting.
880
881 It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
882
883 https://react.dev/link/hydration-mismatch
884
885 <Mismatch isClient={true}>
886 <div className="parent">
887 <header>
888 <main>
889 + third
890 ]
891 Owner Stack:
892 in div (at **)
893 in Mismatch (at **)",
894 ]
895 `);
896 });
897 });
898 });
899
900 describe('extra nodes on the server', () => {
901 describe('extra elements on the server', () => {
902 // @gate __DEV__
903 it('warns when server renders an extra element as only child', () => {
904 function Mismatch({isClient}) {
905 return (
906 <div className="parent">
907 {!isClient && <main className="only" />}
908 </div>
909 );
910 }
911 expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
912 [
913 "Caught [Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used:
914
915 - A server/client branch \`if (typeof window !== 'undefined')\`.
916 - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called.
917 - Date formatting in a user's locale which doesn't match the server.
918 - External changing data without sending a snapshot of it along with the HTML.
919 - Invalid HTML tag nesting.
920
921 It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
922
923 https://react.dev/link/hydration-mismatch
924
925 <Mismatch isClient={true}>
926 <div className="parent">
927 - <main className="only">
928 ]
929 Owner Stack:
930 in div (at **)
931 in Mismatch (at **)",
932 ]
933 `);
934 });
935
936 // @gate __DEV__
937 it('warns when server renders an extra element in the beginning', () => {
938 function Mismatch({isClient}) {
939 return (
940 <div className="parent">
941 {!isClient && <header className="1" />}
942 <main className="2" />
943 <footer className="3" />
944 </div>
945 );
946 }
947 expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
948 [
949 "Caught [Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used:
950
951 - A server/client branch \`if (typeof window !== 'undefined')\`.
952 - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called.
953 - Date formatting in a user's locale which doesn't match the server.
954 - External changing data without sending a snapshot of it along with the HTML.
955 - Invalid HTML tag nesting.
956
957 It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
958
959 https://react.dev/link/hydration-mismatch
960
961 <Mismatch isClient={true}>
962 <div className="parent">
963 + <main className="2">
964 - <header className="1">
965 ...
966 ]
967 Owner Stack:
968 in main (at **)
969 in Mismatch (at **)",
970 ]
971 `);
972 });
973
974 // @gate __DEV__
975 it('warns when server renders an extra element in the middle', () => {
976 function Mismatch({isClient}) {
977 return (
978 <div className="parent">
979 <header className="1" />
980 {!isClient && <main className="2" />}
981 <footer className="3" />
982 </div>
983 );
984 }
985 expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
986 [
987 "Caught [Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used:
988
989 - A server/client branch \`if (typeof window !== 'undefined')\`.
990 - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called.
991 - Date formatting in a user's locale which doesn't match the server.
992 - External changing data without sending a snapshot of it along with the HTML.
993 - Invalid HTML tag nesting.
994
995 It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
996
997 https://react.dev/link/hydration-mismatch
998
999 <Mismatch isClient={true}>
1000 <div className="parent">
1001 <header>
1002 + <footer className="3">
1003 - <main className="2">
1004 ]
1005 Owner Stack:
1006 in footer (at **)
1007 in Mismatch (at **)",
1008 ]
1009 `);
1010 });
1011
1012 // @gate __DEV__
1013 it('warns when server renders an extra element in the end', () => {
1014 function Mismatch({isClient}) {
1015 return (
1016 <div className="parent">
1017 <header className="1" />
1018 <main className="2" />
1019 {!isClient && <footer className="3" />}
1020 </div>
1021 );
1022 }
1023 expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
1024 [
1025 "Caught [Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used:
1026
1027 - A server/client branch \`if (typeof window !== 'undefined')\`.
1028 - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called.
1029 - Date formatting in a user's locale which doesn't match the server.
1030 - External changing data without sending a snapshot of it along with the HTML.
1031 - Invalid HTML tag nesting.
1032
1033 It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
1034
1035 https://react.dev/link/hydration-mismatch
1036
1037 <Mismatch isClient={true}>
1038 <div className="parent">
1039 - <footer className="3">
1040 ]
1041 Owner Stack:
1042 in div (at **)
1043 in Mismatch (at **)",
1044 ]
1045 `);
1046 });
1047 });
1048
1049 describe('extra text nodes on the server', () => {
1050 // @gate __DEV__
1051 it('warns when server renders an extra text node as only child', () => {
1052 function Mismatch({isClient}) {
1053 return <div className="parent">{!isClient && 'only'}</div>;
1054 }
1055 expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
1056 [
1057 "Caught [Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used:
1058
1059 - A server/client branch \`if (typeof window !== 'undefined')\`.
1060 - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called.
1061 - Date formatting in a user's locale which doesn't match the server.
1062 - External changing data without sending a snapshot of it along with the HTML.
1063 - Invalid HTML tag nesting.
1064
1065 It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
1066
1067 https://react.dev/link/hydration-mismatch
1068
1069 <Mismatch isClient={true}>
1070 <div className="parent">
1071 - only
1072 ]
1073 Owner Stack:
1074 in div (at **)
1075 in Mismatch (at **)",
1076 ]
1077 `);
1078 });
1079
1080 // @gate __DEV__
1081 it('warns when server renders an extra text node in the beginning', () => {
1082 function Mismatch({isClient}) {
1083 return (
1084 <div className="parent">
1085 {!isClient && 'first'}
1086 <main className="2" />
1087 <footer className="3" />
1088 </div>
1089 );
1090 }
1091 expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
1092 [
1093 "Caught [Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used:
1094
1095 - A server/client branch \`if (typeof window !== 'undefined')\`.
1096 - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called.
1097 - Date formatting in a user's locale which doesn't match the server.
1098 - External changing data without sending a snapshot of it along with the HTML.
1099 - Invalid HTML tag nesting.
1100
1101 It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
1102
1103 https://react.dev/link/hydration-mismatch
1104
1105 <Mismatch isClient={true}>
1106 <div className="parent">
1107 + <main className="2">
1108 - first
1109 ...
1110 ]
1111 Owner Stack:
1112 in main (at **)
1113 in Mismatch (at **)",
1114 ]
1115 `);
1116 });
1117
1118 // @gate __DEV__
1119 it('warns when server renders an extra text node in the middle', () => {
1120 function Mismatch({isClient}) {
1121 return (
1122 <div className="parent">
1123 <header className="1" />
1124 {!isClient && 'second'}
1125 <footer className="3" />
1126 </div>
1127 );
1128 }
1129 expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
1130 [
1131 "Caught [Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used:
1132
1133 - A server/client branch \`if (typeof window !== 'undefined')\`.
1134 - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called.
1135 - Date formatting in a user's locale which doesn't match the server.
1136 - External changing data without sending a snapshot of it along with the HTML.
1137 - Invalid HTML tag nesting.
1138
1139 It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
1140
1141 https://react.dev/link/hydration-mismatch
1142
1143 <Mismatch isClient={true}>
1144 <div className="parent">
1145 <header>
1146 + <footer className="3">
1147 - second
1148 ]
1149 Owner Stack:
1150 in footer (at **)
1151 in Mismatch (at **)",
1152 ]
1153 `);
1154 });
1155
1156 // @gate __DEV__
1157 it('warns when server renders an extra text node in the end', () => {
1158 function Mismatch({isClient}) {
1159 return (
1160 <div className="parent">
1161 <header className="1" />
1162 <main className="2" />
1163 {!isClient && 'third'}
1164 </div>
1165 );
1166 }
1167 expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
1168 [
1169 "Caught [Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used:
1170
1171 - A server/client branch \`if (typeof window !== 'undefined')\`.
1172 - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called.
1173 - Date formatting in a user's locale which doesn't match the server.
1174 - External changing data without sending a snapshot of it along with the HTML.
1175 - Invalid HTML tag nesting.
1176
1177 It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
1178
1179 https://react.dev/link/hydration-mismatch
1180
1181 <Mismatch isClient={true}>
1182 <div className="parent">
1183 - third
1184 ]
1185 Owner Stack:
1186 in div (at **)
1187 in Mismatch (at **)",
1188 ]
1189 `);
1190 });
1191 });
1192 });
1193
1194 describe('special nodes', () => {
1195 describe('Suspense', () => {
1196 function Never() {
1197 throw new Promise(resolve => {});
1198 }
1199
1200 // @gate __DEV__
1201 it('warns when client renders an extra Suspense node in content mode', () => {
1202 function Mismatch({isClient}) {
1203 return (
1204 <div className="parent">
1205 {isClient && (
1206 <React.Suspense fallback={<p>Loading...</p>}>
1207 <main className="only" />
1208 </React.Suspense>
1209 )}
1210 </div>
1211 );
1212 }
1213 expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
1214 [
1215 "Caught [Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used:
1216
1217 - A server/client branch \`if (typeof window !== 'undefined')\`.
1218 - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called.
1219 - Date formatting in a user's locale which doesn't match the server.
1220 - External changing data without sending a snapshot of it along with the HTML.
1221 - Invalid HTML tag nesting.
1222
1223 It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
1224
1225 https://react.dev/link/hydration-mismatch
1226
1227 <Mismatch isClient={true}>
1228 <div className="parent">
1229 + <Suspense fallback={<p>}>
1230 ]
1231 Owner Stack:
1232 in Suspense (at **)
1233 in Mismatch (at **)",
1234 ]
1235 `);
1236 });
1237
1238 // @gate __DEV__
1239 it('warns when server renders an extra Suspense node in content mode', () => {
1240 function Mismatch({isClient}) {
1241 return (
1242 <div className="parent">
1243 {!isClient && (
1244 <React.Suspense fallback={<p>Loading...</p>}>
1245 <main className="only" />
1246 </React.Suspense>
1247 )}
1248 </div>
1249 );
1250 }
1251 expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
1252 [
1253 "Caught [Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used:
1254
1255 - A server/client branch \`if (typeof window !== 'undefined')\`.
1256 - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called.
1257 - Date formatting in a user's locale which doesn't match the server.
1258 - External changing data without sending a snapshot of it along with the HTML.
1259 - Invalid HTML tag nesting.
1260
1261 It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
1262
1263 https://react.dev/link/hydration-mismatch
1264
1265 <Mismatch isClient={true}>
1266 <div className="parent">
1267 - <Suspense>
1268 ]
1269 Owner Stack:
1270 in div (at **)
1271 in Mismatch (at **)",
1272 ]
1273 `);
1274 });
1275
1276 // @gate __DEV__
1277 it('warns when client renders an extra Suspense node in fallback mode', () => {
1278 function Mismatch({isClient}) {
1279 return (
1280 <div className="parent">
1281 {isClient && (
1282 <React.Suspense fallback={<p>Loading...</p>}>
1283 <main className="only" />
1284 <Never />
1285 </React.Suspense>
1286 )}
1287 </div>
1288 );
1289 }
1290 // TODO: This message doesn't seem to have any useful details.
1291 expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
1292 [
1293 "Caught [Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used:
1294
1295 - A server/client branch \`if (typeof window !== 'undefined')\`.
1296 - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called.
1297 - Date formatting in a user's locale which doesn't match the server.
1298 - External changing data without sending a snapshot of it along with the HTML.
1299 - Invalid HTML tag nesting.
1300
1301 It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
1302
1303 https://react.dev/link/hydration-mismatch
1304
1305 <Mismatch isClient={true}>
1306 <div className="parent">
1307 + <Suspense fallback={<p>}>
1308 ]
1309 Owner Stack:
1310 in Suspense (at **)
1311 in Mismatch (at **)",
1312 ]
1313 `);
1314 });
1315
1316 // @gate __DEV__
1317 it('warns when server renders an extra Suspense node in fallback mode', () => {
1318 function Mismatch({isClient}) {
1319 return (
1320 <div className="parent">
1321 {!isClient && (
1322 <React.Suspense fallback={<p>Loading...</p>}>
1323 <main className="only" />
1324 <Never />
1325 </React.Suspense>
1326 )}
1327 </div>
1328 );
1329 }
1330
1331 // @TODO changes made to sending Fizz errors to client led to the insertion of templates in client rendered
1332 // suspense boundaries. This leaks in this test because the client rendered suspense boundary appears like
1333 // unhydrated tail nodes and this template is the first match. When we add special case handling for client
1334 // rendered suspense boundaries this test will likely change again
1335 expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
1336 [
1337 "Caught [Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used:
1338
1339 - A server/client branch \`if (typeof window !== 'undefined')\`.
1340 - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called.
1341 - Date formatting in a user's locale which doesn't match the server.
1342 - External changing data without sending a snapshot of it along with the HTML.
1343 - Invalid HTML tag nesting.
1344
1345 It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
1346
1347 https://react.dev/link/hydration-mismatch
1348
1349 <Mismatch isClient={true}>
1350 <div className="parent">
1351 - <Suspense>
1352 ]
1353 Owner Stack:
1354 in div (at **)
1355 in Mismatch (at **)",
1356 ]
1357 `);
1358 });
1359
1360 // @gate __DEV__
1361 it('warns when client renders an extra node inside Suspense content', () => {
1362 function Mismatch({isClient}) {
1363 return (
1364 <div className="parent">
1365 <React.Suspense fallback={<p>Loading...</p>}>
1366 <header className="1" />
1367 {isClient && <main className="second" />}
1368 <footer className="3" />
1369 </React.Suspense>
1370 </div>
1371 );
1372 }
1373 expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
1374 [
1375 "Caught [Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used:
1376
1377 - A server/client branch \`if (typeof window !== 'undefined')\`.
1378 - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called.
1379 - Date formatting in a user's locale which doesn't match the server.
1380 - External changing data without sending a snapshot of it along with the HTML.
1381 - Invalid HTML tag nesting.
1382
1383 It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
1384
1385 https://react.dev/link/hydration-mismatch
1386
1387 <Mismatch isClient={true}>
1388 <div className="parent">
1389 <Suspense fallback={<p>}>
1390 <header>
1391 + <main className="second">
1392 - <footer className="3">
1393 ...
1394 ]
1395 Owner Stack:
1396 in main (at **)
1397 in Mismatch (at **)",
1398 ]
1399 `);
1400 });
1401
1402 // @gate __DEV__
1403 it('warns when server renders an extra node inside Suspense content', () => {
1404 function Mismatch({isClient}) {
1405 return (
1406 <div className="parent">
1407 <React.Suspense fallback={<p>Loading...</p>}>
1408 <header className="1" />
1409 {!isClient && <main className="second" />}
1410 <footer className="3" />
1411 </React.Suspense>
1412 </div>
1413 );
1414 }
1415 expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
1416 [
1417 "Caught [Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used:
1418
1419 - A server/client branch \`if (typeof window !== 'undefined')\`.
1420 - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called.
1421 - Date formatting in a user's locale which doesn't match the server.
1422 - External changing data without sending a snapshot of it along with the HTML.
1423 - Invalid HTML tag nesting.
1424
1425 It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
1426
1427 https://react.dev/link/hydration-mismatch
1428
1429 <Mismatch isClient={true}>
1430 <div className="parent">
1431 <Suspense fallback={<p>}>
1432 <header>
1433 + <footer className="3">
1434 - <main className="second">
1435 ]
1436 Owner Stack:
1437 in footer (at **)
1438 in Mismatch (at **)",
1439 ]
1440 `);
1441 });
1442
1443 // @gate __DEV__
1444 it('warns when client renders an extra node inside Suspense fallback', () => {
1445 function Mismatch({isClient}) {
1446 return (
1447 <div className="parent">
1448 <React.Suspense
1449 fallback={
1450 <>
1451 <p>Loading...</p>
1452 {isClient && <br />}
1453 </>
1454 }>
1455 <main className="only" />
1456 <Never />
1457 </React.Suspense>
1458 </div>
1459 );
1460 }
1461
1462 expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
1463 [
1464 "Caught [Switched to client rendering because the server rendering aborted due to:
1465
1466 The server used "renderToString" which does not support Suspense. If you intended for this Suspense boundary to render the fallback content on the server consider throwing an Error somewhere within the Suspense boundary. If you intended to have the server wait for the suspended component please switch to "renderToPipeableStream" which supports Suspense on the server]
1467 Owner Stack: null",
1468 ]
1469 `);
1470 });
1471
1472 // @gate __DEV__
1473 it('warns when server renders an extra node inside Suspense fallback', () => {
1474 function Mismatch({isClient}) {
1475 return (
1476 <div className="parent">
1477 <React.Suspense
1478 fallback={
1479 <>
1480 <p>Loading...</p>
1481 {!isClient && <br />}
1482 </>
1483 }>
1484 <main className="only" />
1485 <Never />
1486 </React.Suspense>
1487 </div>
1488 );
1489 }
1490
1491 expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
1492 [
1493 "Caught [Switched to client rendering because the server rendering aborted due to:
1494
1495 The server used "renderToString" which does not support Suspense. If you intended for this Suspense boundary to render the fallback content on the server consider throwing an Error somewhere within the Suspense boundary. If you intended to have the server wait for the suspended component please switch to "renderToPipeableStream" which supports Suspense on the server]
1496 Owner Stack: null",
1497 ]
1498 `);
1499 });
1500 });
1501
1502 describe('Fragment', () => {
1503 // @gate __DEV__
1504 it('warns when client renders an extra Fragment node', () => {
1505 function Mismatch({isClient}) {
1506 return (
1507 <div className="parent">
1508 {isClient && (
1509 <>
1510 <header className="1" />
1511 <main className="2" />
1512 <footer className="3" />
1513 </>
1514 )}
1515 </div>
1516 );
1517 }
1518 expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
1519 [
1520 "Caught [Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used:
1521
1522 - A server/client branch \`if (typeof window !== 'undefined')\`.
1523 - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called.
1524 - Date formatting in a user's locale which doesn't match the server.
1525 - External changing data without sending a snapshot of it along with the HTML.
1526 - Invalid HTML tag nesting.
1527
1528 It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
1529
1530 https://react.dev/link/hydration-mismatch
1531
1532 <Mismatch isClient={true}>
1533 <div className="parent">
1534 + <header className="1">
1535 ...
1536 ]
1537 Owner Stack:
1538 in header (at **)
1539 in Mismatch (at **)",
1540 ]
1541 `);
1542 });
1543
1544 // @gate __DEV__
1545 it('warns when server renders an extra Fragment node', () => {
1546 function Mismatch({isClient}) {
1547 return (
1548 <div className="parent">
1549 {!isClient && (
1550 <>
1551 <header className="1" />
1552 <main className="2" />
1553 <footer className="3" />
1554 </>
1555 )}
1556 </div>
1557 );
1558 }
1559 expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
1560 [
1561 "Caught [Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used:
1562
1563 - A server/client branch \`if (typeof window !== 'undefined')\`.
1564 - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called.
1565 - Date formatting in a user's locale which doesn't match the server.
1566 - External changing data without sending a snapshot of it along with the HTML.
1567 - Invalid HTML tag nesting.
1568
1569 It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
1570
1571 https://react.dev/link/hydration-mismatch
1572
1573 <Mismatch isClient={true}>
1574 <div className="parent">
1575 - <header className="1">
1576 - <main className="2">
1577 - <footer className="3">
1578 ]
1579 Owner Stack:
1580 in div (at **)
1581 in Mismatch (at **)",
1582 ]
1583 `);
1584 });
1585 });
1586 });
1587
1588 describe('misc cases', () => {
1589 // @gate __DEV__
1590 it('warns when client renders an extra node deeper in the tree', () => {
1591 function Mismatch({isClient}) {
1592 return isClient ? <ProfileSettings /> : <MediaSettings />;
1593 }
1594
1595 function ProfileSettings() {
1596 return (
1597 <div className="parent">
1598 <input />
1599 <Panel type="profile" />
1600 </div>
1601 );
1602 }
1603
1604 function MediaSettings() {
1605 return (
1606 <div className="parent">
1607 <input />
1608 <Panel type="media" />
1609 </div>
1610 );
1611 }
1612
1613 function Panel({type}) {
1614 return (
1615 <>
1616 <header className="1" />
1617 <main className="2" />
1618 {type === 'profile' && <footer className="3" />}
1619 </>
1620 );
1621 }
1622
1623 expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
1624 [
1625 "Caught [Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used:
1626
1627 - A server/client branch \`if (typeof window !== 'undefined')\`.
1628 - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called.
1629 - Date formatting in a user's locale which doesn't match the server.
1630 - External changing data without sending a snapshot of it along with the HTML.
1631 - Invalid HTML tag nesting.
1632
1633 It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
1634
1635 https://react.dev/link/hydration-mismatch
1636
1637 <Mismatch isClient={true}>
1638 <ProfileSettings>
1639 <div className="parent">
1640 <input>
1641 <Panel type="profile">
1642 <header>
1643 <main>
1644 + <footer className="3">
1645 ]
1646 Owner Stack:
1647 in footer (at **)
1648 in Panel (at **)
1649 in ProfileSettings (at **)
1650 in Mismatch (at **)",
1651 ]
1652 `);
1653 });
1654
1655 // @gate __DEV__
1656 it('warns when server renders an extra node deeper in the tree', () => {
1657 function Mismatch({isClient}) {
1658 return isClient ? <ProfileSettings /> : <MediaSettings />;
1659 }
1660
1661 function ProfileSettings() {
1662 return (
1663 <div className="parent">
1664 <input />
1665 <Panel type="profile" />
1666 </div>
1667 );
1668 }
1669
1670 function MediaSettings() {
1671 return (
1672 <div className="parent">
1673 <input />
1674 <Panel type="media" />
1675 </div>
1676 );
1677 }
1678
1679 function Panel({type}) {
1680 return (
1681 <>
1682 <header className="1" />
1683 <main className="2" />
1684 {type !== 'profile' && <footer className="3" />}
1685 </>
1686 );
1687 }
1688
1689 expect(testMismatch(Mismatch)).toMatchInlineSnapshot(`
1690 [
1691 "Caught [Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used:
1692
1693 - A server/client branch \`if (typeof window !== 'undefined')\`.
1694 - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called.
1695 - Date formatting in a user's locale which doesn't match the server.
1696 - External changing data without sending a snapshot of it along with the HTML.
1697 - Invalid HTML tag nesting.
1698
1699 It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
1700
1701 https://react.dev/link/hydration-mismatch
1702
1703 <Mismatch isClient={true}>
1704 <ProfileSettings>
1705 <div className="parent">
1706 - <footer className="3">
1707 ]
1708 Owner Stack:
1709 in div (at **)
1710 in ProfileSettings (at **)
1711 in Mismatch (at **)",
1712 ]
1713 `);
1714 });
1715 });
1716 });