From 1581d81abad72a5c9135879d6b8f5062c119b553 Mon Sep 17 00:00:00 2001 From: Nikolai Laevskii Date: Thu, 12 Oct 2023 07:30:25 +0200 Subject: [PATCH] Allow to specify command with builder pattern --- .../helpers/__tests__/command-runner.test.ts | 91 ++++++++++++------- .../src/command-runner/command-runner.ts | 2 +- packages/helpers/src/command-runner/core.ts | 35 ++++++- 3 files changed, 94 insertions(+), 34 deletions(-) diff --git a/packages/helpers/__tests__/command-runner.test.ts b/packages/helpers/__tests__/command-runner.test.ts index d4ec6746..e7f70973 100644 --- a/packages/helpers/__tests__/command-runner.test.ts +++ b/packages/helpers/__tests__/command-runner.test.ts @@ -36,6 +36,66 @@ describe('command-runner', () => { ) }) + it('throws error if command is not specified', async () => { + const command = createCommandRunner() + await expect(command.run()).rejects.toThrow('Command was not specified') + }) + + it('will have exec error if it occured', async () => { + execSpy.mockImplementation(async () => { + throw new Error('test') + }) + + const command = createCommandRunner('echo', ['hello', 'world'], { + silent: true + }) + const context = await command.run() + + expect(context.execerr).toBeDefined() + expect(context.execerr?.message).toBe('test') + }) + + it('allows to set command, args and options', async () => { + execSpy.mockImplementation(async () => 0) + + createCommandRunner() + .setCommand('echo') + .setArgs(['hello', 'world']) + .setOptions({silent: true}) + .run() + + expect(execSpy).toHaveBeenCalledTimes(1) + expect(execSpy).toHaveBeenCalledWith( + 'echo', + ['hello', 'world'], + expect.objectContaining({ + silent: true, + ignoreReturnCode: true + }) + ) + }) + + it('allows to modify command, args and options', async () => { + execSpy.mockImplementation(async () => 0) + + createCommandRunner('echo', ['hello', 'world'], {silent: true}) + .setCommand(commandLine => `${commandLine} hello world`) + .setArgs(() => []) + .setOptions(options => ({...options, env: {test: 'test'}})) + .run() + + expect(execSpy).toHaveBeenCalledTimes(1) + expect(execSpy).toHaveBeenCalledWith( + 'echo hello world', + [], + expect.objectContaining({ + silent: true, + ignoreReturnCode: true, + env: {test: 'test'} + }) + ) + }) + const createExecMock = (output: { stdout: string stderr: string @@ -139,37 +199,6 @@ describe('command-runner', () => { expect(middleware).toHaveBeenCalledTimes(1) }) - - it('runs a middleware on multiple events', async () => { - execSpy.mockImplementation( - createExecMock({stdout: 'foo', stderr: '', exitCode: 1}) - ) - /* execSpy.mockImplementation( - createExecMock({stdout: '', stderr: '', exitCode: 1}) - ) - - const middleware = jest.fn() - const command = createCommandRunner('echo', ['hello', 'world'], { - silent: true - }).on(['!no-stdout', 'ok'], middleware) - - await command.run() - - expect(middleware).toHaveBeenCalledTimes(1) - - execSpy.mockImplementation(async () => { - return { - stdout: '', - stderr: '', - exitCode: 1 - } - }) - - await command.run() - - expect(middleware).toHaveBeenCalledTimes(1) - */ - }) }) }) }) diff --git a/packages/helpers/src/command-runner/command-runner.ts b/packages/helpers/src/command-runner/command-runner.ts index a4622d4a..6b2f625a 100644 --- a/packages/helpers/src/command-runner/command-runner.ts +++ b/packages/helpers/src/command-runner/command-runner.ts @@ -137,7 +137,7 @@ export class CommandRunner extends CommandRunnerBase { } export const createCommandRunner = ( - commandLine: string, + commandLine = '', args: string[] = [], options: CommandRunnerOptions = {} ): CommandRunner => new CommandRunner(commandLine, args, options, exec.exec) diff --git a/packages/helpers/src/command-runner/core.ts b/packages/helpers/src/command-runner/core.ts index 631a80bc..7989693a 100644 --- a/packages/helpers/src/command-runner/core.ts +++ b/packages/helpers/src/command-runner/core.ts @@ -11,12 +11,39 @@ export class CommandRunnerBase { private middleware: PromisifiedFn[] = [] constructor( - private commandLine: string, + private commandLine = '', private args: string[] = [], private options: CommandRunnerOptions, private executor: typeof exec.exec = exec.exec ) {} + setCommand(commandLine: string | ((commandLine: string) => string)): this { + this.commandLine = + typeof commandLine === 'function' + ? commandLine(this.commandLine) + : commandLine + + return this + } + + setArgs(args: string[] | ((args: string[]) => string[])): this { + this.args = + typeof args === 'function' ? args(this.args) : [...this.args, ...args] + + return this + } + + setOptions( + options: + | CommandRunnerOptions + | ((options: CommandRunnerOptions) => CommandRunnerOptions) + ): this { + this.options = + typeof options === 'function' ? options(this.options) : options + + return this + } + use(middleware: CommandRunnerMiddleware): this { this.middleware.push(promisifyFn(middleware)) return this @@ -47,6 +74,10 @@ export class CommandRunnerBase { exitCode: null } + if (!context.commandLine) { + throw new Error('Command was not specified') + } + try { const stderrDecoder = new StringDecoder('utf8') const stdErrListener = (data: Buffer): void => { @@ -84,7 +115,7 @@ export class CommandRunnerBase { } export function composeMiddleware( - middleware: PromisifiedFn[] + middleware: CommandRunnerMiddleware[] ): PromisifiedFn { middleware = middleware.map(mw => promisifyFn(mw))