@nuxt/test-utils 3.21.0 → 3.22.0
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/README.md +1 -1
- package/dist/config.d.mts +2 -3
- package/dist/config.mjs +22 -20
- package/dist/e2e.d.mts +2 -2
- package/dist/e2e.mjs +17 -13
- package/dist/experimental.mjs +1 -1
- package/dist/module.mjs +271 -146
- package/dist/playwright.d.mts +1 -1
- package/dist/playwright.mjs +3 -3
- package/dist/runtime/shared/nuxt.d.ts +1 -1
- package/dist/runtime/shared/nuxt.mjs +10 -2
- package/dist/runtime/shared/vue-wrapper-plugin.d.ts +50 -0
- package/dist/runtime/shared/vue-wrapper-plugin.mjs +41 -0
- package/dist/runtime-utils/index.d.mts +24 -24
- package/dist/runtime-utils/index.mjs +157 -249
- package/dist/shared/{test-utils.G1ew4kEe.mjs → test-utils.BIY9XRkB.mjs} +1 -1
- package/dist/shared/{test-utils.CtwoJP76.mjs → test-utils.C_clWQBc.mjs} +4 -4
- package/dist/shared/{test-utils.3NR-so9-.mjs → test-utils.D2ZzXztg.mjs} +2 -2
- package/dist/shared/test-utils.DDUpsMYL.mjs +32 -0
- package/dist/shared/{test-utils.20kU0tZa.d.mts → test-utils.ZByFDqps.d.mts} +1 -1
- package/dist/vitest-wrapper/cli.d.mts +2 -0
- package/dist/vitest-wrapper/cli.mjs +78 -0
- package/package.json +25 -25
package/dist/playwright.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import defu from 'defu';
|
|
2
2
|
import { test as test$1 } from '@playwright/test';
|
|
3
3
|
export { expect } from '@playwright/test';
|
|
4
|
-
import { w as waitForHydration, d as createTest } from './shared/test-utils.
|
|
4
|
+
import { w as waitForHydration, d as createTest } from './shared/test-utils.D2ZzXztg.mjs';
|
|
5
5
|
import 'node:path';
|
|
6
6
|
import 'ufo';
|
|
7
7
|
import 'std-env';
|
|
@@ -11,10 +11,10 @@ import 'destr';
|
|
|
11
11
|
import 'scule';
|
|
12
12
|
import 'node:url';
|
|
13
13
|
import 'exsolve';
|
|
14
|
-
import { d as url } from './shared/test-utils.
|
|
14
|
+
import { d as url } from './shared/test-utils.C_clWQBc.mjs';
|
|
15
15
|
import 'pathe';
|
|
16
16
|
import '#dirs';
|
|
17
|
-
import './shared/test-utils.
|
|
17
|
+
import './shared/test-utils.BIY9XRkB.mjs';
|
|
18
18
|
import 'tinyexec';
|
|
19
19
|
import 'get-port-please';
|
|
20
20
|
import 'ofetch';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function setupNuxt(): Promise<
|
|
1
|
+
export declare function setupNuxt(): Promise<any>;
|
|
@@ -1,7 +1,15 @@
|
|
|
1
|
+
import { getVueWrapperPlugin } from "./vue-wrapper-plugin.mjs";
|
|
1
2
|
export async function setupNuxt() {
|
|
2
3
|
const { useRouter } = await import("#app/composables/router");
|
|
3
4
|
await import("#app/nuxt-vitest-app-entry").then((r) => r.default());
|
|
4
5
|
const nuxtApp = useNuxtApp();
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
function sync() {
|
|
7
|
+
return nuxtApp._route.sync ? nuxtApp._route.sync() : nuxtApp.callHook("page:finish");
|
|
8
|
+
}
|
|
9
|
+
const { hasNuxtPage } = getVueWrapperPlugin();
|
|
10
|
+
useRouter().afterEach(() => {
|
|
11
|
+
if (hasNuxtPage()) return;
|
|
12
|
+
return sync();
|
|
13
|
+
});
|
|
14
|
+
return sync();
|
|
7
15
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { VueWrapper } from '@vue/test-utils';
|
|
2
|
+
type Options = ReturnType<typeof createPluginOptions>;
|
|
3
|
+
export declare function getVueWrapperPlugin(): Options;
|
|
4
|
+
declare function createPluginOptions(): {
|
|
5
|
+
_name: string;
|
|
6
|
+
_instances: WeakRef<VueWrapper>[];
|
|
7
|
+
readonly instances: VueWrapper<unknown, {
|
|
8
|
+
$: import("vue").ComponentInternalInstance;
|
|
9
|
+
$data: {};
|
|
10
|
+
$props: {};
|
|
11
|
+
$attrs: {
|
|
12
|
+
[x: string]: unknown;
|
|
13
|
+
};
|
|
14
|
+
$refs: {
|
|
15
|
+
[x: string]: unknown;
|
|
16
|
+
};
|
|
17
|
+
$slots: Readonly<{
|
|
18
|
+
[name: string]: import("vue").Slot<any> | undefined;
|
|
19
|
+
}>;
|
|
20
|
+
$root: import("vue").ComponentPublicInstance | null;
|
|
21
|
+
$parent: import("vue").ComponentPublicInstance | null;
|
|
22
|
+
$host: Element | null;
|
|
23
|
+
$emit: (event: string, ...args: any[]) => void;
|
|
24
|
+
$el: any;
|
|
25
|
+
$options: import("vue").ComponentOptionsBase<any, any, any, any, any, any, any, any, any, {}, {}, string, {}, {}, {}, string, import("vue").ComponentProvideOptions> & {
|
|
26
|
+
beforeCreate?: (() => void) | (() => void)[];
|
|
27
|
+
created?: (() => void) | (() => void)[];
|
|
28
|
+
beforeMount?: (() => void) | (() => void)[];
|
|
29
|
+
mounted?: (() => void) | (() => void)[];
|
|
30
|
+
beforeUpdate?: (() => void) | (() => void)[];
|
|
31
|
+
updated?: (() => void) | (() => void)[];
|
|
32
|
+
activated?: (() => void) | (() => void)[];
|
|
33
|
+
deactivated?: (() => void) | (() => void)[];
|
|
34
|
+
beforeDestroy?: (() => void) | (() => void)[];
|
|
35
|
+
beforeUnmount?: (() => void) | (() => void)[];
|
|
36
|
+
destroyed?: (() => void) | (() => void)[];
|
|
37
|
+
unmounted?: (() => void) | (() => void)[];
|
|
38
|
+
renderTracked?: ((e: import("vue").DebuggerEvent) => void) | ((e: import("vue").DebuggerEvent) => void)[];
|
|
39
|
+
renderTriggered?: ((e: import("vue").DebuggerEvent) => void) | ((e: import("vue").DebuggerEvent) => void)[];
|
|
40
|
+
errorCaptured?: ((err: unknown, instance: import("vue").ComponentPublicInstance | null, info: string) => boolean | void) | ((err: unknown, instance: import("vue").ComponentPublicInstance | null, info: string) => boolean | void)[];
|
|
41
|
+
};
|
|
42
|
+
$forceUpdate: () => void;
|
|
43
|
+
$nextTick: typeof import("vue").nextTick;
|
|
44
|
+
$watch<T extends string | ((...args: any) => any)>(source: T, cb: T extends (...args: any) => infer R ? (...args: [R, R, import("@vue/reactivity").OnCleanup]) => any : (...args: [any, any, import("@vue/reactivity").OnCleanup]) => any, options?: import("vue").WatchOptions): import("vue").WatchStopHandle;
|
|
45
|
+
} & Readonly<{}> & Omit<{}, never> & import("vue").ShallowUnwrapRef<{}> & {} & import("vue").ComponentCustomProperties & {}>[];
|
|
46
|
+
addInstance(instance: VueWrapper): void;
|
|
47
|
+
hasNuxtPage(): boolean;
|
|
48
|
+
_hasComponent(componentName: string): boolean;
|
|
49
|
+
};
|
|
50
|
+
export {};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { config } from "@vue/test-utils";
|
|
2
|
+
const PLUGIN_NAME = "nuxt-test-utils";
|
|
3
|
+
export function getVueWrapperPlugin() {
|
|
4
|
+
const installed = config.plugins.VueWrapper.installedPlugins.find(({ options: options2 }) => options2._name === PLUGIN_NAME);
|
|
5
|
+
if (installed) return installed.options;
|
|
6
|
+
const options = createPluginOptions();
|
|
7
|
+
config.plugins.VueWrapper.install((instance, options2) => {
|
|
8
|
+
options2.addInstance(instance);
|
|
9
|
+
return {};
|
|
10
|
+
}, options);
|
|
11
|
+
return options;
|
|
12
|
+
}
|
|
13
|
+
function createPluginOptions() {
|
|
14
|
+
const options = {
|
|
15
|
+
_name: PLUGIN_NAME,
|
|
16
|
+
_instances: [],
|
|
17
|
+
get instances() {
|
|
18
|
+
const instances = [];
|
|
19
|
+
options._instances = options._instances.filter((ref) => {
|
|
20
|
+
const instance = ref.deref();
|
|
21
|
+
if (!instance) return false;
|
|
22
|
+
instances.push(instance);
|
|
23
|
+
return true;
|
|
24
|
+
});
|
|
25
|
+
return instances;
|
|
26
|
+
},
|
|
27
|
+
addInstance(instance) {
|
|
28
|
+
if (options.instances.includes(instance)) return;
|
|
29
|
+
options._instances.push(new WeakRef(instance));
|
|
30
|
+
},
|
|
31
|
+
hasNuxtPage() {
|
|
32
|
+
return options._hasComponent("NuxtPage");
|
|
33
|
+
},
|
|
34
|
+
_hasComponent(componentName) {
|
|
35
|
+
return options.instances.some(
|
|
36
|
+
(v) => v.exists() && v.findComponent({ name: componentName }).exists()
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
return options;
|
|
41
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { EventHandler, HTTPMethod } from 'h3';
|
|
2
2
|
import { SetupContext, RenderFunction, ComputedOptions, MethodOptions, ComponentOptionsMixin, EmitsOptions, ComponentInjectOptions, ComponentOptionsWithoutProps, ComponentOptionsWithArrayProps, ComponentPropsOptions, ComponentOptionsWithObjectProps } from 'vue';
|
|
3
|
-
import {
|
|
3
|
+
import { mount } from '@vue/test-utils';
|
|
4
4
|
import { RouteLocationRaw } from 'vue-router';
|
|
5
|
-
import {
|
|
5
|
+
import { render } from '@testing-library/vue';
|
|
6
6
|
|
|
7
7
|
type Awaitable<T> = T | Promise<T>;
|
|
8
8
|
type OptionalFunction<T> = T | (() => Awaitable<T>);
|
|
@@ -95,14 +95,26 @@ declare function mockComponent<Props = {}, RawBindings = {}, D = {}, C extends C
|
|
|
95
95
|
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;
|
|
96
96
|
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;
|
|
97
97
|
|
|
98
|
-
type
|
|
99
|
-
|
|
98
|
+
type SetupState = Record<string, any>;
|
|
99
|
+
type WrapperFnComponent<Fn> = Fn extends (c: infer C, o: infer _) => infer _ ? C : never;
|
|
100
|
+
type WrapperFnOption<Fn> = Fn extends (c: WrapperFnComponent<Fn>, o: infer O) => infer _ ? O : never;
|
|
101
|
+
type WrapperFnResult<Fn> = Fn extends (c: WrapperFnComponent<Fn>, o: WrapperFnOption<Fn>) => infer R ? R : never;
|
|
102
|
+
type WrapperSuspendedOptions<Fn> = WrapperFnOption<Fn> & {
|
|
103
|
+
route?: RouteLocationRaw | false;
|
|
100
104
|
scoped?: boolean;
|
|
101
105
|
};
|
|
102
|
-
type
|
|
103
|
-
setupState: SetupState
|
|
106
|
+
type WrapperSuspendedResult<Fn> = WrapperFnResult<Fn> & {
|
|
107
|
+
setupState: SetupState;
|
|
104
108
|
};
|
|
105
|
-
|
|
109
|
+
declare global {
|
|
110
|
+
interface Window {
|
|
111
|
+
__cleanup?: Array<() => void>;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
type WrapperFn$1<C> = typeof mount<C>;
|
|
116
|
+
type WrapperOptions$1<C> = WrapperSuspendedOptions<WrapperFn$1<C>>;
|
|
117
|
+
type WrapperResult$1<C> = WrapperSuspendedResult<WrapperFn$1<C>>;
|
|
106
118
|
/**
|
|
107
119
|
* `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:
|
|
108
120
|
*
|
|
@@ -129,18 +141,11 @@ type SetupState$1 = Record<string, any>;
|
|
|
129
141
|
* @param component the component to be tested
|
|
130
142
|
* @param options optional options to set up your component
|
|
131
143
|
*/
|
|
132
|
-
declare function mountSuspended<T>(component: T, options?:
|
|
133
|
-
declare global {
|
|
134
|
-
var __cleanup: Array<() => void> | undefined;
|
|
135
|
-
}
|
|
144
|
+
declare function mountSuspended<T>(component: T, options?: WrapperOptions$1<T>): Promise<WrapperResult$1<T>>;
|
|
136
145
|
|
|
137
|
-
type
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
type RenderSuspendeResult = RenderResult & {
|
|
141
|
-
setupState: SetupState;
|
|
142
|
-
};
|
|
143
|
-
type SetupState = Record<string, any>;
|
|
146
|
+
type WrapperFn<C> = typeof render<C>;
|
|
147
|
+
type WrapperOptions<C> = WrapperSuspendedOptions<WrapperFn<C>>;
|
|
148
|
+
type WrapperResult<C> = WrapperSuspendedResult<WrapperFn<C>>;
|
|
144
149
|
/**
|
|
145
150
|
* `renderSuspended` allows you to mount any vue component within the Nuxt environment, allowing async setup and access to injections from your Nuxt plugins.
|
|
146
151
|
*
|
|
@@ -171,11 +176,6 @@ type SetupState = Record<string, any>;
|
|
|
171
176
|
* @param component the component to be tested
|
|
172
177
|
* @param options optional options to set up your component
|
|
173
178
|
*/
|
|
174
|
-
declare function renderSuspended<T>(component: T, options?:
|
|
175
|
-
declare global {
|
|
176
|
-
interface Window {
|
|
177
|
-
__cleanup?: Array<() => void>;
|
|
178
|
-
}
|
|
179
|
-
}
|
|
179
|
+
declare function renderSuspended<T>(component: T, options?: WrapperOptions<T>): Promise<WrapperResult<T>>;
|
|
180
180
|
|
|
181
181
|
export { mockComponent, mockNuxtImport, mountSuspended, registerEndpoint, renderSuspended };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { defineEventHandler } from 'h3';
|
|
2
2
|
import { mount } from '@vue/test-utils';
|
|
3
|
-
import { reactive, h as h$1, Suspense, nextTick, getCurrentInstance,
|
|
3
|
+
import { reactive, h as h$1, Suspense, nextTick as nextTick$1, getCurrentInstance, onErrorCaptured, effectScope } from 'vue';
|
|
4
4
|
import { defu } from 'defu';
|
|
5
5
|
import { defineComponent, useRouter, h, tryUseNuxtApp } from '#imports';
|
|
6
6
|
import NuxtRoot from '#build/root-component.mjs';
|
|
@@ -88,19 +88,34 @@ const RouterLink = defineComponent({
|
|
|
88
88
|
}
|
|
89
89
|
});
|
|
90
90
|
|
|
91
|
-
|
|
92
|
-
const {
|
|
93
|
-
|
|
94
|
-
attrs = {},
|
|
95
|
-
slots = {},
|
|
96
|
-
route = "/",
|
|
97
|
-
..._options
|
|
98
|
-
} = options || {};
|
|
99
|
-
for (const cleanupFunction of globalThis.__cleanup || []) {
|
|
100
|
-
cleanupFunction();
|
|
91
|
+
function cleanupAll() {
|
|
92
|
+
for (const fn of (window.__cleanup || []).splice(0)) {
|
|
93
|
+
fn();
|
|
101
94
|
}
|
|
95
|
+
}
|
|
96
|
+
function addCleanup(fn) {
|
|
97
|
+
window.__cleanup ||= [];
|
|
98
|
+
window.__cleanup.push(fn);
|
|
99
|
+
}
|
|
100
|
+
function runEffectScope(fn) {
|
|
101
|
+
const scope = effectScope();
|
|
102
|
+
addCleanup(() => scope.stop());
|
|
103
|
+
return scope.run(fn);
|
|
104
|
+
}
|
|
105
|
+
function wrapperSuspended(component, options, {
|
|
106
|
+
wrapperFn,
|
|
107
|
+
wrappedRender = (fn) => fn,
|
|
108
|
+
suspendedHelperName,
|
|
109
|
+
clonedComponentName
|
|
110
|
+
}) {
|
|
111
|
+
const { props = {}, attrs = {} } = options;
|
|
112
|
+
const { route = "/", scoped = false, ...wrapperFnOptions } = options;
|
|
102
113
|
const vueApp = tryUseNuxtApp()?.vueApp || globalThis.__unctx__.get("nuxt-app").tryUse().vueApp;
|
|
103
|
-
const {
|
|
114
|
+
const {
|
|
115
|
+
render: componentRender,
|
|
116
|
+
setup: componentSetup,
|
|
117
|
+
...componentRest
|
|
118
|
+
} = component;
|
|
104
119
|
let wrappedInstance = null;
|
|
105
120
|
let setupContext;
|
|
106
121
|
let setupState;
|
|
@@ -113,28 +128,19 @@ async function mountSuspended(component, options) {
|
|
|
113
128
|
app[key] = value;
|
|
114
129
|
}
|
|
115
130
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
if (options?.scoped) {
|
|
127
|
-
componentScope = effectScope();
|
|
128
|
-
globalThis.__cleanup ||= [];
|
|
129
|
-
globalThis.__cleanup.push(() => {
|
|
130
|
-
componentScope?.stop();
|
|
131
|
-
});
|
|
132
|
-
result = await componentScope?.run(async () => {
|
|
133
|
-
return await setup(props2, setupContext2);
|
|
134
|
-
});
|
|
135
|
-
} else {
|
|
136
|
-
result = await setup(props2, setupContext2);
|
|
131
|
+
const ClonedComponent = {
|
|
132
|
+
components: {},
|
|
133
|
+
...component,
|
|
134
|
+
name: clonedComponentName,
|
|
135
|
+
async setup(props2, instanceContext) {
|
|
136
|
+
const currentInstance = getCurrentInstance();
|
|
137
|
+
if (currentInstance) {
|
|
138
|
+
currentInstance.emit = (event, ...args) => {
|
|
139
|
+
setupContext.emit(event, ...args);
|
|
140
|
+
};
|
|
137
141
|
}
|
|
142
|
+
if (!componentSetup) return;
|
|
143
|
+
const result = scoped ? await runEffectScope(() => componentSetup(props2, setupContext)) : await componentSetup(props2, setupContext);
|
|
138
144
|
if (wrappedInstance?.exposed) {
|
|
139
145
|
instanceContext.expose(wrappedInstance.exposed);
|
|
140
146
|
}
|
|
@@ -142,97 +148,110 @@ async function mountSuspended(component, options) {
|
|
|
142
148
|
return result;
|
|
143
149
|
}
|
|
144
150
|
};
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
{
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
};
|
|
182
|
-
resolve(wrappedMountedWrapper(vm));
|
|
183
|
-
})
|
|
184
|
-
},
|
|
185
|
-
{
|
|
186
|
-
default: () => h$1({
|
|
187
|
-
name: "MountSuspendedHelper",
|
|
188
|
-
async setup() {
|
|
189
|
-
const router = useRouter();
|
|
190
|
-
await router.replace(route);
|
|
191
|
-
const clonedComponent = {
|
|
192
|
-
components: {},
|
|
193
|
-
...component,
|
|
194
|
-
name: "MountSuspendedComponent",
|
|
195
|
-
setup: (props2, ctx) => wrappedSetup(props2, setupContext, ctx)
|
|
196
|
-
};
|
|
197
|
-
return () => h$1(clonedComponent, { ...props, ...setProps, ...attrs }, setupContext.slots);
|
|
198
|
-
}
|
|
199
|
-
})
|
|
151
|
+
const SuspendedHelper = {
|
|
152
|
+
name: suspendedHelperName,
|
|
153
|
+
render: () => "",
|
|
154
|
+
async setup() {
|
|
155
|
+
if (route) {
|
|
156
|
+
const router = useRouter();
|
|
157
|
+
await router.replace(route);
|
|
158
|
+
}
|
|
159
|
+
return () => h$1(ClonedComponent, { ...props, ...setProps, ...attrs }, setupContext.slots);
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
return new Promise((resolve, reject) => {
|
|
163
|
+
let isMountSettled = false;
|
|
164
|
+
const wrapper = wrapperFn(
|
|
165
|
+
{
|
|
166
|
+
inheritAttrs: false,
|
|
167
|
+
__cssModules: componentRest.__cssModules,
|
|
168
|
+
setup: (props2, ctx) => {
|
|
169
|
+
patchInstanceAppContext();
|
|
170
|
+
wrappedInstance = getCurrentInstance();
|
|
171
|
+
setupContext = ctx;
|
|
172
|
+
const nuxtRootSetupResult = runEffectScope(
|
|
173
|
+
() => NuxtRoot.setup(props2, {
|
|
174
|
+
...ctx,
|
|
175
|
+
expose: () => {
|
|
176
|
+
}
|
|
177
|
+
})
|
|
178
|
+
);
|
|
179
|
+
onErrorCaptured((error, ...args) => {
|
|
180
|
+
if (isMountSettled) return;
|
|
181
|
+
isMountSettled = true;
|
|
182
|
+
try {
|
|
183
|
+
wrappedInstance?.appContext.config.errorHandler?.(error, ...args);
|
|
184
|
+
reject(error);
|
|
185
|
+
} catch (error2) {
|
|
186
|
+
reject(error2);
|
|
200
187
|
}
|
|
201
|
-
|
|
188
|
+
return false;
|
|
189
|
+
});
|
|
190
|
+
return nuxtRootSetupResult;
|
|
202
191
|
},
|
|
203
|
-
|
|
204
|
-
|
|
192
|
+
render: wrappedRender(() => h$1(
|
|
193
|
+
Suspense,
|
|
205
194
|
{
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
...Object.fromEntries(
|
|
215
|
-
Object.getOwnPropertyNames(vueApp.config.globalProperties).map((key) => [key, vueApp.config.globalProperties[key]])
|
|
216
|
-
)
|
|
195
|
+
onResolve: () => nextTick$1().then(() => {
|
|
196
|
+
if (isMountSettled) return;
|
|
197
|
+
isMountSettled = true;
|
|
198
|
+
wrapper.setupState = setupState;
|
|
199
|
+
resolve({
|
|
200
|
+
wrapper,
|
|
201
|
+
setProps: (props2) => {
|
|
202
|
+
Object.assign(setProps, props2);
|
|
217
203
|
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
MountSuspendedHelper: false,
|
|
224
|
-
[component && typeof component === "object" && "name" in component && typeof component.name === "string" ? component.name : "MountSuspendedComponent"]: false
|
|
225
|
-
},
|
|
226
|
-
components: { ...vueApp._context.components, RouterLink }
|
|
227
|
-
}
|
|
204
|
+
});
|
|
205
|
+
})
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
default: () => h$1(SuspendedHelper)
|
|
228
209
|
}
|
|
229
|
-
)
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
210
|
+
))
|
|
211
|
+
},
|
|
212
|
+
defu(wrapperFnOptions, {
|
|
213
|
+
global: {
|
|
214
|
+
config: {
|
|
215
|
+
globalProperties: makeAllPropertiesEnumerable(
|
|
216
|
+
vueApp.config.globalProperties
|
|
217
|
+
)
|
|
218
|
+
},
|
|
219
|
+
directives: vueApp._context.directives,
|
|
220
|
+
provide: vueApp._context.provides,
|
|
221
|
+
stubs: {
|
|
222
|
+
Suspense: false,
|
|
223
|
+
[SuspendedHelper.name]: false,
|
|
224
|
+
[ClonedComponent.name]: false
|
|
225
|
+
},
|
|
226
|
+
components: { ...vueApp._context.components, RouterLink }
|
|
227
|
+
}
|
|
228
|
+
})
|
|
229
|
+
);
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
function makeAllPropertiesEnumerable(target) {
|
|
233
|
+
return {
|
|
234
|
+
...target,
|
|
235
|
+
...Object.fromEntries(
|
|
236
|
+
Object.getOwnPropertyNames(target).map((key) => [key, target[key]])
|
|
237
|
+
)
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
async function mountSuspended(component, options = {}) {
|
|
242
|
+
const suspendedHelperName = "MountSuspendedHelper";
|
|
243
|
+
const clonedComponentName = "MountSuspendedComponent";
|
|
244
|
+
cleanupAll();
|
|
245
|
+
const { wrapper, setProps } = await wrapperSuspended(component, options, {
|
|
246
|
+
wrapperFn: mount,
|
|
247
|
+
suspendedHelperName,
|
|
248
|
+
clonedComponentName
|
|
249
|
+
});
|
|
250
|
+
Object.assign(wrapper, { __setProps: setProps });
|
|
251
|
+
const clonedComponent = wrapper.findComponent({ name: clonedComponentName });
|
|
252
|
+
return wrappedMountedWrapper(wrapper, clonedComponent);
|
|
233
253
|
}
|
|
234
|
-
function wrappedMountedWrapper(wrapper) {
|
|
235
|
-
const component = wrapper.findComponent({ name: "MountSuspendedComponent" });
|
|
254
|
+
function wrappedMountedWrapper(wrapper, component) {
|
|
236
255
|
const wrapperProps = [
|
|
237
256
|
"setProps",
|
|
238
257
|
"emitted",
|
|
@@ -273,138 +292,27 @@ function wrappedMountedWrapper(wrapper) {
|
|
|
273
292
|
}
|
|
274
293
|
}
|
|
275
294
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
const
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
const setProps = reactive({});
|
|
292
|
-
function patchInstanceAppContext() {
|
|
293
|
-
const app = getCurrentInstance()?.appContext.app;
|
|
294
|
-
if (!app) return;
|
|
295
|
-
for (const [key, value] of Object.entries(vueApp)) {
|
|
296
|
-
if (key in app) continue;
|
|
297
|
-
app[key] = value;
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
for (const fn of window.__cleanup || []) {
|
|
301
|
-
fn();
|
|
302
|
-
}
|
|
303
|
-
document.querySelector(`#${WRAPPER_EL_ID}`)?.remove();
|
|
304
|
-
const wrappedSetup = async (props2, setupContext2, instanceContext) => {
|
|
305
|
-
const currentInstance = getCurrentInstance();
|
|
306
|
-
if (currentInstance) {
|
|
307
|
-
currentInstance.emit = (event, ...args) => {
|
|
308
|
-
setupContext2.emit(event, ...args);
|
|
309
|
-
};
|
|
310
|
-
}
|
|
311
|
-
if (setup) {
|
|
312
|
-
const result = await setup(props2, setupContext2);
|
|
313
|
-
setupState = result && typeof result === "object" ? result : {};
|
|
314
|
-
if (wrappedInstance?.exposed) {
|
|
315
|
-
instanceContext.expose(wrappedInstance.exposed);
|
|
316
|
-
}
|
|
317
|
-
return result;
|
|
318
|
-
}
|
|
319
|
-
};
|
|
320
|
-
const WrapperComponent = defineComponent$1({
|
|
321
|
-
inheritAttrs: false,
|
|
322
|
-
render() {
|
|
323
|
-
return h$1("div", { id: WRAPPER_EL_ID }, this.$slots.default?.());
|
|
324
|
-
}
|
|
325
|
-
});
|
|
326
|
-
return new Promise((resolve) => {
|
|
327
|
-
const utils = renderFromTestingLibrary(
|
|
328
|
-
{
|
|
329
|
-
__cssModules: componentRest.__cssModules,
|
|
330
|
-
inheritAttrs: false,
|
|
331
|
-
setup: (props2, ctx) => {
|
|
332
|
-
patchInstanceAppContext();
|
|
333
|
-
wrappedInstance = getCurrentInstance();
|
|
334
|
-
setupContext = ctx;
|
|
335
|
-
const scope = effectScope();
|
|
336
|
-
window.__cleanup ||= [];
|
|
337
|
-
window.__cleanup.push(() => {
|
|
338
|
-
scope.stop();
|
|
339
|
-
});
|
|
340
|
-
return scope.run(() => NuxtRoot.setup(props2, {
|
|
341
|
-
...ctx,
|
|
342
|
-
expose: () => ({})
|
|
343
|
-
}));
|
|
344
|
-
},
|
|
345
|
-
render: () => (
|
|
346
|
-
// See discussions in https://github.com/testing-library/vue-testing-library/issues/230
|
|
347
|
-
// we add this additional root element because otherwise testing-library breaks
|
|
348
|
-
// because there's no root element while Suspense is resolving
|
|
349
|
-
h$1(
|
|
350
|
-
WrapperComponent,
|
|
351
|
-
{},
|
|
352
|
-
{
|
|
353
|
-
default: () => h$1(
|
|
354
|
-
Suspense,
|
|
355
|
-
{
|
|
356
|
-
onResolve: () => nextTick().then(() => {
|
|
357
|
-
utils.setupState = setupState;
|
|
358
|
-
utils.rerender = async (props2) => {
|
|
359
|
-
Object.assign(setProps, props2);
|
|
360
|
-
await nextTick();
|
|
361
|
-
};
|
|
362
|
-
resolve(utils);
|
|
363
|
-
})
|
|
364
|
-
},
|
|
365
|
-
{
|
|
366
|
-
default: () => h$1({
|
|
367
|
-
name: "RenderHelper",
|
|
368
|
-
async setup() {
|
|
369
|
-
const router = useRouter();
|
|
370
|
-
await router.replace(route);
|
|
371
|
-
const clonedComponent = {
|
|
372
|
-
components: {},
|
|
373
|
-
...component,
|
|
374
|
-
name: "RenderSuspendedComponent",
|
|
375
|
-
render,
|
|
376
|
-
setup: (props2, ctx) => wrappedSetup(props2, setupContext, ctx)
|
|
377
|
-
};
|
|
378
|
-
return () => h$1(clonedComponent, { ...props && typeof props === "object" ? props : {}, ...setProps, ...attrs }, setupContext.slots);
|
|
379
|
-
}
|
|
380
|
-
})
|
|
381
|
-
}
|
|
382
|
-
)
|
|
383
|
-
}
|
|
384
|
-
)
|
|
385
|
-
)
|
|
386
|
-
},
|
|
387
|
-
defu(_options, {
|
|
388
|
-
props,
|
|
389
|
-
slots,
|
|
390
|
-
attrs,
|
|
391
|
-
global: {
|
|
392
|
-
config: {
|
|
393
|
-
globalProperties: {
|
|
394
|
-
...vueApp.config.globalProperties,
|
|
395
|
-
// make all properties/keys enumerable.
|
|
396
|
-
...Object.fromEntries(
|
|
397
|
-
Object.getOwnPropertyNames(vueApp.config.globalProperties).map((key) => [key, vueApp.config.globalProperties[key]])
|
|
398
|
-
)
|
|
399
|
-
}
|
|
400
|
-
},
|
|
401
|
-
directives: vueApp._context.directives,
|
|
402
|
-
provide: vueApp._context.provides,
|
|
403
|
-
components: { RouterLink }
|
|
404
|
-
}
|
|
405
|
-
})
|
|
406
|
-
);
|
|
295
|
+
async function renderSuspended(component, options = {}) {
|
|
296
|
+
const wrapperId = "test-wrapper";
|
|
297
|
+
const suspendedHelperName = "RenderHelper";
|
|
298
|
+
const clonedComponentName = "RenderSuspendedComponent";
|
|
299
|
+
const { render: wrapperFn } = await import('@testing-library/vue');
|
|
300
|
+
cleanupAll();
|
|
301
|
+
document.getElementById(wrapperId)?.remove();
|
|
302
|
+
const { wrapper, setProps } = await wrapperSuspended(component, options, {
|
|
303
|
+
wrapperFn,
|
|
304
|
+
wrappedRender: (render) => () => h$1({
|
|
305
|
+
inheritAttrs: false,
|
|
306
|
+
render: () => h$1("div", { id: wrapperId }, render())
|
|
307
|
+
}),
|
|
308
|
+
suspendedHelperName,
|
|
309
|
+
clonedComponentName
|
|
407
310
|
});
|
|
311
|
+
wrapper.rerender = async (props) => {
|
|
312
|
+
setProps(props);
|
|
313
|
+
await nextTick();
|
|
314
|
+
};
|
|
315
|
+
return wrapper;
|
|
408
316
|
}
|
|
409
317
|
|
|
410
318
|
export { mockComponent, mockNuxtImport, mountSuspended, registerEndpoint, renderSuspended };
|