diff --git a/examples/with-test/app.vue b/examples/with-test/app.vue
new file mode 100644
index 000000000..19bf5ff27
--- /dev/null
+++ b/examples/with-test/app.vue
@@ -0,0 +1,16 @@
+
+
+
+
+ Hello Nuxt!
+
+
+
+
diff --git a/examples/with-test/nuxt.config.ts b/examples/with-test/nuxt.config.ts
new file mode 100644
index 000000000..a3e4d6809
--- /dev/null
+++ b/examples/with-test/nuxt.config.ts
@@ -0,0 +1,4 @@
+import { defineNuxtConfig } from 'nuxt3'
+
+export default defineNuxtConfig({
+})
diff --git a/examples/with-test/package.json b/examples/with-test/package.json
new file mode 100644
index 000000000..060cc5231
--- /dev/null
+++ b/examples/with-test/package.json
@@ -0,0 +1,13 @@
+{
+ "name": "example-with-test",
+ "private": true,
+ "scripts": {
+ "build": "nuxi build",
+ "dev": "nuxi dev",
+ "start": "nuxi preview"
+ },
+ "devDependencies": {
+ "@nuxt/test-utils": "latest",
+ "nuxt3": "latest"
+ }
+}
diff --git a/examples/with-test/tests/basic.test.ts b/examples/with-test/tests/basic.test.ts
new file mode 100644
index 000000000..97afb12b9
--- /dev/null
+++ b/examples/with-test/tests/basic.test.ts
@@ -0,0 +1,14 @@
+import { fileURLToPath } from 'url'
+import { describe, expect, it } from 'vitest'
+import { setup, $fetch } from '@nuxt/test-utils'
+
+describe('example', async () => {
+ await setup({
+ rootDir: fileURLToPath(new URL('..', import.meta.url)),
+ server: true
+ })
+
+ it('Renders Hello Nuxt', async () => {
+ expect(await $fetch('/')).toMatch('Hello Nuxt!')
+ })
+})
diff --git a/examples/with-test/tsconfig.json b/examples/with-test/tsconfig.json
new file mode 100644
index 000000000..4b34df157
--- /dev/null
+++ b/examples/with-test/tsconfig.json
@@ -0,0 +1,3 @@
+{
+ "extends": "./.nuxt/tsconfig.json"
+}
diff --git a/package.json b/package.json
index 5caa5f6ec..0ddf400ef 100644
--- a/package.json
+++ b/package.json
@@ -30,6 +30,7 @@
"jiti": "^1.13.0",
"nitropack-dev": "link:../nitropack",
"nuxt3": "workspace:./packages/nuxt3",
+ "@nuxt/test-utils": "workspace:./packages/test-utils",
"vite": "^2.8.6",
"unbuild": "^0.7.0"
},
diff --git a/packages/nuxi/build.config.ts b/packages/nuxi/build.config.ts
index 7a2e98b44..58ea87d0f 100644
--- a/packages/nuxi/build.config.ts
+++ b/packages/nuxi/build.config.ts
@@ -12,8 +12,10 @@ export default defineBuildConfig({
externals: [
'@nuxt/kit',
'@nuxt/schema',
+ '@nuxt/test-utils',
'fsevents',
// TODO: Fix rollup/unbuild issue
+ 'node:url',
'node:buffer',
'node:path',
'node:child_process',
diff --git a/packages/nuxi/src/commands/index.ts b/packages/nuxi/src/commands/index.ts
index 0d8e3987f..652a613cf 100644
--- a/packages/nuxi/src/commands/index.ts
+++ b/packages/nuxi/src/commands/index.ts
@@ -15,7 +15,8 @@ export const commands = {
info: () => import('./info').then(_rDefault),
init: () => import('./init').then(_rDefault),
create: () => import('./init').then(_rDefault),
- upgrade: () => import('./upgrade').then(_rDefault)
+ upgrade: () => import('./upgrade').then(_rDefault),
+ test: () => import('./test').then(_rDefault)
}
export type Command = keyof typeof commands
diff --git a/packages/nuxi/src/commands/test.ts b/packages/nuxi/src/commands/test.ts
new file mode 100644
index 000000000..8fdd58af6
--- /dev/null
+++ b/packages/nuxi/src/commands/test.ts
@@ -0,0 +1,34 @@
+import { resolve } from 'pathe'
+import { defineNuxtCommand } from './index'
+
+export default defineNuxtCommand({
+ meta: {
+ name: 'test',
+ usage: 'npx nuxi test',
+ description: 'Run tests'
+ },
+ async invoke (args) {
+ process.env.NODE_ENV = process.env.NODE_ENV || 'test'
+ const rootDir = resolve(args._[0] || '.')
+ const { runTests } = await importTestUtils()
+ await runTests({
+ rootDir
+ })
+ }
+})
+
+async function importTestUtils (): Promise {
+ let err
+ for (const pkg of ['@nuxt/test-utils-edge', '@nuxt/test-utils']) {
+ try {
+ const exports = await import(pkg)
+ // Detect old @nuxt/test-utils
+ if (!exports.runTests) {
+ throw new Error('Invalid version of `@nuxt/test-utils` is installed!')
+ }
+ return exports
+ } catch (_err) { err = _err }
+ }
+ console.error(err)
+ throw new Error('`@nuxt/test-utils-edge` seems missing. Run `npm i -D @nuxt/test-utils-edge` or `yarn add -D @nuxt/test-utils-edge` to install.')
+}
diff --git a/packages/test-utils/src/index.ts b/packages/test-utils/src/index.ts
index fda71ddb4..55d9ebae5 100644
--- a/packages/test-utils/src/index.ts
+++ b/packages/test-utils/src/index.ts
@@ -3,3 +3,4 @@ export * from './context'
export * from './nuxt'
export * from './server'
export * from './setup'
+export * from './run'
diff --git a/packages/test-utils/src/run.ts b/packages/test-utils/src/run.ts
new file mode 100644
index 000000000..c1ed7c115
--- /dev/null
+++ b/packages/test-utils/src/run.ts
@@ -0,0 +1,35 @@
+export interface RunTestOptions {
+ rootDir: string,
+ runner?: 'vitest'
+}
+
+const RunTestDefaults: Partial = {
+ runner: 'vitest'
+}
+
+export async function runTests (opts: RunTestOptions) {
+ opts = { ...RunTestDefaults, ...opts }
+
+ if (opts.runner !== 'vitest') {
+ throw new Error(`Unsupported runner: ${opts.runner}. Currently only vitest runner is supported.`)
+ }
+ const { startVitest } = await import('vitest/dist/node.js')
+ const succeeded = await startVitest(
+ [] /* argv */,
+ // Vitest options
+ {
+ root: opts.rootDir,
+ run: true
+ },
+ // Vite options
+ {
+ esbuild: {
+ tsconfigRaw: '{}'
+ }
+ }
+ )
+
+ if (!succeeded) {
+ process.exit(1)
+ }
+}
diff --git a/yarn.lock b/yarn.lock
index 48fdf7edb..c621b88ee 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3194,7 +3194,7 @@ __metadata:
languageName: node
linkType: hard
-"@nuxt/test-utils@workspace:packages/test-utils":
+"@nuxt/test-utils@workspace:./packages/test-utils, @nuxt/test-utils@workspace:packages/test-utils":
version: 0.0.0-use.local
resolution: "@nuxt/test-utils@workspace:packages/test-utils"
dependencies:
@@ -10557,6 +10557,15 @@ __metadata:
languageName: unknown
linkType: soft
+"example-with-test@workspace:examples/with-test":
+ version: 0.0.0-use.local
+ resolution: "example-with-test@workspace:examples/with-test"
+ dependencies:
+ "@nuxt/test-utils": latest
+ nuxt3: latest
+ languageName: unknown
+ linkType: soft
+
"example-with-universal-router@workspace:examples/with-universal-router":
version: 0.0.0-use.local
resolution: "example-with-universal-router@workspace:examples/with-universal-router"