gplint
Version:
A Gherkin linter/validator written in Javascript.
136 lines • 5.74 kB
JavaScript
import * as gherkinUtils from './utils/gherkin.js';
import { featureSpread } from './utils/gherkin.js';
import _ from 'lodash';
import { StepKeywordType } from '@cucumber/messages';
export const name = 'max-keywords';
export const availableConfigs = {
given: -1,
when: -1,
then: -1,
onlyContiguous: false,
ignoreConjunctions: false,
};
export function run({ feature }, configuration) {
if (!feature) {
return [];
}
const mergedConfiguration = _.merge({}, availableConfigs, configuration);
const errors = [];
const { children } = featureSpread(feature);
children.forEach(child => {
const node = child.background ?? child.scenario;
const keywordStepsCount = [];
let lastKeyword;
node.steps.forEach((step) => {
const keyword = gherkinUtils.getLanguageInsensitiveKeyword(step, feature.language);
if ([StepKeywordType.CONJUNCTION, StepKeywordType.UNKNOWN].includes(step.keywordType)) {
if (mergedConfiguration.ignoreConjunctions || lastKeyword == null) {
return;
}
(mergedConfiguration.onlyContiguous
? keywordStepsCount[keywordStepsCount.length - 1]
: keywordStepsCount.find(d => d[0] === lastKeyword))[2]++;
}
else if (lastKeyword === keyword) {
(mergedConfiguration.onlyContiguous
? keywordStepsCount[keywordStepsCount.length - 1]
: keywordStepsCount.find(d => d[0] === lastKeyword))[2]++;
}
else {
lastKeyword = keyword;
if (mergedConfiguration.onlyContiguous) {
keywordStepsCount.push([keyword, step.location, 1]);
}
else {
const prevKeywordData = keywordStepsCount.find(d => d[0] === keyword);
if (prevKeywordData) {
prevKeywordData[2]++;
}
else {
keywordStepsCount.push([keyword, step.location, 1]);
}
}
}
});
for (const [keyword, location, count] of keywordStepsCount) {
const maxAllowed = mergedConfiguration[keyword];
if (maxAllowed !== -1 && count > maxAllowed) {
errors.push(createError(keyword, location, count, maxAllowed, mergedConfiguration.onlyContiguous));
}
}
});
return errors;
}
function createError(keyword, location, count, expected, contiguous) {
return {
message: `There are too many${contiguous ? ' contiguous ' : ' '}steps for "${keyword}". Found ${count}, maximum allowed ${expected}.`,
rule: name,
line: location.line,
column: location.column,
};
}
export const documentation = {
description: 'Allow to limit the amount of steps of the specified keywords. The keywords `And`, `But`, or `*` counts as the previous keyword.',
configuration: [{
name: 'given',
type: 'number',
description: 'Max allowed steps for Given. 0 means is not allowed and a negative value (or undefined) means unlimited.',
default: availableConfigs.given,
}, {
name: 'when',
type: 'number',
description: 'Max allowed steps for When. 0 means is not allowed and a negative value (or undefined) means unlimited.',
default: availableConfigs.when,
}, {
name: 'then',
type: 'number',
description: 'Max allowed steps for Then. 0 means is not allowed and a negative value (or undefined) means unlimited.',
default: availableConfigs.then,
}, {
name: 'onlyContiguous',
type: 'boolean',
description: 'If true, only counts contiguous steps, resetting the limit if another keyword is used.',
default: availableConfigs.onlyContiguous,
}, {
name: 'ignoreConjunctions',
type: 'boolean',
description: 'If true, only counts the main keywords, ignoring conjunctions (`And`, `But`, `*`).',
default: availableConfigs.onlyContiguous,
}],
examples: [{
title: 'Limit `When` to 1 occurrence',
description: '`When` is limited to one occurrence. `Given` and `Then` are not defined, which means there are no limit for them.',
config: {
[name]: ['error', {
'when': 1,
}],
},
}, {
title: 'Limit `When` to 3 occurrence, and Given to 1',
description: '`Given` is limited to one occurrence, and `When` is limited to three occurrences. `Then` is not defined, which means there is no limit for it.',
config: {
[name]: ['error', {
'given': 1,
'when': 3,
}],
},
}, {
title: 'Forbid the use of `Given` step',
description: 'Do not allow to use the step `Given`.',
config: {
[name]: ['error', {
'given': 0,
}],
},
}, {
title: 'Limit `When` to 1 when are put together.',
description: 'Limit `When` to 1, but only if there are not other keywords between.',
config: {
[name]: ['error', {
'when': 1,
'onlyContiguous': true,
}],
},
}],
};
//# sourceMappingURL=max-keywords.js.map