diff --git a/src/parser/validate.ts b/src/parser/validate.ts index be3ec1571..d9c242158 100644 --- a/src/parser/validate.ts +++ b/src/parser/validate.ts @@ -82,7 +82,7 @@ export async function validate(parse: { function getPresentFlags(flags: Record): string[] { return Object.keys(flags).reduce((acc, key) => { - if (flags[key]) acc.push(key) + if (flags[key] !== undefined) acc.push(key) return acc }, [] as string[]) } @@ -113,7 +113,7 @@ export async function validate(parse: { continue if (parse.output.metadata.flags && parse.output.metadata.flags[name]?.setFromDefault) continue - if (parse.output.flags[flag]) { + if (parse.output.flags[flag] !== undefined) { return {...base, status: 'failed', reason: `--${flag}=${parse.output.flags[flag]} cannot also be provided when using --${name}`} } } @@ -126,7 +126,7 @@ export async function validate(parse: { const resolved = await resolveFlags(flags) const keys = getPresentFlags(resolved) for (const flag of keys) { - if (flag !== name && parse.output.flags[flag]) { + if (flag !== name && parse.output.flags[flag] !== undefined) { return {...base, status: 'failed', reason: `--${flag} cannot also be provided when using --${name}`} } } @@ -137,7 +137,7 @@ export async function validate(parse: { async function validateDependsOn(name: string, flags: FlagRelationship[]): Promise { const base = {name, validationFn: 'validateDependsOn'} const resolved = await resolveFlags(flags) - const foundAll = Object.values(resolved).every(Boolean) + const foundAll = Object.values(resolved).every(val => val !== undefined) if (!foundAll) { const formattedFlags = Object.keys(resolved).map(f => `--${f}`).join(', ') return {...base, status: 'failed', reason: `All of the following must be provided when using --${name}: ${formattedFlags}`} diff --git a/test/parser/validate.test.ts b/test/parser/validate.test.ts index a64123bc0..61183bec6 100644 --- a/test/parser/validate.test.ts +++ b/test/parser/validate.test.ts @@ -748,6 +748,88 @@ describe('validate', () => { }) }) + it('should pass if the specified flags whose when property resolves to true, flag has a false value', async () => { + const input = { + argv: [], + flags: { + cookies: {input: [], name: 'cookies'}, + sprinkles: {input: [], name: 'sprinkles'}, + dessert: { + input: [], + name: 'dessert', + relationships: [ + { + type: 'all', + flags: [ + 'sprinkles', + {name: 'cookies', when: async () => Promise.resolve(true)}, + ], + }, + ], + }, + }, + args: [], + strict: true, + context: {}, + '--': true, + } + + const output = { + args: {}, + argv: [], + flags: {sprinkles: true, dessert: 'ice-cream', cookies: false}, + raw: [ + {type: 'flag', flag: 'sprinkles', input: true}, + {type: 'flag', flag: 'dessert', input: 'ice-cream'}, + {type: 'flag', flag: 'cookies', input: false}, + ], + metadata: {}, + } + + // @ts-expect-error + await validate({input, output}) + }) + + it('should fail if the specified flags whose when property resolves to true in exclusive, flag has a false value', async () => { + const input = { + argv: [], + flags: { + cookies: {input: [], name: 'cookies'}, + sprinkles: {input: [], name: 'sprinkles'}, + dessert: { + input: [], + name: 'dessert', + exclusive: [{name: 'cookies', when: async () => Promise.resolve(true)}], + }, + }, + args: [], + strict: true, + context: {}, + '--': true, + } + + const output = { + args: {}, + argv: [], + flags: {sprinkles: true, dessert: 'ice-cream', cookies: false}, + raw: [ + {type: 'flag', flag: 'sprinkles', input: true}, + {type: 'flag', flag: 'dessert', input: 'ice-cream'}, + {type: 'flag', flag: 'cookies', input: false}, + ], + metadata: {}, + } + + try { + // @ts-expect-error + await validate({input, output}) + assert.fail('should have thrown') + } catch (error) { + const err = error as CLIError + expect(err.message).to.include('--cookies=false cannot also be provided when using --dessert') + } + }) + describe('mixed', () => { const input = { argv: [],