@nuxt/test-utils
Version:
Test utilities for Nuxt
199 lines (194 loc) • 6.29 kB
JavaScript
import { createFetch } from 'ofetch';
import { indexedDB } from 'fake-indexeddb';
import { joinURL } from 'ufo';
import { createApp, toNodeListener, defineEventHandler, splitCookiesString } from 'h3';
import defu from 'defu';
import { toRouteMatcher, createRouter, exportMatcher } from 'radix3';
import { populateGlobal } from 'vitest/environments';
import { fetchNodeRequestHandler } from 'node-mock-http';
import { importModule } from 'local-pkg';
const happyDom = (async function(_, { happyDom = {} }) {
const { Window, GlobalWindow } = await importModule("happy-dom");
const window = new (GlobalWindow || Window)(happyDom);
return {
window,
teardown() {
window.happyDOM.abort();
}
};
});
const jsdom = (async function(global, { jsdom = {} }) {
const { CookieJar, JSDOM, ResourceLoader, VirtualConsole } = await importModule("jsdom");
const jsdomOptions = defu(jsdom, {
html: "<!DOCTYPE html>",
url: "http://localhost:3000",
contentType: "text/html",
pretendToBeVisual: true,
includeNodeLocations: false,
runScripts: "dangerously",
console: false,
cookieJar: false
});
const window = new JSDOM(jsdomOptions.html, {
...jsdomOptions,
resources: jsdomOptions.resources ?? (jsdomOptions.userAgent ? new ResourceLoader({ userAgent: jsdomOptions.userAgent }) : void 0),
virtualConsole: jsdomOptions.console && global.console ? new VirtualConsole().sendTo(global.console) : void 0,
cookieJar: jsdomOptions.cookieJar ? new CookieJar() : void 0
}).window;
window.scrollTo = () => {
};
return {
window,
teardown() {
}
};
});
const environmentMap = {
"happy-dom": happyDom,
jsdom
};
const index = {
name: "nuxt",
transformMode: "web",
async setup(global, environmentOptions) {
const url = joinURL(
environmentOptions?.nuxt.url ?? "http://localhost:3000",
environmentOptions?.nuxtRuntimeConfig.app?.baseURL || "/"
);
const consoleInfo = console.info;
console.info = (...args) => {
if (args[0] === "<Suspense> is an experimental feature and its API will likely change.") {
return;
}
return consoleInfo(...args);
};
const environmentName = environmentOptions.nuxt.domEnvironment;
const environment = environmentMap[environmentName] || environmentMap["happy-dom"];
const { window: win, teardown } = await environment(global, defu(environmentOptions, {
happyDom: { url },
jsdom: { url }
}));
win.__NUXT_VITEST_ENVIRONMENT__ = true;
win.__NUXT__ = {
serverRendered: false,
config: {
public: {},
app: { baseURL: "/" },
...environmentOptions?.nuxtRuntimeConfig
},
data: {},
state: {}
};
const app = win.document.createElement("div");
app.id = environmentOptions.nuxt.rootId;
win.document.body.appendChild(app);
if (environmentOptions?.nuxt?.mock?.intersectionObserver) {
win.IntersectionObserver = win.IntersectionObserver || class IntersectionObserver {
observe() {
}
unobserve() {
}
disconnect() {
}
};
}
if (environmentOptions?.nuxt?.mock?.indexedDb) {
win.indexedDB = indexedDB;
}
const h3App = createApp();
if (!win.fetch) {
await import('node-fetch-native/polyfill');
win.URLSearchParams = globalThis.URLSearchParams;
}
const nodeHandler = toNodeListener(h3App);
const registry = /* @__PURE__ */ new Set();
win.fetch = async (url2, init) => {
if (typeof url2 === "string") {
const base = url2.split("?")[0];
if (registry.has(base) || registry.has(url2)) {
url2 = "/_" + url2;
}
if (url2.startsWith("/")) {
const response = await fetchNodeRequestHandler(nodeHandler, url2, init);
return normalizeFetchResponse(response);
}
}
return fetch(url2, init);
};
win.$fetch = createFetch({ fetch: win.fetch, Headers: win.Headers });
win.__registry = registry;
win.__app = h3App;
const { keys, originals } = populateGlobal(global, win, {
bindFunctions: true
});
const timestamp = Date.now();
const routeRulesMatcher = toRouteMatcher(
createRouter({ routes: environmentOptions.nuxtRouteRules || {} })
);
const matcher = exportMatcher(routeRulesMatcher);
const manifestOutputPath = joinURL(
"/",
environmentOptions?.nuxtRuntimeConfig.app?.buildAssetsDir || "_nuxt",
"builds"
);
const manifestBaseRoutePath = joinURL("/_", manifestOutputPath);
const buildId = win.__NUXT__.config.app.buildId || "test";
h3App.use(
`${manifestBaseRoutePath}/latest.json`,
defineEventHandler(() => ({
id: buildId,
timestamp
}))
);
h3App.use(
`${manifestBaseRoutePath}/meta/${buildId}.json`,
defineEventHandler(() => ({
id: buildId,
timestamp,
matcher,
prerendered: []
}))
);
registry.add(`${manifestOutputPath}/latest.json`);
registry.add(`${manifestOutputPath}/meta/${buildId}.json`);
return {
// called after all tests with this env have been run
teardown() {
keys.forEach((key) => delete global[key]);
console.info = consoleInfo;
originals.forEach((v, k) => global[k] = v);
teardown();
}
};
}
};
function normalizeFetchResponse(response) {
if (!response.headers.has("set-cookie")) {
return response;
}
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: normalizeCookieHeaders(response.headers)
});
}
function normalizeCookieHeader(header = "") {
return splitCookiesString(joinHeaders(header));
}
function normalizeCookieHeaders(headers) {
const outgoingHeaders = new Headers();
for (const [name, header] of headers) {
if (name === "set-cookie") {
for (const cookie of normalizeCookieHeader(header)) {
outgoingHeaders.append("set-cookie", cookie);
}
} else {
outgoingHeaders.set(name, joinHeaders(header));
}
}
return outgoingHeaders;
}
function joinHeaders(value) {
return Array.isArray(value) ? value.join(", ") : String(value);
}
export { index as default };