@nuxt/test-utils
Version:
Test utilities for Nuxt
201 lines (193 loc) • 6.26 kB
JavaScript
import { u as useTestContext, d as url, c as createTestContext, a as startServer, b as stopServer, s as setTestContext } from './test-utils.CHPAu7TP.mjs';
import { existsSync, promises } from 'node:fs';
import { resolve } from 'node:path';
import { defu } from 'defu';
import * as _kit from '@nuxt/kit';
async function createBrowser() {
const ctx = useTestContext();
let playwright;
try {
playwright = await import(
/* vite-ignore */
'playwright-core'
);
} catch {
throw new Error(`
The dependency 'playwright-core' not found.
Please run 'yarn add --dev playwright-core' or 'npm install --save-dev playwright-core'
`);
}
const { type, launch } = ctx.options.browserOptions;
if (!playwright[type]) {
throw new Error(`Invalid browser '${type}'`);
}
ctx.browser = await playwright[type].launch(launch);
}
async function getBrowser() {
const ctx = useTestContext();
if (!ctx.browser) {
await createBrowser();
}
return ctx.browser;
}
async function createPage(path, options) {
const browser = await getBrowser();
const page = await browser.newPage(options);
const _goto = page.goto.bind(page);
page.goto = async (url2, options2) => {
const waitUntil = options2?.waitUntil;
if (waitUntil && ["hydration", "route"].includes(waitUntil)) {
delete options2.waitUntil;
}
const res = await _goto(url2, options2);
await waitForHydration(page, url2, waitUntil);
return res;
};
if (path) {
await page.goto(url(path), options?.javaScriptEnabled === false ? {} : { waitUntil: "hydration" });
}
return page;
}
async function waitForHydration(page, url2, waitUntil) {
if (waitUntil === "hydration") {
await page.waitForFunction(() => window.useNuxtApp?.().isHydrating === false);
} else if (waitUntil === "route") {
await page.waitForFunction((route) => window.useNuxtApp?.()._route.fullPath === route, url2);
}
}
const kit = _kit.default || _kit;
const isNuxtApp = (dir) => {
return existsSync(dir) && (existsSync(resolve(dir, "pages")) || existsSync(resolve(dir, "nuxt.config.js")) || existsSync(resolve(dir, "nuxt.config.mjs")) || existsSync(resolve(dir, "nuxt.config.cjs")) || existsSync(resolve(dir, "nuxt.config.ts")));
};
const resolveRootDir = () => {
const { options } = useTestContext();
const dirs = [
options.rootDir,
resolve(options.testDir, options.fixture),
process.cwd()
];
for (const dir of dirs) {
if (dir && isNuxtApp(dir)) {
return dir;
}
}
throw new Error("Invalid nuxt app. (Please explicitly set `options.rootDir` pointing to a valid nuxt app)");
};
async function loadFixture() {
const ctx = useTestContext();
ctx.options.rootDir = resolveRootDir();
if (!ctx.options.dev) {
const randomId = Math.random().toString(36).slice(2, 8);
const buildDir = ctx.options.buildDir || resolve(ctx.options.rootDir, ".nuxt", "test", randomId);
ctx.options.nuxtConfig = defu(ctx.options.nuxtConfig, {
buildDir,
nitro: {
output: {
dir: resolve(buildDir, "output")
}
}
});
}
if (ctx.options.build) {
ctx.nuxt = await kit.loadNuxt({
cwd: ctx.options.rootDir,
dev: ctx.options.dev,
overrides: ctx.options.nuxtConfig,
configFile: ctx.options.configFile
});
const buildDir = ctx.nuxt.options.buildDir;
if (!existsSync(buildDir)) {
await promises.mkdir(buildDir, { recursive: true });
ctx.teardown = ctx.teardown || [];
ctx.teardown.push(() => promises.rm(buildDir, { recursive: true, force: true }));
}
}
}
async function buildFixture() {
const ctx = useTestContext();
const prevLevel = kit.logger.level;
kit.logger.level = ctx.options.logLevel;
await kit.buildNuxt(ctx.nuxt);
kit.logger.level = prevLevel;
}
async function setupCucumber(hooks) {
const { After, AfterAll, Before, BeforeAll } = await import('@cucumber/cucumber');
BeforeAll({ timeout: hooks.ctx.options.setupTimeout }, hooks.setup);
Before(hooks.beforeEach);
After(hooks.afterEach);
AfterAll(hooks.afterAll);
}
async function setupJest(hooks) {
const { jest, test, beforeEach, afterAll, afterEach } = await import('@jest/globals');
hooks.ctx.mockFn = jest.fn;
test("setup", hooks.setup, hooks.ctx.options.setupTimeout);
beforeEach(hooks.beforeEach);
afterEach(hooks.afterEach);
afterAll(hooks.afterAll, hooks.ctx.options.teardownTimeout);
}
async function setupVitest(hooks) {
const vitest = await import('vitest');
hooks.ctx.mockFn = vitest.vi.fn;
vitest.beforeAll(hooks.setup, hooks.ctx.options.setupTimeout);
vitest.beforeEach(hooks.beforeEach);
vitest.afterEach(hooks.afterEach);
vitest.afterAll(hooks.afterAll, hooks.ctx.options.teardownTimeout);
}
const setupMaps = {
cucumber: setupCucumber,
jest: setupJest,
vitest: setupVitest
};
function createTest(options) {
const ctx = createTestContext(options);
const beforeEach = () => {
setTestContext(ctx);
};
const afterEach = () => {
setTestContext(void 0);
};
const afterAll = async () => {
if (ctx.serverProcess) {
setTestContext(ctx);
await stopServer();
setTestContext(void 0);
}
if (ctx.nuxt && ctx.nuxt.options.dev) {
await ctx.nuxt.close();
}
if (ctx.browser) {
await ctx.browser.close();
}
await Promise.all(!ctx.teardown ? [] : ctx.teardown.map((fn) => fn()));
};
const setup2 = async () => {
if (ctx.options.fixture) {
await loadFixture();
}
if (ctx.options.build) {
await buildFixture();
}
if (ctx.options.server) {
await startServer(ctx.options.env);
}
if (ctx.options.waitFor) {
await new Promise((resolve) => setTimeout(resolve, ctx.options.waitFor));
}
if (ctx.options.browser) {
await createBrowser();
}
};
return {
beforeEach,
afterEach,
afterAll,
setup: setup2,
ctx
};
}
async function setup(options = {}) {
const hooks = createTest(options);
const setupFn = setupMaps[hooks.ctx.options.runner];
await setupFn(hooks);
}
export { createPage as a, buildFixture as b, createBrowser as c, createTest as d, setup as e, getBrowser as g, loadFixture as l, setupMaps as s, waitForHydration as w };