Skip to content
Draft
Show file tree
Hide file tree
Changes from 2 commits
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
5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,15 @@
},
"dependencies": {
"@vercel/detect-agent": "^1.2.1",
"commander": "^10.0.1",
"cosmiconfig": "^9.0.0",
"execa": "^5.0.0",
"minimatch": "^3.0.4",
"p-graph": "^1.1.2",
"p-limit": "^3.0.2",
"prompts": "^2.4.2",
"semver": "^7.0.0",
"workspace-tools": "^0.41.0",
"yargs-parser": "^21.0.0"
"workspace-tools": "^0.41.0"
},
"devDependencies": {
"@jest/globals": "^29.0.0",
Expand All @@ -70,7 +70,6 @@
"@types/prompts": "^2.4.2",
"@types/semver": "^7.3.13",
"@types/tmp": "^0.2.3",
"@types/yargs-parser": "^21.0.0",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
"@typescript-eslint/utils": "^5.0.0",
Expand Down
70 changes: 28 additions & 42 deletions src/__functional__/options/getCliOptions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ jest.mock('workspace-tools', () => {

//
// These tests cover a mix of built-in parser behavior, provided options, and custom overrides.
// It's worth having tests for relevant built-in behaviors in case we change parsers in the future
// (likely to commander), to ensure there are no undocumented breaking changes from the beachball
// The parser is commander, and these tests document the expected behavior from the beachball
// "end user" perspective.
Comment thread
ecraig12345 marked this conversation as resolved.
Outdated
//
describe('getCliOptions', () => {
Expand Down Expand Up @@ -76,29 +75,19 @@ describe('getCliOptions', () => {
expect(options).toEqual({ ...defaults, scope: ['a,b', 'c,d'] });
});

it('throws if non-array option is specified multiple times', () => {
expect(() => getCliOptionsTest(['--tag', 'foo', '--tag', 'baz'])).toThrow();
it('uses last value if non-array option is specified multiple times', () => {
const options = getCliOptionsTest(['--tag', 'foo', '--tag', 'baz']);
expect(options).toEqual({ ...defaults, tag: 'baz' });
});

it('parses negated boolean option', () => {
const options = getCliOptionsTest(['--no-fetch']);
expect(options).toEqual({ ...defaults, fetch: false });
});

it('parses valid boolean option values', () => {
const falseOptions = getCliOptionsTest(['--fetch=false', '--yes', 'false']);
expect(falseOptions).toEqual({ ...defaults, fetch: false, yes: false });

const trueOptions = getCliOptionsTest(['--fetch=true', '--yes', 'true']);
expect(trueOptions).toEqual({ ...defaults, fetch: true, yes: true });
});

it('parses boolean flag with valid value', () => {
const falseOptions = getCliOptionsTest(['-y', 'false']);
expect(falseOptions).toEqual({ ...defaults, yes: false });

const trueOptions = getCliOptionsTest(['-y', 'true']);
expect(trueOptions).toEqual({ ...defaults, yes: true });
it('parses negated boolean options with --no-X syntax', () => {
const options = getCliOptionsTest(['--no-fetch', '--no-yes']);
expect(options).toEqual({ ...defaults, fetch: false, yes: false });
});

it('throws on invalid numeric value', () => {
Expand All @@ -110,10 +99,10 @@ describe('getCliOptions', () => {
expect(options).toEqual({ ...defaults, gitTags: true, dependentChangeType: 'patch' });
});

it('supports camel case for options defined as hyphenated', () => {
it('requires hyphenated form for multi-word options', () => {
const options = getCliOptionsTest([
'--gitTags',
'--dependentChangeType',
'--git-tags',
'--dependent-change-type',
'patch',
'--disallowed-change-types',
'major',
Expand All @@ -127,6 +116,12 @@ describe('getCliOptions', () => {
});
});

it('rejects camelCase form of multi-word options', () => {
Comment thread
ecraig12345 marked this conversation as resolved.
Outdated
expect(() => getCliOptionsTest(['--gitTags'])).toThrow();
expect(() => getCliOptionsTest(['--dependentChangeType', 'patch'])).toThrow();
expect(() => getCliOptionsTest(['--disallowedChangeTypes', 'major'])).toThrow();
});

it('parses short option aliases', () => {
const options = getCliOptionsTest(['publish', '-t', 'test', '-r', 'http://whatever', '-y']);
expect(options).toEqual({ ...defaults, command: 'publish', tag: 'test', registry: 'http://whatever', yes: true });
Expand Down Expand Up @@ -175,37 +170,28 @@ describe('getCliOptions', () => {
expect(getDefaultRemoteBranch).toHaveBeenCalledWith({ branch: 'foo', verbose: undefined, cwd: projectRoot });
});

it('preserves additional string options', () => {
const options = getCliOptionsTest(['--foo', 'bar', '--baz=qux']);
expect(options).toEqual({ ...defaults, foo: 'bar', baz: 'qux' });
it('throws on unknown string options', () => {
expect(() => getCliOptionsTest(['--foo', 'bar'])).toThrow();
});

it('handles additional boolean flags as booleans', () => {
const options = getCliOptionsTest(['--foo', '--no-bar']);
expect(options).toEqual({ ...defaults, foo: true, bar: false });
it('throws on unknown boolean flags', () => {
expect(() => getCliOptionsTest(['--foo'])).toThrow();
});

it('handles additional boolean text values as booleans', () => {
const options = getCliOptionsTest(['--foo', 'true', '--bar=false']);
expect(options).toEqual({ ...defaults, foo: true, bar: false });
it('throws on unknown negated boolean flags', () => {
expect(() => getCliOptionsTest(['--no-bar'])).toThrow();
});

it('handles additional numeric values as numbers', () => {
const options = getCliOptionsTest(['--foo', '1', '--bar=2']);
expect(options).toEqual({ ...defaults, foo: 1, bar: 2 });
it('throws on unknown option with value', () => {
Comment thread
ecraig12345 marked this conversation as resolved.
Outdated
expect(() => getCliOptionsTest(['--foo', 'true'])).toThrow();
});

it('handles additional option specified multiple times as array', () => {
const options = getCliOptionsTest(['--foo', 'bar', '--foo', 'baz']);
expect(options).toEqual({ ...defaults, foo: ['bar', 'baz'] });
it('throws on unknown option specified multiple times', () => {
expect(() => getCliOptionsTest(['--foo', 'bar', '--foo', 'baz'])).toThrow();
});

// documenting current behavior (doesn't have to stay this way)
it('for additional options, does not handle multiple values as part of array', () => {
// in this case the trailing value "baz" would be treated as the command since it's the first
// positional option
const options = getCliOptionsTest(['--foo', 'bar', 'baz']);
expect(options).toEqual({ ...defaults, foo: 'bar', command: 'baz' });
it('throws on unknown option followed by positional', () => {
expect(() => getCliOptionsTest(['--foo', 'bar', 'baz'])).toThrow();
});

describe('config command', () => {
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/packageManager/listPackageVersions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ describe('list npm versions', () => {
npmMock.setRegistryData({ foo: { 'dist-tags': { latest: '1.0.0', beta: '2.0.0-beta' } } });
const { packages, options } = getOptionsAndPackages({
packages: { foo: {} },
extraArgv: ['--authType', 'password', '--token', 'pass'],
extraArgv: ['--auth-type', 'password', '--token', 'pass'],
});
expect(options).toMatchObject({ authType: 'password', token: 'pass' });

Expand Down
Loading
Loading