@nuxt/test-utils 3.20.1 → 3.21.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/dist/config.mjs +13 -39
- package/dist/e2e.mjs +6 -2
- package/dist/module.mjs +24 -14
- package/dist/playwright.mjs +6 -2
- package/dist/runtime/global-setup.mjs +3 -4
- package/dist/runtime/shared/environment.mjs +0 -3
- package/dist/runtime-utils/index.d.mts +44 -12
- package/dist/runtime-utils/index.mjs +123 -197
- package/dist/shared/{test-utils.CtoalVlS.mjs → test-utils.3NR-so9-.mjs} +8 -7
- package/dist/shared/test-utils.G1ew4kEe.mjs +67 -0
- package/dist/vitest-environment.mjs +0 -3
- package/package.json +33 -45
package/dist/config.mjs
CHANGED
|
@@ -1,42 +1,16 @@
|
|
|
1
|
-
import process
|
|
1
|
+
import process from 'node:process';
|
|
2
2
|
import { defineConfig } from 'vite';
|
|
3
3
|
import { setupDotenv } from 'c12';
|
|
4
4
|
import { defu } from 'defu';
|
|
5
|
-
import { createResolver, findPath
|
|
6
|
-
import
|
|
7
|
-
import
|
|
5
|
+
import { createResolver, findPath } from '@nuxt/kit';
|
|
6
|
+
import { a as applyEnv, l as loadKit } from './shared/test-utils.G1ew4kEe.mjs';
|
|
7
|
+
import 'destr';
|
|
8
|
+
import 'scule';
|
|
9
|
+
import 'node:url';
|
|
10
|
+
import 'exsolve';
|
|
8
11
|
|
|
9
|
-
function
|
|
10
|
-
const
|
|
11
|
-
const envKey = snakeCase(key).toUpperCase();
|
|
12
|
-
return destr(
|
|
13
|
-
env[opts.prefix + envKey] ?? env[opts.altPrefix + envKey]
|
|
14
|
-
);
|
|
15
|
-
}
|
|
16
|
-
function _isObject(input) {
|
|
17
|
-
return typeof input === "object" && !Array.isArray(input);
|
|
18
|
-
}
|
|
19
|
-
function applyEnv(obj, opts, parentKey = "") {
|
|
20
|
-
for (const key in obj) {
|
|
21
|
-
const subKey = parentKey ? `${parentKey}_${key}` : key;
|
|
22
|
-
const envValue = getEnv(subKey, opts);
|
|
23
|
-
if (_isObject(obj[key])) {
|
|
24
|
-
if (_isObject(envValue)) {
|
|
25
|
-
obj[key] = { ...obj[key], ...envValue };
|
|
26
|
-
applyEnv(obj[key], opts, subKey);
|
|
27
|
-
} else if (envValue === void 0) {
|
|
28
|
-
applyEnv(obj[key], opts, subKey);
|
|
29
|
-
} else {
|
|
30
|
-
obj[key] = envValue ?? obj[key];
|
|
31
|
-
}
|
|
32
|
-
} else {
|
|
33
|
-
obj[key] = envValue ?? obj[key];
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
return obj;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
async function startNuxtAndGetViteConfig(rootDir = process$1.cwd(), options = {}) {
|
|
12
|
+
async function startNuxtAndGetViteConfig(rootDir = process.cwd(), options = {}) {
|
|
13
|
+
const { buildNuxt, loadNuxt } = await loadKit(rootDir);
|
|
40
14
|
const nuxt = await loadNuxt({
|
|
41
15
|
cwd: rootDir,
|
|
42
16
|
dev: false,
|
|
@@ -89,7 +63,7 @@ const excludedPlugins = [
|
|
|
89
63
|
"vite-plugin-vue-tracer"
|
|
90
64
|
];
|
|
91
65
|
async function getVitestConfigFromNuxt(options, loadNuxtOptions = {}) {
|
|
92
|
-
const { rootDir = process
|
|
66
|
+
const { rootDir = process.cwd(), ..._overrides } = loadNuxtOptions.overrides || {};
|
|
93
67
|
if (!options) {
|
|
94
68
|
options = await startNuxtAndGetViteConfig(rootDir, {
|
|
95
69
|
dotenv: loadNuxtOptions.dotenv,
|
|
@@ -117,7 +91,7 @@ async function getVitestConfigFromNuxt(options, loadNuxtOptions = {}) {
|
|
|
117
91
|
noDiscovery: true
|
|
118
92
|
},
|
|
119
93
|
test: {
|
|
120
|
-
dir: process
|
|
94
|
+
dir: process.cwd(),
|
|
121
95
|
environmentOptions: {
|
|
122
96
|
nuxtRuntimeConfig: applyEnv(structuredClone(options.nuxt.options.runtimeConfig), {
|
|
123
97
|
prefix: "NUXT_",
|
|
@@ -214,14 +188,14 @@ async function getVitestConfigFromNuxt(options, loadNuxtOptions = {}) {
|
|
|
214
188
|
return resolvedConfig;
|
|
215
189
|
}
|
|
216
190
|
async function defineVitestProject(config) {
|
|
217
|
-
if (process
|
|
191
|
+
if (process.env.__NUXT_VITEST_RESOLVED__) return config;
|
|
218
192
|
const resolvedConfig = await resolveConfig(config);
|
|
219
193
|
resolvedConfig.test.environment = "nuxt";
|
|
220
194
|
return resolvedConfig;
|
|
221
195
|
}
|
|
222
196
|
function defineVitestConfig(config = {}) {
|
|
223
197
|
return defineConfig(async () => {
|
|
224
|
-
if (process
|
|
198
|
+
if (process.env.__NUXT_VITEST_RESOLVED__) return config;
|
|
225
199
|
const resolvedConfig = await resolveConfig(config);
|
|
226
200
|
if (resolvedConfig.test.browser?.enabled) {
|
|
227
201
|
return resolvedConfig;
|
package/dist/e2e.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { b as buildFixture, c as createBrowser, a as createPage, d as createTest, g as getBrowser, l as loadFixture, e as setup, s as setupMaps, w as waitForHydration } from './shared/test-utils.
|
|
1
|
+
export { b as buildFixture, c as createBrowser, a as createPage, d as createTest, g as getBrowser, l as loadFixture, e as setup, s as setupMaps, w as waitForHydration } from './shared/test-utils.3NR-so9-.mjs';
|
|
2
2
|
import { u as useTestContext } from './shared/test-utils.CtwoJP76.mjs';
|
|
3
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.CtwoJP76.mjs';
|
|
4
4
|
import { consola } from 'consola';
|
|
@@ -7,7 +7,11 @@ import { distDir } from '#dirs';
|
|
|
7
7
|
import 'node:fs';
|
|
8
8
|
import 'node:path';
|
|
9
9
|
import 'defu';
|
|
10
|
-
import '
|
|
10
|
+
import './shared/test-utils.G1ew4kEe.mjs';
|
|
11
|
+
import 'destr';
|
|
12
|
+
import 'scule';
|
|
13
|
+
import 'node:url';
|
|
14
|
+
import 'exsolve';
|
|
11
15
|
import 'tinyexec';
|
|
12
16
|
import 'get-port-please';
|
|
13
17
|
import 'ofetch';
|
package/dist/module.mjs
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
import { pathToFileURL } from 'node:url';
|
|
2
|
-
import {
|
|
2
|
+
import { resolveIgnorePatterns, defineNuxtModule, createResolver, resolvePath, importModule, logger } from '@nuxt/kit';
|
|
3
3
|
import { getPort } from 'get-port-please';
|
|
4
4
|
import { h } from 'vue';
|
|
5
5
|
import { debounce } from 'perfect-debounce';
|
|
6
6
|
import { isCI } from 'std-env';
|
|
7
7
|
import { defu } from 'defu';
|
|
8
|
+
import { extname, join, dirname, relative } from 'pathe';
|
|
8
9
|
import { getVitestConfigFromNuxt } from './config.mjs';
|
|
9
10
|
import { walk } from 'estree-walker';
|
|
10
11
|
import MagicString from 'magic-string';
|
|
11
12
|
import { createUnplugin } from 'unplugin';
|
|
13
|
+
import { l as loadKit } from './shared/test-utils.G1ew4kEe.mjs';
|
|
12
14
|
import { readFileSync } from 'node:fs';
|
|
13
|
-
import { extname, join, dirname } from 'pathe';
|
|
14
15
|
import 'node:process';
|
|
15
16
|
import 'vite';
|
|
16
17
|
import 'c12';
|
|
17
18
|
import 'destr';
|
|
18
19
|
import 'scule';
|
|
20
|
+
import 'exsolve';
|
|
19
21
|
|
|
20
22
|
const PLUGIN_NAME$1 = "nuxt:vitest:mock-transform";
|
|
21
23
|
const HELPER_MOCK_IMPORT = "mockNuxtImport";
|
|
@@ -67,16 +69,16 @@ const createMockPlugin = (ctx) => createUnplugin(() => {
|
|
|
67
69
|
startOf(node)
|
|
68
70
|
);
|
|
69
71
|
}
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
+
const importTarget = node.arguments[0];
|
|
73
|
+
const name = isLiteral(importTarget) ? importTarget.value : isIdentifier(importTarget) ? importTarget.name : void 0;
|
|
74
|
+
if (typeof name !== "string") {
|
|
72
75
|
return this.error(
|
|
73
76
|
new Error(
|
|
74
|
-
`The first argument of ${HELPER_MOCK_IMPORT}() must be a string literal`
|
|
77
|
+
`The first argument of ${HELPER_MOCK_IMPORT}() must be a string literal or mocked target`
|
|
75
78
|
),
|
|
76
|
-
startOf(
|
|
79
|
+
startOf(importTarget)
|
|
77
80
|
);
|
|
78
81
|
}
|
|
79
|
-
const name = importName.value;
|
|
80
82
|
const importItem = ctx.imports.find((_) => name === (_.as || _.name));
|
|
81
83
|
if (!importItem) {
|
|
82
84
|
console.log({ imports: ctx.imports });
|
|
@@ -262,8 +264,8 @@ function endOf(node) {
|
|
|
262
264
|
return "range" in node && node.range ? node.range[1] : "end" in node ? node.end : void 0;
|
|
263
265
|
}
|
|
264
266
|
|
|
265
|
-
function setupImportMocking() {
|
|
266
|
-
const
|
|
267
|
+
async function setupImportMocking(nuxt) {
|
|
268
|
+
const { addVitePlugin } = await loadKit(nuxt.options.rootDir);
|
|
267
269
|
const ctx = {
|
|
268
270
|
components: [],
|
|
269
271
|
imports: []
|
|
@@ -317,7 +319,7 @@ const NuxtRootStubPlugin = createUnplugin((options) => {
|
|
|
317
319
|
});
|
|
318
320
|
|
|
319
321
|
const vitePluginBlocklist = ["vite-plugin-vue-inspector", "vite-plugin-vue-inspector:post", "vite-plugin-inspect", "nuxt:type-check"];
|
|
320
|
-
const module = defineNuxtModule({
|
|
322
|
+
const module$1 = defineNuxtModule({
|
|
321
323
|
meta: {
|
|
322
324
|
name: "@nuxt/test-utils",
|
|
323
325
|
configKey: "testUtils"
|
|
@@ -328,8 +330,9 @@ const module = defineNuxtModule({
|
|
|
328
330
|
},
|
|
329
331
|
async setup(options, nuxt) {
|
|
330
332
|
if (nuxt.options.test || nuxt.options.dev) {
|
|
331
|
-
setupImportMocking();
|
|
333
|
+
await setupImportMocking(nuxt);
|
|
332
334
|
}
|
|
335
|
+
const { addVitePlugin } = await loadKit(nuxt.options.rootDir);
|
|
333
336
|
const resolver = createResolver(import.meta.url);
|
|
334
337
|
addVitePlugin(NuxtRootStubPlugin.vite({
|
|
335
338
|
entry: await resolvePath("#app/entry", { alias: nuxt.options.alias }),
|
|
@@ -339,8 +342,15 @@ const module = defineNuxtModule({
|
|
|
339
342
|
nuxt.options.vite.define ||= {};
|
|
340
343
|
nuxt.options.vite.define["import.meta.vitest"] = "undefined";
|
|
341
344
|
}
|
|
342
|
-
nuxt.hook("prepare:types", (
|
|
343
|
-
references.push({ types: "vitest/import-meta" });
|
|
345
|
+
nuxt.hook("prepare:types", (ctx2) => {
|
|
346
|
+
ctx2.references.push({ types: "vitest/import-meta" });
|
|
347
|
+
if (ctx2.nodeTsConfig) {
|
|
348
|
+
ctx2.nodeTsConfig.include ||= [];
|
|
349
|
+
ctx2.nodeTsConfig.include.push(relative(nuxt.options.buildDir, join(nuxt.options.rootDir, "vitest.config.*")));
|
|
350
|
+
if (nuxt.options.workspaceDir !== nuxt.options.rootDir) {
|
|
351
|
+
ctx2.nodeTsConfig.include.push(relative(nuxt.options.buildDir, join(nuxt.options.workspaceDir, "vitest.config.*")));
|
|
352
|
+
}
|
|
353
|
+
}
|
|
344
354
|
});
|
|
345
355
|
if (!nuxt.options.dev) return;
|
|
346
356
|
if (process.env.TEST || process.env.VITE_TEST) return;
|
|
@@ -461,4 +471,4 @@ function toArray(value) {
|
|
|
461
471
|
return Array.isArray(value) ? value : [value];
|
|
462
472
|
}
|
|
463
473
|
|
|
464
|
-
export { module as default };
|
|
474
|
+
export { module$1 as default };
|
package/dist/playwright.mjs
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
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.3NR-so9-.mjs';
|
|
5
5
|
import 'node:path';
|
|
6
6
|
import 'ufo';
|
|
7
7
|
import 'std-env';
|
|
8
8
|
import 'consola';
|
|
9
9
|
import 'node:fs';
|
|
10
|
-
import '
|
|
10
|
+
import 'destr';
|
|
11
|
+
import 'scule';
|
|
12
|
+
import 'node:url';
|
|
13
|
+
import 'exsolve';
|
|
11
14
|
import { d as url } from './shared/test-utils.CtwoJP76.mjs';
|
|
12
15
|
import 'pathe';
|
|
13
16
|
import '#dirs';
|
|
17
|
+
import './shared/test-utils.G1ew4kEe.mjs';
|
|
14
18
|
import 'tinyexec';
|
|
15
19
|
import 'get-port-please';
|
|
16
20
|
import 'ofetch';
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { consola } from "consola";
|
|
2
2
|
import { createTest, exposeContextToEnv } from "@nuxt/test-utils/e2e";
|
|
3
|
-
const kit = _kit.default || _kit;
|
|
4
3
|
const options = JSON.parse(process.env.NUXT_TEST_OPTIONS || "{}");
|
|
5
4
|
const hooks = createTest(options);
|
|
6
5
|
export const setup = async () => {
|
|
7
|
-
|
|
6
|
+
consola.info("Building Nuxt app...");
|
|
8
7
|
await hooks.beforeAll();
|
|
9
8
|
exposeContextToEnv();
|
|
10
|
-
|
|
9
|
+
consola.info("Running tests...");
|
|
11
10
|
};
|
|
12
11
|
export const teardown = async () => {
|
|
13
12
|
await hooks.afterAll();
|
|
@@ -78,9 +78,6 @@ export async function setupWindow(win, environmentOptions) {
|
|
|
78
78
|
return _fetch(input, _init);
|
|
79
79
|
};
|
|
80
80
|
win.$fetch = createFetch({ fetch: win.fetch, Headers: win.Headers });
|
|
81
|
-
win.$fetch.create = (options = {}) => {
|
|
82
|
-
return createFetch({ fetch: win.fetch, Headers: win.Headers, ...options });
|
|
83
|
-
};
|
|
84
81
|
win.__registry = registry;
|
|
85
82
|
win.__app = h3App;
|
|
86
83
|
const timestamp = Date.now();
|
|
@@ -2,18 +2,41 @@ import { EventHandler, HTTPMethod } from 'h3';
|
|
|
2
2
|
import { SetupContext, RenderFunction, ComputedOptions, MethodOptions, ComponentOptionsMixin, EmitsOptions, ComponentInjectOptions, ComponentOptionsWithoutProps, ComponentOptionsWithArrayProps, ComponentPropsOptions, ComponentOptionsWithObjectProps } from 'vue';
|
|
3
3
|
import { ComponentMountingOptions, mount } from '@vue/test-utils';
|
|
4
4
|
import { RouteLocationRaw } from 'vue-router';
|
|
5
|
-
import
|
|
6
|
-
import { RenderOptions as RenderOptions$1 } from '@testing-library/vue';
|
|
5
|
+
import { RenderOptions, RenderResult } from '@testing-library/vue';
|
|
7
6
|
|
|
8
7
|
type Awaitable<T> = T | Promise<T>;
|
|
9
8
|
type OptionalFunction<T> = T | (() => Awaitable<T>);
|
|
9
|
+
/**
|
|
10
|
+
* `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.
|
|
11
|
+
* @param url - endpoint name (e.g. `/test/`).
|
|
12
|
+
* @param options - factory function that returns the mocked data or an object containing the `handler`, `method`, and `once` properties.
|
|
13
|
+
* - `handler`: the event handler function
|
|
14
|
+
* - `method`: (optional) HTTP method to match (e.g., 'GET', 'POST')
|
|
15
|
+
* - `once`: (optional) if true, the handler will only be used for the first matching request and then automatically removed
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* import { registerEndpoint } from '@nuxt/test-utils/runtime'
|
|
19
|
+
*
|
|
20
|
+
* registerEndpoint("/test/", () => ({
|
|
21
|
+
* test: "test-field"
|
|
22
|
+
* }))
|
|
23
|
+
*
|
|
24
|
+
* // With once option
|
|
25
|
+
* registerEndpoint("/api/user", {
|
|
26
|
+
* handler: () => ({ name: "Alice" }),
|
|
27
|
+
* once: true
|
|
28
|
+
* })
|
|
29
|
+
* ```
|
|
30
|
+
* @see https://nuxt.com/docs/getting-started/testing#registerendpoint
|
|
31
|
+
*/
|
|
10
32
|
declare function registerEndpoint(url: string, options: EventHandler | {
|
|
11
33
|
handler: EventHandler;
|
|
12
|
-
method
|
|
34
|
+
method?: HTTPMethod;
|
|
35
|
+
once?: boolean;
|
|
13
36
|
}): () => void;
|
|
14
37
|
/**
|
|
15
38
|
* `mockNuxtImport` allows you to mock Nuxt's auto import functionality.
|
|
16
|
-
* @param
|
|
39
|
+
* @param _target - name of an import to mock or mocked target.
|
|
17
40
|
* @param _factory - factory function that returns mocked import.
|
|
18
41
|
* @example
|
|
19
42
|
* ```ts
|
|
@@ -24,10 +47,17 @@ declare function registerEndpoint(url: string, options: EventHandler | {
|
|
|
24
47
|
* return { value: 'mocked storage' }
|
|
25
48
|
* }
|
|
26
49
|
* })
|
|
50
|
+
*
|
|
51
|
+
* // With mocked target
|
|
52
|
+
* mockNuxtImport(useStorage, () => {
|
|
53
|
+
* return () => {
|
|
54
|
+
* return { value: 'mocked storage' }
|
|
55
|
+
* }
|
|
56
|
+
* })
|
|
27
57
|
* ```
|
|
28
58
|
* @see https://nuxt.com/docs/getting-started/testing#mocknuxtimport
|
|
29
59
|
*/
|
|
30
|
-
declare function mockNuxtImport<T = unknown>(
|
|
60
|
+
declare function mockNuxtImport<T = unknown>(_target: string | T, _factory: () => T | Promise<T>): void;
|
|
31
61
|
/**
|
|
32
62
|
* `mockComponent` allows you to mock Nuxt's component.
|
|
33
63
|
* @param path - component name in PascalCase, or the relative path of the component.
|
|
@@ -69,6 +99,9 @@ type MountSuspendedOptions<T> = ComponentMountingOptions<T> & {
|
|
|
69
99
|
route?: RouteLocationRaw;
|
|
70
100
|
scoped?: boolean;
|
|
71
101
|
};
|
|
102
|
+
type MountSuspendedResult<T> = ReturnType<typeof mount<T>> & {
|
|
103
|
+
setupState: SetupState$1;
|
|
104
|
+
};
|
|
72
105
|
type SetupState$1 = Record<string, any>;
|
|
73
106
|
/**
|
|
74
107
|
* `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:
|
|
@@ -96,16 +129,17 @@ type SetupState$1 = Record<string, any>;
|
|
|
96
129
|
* @param component the component to be tested
|
|
97
130
|
* @param options optional options to set up your component
|
|
98
131
|
*/
|
|
99
|
-
declare function mountSuspended<T>(component: T, options?: MountSuspendedOptions<T>): Promise<
|
|
100
|
-
setupState: SetupState$1;
|
|
101
|
-
}>;
|
|
132
|
+
declare function mountSuspended<T>(component: T, options?: MountSuspendedOptions<T>): Promise<MountSuspendedResult<T>>;
|
|
102
133
|
declare global {
|
|
103
134
|
var __cleanup: Array<() => void> | undefined;
|
|
104
135
|
}
|
|
105
136
|
|
|
106
|
-
type
|
|
137
|
+
type RenderSuspendeOptions<T> = RenderOptions<T> & {
|
|
107
138
|
route?: RouteLocationRaw;
|
|
108
139
|
};
|
|
140
|
+
type RenderSuspendeResult = RenderResult & {
|
|
141
|
+
setupState: SetupState;
|
|
142
|
+
};
|
|
109
143
|
type SetupState = Record<string, any>;
|
|
110
144
|
/**
|
|
111
145
|
* `renderSuspended` allows you to mount any vue component within the Nuxt environment, allowing async setup and access to injections from your Nuxt plugins.
|
|
@@ -137,9 +171,7 @@ type SetupState = Record<string, any>;
|
|
|
137
171
|
* @param component the component to be tested
|
|
138
172
|
* @param options optional options to set up your component
|
|
139
173
|
*/
|
|
140
|
-
declare function renderSuspended<T>(component: T, options?:
|
|
141
|
-
setupState: SetupState;
|
|
142
|
-
}>;
|
|
174
|
+
declare function renderSuspended<T>(component: T, options?: RenderSuspendeOptions<T>): Promise<RenderSuspendeResult>;
|
|
143
175
|
declare global {
|
|
144
176
|
interface Window {
|
|
145
177
|
__cleanup?: Array<() => void>;
|
|
@@ -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,
|
|
3
|
+
import { reactive, h as h$1, Suspense, nextTick, getCurrentInstance, effectScope, defineComponent as defineComponent$1 } 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';
|
|
@@ -11,16 +11,25 @@ function registerEndpoint(url, options) {
|
|
|
11
11
|
if (!app) {
|
|
12
12
|
throw new Error("registerEndpoint() can only be used in a `@nuxt/test-utils` runtime environment");
|
|
13
13
|
}
|
|
14
|
-
const config = typeof options === "function" ? { handler: options, method: void 0 } : options;
|
|
14
|
+
const config = typeof options === "function" ? { handler: options, method: void 0, once: false } : options;
|
|
15
15
|
config.handler = defineEventHandler(config.handler);
|
|
16
16
|
const hasBeenRegistered = window.__registry.has(url);
|
|
17
17
|
endpointRegistry[url] ||= [];
|
|
18
18
|
endpointRegistry[url].push(config);
|
|
19
19
|
if (!hasBeenRegistered) {
|
|
20
20
|
window.__registry.add(url);
|
|
21
|
-
app.use("/_" + url, defineEventHandler((event) => {
|
|
21
|
+
app.use("/_" + url, defineEventHandler(async (event) => {
|
|
22
22
|
const latestHandler = [...endpointRegistry[url] || []].reverse().find((config2) => config2.method ? event.method === config2.method : true);
|
|
23
|
-
|
|
23
|
+
if (!latestHandler) return;
|
|
24
|
+
const result = await latestHandler.handler(event);
|
|
25
|
+
if (!latestHandler.once) return result;
|
|
26
|
+
const index = endpointRegistry[url]?.indexOf(latestHandler);
|
|
27
|
+
if (index === void 0 || index === -1) return result;
|
|
28
|
+
endpointRegistry[url]?.splice(index, 1);
|
|
29
|
+
if (endpointRegistry[url]?.length === 0) {
|
|
30
|
+
window.__registry.delete(url);
|
|
31
|
+
}
|
|
32
|
+
return result;
|
|
24
33
|
}), {
|
|
25
34
|
match(_, event) {
|
|
26
35
|
return endpointRegistry[url]?.some((config2) => config2.method ? event?.method === config2.method : true) ?? false;
|
|
@@ -34,7 +43,7 @@ function registerEndpoint(url, options) {
|
|
|
34
43
|
}
|
|
35
44
|
};
|
|
36
45
|
}
|
|
37
|
-
function mockNuxtImport(
|
|
46
|
+
function mockNuxtImport(_target, _factory) {
|
|
38
47
|
throw new Error(
|
|
39
48
|
"mockNuxtImport() is a macro and it did not get transpiled. This may be an internal bug of @nuxt/test-utils."
|
|
40
49
|
);
|
|
@@ -91,32 +100,27 @@ async function mountSuspended(component, options) {
|
|
|
91
100
|
cleanupFunction();
|
|
92
101
|
}
|
|
93
102
|
const vueApp = tryUseNuxtApp()?.vueApp || globalThis.__unctx__.get("nuxt-app").tryUse().vueApp;
|
|
94
|
-
const { render, setup,
|
|
103
|
+
const { render, setup, ...componentRest } = component;
|
|
104
|
+
let wrappedInstance = null;
|
|
95
105
|
let setupContext;
|
|
96
106
|
let setupState;
|
|
97
107
|
const setProps = reactive({});
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
});
|
|
108
|
+
function patchInstanceAppContext() {
|
|
109
|
+
const app = getCurrentInstance()?.appContext.app;
|
|
110
|
+
if (!app) return;
|
|
111
|
+
for (const [key, value] of Object.entries(vueApp)) {
|
|
112
|
+
if (key in app) continue;
|
|
113
|
+
app[key] = value;
|
|
105
114
|
}
|
|
106
|
-
return interceptedEmit;
|
|
107
115
|
}
|
|
108
|
-
|
|
116
|
+
let componentScope = null;
|
|
117
|
+
const wrappedSetup = async (props2, setupContext2, instanceContext) => {
|
|
109
118
|
const currentInstance = getCurrentInstance();
|
|
110
|
-
if (
|
|
111
|
-
|
|
119
|
+
if (currentInstance) {
|
|
120
|
+
currentInstance.emit = (event, ...args) => {
|
|
121
|
+
setupContext2.emit(event, ...args);
|
|
122
|
+
};
|
|
112
123
|
}
|
|
113
|
-
currentInstance.emit = getInterceptedEmitFunction(currentInstance.emit);
|
|
114
|
-
}
|
|
115
|
-
let passedProps;
|
|
116
|
-
let componentScope = null;
|
|
117
|
-
const wrappedSetup = async (props2, setupContext2) => {
|
|
118
|
-
interceptEmitOnCurrentInstance();
|
|
119
|
-
passedProps = props2;
|
|
120
124
|
if (setup) {
|
|
121
125
|
let result;
|
|
122
126
|
if (options?.scoped) {
|
|
@@ -131,6 +135,9 @@ async function mountSuspended(component, options) {
|
|
|
131
135
|
} else {
|
|
132
136
|
result = await setup(props2, setupContext2);
|
|
133
137
|
}
|
|
138
|
+
if (wrappedInstance?.exposed) {
|
|
139
|
+
instanceContext.expose(wrappedInstance.exposed);
|
|
140
|
+
}
|
|
134
141
|
setupState = result && typeof result === "object" ? result : {};
|
|
135
142
|
return result;
|
|
136
143
|
}
|
|
@@ -139,7 +146,11 @@ async function mountSuspended(component, options) {
|
|
|
139
146
|
(resolve) => {
|
|
140
147
|
const vm = mount(
|
|
141
148
|
{
|
|
149
|
+
__cssModules: componentRest.__cssModules,
|
|
150
|
+
inheritAttrs: false,
|
|
142
151
|
setup: (props2, ctx) => {
|
|
152
|
+
patchInstanceAppContext();
|
|
153
|
+
wrappedInstance = getCurrentInstance();
|
|
143
154
|
setupContext = ctx;
|
|
144
155
|
if (options?.scoped) {
|
|
145
156
|
const scope = effectScope();
|
|
@@ -160,7 +171,7 @@ async function mountSuspended(component, options) {
|
|
|
160
171
|
});
|
|
161
172
|
}
|
|
162
173
|
},
|
|
163
|
-
render: (
|
|
174
|
+
render: () => h$1(
|
|
164
175
|
Suspense,
|
|
165
176
|
{
|
|
166
177
|
onResolve: () => nextTick().then(() => {
|
|
@@ -178,56 +189,12 @@ async function mountSuspended(component, options) {
|
|
|
178
189
|
const router = useRouter();
|
|
179
190
|
await router.replace(route);
|
|
180
191
|
const clonedComponent = {
|
|
181
|
-
|
|
192
|
+
components: {},
|
|
182
193
|
...component,
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
if (data && typeof data === "function") {
|
|
186
|
-
const dataObject = data();
|
|
187
|
-
for (const key in dataObject) {
|
|
188
|
-
renderContext[key] = dataObject[key];
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
for (const key in setupState || {}) {
|
|
192
|
-
const warn = console.warn;
|
|
193
|
-
console.warn = () => {
|
|
194
|
-
};
|
|
195
|
-
try {
|
|
196
|
-
renderContext[key] = isReadonly(setupState[key]) ? unref(setupState[key]) : setupState[key];
|
|
197
|
-
} catch {
|
|
198
|
-
} finally {
|
|
199
|
-
console.warn = warn;
|
|
200
|
-
}
|
|
201
|
-
if (key === "props") {
|
|
202
|
-
renderContext[key] = cloneProps$1(renderContext[key]);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
const propsContext = "props" in renderContext ? renderContext.props : renderContext;
|
|
206
|
-
for (const key in props || {}) {
|
|
207
|
-
propsContext[key] = _ctx[key];
|
|
208
|
-
}
|
|
209
|
-
for (const key in passedProps || {}) {
|
|
210
|
-
propsContext[key] = passedProps[key];
|
|
211
|
-
}
|
|
212
|
-
if (methods && typeof methods === "object") {
|
|
213
|
-
for (const [key, value] of Object.entries(methods)) {
|
|
214
|
-
renderContext[key] = value.bind(renderContext);
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
if (computed && typeof computed === "object") {
|
|
218
|
-
for (const [key, value] of Object.entries(computed)) {
|
|
219
|
-
if ("get" in value) {
|
|
220
|
-
renderContext[key] = value.get.call(renderContext);
|
|
221
|
-
} else {
|
|
222
|
-
renderContext[key] = value.call(renderContext);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
return render.call(this, renderContext, ...args);
|
|
227
|
-
} : void 0,
|
|
228
|
-
setup: (props2) => wrappedSetup(props2, setupContext)
|
|
194
|
+
name: "MountSuspendedComponent",
|
|
195
|
+
setup: (props2, ctx) => wrappedSetup(props2, setupContext, ctx)
|
|
229
196
|
};
|
|
230
|
-
return () => h$1(clonedComponent, { ...props, ...setProps, ...attrs }, slots);
|
|
197
|
+
return () => h$1(clonedComponent, { ...props, ...setProps, ...attrs }, setupContext.slots);
|
|
231
198
|
}
|
|
232
199
|
})
|
|
233
200
|
}
|
|
@@ -236,11 +203,18 @@ async function mountSuspended(component, options) {
|
|
|
236
203
|
defu(
|
|
237
204
|
_options,
|
|
238
205
|
{
|
|
206
|
+
props,
|
|
239
207
|
slots,
|
|
240
208
|
attrs,
|
|
241
209
|
global: {
|
|
242
210
|
config: {
|
|
243
|
-
globalProperties:
|
|
211
|
+
globalProperties: {
|
|
212
|
+
...vueApp.config.globalProperties,
|
|
213
|
+
// make all properties/keys enumerable.
|
|
214
|
+
...Object.fromEntries(
|
|
215
|
+
Object.getOwnPropertyNames(vueApp.config.globalProperties).map((key) => [key, vueApp.config.globalProperties[key]])
|
|
216
|
+
)
|
|
217
|
+
}
|
|
244
218
|
},
|
|
245
219
|
directives: vueApp._context.directives,
|
|
246
220
|
provide: vueApp._context.provides,
|
|
@@ -257,58 +231,46 @@ async function mountSuspended(component, options) {
|
|
|
257
231
|
}
|
|
258
232
|
);
|
|
259
233
|
}
|
|
260
|
-
function cloneProps$1(props) {
|
|
261
|
-
const newProps = reactive({});
|
|
262
|
-
for (const key in props) {
|
|
263
|
-
newProps[key] = props[key];
|
|
264
|
-
}
|
|
265
|
-
return newProps;
|
|
266
|
-
}
|
|
267
234
|
function wrappedMountedWrapper(wrapper) {
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
235
|
+
const component = wrapper.findComponent({ name: "MountSuspendedComponent" });
|
|
236
|
+
const wrapperProps = [
|
|
237
|
+
"setProps",
|
|
238
|
+
"emitted",
|
|
239
|
+
"setupState",
|
|
240
|
+
"unmount"
|
|
241
|
+
];
|
|
242
|
+
return new Proxy(wrapper, {
|
|
243
|
+
get: (_, prop, receiver) => {
|
|
244
|
+
if (prop === "getCurrentComponent") return getCurrentComponentPatchedProxy;
|
|
245
|
+
const target = wrapperProps.includes(prop) ? wrapper : Reflect.has(component, prop) ? component : wrapper;
|
|
246
|
+
const value = Reflect.get(target, prop, receiver);
|
|
247
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
279
248
|
}
|
|
280
249
|
});
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
250
|
+
function getCurrentComponentPatchedProxy() {
|
|
251
|
+
const currentComponent = component.getCurrentComponent();
|
|
252
|
+
return new Proxy(currentComponent, {
|
|
253
|
+
get: (target, prop, receiver) => {
|
|
254
|
+
const value = Reflect.get(target, prop, receiver);
|
|
255
|
+
if (prop === "proxy" && value) {
|
|
256
|
+
return new Proxy(value, {
|
|
257
|
+
get(o, p, r) {
|
|
258
|
+
if (!Reflect.has(currentComponent.props, p)) {
|
|
259
|
+
const setupState = wrapper.setupState;
|
|
260
|
+
if (setupState && typeof setupState === "object") {
|
|
261
|
+
if (Reflect.has(setupState, p)) {
|
|
262
|
+
return Reflect.get(setupState, p, r);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return Reflect.get(o, p, r);
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
return value;
|
|
286
271
|
}
|
|
287
272
|
});
|
|
288
273
|
}
|
|
289
|
-
return proxy;
|
|
290
|
-
}
|
|
291
|
-
function createVMProxy(vm, setupState) {
|
|
292
|
-
return new Proxy(vm, {
|
|
293
|
-
get(target, key, receiver) {
|
|
294
|
-
const value = Reflect.get(target, key, receiver);
|
|
295
|
-
if (setupState && typeof setupState === "object" && key in setupState) {
|
|
296
|
-
return unref(setupState[key]);
|
|
297
|
-
}
|
|
298
|
-
return value;
|
|
299
|
-
},
|
|
300
|
-
set(target, key, value, receiver) {
|
|
301
|
-
if (setupState && typeof setupState === "object" && key in setupState) {
|
|
302
|
-
const setupValue = setupState[key];
|
|
303
|
-
if (setupValue && isRef(setupValue)) {
|
|
304
|
-
setupValue.value = value;
|
|
305
|
-
return true;
|
|
306
|
-
}
|
|
307
|
-
return Reflect.set(setupState, key, value, receiver);
|
|
308
|
-
}
|
|
309
|
-
return Reflect.set(target, key, value, receiver);
|
|
310
|
-
}
|
|
311
|
-
});
|
|
312
274
|
}
|
|
313
275
|
|
|
314
276
|
const WRAPPER_EL_ID = "test-wrapper";
|
|
@@ -322,37 +284,36 @@ async function renderSuspended(component, options) {
|
|
|
322
284
|
} = options || {};
|
|
323
285
|
const { render: renderFromTestingLibrary } = await import('@testing-library/vue');
|
|
324
286
|
const vueApp = tryUseNuxtApp()?.vueApp || globalThis.__unctx__.get("nuxt-app").tryUse().vueApp;
|
|
325
|
-
const { render, setup,
|
|
287
|
+
const { render, setup, ...componentRest } = component;
|
|
288
|
+
let wrappedInstance = null;
|
|
326
289
|
let setupContext;
|
|
327
290
|
let setupState;
|
|
328
|
-
|
|
329
|
-
function
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
}
|
|
336
|
-
return interceptedEmit;
|
|
337
|
-
}
|
|
338
|
-
function interceptEmitOnCurrentInstance() {
|
|
339
|
-
const currentInstance = getCurrentInstance();
|
|
340
|
-
if (!currentInstance) {
|
|
341
|
-
return;
|
|
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;
|
|
342
298
|
}
|
|
343
|
-
currentInstance.emit = getInterceptedEmitFunction(currentInstance.emit);
|
|
344
299
|
}
|
|
345
300
|
for (const fn of window.__cleanup || []) {
|
|
346
301
|
fn();
|
|
347
302
|
}
|
|
348
303
|
document.querySelector(`#${WRAPPER_EL_ID}`)?.remove();
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
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
|
+
}
|
|
353
311
|
if (setup) {
|
|
354
312
|
const result = await setup(props2, setupContext2);
|
|
355
313
|
setupState = result && typeof result === "object" ? result : {};
|
|
314
|
+
if (wrappedInstance?.exposed) {
|
|
315
|
+
instanceContext.expose(wrappedInstance.exposed);
|
|
316
|
+
}
|
|
356
317
|
return result;
|
|
357
318
|
}
|
|
358
319
|
};
|
|
@@ -365,7 +326,11 @@ async function renderSuspended(component, options) {
|
|
|
365
326
|
return new Promise((resolve) => {
|
|
366
327
|
const utils = renderFromTestingLibrary(
|
|
367
328
|
{
|
|
329
|
+
__cssModules: componentRest.__cssModules,
|
|
330
|
+
inheritAttrs: false,
|
|
368
331
|
setup: (props2, ctx) => {
|
|
332
|
+
patchInstanceAppContext();
|
|
333
|
+
wrappedInstance = getCurrentInstance();
|
|
369
334
|
setupContext = ctx;
|
|
370
335
|
const scope = effectScope();
|
|
371
336
|
window.__cleanup ||= [];
|
|
@@ -377,7 +342,7 @@ async function renderSuspended(component, options) {
|
|
|
377
342
|
expose: () => ({})
|
|
378
343
|
}));
|
|
379
344
|
},
|
|
380
|
-
render: (
|
|
345
|
+
render: () => (
|
|
381
346
|
// See discussions in https://github.com/testing-library/vue-testing-library/issues/230
|
|
382
347
|
// we add this additional root element because otherwise testing-library breaks
|
|
383
348
|
// because there's no root element while Suspense is resolving
|
|
@@ -390,6 +355,10 @@ async function renderSuspended(component, options) {
|
|
|
390
355
|
{
|
|
391
356
|
onResolve: () => nextTick().then(() => {
|
|
392
357
|
utils.setupState = setupState;
|
|
358
|
+
utils.rerender = async (props2) => {
|
|
359
|
+
Object.assign(setProps, props2);
|
|
360
|
+
await nextTick();
|
|
361
|
+
};
|
|
393
362
|
resolve(utils);
|
|
394
363
|
})
|
|
395
364
|
},
|
|
@@ -400,56 +369,13 @@ async function renderSuspended(component, options) {
|
|
|
400
369
|
const router = useRouter();
|
|
401
370
|
await router.replace(route);
|
|
402
371
|
const clonedComponent = {
|
|
403
|
-
|
|
372
|
+
components: {},
|
|
404
373
|
...component,
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
const dataObject = data();
|
|
409
|
-
for (const key in dataObject) {
|
|
410
|
-
renderContext[key] = dataObject[key];
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
for (const key in setupState || {}) {
|
|
414
|
-
const warn = console.warn;
|
|
415
|
-
console.warn = () => {
|
|
416
|
-
};
|
|
417
|
-
try {
|
|
418
|
-
renderContext[key] = isReadonly(setupState[key]) ? unref(setupState[key]) : setupState[key];
|
|
419
|
-
} catch {
|
|
420
|
-
} finally {
|
|
421
|
-
console.warn = warn;
|
|
422
|
-
}
|
|
423
|
-
if (key === "props") {
|
|
424
|
-
renderContext[key] = cloneProps(renderContext[key]);
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
const propsContext = "props" in renderContext ? renderContext.props : renderContext;
|
|
428
|
-
for (const key in props || {}) {
|
|
429
|
-
propsContext[key] = _ctx[key];
|
|
430
|
-
}
|
|
431
|
-
for (const key in passedProps || {}) {
|
|
432
|
-
propsContext[key] = passedProps[key];
|
|
433
|
-
}
|
|
434
|
-
if (methods && typeof methods === "object") {
|
|
435
|
-
for (const [key, value] of Object.entries(methods)) {
|
|
436
|
-
renderContext[key] = value.bind(renderContext);
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
if (computed && typeof computed === "object") {
|
|
440
|
-
for (const [key, value] of Object.entries(computed)) {
|
|
441
|
-
if ("get" in value) {
|
|
442
|
-
renderContext[key] = value.get.call(renderContext);
|
|
443
|
-
} else {
|
|
444
|
-
renderContext[key] = value.call(renderContext);
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
return render.call(this, renderContext, ...args);
|
|
449
|
-
} : void 0,
|
|
450
|
-
setup: (props2) => wrappedSetup(props2, setupContext)
|
|
374
|
+
name: "RenderSuspendedComponent",
|
|
375
|
+
render,
|
|
376
|
+
setup: (props2, ctx) => wrappedSetup(props2, setupContext, ctx)
|
|
451
377
|
};
|
|
452
|
-
return () => h$1(clonedComponent, { ...props && typeof props === "object" ? props : {}, ...attrs }, slots);
|
|
378
|
+
return () => h$1(clonedComponent, { ...props && typeof props === "object" ? props : {}, ...setProps, ...attrs }, setupContext.slots);
|
|
453
379
|
}
|
|
454
380
|
})
|
|
455
381
|
}
|
|
@@ -459,11 +385,18 @@ async function renderSuspended(component, options) {
|
|
|
459
385
|
)
|
|
460
386
|
},
|
|
461
387
|
defu(_options, {
|
|
388
|
+
props,
|
|
462
389
|
slots,
|
|
463
390
|
attrs,
|
|
464
391
|
global: {
|
|
465
392
|
config: {
|
|
466
|
-
globalProperties:
|
|
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
|
+
}
|
|
467
400
|
},
|
|
468
401
|
directives: vueApp._context.directives,
|
|
469
402
|
provide: vueApp._context.provides,
|
|
@@ -473,12 +406,5 @@ async function renderSuspended(component, options) {
|
|
|
473
406
|
);
|
|
474
407
|
});
|
|
475
408
|
}
|
|
476
|
-
function cloneProps(props) {
|
|
477
|
-
const newProps = reactive({});
|
|
478
|
-
for (const key in props) {
|
|
479
|
-
newProps[key] = props[key];
|
|
480
|
-
}
|
|
481
|
-
return newProps;
|
|
482
|
-
}
|
|
483
409
|
|
|
484
410
|
export { mockComponent, mockNuxtImport, mountSuspended, registerEndpoint, renderSuspended };
|
|
@@ -2,7 +2,7 @@ import { u as useTestContext, d as url, c as createTestContext, a as startServer
|
|
|
2
2
|
import { existsSync, promises } from 'node:fs';
|
|
3
3
|
import { resolve } from 'node:path';
|
|
4
4
|
import { defu } from 'defu';
|
|
5
|
-
import
|
|
5
|
+
import { l as loadKit } from './test-utils.G1ew4kEe.mjs';
|
|
6
6
|
|
|
7
7
|
async function createBrowser() {
|
|
8
8
|
const ctx = useTestContext();
|
|
@@ -57,7 +57,6 @@ async function waitForHydration(page, url2, waitUntil) {
|
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
const kit = _kit.default || _kit;
|
|
61
60
|
const isNuxtApp = (dir) => {
|
|
62
61
|
return existsSync(dir) && (existsSync(resolve(dir, "pages")) || existsSync(resolve(dir, "nuxt.config.js")) || existsSync(resolve(dir, "nuxt.config.mjs")) || existsSync(resolve(dir, "nuxt.config.cjs")) || existsSync(resolve(dir, "nuxt.config.ts")) || existsSync(resolve(dir, ".config", "nuxt.js")) || existsSync(resolve(dir, ".config", "nuxt.mjs")) || existsSync(resolve(dir, ".config", "nuxt.cjs")) || existsSync(resolve(dir, ".config", "nuxt.ts")));
|
|
63
62
|
};
|
|
@@ -91,7 +90,8 @@ async function loadFixture() {
|
|
|
91
90
|
});
|
|
92
91
|
}
|
|
93
92
|
if (ctx.options.build) {
|
|
94
|
-
|
|
93
|
+
const { loadNuxt } = await loadKit(ctx.options.rootDir);
|
|
94
|
+
ctx.nuxt = await loadNuxt({
|
|
95
95
|
cwd: ctx.options.rootDir,
|
|
96
96
|
dev: ctx.options.dev,
|
|
97
97
|
overrides: ctx.options.nuxtConfig,
|
|
@@ -107,10 +107,11 @@ async function loadFixture() {
|
|
|
107
107
|
}
|
|
108
108
|
async function buildFixture() {
|
|
109
109
|
const ctx = useTestContext();
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
110
|
+
const { buildNuxt, logger } = await loadKit(ctx.options.rootDir);
|
|
111
|
+
const prevLevel = logger.level;
|
|
112
|
+
logger.level = ctx.options.logLevel;
|
|
113
|
+
await buildNuxt(ctx.nuxt);
|
|
114
|
+
logger.level = prevLevel;
|
|
114
115
|
}
|
|
115
116
|
|
|
116
117
|
async function setupBun(hooks) {
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import destr from 'destr';
|
|
2
|
+
import { snakeCase } from 'scule';
|
|
3
|
+
import { pathToFileURL } from 'node:url';
|
|
4
|
+
import { resolveModulePath } from 'exsolve';
|
|
5
|
+
|
|
6
|
+
function getEnv(key, opts) {
|
|
7
|
+
const env = opts.env ?? process.env;
|
|
8
|
+
const envKey = snakeCase(key).toUpperCase();
|
|
9
|
+
return destr(
|
|
10
|
+
env[opts.prefix + envKey] ?? env[opts.altPrefix + envKey]
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
function _isObject(input) {
|
|
14
|
+
return typeof input === "object" && !Array.isArray(input);
|
|
15
|
+
}
|
|
16
|
+
function applyEnv(obj, opts, parentKey = "") {
|
|
17
|
+
for (const key in obj) {
|
|
18
|
+
const subKey = parentKey ? `${parentKey}_${key}` : key;
|
|
19
|
+
const envValue = getEnv(subKey, opts);
|
|
20
|
+
if (_isObject(obj[key])) {
|
|
21
|
+
if (_isObject(envValue)) {
|
|
22
|
+
obj[key] = { ...obj[key], ...envValue };
|
|
23
|
+
applyEnv(obj[key], opts, subKey);
|
|
24
|
+
} else if (envValue === void 0) {
|
|
25
|
+
applyEnv(obj[key], opts, subKey);
|
|
26
|
+
} else {
|
|
27
|
+
obj[key] = envValue ?? obj[key];
|
|
28
|
+
}
|
|
29
|
+
} else {
|
|
30
|
+
obj[key] = envValue ?? obj[key];
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return obj;
|
|
34
|
+
}
|
|
35
|
+
async function loadKit(rootDir) {
|
|
36
|
+
try {
|
|
37
|
+
const kitPath = resolveModulePath("@nuxt/kit", { from: tryResolveNuxt(rootDir) || rootDir });
|
|
38
|
+
let kit = await import(pathToFileURL(kitPath).href);
|
|
39
|
+
if (!kit.writeTypes) {
|
|
40
|
+
kit = {
|
|
41
|
+
...kit,
|
|
42
|
+
writeTypes: () => {
|
|
43
|
+
throw new Error("`writeTypes` is not available in this version of `@nuxt/kit`. Please upgrade to v3.7 or newer.");
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
return kit;
|
|
48
|
+
} catch (e) {
|
|
49
|
+
if (e.toString().includes("Cannot find module '@nuxt/kit'")) {
|
|
50
|
+
throw new Error(
|
|
51
|
+
"nuxi requires `@nuxt/kit` to be installed in your project. Try installing `nuxt` v3+ or `@nuxt/bridge` first."
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
throw e;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function tryResolveNuxt(rootDir) {
|
|
58
|
+
for (const pkg of ["nuxt-nightly", "nuxt", "nuxt3", "nuxt-edge"]) {
|
|
59
|
+
const path = resolveModulePath(pkg, { from: rootDir, try: true });
|
|
60
|
+
if (path) {
|
|
61
|
+
return path;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export { applyEnv as a, loadKit as l };
|
|
@@ -83,9 +83,6 @@ async function setupWindow(win, environmentOptions) {
|
|
|
83
83
|
return _fetch(input, _init);
|
|
84
84
|
};
|
|
85
85
|
win.$fetch = createFetch({ fetch: win.fetch, Headers: win.Headers });
|
|
86
|
-
win.$fetch.create = (options = {}) => {
|
|
87
|
-
return createFetch({ fetch: win.fetch, Headers: win.Headers, ...options });
|
|
88
|
-
};
|
|
89
86
|
win.__registry = registry;
|
|
90
87
|
win.__app = h3App;
|
|
91
88
|
const timestamp = Date.now();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nuxt/test-utils",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.21.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/nuxt/test-utils.git"
|
|
@@ -67,40 +67,41 @@
|
|
|
67
67
|
"dev:prepare": "nuxt prepare && unbuild --stub && pnpm -r dev:prepare"
|
|
68
68
|
},
|
|
69
69
|
"dependencies": {
|
|
70
|
-
"@nuxt/kit": "^
|
|
71
|
-
"c12": "^3.3.
|
|
70
|
+
"@nuxt/kit": "^3.20.1",
|
|
71
|
+
"c12": "^3.3.2",
|
|
72
72
|
"consola": "^3.4.2",
|
|
73
73
|
"defu": "^6.1.4",
|
|
74
74
|
"destr": "^2.0.5",
|
|
75
75
|
"estree-walker": "^3.0.3",
|
|
76
|
-
"
|
|
76
|
+
"exsolve": "^1.0.8",
|
|
77
|
+
"fake-indexeddb": "^6.2.5",
|
|
77
78
|
"get-port-please": "^3.2.0",
|
|
78
79
|
"h3": "^1.15.4",
|
|
79
80
|
"local-pkg": "^1.1.2",
|
|
80
|
-
"magic-string": "^0.30.
|
|
81
|
+
"magic-string": "^0.30.21",
|
|
81
82
|
"node-fetch-native": "^1.6.7",
|
|
82
83
|
"node-mock-http": "^1.0.3",
|
|
83
|
-
"ofetch": "^1.
|
|
84
|
+
"ofetch": "^1.5.1",
|
|
84
85
|
"pathe": "^2.0.3",
|
|
85
86
|
"perfect-debounce": "^2.0.0",
|
|
86
87
|
"radix3": "^1.1.2",
|
|
87
88
|
"scule": "^1.3.0",
|
|
88
89
|
"std-env": "^3.10.0",
|
|
89
|
-
"tinyexec": "^1.0.
|
|
90
|
+
"tinyexec": "^1.0.2",
|
|
90
91
|
"ufo": "^1.6.1",
|
|
91
|
-
"unplugin": "^2.3.
|
|
92
|
+
"unplugin": "^2.3.11",
|
|
92
93
|
"vitest-environment-nuxt": "^1.0.1",
|
|
93
|
-
"vue": "^3.5.
|
|
94
|
+
"vue": "^3.5.25"
|
|
94
95
|
},
|
|
95
96
|
"devDependencies": {
|
|
96
|
-
"@cucumber/cucumber": "12.
|
|
97
|
+
"@cucumber/cucumber": "12.3.0",
|
|
97
98
|
"@jest/globals": "30.2.0",
|
|
98
|
-
"@nuxt/devtools-kit": "2.
|
|
99
|
-
"@nuxt/eslint-config": "1.
|
|
100
|
-
"@nuxt/schema": "4.1
|
|
101
|
-
"@playwright/test": "1.
|
|
99
|
+
"@nuxt/devtools-kit": "2.7.0",
|
|
100
|
+
"@nuxt/eslint-config": "1.11.0",
|
|
101
|
+
"@nuxt/schema": "4.2.1",
|
|
102
|
+
"@playwright/test": "1.57.0",
|
|
102
103
|
"@testing-library/vue": "8.1.0",
|
|
103
|
-
"@types/bun": "1.3.
|
|
104
|
+
"@types/bun": "1.3.3",
|
|
104
105
|
"@types/estree": "1.0.8",
|
|
105
106
|
"@types/jsdom": "27.0.0",
|
|
106
107
|
"@types/node": "latest",
|
|
@@ -108,22 +109,22 @@
|
|
|
108
109
|
"@vue/test-utils": "2.4.6",
|
|
109
110
|
"changelogen": "0.6.2",
|
|
110
111
|
"compatx": "0.2.0",
|
|
111
|
-
"eslint": "9.
|
|
112
|
+
"eslint": "9.39.1",
|
|
112
113
|
"installed-check": "9.3.0",
|
|
113
|
-
"knip": "5.
|
|
114
|
-
"nitropack": "2.12.
|
|
115
|
-
"nuxt": "4.1
|
|
116
|
-
"pkg-pr-new": "0.0.
|
|
117
|
-
"playwright-core": "1.
|
|
118
|
-
"rollup": "4.
|
|
114
|
+
"knip": "5.71.0",
|
|
115
|
+
"nitropack": "2.12.9",
|
|
116
|
+
"nuxt": "4.2.1",
|
|
117
|
+
"pkg-pr-new": "0.0.62",
|
|
118
|
+
"playwright-core": "1.57.0",
|
|
119
|
+
"rollup": "4.53.3",
|
|
119
120
|
"semver": "7.7.3",
|
|
120
121
|
"typescript": "5.9.3",
|
|
121
122
|
"unbuild": "latest",
|
|
122
123
|
"unimport": "5.5.0",
|
|
123
|
-
"vite": "7.
|
|
124
|
+
"vite": "7.2.6",
|
|
124
125
|
"vitest": "3.2.4",
|
|
125
126
|
"vue-router": "4.6.3",
|
|
126
|
-
"vue-tsc": "3.1.
|
|
127
|
+
"vue-tsc": "3.1.5"
|
|
127
128
|
},
|
|
128
129
|
"peerDependencies": {
|
|
129
130
|
"@cucumber/cucumber": "^10.3.1 || >=11.0.0",
|
|
@@ -169,31 +170,18 @@
|
|
|
169
170
|
}
|
|
170
171
|
},
|
|
171
172
|
"resolutions": {
|
|
172
|
-
"@cucumber/cucumber": "12.
|
|
173
|
-
"@nuxt/
|
|
174
|
-
"@nuxt/schema": "4.1.3",
|
|
173
|
+
"@cucumber/cucumber": "12.3.0",
|
|
174
|
+
"@nuxt/schema": "4.2.1",
|
|
175
175
|
"@nuxt/test-utils": "workspace:*",
|
|
176
|
-
"@types/node": "
|
|
177
|
-
"rollup": "4.
|
|
178
|
-
"vite": "7.
|
|
179
|
-
"vite-node": "
|
|
176
|
+
"@types/node": "24.10.1",
|
|
177
|
+
"rollup": "4.53.3",
|
|
178
|
+
"vite": "7.2.6",
|
|
179
|
+
"vite-node": "5.2.0",
|
|
180
180
|
"vitest": "3.2.4",
|
|
181
|
-
"vue": "^3.5.
|
|
181
|
+
"vue": "^3.5.25"
|
|
182
182
|
},
|
|
183
183
|
"engines": {
|
|
184
184
|
"node": "^20.0.0 || ^22.0.0 || >=24.0.0"
|
|
185
185
|
},
|
|
186
|
-
"packageManager": "pnpm@10.
|
|
187
|
-
"pnpm": {
|
|
188
|
-
"onlyBuiltDependencies": [
|
|
189
|
-
"@tailwindcss/oxide",
|
|
190
|
-
"better-sqlite3"
|
|
191
|
-
],
|
|
192
|
-
"ignoredBuiltDependencies": [
|
|
193
|
-
"esbuild",
|
|
194
|
-
"oxc-resolver",
|
|
195
|
-
"unrs-resolver",
|
|
196
|
-
"vue-demi"
|
|
197
|
-
]
|
|
198
|
-
}
|
|
186
|
+
"packageManager": "pnpm@10.24.0"
|
|
199
187
|
}
|