concurrently 4.1.2 → 5.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -8
- package/bin/epilogue.txt +0 -4
- package/package.json +12 -5
- package/src/completion-listener.js +26 -22
- package/src/defaults.js +2 -2
- package/src/flow-control/kill-on-signal.js +1 -1
- package/src/get-spawn-opts.js +1 -1
- package/.editorconfig +0 -28
- package/.eslintrc.json +0 -23
- package/.travis.yml +0 -11
- package/.vscode/settings.json +0 -6
- package/CONTRIBUTING.md +0 -23
- package/appveyor.yml +0 -18
- package/bin/concurrently.spec.js +0 -365
- package/bin/fixtures/read-echo.js +0 -10
- package/docs/demo.gif +0 -0
- package/docs/frontend-demo.gif +0 -0
- package/docs/kill-demo.gif +0 -0
- package/docs/raw-demo.gif +0 -0
- package/src/command-parser/expand-npm-shortcut.spec.js +0 -36
- package/src/command-parser/expand-npm-wildcard.spec.js +0 -58
- package/src/command-parser/strip-quotes.spec.js +0 -20
- package/src/command.spec.js +0 -142
- package/src/completion-listener.spec.js +0 -88
- package/src/concurrently.spec.js +0 -77
- package/src/flow-control/input-handler.spec.js +0 -79
- package/src/flow-control/kill-on-signal.spec.js +0 -70
- package/src/flow-control/kill-others.spec.js +0 -66
- package/src/flow-control/log-error.spec.js +0 -40
- package/src/flow-control/log-exit.spec.js +0 -36
- package/src/flow-control/log-output.spec.js +0 -41
- package/src/flow-control/restart-process.spec.js +0 -129
- package/src/get-spawn-opts.spec.js +0 -18
- package/src/logger.spec.js +0 -178
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
const ExpandNpmShortcut = require('./expand-npm-shortcut');
|
|
2
|
-
const parser = new ExpandNpmShortcut();
|
|
3
|
-
|
|
4
|
-
it('returns same command if no npm: prefix is present', () => {
|
|
5
|
-
const commandInfo = {
|
|
6
|
-
name: 'echo',
|
|
7
|
-
command: 'echo foo'
|
|
8
|
-
};
|
|
9
|
-
expect(parser.parse(commandInfo)).toBe(commandInfo);
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
for (const npmCmd of ['npm', 'yarn']) {
|
|
13
|
-
describe(`with ${npmCmd}: prefix`, () => {
|
|
14
|
-
it(`expands to "${npmCmd} run <script> <args>"`, () => {
|
|
15
|
-
const commandInfo = {
|
|
16
|
-
name: 'echo',
|
|
17
|
-
command: `${npmCmd}:foo -- bar`
|
|
18
|
-
};
|
|
19
|
-
expect(parser.parse(commandInfo)).toEqual({
|
|
20
|
-
name: 'echo',
|
|
21
|
-
command: `${npmCmd} run foo -- bar`
|
|
22
|
-
});
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it('sets name to script name if none', () => {
|
|
26
|
-
const commandInfo = {
|
|
27
|
-
command: `${npmCmd}:foo -- bar`
|
|
28
|
-
};
|
|
29
|
-
expect(parser.parse(commandInfo)).toEqual({
|
|
30
|
-
name: 'foo',
|
|
31
|
-
command: `${npmCmd} run foo -- bar`
|
|
32
|
-
});
|
|
33
|
-
});
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
const ExpandNpmWildcard = require('./expand-npm-wildcard');
|
|
2
|
-
|
|
3
|
-
let parser, readPkg;
|
|
4
|
-
|
|
5
|
-
beforeEach(() => {
|
|
6
|
-
readPkg = jest.fn();
|
|
7
|
-
parser = new ExpandNpmWildcard(readPkg);
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
it('returns same command if not an npm run command', () => {
|
|
11
|
-
const commandInfo = {
|
|
12
|
-
command: 'npm test'
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
expect(readPkg).not.toHaveBeenCalled();
|
|
16
|
-
expect(parser.parse(commandInfo)).toBe(commandInfo);
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
it('returns same command if no wildcard present', () => {
|
|
20
|
-
const commandInfo = {
|
|
21
|
-
command: 'npm run foo bar'
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
expect(readPkg).not.toHaveBeenCalled();
|
|
25
|
-
expect(parser.parse(commandInfo)).toBe(commandInfo);
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it('expands to nothing if no scripts exist in package.json', () => {
|
|
29
|
-
readPkg.mockReturnValue({});
|
|
30
|
-
|
|
31
|
-
expect(parser.parse({ command: 'npm run foo-*-baz qux' })).toEqual([]);
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
for (const npmCmd of ['npm', 'yarn']) {
|
|
35
|
-
describe(`with an ${npmCmd}: prefix`, () => {
|
|
36
|
-
it('expands to all scripts matching pattern', () => {
|
|
37
|
-
readPkg.mockReturnValue({
|
|
38
|
-
scripts: {
|
|
39
|
-
'foo-bar-baz': '',
|
|
40
|
-
'foo--baz': '',
|
|
41
|
-
}
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
expect(parser.parse({ command: `${npmCmd} run foo-*-baz qux` })).toEqual([
|
|
45
|
-
{ name: 'foo-bar-baz', command: `${npmCmd} run foo-bar-baz qux` },
|
|
46
|
-
{ name: 'foo--baz', command: `${npmCmd} run foo--baz qux` },
|
|
47
|
-
]);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it('caches scripts upon calls', () => {
|
|
51
|
-
readPkg.mockReturnValue({});
|
|
52
|
-
parser.parse({ command: `${npmCmd} run foo-*-baz qux` });
|
|
53
|
-
parser.parse({ command: `${npmCmd} run foo-*-baz qux` });
|
|
54
|
-
|
|
55
|
-
expect(readPkg).toHaveBeenCalledTimes(1);
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
const StripQuotes = require('./strip-quotes');
|
|
2
|
-
const parser = new StripQuotes();
|
|
3
|
-
|
|
4
|
-
it('returns command as is if no single/double quote at the beginning', () => {
|
|
5
|
-
expect(parser.parse({ command: 'echo foo' })).toEqual({ command: 'echo foo' });
|
|
6
|
-
});
|
|
7
|
-
|
|
8
|
-
it('strips single quotes', () => {
|
|
9
|
-
expect(parser.parse({ command: '\'echo foo\'' })).toEqual({ command: 'echo foo' });
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
it('strips double quotes', () => {
|
|
13
|
-
expect(parser.parse({ command: '"echo foo"' })).toEqual({ command: 'echo foo' });
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it('does not remove quotes if they are impaired', () => {
|
|
17
|
-
expect(parser.parse({ command: '"echo foo' })).toEqual({ command: '"echo foo' });
|
|
18
|
-
expect(parser.parse({ command: 'echo foo\'' })).toEqual({ command: 'echo foo\'' });
|
|
19
|
-
expect(parser.parse({ command: '"echo foo\'' })).toEqual({ command: '"echo foo\'' });
|
|
20
|
-
});
|
package/src/command.spec.js
DELETED
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
const EventEmitter = require('events');
|
|
2
|
-
const Command = require('./command');
|
|
3
|
-
|
|
4
|
-
const createProcess = () => {
|
|
5
|
-
const process = new EventEmitter();
|
|
6
|
-
process.pid = 1;
|
|
7
|
-
return process;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
const createProcessWithIO = () => {
|
|
11
|
-
const process = createProcess();
|
|
12
|
-
return Object.assign(process, {
|
|
13
|
-
stdout: new EventEmitter(),
|
|
14
|
-
stderr: new EventEmitter(),
|
|
15
|
-
stdin: new EventEmitter()
|
|
16
|
-
});
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
describe('#start()', () => {
|
|
20
|
-
it('spawns process with given command and options', () => {
|
|
21
|
-
const spawn = jest.fn().mockReturnValue(createProcess());
|
|
22
|
-
const command = new Command({
|
|
23
|
-
spawn,
|
|
24
|
-
spawnOpts: { bla: true },
|
|
25
|
-
command: 'echo foo',
|
|
26
|
-
});
|
|
27
|
-
command.start();
|
|
28
|
-
|
|
29
|
-
expect(spawn).toHaveBeenCalledTimes(1);
|
|
30
|
-
expect(spawn).toHaveBeenCalledWith(command.command, { bla: true });
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it('sets stdin, process and PID', () => {
|
|
34
|
-
const process = createProcessWithIO();
|
|
35
|
-
const command = new Command({ spawn: () => process });
|
|
36
|
-
|
|
37
|
-
command.start();
|
|
38
|
-
expect(command.process).toBe(process);
|
|
39
|
-
expect(command.pid).toBe(process.pid);
|
|
40
|
-
expect(command.stdin).toBe(process.stdin);
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it('shares errors to the error stream', done => {
|
|
44
|
-
const process = createProcess();
|
|
45
|
-
const command = new Command({ spawn: () => process });
|
|
46
|
-
|
|
47
|
-
command.error.subscribe(data => {
|
|
48
|
-
expect(data).toBe('foo');
|
|
49
|
-
expect(command.process).toBeUndefined();
|
|
50
|
-
done();
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
command.start();
|
|
54
|
-
process.emit('error', 'foo');
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('shares closes to the close stream with exit code', done => {
|
|
58
|
-
const process = createProcess();
|
|
59
|
-
const command = new Command({ spawn: () => process });
|
|
60
|
-
|
|
61
|
-
command.close.subscribe(data => {
|
|
62
|
-
expect(data).toBe(0);
|
|
63
|
-
expect(command.process).toBeUndefined();
|
|
64
|
-
done();
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
command.start();
|
|
68
|
-
process.emit('close', 0, null);
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it('shares closes to the close stream with signal', done => {
|
|
72
|
-
const process = createProcess();
|
|
73
|
-
const command = new Command({ spawn: () => process });
|
|
74
|
-
|
|
75
|
-
command.close.subscribe(data => {
|
|
76
|
-
expect(data).toBe('SIGKILL');
|
|
77
|
-
done();
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
command.start();
|
|
81
|
-
process.emit('close', null, 'SIGKILL');
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it('shares stdout to the stdout stream', done => {
|
|
85
|
-
const process = createProcessWithIO();
|
|
86
|
-
const command = new Command({ spawn: () => process });
|
|
87
|
-
|
|
88
|
-
command.stdout.subscribe(data => {
|
|
89
|
-
expect(data.toString()).toBe('hello');
|
|
90
|
-
done();
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
command.start();
|
|
94
|
-
process.stdout.emit('data', Buffer.from('hello'));
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it('shares stderr to the stdout stream', done => {
|
|
98
|
-
const process = createProcessWithIO();
|
|
99
|
-
const command = new Command({ spawn: () => process });
|
|
100
|
-
|
|
101
|
-
command.stderr.subscribe(data => {
|
|
102
|
-
expect(data.toString()).toBe('dang');
|
|
103
|
-
done();
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
command.start();
|
|
107
|
-
process.stderr.emit('data', Buffer.from('dang'));
|
|
108
|
-
});
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
describe('#kill()', () => {
|
|
112
|
-
let process, killProcess, command;
|
|
113
|
-
beforeEach(() => {
|
|
114
|
-
process = createProcess();
|
|
115
|
-
killProcess = jest.fn();
|
|
116
|
-
command = new Command({ spawn: () => process, killProcess });
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
it('kills process', () => {
|
|
120
|
-
command.start();
|
|
121
|
-
command.kill();
|
|
122
|
-
|
|
123
|
-
expect(killProcess).toHaveBeenCalledTimes(1);
|
|
124
|
-
expect(killProcess).toHaveBeenCalledWith(command.pid, undefined);
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
it('kills process with some signal', () => {
|
|
128
|
-
command.start();
|
|
129
|
-
command.kill('SIGKILL');
|
|
130
|
-
|
|
131
|
-
expect(killProcess).toHaveBeenCalledTimes(1);
|
|
132
|
-
expect(killProcess).toHaveBeenCalledWith(command.pid, 'SIGKILL');
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
it('does not try to kill inexistent process', () => {
|
|
136
|
-
command.start();
|
|
137
|
-
process.emit('error');
|
|
138
|
-
command.kill();
|
|
139
|
-
|
|
140
|
-
expect(killProcess).not.toHaveBeenCalled();
|
|
141
|
-
});
|
|
142
|
-
});
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
const { TestScheduler } = require('rxjs/testing');
|
|
2
|
-
|
|
3
|
-
const createFakeCommand = require('./flow-control/fixtures/fake-command');
|
|
4
|
-
const CompletionListener = require('./completion-listener');
|
|
5
|
-
|
|
6
|
-
let commands, scheduler;
|
|
7
|
-
beforeEach(() => {
|
|
8
|
-
commands = [createFakeCommand('foo'), createFakeCommand('bar')];
|
|
9
|
-
scheduler = new TestScheduler();
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
const createController = successCondition => new CompletionListener({
|
|
13
|
-
successCondition,
|
|
14
|
-
scheduler
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
describe('with default success condition set', () => {
|
|
18
|
-
it('succeeds if all processes exited with code 0', () => {
|
|
19
|
-
const result = createController().listen(commands);
|
|
20
|
-
|
|
21
|
-
commands[0].close.next(0);
|
|
22
|
-
commands[1].close.next(0);
|
|
23
|
-
|
|
24
|
-
scheduler.flush();
|
|
25
|
-
|
|
26
|
-
return expect(result).resolves.toBeNull();
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it('fails if one of the processes exited with non-0 code', () => {
|
|
30
|
-
const result = createController().listen(commands);
|
|
31
|
-
|
|
32
|
-
commands[0].close.next(0);
|
|
33
|
-
commands[1].close.next(1);
|
|
34
|
-
|
|
35
|
-
scheduler.flush();
|
|
36
|
-
|
|
37
|
-
expect(result).rejects.toThrowError();
|
|
38
|
-
});
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
describe('with success condition set to first', () => {
|
|
43
|
-
it('succeeds if first process to exit has code 0', () => {
|
|
44
|
-
const result = createController('first').listen(commands);
|
|
45
|
-
|
|
46
|
-
commands[1].close.next(0);
|
|
47
|
-
commands[0].close.next(1);
|
|
48
|
-
|
|
49
|
-
scheduler.flush();
|
|
50
|
-
|
|
51
|
-
return expect(result).resolves.toBeNull();
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it('fails if first process to exit has non-0 code', () => {
|
|
55
|
-
const result = createController('first').listen(commands);
|
|
56
|
-
|
|
57
|
-
commands[1].close.next(1);
|
|
58
|
-
commands[0].close.next(0);
|
|
59
|
-
|
|
60
|
-
scheduler.flush();
|
|
61
|
-
|
|
62
|
-
return expect(result).rejects.toThrowError();
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
describe('with success condition set to last', () => {
|
|
67
|
-
it('succeeds if last process to exit has code 0', () => {
|
|
68
|
-
const result = createController('last').listen(commands);
|
|
69
|
-
|
|
70
|
-
commands[1].close.next(1);
|
|
71
|
-
commands[0].close.next(0);
|
|
72
|
-
|
|
73
|
-
scheduler.flush();
|
|
74
|
-
|
|
75
|
-
return expect(result).resolves.toBeNull();
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('fails if last process to exit has non-0 code', () => {
|
|
79
|
-
const result = createController('last').listen(commands);
|
|
80
|
-
|
|
81
|
-
commands[1].close.next(0);
|
|
82
|
-
commands[0].close.next(1);
|
|
83
|
-
|
|
84
|
-
scheduler.flush();
|
|
85
|
-
|
|
86
|
-
return expect(result).rejects.toThrowError();
|
|
87
|
-
});
|
|
88
|
-
});
|
package/src/concurrently.spec.js
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
const EventEmitter = require('events');
|
|
2
|
-
const { Subject } = require('rxjs');
|
|
3
|
-
|
|
4
|
-
const createFakeCommand = require('./flow-control/fixtures/fake-command');
|
|
5
|
-
const concurrently = require('./concurrently');
|
|
6
|
-
|
|
7
|
-
let spawn, kill, controllers;
|
|
8
|
-
beforeEach(() => {
|
|
9
|
-
const process = new EventEmitter();
|
|
10
|
-
process.pid = 1;
|
|
11
|
-
|
|
12
|
-
spawn = jest.fn(() => process);
|
|
13
|
-
kill = jest.fn();
|
|
14
|
-
controllers = [{ handle: jest.fn(arg => arg) }, { handle: jest.fn(arg => arg) }];
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
const create = (commands, options = {}) => concurrently(
|
|
18
|
-
commands,
|
|
19
|
-
Object.assign(options, { controllers, spawn, kill })
|
|
20
|
-
);
|
|
21
|
-
|
|
22
|
-
it('fails if commands is not an array', () => {
|
|
23
|
-
const bomb = () => create('foo');
|
|
24
|
-
expect(bomb).toThrowError();
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('fails if no commands were provided', () => {
|
|
28
|
-
const bomb = () => create([]);
|
|
29
|
-
expect(bomb).toThrowError();
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it('spawns all commands', () => {
|
|
33
|
-
create(['echo', 'kill']);
|
|
34
|
-
expect(spawn).toHaveBeenCalledTimes(2);
|
|
35
|
-
expect(spawn).toHaveBeenCalledWith('echo', expect.objectContaining({}));
|
|
36
|
-
expect(spawn).toHaveBeenCalledWith('kill', expect.objectContaining({}));
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it('runs controllers with the commands', () => {
|
|
40
|
-
create(['echo', '"echo wrapped"']);
|
|
41
|
-
|
|
42
|
-
controllers.forEach(controller => {
|
|
43
|
-
expect(controller.handle).toHaveBeenCalledWith([
|
|
44
|
-
expect.objectContaining({ command: 'echo', index: 0 }),
|
|
45
|
-
expect.objectContaining({ command: 'echo wrapped', index: 1 }),
|
|
46
|
-
]);
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it('runs commands with a name or prefix color', () => {
|
|
51
|
-
create([
|
|
52
|
-
{ command: 'echo', prefixColor: 'red', name: 'foo' },
|
|
53
|
-
'kill'
|
|
54
|
-
]);
|
|
55
|
-
|
|
56
|
-
controllers.forEach(controller => {
|
|
57
|
-
expect(controller.handle).toHaveBeenCalledWith([
|
|
58
|
-
expect.objectContaining({ command: 'echo', index: 0, name: 'foo', prefixColor: 'red' }),
|
|
59
|
-
expect.objectContaining({ command: 'kill', index: 1, name: '', prefixColor: '' }),
|
|
60
|
-
]);
|
|
61
|
-
});
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it('passes commands wrapped from a controller to the next one', () => {
|
|
65
|
-
const fakeCommand = createFakeCommand('banana', 'banana');
|
|
66
|
-
controllers[0].handle.mockReturnValue([fakeCommand]);
|
|
67
|
-
|
|
68
|
-
create(['echo']);
|
|
69
|
-
|
|
70
|
-
expect(controllers[0].handle).toHaveBeenCalledWith([
|
|
71
|
-
expect.objectContaining({ command: 'echo', index: 0 })
|
|
72
|
-
]);
|
|
73
|
-
|
|
74
|
-
expect(controllers[1].handle).toHaveBeenCalledWith([fakeCommand]);
|
|
75
|
-
|
|
76
|
-
expect(fakeCommand.start).toHaveBeenCalledTimes(1);
|
|
77
|
-
});
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
const EventEmitter = require('events');
|
|
2
|
-
const { createMockInstance } = require('jest-create-mock-instance');
|
|
3
|
-
|
|
4
|
-
const Logger = require('../logger');
|
|
5
|
-
const createFakeCommand = require('./fixtures/fake-command');
|
|
6
|
-
const InputHandler = require('./input-handler');
|
|
7
|
-
|
|
8
|
-
let commands, controller, inputStream, logger;
|
|
9
|
-
|
|
10
|
-
beforeEach(() => {
|
|
11
|
-
commands = [
|
|
12
|
-
createFakeCommand('foo', 'echo foo', 0),
|
|
13
|
-
createFakeCommand('bar', 'echo bar', 1),
|
|
14
|
-
];
|
|
15
|
-
inputStream = new EventEmitter();
|
|
16
|
-
logger = createMockInstance(Logger);
|
|
17
|
-
controller = new InputHandler({
|
|
18
|
-
defaultInputTarget: 0,
|
|
19
|
-
inputStream,
|
|
20
|
-
logger
|
|
21
|
-
});
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it('returns same commands', () => {
|
|
25
|
-
expect(controller.handle(commands)).toBe(commands);
|
|
26
|
-
|
|
27
|
-
controller = new InputHandler({ logger });
|
|
28
|
-
expect(controller.handle(commands)).toBe(commands);
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it('forwards input stream to default target ID', () => {
|
|
32
|
-
controller.handle(commands);
|
|
33
|
-
|
|
34
|
-
inputStream.emit('data', Buffer.from('something'));
|
|
35
|
-
|
|
36
|
-
expect(commands[0].stdin.write).toHaveBeenCalledTimes(1);
|
|
37
|
-
expect(commands[0].stdin.write).toHaveBeenCalledWith('something');
|
|
38
|
-
expect(commands[1].stdin.write).not.toHaveBeenCalled();
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it('forwards input stream to target index specified in input', () => {
|
|
42
|
-
controller.handle(commands);
|
|
43
|
-
|
|
44
|
-
inputStream.emit('data', Buffer.from('1:something'));
|
|
45
|
-
|
|
46
|
-
expect(commands[0].stdin.write).not.toHaveBeenCalled();
|
|
47
|
-
expect(commands[1].stdin.write).toHaveBeenCalledTimes(1);
|
|
48
|
-
expect(commands[1].stdin.write).toHaveBeenCalledWith('something');
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('forwards input stream to target name specified in input', () => {
|
|
52
|
-
controller.handle(commands);
|
|
53
|
-
|
|
54
|
-
inputStream.emit('data', Buffer.from('bar:something'));
|
|
55
|
-
|
|
56
|
-
expect(commands[0].stdin.write).not.toHaveBeenCalled();
|
|
57
|
-
expect(commands[1].stdin.write).toHaveBeenCalledTimes(1);
|
|
58
|
-
expect(commands[1].stdin.write).toHaveBeenCalledWith('something');
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('logs error if command has no stdin open', () => {
|
|
62
|
-
commands[0].stdin = null;
|
|
63
|
-
controller.handle(commands);
|
|
64
|
-
|
|
65
|
-
inputStream.emit('data', Buffer.from('something'));
|
|
66
|
-
|
|
67
|
-
expect(commands[1].stdin.write).not.toHaveBeenCalled();
|
|
68
|
-
expect(logger.logGlobalEvent).toHaveBeenCalledWith('Unable to find command 0, or it has no stdin open\n');
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it('logs error if command is not found', () => {
|
|
72
|
-
controller.handle(commands);
|
|
73
|
-
|
|
74
|
-
inputStream.emit('data', Buffer.from('foobar:something'));
|
|
75
|
-
|
|
76
|
-
expect(commands[0].stdin.write).not.toHaveBeenCalled();
|
|
77
|
-
expect(commands[1].stdin.write).not.toHaveBeenCalled();
|
|
78
|
-
expect(logger.logGlobalEvent).toHaveBeenCalledWith('Unable to find command foobar, or it has no stdin open\n');
|
|
79
|
-
});
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
const EventEmitter = require('events');
|
|
2
|
-
|
|
3
|
-
const createFakeCommand = require('./fixtures/fake-command');
|
|
4
|
-
const KillOnSignal = require('./kill-on-signal');
|
|
5
|
-
|
|
6
|
-
let commands, controller, process;
|
|
7
|
-
beforeEach(() => {
|
|
8
|
-
process = new EventEmitter();
|
|
9
|
-
commands = [
|
|
10
|
-
createFakeCommand(),
|
|
11
|
-
createFakeCommand(),
|
|
12
|
-
];
|
|
13
|
-
controller = new KillOnSignal({ process });
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it('returns commands that keep non-close streams from original commands', () => {
|
|
17
|
-
const newCommands = controller.handle(commands);
|
|
18
|
-
newCommands.forEach((newCommand, i) => {
|
|
19
|
-
expect(newCommand.close).not.toBe(commands[i].close);
|
|
20
|
-
expect(newCommand.error).toBe(commands[i].error);
|
|
21
|
-
expect(newCommand.stdout).toBe(commands[i].stdout);
|
|
22
|
-
expect(newCommand.stderr).toBe(commands[i].stderr);
|
|
23
|
-
});
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it('returns commands that map SIGINT to exit code 0', () => {
|
|
27
|
-
const newCommands = controller.handle(commands);
|
|
28
|
-
expect(newCommands).not.toBe(commands);
|
|
29
|
-
expect(newCommands).toHaveLength(commands.length);
|
|
30
|
-
|
|
31
|
-
const callback = jest.fn();
|
|
32
|
-
newCommands[0].close.subscribe(callback);
|
|
33
|
-
process.emit('SIGINT');
|
|
34
|
-
|
|
35
|
-
// A fake command's .kill() call won't trigger a close event automatically...
|
|
36
|
-
commands[0].close.next(1);
|
|
37
|
-
|
|
38
|
-
expect(callback).not.toHaveBeenCalledWith('SIGINT');
|
|
39
|
-
expect(callback).toHaveBeenCalledWith(0);
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it('returns commands that keep non-SIGINT exit codes', () => {
|
|
43
|
-
const newCommands = controller.handle(commands);
|
|
44
|
-
expect(newCommands).not.toBe(commands);
|
|
45
|
-
expect(newCommands).toHaveLength(commands.length);
|
|
46
|
-
|
|
47
|
-
const callback = jest.fn();
|
|
48
|
-
newCommands[0].close.subscribe(callback);
|
|
49
|
-
commands[0].close.next(1);
|
|
50
|
-
|
|
51
|
-
expect(callback).toHaveBeenCalledWith(1);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it('kills all commands on SIGINT', () => {
|
|
55
|
-
controller.handle(commands);
|
|
56
|
-
process.emit('SIGINT');
|
|
57
|
-
|
|
58
|
-
expect(process.listenerCount('SIGINT')).toBe(1);
|
|
59
|
-
expect(commands[0].kill).toHaveBeenCalledWith('SIGINT');
|
|
60
|
-
expect(commands[1].kill).toHaveBeenCalledWith('SIGINT');
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('kills all commands on SIGTERM', () => {
|
|
64
|
-
controller.handle(commands);
|
|
65
|
-
process.emit('SIGTERM');
|
|
66
|
-
|
|
67
|
-
expect(process.listenerCount('SIGTERM')).toBe(1);
|
|
68
|
-
expect(commands[0].kill).toHaveBeenCalledWith('SIGTERM');
|
|
69
|
-
expect(commands[1].kill).toHaveBeenCalledWith('SIGTERM');
|
|
70
|
-
});
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
const { createMockInstance } = require('jest-create-mock-instance');
|
|
2
|
-
|
|
3
|
-
const Logger = require('../logger');
|
|
4
|
-
const createFakeCommand = require('./fixtures/fake-command');
|
|
5
|
-
const KillOthers = require('./kill-others');
|
|
6
|
-
|
|
7
|
-
let commands, logger;
|
|
8
|
-
beforeEach(() => {
|
|
9
|
-
commands = [
|
|
10
|
-
createFakeCommand(),
|
|
11
|
-
createFakeCommand()
|
|
12
|
-
];
|
|
13
|
-
|
|
14
|
-
logger = createMockInstance(Logger);
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
const createWithConditions = conditions => new KillOthers({
|
|
18
|
-
logger,
|
|
19
|
-
conditions
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it('returns same commands', () => {
|
|
23
|
-
expect(createWithConditions(['foo']).handle(commands)).toBe(commands);
|
|
24
|
-
expect(createWithConditions(['failure']).handle(commands)).toBe(commands);
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('does not kill others if condition does not match', () => {
|
|
28
|
-
createWithConditions(['failure']).handle(commands);
|
|
29
|
-
commands[1].killable = true;
|
|
30
|
-
commands[0].close.next(0);
|
|
31
|
-
|
|
32
|
-
expect(logger.logGlobalEvent).not.toHaveBeenCalled();
|
|
33
|
-
expect(commands[0].kill).not.toHaveBeenCalled();
|
|
34
|
-
expect(commands[1].kill).not.toHaveBeenCalled();
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('kills other killable processes on success', () => {
|
|
38
|
-
createWithConditions(['success']).handle(commands);
|
|
39
|
-
commands[1].killable = true;
|
|
40
|
-
commands[0].close.next(0);
|
|
41
|
-
|
|
42
|
-
expect(logger.logGlobalEvent).toHaveBeenCalledTimes(1);
|
|
43
|
-
expect(logger.logGlobalEvent).toHaveBeenCalledWith('Sending SIGTERM to other processes..');
|
|
44
|
-
expect(commands[0].kill).not.toHaveBeenCalled();
|
|
45
|
-
expect(commands[1].kill).toHaveBeenCalled();
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it('kills other killable processes on failure', () => {
|
|
49
|
-
createWithConditions(['failure']).handle(commands);
|
|
50
|
-
commands[1].killable = true;
|
|
51
|
-
commands[0].close.next(1);
|
|
52
|
-
|
|
53
|
-
expect(logger.logGlobalEvent).toHaveBeenCalledTimes(1);
|
|
54
|
-
expect(logger.logGlobalEvent).toHaveBeenCalledWith('Sending SIGTERM to other processes..');
|
|
55
|
-
expect(commands[0].kill).not.toHaveBeenCalled();
|
|
56
|
-
expect(commands[1].kill).toHaveBeenCalled();
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it('does not try to kill processes already dead', () => {
|
|
60
|
-
createWithConditions(['failure']).handle(commands);
|
|
61
|
-
commands[0].close.next(1);
|
|
62
|
-
|
|
63
|
-
expect(logger.logGlobalEvent).not.toHaveBeenCalled();
|
|
64
|
-
expect(commands[0].kill).not.toHaveBeenCalled();
|
|
65
|
-
expect(commands[1].kill).not.toHaveBeenCalled();
|
|
66
|
-
});
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
const { createMockInstance } = require('jest-create-mock-instance');
|
|
2
|
-
const Logger = require('../logger');
|
|
3
|
-
const LogError = require('./log-error');
|
|
4
|
-
const createFakeCommand = require('./fixtures/fake-command');
|
|
5
|
-
|
|
6
|
-
let controller, logger, commands;
|
|
7
|
-
beforeEach(() => {
|
|
8
|
-
commands = [
|
|
9
|
-
createFakeCommand(),
|
|
10
|
-
createFakeCommand(),
|
|
11
|
-
];
|
|
12
|
-
|
|
13
|
-
logger = createMockInstance(Logger);
|
|
14
|
-
controller = new LogError({ logger });
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it('returns same commands', () => {
|
|
18
|
-
expect(controller.handle(commands)).toBe(commands);
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it('logs the error event of each command', () => {
|
|
22
|
-
controller.handle(commands);
|
|
23
|
-
commands[0].error.next('error from command 0');
|
|
24
|
-
|
|
25
|
-
const error = new Error('some error message');
|
|
26
|
-
commands[1].error.next(error);
|
|
27
|
-
|
|
28
|
-
expect(logger.logCommandEvent).toHaveBeenCalledTimes(4);
|
|
29
|
-
expect(logger.logCommandEvent).toHaveBeenCalledWith(
|
|
30
|
-
`Error occurred when executing command: ${commands[0].command}`,
|
|
31
|
-
commands[0]
|
|
32
|
-
);
|
|
33
|
-
expect(logger.logCommandEvent).toHaveBeenCalledWith('error from command 0', commands[0]);
|
|
34
|
-
|
|
35
|
-
expect(logger.logCommandEvent).toHaveBeenCalledWith(
|
|
36
|
-
`Error occurred when executing command: ${commands[1].command}`,
|
|
37
|
-
commands[1]
|
|
38
|
-
);
|
|
39
|
-
expect(logger.logCommandEvent).toHaveBeenCalledWith(error.stack, commands[1]);
|
|
40
|
-
});
|