diff --git a/@commitlint/config-conventional/README.md b/@commitlint/config-conventional/README.md index 7185e71ccb7..e000230f2d5 100644 --- a/@commitlint/config-conventional/README.md +++ b/@commitlint/config-conventional/README.md @@ -27,6 +27,7 @@ Consult [docs/rules](https://marionebl.github.io/commitlint/#/reference-rules) f #### type-enum * **condition**: `type` is found in value * **rule**: `always` +* level: `error` * **value** ```js @@ -53,6 +54,7 @@ echo "fix: some message" # passes #### type-case * **description**: `type` is in case `value` * **rule**: `always` +* level: `error` * **value** ```js 'lowerCase' @@ -66,6 +68,7 @@ echo "fix: some message" # passes #### type-empty * **condition**: `type` is empty * **rule**: `never` +* level: `error` ```sh echo ": some message" # fails @@ -75,6 +78,7 @@ echo "fix: some message" # passes #### scope-case * **condition**: `scope` is in case `value` * **rule**: `always` +* level: `error` ```js 'lowerCase' ``` @@ -87,6 +91,7 @@ echo "fix(scope): some message" # passes #### subject-case * **condition**: `subject` is in one of the cases `['sentence-case', 'start-case', 'pascal-case', 'upper-case']` * **rule**: `never` +* level: `error` ```sh echo "fix(SCOPE): Some message" # fails @@ -100,6 +105,7 @@ echo "fix(scope): some Message" # passes #### subject-empty * **condition**: `subject` is empty * **rule**: `never` +* level: `error` ```sh echo "fix:" # fails @@ -109,6 +115,7 @@ echo "fix: some message" # passes #### subject-full-stop * **condition**: `subject` ends with `value` * **rule**: `never` +* level: `error` * **value** ```js '.' @@ -119,10 +126,10 @@ echo "fix: some message." # fails echo "fix: some message" # passes ``` - #### header-max-length * **condition**: `header` has `value` or less characters * **rule**: `always` +* level: `error` * **value** ```js 72 @@ -132,3 +139,82 @@ echo "fix: some message" # passes echo "fix: some message that is way too long and breaks the line max-length by several characters" # fails echo "fix: some message" # passes ``` + +#### footer-leading-blank +* **condition**: `footer` should have a leading blank line +* **rule**: `always` +* level: `warning` +* **value** +```js + 72 +``` + +```sh +echo "fix: some message +BREAKING CHANGE: It will be significant" # warning + +echo "fix: some message + +BREAKING CHANGE: It will be significant" # passes +``` + +#### footer-max-line-length +* **condition**: `footer` each line has `value` or less characters +* **rule**: `always` +* level: `error` +* **value** +```js + 100 +``` + +```sh +echo "fix: some message + +BREAKING CHANGE: footer with multiple lines +has a message that is way too long and will break the line rule 'line-max-length' by several characters" # fails + +echo "fix: some message + +BREAKING CHANGE: footer with multiple lines +but still no line is too long" # passes +``` + +#### body-leading-blank +* **condition**: `body` should have a leading blank line +* **rule**: `always` +* level: `warning` +* **value** +```js + 72 +``` + +```sh +echo "fix: some message +body" # warning + +echo "fix: some message + +body" # passes +``` + +#### body-max-line-length +* **condition**: `body` each line has `value` or less characters +* **rule**: `always` +* level: `error` +* **value** +```js + 100 +``` + +```sh +echo "fix: some message + +body with multiple lines +has a message that is way too long and will break the line rule 'line-max-length' by several characters" # fails + +echo "fix: some message + +body with multiple lines +but still no line is too long" # passes +``` + diff --git a/@commitlint/config-conventional/index.js b/@commitlint/config-conventional/index.js index f26ad5a57f6..30b044fc336 100644 --- a/@commitlint/config-conventional/index.js +++ b/@commitlint/config-conventional/index.js @@ -1,7 +1,9 @@ module.exports = { rules: { 'body-leading-blank': [1, 'always'], + 'body-max-line-length': [2, 'always', 100], 'footer-leading-blank': [1, 'always'], + 'footer-max-line-length': [2, 'always', 100], 'header-max-length': [2, 'always', 72], 'scope-case': [2, 'always', 'lower-case'], 'subject-case': [ diff --git a/@commitlint/config-conventional/index.test.js b/@commitlint/config-conventional/index.test.js new file mode 100644 index 00000000000..62e639adffd --- /dev/null +++ b/@commitlint/config-conventional/index.test.js @@ -0,0 +1,214 @@ +import test from 'ava'; +import lint from '@commitlint/lint'; +import {rules} from '.'; + +const messages = { + invalidTypeEnum: 'foo: some message', + invalidTypeCase: 'FIX: some message', + invalidTypeEmpty: ': some message', + invalidScopeCase: 'fix(SCOPE): some message', + invalidSubjectCases: [ + 'fix(scope): Some message', + 'fix(scope): Some Message', + 'fix(scope): SomeMessage', + 'fix(scope): SOMEMESSAGE' + ], + invalidSubjectEmpty: 'fix:', + invalidSubjectFullStop: 'fix: some message.', + invalidHeaderMaxLength: + 'fix: some message that is way too long and breaks the line max-length by several characters', + warningFooterLeadingBlank: + 'fix: some message\n\nbody\nBREAKING CHANGE: It will be significant', + invalidFooterMaxLineLength: + 'fix: some message\n\nbody\n\nBREAKING CHANGE: footer with multiple lines\nhas a message that is way too long and will break the line rule "line-max-length" by several characters', + warningBodyLeadingBlank: 'fix: some message\nbody', + invalidBodyMaxLineLength: + 'fix: some message\n\nbody with multiple lines\nhas a message that is way too long and will break the line rule "line-max-length" by several characters', + validMessages: [ + 'fix: some message', + 'fix(scope): some message', + 'fix(scope): some Message', + 'fix(scope): some message\n\nBREAKING CHANGE: it will be significant!', + 'fix(scope): some message\n\nbody' + ] +}; + +const errors = { + typeEnum: { + level: 2, + message: + 'type must be one of [build, chore, ci, docs, feat, fix, perf, refactor, revert, style, test]', + name: 'type-enum', + valid: false + }, + typeCase: { + level: 2, + message: 'type must be lower-case', + name: 'type-case', + valid: false + }, + typeEmpty: { + level: 2, + message: 'type may not be empty', + name: 'type-empty', + valid: false + }, + scopeCase: { + level: 2, + message: 'scope must be lower-case', + name: 'scope-case', + valid: false + }, + subjectCase: { + level: 2, + message: + 'subject must not be sentence-case, start-case, pascal-case, upper-case', + name: 'subject-case', + valid: false + }, + subjectEmpty: { + level: 2, + message: 'message may not be empty', + name: 'subject-empty', + valid: false + }, + subjectFullStop: { + level: 2, + message: 'message may not end with full stop', + name: 'subject-full-stop', + valid: false + }, + headerMaxLength: { + level: 2, + message: 'header must not be longer than 72 characters', + name: 'header-max-length', + valid: false + }, + footerMaxLineLength: { + level: 2, + message: "footer's lines must not be longer than 100 characters", + name: 'footer-max-line-length', + valid: false + }, + bodyMaxLineLength: { + level: 2, + message: "body's lines must not be longer than 100 characters", + name: 'body-max-line-length', + valid: false + } +}; + +const warnings = { + footerLeadingBlank: { + level: 1, + message: 'footer must have leading blank line', + name: 'footer-leading-blank', + valid: false + }, + bodyLeadingBlank: { + level: 1, + message: 'body must have leading blank line', + name: 'body-leading-blank', + valid: false + } +}; + +test('type-enum', async t => { + const result = await lint(messages.invalidTypeEnum, rules); + + t.is(result.valid, false); + t.deepEqual(result.errors, [errors.typeEnum]); +}); + +test('type-case', async t => { + const result = await lint(messages.invalidTypeCase, rules); + + t.is(result.valid, false); + t.deepEqual(result.errors, [errors.typeCase, errors.typeEnum]); +}); + +test('type-empty', async t => { + const result = await lint(messages.invalidTypeEmpty, rules); + + t.is(result.valid, false); + t.deepEqual(result.errors, [errors.typeEmpty]); +}); + +test('scope-case', async t => { + const result = await lint(messages.invalidScopeCase, rules); + + t.is(result.valid, false); + t.deepEqual(result.errors, [errors.scopeCase]); +}); + +test('subject-case', async t => { + const invalidInputs = await Promise.all( + messages.invalidSubjectCases.map(invalidInput => lint(invalidInput, rules)) + ); + + invalidInputs.forEach(result => { + t.is(result.valid, false); + t.deepEqual(result.errors, [errors.subjectCase]); + }); +}); + +test('subject-empty', async t => { + const result = await lint(messages.invalidSubjectEmpty, rules); + + t.is(result.valid, false); + t.deepEqual(result.errors, [errors.subjectEmpty, errors.typeEmpty]); +}); + +test('subject-full-stop', async t => { + const result = await lint(messages.invalidSubjectFullStop, rules); + + t.is(result.valid, false); + t.deepEqual(result.errors, [errors.subjectFullStop]); +}); + +test('header-max-length', async t => { + const result = await lint(messages.invalidHeaderMaxLength, rules); + + t.is(result.valid, false); + t.deepEqual(result.errors, [errors.headerMaxLength]); +}); + +test('footer-leading-blank', async t => { + const result = await lint(messages.warningFooterLeadingBlank, rules); + + t.is(result.valid, true); + t.deepEqual(result.warnings, [warnings.footerLeadingBlank]); +}); + +test('footer-max-line-length', async t => { + const result = await lint(messages.invalidFooterMaxLineLength, rules); + + t.is(result.valid, false); + t.deepEqual(result.errors, [errors.footerMaxLineLength]); +}); + +test('body-leading-blank', async t => { + const result = await lint(messages.warningBodyLeadingBlank, rules); + + t.is(result.valid, true); + t.deepEqual(result.warnings, [warnings.bodyLeadingBlank]); +}); + +test('body-max-line-length', async t => { + const result = await lint(messages.invalidBodyMaxLineLength, rules); + + t.is(result.valid, false); + t.deepEqual(result.errors, [errors.bodyMaxLineLength]); +}); + +test('valid messages', async t => { + const validInputs = await Promise.all( + messages.validMessages.map(input => lint(input, rules)) + ); + + validInputs.forEach(result => { + t.is(result.valid, true); + t.deepEqual(result.errors, []); + t.deepEqual(result.warnings, []); + }); +}); diff --git a/@commitlint/config-conventional/package.json b/@commitlint/config-conventional/package.json index 6487eedb4e5..c9c9097d024 100644 --- a/@commitlint/config-conventional/package.json +++ b/@commitlint/config-conventional/package.json @@ -11,7 +11,24 @@ "pkg": "pkg-check", "lint": "xo", "start": "exit 0", - "test": "exit 0" + "test": "ava -c 4 --verbose" + }, + "ava": { + "files": [ + "*.test.js" + ], + "source": [ + "*.js" + ], + "babel": "inherit", + "require": [ + "babel-register" + ] + }, + "babel": { + "presets": [ + "babel-preset-commitlint" + ] }, "xo": false, "publishConfig": { @@ -34,6 +51,9 @@ }, "homepage": "https://github.com/marionebl/commitlint#readme", "devDependencies": { - "@commitlint/utils": "^7.1.2" + "@commitlint/lint": "^7.1.2", + "@commitlint/utils": "^7.1.2", + "ava": "0.22.0", + "babel-cli": "6.26.0" } }