|
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' |
3 | 3 | import { createRequire } from 'node:module' |
4 | 4 |
|
5 | 5 | const require = createRequire(import.meta.url) |
@@ -50,19 +50,59 @@ async function setupRouteMocking(page: Page): Promise<void> { |
50 | 50 | } |
51 | 51 |
|
52 | 52 | /** |
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. |
54 | 79 | * |
55 | 80 | * All external API requests are intercepted and served from fixtures. |
56 | 81 | * 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. |
57 | 85 | */ |
58 | | -export const test = base.extend<{ mockExternalApis: void }>({ |
| 86 | +export const test = base.extend<{ mockExternalApis: void; hydrationErrors: string[] }>({ |
59 | 87 | mockExternalApis: [ |
60 | 88 | async ({ page }, use) => { |
61 | 89 | await setupRouteMocking(page) |
62 | 90 | await use() |
63 | 91 | }, |
64 | 92 | { auto: true }, |
65 | 93 | ], |
| 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 | + }, |
66 | 106 | }) |
67 | 107 |
|
68 | | -export { expect } from '@nuxt/test-utils/playwright' |
| 108 | +export { expect } |
0 commit comments