diff --git a/src/config.ts b/src/config.ts index dec9449ee..de0ed9df2 100644 --- a/src/config.ts +++ b/src/config.ts @@ -133,8 +133,9 @@ const excludedPlugins = [ export async function getVitestConfigFromNuxt( options?: GetVitestConfigOptions, loadNuxtOptions: LoadNuxtOptions = {}, - testConfig: VitestConfig = {}, + userConfig: ViteUserConfig & { test?: VitestConfig } | UserWorkspaceConfig = {}, ): Promise { + const { test: testConfig = {} } = userConfig const { rootDir = process.cwd(), ..._overrides } = loadNuxtOptions.overrides || {} if (!options) { @@ -173,7 +174,20 @@ export async function getVitestConfigFromNuxt( }, }, optimizeDeps: { - noDiscovery: true, + ...vitestMajor >= 5 && testConfig.browser?.enabled + ? { + // Browser mode in vitest 5 reuses the project's Vite server: dependency + // discovery must stay on so vitest's CJS internals are prebundled, and deps + // imported through nuxt virtual modules are listed upfront because they + // can't be found by vite automatically which reloads the page. + // We leave `noDiscovery` unchanged to respect vitest defaults and user config. + include: ['@testing-library/vue', '#nuxt-test-utils/h3'], + // aliased to mocks, excluded from pre-bundling to avoid runtime discovery + exclude: ['@vue/devtools-kit', '@vue/devtools-core'], + } + : { + noDiscovery: true, + }, }, test: { environmentOptions: { @@ -437,27 +451,12 @@ async function resolveConfig }, + }, config) satisfies ViteUserConfig & { test: NonNullable }, ) as T & { test: NonNullable } resolvedConfig.plugins!.push(NuxtVitestEnvironmentOptionsPlugin(resolvedConfig.test.environmentOptions)) if (resolvedConfig.test.browser?.enabled) { - // browser mode in vitest 5 reuses the project's Vite server: dependency - // discovery must stay on so vitest's CJS internals are prebundled, and deps - // imported through nuxt virtual modules are listed upfront because they - // can't be found by vite automatically which reloads the page - if (vitestMajor >= 5) { - delete resolvedConfig.optimizeDeps?.noDiscovery - resolvedConfig.optimizeDeps ??= {} - resolvedConfig.optimizeDeps.include ??= [] - resolvedConfig.optimizeDeps.include.push('@testing-library/vue', '#nuxt-test-utils/h3') - - // aliased to mocks, excluded from pre-bundling to avoid runtime discovery - resolvedConfig.optimizeDeps.exclude ??= [] - resolvedConfig.optimizeDeps.exclude.push('@vue/devtools-kit', '@vue/devtools-core') - } - if (resolvedConfig.test.environment === 'nuxt') { resolvedConfig.test.setupFiles = Array.isArray(resolvedConfig.test.setupFiles) ? resolvedConfig.test.setupFiles diff --git a/src/module/plugins/entry.ts b/src/module/plugins/entry.ts index 50e5f7493..9187cdc01 100644 --- a/src/module/plugins/entry.ts +++ b/src/module/plugins/entry.ts @@ -24,15 +24,15 @@ export const NuxtRootStubPlugin = (options: NuxtRootStubPluginOptions): Plugin = filter: { id: idFilter, }, - async handler(id, importer) { - return importer?.endsWith('index.html') ? id : entryPath + handler() { + return entryPath }, }, load: { filter: { id: idFilter, }, - async handler() { + handler() { const entryContents = readFileSync(options.entry, 'utf-8') return entryContents.replace('#build/root-component.mjs', options.rootStubPath) }, diff --git a/test/fixtures/isolate-setup/global.setup.ts b/test/fixtures/isolate-setup/global.setup.ts new file mode 100644 index 000000000..9cfb5a242 --- /dev/null +++ b/test/fixtures/isolate-setup/global.setup.ts @@ -0,0 +1,11 @@ +import type { TestProject } from 'vitest/node' + +export default function setup(project: TestProject) { + project.provide('testPattern', process.env.ERROR_TEST_PATTERN) +} + +declare module 'vitest' { + export interface ProvidedContext { + testPattern: string | undefined + } +} diff --git a/test/fixtures/isolate-setup/vitest.config.ts b/test/fixtures/isolate-setup/vitest.config.ts index 8c757a0e5..0530d52b5 100644 --- a/test/fixtures/isolate-setup/vitest.config.ts +++ b/test/fixtures/isolate-setup/vitest.config.ts @@ -22,6 +22,8 @@ export default defineVitestConfig({ instances: [{ browser: 'chromium' }], }, ui: false, + fsModuleCache: true, + globalSetup: './global.setup.ts', onConsoleLog(log) { if (log.includes(' is an experimental feature')) { return false @@ -33,9 +35,7 @@ export default defineVitestConfig({ enforce: 'pre', transform(code, id) { if (id !== setupNuxtPath) return - const failOnSetupBefore = process.env.ERROR_TEST_PATTERN === 'before' - const failOnSetupAfter = process.env.ERROR_TEST_PATTERN === 'after' - return code + return 'import { inject } from "vitest";\n' + code .replace(/export async function setupNuxt/, 'async function _setupNuxt') .concat(` export async function setupNuxt(...args) { @@ -46,9 +46,9 @@ export async function setupNuxt(...args) { console.log(\`### setupNuxt called ### \${window.__setup_calls__}:\${workerId}\`) if (window.__setup_calls__ === 1) { - if (${failOnSetupBefore}) { + if (inject('testPattern') === 'before') { throw new Error('#### setupNuxt failed before ###') - } else if (${failOnSetupAfter}) { + } else if (inject('testPattern') === 'after') { await _setupNuxt(...args) throw new Error('#### setupNuxt failed after ###') }