@nuxt/test-utils 3.18.0 → 3.19.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.d.mts +13 -10
- package/dist/config.mjs +92 -23
- package/dist/module.mjs +7 -0
- package/dist/runtime/browser-entry.d.ts +1 -0
- package/dist/runtime/browser-entry.mjs +8 -0
- package/dist/runtime/entry.mjs +2 -5
- package/dist/runtime/shared/environment.d.ts +7 -0
- package/dist/runtime/shared/environment.mjs +123 -0
- package/dist/runtime/shared/nuxt.d.ts +1 -0
- package/dist/runtime/shared/nuxt.mjs +7 -0
- package/dist/vitest-environment.mjs +124 -107
- package/package.json +22 -22
package/dist/config.d.mts
CHANGED
|
@@ -1,23 +1,25 @@
|
|
|
1
|
-
import * as
|
|
2
|
-
import { InlineConfig } from 'vite';
|
|
1
|
+
import * as vitest_config_js from 'vitest/config.js';
|
|
3
2
|
import { NuxtConfig, Nuxt } from '@nuxt/schema';
|
|
4
|
-
import { InlineConfig
|
|
3
|
+
import { InlineConfig } from 'vitest/node';
|
|
4
|
+
import { TestProjectInlineConfiguration } from 'vitest/config';
|
|
5
5
|
import { DotenvOptions } from 'c12';
|
|
6
|
+
import { UserConfig } from 'vite';
|
|
6
7
|
|
|
7
8
|
interface GetVitestConfigOptions {
|
|
8
9
|
nuxt: Nuxt;
|
|
9
|
-
viteConfig:
|
|
10
|
+
viteConfig: UserConfig;
|
|
10
11
|
}
|
|
11
12
|
interface LoadNuxtOptions {
|
|
12
13
|
dotenv?: Partial<DotenvOptions>;
|
|
13
14
|
overrides?: Partial<NuxtConfig>;
|
|
14
15
|
}
|
|
15
|
-
declare function getVitestConfigFromNuxt(options?: GetVitestConfigOptions, loadNuxtOptions?: LoadNuxtOptions): Promise<
|
|
16
|
-
test: InlineConfig
|
|
16
|
+
declare function getVitestConfigFromNuxt(options?: GetVitestConfigOptions, loadNuxtOptions?: LoadNuxtOptions): Promise<UserConfig & {
|
|
17
|
+
test: InlineConfig;
|
|
17
18
|
}>;
|
|
18
|
-
declare function
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
declare function defineVitestProject(config: TestProjectInlineConfiguration): Promise<TestProjectInlineConfiguration>;
|
|
20
|
+
declare function defineVitestConfig(config?: UserConfig & {
|
|
21
|
+
test?: InlineConfig;
|
|
22
|
+
}): vitest_config_js.UserConfigExport;
|
|
21
23
|
interface NuxtEnvironmentOptions {
|
|
22
24
|
rootDir?: string;
|
|
23
25
|
/**
|
|
@@ -61,4 +63,5 @@ declare module 'vitest' {
|
|
|
61
63
|
}
|
|
62
64
|
}
|
|
63
65
|
|
|
64
|
-
export { defineVitestConfig, getVitestConfigFromNuxt };
|
|
66
|
+
export { defineVitestConfig, defineVitestProject, getVitestConfigFromNuxt };
|
|
67
|
+
export type { NuxtEnvironmentOptions };
|
package/dist/config.mjs
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import process$1 from 'node:process';
|
|
2
|
+
import { defineConfig } from 'vitest/config';
|
|
2
3
|
import { setupDotenv } from 'c12';
|
|
3
4
|
import { defu } from 'defu';
|
|
4
|
-
import { createResolver, findPath } from '@nuxt/kit';
|
|
5
|
+
import { createResolver, findPath, loadNuxt, buildNuxt } from '@nuxt/kit';
|
|
5
6
|
import destr from 'destr';
|
|
6
7
|
import { snakeCase } from 'scule';
|
|
7
8
|
|
|
@@ -35,8 +36,7 @@ function applyEnv(obj, opts, parentKey = "") {
|
|
|
35
36
|
return obj;
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
async function startNuxtAndGetViteConfig(rootDir = process.cwd(), options = {}) {
|
|
39
|
-
const { loadNuxt, buildNuxt } = await import('@nuxt/kit');
|
|
39
|
+
async function startNuxtAndGetViteConfig(rootDir = process$1.cwd(), options = {}) {
|
|
40
40
|
const nuxt = await loadNuxt({
|
|
41
41
|
cwd: rootDir,
|
|
42
42
|
dev: false,
|
|
@@ -85,7 +85,7 @@ const excludedPlugins = [
|
|
|
85
85
|
"vite-plugin-checker"
|
|
86
86
|
];
|
|
87
87
|
async function getVitestConfigFromNuxt(options, loadNuxtOptions = {}) {
|
|
88
|
-
const { rootDir = process.cwd(), ..._overrides } = loadNuxtOptions.overrides || {};
|
|
88
|
+
const { rootDir = process$1.cwd(), ..._overrides } = loadNuxtOptions.overrides || {};
|
|
89
89
|
if (!options) {
|
|
90
90
|
options = await startNuxtAndGetViteConfig(rootDir, {
|
|
91
91
|
dotenv: loadNuxtOptions.dotenv,
|
|
@@ -102,8 +102,11 @@ async function getVitestConfigFromNuxt(options, loadNuxtOptions = {}) {
|
|
|
102
102
|
define: {
|
|
103
103
|
"process.env.NODE_ENV": '"test"'
|
|
104
104
|
},
|
|
105
|
+
optimizeDeps: {
|
|
106
|
+
noDiscovery: true
|
|
107
|
+
},
|
|
105
108
|
test: {
|
|
106
|
-
dir: process.cwd(),
|
|
109
|
+
dir: process$1.cwd(),
|
|
107
110
|
environmentOptions: {
|
|
108
111
|
nuxtRuntimeConfig: applyEnv(structuredClone(options.nuxt.options.runtimeConfig), {
|
|
109
112
|
prefix: "NUXT_",
|
|
@@ -118,10 +121,6 @@ async function getVitestConfigFromNuxt(options, loadNuxtOptions = {}) {
|
|
|
118
121
|
options.nuxt.options.nitro?.routeRules
|
|
119
122
|
)
|
|
120
123
|
},
|
|
121
|
-
environmentMatchGlobs: [
|
|
122
|
-
["**/*.nuxt.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}", "nuxt"],
|
|
123
|
-
["{test,tests}/nuxt/**.*", "nuxt"]
|
|
124
|
-
],
|
|
125
124
|
server: {
|
|
126
125
|
deps: {
|
|
127
126
|
inline: [
|
|
@@ -152,6 +151,7 @@ async function getVitestConfigFromNuxt(options, loadNuxtOptions = {}) {
|
|
|
152
151
|
server: { middlewareMode: false },
|
|
153
152
|
plugins: [
|
|
154
153
|
{
|
|
154
|
+
// TODO: prefix with 'nuxt:test-utils:' in next major version
|
|
155
155
|
name: "disable-auto-execute",
|
|
156
156
|
enforce: "pre",
|
|
157
157
|
transform(code, id) {
|
|
@@ -162,6 +162,17 @@ async function getVitestConfigFromNuxt(options, loadNuxtOptions = {}) {
|
|
|
162
162
|
);
|
|
163
163
|
}
|
|
164
164
|
}
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
name: "nuxt:test-utils:browser-conditions",
|
|
168
|
+
enforce: "pre",
|
|
169
|
+
config() {
|
|
170
|
+
return {
|
|
171
|
+
resolve: {
|
|
172
|
+
conditions: ["web", "import", "module", "default"]
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
}
|
|
165
176
|
}
|
|
166
177
|
]
|
|
167
178
|
},
|
|
@@ -192,22 +203,80 @@ async function getVitestConfigFromNuxt(options, loadNuxtOptions = {}) {
|
|
|
192
203
|
resolvedConfig.test.setupFiles.unshift(await findPath(entryPath) ?? entryPath);
|
|
193
204
|
return resolvedConfig;
|
|
194
205
|
}
|
|
206
|
+
async function defineVitestProject(config) {
|
|
207
|
+
if (process$1.env.__NUXT_VITEST_RESOLVED__) return config;
|
|
208
|
+
const resolvedConfig = await resolveConfig(config);
|
|
209
|
+
resolvedConfig.test.environment = "nuxt";
|
|
210
|
+
return resolvedConfig;
|
|
211
|
+
}
|
|
195
212
|
function defineVitestConfig(config = {}) {
|
|
196
213
|
return defineConfig(async () => {
|
|
197
|
-
if (process.env.__NUXT_VITEST_RESOLVED__) return config;
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
config.test.setupFiles = [config.test.setupFiles].filter(Boolean);
|
|
214
|
+
if (process$1.env.__NUXT_VITEST_RESOLVED__) return config;
|
|
215
|
+
const resolvedConfig = await resolveConfig(config);
|
|
216
|
+
if (resolvedConfig.test.browser?.enabled) {
|
|
217
|
+
return resolvedConfig;
|
|
202
218
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
)
|
|
219
|
+
if ("workspace" in resolvedConfig.test) {
|
|
220
|
+
throw new Error(
|
|
221
|
+
"The `workspace` option is not supported with `defineVitestConfig`. Instead, use `defineVitestProject` to define each workspace project that uses the Nuxt environment."
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
const defaultEnvironment = resolvedConfig.test.environment || "node";
|
|
225
|
+
if (defaultEnvironment !== "nuxt") {
|
|
226
|
+
resolvedConfig.test.workspace = [];
|
|
227
|
+
resolvedConfig.test.workspace.push({
|
|
228
|
+
extends: true,
|
|
229
|
+
test: {
|
|
230
|
+
name: "nuxt",
|
|
231
|
+
environment: "nuxt",
|
|
232
|
+
include: [
|
|
233
|
+
"**/*.nuxt.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}",
|
|
234
|
+
"{test,tests}/nuxt/**.*"
|
|
235
|
+
]
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
return resolvedConfig;
|
|
210
240
|
});
|
|
211
241
|
}
|
|
242
|
+
async function resolveConfig(config) {
|
|
243
|
+
const overrides = config.test?.environmentOptions?.nuxt?.overrides || {};
|
|
244
|
+
overrides.rootDir = config.test?.environmentOptions?.nuxt?.rootDir;
|
|
245
|
+
if (config.test?.setupFiles && !Array.isArray(config.test.setupFiles)) {
|
|
246
|
+
config.test.setupFiles = [config.test.setupFiles].filter(Boolean);
|
|
247
|
+
}
|
|
248
|
+
const resolvedConfig = defu(
|
|
249
|
+
config,
|
|
250
|
+
await getVitestConfigFromNuxt(void 0, {
|
|
251
|
+
dotenv: config.test?.environmentOptions?.nuxt?.dotenv,
|
|
252
|
+
overrides: structuredClone(overrides)
|
|
253
|
+
})
|
|
254
|
+
);
|
|
255
|
+
const PLUGIN_NAME = "nuxt:vitest:nuxt-environment-options";
|
|
256
|
+
const STUB_ID = "nuxt-vitest-environment-options";
|
|
257
|
+
resolvedConfig.plugins.push({
|
|
258
|
+
name: PLUGIN_NAME,
|
|
259
|
+
enforce: "pre",
|
|
260
|
+
resolveId(id) {
|
|
261
|
+
if (id.endsWith(STUB_ID)) {
|
|
262
|
+
return STUB_ID;
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
load(id) {
|
|
266
|
+
if (id.endsWith(STUB_ID)) {
|
|
267
|
+
return `export default ${JSON.stringify(resolvedConfig.test.environmentOptions || {})}`;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
if (resolvedConfig.test.browser?.enabled) {
|
|
272
|
+
if (resolvedConfig.test.environment === "nuxt") {
|
|
273
|
+
resolvedConfig.test.setupFiles = Array.isArray(resolvedConfig.test.setupFiles) ? resolvedConfig.test.setupFiles : [resolvedConfig.test.setupFiles].filter(Boolean);
|
|
274
|
+
const resolver = createResolver(import.meta.url);
|
|
275
|
+
const browserEntry = await findPath(resolver.resolve("./runtime/browser-entry")) || resolver.resolve("./runtime/browser-entry");
|
|
276
|
+
resolvedConfig.test.setupFiles.unshift(browserEntry);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return resolvedConfig;
|
|
280
|
+
}
|
|
212
281
|
|
|
213
|
-
export { defineVitestConfig, getVitestConfigFromNuxt };
|
|
282
|
+
export { defineVitestConfig, defineVitestProject, getVitestConfigFromNuxt };
|
package/dist/module.mjs
CHANGED
|
@@ -12,6 +12,8 @@ import MagicString from 'magic-string';
|
|
|
12
12
|
import { createUnplugin } from 'unplugin';
|
|
13
13
|
import { readFileSync } from 'node:fs';
|
|
14
14
|
import { extname, join, dirname } from 'pathe';
|
|
15
|
+
import 'node:process';
|
|
16
|
+
import 'vitest/config';
|
|
15
17
|
import 'c12';
|
|
16
18
|
import 'destr';
|
|
17
19
|
import 'scule';
|
|
@@ -364,6 +366,11 @@ const module = defineNuxtModule({
|
|
|
364
366
|
viteConfig.plugins = (viteConfig.plugins || []).filter((p) => {
|
|
365
367
|
return !p || !("name" in p) || !vitePluginBlocklist.includes(p.name);
|
|
366
368
|
});
|
|
369
|
+
viteConfig.test.environmentMatchGlobs ||= [];
|
|
370
|
+
viteConfig.test.environmentMatchGlobs.push(
|
|
371
|
+
["**/*.nuxt.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}", "nuxt"],
|
|
372
|
+
["{test,tests}/nuxt/**.*", "nuxt"]
|
|
373
|
+
);
|
|
367
374
|
process.env.__NUXT_VITEST_RESOLVED__ = "true";
|
|
368
375
|
const { startVitest } = await import(pathToFileURL(await resolvePath("vitest/node")).href);
|
|
369
376
|
const customReporter = {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import environmentOptions from "nuxt-vitest-environment-options";
|
|
2
|
+
import { setupNuxt } from "./shared/nuxt.mjs";
|
|
3
|
+
import { setupWindow } from "./shared/environment.mjs";
|
|
4
|
+
const el = document.querySelector(environmentOptions.nuxt.rootId || "nuxt-test");
|
|
5
|
+
if (!el) {
|
|
6
|
+
await setupWindow(window, environmentOptions);
|
|
7
|
+
await setupNuxt();
|
|
8
|
+
}
|
package/dist/runtime/entry.mjs
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import { vi } from "vitest";
|
|
2
|
+
import { setupNuxt } from "./shared/nuxt.mjs";
|
|
2
3
|
if (typeof window !== "undefined" && window.__NUXT_VITEST_ENVIRONMENT__) {
|
|
3
|
-
|
|
4
|
-
await import("#app/nuxt-vitest-app-entry").then((r) => r.default());
|
|
5
|
-
const nuxtApp = useNuxtApp();
|
|
6
|
-
await nuxtApp.callHook("page:finish");
|
|
7
|
-
useRouter().afterEach(() => nuxtApp.callHook("page:finish"));
|
|
4
|
+
await setupNuxt();
|
|
8
5
|
vi.resetModules();
|
|
9
6
|
}
|
|
10
7
|
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { NuxtWindow } from '../../vitest-environment';
|
|
2
|
+
import type { NuxtEnvironmentOptions } from '../../config';
|
|
3
|
+
export declare function setupWindow(win: NuxtWindow, environmentOptions: {
|
|
4
|
+
nuxt: NuxtEnvironmentOptions;
|
|
5
|
+
nuxtRuntimeConfig?: Record<string, any>;
|
|
6
|
+
nuxtRouteRules?: Record<string, any>;
|
|
7
|
+
}): Promise<() => void>;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { createFetch } from "ofetch";
|
|
2
|
+
import { joinURL } from "ufo";
|
|
3
|
+
import { createApp, defineEventHandler, toNodeListener } from "h3";
|
|
4
|
+
import { createRouter as createRadixRouter, exportMatcher, toRouteMatcher } from "radix3";
|
|
5
|
+
import { fetchNodeRequestHandler } from "node-mock-http";
|
|
6
|
+
export async function setupWindow(win, environmentOptions) {
|
|
7
|
+
win.__NUXT_VITEST_ENVIRONMENT__ = true;
|
|
8
|
+
win.__NUXT__ = {
|
|
9
|
+
serverRendered: false,
|
|
10
|
+
config: {
|
|
11
|
+
public: {},
|
|
12
|
+
app: { baseURL: "/" },
|
|
13
|
+
...environmentOptions?.nuxtRuntimeConfig
|
|
14
|
+
},
|
|
15
|
+
data: {},
|
|
16
|
+
state: {}
|
|
17
|
+
};
|
|
18
|
+
const rootId = environmentOptions.nuxt.rootId || "nuxt-test";
|
|
19
|
+
let el;
|
|
20
|
+
try {
|
|
21
|
+
el = win.document.querySelector(rootId);
|
|
22
|
+
} catch {
|
|
23
|
+
}
|
|
24
|
+
if (el) {
|
|
25
|
+
return () => {
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
const consoleInfo = console.info;
|
|
29
|
+
console.info = (...args) => {
|
|
30
|
+
if (args[0] === "<Suspense> is an experimental feature and its API will likely change.") {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
return consoleInfo(...args);
|
|
34
|
+
};
|
|
35
|
+
const app = win.document.createElement("div");
|
|
36
|
+
app.id = rootId;
|
|
37
|
+
win.document.body.appendChild(app);
|
|
38
|
+
const h3App = createApp();
|
|
39
|
+
if (!win.fetch) {
|
|
40
|
+
await import("node-fetch-native/polyfill");
|
|
41
|
+
win.URLSearchParams = globalThis.URLSearchParams;
|
|
42
|
+
}
|
|
43
|
+
const nodeHandler = toNodeListener(h3App);
|
|
44
|
+
const registry = /* @__PURE__ */ new Set();
|
|
45
|
+
win.fetch = async (url, init) => {
|
|
46
|
+
if (typeof url === "string") {
|
|
47
|
+
const base = url.split("?")[0];
|
|
48
|
+
if (registry.has(base) || registry.has(url)) {
|
|
49
|
+
url = "/_" + url;
|
|
50
|
+
}
|
|
51
|
+
if (url.startsWith("/")) {
|
|
52
|
+
const response = await fetchNodeRequestHandler(nodeHandler, url, init);
|
|
53
|
+
return normalizeFetchResponse(response);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return fetch(url, init);
|
|
57
|
+
};
|
|
58
|
+
win.$fetch = createFetch({ fetch: win.fetch, Headers: win.Headers });
|
|
59
|
+
win.__registry = registry;
|
|
60
|
+
win.__app = h3App;
|
|
61
|
+
const timestamp = Date.now();
|
|
62
|
+
const routeRulesMatcher = toRouteMatcher(
|
|
63
|
+
createRadixRouter({ routes: environmentOptions.nuxtRouteRules || {} })
|
|
64
|
+
);
|
|
65
|
+
const matcher = exportMatcher(routeRulesMatcher);
|
|
66
|
+
const manifestOutputPath = joinURL(
|
|
67
|
+
environmentOptions?.nuxtRuntimeConfig?.app?.baseURL || "/",
|
|
68
|
+
environmentOptions?.nuxtRuntimeConfig?.app?.buildAssetsDir || "_nuxt",
|
|
69
|
+
"builds"
|
|
70
|
+
);
|
|
71
|
+
const manifestBaseRoutePath = joinURL("/_", manifestOutputPath);
|
|
72
|
+
const buildId = win.__NUXT__.config?.app.buildId || "test";
|
|
73
|
+
h3App.use(
|
|
74
|
+
`${manifestBaseRoutePath}/latest.json`,
|
|
75
|
+
defineEventHandler(() => ({
|
|
76
|
+
id: buildId,
|
|
77
|
+
timestamp
|
|
78
|
+
}))
|
|
79
|
+
);
|
|
80
|
+
h3App.use(
|
|
81
|
+
`${manifestBaseRoutePath}/meta/${buildId}.json`,
|
|
82
|
+
defineEventHandler(() => ({
|
|
83
|
+
id: buildId,
|
|
84
|
+
timestamp,
|
|
85
|
+
matcher,
|
|
86
|
+
prerendered: []
|
|
87
|
+
}))
|
|
88
|
+
);
|
|
89
|
+
registry.add(`${manifestOutputPath}/latest.json`);
|
|
90
|
+
registry.add(`${manifestOutputPath}/meta/${buildId}.json`);
|
|
91
|
+
return () => {
|
|
92
|
+
console.info = consoleInfo;
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
function normalizeFetchResponse(response) {
|
|
96
|
+
if (!response.headers.has("set-cookie")) {
|
|
97
|
+
return response;
|
|
98
|
+
}
|
|
99
|
+
return new Response(response.body, {
|
|
100
|
+
status: response.status,
|
|
101
|
+
statusText: response.statusText,
|
|
102
|
+
headers: normalizeCookieHeaders(response.headers)
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
function normalizeCookieHeader(header = "") {
|
|
106
|
+
return splitCookiesString(joinHeaders(header));
|
|
107
|
+
}
|
|
108
|
+
function normalizeCookieHeaders(headers) {
|
|
109
|
+
const outgoingHeaders = new Headers();
|
|
110
|
+
for (const [name, header] of headers) {
|
|
111
|
+
if (name === "set-cookie") {
|
|
112
|
+
for (const cookie of normalizeCookieHeader(header)) {
|
|
113
|
+
outgoingHeaders.append("set-cookie", cookie);
|
|
114
|
+
}
|
|
115
|
+
} else {
|
|
116
|
+
outgoingHeaders.set(name, joinHeaders(header));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return outgoingHeaders;
|
|
120
|
+
}
|
|
121
|
+
function joinHeaders(value) {
|
|
122
|
+
return Array.isArray(value) ? value.join(", ") : String(value);
|
|
123
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function setupNuxt(): Promise<void>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export async function setupNuxt() {
|
|
2
|
+
const { useRouter } = await import("#app/composables/router");
|
|
3
|
+
await import("#app/nuxt-vitest-app-entry").then((r) => r.default());
|
|
4
|
+
const nuxtApp = useNuxtApp();
|
|
5
|
+
await nuxtApp.callHook("page:finish");
|
|
6
|
+
useRouter().afterEach(() => nuxtApp.callHook("page:finish"));
|
|
7
|
+
}
|
|
@@ -1,13 +1,132 @@
|
|
|
1
|
-
import { createFetch } from 'ofetch';
|
|
2
1
|
import { indexedDB } from 'fake-indexeddb';
|
|
3
2
|
import { joinURL } from 'ufo';
|
|
4
|
-
import { createApp, toNodeListener, defineEventHandler, splitCookiesString } from 'h3';
|
|
5
3
|
import defu from 'defu';
|
|
6
|
-
import { toRouteMatcher, createRouter, exportMatcher } from 'radix3';
|
|
7
4
|
import { populateGlobal } from 'vitest/environments';
|
|
5
|
+
import { createFetch } from 'ofetch';
|
|
6
|
+
import { createApp, toNodeListener, defineEventHandler } from 'h3';
|
|
7
|
+
import { toRouteMatcher, createRouter, exportMatcher } from 'radix3';
|
|
8
8
|
import { fetchNodeRequestHandler } from 'node-mock-http';
|
|
9
9
|
import { importModule } from 'local-pkg';
|
|
10
10
|
|
|
11
|
+
async function setupWindow(win, environmentOptions) {
|
|
12
|
+
win.__NUXT_VITEST_ENVIRONMENT__ = true;
|
|
13
|
+
win.__NUXT__ = {
|
|
14
|
+
serverRendered: false,
|
|
15
|
+
config: {
|
|
16
|
+
public: {},
|
|
17
|
+
app: { baseURL: "/" },
|
|
18
|
+
...environmentOptions?.nuxtRuntimeConfig
|
|
19
|
+
},
|
|
20
|
+
data: {},
|
|
21
|
+
state: {}
|
|
22
|
+
};
|
|
23
|
+
const rootId = environmentOptions.nuxt.rootId || "nuxt-test";
|
|
24
|
+
let el;
|
|
25
|
+
try {
|
|
26
|
+
el = win.document.querySelector(rootId);
|
|
27
|
+
} catch {
|
|
28
|
+
}
|
|
29
|
+
if (el) {
|
|
30
|
+
return () => {
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
const consoleInfo = console.info;
|
|
34
|
+
console.info = (...args) => {
|
|
35
|
+
if (args[0] === "<Suspense> is an experimental feature and its API will likely change.") {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
return consoleInfo(...args);
|
|
39
|
+
};
|
|
40
|
+
const app = win.document.createElement("div");
|
|
41
|
+
app.id = rootId;
|
|
42
|
+
win.document.body.appendChild(app);
|
|
43
|
+
const h3App = createApp();
|
|
44
|
+
if (!win.fetch) {
|
|
45
|
+
await import('node-fetch-native/polyfill');
|
|
46
|
+
win.URLSearchParams = globalThis.URLSearchParams;
|
|
47
|
+
}
|
|
48
|
+
const nodeHandler = toNodeListener(h3App);
|
|
49
|
+
const registry = /* @__PURE__ */ new Set();
|
|
50
|
+
win.fetch = async (url, init) => {
|
|
51
|
+
if (typeof url === "string") {
|
|
52
|
+
const base = url.split("?")[0];
|
|
53
|
+
if (registry.has(base) || registry.has(url)) {
|
|
54
|
+
url = "/_" + url;
|
|
55
|
+
}
|
|
56
|
+
if (url.startsWith("/")) {
|
|
57
|
+
const response = await fetchNodeRequestHandler(nodeHandler, url, init);
|
|
58
|
+
return normalizeFetchResponse(response);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return fetch(url, init);
|
|
62
|
+
};
|
|
63
|
+
win.$fetch = createFetch({ fetch: win.fetch, Headers: win.Headers });
|
|
64
|
+
win.__registry = registry;
|
|
65
|
+
win.__app = h3App;
|
|
66
|
+
const timestamp = Date.now();
|
|
67
|
+
const routeRulesMatcher = toRouteMatcher(
|
|
68
|
+
createRouter({ routes: environmentOptions.nuxtRouteRules || {} })
|
|
69
|
+
);
|
|
70
|
+
const matcher = exportMatcher(routeRulesMatcher);
|
|
71
|
+
const manifestOutputPath = joinURL(
|
|
72
|
+
environmentOptions?.nuxtRuntimeConfig?.app?.baseURL || "/",
|
|
73
|
+
environmentOptions?.nuxtRuntimeConfig?.app?.buildAssetsDir || "_nuxt",
|
|
74
|
+
"builds"
|
|
75
|
+
);
|
|
76
|
+
const manifestBaseRoutePath = joinURL("/_", manifestOutputPath);
|
|
77
|
+
const buildId = win.__NUXT__.config?.app.buildId || "test";
|
|
78
|
+
h3App.use(
|
|
79
|
+
`${manifestBaseRoutePath}/latest.json`,
|
|
80
|
+
defineEventHandler(() => ({
|
|
81
|
+
id: buildId,
|
|
82
|
+
timestamp
|
|
83
|
+
}))
|
|
84
|
+
);
|
|
85
|
+
h3App.use(
|
|
86
|
+
`${manifestBaseRoutePath}/meta/${buildId}.json`,
|
|
87
|
+
defineEventHandler(() => ({
|
|
88
|
+
id: buildId,
|
|
89
|
+
timestamp,
|
|
90
|
+
matcher,
|
|
91
|
+
prerendered: []
|
|
92
|
+
}))
|
|
93
|
+
);
|
|
94
|
+
registry.add(`${manifestOutputPath}/latest.json`);
|
|
95
|
+
registry.add(`${manifestOutputPath}/meta/${buildId}.json`);
|
|
96
|
+
return () => {
|
|
97
|
+
console.info = consoleInfo;
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function normalizeFetchResponse(response) {
|
|
101
|
+
if (!response.headers.has("set-cookie")) {
|
|
102
|
+
return response;
|
|
103
|
+
}
|
|
104
|
+
return new Response(response.body, {
|
|
105
|
+
status: response.status,
|
|
106
|
+
statusText: response.statusText,
|
|
107
|
+
headers: normalizeCookieHeaders(response.headers)
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
function normalizeCookieHeader(header = "") {
|
|
111
|
+
return splitCookiesString(joinHeaders(header));
|
|
112
|
+
}
|
|
113
|
+
function normalizeCookieHeaders(headers) {
|
|
114
|
+
const outgoingHeaders = new Headers();
|
|
115
|
+
for (const [name, header] of headers) {
|
|
116
|
+
if (name === "set-cookie") {
|
|
117
|
+
for (const cookie of normalizeCookieHeader(header)) {
|
|
118
|
+
outgoingHeaders.append("set-cookie", cookie);
|
|
119
|
+
}
|
|
120
|
+
} else {
|
|
121
|
+
outgoingHeaders.set(name, joinHeaders(header));
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return outgoingHeaders;
|
|
125
|
+
}
|
|
126
|
+
function joinHeaders(value) {
|
|
127
|
+
return Array.isArray(value) ? value.join(", ") : String(value);
|
|
128
|
+
}
|
|
129
|
+
|
|
11
130
|
const happyDom = (async function(_, { happyDom = {} }) {
|
|
12
131
|
const { Window, GlobalWindow } = await importModule("happy-dom");
|
|
13
132
|
const window = new (GlobalWindow || Window)(happyDom);
|
|
@@ -58,33 +177,12 @@ const index = {
|
|
|
58
177
|
environmentOptions?.nuxt.url ?? "http://localhost:3000",
|
|
59
178
|
environmentOptions?.nuxtRuntimeConfig.app?.baseURL || "/"
|
|
60
179
|
);
|
|
61
|
-
const consoleInfo = console.info;
|
|
62
|
-
console.info = (...args) => {
|
|
63
|
-
if (args[0] === "<Suspense> is an experimental feature and its API will likely change.") {
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
return consoleInfo(...args);
|
|
67
|
-
};
|
|
68
180
|
const environmentName = environmentOptions.nuxt.domEnvironment;
|
|
69
181
|
const environment = environmentMap[environmentName] || environmentMap["happy-dom"];
|
|
70
182
|
const { window: win, teardown } = await environment(global, defu(environmentOptions, {
|
|
71
183
|
happyDom: { url },
|
|
72
184
|
jsdom: { url }
|
|
73
185
|
}));
|
|
74
|
-
win.__NUXT_VITEST_ENVIRONMENT__ = true;
|
|
75
|
-
win.__NUXT__ = {
|
|
76
|
-
serverRendered: false,
|
|
77
|
-
config: {
|
|
78
|
-
public: {},
|
|
79
|
-
app: { baseURL: "/" },
|
|
80
|
-
...environmentOptions?.nuxtRuntimeConfig
|
|
81
|
-
},
|
|
82
|
-
data: {},
|
|
83
|
-
state: {}
|
|
84
|
-
};
|
|
85
|
-
const app = win.document.createElement("div");
|
|
86
|
-
app.id = environmentOptions.nuxt.rootId;
|
|
87
|
-
win.document.body.appendChild(app);
|
|
88
186
|
if (environmentOptions?.nuxt?.mock?.intersectionObserver) {
|
|
89
187
|
win.IntersectionObserver = win.IntersectionObserver || class IntersectionObserver {
|
|
90
188
|
observe() {
|
|
@@ -98,101 +196,20 @@ const index = {
|
|
|
98
196
|
if (environmentOptions?.nuxt?.mock?.indexedDb) {
|
|
99
197
|
win.indexedDB = indexedDB;
|
|
100
198
|
}
|
|
101
|
-
const
|
|
102
|
-
if (!win.fetch) {
|
|
103
|
-
await import('node-fetch-native/polyfill');
|
|
104
|
-
win.URLSearchParams = globalThis.URLSearchParams;
|
|
105
|
-
}
|
|
106
|
-
const nodeHandler = toNodeListener(h3App);
|
|
107
|
-
const registry = /* @__PURE__ */ new Set();
|
|
108
|
-
win.fetch = async (url2, init) => {
|
|
109
|
-
if (typeof url2 === "string") {
|
|
110
|
-
const base = url2.split("?")[0];
|
|
111
|
-
if (registry.has(base) || registry.has(url2)) {
|
|
112
|
-
url2 = "/_" + url2;
|
|
113
|
-
}
|
|
114
|
-
if (url2.startsWith("/")) {
|
|
115
|
-
const response = await fetchNodeRequestHandler(nodeHandler, url2, init);
|
|
116
|
-
return normalizeFetchResponse(response);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
return fetch(url2, init);
|
|
120
|
-
};
|
|
121
|
-
win.$fetch = createFetch({ fetch: win.fetch, Headers: win.Headers });
|
|
122
|
-
win.__registry = registry;
|
|
123
|
-
win.__app = h3App;
|
|
199
|
+
const teardownWindow = await setupWindow(win, environmentOptions);
|
|
124
200
|
const { keys, originals } = populateGlobal(global, win, {
|
|
125
201
|
bindFunctions: true
|
|
126
202
|
});
|
|
127
|
-
const timestamp = Date.now();
|
|
128
|
-
const routeRulesMatcher = toRouteMatcher(
|
|
129
|
-
createRouter({ routes: environmentOptions.nuxtRouteRules || {} })
|
|
130
|
-
);
|
|
131
|
-
const matcher = exportMatcher(routeRulesMatcher);
|
|
132
|
-
const manifestOutputPath = joinURL(
|
|
133
|
-
environmentOptions?.nuxtRuntimeConfig.app?.baseURL || "/",
|
|
134
|
-
environmentOptions?.nuxtRuntimeConfig.app?.buildAssetsDir || "_nuxt",
|
|
135
|
-
"builds"
|
|
136
|
-
);
|
|
137
|
-
const manifestBaseRoutePath = joinURL("/_", manifestOutputPath);
|
|
138
|
-
const buildId = win.__NUXT__.config.app.buildId || "test";
|
|
139
|
-
h3App.use(
|
|
140
|
-
`${manifestBaseRoutePath}/latest.json`,
|
|
141
|
-
defineEventHandler(() => ({
|
|
142
|
-
id: buildId,
|
|
143
|
-
timestamp
|
|
144
|
-
}))
|
|
145
|
-
);
|
|
146
|
-
h3App.use(
|
|
147
|
-
`${manifestBaseRoutePath}/meta/${buildId}.json`,
|
|
148
|
-
defineEventHandler(() => ({
|
|
149
|
-
id: buildId,
|
|
150
|
-
timestamp,
|
|
151
|
-
matcher,
|
|
152
|
-
prerendered: []
|
|
153
|
-
}))
|
|
154
|
-
);
|
|
155
|
-
registry.add(`${manifestOutputPath}/latest.json`);
|
|
156
|
-
registry.add(`${manifestOutputPath}/meta/${buildId}.json`);
|
|
157
203
|
return {
|
|
158
204
|
// called after all tests with this env have been run
|
|
159
205
|
teardown() {
|
|
160
206
|
keys.forEach((key) => delete global[key]);
|
|
161
|
-
|
|
207
|
+
teardownWindow();
|
|
162
208
|
originals.forEach((v, k) => global[k] = v);
|
|
163
209
|
teardown();
|
|
164
210
|
}
|
|
165
211
|
};
|
|
166
212
|
}
|
|
167
213
|
};
|
|
168
|
-
function normalizeFetchResponse(response) {
|
|
169
|
-
if (!response.headers.has("set-cookie")) {
|
|
170
|
-
return response;
|
|
171
|
-
}
|
|
172
|
-
return new Response(response.body, {
|
|
173
|
-
status: response.status,
|
|
174
|
-
statusText: response.statusText,
|
|
175
|
-
headers: normalizeCookieHeaders(response.headers)
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
function normalizeCookieHeader(header = "") {
|
|
179
|
-
return splitCookiesString(joinHeaders(header));
|
|
180
|
-
}
|
|
181
|
-
function normalizeCookieHeaders(headers) {
|
|
182
|
-
const outgoingHeaders = new Headers();
|
|
183
|
-
for (const [name, header] of headers) {
|
|
184
|
-
if (name === "set-cookie") {
|
|
185
|
-
for (const cookie of normalizeCookieHeader(header)) {
|
|
186
|
-
outgoingHeaders.append("set-cookie", cookie);
|
|
187
|
-
}
|
|
188
|
-
} else {
|
|
189
|
-
outgoingHeaders.set(name, joinHeaders(header));
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
return outgoingHeaders;
|
|
193
|
-
}
|
|
194
|
-
function joinHeaders(value) {
|
|
195
|
-
return Array.isArray(value) ? value.join(", ") : String(value);
|
|
196
|
-
}
|
|
197
214
|
|
|
198
215
|
export { index as default };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nuxt/test-utils",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.19.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/nuxt/test-utils.git"
|
|
@@ -63,14 +63,14 @@
|
|
|
63
63
|
"dev:prepare": "nuxi prepare && unbuild --stub && pnpm -r dev:prepare"
|
|
64
64
|
},
|
|
65
65
|
"dependencies": {
|
|
66
|
-
"@nuxt/kit": "^3.17.
|
|
67
|
-
"@nuxt/schema": "^3.17.
|
|
66
|
+
"@nuxt/kit": "^3.17.3",
|
|
67
|
+
"@nuxt/schema": "^3.17.3",
|
|
68
68
|
"c12": "^3.0.3",
|
|
69
69
|
"consola": "^3.4.2",
|
|
70
70
|
"defu": "^6.1.4",
|
|
71
71
|
"destr": "^2.0.5",
|
|
72
72
|
"estree-walker": "^3.0.3",
|
|
73
|
-
"fake-indexeddb": "^6.0.
|
|
73
|
+
"fake-indexeddb": "^6.0.1",
|
|
74
74
|
"get-port-please": "^3.1.2",
|
|
75
75
|
"h3": "^1.15.3",
|
|
76
76
|
"local-pkg": "^1.1.1",
|
|
@@ -85,8 +85,8 @@
|
|
|
85
85
|
"std-env": "^3.9.0",
|
|
86
86
|
"tinyexec": "^1.0.1",
|
|
87
87
|
"ufo": "^1.6.1",
|
|
88
|
-
"unplugin": "^2.3.
|
|
89
|
-
"vite": "^6.3.
|
|
88
|
+
"unplugin": "^2.3.3",
|
|
89
|
+
"vite": "^6.3.5",
|
|
90
90
|
"vitest-environment-nuxt": "^1.0.1",
|
|
91
91
|
"vue": "^3.5.13"
|
|
92
92
|
},
|
|
@@ -94,30 +94,30 @@
|
|
|
94
94
|
"@cucumber/cucumber": "11.2.0",
|
|
95
95
|
"@jest/globals": "29.7.0",
|
|
96
96
|
"@nuxt/devtools-kit": "2.4.0",
|
|
97
|
-
"@nuxt/eslint-config": "1.3.
|
|
97
|
+
"@nuxt/eslint-config": "1.3.1",
|
|
98
98
|
"@playwright/test": "1.52.0",
|
|
99
99
|
"@testing-library/vue": "8.1.0",
|
|
100
|
-
"@types/bun": "1.2.
|
|
100
|
+
"@types/bun": "1.2.13",
|
|
101
101
|
"@types/estree": "1.0.7",
|
|
102
102
|
"@types/jsdom": "21.1.7",
|
|
103
|
-
"@types/node": "22.15.
|
|
103
|
+
"@types/node": "22.15.17",
|
|
104
104
|
"@types/semver": "7.7.0",
|
|
105
105
|
"@vue/test-utils": "2.4.6",
|
|
106
106
|
"changelogen": "0.6.1",
|
|
107
107
|
"compatx": "0.2.0",
|
|
108
108
|
"eslint": "9.26.0",
|
|
109
109
|
"installed-check": "9.3.0",
|
|
110
|
-
"knip": "5.
|
|
110
|
+
"knip": "5.55.1",
|
|
111
111
|
"nitropack": "2.11.11",
|
|
112
|
-
"nuxt": "3.17.
|
|
113
|
-
"pkg-pr-new": "0.0.
|
|
112
|
+
"nuxt": "3.17.3",
|
|
113
|
+
"pkg-pr-new": "0.0.44",
|
|
114
114
|
"playwright-core": "1.52.0",
|
|
115
|
-
"rollup": "4.40.
|
|
116
|
-
"semver": "7.7.
|
|
115
|
+
"rollup": "4.40.2",
|
|
116
|
+
"semver": "7.7.2",
|
|
117
117
|
"typescript": "5.8.3",
|
|
118
118
|
"unbuild": "latest",
|
|
119
|
-
"unimport": "5.0.
|
|
120
|
-
"vitest": "3.1.
|
|
119
|
+
"unimport": "5.0.1",
|
|
120
|
+
"vitest": "3.1.3",
|
|
121
121
|
"vue-router": "4.5.1",
|
|
122
122
|
"vue-tsc": "2.2.10"
|
|
123
123
|
},
|
|
@@ -167,13 +167,13 @@
|
|
|
167
167
|
"resolutions": {
|
|
168
168
|
"@cucumber/cucumber": "11.2.0",
|
|
169
169
|
"@nuxt/devtools": "1.0.8",
|
|
170
|
-
"@nuxt/kit": "^3.17.
|
|
171
|
-
"@nuxt/schema": "^3.17.
|
|
170
|
+
"@nuxt/kit": "^3.17.3",
|
|
171
|
+
"@nuxt/schema": "^3.17.3",
|
|
172
172
|
"@nuxt/test-utils": "workspace:*",
|
|
173
|
-
"rollup": "4.40.
|
|
174
|
-
"vite": "^6.3.
|
|
175
|
-
"vite-node": "3.1.
|
|
176
|
-
"vitest": "3.1.
|
|
173
|
+
"rollup": "4.40.2",
|
|
174
|
+
"vite": "^6.3.5",
|
|
175
|
+
"vite-node": "3.1.3",
|
|
176
|
+
"vitest": "3.1.3",
|
|
177
177
|
"vue": "^3.5.13"
|
|
178
178
|
},
|
|
179
179
|
"engines": {
|