diff --git a/fixtures/detach b/fixtures/detach deleted file mode 100755 index 7da1f8fd6..000000000 --- a/fixtures/detach +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env node -'use strict'; - -const execa = require('..'); - -const subprocess = execa('node', ['./fixtures/forever'], {detached: true}); -console.log(subprocess.pid); -process.exit(); diff --git a/fixtures/echo b/fixtures/echo deleted file mode 100755 index cdfa972df..000000000 --- a/fixtures/echo +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env node -'use strict'; -console.log(process.argv.slice(2).join('\n')) diff --git a/index.d.ts b/index.d.ts index cb25030a1..9a2184690 100644 --- a/index.d.ts +++ b/index.d.ts @@ -187,14 +187,29 @@ declare namespace execa { readonly input?: string | Buffer | ReadableStream; } - interface SyncOptions - extends CommonOptions { + interface SyncOptions extends CommonOptions { /** Write some input to the `stdin` of your binary. */ readonly input?: string | Buffer; } + interface NodeOptions extends Options { + /** + The Node.js executable to use. + + @default process.execPath + */ + readonly nodePath?: string; + + /** + List of string arguments passed to the Node.js executable. + + @default process.execArgv + */ + readonly nodeArguments?: string[]; + } + interface ExecaReturnBase { /** The file and arguments that were run. @@ -417,6 +432,26 @@ declare const execa: { */ commandSync(command: string, options?: execa.SyncOptions): execa.ExecaSyncReturnValue; commandSync(command: string, options?: execa.SyncOptions): execa.ExecaSyncReturnValue; + + /** + Execute a Node.js script as a child process. + + @param scriptPath - Node.js script to execute. + @param arguments - Arguments to pass to `scriptPath` on execution. + @returns A [`child_process` instance](https://nodejs.org/api/child_process.html#child_process_class_childprocess), which is enhanced to also be a `Promise` for a result `Object` with `stdout` and `stderr` properties. + */ + node( + scriptPath: string, + arguments?: readonly string[], + options?: execa.NodeOptions + ): execa.ExecaChildProcess; + node( + file: string, + arguments?: readonly string[], + options?: execa.Options + ): execa.ExecaChildProcess; + node(file: string, options?: execa.Options): execa.ExecaChildProcess; + node(file: string, options?: execa.Options): execa.ExecaChildProcess; }; export = execa; diff --git a/index.js b/index.js index 4c28cc007..db4b85ff7 100644 --- a/index.js +++ b/index.js @@ -520,3 +520,30 @@ module.exports.commandSync = (command, options) => { const [file, ...args] = parseCommand(command); return execa.sync(file, args, options); }; + +module.exports.node = (scriptPath, args, options) => { + if (args && !Array.isArray(args) && typeof args === 'object') { + options = args; + args = []; + } + + const stdioOption = stdio.node(options); + options = options || {}; + + return execa( + options.nodePath || process.execPath, + [ + ...(options.nodeArguments || process.execArgv), + scriptPath, + ...(Array.isArray(args) ? args : []) + ], + { + ...options, + stdin: undefined, + stdout: undefined, + stderr: undefined, + stdio: stdioOption, + shell: false + } + ); +}; diff --git a/index.test-d.ts b/index.test-d.ts index a435ef86f..591ad5e5b 100644 --- a/index.test-d.ts +++ b/index.test-d.ts @@ -166,3 +166,16 @@ expectType>(execa.commandSync('unicorns', {encoding expectType>(execa.commandSync('unicorns', {encoding: null})); expectType>(execa.commandSync('unicorns foo', {encoding: 'utf8'})); expectType>(execa.commandSync('unicorns foo', {encoding: null})); + +expectType>(execa.node('unicorns')); +expectType>(await execa.node('unicorns')); +expectType>( + await execa.node('unicorns', {encoding: 'utf8'}) +); +expectType>(await execa.node('unicorns', {encoding: null})); +expectType>( + await execa.node('unicorns', ['foo'], {encoding: 'utf8'}) +); +expectType>( + await execa.node('unicorns', ['foo'], {encoding: null}) +); diff --git a/lib/stdio.js b/lib/stdio.js index 5b4cb9e57..d9f5458b4 100644 --- a/lib/stdio.js +++ b/lib/stdio.js @@ -3,7 +3,7 @@ const alias = ['stdin', 'stdout', 'stderr']; const hasAlias = opts => alias.some(x => Boolean(opts[x])); -module.exports = opts => { +const stdio = opts => { if (!opts) { return; } @@ -39,3 +39,23 @@ module.exports = opts => { return result; }; + +module.exports = stdio; + +module.exports.node = opts => { + const defaultOption = 'pipe'; + + let stdioOption = stdio(opts || {stdio: defaultOption}); + + if (typeof stdioOption === 'string') { + stdioOption = [...new Array(3)].fill(stdioOption); + } else if (Array.isArray(stdioOption)) { + stdioOption = stdioOption.map((channel = defaultOption) => channel); + } + + if (!stdioOption.includes('ipc')) { + stdioOption.push('ipc'); + } + + return stdioOption; +}; diff --git a/readme.md b/readme.md index 5a27204ee..e0e857109 100644 --- a/readme.md +++ b/readme.md @@ -184,6 +184,15 @@ Same as [`execa.command()`](#execacommand-command-options) but synchronous. Returns or throws a [`childProcessResult`](#childProcessResult). +### execa.node(scriptPath, [arguments], [options]) + +Execute a Node.js script as a child process. + +Same as `execa('node', [file, ...arguments], options)` except (like [`child_process#fork()`](https://nodejs.org/api/child_process.html#child_process_child_process_fork_modulepath_args_options)): + - the [`nodePath`](#nodepath-for-node-only) and [`nodeArguments`](#nodearguments-for-node-only) options can be used + - the [`shell`](#shell) option cannot be used + - an extra channel [`ipc`](https://nodejs.org/api/child_process.html#child_process_options_stdio) is passed to [`stdio`](#stdio) + ### childProcessResult Type: `object` @@ -438,6 +447,19 @@ Default: `false` If `true`, no quoting or escaping of arguments is done on Windows. Ignored on other platforms. This is set to `true` automatically when the `shell` option is `true`. +#### nodePath *(for `.node()` only)* + +Type: `string`
+Default: [`process.execPath`](https://nodejs.org/api/process.html#process_process_execpath) + +Node.js executable used to create the child process. + +#### nodeArguments *(for `.node()` only)* + +Type: `string[]`
+Default: [`process.execArgv`](https://nodejs.org/api/process.html#process_process_execargv) + +List of string arguments passed to the Node.js executable. ## Tips diff --git a/fixtures/command with space b/test/fixtures/command with space similarity index 100% rename from fixtures/command with space rename to test/fixtures/command with space diff --git a/fixtures/delay b/test/fixtures/delay similarity index 100% rename from fixtures/delay rename to test/fixtures/delay diff --git a/test/fixtures/detach b/test/fixtures/detach new file mode 100755 index 000000000..80f898009 --- /dev/null +++ b/test/fixtures/detach @@ -0,0 +1,8 @@ +#!/usr/bin/env node +'use strict'; + +const execa = require('../..'); + +const subprocess = execa('node', ['./test/fixtures/forever'], {detached: true}); +console.log(subprocess.pid); +process.exit(); diff --git a/test/fixtures/echo b/test/fixtures/echo new file mode 100755 index 000000000..e9baf7ef6 --- /dev/null +++ b/test/fixtures/echo @@ -0,0 +1,3 @@ +#!/usr/bin/env node +'use strict'; +console.log(process.argv.slice(2).join('\n')); diff --git a/fixtures/environment b/test/fixtures/environment similarity index 100% rename from fixtures/environment rename to test/fixtures/environment diff --git a/fixtures/exit b/test/fixtures/exit similarity index 100% rename from fixtures/exit rename to test/fixtures/exit diff --git a/fixtures/fail b/test/fixtures/fail similarity index 100% rename from fixtures/fail rename to test/fixtures/fail diff --git a/fixtures/fast-exit-darwin b/test/fixtures/fast-exit-darwin similarity index 100% rename from fixtures/fast-exit-darwin rename to test/fixtures/fast-exit-darwin diff --git a/fixtures/fast-exit-linux b/test/fixtures/fast-exit-linux similarity index 100% rename from fixtures/fast-exit-linux rename to test/fixtures/fast-exit-linux diff --git a/fixtures/forever b/test/fixtures/forever similarity index 100% rename from fixtures/forever rename to test/fixtures/forever diff --git a/fixtures/hello.cmd b/test/fixtures/hello.cmd old mode 100644 new mode 100755 similarity index 100% rename from fixtures/hello.cmd rename to test/fixtures/hello.cmd diff --git a/test/fixtures/hello.sh b/test/fixtures/hello.sh new file mode 100755 index 000000000..ff0e12ec6 --- /dev/null +++ b/test/fixtures/hello.sh @@ -0,0 +1 @@ +echo Hello World diff --git a/fixtures/max-buffer b/test/fixtures/max-buffer similarity index 100% rename from fixtures/max-buffer rename to test/fixtures/max-buffer diff --git a/fixtures/no-killable b/test/fixtures/no-killable old mode 100644 new mode 100755 similarity index 100% rename from fixtures/no-killable rename to test/fixtures/no-killable diff --git a/fixtures/non-executable b/test/fixtures/non-executable similarity index 100% rename from fixtures/non-executable rename to test/fixtures/non-executable diff --git a/fixtures/noop b/test/fixtures/noop similarity index 100% rename from fixtures/noop rename to test/fixtures/noop diff --git a/fixtures/noop-132 b/test/fixtures/noop-132 similarity index 100% rename from fixtures/noop-132 rename to test/fixtures/noop-132 diff --git a/fixtures/noop-err b/test/fixtures/noop-err similarity index 100% rename from fixtures/noop-err rename to test/fixtures/noop-err diff --git a/fixtures/noop-throw b/test/fixtures/noop-throw similarity index 100% rename from fixtures/noop-throw rename to test/fixtures/noop-throw diff --git a/test/fixtures/send b/test/fixtures/send new file mode 100755 index 000000000..c215df6f7 --- /dev/null +++ b/test/fixtures/send @@ -0,0 +1,9 @@ +#!/usr/bin/env node +'use strict'; +process.on('message', message => { + if (message === 'ping') { + process.send('pong'); + } else { + throw new Error('Receive wrong message'); + } +}); diff --git a/fixtures/stdin b/test/fixtures/stdin similarity index 100% rename from fixtures/stdin rename to test/fixtures/stdin diff --git a/fixtures/sub-process b/test/fixtures/sub-process similarity index 57% rename from fixtures/sub-process rename to test/fixtures/sub-process index 727aa0019..6920e7934 100755 --- a/fixtures/sub-process +++ b/test/fixtures/sub-process @@ -1,8 +1,8 @@ #!/usr/bin/env node 'use strict'; -const execa = require('..'); +const execa = require('../..'); const cleanup = process.argv[2] === 'true'; const detached = process.argv[3] === 'true'; -const subprocess = execa('node', ['./fixtures/forever'], {cleanup, detached}); +const subprocess = execa('node', ['./test/fixtures/forever'], {cleanup, detached}); process.send(subprocess.pid); diff --git a/fixtures/sub-process-exit b/test/fixtures/sub-process-exit similarity index 56% rename from fixtures/sub-process-exit rename to test/fixtures/sub-process-exit index 3ea8173ff..2b350a2b6 100755 --- a/fixtures/sub-process-exit +++ b/test/fixtures/sub-process-exit @@ -1,7 +1,7 @@ #!/usr/bin/env node 'use strict'; -const execa = require('..'); +const execa = require('../..'); const cleanup = process.argv[2] === 'true'; const detached = process.argv[3] === 'true'; -execa('node', ['./fixtures/noop'], {cleanup, detached}); +execa('node', ['./test/fixtures/noop'], {cleanup, detached}); diff --git a/test/node.js b/test/node.js new file mode 100644 index 000000000..c6ff30d8a --- /dev/null +++ b/test/node.js @@ -0,0 +1,46 @@ +import test from 'ava'; +import pEvent from 'p-event'; +import path from 'path'; +import execa from '..'; + +process.env.PATH = path.join(__dirname, 'fixtures') + path.delimiter + process.env.PATH; + +test('node()', async t => { + const {exitCode} = await execa.node('test/fixtures/noop'); + t.is(exitCode, 0); +}); + +test('node pipe stdout', async t => { + const {stdout} = await execa.node('test/fixtures/noop', ['foo'], { + stdout: 'pipe' + }); + + t.is(stdout, 'foo'); +}); + +test('node correctly use nodePath', async t => { + const {stdout} = await execa.node(process.platform === 'win32' ? 'hello.cmd' : 'hello.sh', { + stdout: 'pipe', + nodePath: process.platform === 'win32' ? 'cmd.exe' : 'bash', + nodeArguments: process.platform === 'win32' ? ['/c'] : [] + }); + + t.is(stdout, 'Hello World'); +}); + +test('node pass on nodeArguments', async t => { + const {stdout} = await execa.node('console.log("foo")', { + stdout: 'pipe', + nodeArguments: ['-e'] + }); + + t.is(stdout, 'foo'); +}); + +test('node\'s forked script has a communication channel', async t => { + const subprocess = execa.node('test/fixtures/send'); + subprocess.send('ping'); + + const message = await pEvent(subprocess, 'message'); + t.is(message, 'pong'); +}); diff --git a/test/stdio.js b/test/stdio.js index e02a32911..a9ccb0325 100644 --- a/test/stdio.js +++ b/test/stdio.js @@ -4,46 +4,69 @@ import stdio from '../lib/stdio'; util.inspect.styles.name = 'magenta'; -function macro(t, input, expected) { - if (expected instanceof Error) { - t.throws(() => stdio(input), expected.message); - return; - } - - const result = stdio(input); - - if (typeof expected === 'object' && expected !== null) { - t.deepEqual(result, expected); - } else { - t.is(result, expected); - } +function createMacro(func) { + const macro = (t, input, expected) => { + if (expected instanceof Error) { + t.throws(() => { + stdio(input); + }, expected.message); + return; + } + + const result = func(input); + + if (typeof expected === 'object' && expected !== null) { + t.deepEqual(result, expected); + } else { + t.is(result, expected); + } + }; + + macro.title = (providedTitle, input) => `${func.name} ${(providedTitle || util.inspect(input, {colors: true}))}`; + + return macro; } -macro.title = (providedTitle, input) => providedTitle || util.inspect(input, {colors: true}); - -test(macro, undefined, undefined); - -test(macro, {stdio: 'inherit'}, 'inherit'); -test(macro, {stdio: 'pipe'}, 'pipe'); -test(macro, {stdio: 'ignore'}, 'ignore'); -test(macro, {stdio: [0, 1, 2]}, [0, 1, 2]); - -test(macro, {}, [undefined, undefined, undefined]); -test(macro, {stdio: []}, [undefined, undefined, undefined]); -test(macro, {stdin: 'pipe'}, ['pipe', undefined, undefined]); -test(macro, {stdout: 'ignore'}, [undefined, 'ignore', undefined]); -test(macro, {stderr: 'inherit'}, [undefined, undefined, 'inherit']); -test(macro, {stdin: 'pipe', stdout: 'ignore', stderr: 'inherit'}, ['pipe', 'ignore', 'inherit']); -test(macro, {stdin: 'pipe', stdout: 'ignore'}, ['pipe', 'ignore', undefined]); -test(macro, {stdin: 'pipe', stderr: 'inherit'}, ['pipe', undefined, 'inherit']); -test(macro, {stdout: 'ignore', stderr: 'inherit'}, [undefined, 'ignore', 'inherit']); -test(macro, {stdin: 0, stdout: 1, stderr: 2}, [0, 1, 2]); -test(macro, {stdin: 0, stdout: 1}, [0, 1, undefined]); -test(macro, {stdin: 0, stderr: 2}, [0, undefined, 2]); -test(macro, {stdout: 1, stderr: 2}, [undefined, 1, 2]); - -test(macro, {stdio: {foo: 'bar'}}, new TypeError('Expected `stdio` to be of type `string` or `Array`, got `object`')); - -test(macro, {stdin: 'inherit', stdio: 'pipe'}, new Error('It\'s not possible to provide `stdio` in combination with one of `stdin`, `stdout`, `stderr`')); -test(macro, {stdin: 'inherit', stdio: ['pipe']}, new Error('It\'s not possible to provide `stdio` in combination with one of `stdin`, `stdout`, `stderr`')); -test(macro, {stdin: 'inherit', stdio: [undefined, 'pipe']}, new Error('It\'s not possible to provide `stdio` in combination with one of `stdin`, `stdout`, `stderr`')); +const stdioMacro = createMacro(stdio); + +test(stdioMacro, undefined, undefined); +test(stdioMacro, null, undefined); + +test(stdioMacro, {stdio: 'inherit'}, 'inherit'); +test(stdioMacro, {stdio: 'pipe'}, 'pipe'); +test(stdioMacro, {stdio: 'ignore'}, 'ignore'); +test(stdioMacro, {stdio: [0, 1, 2]}, [0, 1, 2]); + +test(stdioMacro, {}, [undefined, undefined, undefined]); +test(stdioMacro, {stdio: []}, [undefined, undefined, undefined]); +test(stdioMacro, {stdin: 'pipe'}, ['pipe', undefined, undefined]); +test(stdioMacro, {stdout: 'ignore'}, [undefined, 'ignore', undefined]); +test(stdioMacro, {stderr: 'inherit'}, [undefined, undefined, 'inherit']); +test(stdioMacro, {stdin: 'pipe', stdout: 'ignore', stderr: 'inherit'}, ['pipe', 'ignore', 'inherit']); +test(stdioMacro, {stdin: 'pipe', stdout: 'ignore'}, ['pipe', 'ignore', undefined]); +test(stdioMacro, {stdin: 'pipe', stderr: 'inherit'}, ['pipe', undefined, 'inherit']); +test(stdioMacro, {stdout: 'ignore', stderr: 'inherit'}, [undefined, 'ignore', 'inherit']); +test(stdioMacro, {stdin: 0, stdout: 1, stderr: 2}, [0, 1, 2]); +test(stdioMacro, {stdin: 0, stdout: 1}, [0, 1, undefined]); +test(stdioMacro, {stdin: 0, stderr: 2}, [0, undefined, 2]); +test(stdioMacro, {stdout: 1, stderr: 2}, [undefined, 1, 2]); + +test(stdioMacro, {stdio: {foo: 'bar'}}, new TypeError('Expected `stdio` to be of type `string` or `Array`, got `object`')); + +test(stdioMacro, {stdin: 'inherit', stdio: 'pipe'}, new Error('It\'s not possible to provide `stdio` in combination with one of `stdin`, `stdout`, `stderr`')); +test(stdioMacro, {stdin: 'inherit', stdio: ['pipe']}, new Error('It\'s not possible to provide `stdio` in combination with one of `stdin`, `stdout`, `stderr`')); +test(stdioMacro, {stdin: 'inherit', stdio: [undefined, 'pipe']}, new Error('It\'s not possible to provide `stdio` in combination with one of `stdin`, `stdout`, `stderr`')); + +const forkMacro = createMacro(stdio.node); + +test(forkMacro, undefined, ['pipe', 'pipe', 'pipe', 'ipc']); +test(forkMacro, {stdio: 'ignore'}, ['ignore', 'ignore', 'ignore', 'ipc']); +test(forkMacro, {stdio: [0, 1, 2]}, [0, 1, 2, 'ipc']); +test(forkMacro, {stdio: [0, 1, 2, 3]}, [0, 1, 2, 3, 'ipc']); +test(forkMacro, {stdio: [0, 1, 2, 'ipc']}, [0, 1, 2, 'ipc']); + +test(forkMacro, {stdout: 'ignore'}, ['pipe', 'ignore', 'pipe', 'ipc']); +test(forkMacro, {stdout: 'ignore', stderr: 'ignore'}, ['pipe', 'ignore', 'ignore', 'ipc']); + +test(forkMacro, {stdio: {foo: 'bar'}}, new TypeError('Expected `stdio` to be of type `string` or `Array`, got `object`')); +test(forkMacro, {stdin: 'inherit', stdio: 'pipe'}, new Error('It\'s not possible to provide `stdio` in combination with one of `stdin`, `stdout`, `stderr`')); diff --git a/test.js b/test/test.js similarity index 93% rename from test.js rename to test/test.js index 18bee8c17..613ddecf3 100644 --- a/test.js +++ b/test/test.js @@ -7,7 +7,7 @@ import getStream from 'get-stream'; import isRunning from 'is-running'; import tempfile from 'tempfile'; import pEvent from 'p-event'; -import execa from '.'; +import execa from '..'; process.env.PATH = path.join(__dirname, 'fixtures') + path.delimiter + process.env.PATH; process.env.FOO = 'foo'; @@ -87,13 +87,13 @@ test('stdout/stderr/all on process errors, in sync mode', t => { test('pass `stdout` to a file descriptor', async t => { const file = tempfile('.txt'); - await execa('fixtures/noop', ['foo bar'], {stdout: fs.openSync(file, 'w')}); + await execa('test/fixtures/noop', ['foo bar'], {stdout: fs.openSync(file, 'w')}); t.is(fs.readFileSync(file, 'utf8'), 'foo bar\n'); }); test('pass `stderr` to a file descriptor', async t => { const file = tempfile('.txt'); - await execa('fixtures/noop-err', ['foo bar'], {stderr: fs.openSync(file, 'w')}); + await execa('test/fixtures/noop-err', ['foo bar'], {stderr: fs.openSync(file, 'w')}); t.is(fs.readFileSync(file, 'utf8'), 'foo bar\n'); }); @@ -119,7 +119,7 @@ test('skip throwing when using reject option in sync mode', t => { }); test('kill("SIGKILL") should terminate cleanly', async t => { - const subprocess = execa('node', ['fixtures/no-killable'], {stdio: ['ipc']}); + const subprocess = execa('node', ['./test/fixtures/no-killable'], {stdio: ['ipc']}); await pEvent(subprocess, 'message'); subprocess.kill('SIGKILL'); @@ -132,7 +132,7 @@ test('kill("SIGKILL") should terminate cleanly', async t => { // Therefore, this feature and those tests do not make sense on Windows. if (process.platform !== 'win32') { test('`forceKillAfterTimeout: false` should not kill after a timeout', async t => { - const subprocess = execa('node', ['fixtures/no-killable'], {stdio: ['ipc']}); + const subprocess = execa('node', ['./test/fixtures/no-killable'], {stdio: ['ipc']}); await pEvent(subprocess, 'message'); subprocess.kill('SIGTERM', {forceKillAfterTimeout: false}); @@ -142,7 +142,7 @@ if (process.platform !== 'win32') { }); test('`forceKillAfterTimeout: number` should kill after a timeout', async t => { - const subprocess = execa('node', ['fixtures/no-killable'], {stdio: ['ipc']}); + const subprocess = execa('node', ['./test/fixtures/no-killable'], {stdio: ['ipc']}); await pEvent(subprocess, 'message'); subprocess.kill('SIGTERM', {forceKillAfterTimeout: 50}); @@ -152,7 +152,7 @@ if (process.platform !== 'win32') { }); test('`forceKillAfterTimeout: true` should kill after a timeout', async t => { - const subprocess = execa('node', ['fixtures/no-killable'], {stdio: ['ipc']}); + const subprocess = execa('node', ['./test/fixtures/no-killable'], {stdio: ['ipc']}); await pEvent(subprocess, 'message'); subprocess.kill('SIGTERM', {forceKillAfterTimeout: true}); @@ -162,7 +162,7 @@ if (process.platform !== 'win32') { }); test('kill() with no arguments should kill after a timeout', async t => { - const subprocess = execa('node', ['fixtures/no-killable'], {stdio: ['ipc']}); + const subprocess = execa('node', ['./test/fixtures/no-killable'], {stdio: ['ipc']}); await pEvent(subprocess, 'message'); subprocess.kill(); @@ -382,7 +382,7 @@ test('execa() returns code and failed properties', async t => { }); test('use relative path with \'..\' chars', async t => { - const pathViaParentDir = path.join('..', path.basename(__dirname), 'fixtures', 'noop'); + const pathViaParentDir = path.join('..', path.basename(path.dirname(__dirname)), 'test', 'fixtures', 'noop'); const {stdout} = await execa(pathViaParentDir, ['foo']); t.is(stdout, 'foo'); }); @@ -623,13 +623,13 @@ test('do not extend environment with `extendEnv: false`', async t => { }); test('can use `options.shell: true`', async t => { - const {stdout} = await execa('node fixtures/noop foo', {shell: true}); + const {stdout} = await execa('node test/fixtures/noop foo', {shell: true}); t.is(stdout, 'foo'); }); test('can use `options.shell: string`', async t => { const shell = process.platform === 'win32' ? 'cmd.exe' : '/bin/bash'; - const {stdout} = await execa('node fixtures/noop foo', {shell}); + const {stdout} = await execa('node test/fixtures/noop foo', {shell}); t.is(stdout, 'foo'); }); @@ -777,7 +777,7 @@ test('calling cancel method twice should show the same behaviour as calling it o t.true(subprocess.killed); }); -test('calling cancel method on a successfuly completed process does not make result.isCanceled true', async t => { +test('calling cancel method on a successfully completed process does not make result.isCanceled true', async t => { const subprocess = execa('noop'); const {isCanceled} = await subprocess; subprocess.cancel(); @@ -792,46 +792,46 @@ test('calling cancel method on a process which has been killed does not make err }); test('allow commands with spaces and no array arguments', async t => { - const {stdout} = await execa('./fixtures/command with space'); + const {stdout} = await execa('command with space'); t.is(stdout, ''); }); test('allow commands with spaces and array arguments', async t => { - const {stdout} = await execa('./fixtures/command with space', ['foo', 'bar']); + const {stdout} = await execa('command with space', ['foo', 'bar']); t.is(stdout, 'foo\nbar'); }); test('execa.command()', async t => { - const {stdout} = await execa.command('node fixtures/echo foo bar'); + const {stdout} = await execa.command('node test/fixtures/echo foo bar'); t.is(stdout, 'foo\nbar'); }); test('execa.command() ignores consecutive spaces', async t => { - const {stdout} = await execa.command('node fixtures/echo foo bar'); + const {stdout} = await execa.command('node test/fixtures/echo foo bar'); t.is(stdout, 'foo\nbar'); }); test('execa.command() allows escaping spaces in commands', async t => { - const {stdout} = await execa.command('./fixtures/command\\ with\\ space foo bar'); + const {stdout} = await execa.command('command\\ with\\ space foo bar'); t.is(stdout, 'foo\nbar'); }); test('execa.command() allows escaping spaces in arguments', async t => { - const {stdout} = await execa.command('node fixtures/echo foo\\ bar'); + const {stdout} = await execa.command('node test/fixtures/echo foo\\ bar'); t.is(stdout, 'foo bar'); }); test('execa.command() escapes other whitespaces', async t => { - const {stdout} = await execa.command('node fixtures/echo foo\tbar'); + const {stdout} = await execa.command('node test/fixtures/echo foo\tbar'); t.is(stdout, 'foo\tbar'); }); test('execa.command() trims', async t => { - const {stdout} = await execa.command(' node fixtures/echo foo bar '); + const {stdout} = await execa.command(' node test/fixtures/echo foo bar '); t.is(stdout, 'foo\nbar'); }); test('execa.command.sync()', t => { - const {stdout} = execa.commandSync('node fixtures/echo foo bar'); + const {stdout} = execa.commandSync('node test/fixtures/echo foo bar'); t.is(stdout, 'foo\nbar'); });