gherkin-lint-ts
Version:
Gherkin features linter written in Typescript
135 lines • 6.05 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.run = exports.availableConfigs = exports.name = void 0;
const gherkinUtils = __importStar(require("./utils/gherkin"));
const chalk_1 = __importDefault(require("chalk"));
exports.name = "no-restricted-patterns";
exports.availableConfigs = {
"Global": [],
"Scenario": [],
"ScenarioOutline": [],
"Background": [],
"Feature": [],
};
function run(feature, unused, configuration) {
var _a;
if (!feature) {
return [];
}
let errors = [];
const restrictedPatterns = getRestrictedPatterns(configuration);
const language = feature.language || "en";
// Check the feature itself
checkNameAndDescription(feature, restrictedPatterns, language, errors);
// Check the feature children
(_a = feature.children) === null || _a === void 0 ? void 0 : _a.forEach(child => {
var _a;
if (child.rule) {
(_a = child.rule.children) === null || _a === void 0 ? void 0 : _a.forEach(childRule => {
checkNode(childRule, restrictedPatterns, language, errors);
});
}
else {
checkNode(child, restrictedPatterns, language, errors);
}
});
return errors;
}
exports.run = run;
function checkNode(child, restrictedPatterns, language, errors) {
var _a;
const node = child.background || child.scenario;
if (node) {
checkNameAndDescription(node, restrictedPatterns, language, errors);
// And all the steps of each child
(_a = node.steps) === null || _a === void 0 ? void 0 : _a.forEach(step => {
checkStepNode(step, node, restrictedPatterns, language, errors);
});
}
}
function getRestrictedPatterns(configuration) {
// Patterns applied to everything; feature, scenarios, etc.
let globalPatterns = (configuration.Global || []).map(pattern => new RegExp(pattern, "i"));
let restrictedPatterns = {};
Object.keys(exports.availableConfigs).forEach(key => {
const resolvedKey = key.toLowerCase().replace(/ /g, "");
const resolvedConfig = (configuration[key] || []);
restrictedPatterns[resolvedKey] = resolvedConfig.map(pattern => new RegExp(pattern, "i"));
restrictedPatterns[resolvedKey] = restrictedPatterns[resolvedKey].concat(globalPatterns);
});
return restrictedPatterns;
}
function getRestrictedPatternsForNode(node, restrictedPatterns, language) {
let key = gherkinUtils.getLanguageInsensitiveKeyword(node, language).toLowerCase();
return restrictedPatterns[key];
}
function checkNameAndDescription(node, restrictedPatterns, language, errors) {
getRestrictedPatternsForNode(node, restrictedPatterns, language)
.forEach(pattern => {
check(node, "name", pattern, language, errors);
check(node, "description", pattern, language, errors);
});
}
function checkStepNode(node, parentNode, restrictedPatterns, language, errors) {
// Use the node keyword of the parent to determine which rule configuration to use
getRestrictedPatternsForNode(parentNode, restrictedPatterns, language)
.forEach(pattern => {
check(node, "text", pattern, language, errors);
});
}
function check(node, property, pattern, language, errors) {
if (!node[property]) {
return;
}
let strings = [node[property]];
const type = gherkinUtils.getNodeType(node, language);
if (property === "description") {
// Descriptions can be multiline, in which case the description will contain escaped
// newline characters "\n". If a multiline description matches one of the restricted patterns
// when the error message gets printed in the console, it will break the message into multiple lines.
// So let's split the description on newline chars and test each line separately.
// To make sure we don't accidentally pick up a doubly escaped new line "\\n" which would appear
// if a user wrote the string "\n" in a description, let's replace all escaped new lines
// with a sentinel, split lines and then restore the doubly escaped new line
const escapedNewLineSentinel = "<!gherkin-lint new line sentinel!>";
const escapedNewLine = "\\n";
strings = node[property]
.replace(escapedNewLine, escapedNewLineSentinel)
.split("\n")
.map(string => string.replace(escapedNewLineSentinel, escapedNewLine));
}
strings.forEach(item => {
// We use trim() on the examined string because names and descriptions can contain
// white space before and after, unlike steps
if (item.trim().match(pattern)) {
errors.push({
message: `${type} ${property}: "${chalk_1.default.cyan(item.trim())}" matches restricted pattern "${chalk_1.default.yellow(pattern)}"`,
rule: exports.name,
line: node.location.line,
});
}
});
}
//# sourceMappingURL=no-restricted-patterns.js.map