UNPKG

@scalvert/bin-tester

Version:

A test harness to invoke a CLI in a tmp directory

148 lines (145 loc) 4.24 kB
// src/create-bin-tester.ts import { execaNode } from "execa"; // src/project.ts import { execa } from "execa"; import { Project } from "fixturify-project"; var ROOT = process.cwd(); var BinTesterProject = class extends Project { /** * Constructs an instance of a BinTesterProject. * @param {string} name - The name of the project. Used within the package.json as the name property. * @param {string} version - The version of the project. Used within the package.json as the version property. * @param {Function} cb - An optional callback for additional setup steps after the project is constructed. */ constructor(name = "fake-project", version, cb) { super(name, version, cb); this._dirChanged = false; this.pkg = Object.assign({}, this.pkg, { license: "MIT", description: "Fake project", repository: "http://fakerepo.com" }); } /** * Runs `git init` inside a project. * @returns {*} {ResultPromise} */ gitInit() { return execa("git", ["init", "-q", this.baseDir]); } /** * Writes the project files to disk. */ async write() { return super.write(); } /** * Changes a directory from inside the project. */ async chdir() { this._dirChanged = true; await this.write(); process.chdir(this.baseDir); } /** * Correctly disposes of the project, observing when the directory has been changed. * @returns {void} */ dispose() { if (this._dirChanged) { process.chdir(ROOT); } return super.dispose(); } }; // src/create-bin-tester.ts var DEFAULT_BIN_TESTER_OPTIONS = { staticArgs: [] }; function parseArgs(args) { if (args.length > 0 && typeof args[args.length - 1] === "object") { const argsCopy = [...args]; const execaOptions = argsCopy.pop(); return { args: argsCopy, execaOptions }; } else { return { args: [...args], execaOptions: {} }; } } function createBinTester(options) { let project; const mergedOptions = { ...DEFAULT_BIN_TESTER_OPTIONS, ...options }; function runBin(...args) { const mergedRunOptions = parseArgs(args); const binPath = typeof mergedOptions.binPath === "function" ? mergedOptions.binPath(project) : mergedOptions.binPath; const optionsEnv = mergedRunOptions.execaOptions.env; const debugEnv = optionsEnv?.BIN_TESTER_DEBUG ?? process.env.BIN_TESTER_DEBUG; const nodeOptions = []; if (debugEnv && debugEnv !== "0" && debugEnv.toLowerCase() !== "false") { if (debugEnv.toLowerCase() === "break") { nodeOptions.push("--inspect-brk=0"); } else { nodeOptions.push("--inspect=0"); } console.log(`[bin-tester] Debugging enabled. Fixture: ${project.baseDir}`); } const resolvedCwd = mergedRunOptions.execaOptions.cwd ?? project.baseDir; return execaNode(binPath, [...mergedOptions.staticArgs, ...mergedRunOptions.args], { reject: false, cwd: resolvedCwd, nodeOptions, ...mergedRunOptions.execaOptions }); } function runBinDebug(...args) { const parsedArgs = parseArgs(args); const debugEnv = process.env.BIN_TESTER_DEBUG || "attach"; parsedArgs.execaOptions = { ...parsedArgs.execaOptions, env: { ...parsedArgs.execaOptions.env, BIN_TESTER_DEBUG: debugEnv } }; const reconstructedArgs = [...parsedArgs.args, parsedArgs.execaOptions]; return runBin(...reconstructedArgs); } async function setupProject() { project = "createProject" in mergedOptions ? await mergedOptions.createProject() : new BinTesterProject(); await project.write(); return project; } async function setupTmpDir() { if (typeof project === "undefined") { await setupProject(); } return project.baseDir; } function teardownProject() { const debugEnv = process.env.BIN_TESTER_DEBUG; if (debugEnv && debugEnv !== "0" && debugEnv.toLowerCase() !== "false") { console.log(`[bin-tester] Fixture preserved: ${project.baseDir}`); return; } project.dispose(); } return { runBin, runBinDebug, setupProject, teardownProject, setupTmpDir }; } export { BinTesterProject, createBinTester };