From 3bbaa1769dd555f42e899be19db4aa40476dd01e Mon Sep 17 00:00:00 2001 From: eps1lon Date: Mon, 23 Sep 2024 14:37:29 +0200 Subject: [PATCH] Add extensive hydration error test suite for Pages router Pages router will support both React 18 and 19 which has significant changes for hydration errors. The existing acceptance tests only cover App router which runs exclusive on React 19. Ideally this would be a single test suite that just forks on the assertion. However, there are a lot of differences between App router and Pages router and putting that into a single file takes more time. The goal here is to not regress any further with Pages router when extending support to 18. Once RC2 landed, I'll merge these test suites. --- .../acceptance-app/hydration-error.test.ts | 2 +- .../acceptance/hydration-error.test.ts | 830 +++++++++++++++++- test/development/basic/hmr.test.ts | 19 - .../basic/hmr/pages/hydration-error.js | 3 - test/lib/next-test-utils.ts | 2 +- 5 files changed, 814 insertions(+), 42 deletions(-) delete mode 100644 test/development/basic/hmr/pages/hydration-error.js diff --git a/test/development/acceptance-app/hydration-error.test.ts b/test/development/acceptance-app/hydration-error.test.ts index 6d5baa3a01566a..35888a6cd0ed59 100644 --- a/test/development/acceptance-app/hydration-error.test.ts +++ b/test/development/acceptance-app/hydration-error.test.ts @@ -7,7 +7,7 @@ import { getRedboxTotalErrorCount } from 'next-test-utils' // https://github.com/facebook/react/blob/main/packages/react-dom/src/__tests__/ReactDOMHydrationDiff-test.js used as a reference -describe('Error overlay for hydration errors', () => { +describe('Error overlay for hydration errors in App router', () => { const { next, isTurbopack } = nextTestSetup({ files: new FileRef(path.join(__dirname, 'fixtures', 'default-template')), skipStart: true, diff --git a/test/development/acceptance/hydration-error.test.ts b/test/development/acceptance/hydration-error.test.ts index 47590983fd1e6b..ce60f9acbc46bc 100644 --- a/test/development/acceptance/hydration-error.test.ts +++ b/test/development/acceptance/hydration-error.test.ts @@ -1,11 +1,14 @@ /* eslint-env jest */ import { sandbox } from 'development-sandbox' import { FileRef, nextTestSetup } from 'e2e-utils' -import { outdent } from 'outdent' import path from 'path' +import { outdent } from 'outdent' +import { getRedboxTotalErrorCount } from 'next-test-utils' + +// https://github.com/facebook/react/blob/main/packages/react-dom/src/__tests__/ReactDOMHydrationDiff-test.js used as a reference -describe('Error overlay for hydration errors', () => { - const { next } = nextTestSetup({ +describe('Error overlay for hydration errors in Pages router', () => { + const { next, isTurbopack } = nextTestSetup({ files: new FileRef(path.join(__dirname, 'fixtures', 'default-template')), skipStart: true, }) @@ -31,31 +34,61 @@ describe('Error overlay for hydration errors', () => { ) await session.assertHasRedbox() + expect(await getRedboxTotalErrorCount(browser)).toBe(1) expect(await session.getRedboxDescription()).toMatchInlineSnapshot(` - "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 - See more info here: https://nextjs.org/docs/messages/react-hydration-error" - `) + "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 + See more info here: https://nextjs.org/docs/messages/react-hydration-error" + `) + expect(await session.getRedboxDescriptionWarning()).toMatchInlineSnapshot(` - "- A server/client branch \`if (typeof window !== 'undefined')\`. - - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called. - - Date formatting in a user's locale which doesn't match the server. - - External changing data without sending a snapshot of it along with the HTML. - - Invalid HTML tag nesting. + "- A server/client branch \`if (typeof window !== 'undefined')\`. + - Variable input such as \`Date.now()\` or \`Math.random()\` which changes each time it's called. + - Date formatting in a user's locale which doesn't match the server. + - External changing data without sending a snapshot of it along with the HTML. + - Invalid HTML tag nesting. - It can also happen if the client has a browser extension installed which messes with the HTML before React loaded." + It can also happen if the client has a browser extension installed which messes with the HTML before React loaded." + `) + + const pseudoHtml = await session.getRedboxComponentStack() + + if (isTurbopack) { + expect(pseudoHtml).toMatchInlineSnapshot(` + "... + + + + + + + +
+
+ + client + - server" `) + } else { + expect(pseudoHtml).toMatchInlineSnapshot(` + "... +
+
+ + client + - server" + `) + } await session.patch( 'index.js', outdent` + 'use client' export default function Mismatch() { - return ( -
-
Value
-
- ); - } + return ( +
+
Value
+
+ ); + } ` ) @@ -65,4 +98,765 @@ describe('Error overlay for hydration errors', () => { await cleanup() }) + + it('should show correct hydration error when client renders an extra element', async () => { + const { browser, cleanup, session } = await sandbox( + next, + new Map([ + [ + 'index.js', + outdent` + const isClient = typeof window !== 'undefined' + export default function Mismatch() { + return ( +
+ {isClient &&
} +
+ ); + } + `, + ], + ]) + ) + + await session.assertHasRedbox() + expect(await getRedboxTotalErrorCount(browser)).toBe(1) + + const pseudoHtml = await session.getRedboxComponentStack() + if (isTurbopack) { + expect(pseudoHtml).toMatchInlineSnapshot(` + "... + + + + + + + +
+ ... + +
" + `) + } else { + expect(pseudoHtml).toMatchInlineSnapshot(` + "... +
+ ... + +
" + `) + } + + expect(await session.getRedboxDescription()).toMatchInlineSnapshot(` + "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 + See more info here: https://nextjs.org/docs/messages/react-hydration-error" + `) + + await cleanup() + }) + + it('should show correct hydration error when client renders an extra text node', async () => { + const { browser, cleanup, session } = await sandbox( + next, + new Map([ + [ + 'index.js', + outdent` + const isClient = typeof window !== 'undefined' + export default function Mismatch() { + return ( +
+
+ {isClient && "second"} +
+
+ ); + } + `, + ], + ]) + ) + + await session.assertHasRedbox() + expect(await getRedboxTotalErrorCount(browser)).toBe(1) + + const pseudoHtml = await session.getRedboxComponentStack() + if (isTurbopack) { + expect(pseudoHtml).toMatchInlineSnapshot(` + "... + + + + + + + +
+ + second + -