UNPKG

@sxzz/test-utils

Version:

Collection of common test utils.

170 lines (163 loc) 5.42 kB
import path from "node:path"; import process from "node:process"; import { glob } from "tinyglobby"; import { normalizePath } from "unplugin-utils"; import { describe, expect, test } from "vitest"; import { readFile } from "node:fs/promises"; //#region src/fixture.ts let isSkip; function testFixturesSkip(fn) { isSkip = fn; } async function testFixtures(globsOrFiles, cb, { params, promise, concurrent,...globOptions } = {}) { let files; if (typeof globsOrFiles === "string" || Array.isArray(globsOrFiles)) files = Object.fromEntries((await glob(globsOrFiles, globOptions)).map((file) => [file, undefined])); else files = globsOrFiles; for (const [id, code] of Object.entries(files)) makeTests(id, code, [[normalizePath(id)], ...params || []])(); function makeTests(id, code, params$1, args = {}) { const [currParams, ...restParams] = params$1; const [name, values = [undefined]] = currParams; if (restParams.length > 0) return () => { for (const value of values) { const currArgs = { ...args, [name]: value }; describe(getName(name, value), makeTests(id, code, restParams, currArgs)); } }; else return () => { for (const value of values) { const testName = getName(name, value); let testFn = test.skipIf(isSkip?.(testName)); if (concurrent) testFn = testFn.concurrent; testFn(testName, async ({ expect: expect$1 }) => { const currArgs = { ...args, [name]: value }; const execute = () => cb(currArgs, path.resolve(globOptions.cwd || process.cwd(), id), code); if (id.includes("error")) if (promise) await expect$1(execute()).rejects.toThrowErrorMatchingSnapshot(); else expect$1(execute).toThrowErrorMatchingSnapshot(); else if (promise) await expect$1(execute().catch((error) => { console.warn(error); return Promise.reject(error); })).resolves.toMatchSnapshot(); else expect$1(execute()).toMatchSnapshot(); }); } }; } } function getName(name, value) { return value !== undefined ? `${name} = ${String(value)}` : name; } //#endregion //#region src/snapshot.ts function outputToSnapshot(chunks) { const cwd = process.cwd(); return chunks.map((file) => { let filename, content; if ("path" in file) { filename = file.path; content = file.text; } else { filename = file.fileName; content = file.type === "chunk" ? file.code : typeof file.source === "string" ? file.source : "[BINARY]"; } return `// ${filename.replaceAll("\\", "/")}\n${content.replaceAll(cwd, "[CWD]")}`; }).sort().join("\n"); } async function expectFilesSnapshot(sourceDir, snapshotFile, { pattern = "**/*", expect: expect$1 = expect } = {}) { const cwd = process.cwd(); const files = (await glob(pattern, { cwd: sourceDir })).sort(); const fileMap = Object.fromEntries(await Promise.all(files.map(async (filename) => [filename.replaceAll("\\", "/"), (await readFile(path.resolve(sourceDir, filename), "utf8")).replaceAll(cwd, "[CWD]")]))); const snapshot = Object.entries(fileMap).map(([filename, contents]) => { const ext = path.extname(filename).slice(1); return `## ${filename} \`\`\`${ext} ${contents} \`\`\``; }).join("\n"); await expect$1(snapshot).toMatchFileSnapshot(snapshotFile); return { files, fileMap, snapshot }; } //#endregion //#region src/rolldown.ts async function rolldownBuild(file, plugins = [], inputOptions = {}, outputOptions = {}) { const { rolldown } = await import("rolldown"); const bundle = await rolldown({ input: [file], treeshake: false, onwarn(warning, defaultHandler) { if (["UNUSED_EXTERNAL_IMPORT", "UNRESOLVED_IMPORT"].includes(warning.code)) return; defaultHandler(warning); }, ...inputOptions, plugins: [plugins, inputOptions.plugins] }); const output = await bundle.generate({ format: "esm", sourcemap: false, ...outputOptions }); const chunks = output.output; return { chunks, snapshot: outputToSnapshot(chunks) }; } //#endregion //#region src/rollup.ts async function rollupBuild(file, plugins = [], inputOptions = {}, outputOptions = {}) { const { rollup } = await import("rollup"); const bundle = await rollup({ input: [file], treeshake: false, onwarn(warning, defaultHandler) { if (["UNUSED_EXTERNAL_IMPORT", "UNRESOLVED_IMPORT"].includes(warning.code)) return; defaultHandler(warning); }, ...inputOptions, plugins: [plugins, inputOptions.plugins] }); const output = await bundle.generate({ format: "esm", sourcemap: false, ...outputOptions }); const chunks = output.output; return { chunks, snapshot: outputToSnapshot(chunks) }; } const RollupToStringPlugin = () => { return { name: "to-string", transform: (code) => `export default \`${code.replaceAll("`", "\\`")}\`` }; }; const RollupEscapeNullCharacterPlugin = () => { return { name: "escape-null-character", generateBundle(options, bundle) { for (const filename of Object.keys(bundle)) { const b = bundle[filename]; if (b.type !== "chunk") continue; if (b.code.includes("\0")) b.code = b.code.replaceAll("\0", "[NULL]"); } } }; }; //#endregion //#region src/string.ts function removeSpaces(s) { return s.trim().replaceAll(/[\n\r]/g, "").replaceAll(/\s+/g, " "); } //#endregion export { RollupEscapeNullCharacterPlugin, RollupToStringPlugin, expectFilesSnapshot, outputToSnapshot, removeSpaces, rolldownBuild, rollupBuild, testFixtures, testFixturesSkip };