@nuxt/test-utils
Version:
Test utilities for Nuxt
306 lines (302 loc) • 10.2 kB
JavaScript
import process from 'node:process';
import { defineConfig } from 'vite';
import { setupDotenv } from 'c12';
import { defu } from 'defu';
import { createResolver, findPath } from '@nuxt/kit';
import { resolveModulePath } from 'exsolve';
import { getPackageInfoSync } from 'local-pkg';
import { a as applyEnv, l as loadKit } from './shared/test-utils.BIY9XRkB.mjs';
import 'destr';
import 'scule';
import 'node:url';
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 || {})}`;
}
}
};
}
async function startNuxtAndGetViteConfig(rootDir = process.cwd(), options = {}) {
const { buildNuxt, loadNuxt } = await loadKit(rootDir);
const nuxt = await loadNuxt({
cwd: rootDir,
dev: false,
dotenv: defu(options.dotenv, {
cwd: rootDir,
fileName: ".env.test"
}),
defaults: {
// suppress compatibility date warning for runtime environment tests
compatibilityDate: "2024-04-03"
},
overrides: defu(
{
appId: "nuxt-app",
buildId: "test",
ssr: false,
test: true,
modules: ["@nuxt/test-utils/module"]
},
options.overrides
)
});
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 promise = new Promise((resolve, reject) => {
nuxt.hook("vite:configResolved", (viteConfig, { isClient }) => {
if (isClient) {
resolve({ nuxt, viteConfig });
throw new Error("_stop_");
}
});
buildNuxt(nuxt).catch((err) => {
if (!err.toString().includes("_stop_")) {
reject(err);
}
});
}).finally(() => nuxt.close());
return promise;
}
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,
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 h3Info = getPackageInfoSync("h3", {
paths: nitroPath ? [nitroPath] : options.nuxt.options.modulesDir
});
const resolver = createResolver(import.meta.url);
const resolvedConfig = defu(
// overrides
{
define: {
"process.env.NODE_ENV": '"test"'
},
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(structuredClone(options.nuxt.options.runtimeConfig), {
prefix: "NUXT_",
env: await setupDotenv(defu(loadNuxtOptions.dotenv, {
cwd: rootDir,
fileName: ".env.test"
}))
}),
nuxtRouteRules: defu(
{},
options.nuxt.options.routeRules,
options.nuxt.options.nitro?.routeRules
)
},
server: {
deps: {
inline: [
// vite-node defaults
/\/node_modules\/(.*\/)?(nuxt|nuxt3|nuxt-nightly)\//,
/^#/,
// additional deps
"@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: {
web: {
enabled: false
}
}
}
}
},
{
server: { middlewareMode: false },
plugins: [
{
// TODO: prefix with 'nuxt:test-utils:' in next major version
name: "disable-auto-execute",
enforce: "pre",
transform(code, id) {
if (id.match(/nuxt(3|-nightly)?\/.*\/entry\./)) {
return code.replace(
/(? {
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 key = "projects" in resolvedConfig.test ? "projects" : "workspace" in resolvedConfig.test ? "workspace" : await import('vitest/package.json', { with: { type: 'json' } }).then((r) => {
const [major, minor] = (r.default || r).version.split(".");
return Number.parseInt(major, 10) > 3 || Number.parseInt(major, 10) === 3 && Number.parseInt(minor, 10) >= 2;
}) ? "projects" : "workspace";
resolvedConfig.test[key] = [];
resolvedConfig.test[key].push({
extends: true,
test: {
name: "nuxt",
environment: "nuxt",
include: [
"**/*.nuxt.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}",
"{test,tests}/nuxt/**.*"
]
}
});
resolvedConfig.test[key].push({
extends: true,
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.*",
"./**/*.nuxt.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}",
"./{test,tests}/nuxt/**.*"
]
}
});
}
return resolvedConfig;
});
}
async function resolveConfig(config) {
const overrides = config.test?.environmentOptions?.nuxt?.overrides || {};
overrides.rootDir = config.test?.environmentOptions?.nuxt?.rootDir;
if (config.test?.setupFiles && !Array.isArray(config.test.setupFiles)) {
config.test.setupFiles = [config.test.setupFiles].filter(Boolean);
}
const resolvedConfig = defu(
config,
await getVitestConfigFromNuxt(void 0, {
dotenv: config.test?.environmentOptions?.nuxt?.dotenv,
overrides: structuredClone(overrides)
})
);
resolvedConfig.plugins.push(NuxtVitestEnvironmentOptionsPlugin(resolvedConfig.test.environmentOptions));
if (resolvedConfig.test.browser?.enabled) {
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;
}
export { defineVitestConfig, defineVitestProject, getVitestConfigFromNuxt };