Skip to content

Commit c74b2f5

Browse files
committed
fix(module): devtool test execution compatible with vitest ui
1 parent 4c8a3b7 commit c74b2f5

File tree

5 files changed

+27
-78
lines changed

5 files changed

+27
-78
lines changed

examples/app-vitest-full/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"dev:prepare": "nuxt prepare",
99
"generate": "nuxt generate",
1010
"preview": "nuxt preview",
11-
"test:dev": "NUXT_VITEST_DEV_TEST=true nuxt dev --no-fork",
11+
"test:dev": "nuxt dev --no-fork",
1212
"test:unit": "vitest",
1313
"test:types": "nuxi prepare && vue-tsc --noEmit",
1414
"test:jsdom": "VITEST_DOM_ENV=jsdom pnpm test:unit --run",

examples/app-vitest-full/tests/nuxt/render-suspended.spec.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,7 @@ const formats = {
3333

3434
describe('renderSuspended', () => {
3535
afterEach(() => {
36-
// since we're not running with Vitest globals when running the tests
37-
// from inside the test server. This means testing-library cannot
38-
// auto-attach the cleanup go testing globals, and we have to do
39-
// it here manually.
40-
if (process.env.NUXT_VITEST_DEV_TEST) {
41-
cleanup()
42-
}
36+
cleanup()
4337
})
4438

4539
it('can render components within nuxt suspense', async () => {

examples/app-vitest-full/tests/resolved-files.spec.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ import { fileURLToPath } from 'node:url'
22
import { test, expect } from 'vitest'
33
import { createVitest } from 'vitest/node'
44

5-
// TODO: Investigate why this test fails when executed by the dev server. Once it's fixed, we can reenable this test.
6-
test.skipIf(process.env.NUXT_VITEST_DEV_TEST)('it should include nuxt spec files', { timeout: 30000 }, async () => {
5+
test('it should include nuxt spec files', { timeout: 30000 }, async () => {
76
const vitest = await createVitest('test', {
87
config: fileURLToPath(new URL('../vitest.config.ts', import.meta.url)),
98
dir: fileURLToPath(new URL('../', import.meta.url)),

src/config.ts

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { defineConfig } from 'vite'
66
import type { TestProjectInlineConfiguration } from 'vitest/config'
77
import { setupDotenv } from 'c12'
88
import type { DotenvOptions } from 'c12'
9-
import type { UserConfig as ViteUserConfig } from 'vite'
9+
import type { UserConfigFnPromise, UserConfig as ViteUserConfig } from 'vite'
1010
import type { DateString } from 'compatx'
1111
import { defu } from 'defu'
1212
import { createResolver, findPath } from '@nuxt/kit'
@@ -119,7 +119,6 @@ export async function getVitestConfigFromNuxt(
119119
noDiscovery: true,
120120
},
121121
test: {
122-
dir: process.cwd(),
123122
environmentOptions: {
124123
nuxtRuntimeConfig: applyEnv(structuredClone(options.nuxt.options.runtimeConfig), {
125124
prefix: 'NUXT_',
@@ -224,22 +223,16 @@ export async function getVitestConfigFromNuxt(
224223
return resolvedConfig
225224
}
226225

227-
export async function defineVitestProject(config: TestProjectInlineConfiguration) {
228-
// When Nuxt module calls `startVitest`, we don't need to call `getVitestConfigFromNuxt` again
229-
if (process.env.__NUXT_VITEST_RESOLVED__) return config
230-
226+
export async function defineVitestProject(config: TestProjectInlineConfiguration): Promise<TestProjectInlineConfiguration> {
231227
const resolvedConfig = await resolveConfig(config)
232228

233229
resolvedConfig.test.environment = 'nuxt'
234230

235231
return resolvedConfig
236232
}
237233

238-
export function defineVitestConfig(config: ViteUserConfig & { test?: VitestConfig } = {}) {
234+
export function defineVitestConfig(config: ViteUserConfig & { test?: VitestConfig } = {}): UserConfigFnPromise {
239235
return defineConfig(async () => {
240-
// When Nuxt module calls `startVitest`, we don't need to call `getVitestConfigFromNuxt` again
241-
if (process.env.__NUXT_VITEST_RESOLVED__) return config
242-
243236
const resolvedConfig = await resolveConfig(config)
244237

245238
if (resolvedConfig.test.browser?.enabled) {

src/module.ts

Lines changed: 21 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,16 @@
11
/// <reference types="@nuxt/devtools-kit" />
22

33
import { pathToFileURL } from 'node:url'
4-
import { createResolver, defineNuxtModule, logger, resolvePath, importModule } from '@nuxt/kit'
4+
import { createResolver, defineNuxtModule, logger, resolvePath } from '@nuxt/kit'
55
import type { Vitest, UserConfig as VitestConfig } from 'vitest/node'
66
import type { Reporter } from 'vitest/reporters'
77
import type { RunnerTestFile } from 'vitest'
8-
import type { InlineConfig as ViteConfig } from 'vite'
98
import { getPort } from 'get-port-please'
109
import { h } from 'vue'
1110
import { debounce } from 'perfect-debounce'
1211
import { isCI } from 'std-env'
13-
import { defu } from 'defu'
1412
import { join, relative } from 'pathe'
1513

16-
import { getVitestConfigFromNuxt } from './config'
1714
import { setupImportMocking } from './module/mock'
1815
import { NuxtRootStubPlugin } from './module/plugins/entry'
1916
import { loadKit } from './utils'
@@ -24,12 +21,6 @@ export interface NuxtVitestOptions {
2421
vitestConfig?: VitestConfig
2522
}
2623

27-
/**
28-
* List of plugins that are not compatible with test env.
29-
* Hard-coded for now, should remove by PR to upstream.
30-
*/
31-
const vitePluginBlocklist = ['vite-plugin-vue-inspector', 'vite-plugin-vue-inspector:post', 'vite-plugin-inspect', 'nuxt:type-check']
32-
3324
export default defineNuxtModule<NuxtVitestOptions>({
3425
meta: {
3526
name: '@nuxt/test-utils',
@@ -44,7 +35,7 @@ export default defineNuxtModule<NuxtVitestOptions>({
4435
await setupImportMocking(nuxt)
4536
}
4637

47-
const { addVitePlugin } = await loadKit(nuxt.options.rootDir)
38+
const { addVitePlugin, loadNuxt } = await loadKit(nuxt.options.rootDir)
4839

4940
const resolver = createResolver(import.meta.url)
5041
if (nuxt.options.test || nuxt.options.dev) {
@@ -76,15 +67,6 @@ export default defineNuxtModule<NuxtVitestOptions>({
7667
// the nuxt instance is used by a standalone Vitest env, we skip this module
7768
if (process.env.TEST || process.env.VITE_TEST) return
7869

79-
const rawViteConfigPromise = new Promise<ViteConfig>((resolve) => {
80-
// Wrap with app:resolve to ensure we got the final vite config
81-
nuxt.hook('app:resolve', () => {
82-
nuxt.hook('vite:configResolved', (config, { isClient }) => {
83-
if (isClient) resolve(config)
84-
})
85-
})
86-
})
87-
8870
let loaded = false
8971
let promise: Promise<void> | undefined
9072
let ctx: Vitest = undefined!
@@ -97,23 +79,6 @@ export default defineNuxtModule<NuxtVitestOptions>({
9779
let URL: string
9880

9981
async function start() {
100-
const { mergeConfig } = await importModule<typeof import('vite')>('vite', { paths: nuxt.options.modulesDir })
101-
const rawViteConfig = mergeConfig({}, await rawViteConfigPromise)
102-
103-
const viteConfig = await getVitestConfigFromNuxt({ nuxt, viteConfig: defu({ test: options.vitestConfig }, rawViteConfig) })
104-
105-
viteConfig.plugins = (viteConfig.plugins || []).filter((p) => {
106-
return !p || !('name' in p) || !vitePluginBlocklist.includes(p.name)
107-
})
108-
109-
// TODO: investigate why this is needed
110-
viteConfig.test.environmentMatchGlobs ||= []
111-
viteConfig.test.environmentMatchGlobs.push(
112-
['**/*.nuxt.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}', 'nuxt'],
113-
['{test,tests}/nuxt/**.*', 'nuxt'],
114-
)
115-
116-
process.env.__NUXT_VITEST_RESOLVED__ = 'true'
11782
const { startVitest } = (await import(pathToFileURL(await resolvePath('vitest/node')).href)) as typeof import('vitest/node')
11883

11984
const customReporter: Reporter = {
@@ -130,34 +95,32 @@ export default defineNuxtModule<NuxtVitestOptions>({
13095
},
13196
}
13297

133-
const watchMode = !process.env.NUXT_VITEST_DEV_TEST && !isCI
98+
const watchMode = !isCI
13499

135100
// We resolve the path here to ensure the dev server is already running with the correct protocol
136101
const PORT = await getPort({ port: 15555 })
137102
const PROTOCOL = nuxt.options.devServer.https ? 'https' : 'http'
138103
URL = `${PROTOCOL}://localhost:${PORT}/__vitest__/`
139104

140-
// For testing dev mode in CI, maybe expose an option to user later
141-
const overrides: VitestConfig = watchMode
142-
? {
143-
passWithNoTests: true,
144-
reporters: options.logToConsole
145-
? [
146-
...toArray(options.vitestConfig?.reporters ?? ['default']),
147-
customReporter,
148-
]
149-
: [customReporter], // do not report to console
150-
watch: true,
151-
ui: true,
152-
open: false,
153-
api: {
154-
port: PORT,
155-
},
156-
}
157-
: { watch: false }
158-
159105
// Start Vitest
160-
const promise = startVitest('test', [], defu(overrides, viteConfig.test), viteConfig)
106+
const promise = loadNuxt({ cwd: nuxt.options.rootDir }).then(nuxt => nuxt.runWithContext(
107+
() => startVitest('test', [], { ...watchMode
108+
? {
109+
passWithNoTests: true,
110+
reporters: options.logToConsole
111+
? [
112+
...toArray(options.vitestConfig?.reporters ?? ['default']),
113+
customReporter,
114+
]
115+
: [customReporter], // do not report to console
116+
watch: true,
117+
ui: true,
118+
open: false,
119+
api: {
120+
port: PORT,
121+
},
122+
}
123+
: { watch: false }, root: nuxt.options.rootDir })))
161124
promise.catch(() => process.exit(1))
162125

163126
if (watchMode) {

0 commit comments

Comments
 (0)