Skip to content

Commit

Permalink
Add an option to spawn command in a shell (#5340)
Browse files Browse the repository at this point in the history
* Add tests for Process.js

* Add option to spawn command inside a shell

* Add tests for constructor, start, and closeProcess

* Add option to spawn command in a shell

* Add #5340 to change log

* Fix build errors
  • Loading branch information
seanpoulter authored and cpojer committed Jan 18, 2018
1 parent 30a9ca2 commit 06bbe2d
Show file tree
Hide file tree
Showing 6 changed files with 403 additions and 43 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## master

* `[jest-editor-support]` Add option to spawn command in shell ([#5340](https://github.com/facebook/jest/pull/5340))

## jest 22.1.2

### Fixes
Expand Down
15 changes: 10 additions & 5 deletions packages/jest-editor-support/src/Process.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
*/

import {ChildProcess, spawn} from 'child_process';

import ProjectWorkspace from './project_workspace';
import type {SpawnOptions} from './types';

/**
* Spawns and returns a Jest process with specific args
Expand All @@ -20,6 +20,7 @@ import ProjectWorkspace from './project_workspace';
export const createProcess = (
workspace: ProjectWorkspace,
args: Array<string>,
options?: SpawnOptions = {},
): ChildProcess => {
// A command could look like `npm run test`, which we cannot use as a command
// as they can only be the first command, so take out the command, and add
Expand All @@ -31,16 +32,20 @@ export const createProcess = (
const runtimeArgs = [].concat(initialArgs, args);

// If a path to configuration file was defined, push it to runtimeArgs
const configPath = workspace.pathToConfig;
if (configPath !== '') {
if (workspace.pathToConfig) {
runtimeArgs.push('--config');
runtimeArgs.push(configPath);
runtimeArgs.push(workspace.pathToConfig);
}

// To use our own commands in create-react, we need to tell the command that
// we're in a CI environment, or it will always append --watch
const env = process.env;
env['CI'] = 'true';

return spawn(command, runtimeArgs, {cwd: workspace.rootPath, env});
const spawnOptions = {
cwd: workspace.rootPath,
env,
shell: options.shell,
};
return spawn(command, runtimeArgs, spawnOptions);
};
20 changes: 14 additions & 6 deletions packages/jest-editor-support/src/Runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* @flow
*/

import type {Options, MessageType} from './types';
import type {Options, MessageType, SpawnOptions} from './types';
import {messageTypes} from './types';

import {ChildProcess, spawn} from 'child_process';
Expand All @@ -27,6 +27,7 @@ export default class Runner extends EventEmitter {
_createProcess: (
workspace: ProjectWorkspace,
args: Array<string>,
options?: SpawnOptions,
) => ChildProcess;
watchMode: boolean;
options: Options;
Expand Down Expand Up @@ -64,7 +65,11 @@ export default class Runner extends EventEmitter {
args.push(this.options.testFileNamePattern);
}

this.debugprocess = this._createProcess(this.workspace, args);
const options = {
shell: this.options.shell,
};

this.debugprocess = this._createProcess(this.workspace, args, options);
this.debugprocess.stdout.on('data', (data: Buffer) => {
// Make jest save to a file, otherwise we get chunked data
// and it can be hard to put it back together.
Expand Down Expand Up @@ -118,10 +123,13 @@ export default class Runner extends EventEmitter {

runJestWithUpdateForSnapshots(completion: any, args: string[]) {
const defaultArgs = ['--updateSnapshot'];
const updateProcess = this._createProcess(this.workspace, [
...defaultArgs,
...(args ? args : []),
]);

const options = {shell: this.options.shell};
const updateProcess = this._createProcess(
this.workspace,
[...defaultArgs, ...(args ? args : [])],
options,
);
updateProcess.on('close', () => {
completion();
});
Expand Down
129 changes: 129 additions & 0 deletions packages/jest-editor-support/src/__tests__/process.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/**
* Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

'use strict';

jest.mock('child_process');

import {createProcess} from '../Process';
import {spawn} from 'child_process';

describe('createProcess', () => {
afterEach(() => {
jest.resetAllMocks();
});

it('spawns the process', () => {
const workspace: any = {pathToJest: ''};
const args = [];
createProcess(workspace, args);

expect(spawn).toBeCalled();
});

it('spawns the command from workspace.pathToJest', () => {
const workspace: any = {pathToJest: 'jest'};
const args = [];
createProcess(workspace, args);

expect(spawn.mock.calls[0][0]).toBe('jest');
expect(spawn.mock.calls[0][1]).toEqual([]);
});

it('spawns the first arg from workspace.pathToJest split on " "', () => {
const workspace: any = {pathToJest: 'npm test --'};
const args = [];
createProcess(workspace, args);

expect(spawn.mock.calls[0][0]).toBe('npm');
expect(spawn.mock.calls[0][1]).toEqual(['test', '--']);
});

it('fails to spawn the first quoted arg from workspace.pathToJest', () => {
const workspace: any = {
pathToJest:
'"../build scripts/test" --coverageDirectory="../code coverage"',
};
const args = [];
createProcess(workspace, args);

expect(spawn.mock.calls[0][0]).not.toBe('"../build scripts/test"');
expect(spawn.mock.calls[0][1]).not.toEqual([
'--coverageDirectory="../code coverage"',
]);
});

it('appends args', () => {
const workspace: any = {pathToJest: 'npm test --'};
const args = ['--option', 'value', '--another'];
createProcess(workspace, args);

expect(spawn.mock.calls[0][1]).toEqual(['test', '--', ...args]);
});

it('sets the --config arg to workspace.pathToConfig', () => {
const workspace: any = {
pathToConfig: 'non-standard.jest.js',
pathToJest: 'npm test --',
};
const args = ['--option', 'value'];
createProcess(workspace, args);

expect(spawn.mock.calls[0][1]).toEqual([
'test',
'--',
'--option',
'value',
'--config',
'non-standard.jest.js',
]);
});

it('defines the "CI" environment variable', () => {
const expected = {
...process.env,
CI: 'true',
};

const workspace: any = {pathToJest: ''};
const args = [];
createProcess(workspace, args);

expect(spawn.mock.calls[0][2].env).toEqual(expected);
});

it('sets the current working directory of the child process', () => {
const workspace: any = {
pathToJest: '',
rootPath: 'root directory',
};
const args = [];
createProcess(workspace, args);

expect(spawn.mock.calls[0][2].cwd).toBe(workspace.rootPath);
});

it('should not set the "shell" property when "options" are not provided', () => {
const workspace: any = {pathToJest: ''};
const args = [];
createProcess(workspace, args);

expect(spawn.mock.calls[0][2].shell).not.toBeDefined();
});

it('should set the "shell" property when "options" are provided', () => {
const expected = {};
const workspace: any = {pathToJest: ''};
const args = [];
const options: any = {shell: expected};
createProcess(workspace, args, options);

expect(spawn.mock.calls[0][2].shell).toBe(expected);
});
});
Loading

0 comments on commit 06bbe2d

Please sign in to comment.