ObservableProcess enhances the slightly too low-level Node.JS ChildProcess model with functionality to observe the behavior of processes more conveniently. In particular:
- easy access to the accumulated content of stdout and stderr
- await content in
stdout
andstderr
- combines
stdout
andstderr
into a newoutput
stream - await the process end
- easier access to the process exit code
- signals whether the process ended naturally or was manually terminated
ObservableProcess is for short-lived processes, for example when testing the terminal output of applications. Since ObservableProcess stores all output from the child process in memory, executing long-running processes that produce lots of output through ObservableProcess will cause high memory consumption.
Add this library to your code base:
$ npm install observable-process
Load this library into your JavaScript code:
const observableProcess = require("observable-process")
or
import * as observableProcess from "observable-process"
The best way to provide the command to start is in the form of an argv
array:
const observable = observableProcess.start(["node", "server.js"])
You can also provide the full command line to run as a string:
const observable = observableProcess.start("node server.js")
By default, the process runs in the current directory. To set a different working directory for the subprocess:
const observable = observableProcess.start("node server.js", { cwd: "~/tmp" })
You can provide custom environment variables for the process:
const observable = observableProcess.start("node server.js", {
env: {
foo: "bar",
PATH: process.env.PATH,
},
})
Without an env
parameter, ObservableProcess uses the environment variables
from the parent process.
The stdout
and stderr
variables of an ObservableProcess behave like normal
readable streams:
// normal consumption of data from STDOUT via the event stream
observable.stdout.on("data", function () {
// ...
})
They also provide extra functionality to access and search their aggregated content. To get all content from STDOUT as a string:
const text = observable.stdout.fullText()
To wait for text to appear in STDOUT:
const match = await observable.stdout.waitForText("server is online")
// => "server is online"
To wait for a regular expression on STDOUT:
const match = await observable.stdout.waitForRegex(/running at port \d+/)
// => "running at port 3000"
Comparable functionality is available for stderr
. ObservableProcess also
provides a new output
stream with the combined content of STDOUT and STDERR:
observable.output.on("data", function (data) {
// ...
})
const text = observable.output.fullText()
await observable.output.waitForText("server is online")
const port = await observable.output.waitForRegex(/running at port \d+./)
You also get a copy of the process output after it ended (see below).
ObservableProcess exposes the stdin stream of its underlying ChildProcess:
observable.stdin.write("my input\n")
observable.stdin.end()
observable.pid()
Wait until the process ends naturally:
const result = await observable.waitForEnd()
assert.equal(result, {
status: "finished",
exitCode: 0,
stdText: "... content from STDOUT ...",
errText: "... content from STDERR ...",
combinedText: "... content from both STDOUT and STDERR ...",
})
Manually stop the process:
const result = await observable.kill()
assert.equal(result, {
status: "killed",
stdText: "... content from STDOUT ...",
errText: "... content from STDERR ...",
combinedText: "... content from both STDOUT and STDERR ...",
})
- nexpect: Allows to define expectations on command output, and send it input, but doesn't allow to add more listeners to existing long-running processes, which makes declarative testing hard.
If you want to hack on ObservableProcess:
- run all tests:
make test
- run automated code repair:
make fix
- see all make commands:
make help
To deploy a new version:
- update the version in
package.json
and commit tomaster
- run
npm publish