diff --git a/dev-packages/e2e-tests/test-applications/nuxt-3-dynamic-import/tests/errors.client.test.ts b/dev-packages/e2e-tests/test-applications/nuxt-3-dynamic-import/tests/errors.client.test.ts index 0bf456015834..106e26ff68a2 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-3-dynamic-import/tests/errors.client.test.ts +++ b/dev-packages/e2e-tests/test-applications/nuxt-3-dynamic-import/tests/errors.client.test.ts @@ -1,5 +1,6 @@ import { expect, test } from '@playwright/test'; import { waitForError } from '@sentry-internal/test-utils'; +import { waitForHydration } from './waitForHydration'; test.describe('client-side errors', async () => { test('captures error thrown on click', async ({ page }) => { @@ -8,6 +9,7 @@ test.describe('client-side errors', async () => { }); await page.goto(`/client-error`); + await waitForHydration(page); await page.locator('#errorBtn').click(); const error = await errorPromise; @@ -35,7 +37,7 @@ test.describe('client-side errors', async () => { }); await page.goto(`/test-param/1234`); - await page.waitForFunction(() => typeof window.__SENTRY__ === 'object'); + await waitForHydration(page); await page.locator('#errorBtn').click(); const error = await errorPromise; @@ -65,6 +67,7 @@ test.describe('client-side errors', async () => { }); await page.goto(`/client-error`); + await waitForHydration(page); await page.locator('#errorBtn').click(); const error1 = await error1Promise; diff --git a/dev-packages/e2e-tests/test-applications/nuxt-3-dynamic-import/tests/errors.server.test.ts b/dev-packages/e2e-tests/test-applications/nuxt-3-dynamic-import/tests/errors.server.test.ts index 90771e4174ad..64726a13c12a 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-3-dynamic-import/tests/errors.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/nuxt-3-dynamic-import/tests/errors.server.test.ts @@ -1,5 +1,6 @@ import { expect, test } from '@playwright/test'; import { waitForError } from '@sentry-internal/test-utils'; +import { waitForHydration } from './waitForHydration'; test.describe('server-side errors', async () => { test('captures api fetch error (fetched on click)', async ({ page }) => { @@ -8,6 +9,7 @@ test.describe('server-side errors', async () => { }); await page.goto(`/fetch-server-error`); + await waitForHydration(page); await page.getByText('Fetch Server API Error', { exact: true }).click(); const error = await errorPromise; @@ -42,6 +44,7 @@ test.describe('server-side errors', async () => { }); await page.goto(`/test-param/1234`); + await waitForHydration(page); await page.getByRole('button', { name: 'Fetch Server API Error', exact: true }).click(); const error = await errorPromise; diff --git a/dev-packages/e2e-tests/test-applications/nuxt-3-dynamic-import/tests/waitForHydration.ts b/dev-packages/e2e-tests/test-applications/nuxt-3-dynamic-import/tests/waitForHydration.ts new file mode 100644 index 000000000000..71c53ecda4a5 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nuxt-3-dynamic-import/tests/waitForHydration.ts @@ -0,0 +1,10 @@ +import type { Page } from '@playwright/test'; + +type NuxtRootContainer = HTMLElement & { __vue_app__?: { $nuxt?: { isHydrating?: boolean } } }; + +// Server-rendered elements are clickable before Vue hydration attaches their event handlers. +export async function waitForHydration(page: Page): Promise { + await page.waitForFunction( + () => (document.getElementById('__nuxt') as NuxtRootContainer | null)?.__vue_app__?.$nuxt?.isHydrating === false, + ); +} diff --git a/dev-packages/e2e-tests/test-applications/nuxt-3-min/tests/errors.client.test.ts b/dev-packages/e2e-tests/test-applications/nuxt-3-min/tests/errors.client.test.ts index e9ef67ef75b6..1ed7de457433 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-3-min/tests/errors.client.test.ts +++ b/dev-packages/e2e-tests/test-applications/nuxt-3-min/tests/errors.client.test.ts @@ -1,5 +1,6 @@ import { expect, test } from '@playwright/test'; import { waitForError } from '@sentry-internal/test-utils'; +import { waitForHydration } from './waitForHydration'; test.describe('client-side errors', async () => { test('captures error thrown on click', async ({ page }) => { @@ -8,6 +9,7 @@ test.describe('client-side errors', async () => { }); await page.goto(`/client-error`); + await waitForHydration(page); await page.locator('#errorBtn').click(); const error = await errorPromise; @@ -35,6 +37,7 @@ test.describe('client-side errors', async () => { }); await page.goto(`/test-param/1234`); + await waitForHydration(page); await page.locator('#errorBtn').click(); const error = await errorPromise; @@ -64,6 +67,7 @@ test.describe('client-side errors', async () => { }); await page.goto(`/client-error`); + await waitForHydration(page); await page.locator('#errorBtn').click(); const error1 = await error1Promise; diff --git a/dev-packages/e2e-tests/test-applications/nuxt-3-min/tests/errors.server.test.ts b/dev-packages/e2e-tests/test-applications/nuxt-3-min/tests/errors.server.test.ts index e108a25ea9b1..3edf9e7d8744 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-3-min/tests/errors.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/nuxt-3-min/tests/errors.server.test.ts @@ -1,5 +1,6 @@ import { expect, test } from '@playwright/test'; import { waitForError } from '@sentry-internal/test-utils'; +import { waitForHydration } from './waitForHydration'; test.describe('server-side errors', async () => { test('captures api fetch error (fetched on click)', async ({ page }) => { @@ -8,6 +9,7 @@ test.describe('server-side errors', async () => { }); await page.goto(`/fetch-server-error`); + await waitForHydration(page); await page.getByText('Fetch Server API Error', { exact: true }).click(); const error = await errorPromise; @@ -42,6 +44,7 @@ test.describe('server-side errors', async () => { }); await page.goto(`/test-param/1234`); + await waitForHydration(page); await page.getByRole('button', { name: 'Fetch Server API Error', exact: true }).click(); const error = await errorPromise; diff --git a/dev-packages/e2e-tests/test-applications/nuxt-3-min/tests/waitForHydration.ts b/dev-packages/e2e-tests/test-applications/nuxt-3-min/tests/waitForHydration.ts new file mode 100644 index 000000000000..71c53ecda4a5 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nuxt-3-min/tests/waitForHydration.ts @@ -0,0 +1,10 @@ +import type { Page } from '@playwright/test'; + +type NuxtRootContainer = HTMLElement & { __vue_app__?: { $nuxt?: { isHydrating?: boolean } } }; + +// Server-rendered elements are clickable before Vue hydration attaches their event handlers. +export async function waitForHydration(page: Page): Promise { + await page.waitForFunction( + () => (document.getElementById('__nuxt') as NuxtRootContainer | null)?.__vue_app__?.$nuxt?.isHydrating === false, + ); +} diff --git a/dev-packages/e2e-tests/test-applications/nuxt-3-top-level-import/tests/errors.client.test.ts b/dev-packages/e2e-tests/test-applications/nuxt-3-top-level-import/tests/errors.client.test.ts index ff99eb1b192f..a77959867c2b 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-3-top-level-import/tests/errors.client.test.ts +++ b/dev-packages/e2e-tests/test-applications/nuxt-3-top-level-import/tests/errors.client.test.ts @@ -1,5 +1,6 @@ import { expect, test } from '@playwright/test'; import { waitForError } from '@sentry-internal/test-utils'; +import { waitForHydration } from './waitForHydration'; test.describe('client-side errors', async () => { test('captures error thrown on click', async ({ page }) => { @@ -8,6 +9,7 @@ test.describe('client-side errors', async () => { }); await page.goto(`/client-error`); + await waitForHydration(page); await page.locator('#errorBtn').click(); const error = await errorPromise; @@ -35,6 +37,7 @@ test.describe('client-side errors', async () => { }); await page.goto(`/test-param/1234`); + await waitForHydration(page); await page.locator('#errorBtn').click(); const error = await errorPromise; @@ -64,6 +67,7 @@ test.describe('client-side errors', async () => { }); await page.goto(`/client-error`); + await waitForHydration(page); await page.locator('#errorBtn').click(); const error1 = await error1Promise; diff --git a/dev-packages/e2e-tests/test-applications/nuxt-3-top-level-import/tests/errors.server.test.ts b/dev-packages/e2e-tests/test-applications/nuxt-3-top-level-import/tests/errors.server.test.ts index eb8a9647cdd2..336f3966d94c 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-3-top-level-import/tests/errors.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/nuxt-3-top-level-import/tests/errors.server.test.ts @@ -1,5 +1,6 @@ import { expect, test } from '@playwright/test'; import { getSpanOp, waitForError, waitForStreamedSpan } from '@sentry-internal/test-utils'; +import { waitForHydration } from './waitForHydration'; test.describe('server-side errors', async () => { test('captures api fetch error (fetched on click)', async ({ page }) => { @@ -18,6 +19,7 @@ test.describe('server-side errors', async () => { }); await page.goto(`/fetch-server-error`); + await waitForHydration(page); await page.getByText('Fetch Server API Error', { exact: true }).click(); const serverSpan = await serverSpanPromise; @@ -66,6 +68,7 @@ test.describe('server-side errors', async () => { }); await page.goto(`/fetch-server-error`); + await waitForHydration(page); await page.getByText('Fetch Server API Error', { exact: true }).click(); await serverSpanPromise; @@ -82,6 +85,7 @@ test.describe('server-side errors', async () => { }); await page.goto(`/test-param/1234`); + await waitForHydration(page); await page.getByRole('button', { name: 'Fetch Server API Error', exact: true }).click(); const error = await errorPromise; diff --git a/dev-packages/e2e-tests/test-applications/nuxt-3-top-level-import/tests/waitForHydration.ts b/dev-packages/e2e-tests/test-applications/nuxt-3-top-level-import/tests/waitForHydration.ts new file mode 100644 index 000000000000..71c53ecda4a5 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nuxt-3-top-level-import/tests/waitForHydration.ts @@ -0,0 +1,10 @@ +import type { Page } from '@playwright/test'; + +type NuxtRootContainer = HTMLElement & { __vue_app__?: { $nuxt?: { isHydrating?: boolean } } }; + +// Server-rendered elements are clickable before Vue hydration attaches their event handlers. +export async function waitForHydration(page: Page): Promise { + await page.waitForFunction( + () => (document.getElementById('__nuxt') as NuxtRootContainer | null)?.__vue_app__?.$nuxt?.isHydrating === false, + ); +} diff --git a/dev-packages/e2e-tests/test-applications/nuxt-3/tests/errors.client.test.ts b/dev-packages/e2e-tests/test-applications/nuxt-3/tests/errors.client.test.ts index 11e4a5a367d4..fc10459b544b 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-3/tests/errors.client.test.ts +++ b/dev-packages/e2e-tests/test-applications/nuxt-3/tests/errors.client.test.ts @@ -1,5 +1,6 @@ import { expect, test } from '@playwright/test'; import { waitForError } from '@sentry-internal/test-utils'; +import { waitForHydration } from './waitForHydration'; test.describe('client-side errors', async () => { test('captures error thrown on click', async ({ page }) => { @@ -8,6 +9,7 @@ test.describe('client-side errors', async () => { }); await page.goto(`/client-error`); + await waitForHydration(page); await page.locator('#errorBtn').click(); const error = await errorPromise; @@ -35,6 +37,7 @@ test.describe('client-side errors', async () => { }); await page.goto(`/client-error`); + await waitForHydration(page); await page.locator('#error-in-error-boundary').click(); const error = await errorPromise; @@ -77,6 +80,7 @@ test.describe('client-side errors', async () => { }); await page.goto(`/test-param/1234`); + await waitForHydration(page); await page.locator('#errorBtn').click(); const error = await errorPromise; @@ -106,6 +110,7 @@ test.describe('client-side errors', async () => { }); await page.goto(`/client-error`); + await waitForHydration(page); await page.locator('#errorBtn').click(); const error1 = await error1Promise; diff --git a/dev-packages/e2e-tests/test-applications/nuxt-3/tests/errors.server.test.ts b/dev-packages/e2e-tests/test-applications/nuxt-3/tests/errors.server.test.ts index a360209ec1ce..cdebac5593e5 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-3/tests/errors.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/nuxt-3/tests/errors.server.test.ts @@ -1,5 +1,6 @@ import { expect, test } from '@playwright/test'; import { waitForError } from '@sentry-internal/test-utils'; +import { waitForHydration } from './waitForHydration'; test.describe('server-side errors', async () => { test('captures api fetch error (fetched on click)', async ({ page }) => { @@ -8,6 +9,7 @@ test.describe('server-side errors', async () => { }); await page.goto(`/fetch-server-error`); + await waitForHydration(page); await page.getByText('Fetch Server API Error', { exact: true }).click(); const error = await errorPromise; @@ -42,6 +44,7 @@ test.describe('server-side errors', async () => { }); await page.goto(`/test-param/1234`); + await waitForHydration(page); await page.getByRole('button', { name: 'Fetch Server API Error', exact: true }).click(); const error = await errorPromise; diff --git a/dev-packages/e2e-tests/test-applications/nuxt-3/tests/waitForHydration.ts b/dev-packages/e2e-tests/test-applications/nuxt-3/tests/waitForHydration.ts new file mode 100644 index 000000000000..71c53ecda4a5 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nuxt-3/tests/waitForHydration.ts @@ -0,0 +1,10 @@ +import type { Page } from '@playwright/test'; + +type NuxtRootContainer = HTMLElement & { __vue_app__?: { $nuxt?: { isHydrating?: boolean } } }; + +// Server-rendered elements are clickable before Vue hydration attaches their event handlers. +export async function waitForHydration(page: Page): Promise { + await page.waitForFunction( + () => (document.getElementById('__nuxt') as NuxtRootContainer | null)?.__vue_app__?.$nuxt?.isHydrating === false, + ); +} diff --git a/dev-packages/e2e-tests/test-applications/nuxt-4-static/tests/errors.client.test.ts b/dev-packages/e2e-tests/test-applications/nuxt-4-static/tests/errors.client.test.ts index 1c747e73fe77..64a3f6e7b88d 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-4-static/tests/errors.client.test.ts +++ b/dev-packages/e2e-tests/test-applications/nuxt-4-static/tests/errors.client.test.ts @@ -1,5 +1,6 @@ import { expect, test } from '@playwright/test'; import { waitForError } from '@sentry-internal/test-utils'; +import { waitForHydration } from './waitForHydration'; test.describe('client-side errors', async () => { test('captures error thrown on click', async ({ page }) => { @@ -8,6 +9,7 @@ test.describe('client-side errors', async () => { }); await page.goto(`/client-error`); + await waitForHydration(page); await page.locator('#errorBtn').click(); const error = await errorPromise; @@ -35,6 +37,7 @@ test.describe('client-side errors', async () => { }); await page.goto(`/client-error`); + await waitForHydration(page); await page.locator('#error-in-error-boundary').click(); const error = await errorPromise; @@ -77,6 +80,7 @@ test.describe('client-side errors', async () => { }); await page.goto(`/test-param/1234`); + await waitForHydration(page); await page.locator('#errorBtn').click(); const error = await errorPromise; @@ -106,6 +110,7 @@ test.describe('client-side errors', async () => { }); await page.goto(`/client-error`); + await waitForHydration(page); await page.locator('#errorBtn').click(); const error1 = await error1Promise; diff --git a/dev-packages/e2e-tests/test-applications/nuxt-4-static/tests/errors.server.test.ts b/dev-packages/e2e-tests/test-applications/nuxt-4-static/tests/errors.server.test.ts index b7dce20b0714..1111253ce495 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-4-static/tests/errors.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/nuxt-4-static/tests/errors.server.test.ts @@ -1,5 +1,6 @@ import { expect, test } from '@playwright/test'; import { waitForError } from '@sentry-internal/test-utils'; +import { waitForHydration } from './waitForHydration'; test.describe('server-side errors', async () => { test('captures api fetch error (fetched on click)', async ({ page }) => { @@ -8,6 +9,7 @@ test.describe('server-side errors', async () => { }); await page.goto(`/fetch-server-routes`); + await waitForHydration(page); await page.getByText('Fetch Server API Error', { exact: true }).click(); const error = await errorPromise; @@ -42,6 +44,7 @@ test.describe('server-side errors', async () => { }); await page.goto(`/test-param/1234`); + await waitForHydration(page); await page.getByRole('button', { name: 'Fetch Server API Error', exact: true }).click(); const error = await errorPromise; diff --git a/dev-packages/e2e-tests/test-applications/nuxt-4-static/tests/waitForHydration.ts b/dev-packages/e2e-tests/test-applications/nuxt-4-static/tests/waitForHydration.ts new file mode 100644 index 000000000000..71c53ecda4a5 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nuxt-4-static/tests/waitForHydration.ts @@ -0,0 +1,10 @@ +import type { Page } from '@playwright/test'; + +type NuxtRootContainer = HTMLElement & { __vue_app__?: { $nuxt?: { isHydrating?: boolean } } }; + +// Server-rendered elements are clickable before Vue hydration attaches their event handlers. +export async function waitForHydration(page: Page): Promise { + await page.waitForFunction( + () => (document.getElementById('__nuxt') as NuxtRootContainer | null)?.__vue_app__?.$nuxt?.isHydrating === false, + ); +} diff --git a/dev-packages/e2e-tests/test-applications/nuxt-4/tests/errors.client.test.ts b/dev-packages/e2e-tests/test-applications/nuxt-4/tests/errors.client.test.ts index 2aed77dbfed6..311c49dba8a9 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-4/tests/errors.client.test.ts +++ b/dev-packages/e2e-tests/test-applications/nuxt-4/tests/errors.client.test.ts @@ -1,5 +1,6 @@ import { expect, test } from '@playwright/test'; import { waitForError } from '@sentry-internal/test-utils'; +import { waitForHydration } from './waitForHydration'; test.describe('client-side errors', async () => { test('captures error thrown on click', async ({ page }) => { @@ -8,6 +9,7 @@ test.describe('client-side errors', async () => { }); await page.goto(`/client-error`); + await waitForHydration(page); await page.locator('#errorBtn').click(); const error = await errorPromise; @@ -35,6 +37,7 @@ test.describe('client-side errors', async () => { }); await page.goto(`/client-error`); + await waitForHydration(page); await page.locator('#error-in-error-boundary').click(); const error = await errorPromise; @@ -77,6 +80,7 @@ test.describe('client-side errors', async () => { }); await page.goto(`/test-param/1234`); + await waitForHydration(page); await page.locator('#errorBtn').click(); const error = await errorPromise; @@ -106,6 +110,7 @@ test.describe('client-side errors', async () => { }); await page.goto(`/client-error`); + await waitForHydration(page); await page.locator('#errorBtn').click(); const error1 = await error1Promise; diff --git a/dev-packages/e2e-tests/test-applications/nuxt-4/tests/errors.server.test.ts b/dev-packages/e2e-tests/test-applications/nuxt-4/tests/errors.server.test.ts index 418c09f16c57..928434c44b27 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-4/tests/errors.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/nuxt-4/tests/errors.server.test.ts @@ -1,6 +1,7 @@ import { expect, test } from '@playwright/test'; import { waitForError } from '@sentry-internal/test-utils'; import { IMPORT_SURFACES } from './importSurfaces'; +import { waitForHydration } from './waitForHydration'; IMPORT_SURFACES.forEach(({ name, apiPrefix }) => { test.describe(`server-side errors (${name})`, () => { @@ -10,6 +11,7 @@ IMPORT_SURFACES.forEach(({ name, apiPrefix }) => { }); await page.goto(`/fetch-server-routes?apiPrefix=${apiPrefix}`); + await waitForHydration(page); await page.getByText('Fetch Server API Error', { exact: true }).click(); const error = await errorPromise; @@ -44,6 +46,7 @@ IMPORT_SURFACES.forEach(({ name, apiPrefix }) => { }); await page.goto(`/test-param/1234?apiPrefix=${apiPrefix}`); + await waitForHydration(page); await page.getByRole('button', { name: 'Fetch Server API Error', exact: true }).click(); const error = await errorPromise; @@ -81,6 +84,7 @@ IMPORT_SURFACES.forEach(({ name, apiPrefix }) => { }); await page.goto(`/fetch-server-routes?apiPrefix=${apiPrefix}`); + await waitForHydration(page); await page.getByText('Fetch Third-Party HTTPError', { exact: true }).click(); const error = await errorPromise; diff --git a/dev-packages/e2e-tests/test-applications/nuxt-4/tests/waitForHydration.ts b/dev-packages/e2e-tests/test-applications/nuxt-4/tests/waitForHydration.ts new file mode 100644 index 000000000000..71c53ecda4a5 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nuxt-4/tests/waitForHydration.ts @@ -0,0 +1,10 @@ +import type { Page } from '@playwright/test'; + +type NuxtRootContainer = HTMLElement & { __vue_app__?: { $nuxt?: { isHydrating?: boolean } } }; + +// Server-rendered elements are clickable before Vue hydration attaches their event handlers. +export async function waitForHydration(page: Page): Promise { + await page.waitForFunction( + () => (document.getElementById('__nuxt') as NuxtRootContainer | null)?.__vue_app__?.$nuxt?.isHydrating === false, + ); +} diff --git a/dev-packages/e2e-tests/test-applications/nuxt-5/tests/errors.client.test.ts b/dev-packages/e2e-tests/test-applications/nuxt-5/tests/errors.client.test.ts index 2c6a1be53662..826a578a7e6c 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-5/tests/errors.client.test.ts +++ b/dev-packages/e2e-tests/test-applications/nuxt-5/tests/errors.client.test.ts @@ -1,5 +1,6 @@ import { expect, test } from '@playwright/test'; import { waitForError } from '@sentry-internal/test-utils'; +import { waitForHydration } from './waitForHydration'; test.describe('client-side errors', async () => { test('captures error thrown on click', async ({ page }) => { @@ -8,6 +9,7 @@ test.describe('client-side errors', async () => { }); await page.goto(`/client-error`); + await waitForHydration(page); await page.locator('#errorBtn').click(); const error = await errorPromise; @@ -35,6 +37,7 @@ test.describe('client-side errors', async () => { }); await page.goto(`/client-error`); + await waitForHydration(page); await page.locator('#error-in-error-boundary').click(); const error = await errorPromise; @@ -77,6 +80,7 @@ test.describe('client-side errors', async () => { }); await page.goto(`/test-param/1234`); + await waitForHydration(page); await page.locator('#errorBtn').click(); const error = await errorPromise; @@ -106,6 +110,7 @@ test.describe('client-side errors', async () => { }); await page.goto(`/client-error`); + await waitForHydration(page); await page.locator('#errorBtn').click(); const error1 = await error1Promise; diff --git a/dev-packages/e2e-tests/test-applications/nuxt-5/tests/errors.server.test.ts b/dev-packages/e2e-tests/test-applications/nuxt-5/tests/errors.server.test.ts index 1917990af924..2dcc253a98d1 100644 --- a/dev-packages/e2e-tests/test-applications/nuxt-5/tests/errors.server.test.ts +++ b/dev-packages/e2e-tests/test-applications/nuxt-5/tests/errors.server.test.ts @@ -1,6 +1,7 @@ import { expect, test } from '@playwright/test'; import { waitForError } from '@sentry-internal/test-utils'; import { IMPORT_SURFACES } from './importSurfaces'; +import { waitForHydration } from './waitForHydration'; IMPORT_SURFACES.forEach(({ name, apiPrefix }) => { test.describe(`server-side errors (${name})`, () => { @@ -10,6 +11,7 @@ IMPORT_SURFACES.forEach(({ name, apiPrefix }) => { }); await page.goto(`/fetch-server-routes?apiPrefix=${apiPrefix}`); + await waitForHydration(page); await page.getByText('Fetch Server API Error', { exact: true }).click(); const error = await errorPromise; @@ -44,6 +46,7 @@ IMPORT_SURFACES.forEach(({ name, apiPrefix }) => { }); await page.goto(`/test-param/1234?apiPrefix=${apiPrefix}`); + await waitForHydration(page); await page.getByRole('button', { name: 'Fetch Server API Error', exact: true }).click(); const error = await errorPromise; @@ -81,6 +84,7 @@ IMPORT_SURFACES.forEach(({ name, apiPrefix }) => { }); await page.goto(`/fetch-server-routes?apiPrefix=${apiPrefix}`); + await waitForHydration(page); await page.getByText('Fetch Third-Party HTTPError', { exact: true }).click(); const error = await errorPromise; diff --git a/dev-packages/e2e-tests/test-applications/nuxt-5/tests/waitForHydration.ts b/dev-packages/e2e-tests/test-applications/nuxt-5/tests/waitForHydration.ts new file mode 100644 index 000000000000..71c53ecda4a5 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nuxt-5/tests/waitForHydration.ts @@ -0,0 +1,10 @@ +import type { Page } from '@playwright/test'; + +type NuxtRootContainer = HTMLElement & { __vue_app__?: { $nuxt?: { isHydrating?: boolean } } }; + +// Server-rendered elements are clickable before Vue hydration attaches their event handlers. +export async function waitForHydration(page: Page): Promise { + await page.waitForFunction( + () => (document.getElementById('__nuxt') as NuxtRootContainer | null)?.__vue_app__?.$nuxt?.isHydrating === false, + ); +}