@nuxt/test-utils 4.0.1 → 4.0.3
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/dist/chunks/suspended.mjs +221 -0
- package/dist/config.mjs +15 -1
- package/dist/e2e.d.mts +2 -2
- package/dist/e2e.mjs +3 -3
- package/dist/experimental.mjs +1 -1
- package/dist/module.mjs +7 -1
- package/dist/playwright.d.mts +1 -1
- package/dist/playwright.mjs +2 -2
- package/dist/runtime-utils/index.mjs +5 -217
- package/dist/shared/{test-utils.BLyxqr96.d.mts → test-utils.BX46zKiB.d.mts} +7 -0
- package/dist/shared/{test-utils.BsmyE2FA.mjs → test-utils.BrQgu3Ob.mjs} +44 -21
- package/dist/shared/{test-utils.E_cAGA8l.mjs → test-utils.RVsKJA_7.mjs} +1 -1
- package/package.json +39 -41
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { reactive, h as h$1, Suspense, nextTick, getCurrentInstance, onErrorCaptured, effectScope } from 'vue';
|
|
2
|
+
import { defineComponent, h, tryUseNuxtApp, useRouter } from '#imports';
|
|
3
|
+
import NuxtRoot from '#build/root-component.mjs';
|
|
4
|
+
import { useLink } from 'vue-router';
|
|
5
|
+
|
|
6
|
+
const RouterLink = defineComponent({
|
|
7
|
+
functional: true,
|
|
8
|
+
props: {
|
|
9
|
+
to: {
|
|
10
|
+
type: [String, Object],
|
|
11
|
+
required: true
|
|
12
|
+
},
|
|
13
|
+
custom: Boolean,
|
|
14
|
+
replace: Boolean,
|
|
15
|
+
// Not implemented
|
|
16
|
+
activeClass: String,
|
|
17
|
+
exactActiveClass: String,
|
|
18
|
+
ariaCurrentValue: String
|
|
19
|
+
},
|
|
20
|
+
setup: (props, { slots }) => {
|
|
21
|
+
const link = useLink(props);
|
|
22
|
+
return () => {
|
|
23
|
+
const route = link.route.value;
|
|
24
|
+
const href = link.href.value;
|
|
25
|
+
const isActive = link.isActive.value;
|
|
26
|
+
const isExactActive = link.isExactActive.value;
|
|
27
|
+
return props.custom ? slots.default?.({ href, navigate: link.navigate, route, isActive, isExactActive }) : h(
|
|
28
|
+
"a",
|
|
29
|
+
{
|
|
30
|
+
href,
|
|
31
|
+
onClick: (e) => {
|
|
32
|
+
e.preventDefault();
|
|
33
|
+
return link.navigate(e);
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
slots
|
|
37
|
+
);
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
function cleanupAll() {
|
|
43
|
+
for (const fn of (window.__cleanup || []).splice(0)) {
|
|
44
|
+
fn();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function addCleanup(fn) {
|
|
48
|
+
window.__cleanup ||= [];
|
|
49
|
+
window.__cleanup.push(fn);
|
|
50
|
+
}
|
|
51
|
+
function runEffectScope(fn) {
|
|
52
|
+
const scope = effectScope();
|
|
53
|
+
addCleanup(() => scope.stop());
|
|
54
|
+
return scope.run(fn);
|
|
55
|
+
}
|
|
56
|
+
function wrapperSuspended(component, options, {
|
|
57
|
+
wrapperFn,
|
|
58
|
+
wrappedRender = (fn) => fn,
|
|
59
|
+
suspendedHelperName,
|
|
60
|
+
clonedComponentName
|
|
61
|
+
}) {
|
|
62
|
+
const { props = {}, attrs = {} } = options;
|
|
63
|
+
const { route = "/", scoped = false, ...wrapperFnOptions } = options;
|
|
64
|
+
const vueApp = tryUseNuxtApp()?.vueApp || globalThis.__unctx__.get("nuxt-app").tryUse().vueApp;
|
|
65
|
+
const {
|
|
66
|
+
render: componentRender,
|
|
67
|
+
setup: componentSetup,
|
|
68
|
+
...componentRest
|
|
69
|
+
} = component;
|
|
70
|
+
let wrappedInstance = null;
|
|
71
|
+
let setupContext;
|
|
72
|
+
let setupState;
|
|
73
|
+
const setProps = reactive({});
|
|
74
|
+
function patchInstanceAppContext() {
|
|
75
|
+
const app = getCurrentInstance()?.appContext.app;
|
|
76
|
+
if (!app) return;
|
|
77
|
+
for (const [key, value] of Object.entries(vueApp)) {
|
|
78
|
+
if (key in app) continue;
|
|
79
|
+
app[key] = value;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const ClonedComponent = {
|
|
83
|
+
components: {},
|
|
84
|
+
...component,
|
|
85
|
+
name: clonedComponentName,
|
|
86
|
+
async setup(props2, instanceContext) {
|
|
87
|
+
const currentInstance = getCurrentInstance();
|
|
88
|
+
if (currentInstance) {
|
|
89
|
+
currentInstance.emit = (event, ...args) => {
|
|
90
|
+
setupContext.emit(event, ...args);
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
if (!componentSetup) return;
|
|
94
|
+
const result = scoped ? await runEffectScope(() => componentSetup(props2, setupContext)) : await componentSetup(props2, setupContext);
|
|
95
|
+
if (wrappedInstance?.exposed) {
|
|
96
|
+
instanceContext.expose(wrappedInstance.exposed);
|
|
97
|
+
}
|
|
98
|
+
setupState = result && typeof result === "object" ? result : {};
|
|
99
|
+
return result;
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
const SuspendedHelper = {
|
|
103
|
+
name: suspendedHelperName,
|
|
104
|
+
render: () => "",
|
|
105
|
+
async setup() {
|
|
106
|
+
if (route) {
|
|
107
|
+
const router = useRouter();
|
|
108
|
+
await router.replace(route);
|
|
109
|
+
}
|
|
110
|
+
return () => h$1(ClonedComponent, { ...props, ...setProps, ...attrs }, setupContext.slots);
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
return new Promise((resolve, reject) => {
|
|
114
|
+
let isMountSettled = false;
|
|
115
|
+
const wrapper = wrapperFn(
|
|
116
|
+
{
|
|
117
|
+
inheritAttrs: false,
|
|
118
|
+
__cssModules: componentRest.__cssModules,
|
|
119
|
+
setup: (props2, ctx) => {
|
|
120
|
+
patchInstanceAppContext();
|
|
121
|
+
wrappedInstance = getCurrentInstance();
|
|
122
|
+
setupContext = ctx;
|
|
123
|
+
const nuxtRootSetupResult = runEffectScope(
|
|
124
|
+
() => NuxtRoot.setup(props2, {
|
|
125
|
+
...ctx,
|
|
126
|
+
expose: () => {
|
|
127
|
+
}
|
|
128
|
+
})
|
|
129
|
+
);
|
|
130
|
+
onErrorCaptured((error, ...args) => {
|
|
131
|
+
if (isMountSettled) return;
|
|
132
|
+
isMountSettled = true;
|
|
133
|
+
try {
|
|
134
|
+
wrappedInstance?.appContext.config.errorHandler?.(error, ...args);
|
|
135
|
+
reject(error);
|
|
136
|
+
} catch (error2) {
|
|
137
|
+
reject(error2);
|
|
138
|
+
}
|
|
139
|
+
return false;
|
|
140
|
+
});
|
|
141
|
+
return nuxtRootSetupResult;
|
|
142
|
+
},
|
|
143
|
+
render: wrappedRender(() => h$1(
|
|
144
|
+
Suspense,
|
|
145
|
+
{
|
|
146
|
+
onResolve: () => nextTick().then(() => {
|
|
147
|
+
if (isMountSettled) return;
|
|
148
|
+
isMountSettled = true;
|
|
149
|
+
wrapper.setupState = setupState;
|
|
150
|
+
resolve({
|
|
151
|
+
wrapper,
|
|
152
|
+
setProps: (props2) => {
|
|
153
|
+
Object.assign(setProps, props2);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
})
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
default: () => h$1(SuspendedHelper)
|
|
160
|
+
}
|
|
161
|
+
))
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
...wrapperFnOptions,
|
|
165
|
+
global: mergeComponentMountingGlobalOptions(wrapperFnOptions.global, {
|
|
166
|
+
config: {
|
|
167
|
+
globalProperties: makeAllPropertiesEnumerable(
|
|
168
|
+
vueApp.config.globalProperties
|
|
169
|
+
)
|
|
170
|
+
},
|
|
171
|
+
directives: vueApp._context.directives,
|
|
172
|
+
provide: vueApp._context.provides,
|
|
173
|
+
stubs: {
|
|
174
|
+
Suspense: false,
|
|
175
|
+
[SuspendedHelper.name]: false,
|
|
176
|
+
[ClonedComponent.name]: false
|
|
177
|
+
},
|
|
178
|
+
components: { ...vueApp._context.components, RouterLink }
|
|
179
|
+
})
|
|
180
|
+
}
|
|
181
|
+
);
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
function mergeComponentMountingGlobalOptions(options = {}, defaults = {}) {
|
|
185
|
+
const compilerOptions = {
|
|
186
|
+
...defaults.config?.compilerOptions,
|
|
187
|
+
...options.config?.compilerOptions
|
|
188
|
+
};
|
|
189
|
+
return {
|
|
190
|
+
...options,
|
|
191
|
+
mixins: [...defaults.mixins || [], ...options.mixins || []],
|
|
192
|
+
stubs: {
|
|
193
|
+
...defaults.stubs,
|
|
194
|
+
...Array.isArray(options.stubs) ? Object.fromEntries(options.stubs.map((n) => [n, true])) : options.stubs
|
|
195
|
+
},
|
|
196
|
+
plugins: [...defaults.plugins || [], ...options.plugins || []],
|
|
197
|
+
components: { ...defaults.components, ...options.components },
|
|
198
|
+
provide: { ...defaults.provide, ...options.provide },
|
|
199
|
+
mocks: { ...defaults.mocks, ...options.mocks },
|
|
200
|
+
config: {
|
|
201
|
+
...defaults.config,
|
|
202
|
+
...options.config,
|
|
203
|
+
...Object.keys(compilerOptions).length ? { compilerOptions } : void 0,
|
|
204
|
+
globalProperties: {
|
|
205
|
+
...defaults.config?.globalProperties,
|
|
206
|
+
...options.config?.globalProperties
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
directives: { ...defaults.directives, ...options.directives }
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
function makeAllPropertiesEnumerable(target) {
|
|
213
|
+
return {
|
|
214
|
+
...target,
|
|
215
|
+
...Object.fromEntries(
|
|
216
|
+
Object.getOwnPropertyNames(target).map((key) => [key, target[key]])
|
|
217
|
+
)
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export { cleanupAll, wrapperSuspended };
|
package/dist/config.mjs
CHANGED
|
@@ -32,7 +32,8 @@ async function startNuxtAndGetViteConfig(rootDir = process.cwd(), options = {})
|
|
|
32
32
|
const { buildNuxt, loadNuxt } = await loadKit(rootDir);
|
|
33
33
|
const nuxt = await loadNuxt({
|
|
34
34
|
cwd: rootDir,
|
|
35
|
-
|
|
35
|
+
// https://github.com/nuxt/nuxt/blob/d52a4fdd7ad5feb035dcf3f56c3b2d0ab059b1d4/packages/kit/src/loader/nuxt.ts#L24
|
|
36
|
+
dev: options.overrides?.dev ?? false,
|
|
36
37
|
dotenv: defu(options.dotenv, {
|
|
37
38
|
cwd: rootDir,
|
|
38
39
|
fileName: ".env.test"
|
|
@@ -289,9 +290,22 @@ function defineVitestConfig(config = {}) {
|
|
|
289
290
|
return resolvedConfig;
|
|
290
291
|
});
|
|
291
292
|
}
|
|
293
|
+
function isCoverageEnabled(config) {
|
|
294
|
+
if (config.test && "coverage" in config.test && config.test.coverage?.enabled) {
|
|
295
|
+
return true;
|
|
296
|
+
}
|
|
297
|
+
return process.argv.some((arg) => arg === "--coverage" || arg === "--coverage.enabled" || arg === "--coverage=true" || arg === "--coverage.enabled=true");
|
|
298
|
+
}
|
|
292
299
|
async function resolveConfig(config) {
|
|
293
300
|
const overrides = config.test?.environmentOptions?.nuxt?.overrides || {};
|
|
294
301
|
overrides.rootDir = config.test?.environmentOptions?.nuxt?.rootDir;
|
|
302
|
+
if (isCoverageEnabled(config)) {
|
|
303
|
+
if (overrides.sourcemap === void 0) {
|
|
304
|
+
overrides.sourcemap = { client: true };
|
|
305
|
+
} else if (typeof overrides.sourcemap === "object" && overrides.sourcemap.client === void 0) {
|
|
306
|
+
overrides.sourcemap.client = true;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
295
309
|
if (config.test?.setupFiles && !Array.isArray(config.test.setupFiles)) {
|
|
296
310
|
config.test.setupFiles = [config.test.setupFiles].filter(Boolean);
|
|
297
311
|
}
|
package/dist/e2e.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { T as TestOptions, b as TestContext, a as TestHooks } from './shared/test-utils.
|
|
2
|
-
export { $ as $fetch, G as GotoOptions, N as NuxtPage, S as StartServerOptions, c as TestRunner, d as createBrowser, e as createPage, f as fetch, g as getBrowser, s as startServer, h as stopServer, u as url, w as waitForHydration } from './shared/test-utils.
|
|
1
|
+
import { T as TestOptions, b as TestContext, a as TestHooks } from './shared/test-utils.BX46zKiB.mjs';
|
|
2
|
+
export { $ as $fetch, G as GotoOptions, N as NuxtPage, S as StartServerOptions, c as TestRunner, d as createBrowser, e as createPage, f as fetch, g as getBrowser, s as startServer, h as stopServer, u as url, w as waitForHydration } from './shared/test-utils.BX46zKiB.mjs';
|
|
3
3
|
import { LogType } from 'consola';
|
|
4
4
|
import 'playwright-core';
|
|
5
5
|
import '@nuxt/schema';
|
package/dist/e2e.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export { b as buildFixture, c as createBrowser, a as createPage, d as createTest, g as getBrowser, l as loadFixture, s as setup, e as setupMaps, w as waitForHydration } from './shared/test-utils.
|
|
2
|
-
import { u as useTestContext } from './shared/test-utils.
|
|
3
|
-
export { $ as $fetch, c as createTestContext, e as exposeContextToEnv, f as fetch, i as isDev, r as recoverContextFromEnv, s as setTestContext, a as startServer, b as stopServer, d as url } from './shared/test-utils.
|
|
1
|
+
export { b as buildFixture, c as createBrowser, a as createPage, d as createTest, g as getBrowser, l as loadFixture, s as setup, e as setupMaps, w as waitForHydration } from './shared/test-utils.RVsKJA_7.mjs';
|
|
2
|
+
import { u as useTestContext } from './shared/test-utils.BrQgu3Ob.mjs';
|
|
3
|
+
export { $ as $fetch, c as createTestContext, e as exposeContextToEnv, f as fetch, i as isDev, r as recoverContextFromEnv, s as setTestContext, a as startServer, b as stopServer, d as url } from './shared/test-utils.BrQgu3Ob.mjs';
|
|
4
4
|
import { consola } from 'consola';
|
|
5
5
|
import { resolve } from 'pathe';
|
|
6
6
|
import { distDir } from '#dirs';
|
package/dist/experimental.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { resolve } from 'pathe';
|
|
2
2
|
import { stringifyQuery } from 'ufo';
|
|
3
|
-
import { $ as $fetch, u as useTestContext } from './shared/test-utils.
|
|
3
|
+
import { $ as $fetch, u as useTestContext } from './shared/test-utils.BrQgu3Ob.mjs';
|
|
4
4
|
import 'tinyexec';
|
|
5
5
|
import 'get-port-please';
|
|
6
6
|
import 'ofetch';
|
package/dist/module.mjs
CHANGED
|
@@ -263,6 +263,9 @@ function endOf(node) {
|
|
|
263
263
|
return "range" in node && node.range ? node.range[1] : "end" in node ? node.end : void 0;
|
|
264
264
|
}
|
|
265
265
|
|
|
266
|
+
function isTestPluginFile(src) {
|
|
267
|
+
return src.includes(".spec.") || src.includes(".test.");
|
|
268
|
+
}
|
|
266
269
|
async function setupImportMocking(nuxt) {
|
|
267
270
|
const { addVitePlugin } = await loadKit(nuxt.options.rootDir);
|
|
268
271
|
const ctx = {
|
|
@@ -291,6 +294,9 @@ async function setupImportMocking(nuxt) {
|
|
|
291
294
|
nuxt._ignore.add(`!${pattern}`);
|
|
292
295
|
}
|
|
293
296
|
}
|
|
297
|
+
nuxt.hook("app:resolve", (app) => {
|
|
298
|
+
app.plugins = app.plugins.filter((plugin) => !isTestPluginFile(plugin.src));
|
|
299
|
+
});
|
|
294
300
|
addVitePlugin(createMockPlugin(ctx).vite());
|
|
295
301
|
}
|
|
296
302
|
|
|
@@ -952,7 +958,7 @@ function vitestWrapper(options) {
|
|
|
952
958
|
};
|
|
953
959
|
}
|
|
954
960
|
|
|
955
|
-
const version = "4.0.
|
|
961
|
+
const version = "4.0.3";
|
|
956
962
|
const pkg = {
|
|
957
963
|
version: version};
|
|
958
964
|
|
package/dist/playwright.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as _playwright_test from '@playwright/test';
|
|
2
2
|
export { expect } from '@playwright/test';
|
|
3
3
|
import { Response } from 'playwright-core';
|
|
4
|
-
import { T as TestOptions$1, G as GotoOptions, a as TestHooks } from './shared/test-utils.
|
|
4
|
+
import { T as TestOptions$1, G as GotoOptions, a as TestHooks } from './shared/test-utils.BX46zKiB.mjs';
|
|
5
5
|
import '@nuxt/schema';
|
|
6
6
|
import 'tinyexec';
|
|
7
7
|
import 'ofetch';
|
package/dist/playwright.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import defu from 'defu';
|
|
|
2
2
|
import { test as test$1 } from '@playwright/test';
|
|
3
3
|
export { expect } from '@playwright/test';
|
|
4
4
|
import { isWindows } from 'std-env';
|
|
5
|
-
import { w as waitForHydration, d as createTest } from './shared/test-utils.
|
|
5
|
+
import { w as waitForHydration, d as createTest } from './shared/test-utils.RVsKJA_7.mjs';
|
|
6
6
|
import 'node:path';
|
|
7
7
|
import 'ufo';
|
|
8
8
|
import 'consola';
|
|
@@ -11,7 +11,7 @@ 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.BrQgu3Ob.mjs';
|
|
15
15
|
import 'pathe';
|
|
16
16
|
import '#dirs';
|
|
17
17
|
import './shared/test-utils.BIY9XRkB.mjs';
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { mount } from '@vue/test-utils';
|
|
2
|
-
import {
|
|
3
|
-
import { defineComponent, useRouter, h, tryUseNuxtApp } from '#imports';
|
|
4
|
-
import NuxtRoot from '#build/root-component.mjs';
|
|
2
|
+
import { h, nextTick } from 'vue';
|
|
5
3
|
|
|
6
4
|
function getEndpointRegistry() {
|
|
7
5
|
const app = window.__app ?? {};
|
|
@@ -75,219 +73,8 @@ function registerGlobalHandler(app) {
|
|
|
75
73
|
return true;
|
|
76
74
|
}
|
|
77
75
|
|
|
78
|
-
const RouterLink = defineComponent({
|
|
79
|
-
functional: true,
|
|
80
|
-
props: {
|
|
81
|
-
to: {
|
|
82
|
-
type: [String, Object],
|
|
83
|
-
required: true
|
|
84
|
-
},
|
|
85
|
-
custom: Boolean,
|
|
86
|
-
replace: Boolean,
|
|
87
|
-
// Not implemented
|
|
88
|
-
activeClass: String,
|
|
89
|
-
exactActiveClass: String,
|
|
90
|
-
ariaCurrentValue: String
|
|
91
|
-
},
|
|
92
|
-
setup: (props, { slots }) => {
|
|
93
|
-
const navigate = () => {
|
|
94
|
-
};
|
|
95
|
-
return () => {
|
|
96
|
-
const route = useRouter().resolve(props.to);
|
|
97
|
-
return props.custom ? slots.default?.({ href: route.href, navigate, route }) : h(
|
|
98
|
-
"a",
|
|
99
|
-
{
|
|
100
|
-
href: route.href,
|
|
101
|
-
onClick: (e) => {
|
|
102
|
-
e.preventDefault();
|
|
103
|
-
return navigate();
|
|
104
|
-
}
|
|
105
|
-
},
|
|
106
|
-
slots
|
|
107
|
-
);
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
function cleanupAll() {
|
|
113
|
-
for (const fn of (window.__cleanup || []).splice(0)) {
|
|
114
|
-
fn();
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
function addCleanup(fn) {
|
|
118
|
-
window.__cleanup ||= [];
|
|
119
|
-
window.__cleanup.push(fn);
|
|
120
|
-
}
|
|
121
|
-
function runEffectScope(fn) {
|
|
122
|
-
const scope = effectScope();
|
|
123
|
-
addCleanup(() => scope.stop());
|
|
124
|
-
return scope.run(fn);
|
|
125
|
-
}
|
|
126
|
-
function wrapperSuspended(component, options, {
|
|
127
|
-
wrapperFn,
|
|
128
|
-
wrappedRender = (fn) => fn,
|
|
129
|
-
suspendedHelperName,
|
|
130
|
-
clonedComponentName
|
|
131
|
-
}) {
|
|
132
|
-
const { props = {}, attrs = {} } = options;
|
|
133
|
-
const { route = "/", scoped = false, ...wrapperFnOptions } = options;
|
|
134
|
-
const vueApp = tryUseNuxtApp()?.vueApp || globalThis.__unctx__.get("nuxt-app").tryUse().vueApp;
|
|
135
|
-
const {
|
|
136
|
-
render: componentRender,
|
|
137
|
-
setup: componentSetup,
|
|
138
|
-
...componentRest
|
|
139
|
-
} = component;
|
|
140
|
-
let wrappedInstance = null;
|
|
141
|
-
let setupContext;
|
|
142
|
-
let setupState;
|
|
143
|
-
const setProps = reactive({});
|
|
144
|
-
function patchInstanceAppContext() {
|
|
145
|
-
const app = getCurrentInstance()?.appContext.app;
|
|
146
|
-
if (!app) return;
|
|
147
|
-
for (const [key, value] of Object.entries(vueApp)) {
|
|
148
|
-
if (key in app) continue;
|
|
149
|
-
app[key] = value;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
const ClonedComponent = {
|
|
153
|
-
components: {},
|
|
154
|
-
...component,
|
|
155
|
-
name: clonedComponentName,
|
|
156
|
-
async setup(props2, instanceContext) {
|
|
157
|
-
const currentInstance = getCurrentInstance();
|
|
158
|
-
if (currentInstance) {
|
|
159
|
-
currentInstance.emit = (event, ...args) => {
|
|
160
|
-
setupContext.emit(event, ...args);
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
if (!componentSetup) return;
|
|
164
|
-
const result = scoped ? await runEffectScope(() => componentSetup(props2, setupContext)) : await componentSetup(props2, setupContext);
|
|
165
|
-
if (wrappedInstance?.exposed) {
|
|
166
|
-
instanceContext.expose(wrappedInstance.exposed);
|
|
167
|
-
}
|
|
168
|
-
setupState = result && typeof result === "object" ? result : {};
|
|
169
|
-
return result;
|
|
170
|
-
}
|
|
171
|
-
};
|
|
172
|
-
const SuspendedHelper = {
|
|
173
|
-
name: suspendedHelperName,
|
|
174
|
-
render: () => "",
|
|
175
|
-
async setup() {
|
|
176
|
-
if (route) {
|
|
177
|
-
const router = useRouter();
|
|
178
|
-
await router.replace(route);
|
|
179
|
-
}
|
|
180
|
-
return () => h$1(ClonedComponent, { ...props, ...setProps, ...attrs }, setupContext.slots);
|
|
181
|
-
}
|
|
182
|
-
};
|
|
183
|
-
return new Promise((resolve, reject) => {
|
|
184
|
-
let isMountSettled = false;
|
|
185
|
-
const wrapper = wrapperFn(
|
|
186
|
-
{
|
|
187
|
-
inheritAttrs: false,
|
|
188
|
-
__cssModules: componentRest.__cssModules,
|
|
189
|
-
setup: (props2, ctx) => {
|
|
190
|
-
patchInstanceAppContext();
|
|
191
|
-
wrappedInstance = getCurrentInstance();
|
|
192
|
-
setupContext = ctx;
|
|
193
|
-
const nuxtRootSetupResult = runEffectScope(
|
|
194
|
-
() => NuxtRoot.setup(props2, {
|
|
195
|
-
...ctx,
|
|
196
|
-
expose: () => {
|
|
197
|
-
}
|
|
198
|
-
})
|
|
199
|
-
);
|
|
200
|
-
onErrorCaptured((error, ...args) => {
|
|
201
|
-
if (isMountSettled) return;
|
|
202
|
-
isMountSettled = true;
|
|
203
|
-
try {
|
|
204
|
-
wrappedInstance?.appContext.config.errorHandler?.(error, ...args);
|
|
205
|
-
reject(error);
|
|
206
|
-
} catch (error2) {
|
|
207
|
-
reject(error2);
|
|
208
|
-
}
|
|
209
|
-
return false;
|
|
210
|
-
});
|
|
211
|
-
return nuxtRootSetupResult;
|
|
212
|
-
},
|
|
213
|
-
render: wrappedRender(() => h$1(
|
|
214
|
-
Suspense,
|
|
215
|
-
{
|
|
216
|
-
onResolve: () => nextTick().then(() => {
|
|
217
|
-
if (isMountSettled) return;
|
|
218
|
-
isMountSettled = true;
|
|
219
|
-
wrapper.setupState = setupState;
|
|
220
|
-
resolve({
|
|
221
|
-
wrapper,
|
|
222
|
-
setProps: (props2) => {
|
|
223
|
-
Object.assign(setProps, props2);
|
|
224
|
-
}
|
|
225
|
-
});
|
|
226
|
-
})
|
|
227
|
-
},
|
|
228
|
-
{
|
|
229
|
-
default: () => h$1(SuspendedHelper)
|
|
230
|
-
}
|
|
231
|
-
))
|
|
232
|
-
},
|
|
233
|
-
{
|
|
234
|
-
...wrapperFnOptions,
|
|
235
|
-
global: mergeComponentMountingGlobalOptions(wrapperFnOptions.global, {
|
|
236
|
-
config: {
|
|
237
|
-
globalProperties: makeAllPropertiesEnumerable(
|
|
238
|
-
vueApp.config.globalProperties
|
|
239
|
-
)
|
|
240
|
-
},
|
|
241
|
-
directives: vueApp._context.directives,
|
|
242
|
-
provide: vueApp._context.provides,
|
|
243
|
-
stubs: {
|
|
244
|
-
Suspense: false,
|
|
245
|
-
[SuspendedHelper.name]: false,
|
|
246
|
-
[ClonedComponent.name]: false
|
|
247
|
-
},
|
|
248
|
-
components: { ...vueApp._context.components, RouterLink }
|
|
249
|
-
})
|
|
250
|
-
}
|
|
251
|
-
);
|
|
252
|
-
});
|
|
253
|
-
}
|
|
254
|
-
function mergeComponentMountingGlobalOptions(options = {}, defaults = {}) {
|
|
255
|
-
return {
|
|
256
|
-
...options,
|
|
257
|
-
mixins: [...defaults.mixins || [], ...options.mixins || []],
|
|
258
|
-
stubs: {
|
|
259
|
-
...defaults.stubs,
|
|
260
|
-
...Array.isArray(options.stubs) ? Object.fromEntries(options.stubs.map((n) => [n, true])) : options.stubs
|
|
261
|
-
},
|
|
262
|
-
plugins: [...defaults.plugins || [], ...options.plugins || []],
|
|
263
|
-
components: { ...defaults.components, ...options.components },
|
|
264
|
-
provide: { ...defaults.provide, ...options.provide },
|
|
265
|
-
mocks: { ...defaults.mocks, ...options.mocks },
|
|
266
|
-
config: {
|
|
267
|
-
...defaults.config,
|
|
268
|
-
...options.config,
|
|
269
|
-
compilerOptions: {
|
|
270
|
-
...defaults.config?.compilerOptions,
|
|
271
|
-
...options.config?.compilerOptions
|
|
272
|
-
},
|
|
273
|
-
globalProperties: {
|
|
274
|
-
...defaults.config?.globalProperties,
|
|
275
|
-
...options.config?.globalProperties
|
|
276
|
-
}
|
|
277
|
-
},
|
|
278
|
-
directives: { ...defaults.directives, ...options.directives }
|
|
279
|
-
};
|
|
280
|
-
}
|
|
281
|
-
function makeAllPropertiesEnumerable(target) {
|
|
282
|
-
return {
|
|
283
|
-
...target,
|
|
284
|
-
...Object.fromEntries(
|
|
285
|
-
Object.getOwnPropertyNames(target).map((key) => [key, target[key]])
|
|
286
|
-
)
|
|
287
|
-
};
|
|
288
|
-
}
|
|
289
|
-
|
|
290
76
|
async function mountSuspended(component, options = {}) {
|
|
77
|
+
const { cleanupAll, wrapperSuspended } = await import('../chunks/suspended.mjs');
|
|
291
78
|
const suspendedHelperName = "MountSuspendedHelper";
|
|
292
79
|
const clonedComponentName = "MountSuspendedComponent";
|
|
293
80
|
cleanupAll();
|
|
@@ -342,6 +129,7 @@ function wrappedMountedWrapper(wrapper, component) {
|
|
|
342
129
|
}
|
|
343
130
|
|
|
344
131
|
async function renderSuspended(component, options = {}) {
|
|
132
|
+
const { cleanupAll, wrapperSuspended } = await import('../chunks/suspended.mjs');
|
|
345
133
|
const wrapperId = "test-wrapper";
|
|
346
134
|
const suspendedHelperName = "RenderHelper";
|
|
347
135
|
const clonedComponentName = "RenderSuspendedComponent";
|
|
@@ -350,9 +138,9 @@ async function renderSuspended(component, options = {}) {
|
|
|
350
138
|
document.getElementById(wrapperId)?.remove();
|
|
351
139
|
const { wrapper, setProps } = await wrapperSuspended(component, options, {
|
|
352
140
|
wrapperFn,
|
|
353
|
-
wrappedRender: (render) => () => h
|
|
141
|
+
wrappedRender: (render) => () => h({
|
|
354
142
|
inheritAttrs: false,
|
|
355
|
-
render: () => h
|
|
143
|
+
render: () => h("div", { id: wrapperId }, render())
|
|
356
144
|
}),
|
|
357
145
|
suspendedHelperName,
|
|
358
146
|
clonedComponentName
|
|
@@ -56,6 +56,13 @@ interface TestOptions {
|
|
|
56
56
|
* @default 30000
|
|
57
57
|
*/
|
|
58
58
|
teardownTimeout: number;
|
|
59
|
+
/**
|
|
60
|
+
* The amount of time (in milliseconds) to wait for the dev or built server to become ready (i.e. respond successfully on the configured base URL) before failing.
|
|
61
|
+
*
|
|
62
|
+
* This is bounded by `setupTimeout`, so increasing this is only useful in combination with a sufficiently large `setupTimeout`.
|
|
63
|
+
* @default 120000 // on windows; otherwise 60000
|
|
64
|
+
*/
|
|
65
|
+
serverStartTimeout: number;
|
|
59
66
|
waitFor: number;
|
|
60
67
|
/**
|
|
61
68
|
* Under the hood, Nuxt test utils uses [`playwright`](https://playwright.dev) to carry out browser testing. If this option is set, a browser will be launched and can be controlled in the subsequent test suite.
|
|
@@ -15,6 +15,7 @@ function createTestContext(options) {
|
|
|
15
15
|
configFile: "nuxt.config",
|
|
16
16
|
setupTimeout: isWindows ? 24e4 : 12e4,
|
|
17
17
|
teardownTimeout: isWindows ? 6e4 : 3e4,
|
|
18
|
+
serverStartTimeout: isWindows ? 12e4 : 6e4,
|
|
18
19
|
dev: !!JSON.parse(process.env.NUXT_TEST_DEV || "false"),
|
|
19
20
|
logLevel: 1,
|
|
20
21
|
server: true,
|
|
@@ -97,24 +98,6 @@ async function startServer(options = {}) {
|
|
|
97
98
|
}
|
|
98
99
|
}
|
|
99
100
|
});
|
|
100
|
-
await waitForPort(port, { retries: 32, host }).catch(() => {
|
|
101
|
-
});
|
|
102
|
-
let lastError;
|
|
103
|
-
for (let i = 0; i < 150; i++) {
|
|
104
|
-
await new Promise((resolve2) => setTimeout(resolve2, 100));
|
|
105
|
-
try {
|
|
106
|
-
const res = await $fetch(ctx.nuxt.options.app.baseURL, {
|
|
107
|
-
responseType: "text"
|
|
108
|
-
});
|
|
109
|
-
if (!res.includes("__NUXT_LOADING__")) {
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
} catch (e) {
|
|
113
|
-
lastError = e;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
ctx.serverProcess.kill();
|
|
117
|
-
throw lastError || new Error("Timeout waiting for dev server!");
|
|
118
101
|
} else {
|
|
119
102
|
const outputDir = ctx.nuxt ? ctx.nuxt.options.nitro.output.dir : ctx.options.nuxtConfig.nitro.output.dir;
|
|
120
103
|
ctx.serverProcess = x(
|
|
@@ -135,13 +118,53 @@ async function startServer(options = {}) {
|
|
|
135
118
|
}
|
|
136
119
|
}
|
|
137
120
|
);
|
|
138
|
-
await waitForPort(port, { retries: 20, host });
|
|
139
121
|
}
|
|
122
|
+
await waitForServer({ host, port, dev: ctx.options.dev });
|
|
123
|
+
}
|
|
124
|
+
async function waitForServer({ host, port, dev }) {
|
|
125
|
+
const ctx = useTestContext();
|
|
126
|
+
const baseURL = ctx.nuxt?.options.app.baseURL ?? "/";
|
|
127
|
+
const deadline = Date.now() + ctx.options.serverStartTimeout;
|
|
128
|
+
await waitForPort(port, { retries: 8, host }).catch(() => {
|
|
129
|
+
});
|
|
130
|
+
let lastError;
|
|
131
|
+
while (Date.now() < deadline) {
|
|
132
|
+
if (ctx.serverProcess && (ctx.serverProcess.killed || ctx.serverProcess.exitCode != null)) {
|
|
133
|
+
throw new Error(`Server process exited before becoming ready (exit code: ${ctx.serverProcess.exitCode ?? "unknown"})`);
|
|
134
|
+
}
|
|
135
|
+
try {
|
|
136
|
+
const res = await globalFetch(joinURL(ctx.url, baseURL), { signal: AbortSignal.timeout(1e4) });
|
|
137
|
+
if (dev && res.status === 503) {
|
|
138
|
+
lastError = new Error(`Server responded with ${res.status} ${res.statusText}`);
|
|
139
|
+
} else if (dev && (await res.text()).includes("__NUXT_LOADING__")) {
|
|
140
|
+
lastError = new Error("Dev server is still starting up");
|
|
141
|
+
} else {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
} catch (e) {
|
|
145
|
+
lastError = e;
|
|
146
|
+
}
|
|
147
|
+
await new Promise((resolve2) => setTimeout(resolve2, 100));
|
|
148
|
+
}
|
|
149
|
+
await stopServer();
|
|
150
|
+
throw lastError instanceof Error ? lastError : new Error(`Timeout (${ctx.options.serverStartTimeout}ms) waiting for ${dev ? "dev" : "built"} server to become ready at ${ctx.url}`);
|
|
140
151
|
}
|
|
141
152
|
async function stopServer() {
|
|
142
153
|
const ctx = useTestContext();
|
|
143
|
-
|
|
144
|
-
|
|
154
|
+
const proc = ctx.serverProcess;
|
|
155
|
+
if (!proc) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
ctx.serverProcess = void 0;
|
|
159
|
+
const exited = Promise.resolve(proc).then(() => {
|
|
160
|
+
}, () => {
|
|
161
|
+
});
|
|
162
|
+
const sleep = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
163
|
+
proc.kill();
|
|
164
|
+
await Promise.race([exited, sleep(5e3)]);
|
|
165
|
+
if (proc.exitCode == null) {
|
|
166
|
+
proc.kill("SIGKILL");
|
|
167
|
+
await Promise.race([exited, sleep(5e3)]);
|
|
145
168
|
}
|
|
146
169
|
}
|
|
147
170
|
function fetch(path, options) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { u as useTestContext, d as url, c as createTestContext, a as startServer, b as stopServer, s as setTestContext } from './test-utils.
|
|
1
|
+
import { u as useTestContext, d as url, c as createTestContext, a as startServer, b as stopServer, s as setTestContext } from './test-utils.BrQgu3Ob.mjs';
|
|
2
2
|
import { existsSync, promises } from 'node:fs';
|
|
3
3
|
import { resolve } from 'node:path';
|
|
4
4
|
import { defu } from 'defu';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nuxt/test-utils",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.3",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/nuxt/test-utils.git"
|
|
@@ -53,26 +53,13 @@
|
|
|
53
53
|
"dist",
|
|
54
54
|
"dirs.js"
|
|
55
55
|
],
|
|
56
|
-
"scripts": {
|
|
57
|
-
"lint": "eslint .",
|
|
58
|
-
"lint:fix": "eslint . --fix",
|
|
59
|
-
"test": "pnpm test:types && pnpm test:unit && pnpm test:examples",
|
|
60
|
-
"test:examples": "pnpm --filter '!@nuxt/test-utils' --filter '!example-app-cucumber' --filter '!example-app-jest' --filter '!example-app-bun' -r test && pnpm --filter example-app-cucumber -r test",
|
|
61
|
-
"test:knip": "knip",
|
|
62
|
-
"test:engines": "pnpm installed-check --no-workspaces --ignore-dev",
|
|
63
|
-
"test:types": "vue-tsc --noEmit && vitest --dir ./test/types --typecheck.only --run",
|
|
64
|
-
"test:unit": "vitest --dir ./test/unit --run",
|
|
65
|
-
"build": "unbuild",
|
|
66
|
-
"prepack": "unbuild",
|
|
67
|
-
"dev:prepare": "nuxt prepare && unbuild --stub && pnpm -r dev:prepare"
|
|
68
|
-
},
|
|
69
56
|
"dependencies": {
|
|
70
57
|
"@clack/prompts": "1.2.0",
|
|
71
58
|
"@nuxt/devtools-kit": "^2.7.0",
|
|
72
59
|
"@nuxt/kit": "^3.21.2",
|
|
73
60
|
"c12": "^3.3.4",
|
|
74
61
|
"consola": "^3.4.2",
|
|
75
|
-
"defu": "^6.1.
|
|
62
|
+
"defu": "^6.1.7",
|
|
76
63
|
"destr": "^2.0.5",
|
|
77
64
|
"estree-walker": "^3.0.3",
|
|
78
65
|
"exsolve": "^1.0.8",
|
|
@@ -84,52 +71,52 @@
|
|
|
84
71
|
"magic-string": "^0.30.21",
|
|
85
72
|
"node-fetch-native": "^1.6.7",
|
|
86
73
|
"node-mock-http": "^1.0.4",
|
|
87
|
-
"nypm": "^0.6.
|
|
74
|
+
"nypm": "^0.6.6",
|
|
88
75
|
"ofetch": "^1.5.1",
|
|
89
76
|
"pathe": "^2.0.3",
|
|
90
77
|
"perfect-debounce": "^2.1.0",
|
|
91
78
|
"radix3": "^1.1.2",
|
|
92
79
|
"scule": "^1.3.0",
|
|
93
|
-
"std-env": "^4.
|
|
80
|
+
"std-env": "^4.1.0",
|
|
94
81
|
"tinyexec": "^1.1.1",
|
|
95
82
|
"ufo": "^1.6.3",
|
|
96
83
|
"unplugin": "^3.0.0",
|
|
97
|
-
"
|
|
98
|
-
"
|
|
84
|
+
"vue": "^3.5.33",
|
|
85
|
+
"vitest-environment-nuxt": "2.0.0"
|
|
99
86
|
},
|
|
100
87
|
"devDependencies": {
|
|
101
|
-
"@cucumber/cucumber": "12.
|
|
88
|
+
"@cucumber/cucumber": "12.8.2",
|
|
102
89
|
"@jest/globals": "30.3.0",
|
|
103
90
|
"@nuxt/eslint-config": "1.15.2",
|
|
104
91
|
"@nuxt/schema": "4.4.2",
|
|
105
92
|
"@playwright/test": "1.59.1",
|
|
106
93
|
"@testing-library/vue": "8.1.0",
|
|
107
|
-
"@types/bun": "1.3.
|
|
94
|
+
"@types/bun": "1.3.13",
|
|
108
95
|
"@types/estree": "1.0.8",
|
|
109
96
|
"@types/jsdom": "28.0.1",
|
|
110
97
|
"@types/node": "latest",
|
|
111
98
|
"@types/semver": "7.7.1",
|
|
112
|
-
"@vitest/browser-playwright": "4.1.
|
|
113
|
-
"@vue/test-utils": "2.4.
|
|
99
|
+
"@vitest/browser-playwright": "4.1.5",
|
|
100
|
+
"@vue/test-utils": "2.4.9",
|
|
114
101
|
"changelogen": "0.6.2",
|
|
115
102
|
"compatx": "0.2.0",
|
|
116
|
-
"eslint": "10.2.
|
|
103
|
+
"eslint": "10.2.1",
|
|
117
104
|
"installed-check": "10.0.1",
|
|
118
|
-
"knip": "6.
|
|
105
|
+
"knip": "6.7.0",
|
|
119
106
|
"nitropack": "2.13.3",
|
|
120
107
|
"nuxt": "4.4.2",
|
|
121
|
-
"oxc-parser": "0.
|
|
122
|
-
"pkg-pr-new": "0.0.
|
|
108
|
+
"oxc-parser": "0.127.0",
|
|
109
|
+
"pkg-pr-new": "0.0.67",
|
|
123
110
|
"playwright-core": "1.59.1",
|
|
124
|
-
"rollup": "4.60.
|
|
111
|
+
"rollup": "4.60.2",
|
|
125
112
|
"semver": "7.7.4",
|
|
126
|
-
"typescript": "6.0.
|
|
113
|
+
"typescript": "6.0.3",
|
|
127
114
|
"unbuild": "latest",
|
|
128
|
-
"unimport": "6.
|
|
129
|
-
"vite": "8.0.
|
|
130
|
-
"vitest": "4.1.
|
|
131
|
-
"vue-router": "5.0.
|
|
132
|
-
"vue-tsc": "3.2.
|
|
115
|
+
"unimport": "6.1.1",
|
|
116
|
+
"vite": "8.0.10",
|
|
117
|
+
"vitest": "4.1.5",
|
|
118
|
+
"vue-router": "5.0.6",
|
|
119
|
+
"vue-tsc": "3.2.7"
|
|
133
120
|
},
|
|
134
121
|
"peerDependencies": {
|
|
135
122
|
"@cucumber/cucumber": ">=11.0.0",
|
|
@@ -175,19 +162,30 @@
|
|
|
175
162
|
}
|
|
176
163
|
},
|
|
177
164
|
"resolutions": {
|
|
178
|
-
"@cucumber/cucumber": "12.
|
|
165
|
+
"@cucumber/cucumber": "12.8.2",
|
|
179
166
|
"@nuxt/schema": "4.4.2",
|
|
180
167
|
"@nuxt/test-utils": "workspace:*",
|
|
181
168
|
"@types/node": "24.12.2",
|
|
182
169
|
"nitro": "https://pkg.pr.new/nitrojs/nitro@00598a8",
|
|
183
|
-
"rollup": "4.60.
|
|
184
|
-
"vite": "8.0.
|
|
170
|
+
"rollup": "4.60.2",
|
|
171
|
+
"vite": "8.0.10",
|
|
185
172
|
"vite-node": "6.0.0",
|
|
186
|
-
"vitest": "4.1.
|
|
187
|
-
"vue": "^3.5.
|
|
173
|
+
"vitest": "4.1.5",
|
|
174
|
+
"vue": "^3.5.33"
|
|
188
175
|
},
|
|
189
176
|
"engines": {
|
|
190
177
|
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
|
|
191
178
|
},
|
|
192
|
-
"
|
|
193
|
-
|
|
179
|
+
"scripts": {
|
|
180
|
+
"lint": "eslint .",
|
|
181
|
+
"lint:fix": "eslint . --fix",
|
|
182
|
+
"test": "pnpm test:types && pnpm test:unit && pnpm test:examples",
|
|
183
|
+
"test:examples": "pnpm --filter '!@nuxt/test-utils' --filter '!example-app-cucumber' --filter '!example-app-jest' --filter '!example-app-bun' -r test && pnpm --filter example-app-cucumber -r test",
|
|
184
|
+
"test:knip": "knip",
|
|
185
|
+
"test:engines": "pnpm installed-check --no-workspaces --ignore-dev",
|
|
186
|
+
"test:types": "vue-tsc --noEmit && vitest --dir ./test/types --typecheck.only --run",
|
|
187
|
+
"test:unit": "vitest --dir ./test/unit --run",
|
|
188
|
+
"build": "unbuild",
|
|
189
|
+
"dev:prepare": "nuxt prepare && unbuild --stub && pnpm -r dev:prepare"
|
|
190
|
+
}
|
|
191
|
+
}
|