123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365 |
- const readline = require('readline');
- const _ = require('lodash');
- const Rx = require('rxjs');
- const { buffer, map } = require('rxjs/operators');
- const spawn = require('spawn-command');
- const isWindows = process.platform === 'win32';
- const createKillMessage = prefix => new RegExp(
- _.escapeRegExp(prefix) +
- ' exited with code ' +
- (isWindows ? 1 : '(SIGTERM|143)')
- );
- const run = args => {
- const child = spawn('node ./concurrently.js ' + args, {
- cwd: __dirname,
- env: Object.assign({}, process.env, {
- // When upgrading from jest 23 -> 24, colors started printing in the test output.
- // They are forcibly disabled here
- FORCE_COLOR: 0
- }),
- });
- const stdout = readline.createInterface({
- input: child.stdout,
- output: null
- });
- const stderr = readline.createInterface({
- input: child.stderr,
- output: null
- });
- const close = Rx.fromEvent(child, 'close');
- const log = Rx.merge(
- Rx.fromEvent(stdout, 'line'),
- Rx.fromEvent(stderr, 'line')
- ).pipe(map(data => data.toString()));
- return {
- close,
- log,
- stdin: child.stdin,
- pid: child.pid
- };
- };
- it('has help command', done => {
- run('--help').close.subscribe(event => {
- expect(event[0]).toBe(0);
- done();
- }, done);
- });
- it('has version command', done => {
- Rx.combineLatest(
- run('--version').close,
- run('-V').close,
- run('-v').close
- ).subscribe(events => {
- expect(events[0][0]).toBe(0);
- expect(events[1][0]).toBe(0);
- expect(events[2][0]).toBe(0);
- done();
- }, done);
- });
- describe('exiting conditions', () => {
- it('is of success by default when running successful commands', done => {
- run('"echo foo" "echo bar"')
- .close
- .subscribe(exit => {
- expect(exit[0]).toBe(0);
- done();
- }, done);
- });
- it('is of failure by default when one of the command fails', done => {
- run('"echo foo" "exit 1"')
- .close
- .subscribe(exit => {
- expect(exit[0]).toBeGreaterThan(0);
- done();
- }, done);
- });
- it('is of success when --success=first and first command to exit succeeds', done => {
- run('--success=first "echo foo" "sleep 0.5 && exit 1"')
- .close
- .subscribe(exit => {
- expect(exit[0]).toBe(0);
- done();
- }, done);
- });
- it('is of failure when --success=first and first command to exit fails', done => {
- run('--success=first "exit 1" "sleep 0.5 && echo foo"')
- .close
- .subscribe(exit => {
- expect(exit[0]).toBeGreaterThan(0);
- done();
- }, done);
- });
- it('is of success when --success=last and last command to exit succeeds', done => {
- run('--success=last "exit 1" "sleep 0.5 && echo foo"')
- .close
- .subscribe(exit => {
- expect(exit[0]).toBe(0);
- done();
- }, done);
- });
- it('is of failure when --success=last and last command to exit fails', done => {
- run('--success=last "echo foo" "sleep 0.5 && exit 1"')
- .close
- .subscribe(exit => {
- expect(exit[0]).toBeGreaterThan(0);
- done();
- }, done);
- });
- it.skip('is of success when a SIGINT is sent', done => {
- const child = run('"node fixtures/read-echo.js"');
- child.close.subscribe(exit => {
- // TODO This is null within Node, but should be 0 outside (eg from real terminal)
- expect(exit[0]).toBe(0);
- done();
- }, done);
- process.kill(child.pid, 'SIGINT');
- });
- it('is aliased to -s', done => {
- run('-s last "exit 1" "sleep 0.5 && echo foo"')
- .close
- .subscribe(exit => {
- expect(exit[0]).toBe(0);
- done();
- }, done);
- });
- });
- describe('--raw', () => {
- it('is aliased to -r', done => {
- const child = run('-r "echo foo" "echo bar"');
- child.log.pipe(buffer(child.close)).subscribe(lines => {
- expect(lines).toHaveLength(2);
- expect(lines).toContainEqual(expect.stringContaining('foo'));
- expect(lines).toContainEqual(expect.stringContaining('bar'));
- done();
- }, done);
- });
- it('does not log any extra output', done => {
- const child = run('--raw "echo foo" "echo bar"');
- child.log.pipe(buffer(child.close)).subscribe(lines => {
- expect(lines).toHaveLength(2);
- expect(lines).toContainEqual(expect.stringContaining('foo'));
- expect(lines).toContainEqual(expect.stringContaining('bar'));
- done();
- }, done);
- });
- });
- describe('--names', () => {
- it('is aliased to -n', done => {
- const child = run('-n foo,bar "echo foo" "echo bar"');
- child.log.pipe(buffer(child.close)).subscribe(lines => {
- expect(lines).toContainEqual(expect.stringContaining('[foo] foo'));
- expect(lines).toContainEqual(expect.stringContaining('[bar] bar'));
- done();
- }, done);
- });
- it('prefixes with names', done => {
- const child = run('--names foo,bar "echo foo" "echo bar"');
- child.log.pipe(buffer(child.close)).subscribe(lines => {
- expect(lines).toContainEqual(expect.stringContaining('[foo] foo'));
- expect(lines).toContainEqual(expect.stringContaining('[bar] bar'));
- done();
- }, done);
- });
- it('is split using --name-separator arg', done => {
- const child = run('--names "foo|bar" --name-separator "|" "echo foo" "echo bar"');
- child.log.pipe(buffer(child.close)).subscribe(lines => {
- expect(lines).toContainEqual(expect.stringContaining('[foo] foo'));
- expect(lines).toContainEqual(expect.stringContaining('[bar] bar'));
- done();
- }, done);
- });
- });
- describe('--prefix', () => {
- it('is alised to -p', done => {
- const child = run('-p command "echo foo" "echo bar"');
- child.log.pipe(buffer(child.close)).subscribe(lines => {
- expect(lines).toContainEqual(expect.stringContaining('[echo foo] foo'));
- expect(lines).toContainEqual(expect.stringContaining('[echo bar] bar'));
- done();
- }, done);
- });
- it('specifies custom prefix', done => {
- const child = run('--prefix command "echo foo" "echo bar"');
- child.log.pipe(buffer(child.close)).subscribe(lines => {
- expect(lines).toContainEqual(expect.stringContaining('[echo foo] foo'));
- expect(lines).toContainEqual(expect.stringContaining('[echo bar] bar'));
- done();
- }, done);
- });
- });
- describe('--prefix-length', () => {
- it('is alised to -l', done => {
- const child = run('-p command -l 5 "echo foo" "echo bar"');
- child.log.pipe(buffer(child.close)).subscribe(lines => {
- expect(lines).toContainEqual(expect.stringContaining('[ec..o] foo'));
- expect(lines).toContainEqual(expect.stringContaining('[ec..r] bar'));
- done();
- }, done);
- });
- it('specifies custom prefix length', done => {
- const child = run('--prefix command --prefix-length 5 "echo foo" "echo bar"');
- child.log.pipe(buffer(child.close)).subscribe(lines => {
- expect(lines).toContainEqual(expect.stringContaining('[ec..o] foo'));
- expect(lines).toContainEqual(expect.stringContaining('[ec..r] bar'));
- done();
- }, done);
- });
- });
- describe('--restart-tries', () => {
- it('changes how many times a command will restart', done => {
- const child = run('--restart-tries 1 "exit 1"');
- child.log.pipe(buffer(child.close)).subscribe(lines => {
- expect(lines).toEqual([
- expect.stringContaining('[0] exit 1 exited with code 1'),
- expect.stringContaining('[0] exit 1 restarted'),
- expect.stringContaining('[0] exit 1 exited with code 1'),
- ]);
- done();
- }, done);
- });
- });
- describe('--kill-others', () => {
- it('is alised to -k', done => {
- const child = run('-k "sleep 10" "exit 0"');
- child.log.pipe(buffer(child.close)).subscribe(lines => {
- expect(lines).toContainEqual(expect.stringContaining('[1] exit 0 exited with code 0'));
- expect(lines).toContainEqual(expect.stringContaining('Sending SIGTERM to other processes'));
- expect(lines).toContainEqual(expect.stringMatching(createKillMessage('[0] sleep 10')));
- done();
- }, done);
- });
- it('kills on success', done => {
- const child = run('--kill-others "sleep 10" "exit 0"');
- child.log.pipe(buffer(child.close)).subscribe(lines => {
- expect(lines).toContainEqual(expect.stringContaining('[1] exit 0 exited with code 0'));
- expect(lines).toContainEqual(expect.stringContaining('Sending SIGTERM to other processes'));
- expect(lines).toContainEqual(expect.stringMatching(createKillMessage('[0] sleep 10')));
- done();
- }, done);
- });
- it('kills on failure', done => {
- const child = run('--kill-others "sleep 10" "exit 1"');
- child.log.pipe(buffer(child.close)).subscribe(lines => {
- expect(lines).toContainEqual(expect.stringContaining('[1] exit 1 exited with code 1'));
- expect(lines).toContainEqual(expect.stringContaining('Sending SIGTERM to other processes'));
- expect(lines).toContainEqual(expect.stringMatching(createKillMessage('[0] sleep 10')));
- done();
- }, done);
- });
- });
- describe('--kill-others-on-fail', () => {
- it('does not kill on success', done => {
- const child = run('--kill-others-on-fail "sleep 0.5" "exit 0"');
- child.log.pipe(buffer(child.close)).subscribe(lines => {
- expect(lines).toContainEqual(expect.stringContaining('[1] exit 0 exited with code 0'));
- expect(lines).toContainEqual(expect.stringContaining('[0] sleep 0.5 exited with code 0'));
- done();
- }, done);
- });
- it('kills on failure', done => {
- const child = run('--kill-others-on-fail "sleep 10" "exit 1"');
- child.log.pipe(buffer(child.close)).subscribe(lines => {
- expect(lines).toContainEqual(expect.stringContaining('[1] exit 1 exited with code 1'));
- expect(lines).toContainEqual(expect.stringContaining('Sending SIGTERM to other processes'));
- expect(lines).toContainEqual(expect.stringMatching(createKillMessage('[0] sleep 10')));
- done();
- }, done);
- });
- });
- describe('--handle-input', () => {
- it('is aliased to -i', done => {
- const child = run('-i "node fixtures/read-echo.js"');
- child.log.subscribe(line => {
- if (/READING/.test(line)) {
- child.stdin.write('stop\n');
- }
- if (/\[0\] stop/.test(line)) {
- done();
- }
- }, done);
- });
- it('forwards input to first process by default', done => {
- const child = run('--handle-input "node fixtures/read-echo.js"');
- child.log.subscribe(line => {
- if (/READING/.test(line)) {
- child.stdin.write('stop\n');
- }
- if (/\[0\] stop/.test(line)) {
- done();
- }
- }, done);
- });
- it('forwards input to process --default-input-target', done => {
- const lines = [];
- const child = run('-ki --default-input-target 1 "node fixtures/read-echo.js" "node fixtures/read-echo.js"');
- child.log.subscribe(line => {
- lines.push(line);
- if (/\[1\] READING/.test(line)) {
- child.stdin.write('stop\n');
- }
- }, done);
- child.close.subscribe(exit => {
- expect(exit[0]).toBeGreaterThan(0);
- expect(lines).toContainEqual(expect.stringContaining('[1] stop'));
- expect(lines).toContainEqual(expect.stringMatching(createKillMessage('[0] node fixtures/read-echo.js')));
- done();
- }, done);
- });
- it('forwards input to specified process', done => {
- const lines = [];
- const child = run('-ki "node fixtures/read-echo.js" "node fixtures/read-echo.js"');
- child.log.subscribe(line => {
- lines.push(line);
- if (/\[1\] READING/.test(line)) {
- child.stdin.write('1:stop\n');
- }
- }, done);
- child.close.subscribe(exit => {
- expect(exit[0]).toBeGreaterThan(0);
- expect(lines).toContainEqual(expect.stringContaining('[1] stop'));
- expect(lines).toContainEqual(expect.stringMatching(createKillMessage('[0] node fixtures/read-echo.js')));
- done();
- }, done);
- });
- });
|