first-base
v4.0.1
Published
Integration testing for CLI applications
Downloads
53,939
Readme
first-base
Integration testing for CLI applications.
Usage Example
const { spawn } = require("first-base");
test("something", async () => {
const run = spawn("node", ["-i"]); // launch node REPL
await run.outputContains("> "); // wait until `> ` is logged
run.write("2 + 2\n"); // enter `2 + 2` and press enter
await run.outputContains("4"); // wait until `4` is logged
run.kill(); // Ctrl+C
await run.completion; // Wait until process exits
});API
spawn(command: string, args?: Array<string>, options?: Object) => RunContext
args and options are the same as child_process.spawn.
Returns a RunContext object; see below.
RunContext#result
An object with the following properties on it:
stdout(string): All the data the process has written to STDOUT so farstderr(string): All the data the process has written to STDERR so farcode(number | null): Exit status code, if the process has finishederror(null | Error): If the process errored out, this is the Error
NOTE: If spawned with option
pty: true,RunContext#resulthas a singleoutput: stringproperty instead of bothstdoutandstderr.
This object gets updated over time as the process runs.
Usage
const run = spawn("ls", { cwd: __dirname });
console.log(run.result); // { stdout: '', stderr: '', code: null, error: null }
await run.completion;
console.log(run.result); // { stdout: 'README.md\npackage.json\nindex.js\n', stderr: '', code: 0, error: null }RunContext#completion
A Promise that gets resolved when the process completes. You can await it in your tests.
Usage
const run = spawn("ls", { cwd: __dirname });
await run.completion; // Waits until the `ls` process finishesRunContext#outputContains(value: string | RegExp) => Promise<void>
Returns a Promise that will resolve once the process's output (combined STDOUT/STDERR) contains either the specified string or matches the specified RegExp. Ignores ANSI control characters.
Usage
const run = spawn("node", ["-i"]); // start Node.js REPL
await run.outputContains("> "); // Wait until prompt appearsRunContext#clearOutputContainsBuffer()
Clears the buffer of "seen" output as far as the outputContains method is concerned. Useful if the output already contains the specified value, and you want to wait until it appears a second time.
Usage
const run = spawn("node", ["-i"]); // start Node.js REPL
await run.outputContains("> "); // Wait until prompt appears
run.write("2 + 2\n"); // Write 2 + 2 then press enter
run.clearOutputContainsBuffer();
await run.outputContains("> "); // Wait until prompt appears a second time. If we hadn't cleared the buffer, this would resolve immediately.RunContext#write(data: string | Buffer)
Write some data into the process's STDIN stream.
Usage
const run = spawn("node", ["-i"]); // start Node.js REPL
await run.outputContains("> "); // Wait until prompt appears
run.write("2 + 2\n"); // Write 2 + 2 then press enter
await run.outputContains("4");RunContext#close(stream: 'stdin' | 'stdout' | 'stderr')
Close one of the processes's associated stdio streams.
NOTE: If spawned with option
pty: true,RunContext#closeis not present.
Usage
const run = spawn("node", ["-i"]); // start Node.js REPL
await run.outputContains("> "); // Wait until prompt appears
run.close("stdin"); // Like pressing Ctrl+D; sends EOF
await run.completion;RunContext#kill(signal?: string)
Kills the process. If no signal is specified, it defaults to "SIGINT".
Usage
const run = spawn("node", ["-i"]);
run.kill(); // Kill with SIGINT
// OR:
run.kill("SIGTERM"); // Kill with SIGTERM
run.kill("SIGKILL"); // Kill with SIGKILL
// etcRunContext#cleanResult()
A function which returns a new object with the same shape as RunContext#result but with its stdout/stderr/output passed through the sanitizers Array (see below).
Unlike result, this object does NOT get updated over time as the process runs.
sanitizers: Array<(string) => string>
An array of functions that will be run on stdout/stderr when calling
RunContext#cleanResult().
By default, it contains 5 functions, which are run in order:
stripAnsi: Removes ANSI control charactersreplaceRootDir: Replaces eg/home/suchipi/Code/first-base/src/index.jswith<rootDir>/src/index.js- This function searches upwards for the root dir using a heuristic, and caches results in the {@link Map}
replaceRootDir.cache. - The heuristic is:
- Look upwards for a folder containing
.gitor.hg - if none is found, look upwards for a folder containing
package-lock.json,.gitignoreor.hgignore, - if none is found, look upwards for a folder containing
package.jsonorREADME.md - if none is found, consider the present working directory to be the root dir.
- Look upwards for a folder containing
- This function searches upwards for the root dir using a heuristic, and caches results in the {@link Map}
replaceCwd: Replaces the current working directory with<cwd>collapseStackTrace: For Node.JS-style stack traces, replaces the long chain of "at ..." lines with a single "at somewhere" lineomitThrowLineNumber: For Node.JS error source display, removes the line number
You can remove them or replace them or add to them by mutating the sanitizers Array.
allInflightRunContexts: Set<RunContext>
All runs that have been spawned which haven't reached completion (see RunContext#completion).
It may be beneficial to clean these up in a test timeout handler or etc.
License
MIT
Upgrading from 1.x to 2.x
The only things that changed between 1.x and 2.0 are:
- The
errorproperty of aRunContextchanged frombooleantonull | Error - We switched node-pty fork to a prebuilt one instead of an install-time compiled node-gyp one, which has the side-effect that we only support the following platforms:
- Linux arm64 and x86_64
- macOS arm64 and x86_64
- Windows arm64 and x86_64
Upgrading from 2.x to 3.x
- The type definitions for
RunContext.writeandRunContext.killwere narrowed slightly. The runtime behavior of those methods didn't change. RunContext.debug()is deprecated (but not removed). Usespawnoptiondebug: trueinstead.
Upgrading from 3.x to 4.x
RunContext.debug()is removed. Usespawnoptiondebug: trueinstead.- The RunContext.result of children spawned with option
pty: truenow has oneoutput: stringproperty instead of a fakestdout: stringand always-emptystderr: string.- In other words, stuff that was being written to
.stdoutis now written to.output.stderrwas always empty for pty children because of how node-pty works. - Non-pty children still have distinct
stdoutandstderrstrings like always.
- In other words, stuff that was being written to
- For pty children, the "close" method which was a no-op on pty children has been removed.
- Non-pty children still have the "close" method.
- For non-pty children, RunContext.completion now doesn't resolve until both the "exit" AND "close" event have been fired.
- Previously it fired when the "close" event was fired, which was usually after "exit".
- A pty child only has one distinct exit event, which is when its completion promise resolves. Same as before.
- For non-pty children, RunContext.completion now rejects when the "error" event is fired.
- Previously, RunContext.completion always resolved, even if RunContext.error was non-null.
