@nuxt/test-utils-nightly 4.0.0-1699284628.89ffa77 → 4.0.0-1702810232.fba662c
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +18 -0
- package/config.d.ts +1 -0
- package/dist/config.d.mts +46 -0
- package/dist/config.d.ts +46 -0
- package/dist/config.mjs +156 -0
- package/dist/{index.d.mts → e2e.d.mts} +5 -0
- package/dist/{index.d.ts → e2e.d.ts} +5 -0
- package/dist/{index.mjs → e2e.mjs} +18 -8
- package/dist/experimental.mjs +1 -1
- package/dist/module.d.mts +11 -0
- package/dist/module.d.ts +11 -0
- package/dist/module.mjs +461 -0
- package/dist/runtime/entry.d.ts +1 -0
- package/dist/runtime/entry.mjs +9 -0
- package/dist/runtime/global-setup.mjs +1 -1
- package/dist/runtime/nuxt-root.d.ts +4 -0
- package/dist/runtime/nuxt-root.mjs +21 -0
- package/dist/runtime-utils/index.d.mts +150 -0
- package/dist/runtime-utils/index.d.ts +150 -0
- package/dist/runtime-utils/index.mjs +223 -0
- package/dist/vitest-environment.d.mts +22 -0
- package/dist/vitest-environment.d.ts +22 -0
- package/dist/vitest-environment.mjs +175 -0
- package/e2e.d.ts +1 -0
- package/module.d.ts +1 -0
- package/package.json +88 -28
- package/runtime.d.ts +1 -0
- package/vitest-environment.d.ts +1 -0
- /package/dist/shared/{test-utils-nightly.8f432eb9.mjs → test-utils-nightly.ddf5bsCK.mjs} +0 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { EventHandler, HTTPMethod } from 'h3';
|
|
2
|
+
import { SetupContext, RenderFunction, ComputedOptions, MethodOptions, ComponentOptionsMixin, EmitsOptions, ComponentInjectOptions, ComponentOptionsWithoutProps, ComponentOptionsWithArrayProps, ComponentPropsOptions, ComponentOptionsWithObjectProps } from 'vue';
|
|
3
|
+
import { mount, ComponentMountingOptions } from '@vue/test-utils';
|
|
4
|
+
import { RouteLocationRaw } from 'vue-router';
|
|
5
|
+
import * as _testing_library_vue from '@testing-library/vue';
|
|
6
|
+
import { RenderOptions as RenderOptions$1 } from '@testing-library/vue';
|
|
7
|
+
|
|
8
|
+
type Awaitable<T> = T | Promise<T>;
|
|
9
|
+
type OptionalFunction<T> = T | (() => Awaitable<T>);
|
|
10
|
+
/**
|
|
11
|
+
* `registerEndpoint` allows you create Nitro endpoint that returns mocked data. It can come in handy if you want to test a component that makes requests to API to display some data.
|
|
12
|
+
* @param url - endpoint name (e.g. `/test/`).
|
|
13
|
+
* @param options - factory function that returns the mocked data or an object containing both the `handler` and the `method` properties.
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* import { registerEndpoint } from '@nuxt/test-utils/runtime'
|
|
17
|
+
*
|
|
18
|
+
* registerEndpoint("/test/", () => {
|
|
19
|
+
* test: "test-field"
|
|
20
|
+
* })
|
|
21
|
+
* ```
|
|
22
|
+
* @see https://github.com/danielroe/nuxt-vitest#registerendpoint
|
|
23
|
+
*/
|
|
24
|
+
declare function registerEndpoint(url: string, options: EventHandler | {
|
|
25
|
+
handler: EventHandler;
|
|
26
|
+
method: HTTPMethod;
|
|
27
|
+
}): void;
|
|
28
|
+
/**
|
|
29
|
+
* `mockNuxtImport` allows you to mock Nuxt's auto import functionality.
|
|
30
|
+
* @param _name - name of an import to mock.
|
|
31
|
+
* @param _factory - factory function that returns mocked import.
|
|
32
|
+
* @example
|
|
33
|
+
* ```ts
|
|
34
|
+
* import { mockNuxtImport } from '@nuxt/test-utils/runtime'
|
|
35
|
+
*
|
|
36
|
+
* mockNuxtImport('useStorage', () => {
|
|
37
|
+
* return () => {
|
|
38
|
+
* return { value: 'mocked storage' }
|
|
39
|
+
* }
|
|
40
|
+
* })
|
|
41
|
+
* ```
|
|
42
|
+
* @see https://github.com/danielroe/nuxt-vitest#mocknuxtimport
|
|
43
|
+
*/
|
|
44
|
+
declare function mockNuxtImport<T = any>(_name: string, _factory: () => T | Promise<T>): void;
|
|
45
|
+
/**
|
|
46
|
+
* `mockComponent` allows you to mock Nuxt's component.
|
|
47
|
+
* @param path - component name in PascalCase, or the relative path of the component.
|
|
48
|
+
* @param setup - factory function that returns the mocked component.
|
|
49
|
+
* @example
|
|
50
|
+
* ```ts
|
|
51
|
+
* import { mockComponent } from '@nuxt/test-utils/runtime'
|
|
52
|
+
*
|
|
53
|
+
* mockComponent('MyComponent', {
|
|
54
|
+
* props: {
|
|
55
|
+
* value: String
|
|
56
|
+
* },
|
|
57
|
+
* setup(props) {
|
|
58
|
+
* // ...
|
|
59
|
+
* }
|
|
60
|
+
* })
|
|
61
|
+
*
|
|
62
|
+
* // relative path or alias also works
|
|
63
|
+
* mockComponent('~/components/my-component.vue', async () => {
|
|
64
|
+
* // or a factory function
|
|
65
|
+
* return {
|
|
66
|
+
* setup(props) {
|
|
67
|
+
* // ...
|
|
68
|
+
* }
|
|
69
|
+
* }
|
|
70
|
+
* })
|
|
71
|
+
*
|
|
72
|
+
* // or you can use SFC for redirecting to a mock component
|
|
73
|
+
* mockComponent('MyComponent', () => import('./MockComponent.vue'))
|
|
74
|
+
* ```
|
|
75
|
+
* @see https://github.com/danielroe/nuxt-vitest#mockcomponent
|
|
76
|
+
*/
|
|
77
|
+
declare function mockComponent<Props, RawBindings = object>(path: string, setup: OptionalFunction<(props: Readonly<Props>, ctx: SetupContext) => RawBindings | RenderFunction>): void;
|
|
78
|
+
declare function mockComponent<Props = {}, RawBindings = {}, D = {}, C extends ComputedOptions = {}, M extends MethodOptions = {}, Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = {}, EE extends string = string, I extends ComponentInjectOptions = {}, II extends string = string>(path: string, options: OptionalFunction<ComponentOptionsWithoutProps<Props, RawBindings, D, C, M, Mixin, Extends, E, EE, I, II>>): void;
|
|
79
|
+
declare function mockComponent<PropNames extends string, RawBindings, D, C extends ComputedOptions = {}, M extends MethodOptions = {}, Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = {}, EE extends string = string, I extends ComponentInjectOptions = {}, II extends string = string>(path: string, options: OptionalFunction<ComponentOptionsWithArrayProps<PropNames, RawBindings, D, C, M, Mixin, Extends, E, EE, I, II>>): void;
|
|
80
|
+
declare function mockComponent<PropsOptions extends Readonly<ComponentPropsOptions>, RawBindings, D, C extends ComputedOptions = {}, M extends MethodOptions = {}, Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, Extends extends ComponentOptionsMixin = ComponentOptionsMixin, E extends EmitsOptions = {}, EE extends string = string, I extends ComponentInjectOptions = {}, II extends string = string>(path: string, options: OptionalFunction<ComponentOptionsWithObjectProps<PropsOptions, RawBindings, D, C, M, Mixin, Extends, E, EE, I, II>>): void;
|
|
81
|
+
|
|
82
|
+
type MountSuspendedOptions<T> = ComponentMountingOptions<T> & {
|
|
83
|
+
route?: RouteLocationRaw;
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* `mountSuspended` allows you to mount any vue component within the Nuxt environment, allowing async setup and access to injections from your Nuxt plugins. For example:
|
|
87
|
+
*
|
|
88
|
+
* ```ts
|
|
89
|
+
* // tests/components/SomeComponents.nuxt.spec.ts
|
|
90
|
+
* it('can mount some component', async () => {
|
|
91
|
+
* const component = await mountSuspended(SomeComponent)
|
|
92
|
+
* expect(component.text()).toMatchInlineSnapshot(
|
|
93
|
+
* 'This is an auto-imported component'
|
|
94
|
+
* )
|
|
95
|
+
* })
|
|
96
|
+
*
|
|
97
|
+
* // tests/App.nuxt.spec.ts
|
|
98
|
+
* it('can also mount an app', async () => {
|
|
99
|
+
* const component = await mountSuspended(App, { route: '/test' })
|
|
100
|
+
* expect(component.html()).toMatchInlineSnapshot(`
|
|
101
|
+
* "<div>This is an auto-imported component</div>
|
|
102
|
+
* <div> I am a global component </div>
|
|
103
|
+
* <div>/</div>
|
|
104
|
+
* <a href=\\"/test\\"> Test link </a>"
|
|
105
|
+
* `)
|
|
106
|
+
* })
|
|
107
|
+
* ```
|
|
108
|
+
* @param component the component to be tested
|
|
109
|
+
* @param options optional options to set up your component
|
|
110
|
+
*/
|
|
111
|
+
declare function mountSuspended<T>(component: T, options?: MountSuspendedOptions<T>): Promise<ReturnType<typeof mount<T>> & {
|
|
112
|
+
setupState: any;
|
|
113
|
+
}>;
|
|
114
|
+
|
|
115
|
+
type RenderOptions = RenderOptions$1 & {
|
|
116
|
+
route?: RouteLocationRaw;
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* `renderSuspended` allows you to mount any vue component within the Nuxt environment, allowing async setup and access to injections from your Nuxt plugins.
|
|
120
|
+
*
|
|
121
|
+
* This is a wrapper around the `render` function from @testing-libary/vue, and should be used together with
|
|
122
|
+
* utilities from that package.
|
|
123
|
+
*
|
|
124
|
+
* ```ts
|
|
125
|
+
* // tests/components/SomeComponents.nuxt.spec.ts
|
|
126
|
+
* import { renderSuspended } from '@nuxt/test-utils/runtime'
|
|
127
|
+
*
|
|
128
|
+
* it('can render some component', async () => {
|
|
129
|
+
* const { html } = await renderSuspended(SomeComponent)
|
|
130
|
+
* expect(html()).toMatchInlineSnapshot(
|
|
131
|
+
* 'This is an auto-imported component'
|
|
132
|
+
* )
|
|
133
|
+
*
|
|
134
|
+
* })
|
|
135
|
+
*
|
|
136
|
+
* // tests/App.nuxt.spec.ts
|
|
137
|
+
* import { renderSuspended } from '@nuxt/test-utils/runtime'
|
|
138
|
+
* import { screen } from '@testing-library/vue'
|
|
139
|
+
*
|
|
140
|
+
* it('can also mount an app', async () => {
|
|
141
|
+
* const { html } = await renderSuspended(App, { route: '/test' })
|
|
142
|
+
* expect(screen.getByRole('link', { name: 'Test Link' })).toBeVisible()
|
|
143
|
+
* })
|
|
144
|
+
* ```
|
|
145
|
+
* @param component the component to be tested
|
|
146
|
+
* @param options optional options to set up your component
|
|
147
|
+
*/
|
|
148
|
+
declare function renderSuspended<T>(component: T, options?: RenderOptions): Promise<_testing_library_vue.RenderResult>;
|
|
149
|
+
|
|
150
|
+
export { mockComponent, mockNuxtImport, mountSuspended, registerEndpoint, renderSuspended };
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { defineEventHandler } from 'h3';
|
|
2
|
+
import { mount } from '@vue/test-utils';
|
|
3
|
+
import { h as h$1, Suspense, nextTick } from 'vue';
|
|
4
|
+
import { defu } from 'defu';
|
|
5
|
+
import { defineComponent, useRouter, h } from '#imports';
|
|
6
|
+
import NuxtRoot from '#build/root-component.mjs';
|
|
7
|
+
|
|
8
|
+
function registerEndpoint(url, options) {
|
|
9
|
+
const app = window.__app;
|
|
10
|
+
if (!app)
|
|
11
|
+
return;
|
|
12
|
+
const config = typeof options === "function" ? {
|
|
13
|
+
handler: options,
|
|
14
|
+
method: void 0
|
|
15
|
+
} : options;
|
|
16
|
+
app.use("/_" + url, defineEventHandler(config.handler), {
|
|
17
|
+
match(_, event) {
|
|
18
|
+
return config.method ? event?.method === config.method : true;
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
window.__registry.add(url);
|
|
22
|
+
}
|
|
23
|
+
function mockNuxtImport(_name, _factory) {
|
|
24
|
+
throw new Error(
|
|
25
|
+
"mockNuxtImport() is a macro and it did not get transpiled, this may be an internal bug of nuxt-vitest."
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
function mockComponent(_path, _component) {
|
|
29
|
+
throw new Error(
|
|
30
|
+
"mockComponent() is a macro and it did not get transpiled, this may be an internal bug of nuxt-vitest."
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const RouterLink = defineComponent({
|
|
35
|
+
functional: true,
|
|
36
|
+
props: {
|
|
37
|
+
to: [String, Object],
|
|
38
|
+
custom: Boolean,
|
|
39
|
+
replace: Boolean,
|
|
40
|
+
// Not implemented
|
|
41
|
+
activeClass: String,
|
|
42
|
+
exactActiveClass: String,
|
|
43
|
+
ariaCurrentValue: String
|
|
44
|
+
},
|
|
45
|
+
setup: (props, { slots }) => {
|
|
46
|
+
const navigate = () => {
|
|
47
|
+
};
|
|
48
|
+
return () => {
|
|
49
|
+
const route = props.to ? useRouter().resolve(props.to) : {};
|
|
50
|
+
return props.custom ? slots.default?.({ href: props.to, navigate, route }) : h(
|
|
51
|
+
"a",
|
|
52
|
+
{
|
|
53
|
+
href: props.to,
|
|
54
|
+
onClick: (e) => {
|
|
55
|
+
e.preventDefault();
|
|
56
|
+
return navigate();
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
slots
|
|
60
|
+
);
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
async function mountSuspended(component, options) {
|
|
66
|
+
const {
|
|
67
|
+
props = {},
|
|
68
|
+
attrs = {},
|
|
69
|
+
slots = {},
|
|
70
|
+
route = "/",
|
|
71
|
+
..._options
|
|
72
|
+
} = options || {};
|
|
73
|
+
const vueApp = globalThis.__unctx__.get("nuxt-app").tryUse().vueApp;
|
|
74
|
+
const { render, setup } = component;
|
|
75
|
+
let setupContext;
|
|
76
|
+
let setupState;
|
|
77
|
+
const wrappedSetup = async (props2, setupContext2) => {
|
|
78
|
+
if (setup) {
|
|
79
|
+
setupState = await setup(props2, setupContext2);
|
|
80
|
+
return setupState;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
return new Promise(
|
|
84
|
+
(resolve) => {
|
|
85
|
+
const vm = mount(
|
|
86
|
+
{
|
|
87
|
+
setup: (props2, ctx) => {
|
|
88
|
+
setupContext = ctx;
|
|
89
|
+
return NuxtRoot.setup(props2, {
|
|
90
|
+
...ctx,
|
|
91
|
+
expose: () => {
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
},
|
|
95
|
+
render: (renderContext) => h$1(
|
|
96
|
+
Suspense,
|
|
97
|
+
{
|
|
98
|
+
onResolve: () => nextTick().then(() => {
|
|
99
|
+
vm.setupState = setupState;
|
|
100
|
+
resolve(vm);
|
|
101
|
+
})
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
default: () => h$1({
|
|
105
|
+
name: "MountSuspendedHelper",
|
|
106
|
+
async setup() {
|
|
107
|
+
const router = useRouter();
|
|
108
|
+
await router.replace(route);
|
|
109
|
+
const clonedComponent = {
|
|
110
|
+
name: "MountSuspendedComponent",
|
|
111
|
+
...component,
|
|
112
|
+
render: render ? function(_ctx, ...args) {
|
|
113
|
+
for (const key in props || {}) {
|
|
114
|
+
renderContext[key] = _ctx[key];
|
|
115
|
+
}
|
|
116
|
+
for (const key in setupState || {}) {
|
|
117
|
+
renderContext[key] = setupState[key];
|
|
118
|
+
}
|
|
119
|
+
return render.call(this, renderContext, ...args);
|
|
120
|
+
} : void 0,
|
|
121
|
+
setup: setup ? (props2) => wrappedSetup(props2, setupContext) : void 0
|
|
122
|
+
};
|
|
123
|
+
return () => h$1(clonedComponent, { ...props, ...attrs }, slots);
|
|
124
|
+
}
|
|
125
|
+
})
|
|
126
|
+
}
|
|
127
|
+
)
|
|
128
|
+
},
|
|
129
|
+
defu(
|
|
130
|
+
_options,
|
|
131
|
+
{
|
|
132
|
+
slots,
|
|
133
|
+
global: {
|
|
134
|
+
config: {
|
|
135
|
+
globalProperties: vueApp.config.globalProperties
|
|
136
|
+
},
|
|
137
|
+
provide: vueApp._context.provides,
|
|
138
|
+
stubs: {
|
|
139
|
+
Suspense: false,
|
|
140
|
+
MountSuspendedHelper: false,
|
|
141
|
+
[typeof component.name === "string" ? component.name : "MountSuspendedComponent"]: false
|
|
142
|
+
},
|
|
143
|
+
components: { RouterLink }
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
)
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const WRAPPER_EL_ID = "test-wrapper";
|
|
153
|
+
async function renderSuspended(component, options) {
|
|
154
|
+
const {
|
|
155
|
+
props = {},
|
|
156
|
+
attrs = {},
|
|
157
|
+
slots = {},
|
|
158
|
+
route = "/",
|
|
159
|
+
..._options
|
|
160
|
+
} = options || {};
|
|
161
|
+
const { render: renderFromTestingLibrary } = await import('@testing-library/vue');
|
|
162
|
+
const { vueApp } = globalThis.__unctx__.get("nuxt-app").tryUse();
|
|
163
|
+
const { render, setup } = component;
|
|
164
|
+
document.querySelector(`#${WRAPPER_EL_ID}`)?.remove();
|
|
165
|
+
let setupContext;
|
|
166
|
+
return new Promise((resolve) => {
|
|
167
|
+
const utils = renderFromTestingLibrary(
|
|
168
|
+
{
|
|
169
|
+
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
170
|
+
setup: (props2, ctx) => {
|
|
171
|
+
setupContext = ctx;
|
|
172
|
+
return NuxtRoot.setup(props2, {
|
|
173
|
+
...ctx,
|
|
174
|
+
expose: () => {
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
},
|
|
178
|
+
render: (renderContext) => (
|
|
179
|
+
// See discussions in https://github.com/testing-library/vue-testing-library/issues/230
|
|
180
|
+
// we add this additional root element because otherwise testing-library breaks
|
|
181
|
+
// because there's no root element while Suspense is resolving
|
|
182
|
+
h$1(
|
|
183
|
+
"div",
|
|
184
|
+
{ id: WRAPPER_EL_ID },
|
|
185
|
+
h$1(
|
|
186
|
+
Suspense,
|
|
187
|
+
{ onResolve: () => nextTick().then(() => resolve(utils)) },
|
|
188
|
+
{
|
|
189
|
+
default: () => h$1({
|
|
190
|
+
async setup() {
|
|
191
|
+
const router = useRouter();
|
|
192
|
+
await router.replace(route);
|
|
193
|
+
const clonedComponent = {
|
|
194
|
+
...component,
|
|
195
|
+
render: render ? (_ctx, ...args) => render(renderContext, ...args) : void 0,
|
|
196
|
+
setup: setup ? (
|
|
197
|
+
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
198
|
+
(props2) => setup(props2, setupContext)
|
|
199
|
+
) : void 0
|
|
200
|
+
};
|
|
201
|
+
return () => h$1(clonedComponent, { ...props, ...attrs }, slots);
|
|
202
|
+
}
|
|
203
|
+
})
|
|
204
|
+
}
|
|
205
|
+
)
|
|
206
|
+
)
|
|
207
|
+
)
|
|
208
|
+
},
|
|
209
|
+
defu(_options, {
|
|
210
|
+
slots,
|
|
211
|
+
global: {
|
|
212
|
+
config: {
|
|
213
|
+
globalProperties: vueApp.config.globalProperties
|
|
214
|
+
},
|
|
215
|
+
provide: vueApp._context.provides,
|
|
216
|
+
components: { RouterLink }
|
|
217
|
+
}
|
|
218
|
+
})
|
|
219
|
+
);
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export { mockComponent, mockNuxtImport, mountSuspended, registerEndpoint, renderSuspended };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Environment } from 'vitest';
|
|
2
|
+
import { App } from 'h3';
|
|
3
|
+
|
|
4
|
+
declare const _default: Environment;
|
|
5
|
+
|
|
6
|
+
type NuxtBuiltinEnvironment = 'happy-dom' | 'jsdom';
|
|
7
|
+
interface NuxtWindow extends Window {
|
|
8
|
+
__app: App;
|
|
9
|
+
__registry: Set<string>;
|
|
10
|
+
__NUXT_VITEST_ENVIRONMENT__?: boolean;
|
|
11
|
+
__NUXT__: any;
|
|
12
|
+
$fetch: any;
|
|
13
|
+
fetch: any;
|
|
14
|
+
IntersectionObserver: any;
|
|
15
|
+
Headers: any;
|
|
16
|
+
}
|
|
17
|
+
type EnvironmentNuxt = (global: any, options: Record<string, any>) => Promise<{
|
|
18
|
+
window: NuxtWindow;
|
|
19
|
+
teardown(): void;
|
|
20
|
+
}>;
|
|
21
|
+
|
|
22
|
+
export { type EnvironmentNuxt, type NuxtBuiltinEnvironment, type NuxtWindow, _default as default };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Environment } from 'vitest';
|
|
2
|
+
import { App } from 'h3';
|
|
3
|
+
|
|
4
|
+
declare const _default: Environment;
|
|
5
|
+
|
|
6
|
+
type NuxtBuiltinEnvironment = 'happy-dom' | 'jsdom';
|
|
7
|
+
interface NuxtWindow extends Window {
|
|
8
|
+
__app: App;
|
|
9
|
+
__registry: Set<string>;
|
|
10
|
+
__NUXT_VITEST_ENVIRONMENT__?: boolean;
|
|
11
|
+
__NUXT__: any;
|
|
12
|
+
$fetch: any;
|
|
13
|
+
fetch: any;
|
|
14
|
+
IntersectionObserver: any;
|
|
15
|
+
Headers: any;
|
|
16
|
+
}
|
|
17
|
+
type EnvironmentNuxt = (global: any, options: Record<string, any>) => Promise<{
|
|
18
|
+
window: NuxtWindow;
|
|
19
|
+
teardown(): void;
|
|
20
|
+
}>;
|
|
21
|
+
|
|
22
|
+
export { type EnvironmentNuxt, type NuxtBuiltinEnvironment, type NuxtWindow, _default as default };
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { createFetch as createFetch$1 } from 'ofetch';
|
|
2
|
+
import { indexedDB } from 'fake-indexeddb';
|
|
3
|
+
import { joinURL } from 'ufo';
|
|
4
|
+
import { createApp, toNodeListener, defineEventHandler } from 'h3';
|
|
5
|
+
import defu from 'defu';
|
|
6
|
+
import { toRouteMatcher, createRouter, exportMatcher } from 'radix3';
|
|
7
|
+
import { populateGlobal } from 'vitest/environments';
|
|
8
|
+
import { createCall, createFetch } from 'unenv/runtime/fetch/index';
|
|
9
|
+
import { importModule } from 'local-pkg';
|
|
10
|
+
|
|
11
|
+
const happyDom = (async function(_, { happyDom = {} }) {
|
|
12
|
+
const { Window, GlobalWindow } = await importModule(
|
|
13
|
+
"happy-dom"
|
|
14
|
+
);
|
|
15
|
+
const window = new (GlobalWindow || Window)(happyDom);
|
|
16
|
+
return {
|
|
17
|
+
window,
|
|
18
|
+
teardown() {
|
|
19
|
+
window.happyDOM.cancelAsync();
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const jsdom = (async function(global, { jsdom = {} }) {
|
|
25
|
+
const { CookieJar, JSDOM, ResourceLoader, VirtualConsole } = await importModule("jsdom");
|
|
26
|
+
const {
|
|
27
|
+
html = "<!DOCTYPE html>",
|
|
28
|
+
userAgent,
|
|
29
|
+
url = "http://localhost:3000",
|
|
30
|
+
contentType = "text/html",
|
|
31
|
+
pretendToBeVisual = true,
|
|
32
|
+
includeNodeLocations = false,
|
|
33
|
+
runScripts = "dangerously",
|
|
34
|
+
resources,
|
|
35
|
+
console = false,
|
|
36
|
+
cookieJar = false,
|
|
37
|
+
...restOptions
|
|
38
|
+
} = jsdom;
|
|
39
|
+
const window = new JSDOM(html, {
|
|
40
|
+
pretendToBeVisual,
|
|
41
|
+
resources: resources ?? (userAgent ? new ResourceLoader({ userAgent }) : void 0),
|
|
42
|
+
runScripts,
|
|
43
|
+
url,
|
|
44
|
+
virtualConsole: console && global.console ? new VirtualConsole().sendTo(global.console) : void 0,
|
|
45
|
+
cookieJar: cookieJar ? new CookieJar() : void 0,
|
|
46
|
+
includeNodeLocations,
|
|
47
|
+
contentType,
|
|
48
|
+
userAgent,
|
|
49
|
+
...restOptions
|
|
50
|
+
}).window;
|
|
51
|
+
window.scrollTo = () => {
|
|
52
|
+
};
|
|
53
|
+
return {
|
|
54
|
+
window,
|
|
55
|
+
teardown() {
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const environmentMap = {
|
|
61
|
+
"happy-dom": happyDom,
|
|
62
|
+
jsdom
|
|
63
|
+
};
|
|
64
|
+
const index = {
|
|
65
|
+
name: "nuxt",
|
|
66
|
+
transformMode: "web",
|
|
67
|
+
async setup(global, environmentOptions) {
|
|
68
|
+
const url = joinURL("http://localhost:3000", environmentOptions?.nuxtRuntimeConfig.app?.baseURL || "/");
|
|
69
|
+
const environmentName = environmentOptions.nuxt.domEnvironment;
|
|
70
|
+
const environment = environmentMap[environmentName] || environmentMap["happy-dom"];
|
|
71
|
+
const { window: win, teardown } = await environment(global, defu(environmentOptions, {
|
|
72
|
+
happyDom: { url },
|
|
73
|
+
jsdom: { url }
|
|
74
|
+
}));
|
|
75
|
+
win.__NUXT_VITEST_ENVIRONMENT__ = true;
|
|
76
|
+
win.__NUXT__ = {
|
|
77
|
+
serverRendered: false,
|
|
78
|
+
config: {
|
|
79
|
+
public: {},
|
|
80
|
+
app: { baseURL: "/" },
|
|
81
|
+
...environmentOptions?.nuxtRuntimeConfig
|
|
82
|
+
},
|
|
83
|
+
data: {},
|
|
84
|
+
state: {}
|
|
85
|
+
};
|
|
86
|
+
const app = win.document.createElement("div");
|
|
87
|
+
app.id = environmentOptions.nuxt.rootId;
|
|
88
|
+
win.document.body.appendChild(app);
|
|
89
|
+
if (environmentOptions?.nuxt?.mock?.intersectionObserver) {
|
|
90
|
+
win.IntersectionObserver = win.IntersectionObserver || class IntersectionObserver {
|
|
91
|
+
observe() {
|
|
92
|
+
}
|
|
93
|
+
unobserve() {
|
|
94
|
+
}
|
|
95
|
+
disconnect() {
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
if (environmentOptions?.nuxt?.mock?.indexedDb) {
|
|
100
|
+
win.indexedDB = indexedDB;
|
|
101
|
+
}
|
|
102
|
+
const h3App = createApp();
|
|
103
|
+
if (!win.fetch) {
|
|
104
|
+
await import('node-fetch-native/polyfill');
|
|
105
|
+
win.URLSearchParams = globalThis.URLSearchParams;
|
|
106
|
+
}
|
|
107
|
+
const localCall = createCall(toNodeListener(h3App));
|
|
108
|
+
const localFetch = createFetch(localCall, win.fetch);
|
|
109
|
+
const registry = /* @__PURE__ */ new Set();
|
|
110
|
+
win.fetch = (init, options) => {
|
|
111
|
+
if (typeof init === "string") {
|
|
112
|
+
const base = init.split("?")[0];
|
|
113
|
+
if (registry.has(base) || registry.has(init)) {
|
|
114
|
+
init = "/_" + init;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return localFetch(init, options);
|
|
118
|
+
};
|
|
119
|
+
win.$fetch = createFetch$1({ fetch: win.fetch, Headers: win.Headers });
|
|
120
|
+
win.__registry = registry;
|
|
121
|
+
win.__app = h3App;
|
|
122
|
+
const { keys, originals } = populateGlobal(global, win, {
|
|
123
|
+
bindFunctions: true
|
|
124
|
+
});
|
|
125
|
+
const timestamp = Date.now();
|
|
126
|
+
const routeRulesMatcher = toRouteMatcher(
|
|
127
|
+
createRouter({ routes: environmentOptions.nuxtRouteRules || {} })
|
|
128
|
+
);
|
|
129
|
+
const matcher = exportMatcher(routeRulesMatcher);
|
|
130
|
+
const manifestOutputPath = joinURL(
|
|
131
|
+
"/",
|
|
132
|
+
environmentOptions?.nuxtRuntimeConfig.app?.buildAssetsDir || "_nuxt",
|
|
133
|
+
"builds"
|
|
134
|
+
);
|
|
135
|
+
const manifestBaseRoutePath = joinURL("/_", manifestOutputPath);
|
|
136
|
+
h3App.use(
|
|
137
|
+
`${manifestBaseRoutePath}/latest.json`,
|
|
138
|
+
defineEventHandler(() => ({
|
|
139
|
+
id: "test",
|
|
140
|
+
timestamp
|
|
141
|
+
}))
|
|
142
|
+
);
|
|
143
|
+
h3App.use(
|
|
144
|
+
`${manifestBaseRoutePath}/meta/test.json`,
|
|
145
|
+
defineEventHandler(() => ({
|
|
146
|
+
id: "test",
|
|
147
|
+
timestamp,
|
|
148
|
+
matcher,
|
|
149
|
+
prerendered: []
|
|
150
|
+
}))
|
|
151
|
+
);
|
|
152
|
+
h3App.use(
|
|
153
|
+
`${manifestBaseRoutePath}/meta/dev.json`,
|
|
154
|
+
defineEventHandler(() => ({
|
|
155
|
+
id: "test",
|
|
156
|
+
timestamp,
|
|
157
|
+
matcher,
|
|
158
|
+
prerendered: []
|
|
159
|
+
}))
|
|
160
|
+
);
|
|
161
|
+
registry.add(`${manifestOutputPath}/latest.json`);
|
|
162
|
+
registry.add(`${manifestOutputPath}/meta/test.json`);
|
|
163
|
+
registry.add(`${manifestOutputPath}/meta/dev.json`);
|
|
164
|
+
return {
|
|
165
|
+
// called after all tests with this env have been run
|
|
166
|
+
teardown() {
|
|
167
|
+
keys.forEach((key) => delete global[key]);
|
|
168
|
+
originals.forEach((v, k) => global[k] = v);
|
|
169
|
+
teardown();
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
export { index as default };
|
package/e2e.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './dist/e2e'
|
package/module.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './dist/module'
|