Skip to content

Commit

Permalink
style: validate rule configuration
Browse files Browse the repository at this point in the history
BREAKING CHANGE:
Due to additional validation while reading
commitlint config, previously ignored rule
settings are now considered critical errors
when starting the CLI. The new behaviour is
designed to help developers find issues with
their configuration quicker.
  • Loading branch information
marionebl committed Jan 9, 2018
1 parent b60adbc commit edf7187
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 2 deletions.
61 changes: 61 additions & 0 deletions @commitlint/core/src/lint.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import util from 'util';
import isIgnored from '@commitlint/is-ignored';
import parse from '@commitlint/parse';
import implementations from '@commitlint/rules';
Expand Down Expand Up @@ -30,6 +31,66 @@ export default async (message, rules = {}, opts = {}) => {
);
}

const invalid = entries(rules)
.map(([name, config]) => {
if (!Array.isArray(config)) {
return new Error(
`config for rule ${name} must be array, received ${util.inspect(
config
)} of type ${typeof config}`
);
}

if (config.length !== 2 && config.length !== 3) {
return new Error(
`config for rule ${name} must be 2 or 3 items long, received ${util.inspect(
config
)} of length ${config.length}`
);
}

const [level, when] = config;

if (typeof level !== 'number' || isNaN(level)) {
return new Error(
`level for rule ${name} must be number, received ${util.inspect(
level
)} of type ${typeof level}`
);
}

if (level < 0 || level > 2) {
return new RangeError(
`level for rule ${name} must be between 0 and 2, received ${util.inspect(
level
)}`
);
}

if (typeof when !== 'string') {
return new Error(
`condition for rule ${name} must be string, received ${util.inspect(
when
)} of type ${typeof when}`
);
}

if (when !== 'never' && when !== 'always') {
return new Error(
`condition for rule ${name} must be "always" or "never", received ${util.inspect(
when
)}`
);
}

return null;
})
.filter(item => item instanceof Error);

if (invalid.length > 0) {
throw new Error(invalid.map(i => i.message).join('\n'));
}

// Validate against all rules
const results = entries(rules)
.filter(entry => {
Expand Down
75 changes: 73 additions & 2 deletions @commitlint/core/src/lint.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,81 @@ test('positive on stub message and opts', async t => {
t.true(actual.valid);
});

test('should throw for invalid rule names', async t => {
test('throws for invalid rule names', async t => {
const error = await t.throws(
lint('foo', {foo: [2, 'always'], bar: [1, 'never']})
);

t.is(error.message.indexOf('Found invalid rule names: foo, bar'), 0);
t.is(error.message.indexOf('Found missing rule names: foo, bar'), 0);
});

test('throws for invalid rule config', async t => {
const error = await t.throws(
lint('type(scope): foo', {
'type-enum': 1,
'scope-enum': {0: 2, 1: 'never', 2: ['foo'], length: 3}
})
);

t.true(error.message.indexOf('type-enum must be array') > -1);
t.true(error.message.indexOf('scope-enum must be array') > -1);
});

test('throws for rule with invalid length', async t => {
const error = await t.throws(
lint('type(scope): foo', {'type-enum': [], 'scope-enum': [1, 2, 3, 4]})
);

t.true(error.message.indexOf('type-enum must be 2 or 3 items long') > -1);
t.true(error.message.indexOf('scope-enum must be 2 or 3 items long') > -1);
});

test('throws for rule with invalid level', async t => {
const error = await t.throws(
lint('type(scope): foo', {
'type-enum': ['2', 'always'],
'header-max-length': [{}, 'always']
})
);

t.true(error.message.indexOf('rule type-enum must be number') > -1);
t.true(error.message.indexOf('rule type-enum must be number') > -1);
});

test('throws for rule with out of range level', async t => {
const error = await t.throws(
lint('type(scope): foo', {
'type-enum': [-1, 'always'],
'header-max-length': [3, 'always']
})
);

t.true(error.message.indexOf('rule type-enum must be between 0 and 2') > -1);
t.true(error.message.indexOf('rule type-enum must be between 0 and 2') > -1);
});

test('throws for rule with invalid condition', async t => {
const error = await t.throws(
lint('type(scope): foo', {
'type-enum': [1, 2],
'header-max-length': [1, {}]
})
);

t.true(error.message.indexOf('type-enum must be string') > -1);
t.true(error.message.indexOf('header-max-length must be string') > -1);
});

test('throws for rule with out of range condition', async t => {
const error = await t.throws(
lint('type(scope): foo', {
'type-enum': [1, 'foo'],
'header-max-length': [1, 'bar']
})
);

t.true(error.message.indexOf('type-enum must be "always" or "never"') > -1);
t.true(
error.message.indexOf('header-max-length must be "always" or "never"') > -1
);
});

0 comments on commit edf7187

Please sign in to comment.