@nuxt/test-utils 3.18.0 → 3.19.1

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 CHANGED
@@ -1,23 +1,25 @@
1
1
  import * as vite from 'vite';
2
- import { InlineConfig } from 'vite';
2
+ import { UserConfig } from 'vite';
3
3
  import { NuxtConfig, Nuxt } from '@nuxt/schema';
4
- import { InlineConfig as InlineConfig$1 } from 'vitest/node';
4
+ import { InlineConfig } from 'vitest/node';
5
+ import { TestProjectInlineConfiguration } from 'vitest/config';
5
6
  import { DotenvOptions } from 'c12';
6
7
 
7
8
  interface GetVitestConfigOptions {
8
9
  nuxt: Nuxt;
9
- viteConfig: InlineConfig;
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<InlineConfig & {
16
- test: InlineConfig$1;
16
+ declare function getVitestConfigFromNuxt(options?: GetVitestConfigOptions, loadNuxtOptions?: LoadNuxtOptions): Promise<UserConfig & {
17
+ test: InlineConfig;
17
18
  }>;
18
- declare function defineVitestConfig(config?: InlineConfig & {
19
- test?: InlineConfig$1;
20
- }): vite.UserConfig & Promise<vite.UserConfig> & vite.UserConfigFnObject & vite.UserConfigFnPromise & vite.UserConfigFn;
19
+ declare function defineVitestProject(config: TestProjectInlineConfiguration): Promise<TestProjectInlineConfiguration>;
20
+ declare function defineVitestConfig(config?: UserConfig & {
21
+ test?: InlineConfig;
22
+ }): vite.UserConfigFnPromise;
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 process$1 from 'node:process';
1
2
  import { defineConfig } from 'vite';
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,
@@ -82,10 +82,14 @@ async function startNuxtAndGetViteConfig(rootDir = process.cwd(), options = {})
82
82
  const excludedPlugins = [
83
83
  "nuxt:import-protection",
84
84
  "nuxt:import-conditions",
85
- "vite-plugin-checker"
85
+ "nuxt:devtools:rpc",
86
+ "nuxt:devtools:config-retriever",
87
+ "vite-plugin-checker",
88
+ "vite-plugin-inspect",
89
+ "vite-plugin-vue-tracer"
86
90
  ];
87
91
  async function getVitestConfigFromNuxt(options, loadNuxtOptions = {}) {
88
- const { rootDir = process.cwd(), ..._overrides } = loadNuxtOptions.overrides || {};
92
+ const { rootDir = process$1.cwd(), ..._overrides } = loadNuxtOptions.overrides || {};
89
93
  if (!options) {
90
94
  options = await startNuxtAndGetViteConfig(rootDir, {
91
95
  dotenv: loadNuxtOptions.dotenv,
@@ -96,14 +100,24 @@ async function getVitestConfigFromNuxt(options, loadNuxtOptions = {}) {
96
100
  });
97
101
  }
98
102
  options.viteConfig.plugins = (options.viteConfig.plugins || []).filter((p) => !p || !("name" in p) || !excludedPlugins.includes(p.name));
103
+ const resolver = createResolver(import.meta.url);
99
104
  const resolvedConfig = defu(
100
105
  // overrides
101
106
  {
102
107
  define: {
103
108
  "process.env.NODE_ENV": '"test"'
104
109
  },
110
+ resolve: {
111
+ alias: {
112
+ "@vue/devtools-kit": resolver.resolve("./runtime/mocks/vue-devtools"),
113
+ "@vue/devtools-core": resolver.resolve("./runtime/mocks/vue-devtools")
114
+ }
115
+ },
116
+ optimizeDeps: {
117
+ noDiscovery: true
118
+ },
105
119
  test: {
106
- dir: process.cwd(),
120
+ dir: process$1.cwd(),
107
121
  environmentOptions: {
108
122
  nuxtRuntimeConfig: applyEnv(structuredClone(options.nuxt.options.runtimeConfig), {
109
123
  prefix: "NUXT_",
@@ -118,10 +132,6 @@ async function getVitestConfigFromNuxt(options, loadNuxtOptions = {}) {
118
132
  options.nuxt.options.nitro?.routeRules
119
133
  )
120
134
  },
121
- environmentMatchGlobs: [
122
- ["**/*.nuxt.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}", "nuxt"],
123
- ["{test,tests}/nuxt/**.*", "nuxt"]
124
- ],
125
135
  server: {
126
136
  deps: {
127
137
  inline: [
@@ -152,6 +162,7 @@ async function getVitestConfigFromNuxt(options, loadNuxtOptions = {}) {
152
162
  server: { middlewareMode: false },
153
163
  plugins: [
154
164
  {
165
+ // TODO: prefix with 'nuxt:test-utils:' in next major version
155
166
  name: "disable-auto-execute",
156
167
  enforce: "pre",
157
168
  transform(code, id) {
@@ -162,6 +173,17 @@ async function getVitestConfigFromNuxt(options, loadNuxtOptions = {}) {
162
173
  );
163
174
  }
164
175
  }
176
+ },
177
+ {
178
+ name: "nuxt:test-utils:browser-conditions",
179
+ enforce: "pre",
180
+ config() {
181
+ return {
182
+ resolve: {
183
+ conditions: ["web", "import", "module", "default"]
184
+ }
185
+ };
186
+ }
165
187
  }
166
188
  ]
167
189
  },
@@ -187,27 +209,84 @@ async function getVitestConfigFromNuxt(options, loadNuxtOptions = {}) {
187
209
  if (!Array.isArray(resolvedConfig.test.setupFiles)) {
188
210
  resolvedConfig.test.setupFiles = [resolvedConfig.test.setupFiles].filter(Boolean);
189
211
  }
190
- const resolver = createResolver(import.meta.url);
191
212
  const entryPath = resolver.resolve("./runtime/entry");
192
213
  resolvedConfig.test.setupFiles.unshift(await findPath(entryPath) ?? entryPath);
193
214
  return resolvedConfig;
194
215
  }
216
+ async function defineVitestProject(config) {
217
+ if (process$1.env.__NUXT_VITEST_RESOLVED__) return config;
218
+ const resolvedConfig = await resolveConfig(config);
219
+ resolvedConfig.test.environment = "nuxt";
220
+ return resolvedConfig;
221
+ }
195
222
  function defineVitestConfig(config = {}) {
196
223
  return defineConfig(async () => {
197
- if (process.env.__NUXT_VITEST_RESOLVED__) return config;
198
- const overrides = config.test?.environmentOptions?.nuxt?.overrides || {};
199
- overrides.rootDir = config.test?.environmentOptions?.nuxt?.rootDir;
200
- if (config.test?.setupFiles && !Array.isArray(config.test.setupFiles)) {
201
- config.test.setupFiles = [config.test.setupFiles].filter(Boolean);
224
+ if (process$1.env.__NUXT_VITEST_RESOLVED__) return config;
225
+ const resolvedConfig = await resolveConfig(config);
226
+ if (resolvedConfig.test.browser?.enabled) {
227
+ return resolvedConfig;
202
228
  }
203
- return defu(
204
- config,
205
- await getVitestConfigFromNuxt(void 0, {
206
- dotenv: config.test?.environmentOptions?.nuxt?.dotenv,
207
- overrides: structuredClone(overrides)
208
- })
209
- );
229
+ if ("workspace" in resolvedConfig.test) {
230
+ throw new Error(
231
+ "The `workspace` option is not supported with `defineVitestConfig`. Instead, use `defineVitestProject` to define each workspace project that uses the Nuxt environment."
232
+ );
233
+ }
234
+ const defaultEnvironment = resolvedConfig.test.environment || "node";
235
+ if (defaultEnvironment !== "nuxt") {
236
+ resolvedConfig.test.workspace = [];
237
+ resolvedConfig.test.workspace.push({
238
+ extends: true,
239
+ test: {
240
+ name: "nuxt",
241
+ environment: "nuxt",
242
+ include: [
243
+ "**/*.nuxt.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}",
244
+ "{test,tests}/nuxt/**.*"
245
+ ]
246
+ }
247
+ });
248
+ }
249
+ return resolvedConfig;
210
250
  });
211
251
  }
252
+ async function resolveConfig(config) {
253
+ const overrides = config.test?.environmentOptions?.nuxt?.overrides || {};
254
+ overrides.rootDir = config.test?.environmentOptions?.nuxt?.rootDir;
255
+ if (config.test?.setupFiles && !Array.isArray(config.test.setupFiles)) {
256
+ config.test.setupFiles = [config.test.setupFiles].filter(Boolean);
257
+ }
258
+ const resolvedConfig = defu(
259
+ config,
260
+ await getVitestConfigFromNuxt(void 0, {
261
+ dotenv: config.test?.environmentOptions?.nuxt?.dotenv,
262
+ overrides: structuredClone(overrides)
263
+ })
264
+ );
265
+ const PLUGIN_NAME = "nuxt:vitest:nuxt-environment-options";
266
+ const STUB_ID = "nuxt-vitest-environment-options";
267
+ resolvedConfig.plugins.push({
268
+ name: PLUGIN_NAME,
269
+ enforce: "pre",
270
+ resolveId(id) {
271
+ if (id.endsWith(STUB_ID)) {
272
+ return STUB_ID;
273
+ }
274
+ },
275
+ load(id) {
276
+ if (id.endsWith(STUB_ID)) {
277
+ return `export default ${JSON.stringify(resolvedConfig.test.environmentOptions || {})}`;
278
+ }
279
+ }
280
+ });
281
+ if (resolvedConfig.test.browser?.enabled) {
282
+ if (resolvedConfig.test.environment === "nuxt") {
283
+ resolvedConfig.test.setupFiles = Array.isArray(resolvedConfig.test.setupFiles) ? resolvedConfig.test.setupFiles : [resolvedConfig.test.setupFiles].filter(Boolean);
284
+ const resolver = createResolver(import.meta.url);
285
+ const browserEntry = await findPath(resolver.resolve("./runtime/browser-entry")) || resolver.resolve("./runtime/browser-entry");
286
+ resolvedConfig.test.setupFiles.unshift(browserEntry);
287
+ }
288
+ }
289
+ return resolvedConfig;
290
+ }
212
291
 
213
- export { defineVitestConfig, getVitestConfigFromNuxt };
292
+ export { defineVitestConfig, defineVitestProject, getVitestConfigFromNuxt };
package/dist/module.mjs CHANGED
@@ -12,6 +12,7 @@ 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';
15
16
  import 'c12';
16
17
  import 'destr';
17
18
  import 'scule';
@@ -364,6 +365,11 @@ const module = defineNuxtModule({
364
365
  viteConfig.plugins = (viteConfig.plugins || []).filter((p) => {
365
366
  return !p || !("name" in p) || !vitePluginBlocklist.includes(p.name);
366
367
  });
368
+ viteConfig.test.environmentMatchGlobs ||= [];
369
+ viteConfig.test.environmentMatchGlobs.push(
370
+ ["**/*.nuxt.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}", "nuxt"],
371
+ ["{test,tests}/nuxt/**.*", "nuxt"]
372
+ );
367
373
  process.env.__NUXT_VITEST_RESOLVED__ = "true";
368
374
  const { startVitest } = await import(pathToFileURL(await resolvePath("vitest/node")).href);
369
375
  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
+ }
@@ -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
- const { useRouter } = await import("#app/composables/router");
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,5 @@
1
+ export declare const functions: never[];
2
+ export declare function createRpcServer(): void;
3
+ export declare const devtools: {
4
+ init(): void;
5
+ };
@@ -0,0 +1,7 @@
1
+ export const functions = [];
2
+ export function createRpcServer() {
3
+ }
4
+ export const devtools = {
5
+ init() {
6
+ }
7
+ };
@@ -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 h3App = createApp();
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
- console.info = consoleInfo;
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.18.0",
3
+ "version": "3.19.1",
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.1",
67
- "@nuxt/schema": "^3.17.1",
68
- "c12": "^3.0.3",
66
+ "@nuxt/kit": "^3.17.4",
67
+ "@nuxt/schema": "^3.17.4",
68
+ "c12": "^3.0.4",
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.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,39 +85,39 @@
85
85
  "std-env": "^3.9.0",
86
86
  "tinyexec": "^1.0.1",
87
87
  "ufo": "^1.6.1",
88
- "unplugin": "^2.3.2",
89
- "vite": "^6.3.4",
88
+ "unplugin": "^2.3.4",
89
+ "vite": "^6.3.5",
90
90
  "vitest-environment-nuxt": "^1.0.1",
91
- "vue": "^3.5.13"
91
+ "vue": "^3.5.14"
92
92
  },
93
93
  "devDependencies": {
94
- "@cucumber/cucumber": "11.2.0",
94
+ "@cucumber/cucumber": "11.3.0",
95
95
  "@jest/globals": "29.7.0",
96
- "@nuxt/devtools-kit": "2.4.0",
97
- "@nuxt/eslint-config": "1.3.0",
96
+ "@nuxt/devtools-kit": "2.4.1",
97
+ "@nuxt/eslint-config": "1.4.0",
98
98
  "@playwright/test": "1.52.0",
99
99
  "@testing-library/vue": "8.1.0",
100
- "@types/bun": "1.2.11",
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.3",
103
+ "@types/node": "22.15.19",
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
- "eslint": "9.26.0",
108
+ "eslint": "9.27.0",
109
109
  "installed-check": "9.3.0",
110
- "knip": "5.53.0",
111
- "nitropack": "2.11.11",
112
- "nuxt": "3.17.1",
113
- "pkg-pr-new": "0.0.43",
110
+ "knip": "5.56.0",
111
+ "nitropack": "2.11.12",
112
+ "nuxt": "3.17.4",
113
+ "pkg-pr-new": "0.0.50",
114
114
  "playwright-core": "1.52.0",
115
- "rollup": "4.40.1",
116
- "semver": "7.7.1",
115
+ "rollup": "4.41.0",
116
+ "semver": "7.7.2",
117
117
  "typescript": "5.8.3",
118
118
  "unbuild": "latest",
119
- "unimport": "5.0.0",
120
- "vitest": "3.1.2",
119
+ "unimport": "5.0.1",
120
+ "vitest": "3.1.4",
121
121
  "vue-router": "4.5.1",
122
122
  "vue-tsc": "2.2.10"
123
123
  },
@@ -165,21 +165,20 @@
165
165
  }
166
166
  },
167
167
  "resolutions": {
168
- "@cucumber/cucumber": "11.2.0",
169
- "@nuxt/devtools": "1.0.8",
170
- "@nuxt/kit": "^3.17.1",
171
- "@nuxt/schema": "^3.17.1",
168
+ "@cucumber/cucumber": "11.3.0",
169
+ "@nuxt/kit": "^3.17.4",
170
+ "@nuxt/schema": "^3.17.4",
172
171
  "@nuxt/test-utils": "workspace:*",
173
- "rollup": "4.40.1",
174
- "vite": "^6.3.4",
175
- "vite-node": "3.1.2",
176
- "vitest": "3.1.2",
177
- "vue": "^3.5.13"
172
+ "rollup": "4.41.0",
173
+ "vite": "^6.3.5",
174
+ "vite-node": "3.1.4",
175
+ "vitest": "3.1.4",
176
+ "vue": "^3.5.14"
178
177
  },
179
178
  "engines": {
180
179
  "node": "^18.20.5 || ^20.9.0 || ^22.0.0 || >=23.0.0"
181
180
  },
182
- "packageManager": "pnpm@10.10.0",
181
+ "packageManager": "pnpm@10.11.0",
183
182
  "pnpm": {
184
183
  "onlyBuiltDependencies": [
185
184
  "better-sqlite3"