diff --git a/README.md b/README.md index 18c6f82..72425ab 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,9 @@ Returns an object with the following members: substring or regular expression to be output, for up to a given timeout. - `childProcess`: The underlying instance of `ChildProcess` +The returned object is also a `Promise` that when the process exits, resolves +with an object containing `output` and the exit `code`. + ```js import test from 'ava' import {runProcess} from 'ava-patterns' diff --git a/run-process/index.js b/run-process/index.js index 6a51a68..069459b 100644 --- a/run-process/index.js +++ b/run-process/index.js @@ -12,62 +12,71 @@ export default function ( detached: true }) t.teardown(() => { - process.kill(-child.pid) + try { + process.kill(-child.pid) + } catch (error) { + if (error.code !== 'ESRCH') { + throw error + } + } + }) + + const program = new Promise((resolve) => { + child.on('close', (code) => { + resolve({code, output: program.output}) + }) }) + program.childProcess = child + program.output = '' + program.outputStream = new PassThrough() - let output = '' - const outputStream = new PassThrough() + program.outputStream.setEncoding('utf8') child.stdout.setEncoding('utf8') child.stderr.setEncoding('utf8') Promise.all([ (async () => { for await (const data of child.stdout) { - output += data - outputStream.write(data) + program.output += data + program.outputStream.write(data) } })(), (async () => { for await (const data of child.stderr) { - output += data - outputStream.write(data) + program.output += data + program.outputStream.write(data) } })() ]).then( () => { - outputStream.end() + program.outputStream.end() }, (error) => { - outputStream.emit('error', error) + program.outputStream.emit('error', error) } ) - return { - childProcess: child, - get output() { - return output - }, - outputStream, - async waitForOutput(pattern, timeout = 1000) { - const match = - typeof pattern === 'string' - ? (string) => string.includes(pattern) - : (string) => Boolean(string.match(pattern)) + program.waitForOutput = async (pattern, timeout = 1000) => { + const match = + typeof pattern === 'string' + ? (string) => string.includes(pattern) + : (string) => Boolean(string.match(pattern)) - await Promise.race([ - (async () => { - await wait(timeout) - throw new Error('Timeout exceeded without seeing expected output.') - })(), - (async () => { - for await (const data of outputStream) { - if (match(data)) { - return - } + await Promise.race([ + (async () => { + await wait(timeout) + throw new Error('Timeout exceeded without seeing expected output.') + })(), + (async () => { + for await (const data of program.outputStream) { + if (match(data)) { + return } + } - throw new Error('Process ended without emitting expected output.') - })() - ]) - } + throw new Error('Process ended without emitting expected output.') + })() + ]) } + + return program } diff --git a/run-process/index.test.js b/run-process/index.test.js index 324493d..d1fbddd 100644 --- a/run-process/index.test.js +++ b/run-process/index.test.js @@ -81,3 +81,12 @@ test('setting the working directory and environment variables', async (t) => { await program.waitForOutput('Done!') t.is(program.output, '/tmp\nFOO bar\nDone!\n') }) + +test('running a simple command that terminates', async (t) => { + const {output, code} = await runProcess(t, { + command: ['ls', '/'] + }) + + t.true(output.includes('tmp')) + t.is(code, 0) +})