diff --git a/src/prompts/scope-maker.test.ts b/src/prompts/scope-maker.test.ts index ba86645..805b4d5 100644 --- a/src/prompts/scope-maker.test.ts +++ b/src/prompts/scope-maker.test.ts @@ -1,6 +1,6 @@ import { ListQuestion } from 'inquirer'; import { Rule, Case, Level, Rules } from '@commitlint/load'; -import { scopeMaker, filterFactory, validatorFactory } from './scope-maker'; +import { scopeMaker, filterFactory, validatorFactory, choicesFactory } from './scope-maker'; describe('scopeMaker', () => { describe('validatorFactory', () => { @@ -54,7 +54,7 @@ describe('scopeMaker', () => { }); describe('choices', () => { - test('should display choices if array scope enum is present', () => { + it('should display choices if array scope enum is present', () => { const scopeConfig = scopeMaker([], { 'scope-enum': [2, 'always', ['foo', 'bar']] })[0] as ListQuestion; if (scopeConfig.choices) { @@ -76,6 +76,38 @@ describe('scopeMaker', () => { }); }); + describe('choicesFactory', () => { + it('should not allow non-empty scope when empty scope is required', () => { + const scopeConfig = choicesFactory({ + 'scope-empty': [2, 'always', undefined] + }); + + expect(scopeConfig).toEqual([{ name: ':skip', value: '' }]); + }); + + it('should not allow skipping scope when is required', () => { + const scopeConfig = choicesFactory({ + 'scope-empty': [2, 'never', undefined] + }); + + expect(scopeConfig).not.toContainEqual({ name: ':skip', value: '' }); + }); + }); + + it('should allow skipping scope when "scope-empty" severity is "warn"', () => { + const scopeConfig = choicesFactory({ + 'scope-empty': [1, 'always', undefined] + }); + + expect(scopeConfig).toContainEqual({ name: ':skip', value: '' }); + }); + + it('should allow skipping scope when "scope-empty" rule is not set', () => { + const scopeConfig = choicesFactory({}); + + expect(scopeConfig).toContainEqual({ name: ':skip', value: '' }); + }); + describe('filterFactory', () => { test.each<[Rule, string, string]>([ [[Level.Error, 'always', 'camel-case'], 'FOO_BAR', 'fooBar'], diff --git a/src/prompts/scope-maker.ts b/src/prompts/scope-maker.ts index 337ea0e..dd0bb63 100644 --- a/src/prompts/scope-maker.ts +++ b/src/prompts/scope-maker.ts @@ -1,4 +1,4 @@ -import { Rules } from '@commitlint/load'; +import { Rules, Level } from '@commitlint/load'; import { ChoiceOptions } from 'inquirer'; import { whenFactory } from '../when'; import { caseValidator, emptyValidator, maxLengthValidator, minLengthValidator, validate } from '../validators'; @@ -36,14 +36,40 @@ export function validatorFactory(rules: Rules) { }; } -export function choicesFactory(rules: Rules) { - let choices: ChoiceOptions[] | undefined; - if (rules['scope-enum']) { - const [, , scopeEnum] = rules['scope-enum']; - if (scopeEnum && scopeEnum.length > 0) { - choices = [...scopeEnum.map(scope => ({ name: scope, value: scope })), { name: ':skip', value: '' }]; +function parseEmptyScopeRule(rule: Rules['scope-empty']): [boolean, ChoiceOptions | undefined] { + const skipChoice: ChoiceOptions = { name: ':skip', value: '' }; + if (rule !== undefined) { + const [level, applicability] = rule; + if (level === Level.Error) { + if (applicability === 'always') { + return [true, skipChoice]; + } + return [false, undefined]; } } + return [true, skipChoice]; +} + +function parseScopeEnumRule(rule: Rules['scope-enum']): [boolean, ChoiceOptions[] | undefined] { + if (rule !== undefined) { + const [, , scopeEnum] = rule; + return [true, scopeEnum.map(scope => ({ name: scope, value: scope }))]; + } + return [false, undefined]; +} + +export function choicesFactory(rules: Rules): ChoiceOptions[] | undefined { + const choices: ChoiceOptions[] = []; + + const [containsSkipChoice, skipChoice] = parseEmptyScopeRule(rules['scope-empty']); + if (containsSkipChoice) { + choices.push(skipChoice as ChoiceOptions); + } + + const [containsScopeEnumChoices, scopeEnumChoices] = parseScopeEnumRule(rules['scope-enum']); + if (containsScopeEnumChoices) { + choices.unshift(...(scopeEnumChoices as ChoiceOptions[])); + } return choices; }