Skip to content

Commit 9c45a2d

Browse files
committed
test: add hydration test
1 parent 6b33775 commit 9c45a2d

File tree

2 files changed

+56
-5
lines changed

2 files changed

+56
-5
lines changed

test/e2e/hydration.spec.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { expect, test } from './test-utils'
2+
3+
test.describe('Hydration', () => {
4+
test('/ (homepage) has no hydration mismatches', async ({ goto, hydrationErrors }) => {
5+
await goto('/', { waitUntil: 'hydration' })
6+
// await goto('/about', { waitUntil: 'hydration' })
7+
// await goto('/settings', { waitUntil: 'hydration' })
8+
9+
expect(hydrationErrors).toEqual([])
10+
})
11+
})

test/e2e/test-utils.ts

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import type { Page, Route } from '@playwright/test'
2-
import { test as base } from '@nuxt/test-utils/playwright'
1+
import type { ConsoleMessage, Page, Route } from '@playwright/test'
2+
import { test as base, expect } from '@nuxt/test-utils/playwright'
33
import { createRequire } from 'node:module'
44

55
const require = createRequire(import.meta.url)
@@ -50,19 +50,59 @@ async function setupRouteMocking(page: Page): Promise<void> {
5050
}
5151

5252
/**
53-
* Extended test fixture with automatic external API mocking.
53+
* Patterns that indicate a Vue hydration mismatch in console output.
54+
*
55+
* Vue always emits `console.error("Hydration completed but contains mismatches.")`
56+
* in production builds when a hydration mismatch occurs.
57+
*
58+
* When `debug.hydration: true` is enabled (sets `__VUE_PROD_HYDRATION_MISMATCH_DETAILS__`),
59+
* Vue also emits more detailed warnings (text content mismatch, node mismatch, etc.).
60+
* We catch both the summary error and the detailed warnings.
61+
*/
62+
const HYDRATION_MISMATCH_PATTERNS = [
63+
'Hydration completed but contains mismatches',
64+
'Hydration text content mismatch',
65+
'Hydration node mismatch',
66+
'Hydration children mismatch',
67+
'Hydration attribute mismatch',
68+
'Hydration class mismatch',
69+
'Hydration style mismatch',
70+
]
71+
72+
function isHydrationMismatch(message: ConsoleMessage): boolean {
73+
const text = message.text()
74+
return HYDRATION_MISMATCH_PATTERNS.some(pattern => text.includes(pattern))
75+
}
76+
77+
/**
78+
* Extended test fixture with automatic external API mocking and hydration mismatch detection.
5479
*
5580
* All external API requests are intercepted and served from fixtures.
5681
* If a request cannot be mocked, the test will fail with a clear error.
82+
*
83+
* Hydration mismatches are detected via Vue's console.error output, which is always
84+
* emitted in production builds when server-rendered HTML doesn't match client expectations.
5785
*/
58-
export const test = base.extend<{ mockExternalApis: void }>({
86+
export const test = base.extend<{ mockExternalApis: void; hydrationErrors: string[] }>({
5987
mockExternalApis: [
6088
async ({ page }, use) => {
6189
await setupRouteMocking(page)
6290
await use()
6391
},
6492
{ auto: true },
6593
],
94+
95+
hydrationErrors: async ({ page }, use) => {
96+
const errors: string[] = []
97+
98+
page.on('console', message => {
99+
if (isHydrationMismatch(message)) {
100+
errors.push(message.text())
101+
}
102+
})
103+
104+
await use(errors)
105+
},
66106
})
67107

68-
export { expect } from '@nuxt/test-utils/playwright'
108+
export { expect }

0 commit comments

Comments
 (0)