concurrently
Version:
Run commands concurrently
117 lines (116 loc) • 5.68 kB
JavaScript
import { Buffer } from 'node:buffer';
import { PassThrough } from 'node:stream';
import { beforeEach, expect, it } from 'vitest';
import { createMockInstance } from '../__fixtures__/create-mock-instance.js';
import { FakeCommand } from '../__fixtures__/fake-command.js';
import { Logger } from '../logger.js';
import { InputHandler } from './input-handler.js';
let commands;
let controller;
let inputStream;
let logger;
beforeEach(() => {
commands = [new FakeCommand('foo', 'echo foo', 0), new FakeCommand('bar', 'echo bar', 1)];
inputStream = new PassThrough();
logger = createMockInstance(Logger);
controller = new InputHandler({
defaultInputTarget: 0,
inputStream,
logger,
});
});
it('returns same commands', () => {
expect(controller.handle(commands)).toMatchObject({ commands });
controller = new InputHandler({ logger, inputStream });
expect(controller.handle(commands)).toMatchObject({ commands });
});
it('does nothing if called without input stream', () => {
new InputHandler({
defaultInputTarget: 0,
inputStream: undefined,
logger,
}).handle(commands);
inputStream.write('something');
expect(commands[0].stdin?.write).not.toHaveBeenCalled();
});
it('forwards input stream to default target ID', () => {
controller.handle(commands);
inputStream.write('something');
expect(commands[0].stdin?.write).toHaveBeenCalledExactlyOnceWith('something');
expect(commands[1].stdin?.write).not.toHaveBeenCalled();
});
it('forwards input stream to target index specified in input', () => {
controller.handle(commands);
inputStream.write('1:something');
inputStream.write('1:multi\nline\n');
expect(commands[0].stdin?.write).not.toHaveBeenCalled();
expect(commands[1].stdin?.write).toHaveBeenCalledTimes(2);
expect(commands[1].stdin?.write).toHaveBeenCalledWith('something');
expect(commands[1].stdin?.write).toHaveBeenCalledWith('multi\nline\n');
});
it('forwards input stream to target index specified in input when input contains colon', () => {
controller.handle(commands);
inputStream.emit('data', Buffer.from('1:some:thing'));
inputStream.emit('data', Buffer.from('1: :something'));
inputStream.emit('data', Buffer.from('1::something'));
expect(commands[0].stdin?.write).not.toHaveBeenCalled();
expect(commands[1].stdin?.write).toHaveBeenCalledTimes(3);
expect(commands[1].stdin?.write).toHaveBeenCalledWith('some:thing');
expect(commands[1].stdin?.write).toHaveBeenCalledWith(' :something');
expect(commands[1].stdin?.write).toHaveBeenCalledWith(':something');
});
it('does not forward input stream when input contains colon in a different format', () => {
controller.handle(commands);
inputStream.emit('data', Buffer.from('Ruby0::Const::Syntax'));
inputStream.emit('data', Buffer.from('1:Ruby1::Const::Syntax'));
inputStream.emit('data', Buffer.from('ruby_symbol_arg :my_symbol'));
inputStream.emit('data', Buffer.from('ruby_symbol_arg(:my_symbol)'));
inputStream.emit('data', Buffer.from('{ruby_key: :my_val}'));
inputStream.emit('data', Buffer.from('{:ruby_key=>:my_val}'));
inputStream.emit('data', Buffer.from('js_obj = {key: "my_val"}'));
expect(commands[1].stdin?.write).toHaveBeenCalledExactlyOnceWith('Ruby1::Const::Syntax');
expect(commands[0].stdin?.write).toHaveBeenCalledTimes(6);
expect(commands[0].stdin?.write).toHaveBeenCalledWith('Ruby0::Const::Syntax');
expect(commands[0].stdin?.write).toHaveBeenCalledWith('ruby_symbol_arg :my_symbol');
expect(commands[0].stdin?.write).toHaveBeenCalledWith('ruby_symbol_arg(:my_symbol)');
expect(commands[0].stdin?.write).toHaveBeenCalledWith('{ruby_key: :my_val}');
expect(commands[0].stdin?.write).toHaveBeenCalledWith('{:ruby_key=>:my_val}');
expect(commands[0].stdin?.write).toHaveBeenCalledWith('js_obj = {key: "my_val"}');
});
it('forwards input stream to target name specified in input', () => {
controller.handle(commands);
inputStream.write('bar:something');
expect(commands[0].stdin?.write).not.toHaveBeenCalled();
expect(commands[1].stdin?.write).toHaveBeenCalledExactlyOnceWith('something');
});
it('logs error if command has no stdin open', () => {
commands[0].stdin = undefined;
controller.handle(commands);
inputStream.write('something');
expect(commands[1].stdin?.write).not.toHaveBeenCalled();
expect(logger.logGlobalEvent).toHaveBeenCalledWith('Unable to find command "0", or it has no stdin open\n');
});
it('fallback to default input stream if command is not found', () => {
controller.handle(commands);
inputStream.write('foobar:something');
expect(commands[0].stdin?.write).toHaveBeenCalledExactlyOnceWith('foobar:something');
expect(commands[1].stdin?.write).not.toHaveBeenCalled();
expect(logger.logGlobalEvent).not.toHaveBeenCalled();
});
it('pauses input stream when finished', () => {
expect(inputStream.readableFlowing).toBeNull();
const { onFinish } = controller.handle(commands);
expect(inputStream.readableFlowing).toBe(true);
expect(onFinish).toBeDefined();
onFinish?.();
expect(inputStream.readableFlowing).toBe(false);
});
it('does not pause input stream when pauseInputStreamOnFinish is set to false', () => {
controller = new InputHandler({ logger, inputStream, pauseInputStreamOnFinish: false });
expect(inputStream.readableFlowing).toBeNull();
const { onFinish } = controller.handle(commands);
expect(inputStream.readableFlowing).toBe(true);
expect(onFinish).toBeDefined();
onFinish?.();
expect(inputStream.readableFlowing).toBe(true);
});