ObservableProcess

ObservableProcess enhances the slightly too low-level
Node.JS ChildProcess model with
functionality to observe the behavior of processes more conveniently. In
particular:
- easier access to the textual content of the
stdout
and
stderr
streams
- augments
stdout and stderr with methods to wait for textual content
- create a new
output stream that combines stdout and stderr
- ability to
await the process end
- easier access to the process exit code
- signals whether the process ended naturally or was manually terminated
This is useful for example when testing the terminal output of applications or
waiting until a server has fully booted up. Executing long-running processes
that produce lots of output through ObservableProcess will cause high memory
consumption because it stores all the terminal output in RAM.
Setup
Add this library to your code base:
$ npm install observable-process
Load this library into your JavaScript code:
const { start } = require("observable-process")
or
import { start } from "observable-process"
Starting processes
The best way to provide the command to start is in the form of an argv array:
const observable = start(["node", "server.js"])
You can also provide the full command line to run as a string:
const observable = start("node server.js")
By default, the process runs in the current directory. To set the different
working directory for the subprocess:
const observable = start("node server.js", { cwd: "~/tmp" })
You can provide custom environment variables for the process:
const observable = start("node server.js", {
env: {
foo: "bar",
PATH: process.env.PATH,
},
})
Without a custom env parameter, ObservableProcess uses the environment
variables from the parent process.
Reading output
The stdout and stderr variables of an ObservableProcess behave like normal
readable streams
and provide extra functionality to access and search their content while the
process runs.
observable.stdout.on("data", function () {
})
const text = observable.stdout.fullText()
await observable.stdout.waitForText("server is online")
const port = await observable.stdout.waitForRegex(/running at port \d+./)
Comparable functionality is available for STDERR. ObservableProcess also creates
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+./)
The Result instance you get when the process ends also contains
the full content of the output streams:
const result = observable.waitForEnd()
assert.equal(result, {
exitCode: 0,
killed: false,
stdText: "... content from STDOUT ...",
errText: "... content from STDERR ...",
combinedText: "... content from both STDOUT and STDERR ...",
})
Sending input to the process
ObservableProcess exposes the
stdin
stream of its underlying
ChildProcess:
observable.stdin.write("my input\n")
observable.stdin.end()
Get the process id
observable.pid()
Stop the process
To let ObservableProcess notify you when a process ended:
const result = await observable.waitForEnd()
You can also listen to this in the background:
observable.waitForEnd().then(function () {
})
The exit code is available via an attribute:
observable.exitCode
You can also manually stop a running process via:
const result = await observable.kill()
Related libraries
- 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.
Development
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 to master
- run
npm publish