From 72b02c5c4cfced0b52d395fd2141803b712c335c Mon Sep 17 00:00:00 2001 From: Harlan Wilton Date: Wed, 16 Sep 2026 23:30:16 +1000 Subject: [PATCH] test(e2e): build fixtures with template comments stripped @nuxt/test-utils builds fixtures in the Vitest worker with NODE_ENV=test. The Vue compiler then loads its dev build and keeps template comments, so production-only hydration bugs such as comment-only templates never fail. Every e2e test now calls setupFixture(), which forces vue.compilerOptions.comments to false. An ESLint rule blocks direct setup() imports in test/e2e and test/e2e-dev. A shared probe fixture fails if the server HTML keeps a comment, or if a comment-only template stops reporting a hydration mismatch. --- eslint.config.mjs | 14 +++++++ package.json | 2 +- test/e2e-dev/first-party.test.ts | 5 ++- test/e2e/ahrefs-analytics-cdn.test.ts | 4 +- test/e2e/ahrefs-analytics.test.ts | 4 +- test/e2e/base.test.ts | 5 ++- test/e2e/basic.test.ts | 5 ++- test/e2e/calendly-cdn.test.ts | 4 +- test/e2e/calendly.test.ts | 4 +- test/e2e/cdn.test.ts | 5 ++- test/e2e/extend-registry.test.ts | 5 ++- .../issue-759-globals-env-override.test.ts | 5 ++- .../e2e/issue-783-proxy-token-payload.test.ts | 5 ++- test/e2e/linkedin-insight-cdn.test.ts | 4 +- test/e2e/linkedin-insight.test.ts | 4 +- test/e2e/map-hydration.test.ts | 20 +++------- test/e2e/maplibre-hydration.test.ts | 20 +++------- test/e2e/maplibre.test.ts | 5 ++- test/e2e/partytown.test.ts | 5 ++- test/e2e/production-compile.test.ts | 37 +++++++++++++++++++ test/e2e/proxy-alias.test.ts | 5 ++- test/e2e/speedcurve.test.ts | 4 +- test/e2e/tiktok-pixel.test.ts | 5 ++- test/e2e/usercentrics.test.ts | 4 +- .../map-hydration/pages/google-maps.vue | 1 - test/fixtures/map-hydration/pages/leaflet.vue | 1 - .../fixtures/map-hydration/pages/maplibre.vue | 1 - test/fixtures/maplibre/pages/controls.vue | 1 - test/fixtures/production-compile/app.vue | 7 ++++ .../components/CommentOnly.vue | 3 ++ .../production-compile/nuxt.config.ts | 6 +++ test/fixtures/production-compile/package.json | 1 + .../fixtures/production-compile/tsconfig.json | 3 ++ test/utils/setup-fixture.ts | 29 +++++++++++++++ 34 files changed, 160 insertions(+), 73 deletions(-) create mode 100644 test/e2e/production-compile.test.ts create mode 100644 test/fixtures/production-compile/app.vue create mode 100644 test/fixtures/production-compile/components/CommentOnly.vue create mode 100644 test/fixtures/production-compile/nuxt.config.ts create mode 100644 test/fixtures/production-compile/package.json create mode 100644 test/fixtures/production-compile/tsconfig.json create mode 100644 test/utils/setup-fixture.ts diff --git a/eslint.config.mjs b/eslint.config.mjs index 25d2c8307..18a34e6f4 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -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: { diff --git a/package.json b/package.json index 722ebb844..1740f1aca 100644 --- a/package.json +++ b/package.json @@ -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 .", diff --git a/test/e2e-dev/first-party.test.ts b/test/e2e-dev/first-party.test.ts index 18cf3ec70..947442e4b 100644 --- a/test/e2e-dev/first-party.test.ts +++ b/test/e2e-dev/first-party.test.ts @@ -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') @@ -22,7 +23,7 @@ async function tolerateBrowserRace(operation: Promise): Promise { - await setup({ + await setupFixture({ rootDir: resolve('../fixtures/ahrefs-analytics-cdn'), browser: true, }) diff --git a/test/e2e/ahrefs-analytics.test.ts b/test/e2e/ahrefs-analytics.test.ts index 87e4a075c..6ae3b9e0d 100644 --- a/test/e2e/ahrefs-analytics.test.ts +++ b/test/e2e/ahrefs-analytics.test.ts @@ -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, }) diff --git a/test/e2e/base.test.ts b/test/e2e/base.test.ts index dead02b16..f262110fd 100644 --- a/test/e2e/base.test.ts +++ b/test/e2e/base.test.ts @@ -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, diff --git a/test/e2e/basic.test.ts b/test/e2e/basic.test.ts index b48427707..117ddbbe0 100644 --- a/test/e2e/basic.test.ts +++ b/test/e2e/basic.test.ts @@ -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, diff --git a/test/e2e/calendly-cdn.test.ts b/test/e2e/calendly-cdn.test.ts index fb603b898..6b9ffd320 100644 --- a/test/e2e/calendly-cdn.test.ts +++ b/test/e2e/calendly-cdn.test.ts @@ -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, }) diff --git a/test/e2e/calendly.test.ts b/test/e2e/calendly.test.ts index 2a22943cf..f9f6c29dc 100644 --- a/test/e2e/calendly.test.ts +++ b/test/e2e/calendly.test.ts @@ -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, }) diff --git a/test/e2e/cdn.test.ts b/test/e2e/cdn.test.ts index 0a1171701..182ce666d 100644 --- a/test/e2e/cdn.test.ts +++ b/test/e2e/cdn.test.ts @@ -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: { diff --git a/test/e2e/extend-registry.test.ts b/test/e2e/extend-registry.test.ts index 8036f6b81..cc35c8b46 100644 --- a/test/e2e/extend-registry.test.ts +++ b/test/e2e/extend-registry.test.ts @@ -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 () => { diff --git a/test/e2e/issue-759-globals-env-override.test.ts b/test/e2e/issue-759-globals-env-override.test.ts index e471bfaf6..b758e822e 100644 --- a/test/e2e/issue-759-globals-env-override.test.ts +++ b/test/e2e/issue-759-globals-env-override.test.ts @@ -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) @@ -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, diff --git a/test/e2e/issue-783-proxy-token-payload.test.ts b/test/e2e/issue-783-proxy-token-payload.test.ts index 44b6e00f0..caad07a46 100644 --- a/test/e2e/issue-783-proxy-token-payload.test.ts +++ b/test/e2e/issue-783-proxy-token-payload.test.ts @@ -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) @@ -8,7 +9,7 @@ const { resolve } = createResolver(import.meta.url) // 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, diff --git a/test/e2e/linkedin-insight-cdn.test.ts b/test/e2e/linkedin-insight-cdn.test.ts index 590aaec95..49b1fb7a4 100644 --- a/test/e2e/linkedin-insight-cdn.test.ts +++ b/test/e2e/linkedin-insight-cdn.test.ts @@ -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, }) diff --git a/test/e2e/linkedin-insight.test.ts b/test/e2e/linkedin-insight.test.ts index 01ada09f9..7713f1d8b 100644 --- a/test/e2e/linkedin-insight.test.ts +++ b/test/e2e/linkedin-insight.test.ts @@ -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, }) diff --git a/test/e2e/map-hydration.test.ts b/test/e2e/map-hydration.test.ts index 444f10cab..6af0abc6a 100644 --- a/test/e2e/map-hydration.test.ts +++ b/test/e2e/map-hydration.test.ts @@ -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) @@ -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(path) - expect(html).toContain('
') - expect(html).not.toContain('production-build-probe') }) it.each(pages)('hydrates %s without a mismatch', async (path) => { diff --git a/test/e2e/maplibre-hydration.test.ts b/test/e2e/maplibre-hydration.test.ts index a10917498..3d99e3480 100644 --- a/test/e2e/maplibre-hydration.test.ts +++ b/test/e2e/maplibre-hydration.test.ts @@ -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) @@ -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('/controls') - expect(html).not.toContain('production-build-probe') }) it('hydrates every control component without a mismatch', async () => { diff --git a/test/e2e/maplibre.test.ts b/test/e2e/maplibre.test.ts index c71296398..a20dbe127 100644 --- a/test/e2e/maplibre.test.ts +++ b/test/e2e/maplibre.test.ts @@ -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) @@ -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, }) diff --git a/test/e2e/partytown.test.ts b/test/e2e/partytown.test.ts index 3a906e24b..cc66f703d 100644 --- a/test/e2e/partytown.test.ts +++ b/test/e2e/partytown.test.ts @@ -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, }) diff --git a/test/e2e/production-compile.test.ts b/test/e2e/production-compile.test.ts new file mode 100644 index 000000000..815fa8bd4 --- /dev/null +++ b/test/e2e/production-compile.test.ts @@ -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('/') + 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() + }) +}) diff --git a/test/e2e/proxy-alias.test.ts b/test/e2e/proxy-alias.test.ts index 33ac6be85..5e95e5cc9 100644 --- a/test/e2e/proxy-alias.test.ts +++ b/test/e2e/proxy-alias.test.ts @@ -1,13 +1,14 @@ import { createResolver } from '@nuxt/kit' -import { $fetch, setup, url } from '@nuxt/test-utils/e2e' +import { $fetch, url } from '@nuxt/test-utils/e2e' import { describe, expect, it } from 'vitest' +import { setupFixture } from '../utils/setup-fixture' const { resolve } = createResolver(import.meta.url) // End-to-end coverage for proxy path aliases (#814). Builds a real app with // `scripts.proxy.alias` set and verifies the full module wiring: the alias reaches // both the auto-injected endpoint config and the runtime proxy handler. -await setup({ +await setupFixture({ rootDir: resolve('../fixtures/proxy-alias'), build: true, }) diff --git a/test/e2e/speedcurve.test.ts b/test/e2e/speedcurve.test.ts index c82afb28c..fec1134fc 100644 --- a/test/e2e/speedcurve.test.ts +++ b/test/e2e/speedcurve.test.ts @@ -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 { defineSpeedCurveSuite } from './_speedcurve-suite' const { resolve } = createResolver(import.meta.url) describe('speedcurve', { timeout: 15000 }, async () => { - await setup({ + await setupFixture({ rootDir: resolve('../fixtures/speedcurve'), browser: true, }) diff --git a/test/e2e/tiktok-pixel.test.ts b/test/e2e/tiktok-pixel.test.ts index 09d90c746..0ec2f8076 100644 --- a/test/e2e/tiktok-pixel.test.ts +++ b/test/e2e/tiktok-pixel.test.ts @@ -1,6 +1,7 @@ import { createResolver } from '@nuxt/kit' -import { getBrowser, setup, url } from '@nuxt/test-utils/e2e' +import { getBrowser, url } from '@nuxt/test-utils/e2e' import { beforeAll, describe, expect, it } from 'vitest' +import { setupFixture } from '../utils/setup-fixture' const { resolve } = createResolver(import.meta.url) @@ -39,7 +40,7 @@ async function waitFor( } describe('tiktokPixel', async () => { - await setup({ + await setupFixture({ rootDir: resolve('../fixtures/tiktok-pixel'), browser: true, }) diff --git a/test/e2e/usercentrics.test.ts b/test/e2e/usercentrics.test.ts index a16d1ee36..df39e6c41 100644 --- a/test/e2e/usercentrics.test.ts +++ b/test/e2e/usercentrics.test.ts @@ -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 { defineUsercentricsSuite } from './_usercentrics-suite' const { resolve } = createResolver(import.meta.url) describe('usercentrics (CMP v3 loader served from web.cmp.usercentrics.eu)', async () => { - await setup({ + await setupFixture({ rootDir: resolve('../fixtures/usercentrics'), browser: true, }) diff --git a/test/fixtures/map-hydration/pages/google-maps.vue b/test/fixtures/map-hydration/pages/google-maps.vue index e7b51a2c4..fd1de680a 100644 --- a/test/fixtures/map-hydration/pages/google-maps.vue +++ b/test/fixtures/map-hydration/pages/google-maps.vue @@ -1,6 +1,5 @@ diff --git a/test/fixtures/production-compile/components/CommentOnly.vue b/test/fixtures/production-compile/components/CommentOnly.vue new file mode 100644 index 000000000..219d40e9e --- /dev/null +++ b/test/fixtures/production-compile/components/CommentOnly.vue @@ -0,0 +1,3 @@ + diff --git a/test/fixtures/production-compile/nuxt.config.ts b/test/fixtures/production-compile/nuxt.config.ts new file mode 100644 index 000000000..d5d1efecf --- /dev/null +++ b/test/fixtures/production-compile/nuxt.config.ts @@ -0,0 +1,6 @@ +import { defineNuxtConfig } from 'nuxt/config' + +// No modules: this fixture tests the e2e build setup, not `@nuxt/scripts`. +export default defineNuxtConfig({ + compatibilityDate: '2024-07-05', +}) diff --git a/test/fixtures/production-compile/package.json b/test/fixtures/production-compile/package.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/test/fixtures/production-compile/package.json @@ -0,0 +1 @@ +{} diff --git a/test/fixtures/production-compile/tsconfig.json b/test/fixtures/production-compile/tsconfig.json new file mode 100644 index 000000000..4b34df157 --- /dev/null +++ b/test/fixtures/production-compile/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./.nuxt/tsconfig.json" +} diff --git a/test/utils/setup-fixture.ts b/test/utils/setup-fixture.ts new file mode 100644 index 000000000..3f290b09a --- /dev/null +++ b/test/utils/setup-fixture.ts @@ -0,0 +1,29 @@ +import type { TestOptions } from '@nuxt/test-utils/e2e' +import { setup } from '@nuxt/test-utils/e2e' +import { defu } from 'defu' + +/** + * Compile settings that a production build applies and a fixture build does not. + * + * `@nuxt/test-utils` builds fixtures inside the Vitest worker, where `NODE_ENV` is + * `test`. `@vue/compiler-core` then loads its development build, and its + * `comments` option defaults to `true`. A production build strips template + * comments, so a bug that depends on a missing comment node cannot appear. + * + * `test/e2e/production-compile.test.ts` fails if this setting stops taking effect. + */ +export const productionCompileConfig = { + vue: { compilerOptions: { comments: false } }, +} + +/** + * Wraps `setup()` from `@nuxt/test-utils/e2e` so every e2e fixture compiles + * templates like a production build. The production settings override the + * caller's `nuxtConfig`. + */ +export function setupFixture(options: Partial): Promise { + return setup({ + ...options, + nuxtConfig: defu(productionCompileConfig, options.nuxtConfig), + }) +}