Skip to content
This repository has been archived by the owner on Apr 7, 2021. It is now read-only.

Commit

Permalink
fix(child): iron out a few crinkles and add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
zkat committed Jun 22, 2017
1 parent e413cff commit b3b5ef6
Show file tree
Hide file tree
Showing 4 changed files with 194 additions and 25 deletions.
17 changes: 14 additions & 3 deletions child.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ function runCommand (command, opts) {
}).catch(err => {
if (err.code === 'ENOENT') {
err = new Error(
require('./y.js')`npx: command not found: ${path.basename(cmd)}`
`npx: ${
require('./y.js')`command not found: ${path.basename(cmd)}`
}`
)
err.exitCode = 127
}
Expand Down Expand Up @@ -51,7 +53,16 @@ function exec (cmd, args, opts) {
return new Promise((resolve, reject) => {
cp.exec(`${escapeArg(cmd, true)} ${
args.join(' ')
}`, opts, (err, stdout) => err ? reject(err) : resolve(stdout))
}`, opts, (err, stdout) => {
if (err) {
if (typeof err.code === 'number') {
err.exitCode = err.code
}
reject(err)
} else {
resolve(stdout)
}
})
})
}

Expand All @@ -63,7 +74,7 @@ function escapeArg (str, asPath) {
.map(s => s.match(/\s+/) ? `"${s}"` : s)
.join('\\')
: process.platform === 'win32'
? `"${path.normalize(str)}"`
? `"${str}"`
: str.match(/[^-_.~/\w]/)
? `'${str.replace(/'/g, "'\"'\"'")}'`
: str
Expand Down
48 changes: 27 additions & 21 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,11 @@
"marked-man": "^0.2.1",
"mkdirp": "^0.5.1",
"nyc": "^11.0.2",
"require-inject": "^1.4.0",
"standard": "^10.0.2",
"standard-version": "^4.2.0",
"tacks": "^1.2.6",
"tap": "^10.3.4",
"tap": "^10.5.1",
"weallbehave": "^1.2.0",
"weallcontribute": "^1.0.8"
},
Expand Down
151 changes: 151 additions & 0 deletions test/child.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
'use strict'

const test = require('tap').test

const child = require('../child.js')
const requireInject = require('require-inject')

test('escapeArg on *nix', t => {
const origPlatform = process.platform
Object.defineProperty(process, 'platform', {value: 'linux'})
t.equal(child.escapeArg('foo'), 'foo', 'standard arg left intact')
t.equal(child.escapeArg('foo bar'), '\'foo bar\'', '\'-escaped on *nix')
t.equal(
child.escapeArg('/foo bar/baz\'quux.JPg', true),
'\'/foo bar/baz\'"\'"\'quux.JPg\'',
'paths escaped as usual'
)
Object.defineProperty(process, 'platform', {value: origPlatform})
t.done()
})

test('escapeArg on win32', t => {
const origPlatform = process.platform
Object.defineProperty(process, 'platform', {value: 'win32'})
t.equal(child.escapeArg('foo'), '"foo"', 'standard arg escaped')
t.equal(child.escapeArg('foo bar'), '"foo bar"', '"-escaped on win32')
t.equal(
child.escapeArg('C:\\Foo bar\\baz\'"\\quux.JPg', true),
'C:\\"Foo bar"\\baz\'"\\quux.JPg',
'paths escaped as usual'
)
Object.defineProperty(process, 'platform', {value: origPlatform})
t.done()
})

test('exec', t => {
const child = requireInject('../child.js', {
'child_process': {
exec (cmd, opts, cb) {
if (opts.fail) {
cb(new Error('exec failure requested'))
} else {
cb(null, {cmd, opts})
}
}
}
})
const origPlatform = process.platform
return child.exec('cmd', ['arg1', 'arg2'], {opt: 1}).then(ret => {
t.equal(ret.cmd, 'cmd arg1 arg2', 'command string concatenated')
t.deepEqual(ret.opts, {opt: 1}, 'options received!')
Object.defineProperty(process, 'platform', {value: 'linux'})
return child.exec('/foo bar/baz .quux\\', ['arg1', 'arg 2'])
}).then(ret => {
t.equal(ret.cmd, "'/foo bar/baz .quux\\' arg1 arg 2", 'unix-style escapes')
Object.defineProperty(process, 'platform', {value: 'win32'})
return child.exec('C:\\foo bar\\baz .quux\\a', ['arg1', 'arg 2'])
}).then(ret => {
t.equal(ret.cmd, 'C:\\"foo bar"\\"baz .quux"\\a arg1 arg 2', 'win32-style escapes')
Object.defineProperty(process, 'platform', {value: origPlatform})
}).then(() => {
return child.exec('fail', [], {fail: true}).then(() => {
throw new Error('was supposed to fail')
}, err => {
t.equal(err.message, 'exec failure requested', 'got error')
})
})
})

test('exec (integration)', t => {
return child.exec('node', ['-p', '1+1']).then(stdout => {
t.equal(stdout.trim(), '2', 'node ran successfully')
return child.exec('node', ['-e', '"process.exit(123)"']).then(() => {
throw new Error('was not supposed to succeed')
}, err => {
t.equal(err.exitCode, 123, 'got the exit code from subproc')
})
})
})

test('spawn', t => {
return child.spawn('node', ['-p', '1+1']).then(res => {
t.deepEqual(res, {
code: 0,
stdout: '2\n',
stderr: ''
})
return child.spawn('node', ['-e', 'process.exit(123)']).then(() => {
throw new Error('was not supposed to succeed')
}, err => {
t.equal(err.exitCode, 123, 'got the exit code from ')
})
})
})

test('runCommand with command arg', t => {
return child.runCommand('node', {
cmdOpts: ['-p', '1+1'],
stdio: 'pipe'
}).then(res => {
t.deepEqual(res, {
code: 0,
stdout: '2\n',
stderr: ''
})
return child.runCommand('node', {
cmdOpts: ['-e', 'process.exit(123)']
}).then(() => {
throw new Error('was not supposed to succeed')
}, err => {
t.equal(err.exitCode, 123, 'got the exit code from subproc')
})
}).then(() => {
return child.runCommand('./not-a-command-at-all', {}).then(() => {
throw new Error('was not supposed to succeed')
}, err => {
t.match(err.message, /command not found/, 'error message reports ENOENT')
t.equal(err.exitCode, 127, '"not found" has code 127')
})
})
})

test('runCommand with opts.command', t => {
return child.runCommand(null, {
command: 'node',
cmdOpts: ['-p', '1+1'],
stdio: 'pipe'
}).then(res => {
t.deepEqual(res, {
code: 0,
stdout: '2\n',
stderr: ''
})
})
})

test('runCommand with opts.call and opts.shell', {
skip: process.platform === 'win32' && 'Windows passes different flags to shell'
}, t => {
return child.runCommand(null, {
shell: 'node',
call: './child.js',
stdio: 'pipe'
}).then(res => {
t.deepEqual(res, {
code: 0,
stdout: '',
stderr: ''
})
})
})

0 comments on commit b3b5ef6

Please sign in to comment.