Skip to content

Commit 9819b52

Browse files
feat: execInteractive takes string or string[] (#637)
* feat: execInteractive takes string or string[] * chore(release): 5.2.4-qa.0 [skip ci] * fix: restore mutation of cmd * chore(release): 5.2.4-qa.1 [skip ci] * feat: throw an error message on commands with quotes to encourage the array form --------- Co-authored-by: svc-cli-bot <[email protected]>
1 parent f738ee3 commit 9819b52

File tree

2 files changed

+18
-7
lines changed

2 files changed

+18
-7
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@salesforce/cli-plugins-testkit",
33
"description": "Provides test utilities to assist Salesforce CLI plug-in authors with writing non-unit tests (NUT).",
4-
"version": "5.2.3",
4+
"version": "5.2.4-qa.1",
55
"author": "Salesforce",
66
"license": "BSD-3-Clause",
77
"main": "lib/index.js",

src/execCmd.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ export type ExecCmdResult<T> = {
6464
* Command execution duration.
6565
*/
6666
execCmdDuration: Duration;
67-
}
67+
};
6868

6969
const buildCmdOptions = (options?: ExecCmdOptions): ExecCmdOptions & { cwd: string } => {
7070
const defaults: ExecCmdOptions = {
@@ -89,9 +89,12 @@ const hrtimeToMillisDuration = (hrTime: [number, number]) =>
8989
const addJsonOutput = <T>(cmd: string, result: ExecCmdResult<T>, file: string): ExecCmdResult<T> => {
9090
if (cmd.includes('--json')) {
9191
try {
92-
result.jsonOutput = parseJson(stripAnsi(fs.readFileSync(file, 'utf-8'))) as unknown as JsonOutput<T>;
92+
return {
93+
...result,
94+
jsonOutput: parseJson(stripAnsi(fs.readFileSync(file, 'utf-8'))) as unknown as JsonOutput<T>,
95+
};
9396
} catch (parseErr: unknown) {
94-
result.jsonError = parseErr as Error;
97+
return { ...result, jsonError: parseErr as Error };
9598
}
9699
}
97100
return result;
@@ -363,22 +366,30 @@ export type PromptAnswers = Record<string, Many<string>>;
363366
* { cwd: session.dir, ensureExitCode: 0 }
364367
* );
365368
* ```
369+
*
370+
* If your flag values included spaces (where you'd normally need quotes like `some:cmd --flag "value with spaces"`),
371+
* use an array of strings to represent the command ex: `['some:cmd', '--flag', 'value with spaces']`
366372
*/
367373
export async function execInteractiveCmd(
368-
command: string,
374+
command: string | string[],
369375
answers: PromptAnswers,
370376
options: InteractiveCommandExecutionOptions = {}
371377
): Promise<InteractiveCommandExecutionResult> {
372378
const debug = Debug('testkit:execInteractiveCmd');
373379

374380
return new Promise((resolve, reject) => {
381+
if (typeof command === 'string' && command.includes('"')) {
382+
throw new Error(
383+
'Use an array of strings to represent the command when it includes quotes, ex: ["some:cmd", "--flag", "value with spaces"]'
384+
);
385+
}
375386
const bin = determineExecutable(options?.cli).trim();
376387
const startTime = process.hrtime();
377388
const opts =
378389
process.platform === 'win32'
379390
? { shell: true, cwd: process.cwd(), ...options }
380391
: { cwd: process.cwd(), ...options };
381-
const child = spawn(bin, command.split(' '), opts);
392+
const child = spawn(bin, Array.isArray(command) ? command : command.split(' '), opts);
382393
child.stdin.setDefaultEncoding('utf-8');
383394

384395
const seen = new Set<string>();
@@ -448,7 +459,7 @@ export async function execInteractiveCmd(
448459

449460
if (isNumber(options.ensureExitCode) && code !== options.ensureExitCode) {
450461
reject(
451-
getExitCodeError(command, options.ensureExitCode, {
462+
getExitCodeError(Array.isArray(command) ? command.join(' ') : command, options.ensureExitCode, {
452463
stdout: result.stdout,
453464
stderr: result.stderr,
454465
code: result.code,

0 commit comments

Comments
 (0)