···11-import type { Page, Route } from '@playwright/test'
22-import { test as base } from '@nuxt/test-utils/playwright'
11+import type { ConsoleMessage, Page, Route } from '@playwright/test'
22+import { test as base, expect } from '@nuxt/test-utils/playwright'
33import { createRequire } from 'node:module'
4455const require = createRequire(import.meta.url)
···5050}
51515252/**
5353- * Extended test fixture with automatic external API mocking.
5353+ * Patterns that indicate a Vue hydration mismatch in console output.
5454+ *
5555+ * Vue always emits `console.error("Hydration completed but contains mismatches.")`
5656+ * in production builds when a hydration mismatch occurs.
5757+ *
5858+ * When `debug.hydration: true` is enabled (sets `__VUE_PROD_HYDRATION_MISMATCH_DETAILS__`),
5959+ * Vue also emits more detailed warnings (text content mismatch, node mismatch, etc.).
6060+ * We catch both the summary error and the detailed warnings.
6161+ */
6262+const HYDRATION_MISMATCH_PATTERNS = [
6363+ 'Hydration completed but contains mismatches',
6464+ 'Hydration text content mismatch',
6565+ 'Hydration node mismatch',
6666+ 'Hydration children mismatch',
6767+ 'Hydration attribute mismatch',
6868+ 'Hydration class mismatch',
6969+ 'Hydration style mismatch',
7070+]
7171+7272+function isHydrationMismatch(message: ConsoleMessage): boolean {
7373+ const text = message.text()
7474+ return HYDRATION_MISMATCH_PATTERNS.some(pattern => text.includes(pattern))
7575+}
7676+7777+/**
7878+ * 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.
8282+ *
8383+ * Hydration mismatches are detected via Vue's console.error output, which is always
8484+ * emitted in production builds when server-rendered HTML doesn't match client expectations.
5785 */
5858-export const test = base.extend<{ mockExternalApis: void }>({
8686+export const test = base.extend<{ mockExternalApis: void; hydrationErrors: string[] }>({
5987 mockExternalApis: [
6088 async ({ page }, use) => {
6189 await setupRouteMocking(page)
···6391 },
6492 { auto: true },
6593 ],
9494+9595+ hydrationErrors: async ({ page }, use) => {
9696+ const errors: string[] = []
9797+9898+ page.on('console', message => {
9999+ if (isHydrationMismatch(message)) {
100100+ errors.push(message.text())
101101+ }
102102+ })
103103+104104+ await use(errors)
105105+ },
66106})
671076868-export { expect } from '@nuxt/test-utils/playwright'
108108+export { expect }
+1-1
test/unit/a11y-component-coverage.spec.ts
···3636 'Modal.client.vue':
3737 'Base modal component - tested via specific modals like ChartModal, ConnectorModal',
3838 'Package/SkillsModal.vue': 'Complex modal with tabs - requires modal context and state',
3939- 'ScrollToTop.vue': 'Requires scroll position and CSS scroll-state queries',
3939+ 'ScrollToTop.client.vue': 'Requires scroll position and CSS scroll-state queries',
4040 'Settings/TranslationHelper.vue': 'i18n helper component - requires specific locale status data',
4141 'Package/WeeklyDownloadStats.vue':
4242 'Uses vue-data-ui VueUiSparkline - has DOM measurement issues in test environment',