UNPKG

mocha

Version:

simple, flexible, fun test framework

151 lines (131 loc) 4.36 kB
#!/usr/bin/env node "use strict"; /** * This wrapper executable checks for known node flags and appends them when found, * before invoking the "real" executable (`lib/cli/cli.js`) * * @module bin/mocha * @private */ const os = require("node:os"); const { loadOptions } = require("../lib/cli/options"); const { unparseNodeFlags, isNodeFlag, impliesNoTimeouts, } = require("../lib/cli/node-flags"); const unparse = require("yargs-unparser"); const debug = require("debug")("mocha:cli:mocha"); const { aliases } = require("../lib/cli/run-option-metadata"); const mochaArgs = {}; const nodeArgs = {}; const SIGNAL_OFFSET = 128; let hasInspect = false; const opts = loadOptions(process.argv.slice(2)); debug("loaded opts", opts); /** * Given option/command `value`, disable timeouts if applicable * @param {string} [value] - Value to check * @ignore */ const disableTimeouts = (value) => { if (impliesNoTimeouts(value)) { debug("option %s disabled timeouts", value); mochaArgs.timeout = 0; } }; /** * If `value` begins with `v8-` and is not explicitly `v8-options`, remove prefix * @param {string} [value] - Value to check * @returns {string} `value` with prefix (maybe) removed * @ignore */ const trimV8Option = (value) => value !== "v8-options" && /^v8-/.test(value) ? value.slice(3) : value; // sort options into "node" and "mocha" buckets Object.keys(opts).forEach((opt) => { if (isNodeFlag(opt)) { nodeArgs[trimV8Option(opt)] = opts[opt]; } else { mochaArgs[opt] = opts[opt]; } }); // disable 'timeout' for debugFlags Object.keys(nodeArgs).forEach((opt) => disableTimeouts(opt)); mochaArgs["node-option"] && mochaArgs["node-option"].forEach((opt) => disableTimeouts(opt)); // Native debugger handling // see https://nodejs.org/api/debugger.html#debugger_debugger // look for 'inspect' that would launch this debugger, // remove it from Mocha's opts and prepend it to Node's opts. // A deprecation warning will be printed by node, if applicable. // (mochaArgs._ are "positional" arguments, not prefixed with - or --) if (mochaArgs._) { const i = mochaArgs._.findIndex((val) => val === "inspect"); if (i > -1) { mochaArgs._.splice(i, 1); disableTimeouts("inspect"); hasInspect = true; } } if (mochaArgs["node-option"] || Object.keys(nodeArgs).length || hasInspect) { const { spawn } = require("node:child_process"); const mochaPath = require.resolve("../lib/cli/cli.js"); const nodeArgv = (mochaArgs["node-option"] && mochaArgs["node-option"].map((v) => "--" + v)) || unparseNodeFlags(nodeArgs); if (hasInspect) nodeArgv.unshift("inspect"); delete mochaArgs["node-option"]; debug("final node argv", nodeArgv); const args = [].concat( nodeArgv, mochaPath, unparse(mochaArgs, { alias: aliases }), ); debug( "forking child process via command: %s %s", process.execPath, args.join(" "), ); const proc = spawn(process.execPath, args, { stdio: "inherit", }); proc.on("exit", (code, signal) => { process.on("exit", () => { if (signal) { signal = typeof signal === "string" ? os.constants.signals[signal] : signal; if (mochaArgs["posix-exit-codes"] === true) { process.exitCode = SIGNAL_OFFSET + signal; } process.kill(process.pid, signal); } else { process.exit(Math.min(code, mochaArgs["posix-exit-codes"] ? 1 : 255)); } }); }); // terminate children. process.on("SIGINT", () => { // XXX: a previous comment said this would abort the runner, but I can't see that it does // anything with the default runner. debug("main process caught SIGINT"); proc.kill("SIGINT"); // if running in parallel mode, we will have a proper SIGINT handler, so the below won't // be needed. if (!args.parallel || args.jobs < 2) { // win32 does not support SIGTERM, so use next best thing. if (os.platform() === "win32") { proc.kill("SIGKILL"); } else { // using SIGKILL won't cleanly close the output streams, which can result // in cut-off text or a befouled terminal. debug("sending SIGTERM to child process"); proc.kill("SIGTERM"); } } }); } else { debug("running Mocha in-process"); require("../lib/cli/cli").main([], mochaArgs); }