Skip to content

Commit 7e235ea

Browse files
committed
feat(runtime): add startOn environment option
1 parent fd77ec0 commit 7e235ea

File tree

21 files changed

+390
-16
lines changed

21 files changed

+390
-16
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<template>
2+
<div>
3+
{{ value }}
4+
</div>
5+
</template>
6+
7+
<script setup lang="ts">
8+
import { CUSTOM_VUE_PLUGIN_SYMBOL2 } from '../plugins/inject-value'
9+
10+
const value = inject(CUSTOM_VUE_PLUGIN_SYMBOL2)
11+
</script>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export const useInsideGlobalMiddleware = (): Parameters<typeof createError>[0] | undefined => {
2+
return undefined
3+
}
4+
5+
export const useInsideNamedMiddleware = (): Parameters<typeof createError>[0] | undefined => {
6+
return undefined
7+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export default defineNuxtRouteMiddleware((_to, _from) => {
2+
const value = useInsideGlobalMiddleware()
3+
4+
if (value) {
5+
return abortNavigation(value)
6+
}
7+
})
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export default defineNuxtRouteMiddleware((_to, _from) => {
2+
const value = useInsideNamedMiddleware()
3+
4+
if (value) {
5+
return abortNavigation(value)
6+
}
7+
})

examples/app-vitest-full/pages/index.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
<script setup lang="ts">
66
definePageMeta({
77
value: 'set in index',
8+
middleware: ['test-named-middleware'],
89
})
910
</script>
1011

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<script setup lang="ts">
2+
definePageMeta({
3+
middleware: ['test-named-middleware'],
4+
})
5+
</script>
6+
7+
<template>
8+
<div>named-middleware</div>
9+
</template>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export default defineNuxtPlugin(() => {
2+
const counter = useCounter()
3+
return {
4+
provide: {
5+
counter,
6+
},
7+
}
8+
})

examples/app-vitest-full/plugins/inject-value.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
import type { Plugin } from 'vue'
22

3-
// TODO: investigate why `Symbol()` export without global registry doesn't match
43
export const CUSTOM_VUE_PLUGIN_SYMBOL = Symbol.for('CUSTOM_VUE_PLUGIN_INJECTED')
4+
// Imported `Symbol()` will not match across vi.resetModules() calls.
5+
export const CUSTOM_VUE_PLUGIN_SYMBOL2 = Symbol('CUSTOM_VUE_PLUGIN_INJECTED2')
56

67
export const VUE_INJECTED_VALUE = 'injected vue plugin value'
8+
export const VUE_INJECTED_VALUE2 = 'injected vue plugin value 2'
79

810
export default defineNuxtPlugin((nuxtApp) => {
911
const vuePlugin: Plugin = {
1012
install(app) {
1113
app.provide(CUSTOM_VUE_PLUGIN_SYMBOL, VUE_INJECTED_VALUE)
14+
app.provide(CUSTOM_VUE_PLUGIN_SYMBOL2, VUE_INJECTED_VALUE2)
1215
},
1316
}
1417

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// @vitest-environment-options { "startOn": "beforeAll", "url": "http://localhost:4000", "domEnvironment": "jsdom" }
2+
import { describe, expect, it } from 'vitest'
3+
4+
describe('inline environment-options', () => {
5+
it('should applied @vitest-environment-options', async () => {
6+
expect(Reflect.get(window, '__NUXT_VITEST_ENVIRONMENT_OPTIONS__')).toMatchObject({
7+
url: 'http://localhost:4000',
8+
startOn: 'beforeAll',
9+
domEnvironment: 'jsdom',
10+
})
11+
expect(location.origin).toBe('http://localhost:4000')
12+
})
13+
})
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
// @vitest-environment-options { "startOn": "beforeAll" }
2+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
3+
import { mockNuxtImport, mountSuspended, renderSuspended } from '@nuxt/test-utils/runtime'
4+
5+
import App from '~/app.vue'
6+
7+
const {
8+
useInsideGlobalMiddlewareMock,
9+
useInsideNamedMiddlewareMock,
10+
} = vi.hoisted(() => ({
11+
useInsideGlobalMiddlewareMock: vi.fn<typeof useInsideGlobalMiddleware>(),
12+
useInsideNamedMiddlewareMock: vi.fn<typeof useInsideNamedMiddleware>(),
13+
}))
14+
15+
mockNuxtImport(useInsideGlobalMiddleware, () => useInsideGlobalMiddlewareMock)
16+
mockNuxtImport(useInsideNamedMiddleware, () => useInsideNamedMiddlewareMock)
17+
18+
describe('middleware', () => {
19+
beforeEach(() => {
20+
vi.resetAllMocks()
21+
})
22+
23+
afterEach(() => {
24+
vi.restoreAllMocks()
25+
})
26+
27+
describe('global middleware', () => {
28+
describe('when mock returns value navigation fails', () => {
29+
beforeEach(() => {
30+
useInsideGlobalMiddlewareMock.mockImplementation(() => ({
31+
statusCode: 401,
32+
message: 'mock return error',
33+
}))
34+
})
35+
36+
it('navigateTo', async () => {
37+
await expect(
38+
navigateTo({ path: '/foo', force: true }),
39+
).rejects.toMatchObject(
40+
{ statusCode: 401, message: 'mock return error' },
41+
)
42+
expect(useRoute().path).toBe('/')
43+
expect(useInsideGlobalMiddlewareMock).toHaveBeenCalledOnce()
44+
})
45+
46+
it('mountSuspended', async () => {
47+
await expect(
48+
mountSuspended(App, { route: { path: '/foo', force: true } }),
49+
).rejects.toMatchObject(
50+
{ statusCode: 401, message: 'mock return error' },
51+
)
52+
expect(useRoute().path).toBe('/')
53+
expect(useInsideGlobalMiddlewareMock).toHaveBeenCalledOnce()
54+
})
55+
56+
it('renderSuspended', async () => {
57+
await expect(
58+
renderSuspended(App, { route: { path: '/foo', force: true } }),
59+
).rejects.toMatchObject(
60+
{ statusCode: 401, message: 'mock return error' },
61+
)
62+
expect(useRoute().path).toBe('/')
63+
expect(useInsideGlobalMiddlewareMock).toHaveBeenCalledOnce()
64+
})
65+
})
66+
67+
describe('when mock returns undefined inside navigation succeeds', () => {
68+
beforeEach(async () => {
69+
useInsideGlobalMiddlewareMock.mockImplementation(() => undefined)
70+
})
71+
72+
it('navigateTo', async () => {
73+
await navigateTo({ path: '/foo', force: true })
74+
await nextTick()
75+
expect(useRoute().path).toBe('/foo')
76+
expect(useInsideGlobalMiddlewareMock).toHaveBeenCalledOnce()
77+
})
78+
79+
it('mountSuspended', async () => {
80+
const wrapper = await mountSuspended(App, {
81+
route: { path: '/foo', force: true },
82+
})
83+
expect(wrapper.html()).toContain('<div>/foo</div>')
84+
expect(useInsideGlobalMiddlewareMock).toHaveBeenCalledOnce()
85+
})
86+
87+
it('renderSuspended', async () => {
88+
const wrapper = await renderSuspended(App, {
89+
route: { path: '/foo', force: true },
90+
})
91+
expect(wrapper.html()).toContain('<div>/foo</div>')
92+
expect(useInsideGlobalMiddlewareMock).toHaveBeenCalledOnce()
93+
})
94+
})
95+
})
96+
97+
describe('named middleware', () => {
98+
describe('when mock returns value navigation fails', () => {
99+
beforeEach(() => {
100+
useInsideNamedMiddlewareMock.mockImplementation(() => ({
101+
statusCode: 401,
102+
message: 'mock return error',
103+
}))
104+
})
105+
106+
it('navigateTo', async () => {
107+
await expect(
108+
navigateTo({ path: '/other/named-middleware', force: true }),
109+
).rejects.toMatchObject(
110+
{ statusCode: 401, message: 'mock return error' },
111+
)
112+
expect(useInsideNamedMiddlewareMock).toHaveBeenCalledOnce()
113+
})
114+
115+
it('mountSuspended', async () => {
116+
await expect(
117+
mountSuspended(App, {
118+
route: { path: '/other/named-middleware', force: true },
119+
}),
120+
).rejects.toMatchObject(
121+
{ statusCode: 401, message: 'mock return error' },
122+
)
123+
expect(useInsideNamedMiddlewareMock).toHaveBeenCalledOnce()
124+
})
125+
126+
it('renderSuspended', async () => {
127+
await expect(
128+
renderSuspended(App, {
129+
route: { path: '/other/named-middleware', force: true },
130+
}),
131+
).rejects.toMatchObject(
132+
{ statusCode: 401, message: 'mock return error' },
133+
)
134+
expect(useInsideNamedMiddlewareMock).toHaveBeenCalledOnce()
135+
})
136+
})
137+
138+
describe('when mock returns undefined navigation succeeds', () => {
139+
beforeEach(async () => {
140+
useInsideNamedMiddlewareMock.mockImplementation(() => undefined)
141+
})
142+
143+
it('navigateTo', async () => {
144+
await navigateTo({ path: '/other/named-middleware', force: true })
145+
await nextTick()
146+
expect(useRoute().path).toBe('/other/named-middleware')
147+
expect(useInsideNamedMiddlewareMock).toHaveBeenCalledOnce()
148+
})
149+
150+
it('mountSuspended', async () => {
151+
const wrapper = await mountSuspended(App, {
152+
route: { path: '/other/named-middleware', force: true },
153+
})
154+
expect(wrapper.html()).toContain('<div>named-middleware</div>')
155+
expect(useInsideNamedMiddlewareMock).toHaveBeenCalledOnce()
156+
})
157+
158+
it('renderSuspended', async () => {
159+
const wrapper = await renderSuspended(App, {
160+
route: { path: '/other/named-middleware', force: true },
161+
})
162+
expect(wrapper.html()).toContain('<div>named-middleware</div>')
163+
expect(useInsideNamedMiddlewareMock).toHaveBeenCalledOnce()
164+
})
165+
})
166+
})
167+
})

0 commit comments

Comments
 (0)