From 72496eb52fe681b87f838d4973b1bba23f4dee38 Mon Sep 17 00:00:00 2001 From: Cedric van Putten Date: Fri, 25 Jan 2019 01:50:28 +0100 Subject: [PATCH] feat: create header-case rule --- @commitlint/rules/src/header-case.js | 34 +++ @commitlint/rules/src/header-case.test.js | 344 ++++++++++++++++++++++ @commitlint/rules/src/index.js | 1 + 3 files changed, 379 insertions(+) create mode 100644 @commitlint/rules/src/header-case.js create mode 100644 @commitlint/rules/src/header-case.test.js diff --git a/@commitlint/rules/src/header-case.js b/@commitlint/rules/src/header-case.js new file mode 100644 index 00000000000..779f4a5e97c --- /dev/null +++ b/@commitlint/rules/src/header-case.js @@ -0,0 +1,34 @@ +import * as ensure from '@commitlint/ensure'; +import message from '@commitlint/message'; + +const negated = when => when === 'never'; + +export default (parsed, when, value) => { + const {header} = parsed; + + if (typeof header !== 'string' || !header.match(/^[a-z]/i)) { + return [true]; + } + + const checks = (Array.isArray(value) ? value : [value]).map(check => { + if (typeof check === 'string') { + return { + when: 'always', + case: check + }; + } + return check; + }); + + const result = checks.some(check => { + const r = ensure.case(header, check.case); + return negated(check.when) ? !r : r; + }); + + const list = checks.map(c => c.case).join(', '); + + return [ + negated(when) ? !result : result, + message([`header must`, negated(when) ? `not` : null, `be ${list}`]) + ]; +}; diff --git a/@commitlint/rules/src/header-case.test.js b/@commitlint/rules/src/header-case.test.js new file mode 100644 index 00000000000..cbb8473870a --- /dev/null +++ b/@commitlint/rules/src/header-case.test.js @@ -0,0 +1,344 @@ +import test from 'ava'; +import parse from '@commitlint/parse'; +import headerCase from './header-case'; + +const messages = { + empty: '\n', + numeric: '1.0.0', + lowercase: 'header test', + mixedcase: 'hEaDeR tEsT', + uppercase: 'HEADER TEST', + camelcase: 'heaDer teSt', + kebabcase: 'hea-der te-st', + pascalcase: 'HeaDer TeSt', + snakecase: 'hea_der te_st', + startcase: 'Hea Der Te St' +}; + +const parsed = { + empty: parse(messages.empty), + numeric: parse(messages.numeric), + lowercase: parse(messages.lowercase), + mixedcase: parse(messages.mixedcase), + uppercase: parse(messages.uppercase), + camelcase: parse(messages.camelcase), + kebabcase: parse(messages.kebabcase), + pascalcase: parse(messages.pascalcase), + snakecase: parse(messages.snakecase), + startcase: parse(messages.startcase) +}; + +test('with empty header should succeed for "never lowercase"', async t => { + const [actual] = headerCase(await parsed.empty, 'never', 'lowercase'); + const expected = true; + t.is(actual, expected); +}); + +test('with empty header should succeed for "always lowercase"', async t => { + const [actual] = headerCase(await parsed.empty, 'always', 'lowercase'); + const expected = true; + t.is(actual, expected); +}); + +test('with empty header should succeed for "never uppercase"', async t => { + const [actual] = headerCase(await parsed.empty, 'never', 'uppercase'); + const expected = true; + t.is(actual, expected); +}); + +test('with empty header should succeed for "always uppercase"', async t => { + const [actual] = headerCase(await parsed.empty, 'always', 'uppercase'); + const expected = true; + t.is(actual, expected); +}); + +test('with lowercase header should fail for "never lowercase"', async t => { + const [actual] = headerCase(await parsed.lowercase, 'never', 'lowercase'); + const expected = false; + t.is(actual, expected); +}); + +test('with lowercase header should succeed for "always lowercase"', async t => { + const [actual] = headerCase(await parsed.lowercase, 'always', 'lowercase'); + const expected = true; + t.is(actual, expected); +}); + +test('with mixedcase header should succeed for "never lowercase"', async t => { + const [actual] = headerCase(await parsed.mixedcase, 'never', 'lowercase'); + const expected = true; + t.is(actual, expected); +}); + +test('with mixedcase header should fail for "always lowercase"', async t => { + const [actual] = headerCase(await parsed.mixedcase, 'always', 'lowercase'); + const expected = false; + t.is(actual, expected); +}); + +test('with mixedcase header should succeed for "never uppercase"', async t => { + const [actual] = headerCase(await parsed.mixedcase, 'never', 'uppercase'); + const expected = true; + t.is(actual, expected); +}); + +test('with mixedcase header should fail for "always uppercase"', async t => { + const [actual] = headerCase(await parsed.mixedcase, 'always', 'uppercase'); + const expected = false; + t.is(actual, expected); +}); + +test('with uppercase header should fail for "never uppercase"', async t => { + const [actual] = headerCase(await parsed.uppercase, 'never', 'uppercase'); + const expected = false; + t.is(actual, expected); +}); + +test('with lowercase header should succeed for "always uppercase"', async t => { + const [actual] = headerCase(await parsed.uppercase, 'always', 'uppercase'); + const expected = true; + t.is(actual, expected); +}); + +test('with camelcase header should fail for "always uppercase"', async t => { + const [actual] = headerCase(await parsed.camelcase, 'always', 'uppercase'); + const expected = false; + t.is(actual, expected); +}); + +test('with camelcase header should succeed for "never uppercase"', async t => { + const [actual] = headerCase(await parsed.camelcase, 'never', 'uppercase'); + const expected = true; + t.is(actual, expected); +}); + +test('with camelcase header should fail for "always pascalcase"', async t => { + const [actual] = headerCase(await parsed.camelcase, 'always', 'pascal-case'); + const expected = false; + t.is(actual, expected); +}); + +test('with camelcase header should fail for "always kebabcase"', async t => { + const [actual] = headerCase(await parsed.camelcase, 'always', 'kebab-case'); + const expected = false; + t.is(actual, expected); +}); + +test('with camelcase header should fail for "always snakecase"', async t => { + const [actual] = headerCase(await parsed.camelcase, 'always', 'snake-case'); + const expected = false; + t.is(actual, expected); +}); + +test('with camelcase header should succeed for "always camelcase"', async t => { + const [actual] = headerCase(await parsed.camelcase, 'always', 'camel-case'); + const expected = true; + t.is(actual, expected); +}); + +test('with pascalcase header should fail for "always uppercase"', async t => { + const [actual] = headerCase(await parsed.pascalcase, 'always', 'uppercase'); + const expected = false; + t.is(actual, expected); +}); + +test('with pascalcase header should succeed for "never uppercase"', async t => { + const [actual] = headerCase(await parsed.pascalcase, 'never', 'uppercase'); + const expected = true; + t.is(actual, expected); +}); + +test('with pascalcase header should succeed for "always pascalcase"', async t => { + const [actual] = headerCase(await parsed.pascalcase, 'always', 'pascal-case'); + const expected = true; + t.is(actual, expected); +}); + +test('with pascalcase header should fail for "always kebabcase"', async t => { + const [actual] = headerCase(await parsed.pascalcase, 'always', 'kebab-case'); + const expected = false; + t.is(actual, expected); +}); + +test('with pascalcase header should fail for "always snakecase"', async t => { + const [actual] = headerCase(await parsed.pascalcase, 'always', 'snake-case'); + const expected = false; + t.is(actual, expected); +}); + +test('with pascalcase header should fail for "always camelcase"', async t => { + const [actual] = headerCase(await parsed.pascalcase, 'always', 'camel-case'); + const expected = false; + t.is(actual, expected); +}); + +test('with snakecase header should fail for "always uppercase"', async t => { + const [actual] = headerCase(await parsed.snakecase, 'always', 'uppercase'); + const expected = false; + t.is(actual, expected); +}); + +test('with snakecase header should succeed for "never uppercase"', async t => { + const [actual] = headerCase(await parsed.snakecase, 'never', 'uppercase'); + const expected = true; + t.is(actual, expected); +}); + +test('with snakecase header should fail for "always pascalcase"', async t => { + const [actual] = headerCase(await parsed.snakecase, 'always', 'pascal-case'); + const expected = false; + t.is(actual, expected); +}); + +test('with snakecase header should fail for "always kebabcase"', async t => { + const [actual] = headerCase(await parsed.snakecase, 'always', 'kebab-case'); + const expected = false; + t.is(actual, expected); +}); + +test('with snakecase header should succeed for "always snakecase"', async t => { + const [actual] = headerCase(await parsed.snakecase, 'always', 'snake-case'); + const expected = true; + t.is(actual, expected); +}); + +test('with snakecase header should fail for "always camelcase"', async t => { + const [actual] = headerCase(await parsed.snakecase, 'always', 'camel-case'); + const expected = false; + t.is(actual, expected); +}); + +test('with startcase header should fail for "always uppercase"', async t => { + const [actual] = headerCase(await parsed.startcase, 'always', 'uppercase'); + const expected = false; + t.is(actual, expected); +}); + +test('with startcase header should succeed for "never uppercase"', async t => { + const [actual] = headerCase(await parsed.startcase, 'never', 'uppercase'); + const expected = true; + t.is(actual, expected); +}); + +test('with startcase header should fail for "always pascalcase"', async t => { + const [actual] = headerCase(await parsed.startcase, 'always', 'pascal-case'); + const expected = false; + t.is(actual, expected); +}); + +test('with startcase header should fail for "always kebabcase"', async t => { + const [actual] = headerCase(await parsed.startcase, 'always', 'kebab-case'); + const expected = false; + t.is(actual, expected); +}); + +test('with startcase header should fail for "always snakecase"', async t => { + const [actual] = headerCase(await parsed.startcase, 'always', 'snake-case'); + const expected = false; + t.is(actual, expected); +}); + +test('with startcase header should fail for "always camelcase"', async t => { + const [actual] = headerCase(await parsed.startcase, 'always', 'camel-case'); + const expected = false; + t.is(actual, expected); +}); + +test('with startcase header should succeed for "always startcase"', async t => { + const [actual] = headerCase(await parsed.startcase, 'always', 'start-case'); + const expected = true; + t.is(actual, expected); +}); + +test('should use expected message with "always"', async t => { + const [, message] = headerCase( + await parsed.uppercase, + 'always', + 'lower-case' + ); + t.true(message.indexOf('must be lower-case') > -1); +}); + +test('should use expected message with "never"', async t => { + const [, message] = headerCase(await parsed.uppercase, 'never', 'upper-case'); + t.true(message.indexOf('must not be upper-case') > -1); +}); + +test('with uppercase scope should succeed for "always [uppercase, lowercase]"', async t => { + const [actual] = headerCase(await parsed.uppercase, 'always', [ + 'uppercase', + 'lowercase' + ]); + const expected = true; + t.is(actual, expected); +}); + +test('with lowercase header should succeed for "always [uppercase, lowercase]"', async t => { + const [actual] = headerCase(await parsed.lowercase, 'always', [ + 'uppercase', + 'lowercase' + ]); + const expected = true; + t.is(actual, expected); +}); + +test('with mixedcase header should fail for "always [uppercase, lowercase]"', async t => { + const [actual] = headerCase(await parsed.mixedcase, 'always', [ + 'uppercase', + 'lowercase' + ]); + const expected = false; + t.is(actual, expected); +}); + +test('with mixedcase header should pass for "always [uppercase, lowercase, camel-case]"', async t => { + const [actual] = headerCase(await parsed.mixedcase, 'always', [ + 'uppercase', + 'lowercase', + 'camel-case' + ]); + const expected = true; + t.is(actual, expected); +}); + +test('with mixedcase scope should pass for "never [uppercase, lowercase]"', async t => { + const [actual] = headerCase(await parsed.mixedcase, 'never', [ + 'uppercase', + 'lowercase' + ]); + const expected = true; + t.is(actual, expected); +}); + +test('with uppercase scope should fail for "never [uppercase, lowercase]"', async t => { + const [actual] = headerCase(await parsed.uppercase, 'never', [ + 'uppercase', + 'lowercase' + ]); + const expected = false; + t.is(actual, expected); +}); + +test('with numeric header should succeed for "never lowercase"', async t => { + const [actual] = headerCase(await parsed.numeric, 'never', 'lowercase'); + const expected = true; + t.is(actual, expected); +}); + +test('with numeric header should succeed for "always lowercase"', async t => { + const [actual] = headerCase(await parsed.numeric, 'always', 'lowercase'); + const expected = true; + t.is(actual, expected); +}); + +test('with numeric header should succeed for "never uppercase"', async t => { + const [actual] = headerCase(await parsed.numeric, 'never', 'uppercase'); + const expected = true; + t.is(actual, expected); +}); + +test('with numeric header should succeed for "always uppercase"', async t => { + const [actual] = headerCase(await parsed.numeric, 'always', 'uppercase'); + const expected = true; + t.is(actual, expected); +}); diff --git a/@commitlint/rules/src/index.js b/@commitlint/rules/src/index.js index 5779959b0b3..4d61033f57f 100644 --- a/@commitlint/rules/src/index.js +++ b/@commitlint/rules/src/index.js @@ -10,6 +10,7 @@ export default { 'footer-max-length': require('./footer-max-length'), 'footer-max-line-length': require('./footer-max-line-length'), 'footer-min-length': require('./footer-min-length'), + 'header-case': require('./header-case'), 'header-full-stop': require('./header-full-stop'), 'header-max-length': require('./header-max-length'), 'header-min-length': require('./header-min-length'),