Skip to content

Commit

Permalink
Tweaks to allow for unscoped calls to scoping actions, allowing the d…
Browse files Browse the repository at this point in the history
…eveloper to decide if the scope is required
  • Loading branch information
D4N14L committed Apr 25, 2022
1 parent 9d117e9 commit d810a90
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 14 deletions.
1 change: 1 addition & 0 deletions common/reviews/api/ts-command-line.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,7 @@ export abstract class ScopedCommandLineAction extends CommandLineAction {
protected abstract onDefineScopedParameters(scopedParameterProvider: CommandLineParameterProvider): void;
protected abstract onDefineUnscopedParameters(): void;
protected abstract onExecute(): Promise<void>;
get parameters(): ReadonlyArray<CommandLineParameter>;
// @internal
_processParsedData(parserOptions: ICommandLineParserOptions, data: _ICommandLineParserData): void;
static ScopingParameterGroupName: 'scoping';
Expand Down
4 changes: 3 additions & 1 deletion libraries/ts-command-line/src/providers/CommandLineParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,9 @@ export abstract class CommandLineParser extends CommandLineParameterProvider {
// 0=node.exe, 1=script name
args = process.argv.slice(2);
}
if (args.length === 0) {
if (this.actions.length > 0 && args.length === 0) {
// Parsers that use actions should print help when 0 args are provided. Allow
// actionless parsers to continue on zero args.
this._argumentParser.printHelp();
return;
}
Expand Down
16 changes: 14 additions & 2 deletions libraries/ts-command-line/src/providers/ScopedCommandLineAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,10 @@ class InternalScopedCommandLineParser extends CommandLineParser {
// No-op. Parameters are manually defined in the constructor.
}

protected onExecute(): Promise<void> {
protected async onExecute(): Promise<void> {
await super.onExecute();
// Redirect action execution to the provided callback
return this._internalOptions.onExecute();
await this._internalOptions.onExecute();
}
}

Expand Down Expand Up @@ -87,6 +88,17 @@ export abstract class ScopedCommandLineAction extends CommandLineAction {
this._scopingParameters = [];
}

/**
* {@inheritDoc CommandLineParameterProvider.parameters}
*/
public get parameters(): ReadonlyArray<CommandLineParameter> {
if (this._scopedCommandLineParser) {
return [...super.parameters, ...this._scopedCommandLineParser.parameters];
} else {
return super.parameters;
}
}

/**
* {@inheritdoc CommandLineAction._processParsedData}
* @internal
Expand Down
18 changes: 18 additions & 0 deletions libraries/ts-command-line/src/test/ActionlessParser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { CommandLineFlagParameter } from '../parameters/CommandLineFlagParameter

class TestCommandLine extends CommandLineParser {
public flag!: CommandLineFlagParameter;
public done: boolean = false;

public constructor() {
super({
Expand All @@ -14,6 +15,11 @@ class TestCommandLine extends CommandLineParser {
});
}

protected async onExecute(): Promise<void> {
await super.onExecute();
this.done = true;
}

protected onDefineParameters(): void {
this.flag = this.defineFlagParameter({
parameterLongName: '--flag',
Expand All @@ -23,11 +29,22 @@ class TestCommandLine extends CommandLineParser {
}

describe(`Actionless ${CommandLineParser.name}`, () => {
it('parses an empty arg list', async () => {
const commandLineParser: TestCommandLine = new TestCommandLine();

await commandLineParser.execute([]);

expect(commandLineParser.done).toBe(true);
expect(commandLineParser.selectedAction).toBeUndefined();
expect(commandLineParser.flag.value).toBe(false);
});

it('parses a flag', async () => {
const commandLineParser: TestCommandLine = new TestCommandLine();

await commandLineParser.execute(['--flag']);

expect(commandLineParser.done).toBe(true);
expect(commandLineParser.selectedAction).toBeUndefined();
expect(commandLineParser.flag.value).toBe(true);
});
Expand All @@ -41,6 +58,7 @@ describe(`Actionless ${CommandLineParser.name}`, () => {

await commandLineParser.execute(['--flag', 'the', 'remaining', 'args']);

expect(commandLineParser.done).toBe(true);
expect(commandLineParser.selectedAction).toBeUndefined();
expect(commandLineParser.flag.value).toBe(true);
expect(commandLineParser.remainder!.values).toEqual(['the', 'remaining', 'args']);
Expand Down
62 changes: 52 additions & 10 deletions libraries/ts-command-line/src/test/ScopedCommandLineAction.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ import { ScopedCommandLineAction } from '../providers/ScopedCommandLineAction';
import { CommandLineStringParameter } from '../parameters/CommandLineStringParameter';
import { CommandLineParser } from '../providers/CommandLineParser';
import { CommandLineParameterProvider } from '../providers/CommandLineParameterProvider';
import { CommandLineFlagParameter } from '../parameters/CommandLineFlagParameter';

class TestScopedAction extends ScopedCommandLineAction {
public done: boolean = false;
public scopedValue: string | undefined;
private _verboseArg!: CommandLineFlagParameter;
private _scopeArg!: CommandLineStringParameter;
private _scopedArg!: CommandLineStringParameter;
private _scopedArg: CommandLineStringParameter | undefined;

public constructor() {
super({
Expand All @@ -23,12 +25,19 @@ class TestScopedAction extends ScopedCommandLineAction {
}

protected async onExecute(): Promise<void> {
expect(this._scopedArg.longName).toBe(`--scoped-${this._scopeArg.value}`);
this.scopedValue = this._scopedArg.value;
if (this._scopedArg) {
expect(this._scopedArg.longName).toBe(`--scoped-${this._scopeArg.value}`);
this.scopedValue = this._scopedArg.value;
}
this.done = true;
}

protected onDefineUnscopedParameters(): void {
this._verboseArg = this.defineFlagParameter({
parameterLongName: '--verbose',
description: 'A flag parameter.'
});

this._scopeArg = this.defineStringParameter({
parameterLongName: '--scope',
parameterGroupName: ScopedCommandLineAction.ScopingParameterGroupName,
Expand All @@ -38,11 +47,13 @@ class TestScopedAction extends ScopedCommandLineAction {
}

protected onDefineScopedParameters(scopedParameterProvider: CommandLineParameterProvider): void {
this._scopedArg = scopedParameterProvider.defineStringParameter({
parameterLongName: `--scoped-${this._scopeArg.value}`,
argumentName: 'SCOPED',
description: 'The scoped argument.'
});
if (this._scopeArg.value) {
this._scopedArg = scopedParameterProvider.defineStringParameter({
parameterLongName: `--scoped-${this._scopeArg.value}`,
argumentName: 'SCOPED',
description: 'The scoped argument.'
});
}
}
}

Expand Down Expand Up @@ -101,12 +112,43 @@ describe(CommandLineParser.name, () => {
const commandLineParser: TestCommandLine = new TestCommandLine();
// Execute the parser in order to populate the scoped action
await commandLineParser.execute(['scoped-action', '--scope', 'foo', '--', '--scoped-foo', 'bar']);
const unscopedAction: TestScopedAction & { _getScopedCommandLineParser(): CommandLineParser } =
const scopedAction: TestScopedAction & { _getScopedCommandLineParser(): CommandLineParser } =
commandLineParser.getAction('scoped-action') as TestScopedAction & {
_getScopedCommandLineParser(): CommandLineParser;
};
const scopedCommandLineParser: CommandLineParser = unscopedAction._getScopedCommandLineParser();
const scopedCommandLineParser: CommandLineParser = scopedAction._getScopedCommandLineParser();
const helpText: string = colors.stripColors(scopedCommandLineParser.renderHelpText());
expect(helpText).toMatchSnapshot();
});

it('prints the unscoped action parameter map', async () => {
const commandLineParser: TestCommandLine = new TestCommandLine();
// Execute the parser in order to populate the scoped action
await commandLineParser.execute(['scoped-action', '--verbose']);
const scopedAction: TestScopedAction = commandLineParser.getAction('scoped-action') as TestScopedAction;
expect(scopedAction.done).toBe(true);
expect(scopedAction.parameters.length).toBe(2);
const parameterStringMap: Record<string, string> = scopedAction.getParameterStringMap();
expect(parameterStringMap).toMatchSnapshot();
});

it('prints the scoped action parameter map', async () => {
let commandLineParser: TestCommandLine = new TestCommandLine();
// Execute the parser in order to populate the scoped action
await commandLineParser.execute(['scoped-action', '--scope', 'foo']);
let scopedAction: TestScopedAction = commandLineParser.getAction('scoped-action') as TestScopedAction;
expect(scopedAction.done).toBe(true);
expect(scopedAction.parameters.length).toBe(3);
let parameterStringMap: Record<string, string> = scopedAction.getParameterStringMap();
expect(parameterStringMap).toMatchSnapshot();

commandLineParser = new TestCommandLine();
// Execute the parser in order to populate the scoped action
await commandLineParser.execute(['scoped-action', '--scope', 'foo', '--', '--scoped-foo', 'bar']);
scopedAction = commandLineParser.getAction('scoped-action') as TestScopedAction;
expect(scopedAction.done).toBe(true);
expect(scopedAction.parameters.length).toBe(3);
parameterStringMap = scopedAction.getParameterStringMap();
expect(parameterStringMap).toMatchSnapshot();
});
});
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`CommandLineParser prints the action help 1`] = `
"usage: example scoped-action [-h] [--scope SCOPE] ...
"usage: example scoped-action [-h] [--verbose] [--scope SCOPE] ...
a longer description
Expand All @@ -12,6 +12,7 @@ Positional arguments:
Optional arguments:
-h, --help Show this help message and exit.
--verbose A flag parameter.
Optional scoping arguments:
--scope SCOPE The scope
Expand All @@ -32,6 +33,29 @@ scoped-action --help\\"
"
`;

exports[`CommandLineParser prints the scoped action parameter map 1`] = `
Object {
"--scope": "\\"foo\\"",
"--scoped-foo": undefined,
"--verbose": "false",
}
`;

exports[`CommandLineParser prints the scoped action parameter map 2`] = `
Object {
"--scope": "\\"foo\\"",
"--scoped-foo": "\\"bar\\"",
"--verbose": "false",
}
`;

exports[`CommandLineParser prints the unscoped action parameter map 1`] = `
Object {
"--scope": undefined,
"--verbose": "true",
}
`;

exports[`CommandLineParser throws on missing positional arg divider with unknown positional args 1`] = `"example scoped-action: error: Unrecognized arguments: bar."`;

exports[`CommandLineParser throws on unknown scoped arg 1`] = `
Expand Down

0 comments on commit d810a90

Please sign in to comment.