Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add scope selector #118

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
256 changes: 145 additions & 111 deletions engine.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ module.exports = function(options) {
var types = options.types;

var length = longest(Object.keys(types)).length + 1;
var choices = map(types, function(type, key) {
var typeChoices = map(types, function(type, key) {
return {
name: (key + ':').padEnd(length) + ' ' + type.description,
value: key
Expand Down Expand Up @@ -72,122 +72,156 @@ module.exports = function(options) {
type: 'list',
name: 'type',
message: "Select the type of change that you're committing:",
choices: choices,
choices: typeChoices,
default: options.defaultType
},
{
type: 'input',
name: 'scope',
message:
'What is the scope of this change (e.g. component or file name): (press enter to skip)',
default: options.defaultScope,
filter: function(value) {
}
])
.then(function(firstAnswers) {
var scopes = options.allowedScopes;

var scopeChoices = scopes && scopes.length ?
[{
name: 'Empty',
value: undefined
}].concat(map(scopes, function(scope) {
return {
name: scope,
value: scope
};
}))
: null;
var filterScope = function(value) {
if (!value) {
return value;
}

return options.disableScopeLowerCase
? value.trim()
: value.trim().toLowerCase();
}
},
{
type: 'input',
name: 'subject',
message: function(answers) {
return (
'Write a short, imperative tense description of the change (max ' +
maxSummaryLength(options, answers) +
' chars):\n'
);
},
default: options.defaultSubject,
validate: function(subject, answers) {
var filteredSubject = filterSubject(subject, options.disableSubjectLowerCase);
return filteredSubject.length == 0
? 'subject is required'
: filteredSubject.length <= maxSummaryLength(options, answers)
? true
: 'Subject length must be less than or equal to ' +
maxSummaryLength(options, answers) +
' characters. Current length is ' +
filteredSubject.length +
' characters.';
},
transformer: function(subject, answers) {
var filteredSubject = filterSubject(subject, options.disableSubjectLowerCase);
var color =
filteredSubject.length <= maxSummaryLength(options, answers)
? chalk.green
: chalk.red;
return color('(' + filteredSubject.length + ') ' + subject);
},
filter: function(subject) {
return filterSubject(subject, options.disableSubjectLowerCase);
}
},
{
type: 'input',
name: 'body',
message:
'Provide a longer description of the change: (press enter to skip)\n',
default: options.defaultBody
},
{
type: 'confirm',
name: 'isBreaking',
message: 'Are there any breaking changes?',
default: false
},
{
type: 'input',
name: 'breakingBody',
default: '-',
message:
'A BREAKING CHANGE commit requires a body. Please enter a longer description of the commit itself:\n',
when: function(answers) {
return answers.isBreaking && !answers.body;
},
validate: function(breakingBody, answers) {
return (
breakingBody.trim().length > 0 ||
'Body is required for BREAKING CHANGE'
);
}
},
{
type: 'input',
name: 'breaking',
message: 'Describe the breaking changes:\n',
when: function(answers) {
return answers.isBreaking;
}
},

{
type: 'confirm',
name: 'isIssueAffected',
message: 'Does this change affect any open issues?',
default: options.defaultIssues ? true : false
},
{
type: 'input',
name: 'issuesBody',
default: '-',
message:
'If issues are closed, the commit requires a body. Please enter a longer description of the commit itself:\n',
when: function(answers) {
return (
answers.isIssueAffected && !answers.body && !answers.breakingBody
);
}
},
{
type: 'input',
name: 'issues',
message: 'Add issue references (e.g. "fix #123", "re #123".):\n',
when: function(answers) {
return answers.isIssueAffected;
},
default: options.defaultIssues ? options.defaultIssues : undefined
}
]).then(function(answers) {
var scopePrompt = scopeChoices ? {
type: 'list',
name: 'type',
message: "Select the scope of change that you're committing:",
choices: scopeChoices,
default: options.defaultScope,
filter: filterScope
} : {
type: 'input',
name: 'scope',
message:
'What is the scope of this change (e.g. component or file name): (press enter to skip)',
default: options.defaultScope,
filter: filterScope
};

return cz.prompt([
scopePrompt,
{
type: 'input',
name: 'subject',
message: function(answers) {
return (
'Write a short, imperative tense description of the change (max ' +
maxSummaryLength(options, answers) +
' chars):\n'
);
},
default: options.defaultSubject,
validate: function(subject, answers) {
var filteredSubject = filterSubject(subject, options.disableSubjectLowerCase);
return filteredSubject.length == 0
? 'subject is required'
: filteredSubject.length <= maxSummaryLength(options, answers)
? true
: 'Subject length must be less than or equal to ' +
maxSummaryLength(options, answers) +
' characters. Current length is ' +
filteredSubject.length +
' characters.';
},
transformer: function(subject, answers) {
var filteredSubject = filterSubject(subject, options.disableSubjectLowerCase);
var color =
filteredSubject.length <= maxSummaryLength(options, answers)
? chalk.green
: chalk.red;
return color('(' + filteredSubject.length + ') ' + subject);
},
filter: function(subject) {
return filterSubject(subject, options.disableSubjectLowerCase);
}
},
{
type: 'input',
name: 'body',
message:
'Provide a longer description of the change: (press enter to skip)\n',
default: options.defaultBody
},
{
type: 'confirm',
name: 'isBreaking',
message: 'Are there any breaking changes?',
default: false
},
{
type: 'input',
name: 'breakingBody',
default: '-',
message:
'A BREAKING CHANGE commit requires a body. Please enter a longer description of the commit itself:\n',
when: function(answers) {
return answers.isBreaking && !answers.body;
},
validate: function(breakingBody, answers) {
return (
breakingBody.trim().length > 0 ||
'Body is required for BREAKING CHANGE'
);
}
},
{
type: 'input',
name: 'breaking',
message: 'Describe the breaking changes:\n',
when: function(answers) {
return answers.isBreaking;
}
},

{
type: 'confirm',
name: 'isIssueAffected',
message: 'Does this change affect any open issues?',
default: options.defaultIssues ? true : false
},
{
type: 'input',
name: 'issuesBody',
default: '-',
message:
'If issues are closed, the commit requires a body. Please enter a longer description of the commit itself:\n',
when: function(answers) {
return (
answers.isIssueAffected && !answers.body && !answers.breakingBody
);
}
},
{
type: 'input',
name: 'issues',
message: 'Add issue references (e.g. "fix #123", "re #123".):\n',
when: function(answers) {
return answers.isIssueAffected;
},
default: options.defaultIssues ? options.defaultIssues : undefined
}
]).then(function(restOfAnswers) {
return Object.assign(firstAnswers, restOfAnswers);
});
}).then(function(answers) {
var wrapOptions = {
trim: true,
cut: false,
Expand Down
99 changes: 97 additions & 2 deletions engine.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,88 @@ describe('commitlint config header-max-length', function() {
});
}
});

describe('commitlint config scope-enum', function() {
//commitlint config parser only supports Node 6.0.0 and higher
if (semver.gte(process.version, '6.0.0')) {
function mockOptions(allowedScopes) {
var options = undefined;
mock('./engine', function(opts) {
options = opts;
});
if (allowedScopes) {
mock('cosmiconfig', function() {
return {
load: function(cwd) {
return {
filepath: cwd + '/.commitlintrc.js',
config: {
rules: {
'scope-enum': [2, 'always', allowedScopes]
}
}
};
}
};
});
}

mock.reRequire('./index');
try {
return mock
.reRequire('@commitlint/load')()
.then(function() {
return options;
});
} catch (err) {
return Promise.resolve(options);
}
}

afterEach(function() {
delete require.cache[require.resolve('./index')];
delete require.cache[require.resolve('@commitlint/load')];
delete process.env.CZ_ALLOWED_SCOPES;
mock.stopAll();
});

it('with no environment or commitizen config override', function() {
return mockOptions(['client', 'server']).then(function(options) {
expect(options).to.have.deep.property('allowedScopes', ['client', 'server']);
});
});

it('with environment variable override', function() {
process.env.CZ_ALLOWED_SCOPES = 'other,scopes';
return mockOptions(['client', 'server']).then(function(options) {
expect(options).to.have.deep.property('allowedScopes', ['other', 'scopes']);
});
});

it('with commitizen config override', function() {
mock('commitizen', {
configLoader: {
load: function() {
return {
allowedScopes: ['other', 'scopes']
};
}
}
});
return mockOptions(['client', 'server']).then(function(options) {
expect(options).to.have.deep.property('allowedScopes', ['other', 'scopes']);
});
});
} else {
//Node 4 doesn't support commitlint so the config value should remain the same
it('default value for Node 4', function() {
return mockOptions(['other', 'scopes']).then(function(options) {
expect(options).to.have.deep.property('allowedScopes', []);
});
});
}
});

function commitMessage(answers, options) {
options = options || defaultOptions;
var result = null;
Expand All @@ -490,6 +572,13 @@ function commitMessage(answers, options) {
then: function(finalizer) {
processQuestions(questions, answers, options);
finalizer(answers);

return {
then: function(finalizer) {
processQuestions(questions, answers, options);
finalizer(answers);
}
};
}
};
}
Expand Down Expand Up @@ -526,9 +615,15 @@ function getQuestions(options) {
var result = null;
engine(options).prompter({
prompt: function(questions) {
result = questions;
result = result ? result.concat(questions) : questions;
return {
then: function() {}
then: function(secondRun) {
secondRun({});

return {
then: function() {}
};
}
};
}
});
Expand Down
Loading