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
3 changes: 3 additions & 0 deletions examples/env-options/app/app.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<template>
<NuxtPage />
</template>
9 changes: 9 additions & 0 deletions examples/env-options/app/components/InjectedValue.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<template>
<div>{{ value }}</div>
</template>

<script setup lang="ts">
import { INJECTED_SYMBOL } from '~/plugins/injected-value'
const value = inject(INJECTED_SYMBOL)
</script>
10 changes: 10 additions & 0 deletions examples/env-options/app/composables/useGlobalCounter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export default function useGlobalCounter() {
const count = useState('GlobalCounter', () => 0)

return {
count,
increment: () => {
count.value++
},
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default defineNuxtRouteMiddleware((_to, _from) => {
useGlobalCounter().increment()
})
3 changes: 3 additions & 0 deletions examples/env-options/app/pages/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<template>
<div>index</div>
</template>
7 changes: 7 additions & 0 deletions examples/env-options/app/plugins/global-counter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default defineNuxtPlugin(() => {
return {
provide: {
counter: useGlobalCounter(),
},
}
})
10 changes: 10 additions & 0 deletions examples/env-options/app/plugins/injected-value.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export const INJECTED_SYMBOL = Symbol()
export const INJECTED_VALUE = 'INJECTED_VALUE'

export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.use({
install(app) {
app.provide(INJECTED_SYMBOL, INJECTED_VALUE)
},
})
})
5 changes: 5 additions & 0 deletions examples/env-options/nuxt.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
devtools: { enabled: true },
compatibilityDate: '2024-04-03',
})
23 changes: 23 additions & 0 deletions examples/env-options/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "example-env-options",
"private": true,
"type": "module",
"scripts": {
"build": "nuxt build",
"dev": "nuxt dev",
"dev:prepare": "nuxt prepare",
"generate": "nuxt generate",
"preview": "nuxt preview",
"postinstall": "nuxt prepare",
"test": "vitest run"
},
"dependencies": {
"nuxt": "^4.2.2"
},
"devDependencies": {
"@nuxt/test-utils": "latest",
"happy-dom": "20.0.11",
"typescript": "5.9.3",
"vitest": "4.0.16"
}
}
12 changes: 12 additions & 0 deletions examples/env-options/test/nuxt/app/global-middleware.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { it, describe, expect, beforeEach } from 'vitest'

describe('global middleware', () => {
beforeEach(() => {
useGlobalCounter().count.value = 0
})

it('increment count by global middleware', async () => {
await navigateTo({ path: '/', force: true })
expect(useGlobalCounter().count.value).toBe(1)
})
})
27 changes: 27 additions & 0 deletions examples/env-options/test/nuxt/boot/global-middleware.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { it, describe, expect, beforeEach, vi } from 'vitest'
import { mockNuxtImport } from '@nuxt/test-utils/runtime'

const {
increment,
} = vi.hoisted(() => ({
increment: vi.fn(),
}))

mockNuxtImport(useGlobalCounter, () => () => ({
count: ref(0),
increment,
}))

describe('global middleware', () => {
beforeEach(() => {
vi.resetAllMocks()
vi.restoreAllMocks()
useGlobalCounter().count.value = 0
})

it('can mock composable inside global middleware', async () => {
await navigateTo({ path: '/', force: true })
expect(increment).toHaveBeenCalledOnce()
expect(useGlobalCounter().count.value).toBe(0)
})
})
34 changes: 34 additions & 0 deletions examples/env-options/test/nuxt/boot/plugins.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { it, describe, expect, beforeEach, vi } from 'vitest'
import { mockNuxtImport, mountSuspended } from '@nuxt/test-utils/runtime'

import { INJECTED_VALUE } from '~/plugins/injected-value'

import InjectedValue from '~/components/InjectedValue.vue'

const { increment } = vi.hoisted(() => ({
increment: vi.fn(),
}))

mockNuxtImport(useGlobalCounter, () => () => ({
count: ref(0),
increment,
}))

