Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,20 @@ export default antfu(
'e18e/prefer-static-regex': 'off',
},
},
{
// `setupFixture()` builds fixtures like production. A direct `setup()` call
// keeps template comments and hides production-only hydration bugs.
files: ['test/e2e/**', 'test/e2e-dev/**'],
rules: {
'no-restricted-imports': ['error', {
paths: [{
name: '@nuxt/test-utils/e2e',
importNames: ['setup'],
message: 'Use setupFixture() from test/utils/setup-fixture.ts. It compiles fixtures like a production build.',
}],
}],
},
},
...harlanzw({ link: true, nuxt: true, vue: true, content: true }),
{
rules: {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"dev": "nuxt dev playground",
"dev:ssl": "nuxt dev playground --https",
"dev:prepare": "pnpm -r dev:prepare && nuxt prepare && nuxt prepare playground && pnpm prepare:fixtures",
"prepare:fixtures": "nuxt prepare test/fixtures/basic && nuxt prepare test/fixtures/cdn && nuxt prepare test/fixtures/extend-registry && nuxt prepare test/fixtures/partytown && nuxt prepare test/fixtures/first-party && nuxt prepare test/fixtures/linkedin-insight && nuxt prepare test/fixtures/linkedin-insight-cdn && nuxt prepare test/fixtures/tiktok-pixel && nuxt prepare test/fixtures/calendly && nuxt prepare test/fixtures/calendly-cdn && nuxt prepare test/fixtures/ahrefs-analytics && nuxt prepare test/fixtures/ahrefs-analytics-cdn && nuxt prepare test/fixtures/usercentrics && nuxt prepare test/fixtures/speedcurve && nuxt prepare test/fixtures/maplibre && nuxt prepare test/fixtures/map-hydration",
"prepare:fixtures": "nuxt prepare test/fixtures/basic && nuxt prepare test/fixtures/cdn && nuxt prepare test/fixtures/extend-registry && nuxt prepare test/fixtures/partytown && nuxt prepare test/fixtures/first-party && nuxt prepare test/fixtures/linkedin-insight && nuxt prepare test/fixtures/linkedin-insight-cdn && nuxt prepare test/fixtures/tiktok-pixel && nuxt prepare test/fixtures/calendly && nuxt prepare test/fixtures/calendly-cdn && nuxt prepare test/fixtures/ahrefs-analytics && nuxt prepare test/fixtures/ahrefs-analytics-cdn && nuxt prepare test/fixtures/usercentrics && nuxt prepare test/fixtures/speedcurve && nuxt prepare test/fixtures/maplibre && nuxt prepare test/fixtures/map-hydration && nuxt prepare test/fixtures/production-compile",
"typecheck": "pnpm --filter @nuxt/scripts-cli typecheck && nuxt typecheck",
"release": "pnpm build && bumpp -r --output=CHANGELOG.md",
"lint": "eslint .",
Expand Down
5 changes: 3 additions & 2 deletions test/e2e-dev/first-party.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import { existsSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'no
import { join } from 'node:path'
import { createResolver } from '@nuxt/kit'
import { getProxyDef, registry } from '@nuxt/scripts/registry'
import { $fetch, getBrowser, setup, url } from '@nuxt/test-utils/e2e'
import { $fetch, getBrowser, url } from '@nuxt/test-utils/e2e'
import { afterAll, beforeAll, describe, expect, it } from 'vitest'
import { setupFixture } from '../utils/setup-fixture'

const { resolve } = createResolver(import.meta.url)
const fixtureDir = resolve('../fixtures/first-party')
Expand All @@ -22,7 +23,7 @@ async function tolerateBrowserRace<T>(operation: Promise<T>): Promise<T | undefi
}
}

await setup({
await setupFixture({
rootDir: fixtureDir,
browser: true,
build: true,
Expand Down
4 changes: 2 additions & 2 deletions test/e2e/ahrefs-analytics-cdn.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { createResolver } from '@nuxt/kit'
import { setup } from '@nuxt/test-utils/e2e'
import { describe } from 'vitest'
import { setupFixture } from '../utils/setup-fixture'
import { defineAhrefsAnalyticsSuite } from './_ahrefs-analytics-suite'

const { resolve } = createResolver(import.meta.url)

describe('ahrefsAnalytics (unbundled — script served from analytics.ahrefs.com)', async () => {
await setup({
await setupFixture({
rootDir: resolve('../fixtures/ahrefs-analytics-cdn'),
browser: true,
})
Expand Down
4 changes: 2 additions & 2 deletions test/e2e/ahrefs-analytics.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { createResolver } from '@nuxt/kit'
import { setup } from '@nuxt/test-utils/e2e'
import { describe } from 'vitest'
import { setupFixture } from '../utils/setup-fixture'
import { defineAhrefsAnalyticsSuite } from './_ahrefs-analytics-suite'

const { resolve } = createResolver(import.meta.url)

describe('ahrefsAnalytics (bundled — script served from /_scripts/assets/)', async () => {
await setup({
await setupFixture({
rootDir: resolve('../fixtures/ahrefs-analytics'),
browser: true,
})
Expand Down
5 changes: 3 additions & 2 deletions test/e2e/base.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { createResolver } from '@nuxt/kit'
import { createPage, setup } from '@nuxt/test-utils/e2e'
import { createPage } from '@nuxt/test-utils/e2e'
import { describe, expect, it } from 'vitest'
import { setupFixture } from '../utils/setup-fixture'

const { resolve } = createResolver(import.meta.url)

describe('base', async () => {
await setup({
await setupFixture({
rootDir: resolve('../fixtures/basic'),
// dev: true,
browser: true,
Expand Down
5 changes: 3 additions & 2 deletions test/e2e/basic.test.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import type { Page } from 'playwright-core'
import { join } from 'node:path'
import { createResolver } from '@nuxt/kit'
import { getBrowser, setup, url, waitForHydration } from '@nuxt/test-utils/e2e'
import { getBrowser, url, waitForHydration } from '@nuxt/test-utils/e2e'
import { parseURL } from 'ufo'
import { afterEach, describe, expect, it } from 'vitest'
import { setupFixture } from '../utils/setup-fixture'

const { resolve } = createResolver(import.meta.url)

await setup({
await setupFixture({
rootDir: resolve('../fixtures/basic'),
// dev: true,
browser: true,
Expand Down
4 changes: 2 additions & 2 deletions test/e2e/calendly-cdn.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { createResolver } from '@nuxt/kit'
import { setup } from '@nuxt/test-utils/e2e'
import { describe } from 'vitest'
import { setupFixture } from '../utils/setup-fixture'
import { defineCalendlySuite } from './_calendly-suite'

const { resolve } = createResolver(import.meta.url)

describe('calendly (unbundled — script served from assets.calendly.com)', async () => {
await setup({
await setupFixture({
rootDir: resolve('../fixtures/calendly-cdn'),
browser: true,
})
Expand Down
4 changes: 2 additions & 2 deletions test/e2e/calendly.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { createResolver } from '@nuxt/kit'
import { setup } from '@nuxt/test-utils/e2e'
import { describe } from 'vitest'
import { setupFixture } from '../utils/setup-fixture'
import { defineCalendlySuite } from './_calendly-suite'

const { resolve } = createResolver(import.meta.url)

describe('calendly (bundled — script served from /_scripts/assets/)', async () => {
await setup({
await setupFixture({
rootDir: resolve('../fixtures/calendly'),
browser: true,
})
Expand Down
5 changes: 3 additions & 2 deletions test/e2e/cdn.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { createResolver } from '@nuxt/kit'
import { $fetch, setup } from '@nuxt/test-utils/e2e'
import { $fetch } from '@nuxt/test-utils/e2e'
import { describe, expect, it } from 'vitest'
import { setupFixture } from '../utils/setup-fixture'

const { resolve } = createResolver(import.meta.url)

describe('cdnURL', async () => {
await setup({
await setupFixture({
rootDir: resolve('../fixtures/cdn'),
nuxtConfig: {
nitro: {
Expand Down
5 changes: 3 additions & 2 deletions test/e2e/extend-registry.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { createResolver } from '@nuxt/kit'
import { createPage, setup, url } from '@nuxt/test-utils/e2e'
import { createPage, url } from '@nuxt/test-utils/e2e'
import { parseURL } from 'ufo'
import { describe, expect, it } from 'vitest'
import { setupFixture } from '../utils/setup-fixture'

const { resolve } = createResolver(import.meta.url)

describe('basic', async () => {
await setup({
await setupFixture({
rootDir: resolve('../fixtures/extend-registry'),
})
it('extended registry script loads and executes function', { timeout: 30000 }, async () => {
Expand Down
5 changes: 3 additions & 2 deletions test/e2e/issue-759-globals-env-override.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createResolver } from '@nuxt/kit'
import { $fetch, setup } from '@nuxt/test-utils/e2e'
import { $fetch } from '@nuxt/test-utils/e2e'
import { describe, expect, it } from 'vitest'
import { setupFixture } from '../utils/setup-fixture'

const { resolve } = createResolver(import.meta.url)

Expand All @@ -11,7 +12,7 @@ process.env.NUXT_PUBLIC_SCRIPTS_GLOBALS_TRUSTED_SHOPS_SRC = 'https://widgets.tru
// Empty src disables the global for this instance (multi-tenant single build).
process.env.NUXT_PUBLIC_SCRIPTS_GLOBALS_AWIN_SRC = ''

await setup({
await setupFixture({
rootDir: resolve('../fixtures/issue-759'),
dev: true,
browser: false,
Expand Down
5 changes: 3 additions & 2 deletions test/e2e/issue-783-proxy-token-payload.test.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { createResolver } from '@nuxt/kit'
import { $fetch, setup } from '@nuxt/test-utils/e2e'
import { $fetch } from '@nuxt/test-utils/e2e'
import { describe, expect, it } from 'vitest'
import { setupFixture } from '../utils/setup-fixture'

const { resolve } = createResolver(import.meta.url)

// https://github.com/nuxt/scripts/issues/783
// Proxy URL signing and the per-request page token were removed. Static Maps
// now uses the public, application-restricted key directly, so the SSR payload
// is identical across requests (which a response `etag` can rely on).
await setup({
await setupFixture({
rootDir: resolve('../fixtures/issue-783'),
dev: true,
browser: false,
Expand Down
4 changes: 2 additions & 2 deletions test/e2e/linkedin-insight-cdn.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { createResolver } from '@nuxt/kit'
import { setup } from '@nuxt/test-utils/e2e'
import { describe } from 'vitest'
import { setupFixture } from '../utils/setup-fixture'
import { defineLinkedInInsightSuite } from './_linkedin-insight-suite'

const { resolve } = createResolver(import.meta.url)

describe('linkedinInsight (unbundled — script served from snap.licdn.com)', async () => {
await setup({
await setupFixture({
rootDir: resolve('../fixtures/linkedin-insight-cdn'),
browser: true,
})
Expand Down
4 changes: 2 additions & 2 deletions test/e2e/linkedin-insight.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { createResolver } from '@nuxt/kit'
import { setup } from '@nuxt/test-utils/e2e'
import { describe } from 'vitest'
import { setupFixture } from '../utils/setup-fixture'
import { defineLinkedInInsightSuite } from './_linkedin-insight-suite'

const { resolve } = createResolver(import.meta.url)

describe('linkedinInsight (bundled — script served from /_scripts/assets/)', async () => {
await setup({
await setupFixture({
rootDir: resolve('../fixtures/linkedin-insight'),
browser: true,
})
Expand Down
20 changes: 5 additions & 15 deletions test/e2e/map-hydration.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createResolver } from '@nuxt/kit'
import { $fetch, createPage, setup, url } from '@nuxt/test-utils/e2e'
import { createPage, url } from '@nuxt/test-utils/e2e'
import { describe, expect, it } from 'vitest'
import { setupFixture } from '../utils/setup-fixture'

const { resolve } = createResolver(import.meta.url)

Expand All @@ -10,26 +11,15 @@ const { resolve } = createResolver(import.meta.url)
* the server while the client expects a comment node, so hydration reports a
* mismatch.
*
* `@nuxt/test-utils` builds inside the Vitest worker, where `NODE_ENV` is `test`.
* `@vue/compiler-core` picks its development build there, and its `comments`
* option defaults to `true`, so a comment-only template cannot fail. Setting
* `comments: false` applies the production default. The probe test proves it.
* `setupFixture()` strips template comments like a production build, so this
* mismatch can appear. `production-compile.test.ts` proves the setting works.
*/
const pages = ['/maplibre', '/leaflet', '/google-maps']

describe('map component hydration in a production build', { timeout: 120000 }, async () => {
await setup({
await setupFixture({
rootDir: resolve('../fixtures/map-hydration'),
browser: true,
nuxtConfig: {
vue: { compilerOptions: { comments: false } },
},
})

it.each(pages)('strips template comments from %s, like a production build', async (path) => {
const html = await $fetch<string>(path)
expect(html).toContain('<div id="__nuxt">')
expect(html).not.toContain('production-build-probe')
})

it.each(pages)('hydrates %s without a mismatch', async (path) => {
Expand Down
20 changes: 5 additions & 15 deletions test/e2e/maplibre-hydration.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createResolver } from '@nuxt/kit'
import { $fetch, createPage, setup, url } from '@nuxt/test-utils/e2e'
import { createPage, url } from '@nuxt/test-utils/e2e'
import { describe, expect, it } from 'vitest'
import { setupFixture } from '../utils/setup-fixture'

const { resolve } = createResolver(import.meta.url)

Expand All @@ -9,24 +10,13 @@ const { resolve } = createResolver(import.meta.url)
* template holds only a comment then renders nothing on the server, while the
* client expects a comment node, so hydration reports a mismatch.
*
* `@nuxt/test-utils` builds inside the Vitest worker, where `NODE_ENV` is `test`.
* `@vue/compiler-core` picks its development build there, and its `comments`
* option defaults to `true`, so the bug cannot appear. Setting `comments: false`
* applies the production default. The first test proves it took effect.
* `setupFixture()` strips template comments like a production build, so this
* mismatch can appear. `production-compile.test.ts` proves the setting works.
*/
describe('maplibre hydration in a production build', { timeout: 120000 }, async () => {
await setup({
await setupFixture({
rootDir: resolve('../fixtures/maplibre'),
browser: true,
nuxtConfig: {
vue: { compilerOptions: { comments: false } },
},
})

it('compiles the fixture in production mode', async () => {
// Guards the guard: a development compile keeps this template comment.
const html = await $fetch<string>('/controls')
expect(html).not.toContain('production-build-probe')
})

it('hydrates every control component without a mismatch', async () => {
Expand Down
5 changes: 3 additions & 2 deletions test/e2e/maplibre.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import type { Page } from 'playwright-core'
import { createResolver } from '@nuxt/kit'
import { createPage, setup } from '@nuxt/test-utils/e2e'
import { createPage } from '@nuxt/test-utils/e2e'
import { describe, expect, it } from 'vitest'
import { setupFixture } from '../utils/setup-fixture'

const { resolve } = createResolver(import.meta.url)

Expand Down Expand Up @@ -42,7 +43,7 @@ async function waitForRenderedFeatures(page: Page, layers: string[], count: numb
}

describe('maplibre in a real browser', { timeout: 60000 }, async () => {
await setup({
await setupFixture({
rootDir: resolve('../fixtures/maplibre'),
browser: true,
})
Expand Down
5 changes: 3 additions & 2 deletions test/e2e/partytown.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { createResolver } from '@nuxt/kit'
import { getBrowser, setup, url, waitForHydration } from '@nuxt/test-utils/e2e'
import { getBrowser, url, waitForHydration } from '@nuxt/test-utils/e2e'
import { describe, expect, it } from 'vitest'
import { setupFixture } from '../utils/setup-fixture'

const { resolve } = createResolver(import.meta.url)

await setup({
await setupFixture({
rootDir: resolve('../fixtures/partytown'),
browser: true,
})
Expand Down
37 changes: 37 additions & 0 deletions test/e2e/production-compile.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { createResolver } from '@nuxt/kit'
import { $fetch, createPage, url } from '@nuxt/test-utils/e2e'
import { describe, expect, it } from 'vitest'
import { setupFixture } from '../utils/setup-fixture'

const { resolve } = createResolver(import.meta.url)

/**
* Guards `setupFixture()`. Every e2e fixture must compile templates like a
* production build, or production-only hydration bugs cannot fail a test.
*
* If one of these tests fails, the fixture build keeps template comments again.
* Fix `test/utils/setup-fixture.ts`. Do not change these tests.
*/
describe('e2e fixtures compile like a production build', { timeout: 120000 }, async () => {
await setupFixture({
rootDir: resolve('../fixtures/production-compile'),
browser: true,
})

it('strips template comments from the server HTML', async () => {
const html = await $fetch<string>('/')
expect(html).toContain('Production compile probe')
expect(html).not.toContain('production-compile-probe')
})

it('reports the hydration mismatch of a comment-only template', async () => {
const page = await createPage()
const messages: string[] = []
page.on('console', message => messages.push(`${message.type()}: ${message.text()}`))
await page.goto(url('/'), { waitUntil: 'hydration' })

// A known bug shape must fail visibly. Without comment stripping, it hydrates cleanly.
expect(messages.filter(message => /hydrat|mismatch/i.test(message))).not.toEqual([])
await page.close()
})
})
Loading
Loading