@nuxt/test-utils-nightly 4.0.0-1699284348.a89cc29 → 4.0.0-1701501480.7490334

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.
@@ -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-utils'
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-utils'
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-utils'
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-utils'
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-utils'
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,212 @@
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
+ async setup() {
106
+ const router = useRouter();
107
+ await router.replace(route);
108
+ const clonedComponent = {
109
+ ...component,
110
+ render: render ? (_ctx, ...args) => {
111
+ for (const key in _ctx) {
112
+ renderContext[key] = _ctx[key];
113
+ }
114
+ return render.apply(_ctx, [
115
+ renderContext,
116
+ ...args
117
+ ]);
118
+ } : void 0,
119
+ setup: setup ? (props2) => wrappedSetup(props2, setupContext) : void 0
120
+ };
121
+ return () => h$1(clonedComponent, { ...props, ...attrs }, slots);
122
+ }
123
+ })
124
+ }
125
+ )
126
+ },
127
+ defu(_options, {
128
+ slots,
129
+ global: {
130
+ config: {
131
+ globalProperties: vueApp.config.globalProperties
132
+ },
133
+ provide: vueApp._context.provides,
134
+ components: { RouterLink }
135
+ }
136
+ })
137
+ );
138
+ }
139
+ );
140
+ }
141
+
142
+ const WRAPPER_EL_ID = "test-wrapper";
143
+ async function renderSuspended(component, options) {
144
+ const {
145
+ props = {},
146
+ attrs = {},
147
+ slots = {},
148
+ route = "/",
149
+ ..._options
150
+ } = options || {};
151
+ const { render: renderFromTestingLibrary } = await import('@testing-library/vue');
152
+ const { vueApp } = globalThis.__unctx__.get("nuxt-app").tryUse();
153
+ const { render, setup } = component;
154
+ let setupContext;
155
+ return new Promise((resolve) => {
156
+ const utils = renderFromTestingLibrary(
157
+ {
158
+ // eslint-disable-next-line @typescript-eslint/no-shadow
159
+ setup: (props2, ctx) => {
160
+ setupContext = ctx;
161
+ return NuxtRoot.setup(props2, {
162
+ ...ctx,
163
+ expose: () => {
164
+ }
165
+ });
166
+ },
167
+ render: (renderContext) => (
168
+ // See discussions in https://github.com/testing-library/vue-testing-library/issues/230
169
+ // we add this additional root element because otherwise testing-library breaks
170
+ // because there's no root element while Suspense is resolving
171
+ h$1(
172
+ "div",
173
+ { id: WRAPPER_EL_ID },
174
+ h$1(
175
+ Suspense,
176
+ { onResolve: () => nextTick().then(() => resolve(utils)) },
177
+ {
178
+ default: () => h$1({
179
+ async setup() {
180
+ const router = useRouter();
181
+ await router.replace(route);
182
+ const clonedComponent = {
183
+ ...component,
184
+ render: render ? (_ctx, ...args) => render(renderContext, ...args) : void 0,
185
+ setup: setup ? (
186
+ // eslint-disable-next-line @typescript-eslint/no-shadow
187
+ (props2) => setup(props2, setupContext)
188
+ ) : void 0
189
+ };
190
+ return () => h$1(clonedComponent, { ...props, ...attrs }, slots);
191
+ }
192
+ })
193
+ }
194
+ )
195
+ )
196
+ )
197
+ },
198
+ defu(_options, {
199
+ slots,
200
+ global: {
201
+ config: {
202
+ globalProperties: vueApp.config.globalProperties
203
+ },
204
+ provide: vueApp._context.provides,
205
+ components: { RouterLink }
206
+ }
207
+ })
208
+ );
209
+ });
210
+ }
211
+
212
+ 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/module.d.ts ADDED
@@ -0,0 +1 @@
1
+ export { default } from './dist/module'