describe('plugins', () => {
beforeEach(() => {
vi.resetAllMocks()
vi.restoreAllMocks()
useGlobalCounter().count.value = 0
})

it('can mock composable inside plugin', async () => {
useNuxtApp().$counter.increment()
expect(increment).toHaveBeenCalledOnce()
expect(useGlobalCounter().count.value).toBe(0)
})

it('export `Symbol()` without global registry does match', async () => {
const wrapper = await mountSuspended(InjectedValue)
expect(wrapper.text()).toBe(INJECTED_VALUE)
})
})
17 changes: 17 additions & 0 deletions examples/env-options/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"files": [],
"references": [
{
"path": "./.nuxt/tsconfig.app.json"
},
{
"path": "./.nuxt/tsconfig.server.json"
},
{
"path": "./.nuxt/tsconfig.shared.json"
},
{
"path": "./.nuxt/tsconfig.node.json"
}
]
}
26 changes: 26 additions & 0 deletions examples/env-options/vitest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { defineConfig } from 'vitest/config'
import { defineVitestProject } from '@nuxt/test-utils/config'

export default defineConfig({
test: {
projects: [
await defineVitestProject({
test: {
name: 'app',
dir: 'test/nuxt/app',
},
}),
await defineVitestProject({
test: {
name: 'boot',
dir: 'test/nuxt/boot',
environmentOptions: {
nuxt: {
startOn: 'beforeAll',
},
},
},
}),
],
},
})
19 changes: 19 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,12 @@ export interface NuxtEnvironmentOptions {
* @default 'http://localhost:3000'
*/
url?: string
/**
* When to start the NuxtApp.
* Set to `beforeAll` if you need to mock composables used during NuxtApp startup (e.g. plugins, global middlewares).
* @default 'setupFile'
*/
startOn?: 'setupFile' | 'beforeAll'
/**
* You can define how environment options are read when loading the Nuxt configuration.
*/
Expand Down
3 changes: 3 additions & 0 deletions src/environments/vitest/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ export interface NuxtWindow extends Window {
__app?: GenericApp
__registry: Set<string>
__NUXT_VITEST_ENVIRONMENT__?: boolean
__NUXT_VITEST_ENVIRONMENT_OPTIONS__?: {
startOn?: 'setupFile' | 'beforeAll'
}
__NUXT__: Record<string, unknown>
$fetch: $Fetch
fetch: ((input: RequestInfo | URL, init?: RequestInit | undefined) => Promise<Response>)
Expand Down
15 changes: 12 additions & 3 deletions src/runtime/entry.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
import { vi } from 'vitest'
import { beforeAll, vi } from 'vitest'
import { setupNuxt } from './shared/nuxt'
import type { NuxtWindow } from '../vitest-environment'

if (
typeof window !== 'undefined'
// @ts-expect-error undefined property
&& window.__NUXT_VITEST_ENVIRONMENT__
) {
await setupNuxt()
vi.resetModules()
const options = (window as unknown as NuxtWindow).__NUXT_VITEST_ENVIRONMENT_OPTIONS__ ?? {}
if (options.startOn === 'beforeAll') {
beforeAll(async () => {
await setupNuxt()
})
}
else {
await setupNuxt()
vi.resetModules()
}
}

export {}
3 changes: 3 additions & 0 deletions src/runtime/shared/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import { createFetchForH3V2 } from './h3-v2'
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export async function setupWindow(win: NuxtWindow, environmentOptions: { nuxt: NuxtEnvironmentOptions, nuxtRuntimeConfig?: Record<string, any>, nuxtRouteRules?: Record<string, any> }) {
win.__NUXT_VITEST_ENVIRONMENT__ = true
win.__NUXT_VITEST_ENVIRONMENT_OPTIONS__ = {
startOn: environmentOptions.nuxt.startOn,
}
win.__NUXT__ = {
serverRendered: false,
config: {
Expand Down
Loading