Skip to content

Commit

Permalink
Add getOptionValueSourceWithGlobals for completeness. (#1832)
Browse files Browse the repository at this point in the history
  • Loading branch information
shadowspawn committed Dec 19, 2022
1 parent 0ae5b2f commit 5a201ec
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 57 deletions.
19 changes: 19 additions & 0 deletions lib/command.js
Expand Up @@ -814,6 +814,25 @@ Expecting one of '${allowedValues.join("', '")}'`);
return this._optionValueSources[key];
}

/**
* Get source of option value. See also .optsWithGlobals().
* Expected values are default | config | env | cli | implied
*
* @param {string} key
* @return {string}
*/

getOptionValueSourceWithGlobals(key) {
// global overwrites local, like optsWithGlobals
let source;
getCommandAndParents(this).forEach((cmd) => {
if (cmd.getOptionValueSource(key) !== undefined) {
source = cmd.getOptionValueSource(key);
}
});
return source;
}

/**
* Get user arguments from implied or explicit arguments.
* Side-effects: set _scriptPath if args included script. Used for default program name, and subcommand searches.
Expand Down
164 changes: 108 additions & 56 deletions tests/options.optsWithGlobals.test.js
@@ -1,63 +1,115 @@
const commander = require('../');

test('when variety of options used with program then opts is same as optsWithGlobals', () => {
const program = new commander.Command();
program
.option('-b, --boolean')
.option('-r, --require-value <value)')
.option('-f, --float <value>', 'description', parseFloat)
.option('-d, --default-value <value)', 'description', 'default value')
.option('-n, --no-something');

program.parse(['-b', '-r', 'req', '-f', '1e2'], { from: 'user' });
expect(program.opts()).toEqual(program.optsWithGlobals());
});
// Testing optsWithGlobals and getOptionValueSourceWithGlobals with focus on globals.

test('when options in sub and program then optsWithGlobals includes both', () => {
const program = new commander.Command();
let mergedOptions;
program
.option('-g, --global <value>');
program
.command('sub')
.option('-l, --local <value)')
.action((options, cmd) => {
mergedOptions = cmd.optsWithGlobals();
});

program.parse(['-g', 'GGG', 'sub', '-l', 'LLL'], { from: 'user' });
expect(mergedOptions).toEqual({ global: 'GGG', local: 'LLL' });
});
describe('optsWithGlobals', () => {
test('when variety of options used with program then opts is same as optsWithGlobals', () => {
const program = new commander.Command();
program
.option('-b, --boolean')
.option('-r, --require-value <value)')
.option('-f, --float <value>', 'description', parseFloat)
.option('-d, --default-value <value)', 'description', 'default value')
.option('-n, --no-something');

program.parse(['-b', '-r', 'req', '-f', '1e2'], { from: 'user' });
expect(program.opts()).toEqual(program.optsWithGlobals());
});

test('when options in sub and program then optsWithGlobals includes both', () => {
const program = new commander.Command();
let mergedOptions;
program
.option('-g, --global <value>');
program
.command('sub')
.option('-l, --local <value)')
.action((options, cmd) => {
mergedOptions = cmd.optsWithGlobals();
});

program.parse(['-g', 'GGG', 'sub', '-l', 'LLL'], { from: 'user' });
expect(mergedOptions).toEqual({ global: 'GGG', local: 'LLL' });
});

test('when options in sub and subsub then optsWithGlobals includes both', () => {
const program = new commander.Command();
let mergedOptions;
program
.command('sub')
.option('-g, --global <value)')
.command('subsub')
.option('-l, --local <value)')
.action((options, cmd) => {
mergedOptions = cmd.optsWithGlobals();
});

program.parse(['sub', '-g', 'GGG', 'subsub', '-l', 'LLL'], { from: 'user' });
expect(mergedOptions).toEqual({ global: 'GGG', local: 'LLL' });
});

test('when same named option in sub and program then optsWithGlobals includes global', () => {
const program = new commander.Command();
let mergedOptions;
program
.option('-c, --common <value>')
.enablePositionalOptions();
program
.command('sub')
.option('-c, --common <value)')
.action((options, cmd) => {
mergedOptions = cmd.optsWithGlobals();
});

test('when options in sub and subsub then optsWithGlobals includes both', () => {
const program = new commander.Command();
let mergedOptions;
program
.command('sub')
.option('-g, --global <value)')
.command('subsub')
.option('-l, --local <value)')
.action((options, cmd) => {
mergedOptions = cmd.optsWithGlobals();
});

program.parse(['sub', '-g', 'GGG', 'subsub', '-l', 'LLL'], { from: 'user' });
expect(mergedOptions).toEqual({ global: 'GGG', local: 'LLL' });
program.parse(['-c', 'GGG', 'sub', '-c', 'LLL'], { from: 'user' });
expect(mergedOptions).toEqual({ common: 'GGG' });
});
});

test('when same named option in sub and program then optsWithGlobals includes global', () => {
const program = new commander.Command();
let mergedOptions;
program
.option('-c, --common <value>')
.enablePositionalOptions();
program
.command('sub')
.option('-c, --common <value)')
.action((options, cmd) => {
mergedOptions = cmd.optsWithGlobals();
});

program.parse(['-c', 'GGG', 'sub', '-c', 'LLL'], { from: 'user' });
expect(mergedOptions).toEqual({ common: 'GGG' });
describe('getOptionValueSourceWithGlobals', () => {
test('when option used with simple command then source is defined', () => {
const program = new commander.Command();
program
.option('-g, --global');

program.parse(['-g'], { from: 'user' });
expect(program.getOptionValueSourceWithGlobals('global')).toEqual('cli');
});

test('when option used with program then source is defined', () => {
const program = new commander.Command();
program
.option('-g, --global');
const sub = program.command('sub')
.option('-l, --local')
.action(() => {});

program.parse(['sub', '-g'], { from: 'user' });
expect(sub.getOptionValueSourceWithGlobals('global')).toEqual('cli');
});

test('when option used with subcommand then source is defined', () => {
const program = new commander.Command();
program
.option('-g, --global');
const sub = program.command('sub')
.option('-l, --local')
.action(() => {});

program.parse(['sub', '-l'], { from: 'user' });
expect(sub.getOptionValueSourceWithGlobals('local')).toEqual('cli');
});

test('when same named option in sub and program then source is defined by global', () => {
const program = new commander.Command();
program
.enablePositionalOptions()
.option('-c, --common <value>', 'description', 'default value');
const sub = program.command('sub')
.option('-c, --common <value>')
.action(() => {});

program.parse(['sub', '--common', 'value'], { from: 'user' });
expect(sub.getOptionValueSourceWithGlobals('common')).toEqual('default');
});
});
7 changes: 6 additions & 1 deletion typings/index.d.ts
Expand Up @@ -600,10 +600,15 @@ export class Command {
setOptionValueWithSource(key: string, value: unknown, source: OptionValueSource): this;

/**
* Retrieve option value source.
* Get source of option value.
*/
getOptionValueSource(key: string): OptionValueSource | undefined;

/**
* Get source of option value. See also .optsWithGlobals().
*/
getOptionValueSourceWithGlobals(key: string): OptionValueSource | undefined;

/**
* Alter parsing of short flags with optional values.
*
Expand Down
3 changes: 3 additions & 0 deletions typings/index.test-d.ts
Expand Up @@ -174,6 +174,9 @@ expectType<commander.Command>(program.setOptionValueWithSource('example', [], 'c
// getOptionValueSource
expectType<commander.OptionValueSource | undefined>(program.getOptionValueSource('example'));

// getOptionValueSourceWithGlobals
expectType<commander.OptionValueSource | undefined>(program.getOptionValueSourceWithGlobals('example'));

// combineFlagAndOptionalValue
expectType<commander.Command>(program.combineFlagAndOptionalValue());
expectType<commander.Command>(program.combineFlagAndOptionalValue(false));
Expand Down

0 comments on commit 5a201ec

Please sign in to comment.