@nuxt/test-utils
Version:
Test utilities for Nuxt
295 lines (294 loc) • 11.5 kB
JavaScript
import { n as deepCopy, r as loadKit, t as applyEnv } from "./utils-whMnUpQ0.mjs";
import { createDefu, defu as defu$1 } from "defu";
import { resolveModulePath } from "exsolve";
import process from "node:process";
import { version } from "vitest/node";
import { setupDotenv } from "c12";
import { createResolver, findPath } from "@nuxt/kit";
import { getPackageInfoSync } from "local-pkg";
//#region src/module/plugins/options.ts
const PLUGIN_NAME = "nuxt:vitest:nuxt-environment-options";
const STUB_ID = "nuxt-vitest-environment-options";
function NuxtVitestEnvironmentOptionsPlugin(environmentOptions = {}) {
return {
name: PLUGIN_NAME,
enforce: "pre",
resolveId(id) {
if (id.endsWith(STUB_ID)) return STUB_ID;
},
load(id) {
if (id.endsWith(STUB_ID)) return `export default ${JSON.stringify(environmentOptions || {})}`;
}
};
}
//#endregion
//#region src/config.ts
async function startNuxtAndGetViteConfig(rootDir = process.cwd(), options = {}) {
const { buildNuxt, loadNuxt } = await loadKit(rootDir);
const nuxt = await loadNuxt({
cwd: rootDir,
dev: options.nitroEnvironment || (options.overrides?.dev ?? false),
dotenv: defu$1(options.dotenv, {
cwd: rootDir,
fileName: ".env.test"
}),
defaults: { compatibilityDate: "2024-04-03" },
overrides: defu$1({
appId: "nuxt-app",
buildId: "test",
ssr: false,
test: true,
modules: ["@nuxt/test-utils/module"]
}, options.overrides, options.nitroEnvironment ? {} : DISABLE_NITRO_ENVIRONMENT)
});
if (!nuxt.options._installedModules.find((i) => i?.meta?.name === "@nuxt/test-utils")) throw new Error("Failed to load `@nuxt/test-utils/module`. You may need to add it to your nuxt.config.");
const nitro = options.nitroEnvironment ? {} : void 0;
if (nitro) nuxt.hook("nitro:init", (instance) => {
nitro.current = instance;
});
return new Promise((resolve, reject) => {
nuxt.hook("vite:configResolved", (viteConfig, { isClient }) => {
if (isClient) {
resolve({
nuxt,
viteConfig,
nitro
});
throw new Error("_stop_");
}
});
buildNuxt(nuxt).catch((err) => {
if (!err.toString().includes("_stop_")) reject(err);
});
}).finally(() => nuxt.close());
}
const DISABLE_NITRO_ENVIRONMENT = { experimental: { nitroViteEnvironment: false } };
function nitroTeardownPlugin(nitro) {
let closed;
return {
name: "nuxt:test-utils:nitro-teardown",
async closeBundle() {
closed ||= nitro.current?.hooks.callHook("close") ?? Promise.resolve();
await closed;
}
};
}
const excludedPlugins = [
"nuxt:import-protection",
"nuxt:import-conditions",
"nuxt:devtools:rpc",
"nuxt:devtools:config-retriever",
"vite-plugin-checker",
"vite-plugin-inspect",
"vite-plugin-vue-tracer"
];
async function getVitestConfigFromNuxt(options, loadNuxtOptions = {}) {
const { rootDir = process.cwd(), ..._overrides } = loadNuxtOptions.overrides || {};
if (!options) options = await startNuxtAndGetViteConfig(rootDir, {
dotenv: loadNuxtOptions.dotenv,
nitroEnvironment: loadNuxtOptions.nitroEnvironment,
overrides: {
test: true,
..._overrides
}
});
delete options.viteConfig.root;
options.viteConfig.plugins = (options.viteConfig.plugins || []).filter((p) => !p || !("name" in p) || !excludedPlugins.includes(p.name));
const nuxtServerIntegration = getPackageInfoSync("@nuxt/nitro-server", { paths: [options.nuxt.options.appDir] });
let nitroPath;
for (const nitroCandidate of [...nuxtServerIntegration?.packageJson.dependencies?.nitro ? ["nitro", "nitro-nightly"] : ["nitropack", "nitropack-nightly"]]) {
nitroPath = resolveModulePath(nitroCandidate, {
from: nuxtServerIntegration?.rootPath || options.nuxt.options.appDir,
try: true
});
if (nitroPath) break;
}
const projectH3Path = resolveModulePath("h3/package.json", {
from: rootDir,
try: true
});
const h3Info = (projectH3Path ? getPackageInfoSync("h3", { paths: [projectH3Path] }) : void 0) || getPackageInfoSync("h3", { paths: nitroPath ? [nitroPath] : options.nuxt.options.modulesDir });
const resolver = createResolver(import.meta.url);
const resolvedConfig = defu$1({
define: {
"process.env.NODE_ENV": "\"test\"",
"__NUXT_VITEST_RESOLVED__": "true"
},
resolve: { alias: {
"@vue/devtools-kit": resolver.resolve("./runtime/mocks/vue-devtools"),
"@vue/devtools-core": resolver.resolve("./runtime/mocks/vue-devtools")
} },
optimizeDeps: { noDiscovery: true },
test: {
environmentOptions: {
nuxtRuntimeConfig: applyEnv(deepCopy(options.nuxt.options.runtimeConfig), {
prefix: "NUXT_",
env: await setupDotenv(defu$1(loadNuxtOptions.dotenv, {
cwd: rootDir,
fileName: ".env.test"
}))
}),
nuxtRouteRules: defu$1({}, options.nuxt.options.routeRules, options.nuxt.options.nitro?.routeRules),
nuxtAppConfig: {
rootAttrs: options.nuxt.options.app.rootAttrs,
rootTag: options.nuxt.options.app.rootTag,
teleportAttrs: options.nuxt.options.app.teleportAttrs,
teleportTag: options.nuxt.options.app.teleportTag
}
},
server: { deps: { inline: [
/\/node_modules\/(.*\/)?(nuxt|nuxt3|nuxt-nightly)\//,
/^#/,
"@nuxt/test-utils",
"@nuxt/test-utils-nightly",
"@nuxt/test-utils-edge",
"vitest-environment-nuxt",
...options.nuxt.options.build.transpile.filter((r) => typeof r === "string" || r instanceof RegExp)
] } },
deps: { optimizer: { client: { enabled: false } } }
}
}, {
server: { middlewareMode: false },
plugins: [
{
name: "disable-auto-execute",
enforce: "pre",
transform(code, id) {
if (id.match(/nuxt(3|-nightly)?\/.*\/entry\./)) return code.replace(/(?<!vueAppPromise = )entry\(\)/, "Promise.resolve()");
}
},
...options.nitro ? [nitroTeardownPlugin(options.nitro)] : [],
{
name: "nuxt:test-utils:browser-conditions",
enforce: "pre",
config() {
return { resolve: { conditions: [
"web",
"import",
"module",
"default"
] } };
},
configResolved(config) {
if (config.ssr.resolve?.conditions) config.ssr.resolve.conditions = config.ssr.resolve.conditions.filter((x) => x !== "import");
}
}
]
}, options.viteConfig, { test: { environmentOptions: { nuxt: {
rootId: options.nuxt.options.app.rootAttrs?.id || void 0,
h3Version: h3Info?.version?.startsWith("2.") ? 2 : 1,
mock: {
intersectionObserver: true,
indexedDb: false
}
} } } });
delete resolvedConfig.define["process.browser"];
delete resolvedConfig.customLogger;
delete resolvedConfig.ssr;
if (!Array.isArray(resolvedConfig.test.setupFiles)) resolvedConfig.test.setupFiles = [resolvedConfig.test.setupFiles].filter(Boolean);
const entryPath = resolver.resolve("./runtime/entry");
resolvedConfig.test.setupFiles.unshift(await findPath(entryPath) ?? entryPath);
return resolvedConfig;
}
const vitestMajor = Number(version.split(".")[0]);
const optOutOfExtends = false;
async function defineVitestProject(config) {
const resolvedConfig = await resolveConfig(defu$1({ test: { environment: "nuxt" } }, config));
resolvedConfig.extends ??= optOutOfExtends;
return resolvedConfig;
}
const defineViteConfig = (config) => config;
function defineVitestConfig(config = {}) {
return defineViteConfig(async () => {
const resolvedConfig = await resolveConfig(config);
if (resolvedConfig.test.browser?.enabled) return resolvedConfig;
if ("workspace" in resolvedConfig.test || "projects" in resolvedConfig.test) throw new Error("The `projects` option is not supported with `defineVitestConfig`. Instead, use `defineVitestProject` to define each workspace project that uses the Nuxt environment.");
const defaultEnvironment = resolvedConfig.test.environment || "node";
if (defaultEnvironment !== "nuxt") {
const merge = createDefu((obj, key, value) => {
if (Array.isArray(value) && Array.isArray(obj[key])) {
obj[key] = [.../* @__PURE__ */ new Set([...value, ...obj[key]])];
return true;
}
});
const nuxtProject = merge({
...resolvedConfig,
extends: optOutOfExtends,
test: {
...resolvedConfig.test,
name: "nuxt",
environment: "nuxt",
include: []
}
}, resolvedConfig);
nuxtProject.test.include = ["**/*.nuxt.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}", "{test,tests}/nuxt/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"];
const defaultProject = merge({
...resolvedConfig,
extends: optOutOfExtends,
test: {
...resolvedConfig.test,
name: defaultEnvironment,
environment: defaultEnvironment,
exclude: [
"**/node_modules/**",
"**/dist/**",
"**/cypress/**",
"**/.{idea,git,cache,output,temp}/**",
"**/{karma,rollup,webpack,vite,vitest,jest,ava,babel,nyc,cypress,tsup,build,eslint,prettier}.config.*",
...nuxtProject.test.include
]
}
}, resolvedConfig);
delete resolvedConfig.test.name;
delete resolvedConfig.test.environment;
delete resolvedConfig.test.include;
delete resolvedConfig.test.exclude;
delete resolvedConfig.test.includeSource;
delete defaultProject.test.includeSource;
resolvedConfig.test.projects = [nuxtProject, defaultProject];
}
return resolvedConfig;
});
}
function isCoverageEnabled(config) {
if (config.test && "coverage" in config.test && config.test.coverage?.enabled) return true;
return process.argv.some((arg) => arg === "--coverage" || arg === "--coverage.enabled" || arg === "--coverage=true" || arg === "--coverage.enabled=true");
}
async function resolveConfig(config) {
const overrides = config.test?.environmentOptions?.nuxt?.overrides || {};
overrides.rootDir = config.test?.environmentOptions?.nuxt?.rootDir;
if (isCoverageEnabled(config)) {
if (overrides.sourcemap === void 0) overrides.sourcemap = { client: true };
else if (typeof overrides.sourcemap === "object" && overrides.sourcemap.client === void 0) overrides.sourcemap.client = true;
}
if (config.test?.setupFiles && !Array.isArray(config.test.setupFiles)) config.test.setupFiles = [config.test.setupFiles].filter(Boolean);
const resolvedConfig = defu$1(config, await getVitestConfigFromNuxt(void 0, {
dotenv: config.test?.environmentOptions?.nuxt?.dotenv,
nitroEnvironment: config.test?.environmentOptions?.nuxt?.nitroEnvironment,
overrides: deepCopy(overrides)
}));
resolvedConfig.plugins.push(NuxtVitestEnvironmentOptionsPlugin(resolvedConfig.test.environmentOptions));
if (resolvedConfig.test.browser?.enabled) {
if (vitestMajor >= 5) {
delete resolvedConfig.optimizeDeps?.noDiscovery;
resolvedConfig.optimizeDeps ??= {};
resolvedConfig.optimizeDeps.include ??= [];
resolvedConfig.optimizeDeps.include.push("@testing-library/vue", "h3-next/generic");
}
resolvedConfig.plugins.push({
name: "nuxt:test-utils:browser-client-environment",
configEnvironment(name) {
if (name === "client") return { dev: { moduleRunnerTransform: false } };
}
});
if (resolvedConfig.test.environment === "nuxt") {
resolvedConfig.test.setupFiles = Array.isArray(resolvedConfig.test.setupFiles) ? resolvedConfig.test.setupFiles : [resolvedConfig.test.setupFiles].filter(Boolean);
const resolver = createResolver(import.meta.url);
const browserEntry = await findPath(resolver.resolve("./runtime/browser-entry")) || resolver.resolve("./runtime/browser-entry");
resolvedConfig.test.setupFiles.unshift(browserEntry);
}
}
return resolvedConfig;
}
//#endregion
export { defineVitestConfig, defineVitestProject, getVitestConfigFromNuxt };