Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Allow to specify command with builder pattern
  • Loading branch information
nikolai-laevskii committed Oct 12, 2023
commit 1581d81abad72a5c9135879d6b8f5062c119b553
91 changes: 60 additions & 31 deletions packages/helpers/__tests__/command-runner.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
*/
})
})
})
})
2 changes: 1 addition & 1 deletion packages/helpers/src/command-runner/command-runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
35 changes: 33 additions & 2 deletions packages/helpers/src/command-runner/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,39 @@ export class CommandRunnerBase {
private middleware: PromisifiedFn<CommandRunnerMiddleware>[] = []

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
Expand Down Expand Up @@ -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 => {
Expand Down Expand Up @@ -84,7 +115,7 @@ export class CommandRunnerBase {
}

export function composeMiddleware(
middleware: PromisifiedFn<CommandRunnerMiddleware>[]
middleware: CommandRunnerMiddleware[]
): PromisifiedFn<CommandRunnerMiddleware> {
middleware = middleware.map(mw => promisifyFn(mw))

Expand Down