"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _experimentalUtils = require("@typescript-eslint/experimental-utils"); var _utils = require("./utils"); const trimFXprefix = word => ['f', 'x'].includes(word.charAt(0)) ? word.substr(1) : word; const doesBinaryExpressionContainStringNode = binaryExp => { if ((0, _utils.isStringNode)(binaryExp.right)) { return true; } if (binaryExp.left.type === _experimentalUtils.AST_NODE_TYPES.BinaryExpression) { return doesBinaryExpressionContainStringNode(binaryExp.left); } return (0, _utils.isStringNode)(binaryExp.left); }; const quoteStringValue = node => node.type === _experimentalUtils.AST_NODE_TYPES.TemplateLiteral ? `\`${node.quasis[0].value.raw}\`` : node.raw; const compileMatcherPattern = matcherMaybeWithMessage => { const [matcher, message] = Array.isArray(matcherMaybeWithMessage) ? matcherMaybeWithMessage : [matcherMaybeWithMessage]; return [new RegExp(matcher, 'u'), message]; }; const compileMatcherPatterns = matchers => { if (typeof matchers === 'string' || Array.isArray(matchers)) { const compiledMatcher = compileMatcherPattern(matchers); return { describe: compiledMatcher, test: compiledMatcher, it: compiledMatcher }; } return { describe: matchers.describe ? compileMatcherPattern(matchers.describe) : null, test: matchers.test ? compileMatcherPattern(matchers.test) : null, it: matchers.it ? compileMatcherPattern(matchers.it) : null }; }; const MatcherAndMessageSchema = { type: 'array', items: { type: 'string' }, minItems: 1, maxItems: 2, additionalItems: false }; var _default = (0, _utils.createRule)({ name: __filename, meta: { docs: { category: 'Best Practices', description: 'Enforce valid titles', recommended: 'error' }, messages: { titleMustBeString: 'Title must be a string', emptyTitle: '{{ jestFunctionName }} should not have an empty title', duplicatePrefix: 'should not have duplicate prefix', accidentalSpace: 'should not have leading or trailing spaces', disallowedWord: '"{{ word }}" is not allowed in test titles.', mustNotMatch: '{{ jestFunctionName }} should not match {{ pattern }}', mustMatch: '{{ jestFunctionName }} should match {{ pattern }}', mustNotMatchCustom: '{{ message }}', mustMatchCustom: '{{ message }}' }, type: 'suggestion', schema: [{ type: 'object', properties: { ignoreTypeOfDescribeName: { type: 'boolean', default: false }, disallowedWords: { type: 'array', items: { type: 'string' } } }, patternProperties: { [/^must(?:Not)?Match$/u.source]: { oneOf: [{ type: 'string' }, MatcherAndMessageSchema, { type: 'object', propertyNames: { enum: ['describe', 'test', 'it'] }, additionalProperties: { oneOf: [{ type: 'string' }, MatcherAndMessageSchema] } }] } }, additionalProperties: false }], fixable: 'code' }, defaultOptions: [{ ignoreTypeOfDescribeName: false, disallowedWords: [] }], create(context, [{ ignoreTypeOfDescribeName, disallowedWords = [], mustNotMatch, mustMatch }]) { const disallowedWordsRegexp = new RegExp(`\\b(${disallowedWords.join('|')})\\b`, 'iu'); const mustNotMatchPatterns = compileMatcherPatterns(mustNotMatch !== null && mustNotMatch !== void 0 ? mustNotMatch : {}); const mustMatchPatterns = compileMatcherPatterns(mustMatch !== null && mustMatch !== void 0 ? mustMatch : {}); return { CallExpression(node) { var _mustNotMatchPatterns, _mustMatchPatterns$je; if (!(0, _utils.isDescribeCall)(node) && !(0, _utils.isTestCaseCall)(node)) { return; } const [argument] = node.arguments; if (!argument) { return; } if (!(0, _utils.isStringNode)(argument)) { if (argument.type === _experimentalUtils.AST_NODE_TYPES.BinaryExpression && doesBinaryExpressionContainStringNode(argument)) { return; } if (argument.type !== _experimentalUtils.AST_NODE_TYPES.TemplateLiteral && !(ignoreTypeOfDescribeName && (0, _utils.isDescribeCall)(node))) { context.report({ messageId: 'titleMustBeString', loc: argument.loc }); } return; } const title = (0, _utils.getStringValue)(argument); if (!title) { context.report({ messageId: 'emptyTitle', data: { jestFunctionName: (0, _utils.isDescribeCall)(node) ? _utils.DescribeAlias.describe : _utils.TestCaseName.test }, node }); return; } if (disallowedWords.length > 0) { const disallowedMatch = disallowedWordsRegexp.exec(title); if (disallowedMatch) { context.report({ data: { word: disallowedMatch[1] }, messageId: 'disallowedWord', node: argument }); return; } } if (title.trim().length !== title.length) { context.report({ messageId: 'accidentalSpace', node: argument, fix: fixer => [fixer.replaceTextRange(argument.range, quoteStringValue(argument).replace(/^([`'"]) +?/u, '$1').replace(/ +?([`'"])$/u, '$1'))] }); } const nodeName = trimFXprefix((0, _utils.getNodeName)(node)); const [firstWord] = title.split(' '); if (firstWord.toLowerCase() === nodeName) { context.report({ messageId: 'duplicatePrefix', node: argument, fix: fixer => [fixer.replaceTextRange(argument.range, quoteStringValue(argument).replace(/^([`'"]).+? /u, '$1'))] }); } const [jestFunctionName] = nodeName.split('.'); const [mustNotMatchPattern, mustNotMatchMessage] = (_mustNotMatchPatterns = mustNotMatchPatterns[jestFunctionName]) !== null && _mustNotMatchPatterns !== void 0 ? _mustNotMatchPatterns : []; if (mustNotMatchPattern) { if (mustNotMatchPattern.test(title)) { context.report({ messageId: mustNotMatchMessage ? 'mustNotMatchCustom' : 'mustNotMatch', node: argument, data: { jestFunctionName, pattern: mustNotMatchPattern, message: mustNotMatchMessage } }); return; } } const [mustMatchPattern, mustMatchMessage] = (_mustMatchPatterns$je = mustMatchPatterns[jestFunctionName]) !== null && _mustMatchPatterns$je !== void 0 ? _mustMatchPatterns$je : []; if (mustMatchPattern) { if (!mustMatchPattern.test(title)) { context.report({ messageId: mustMatchMessage ? 'mustMatchCustom' : 'mustMatch', node: argument, data: { jestFunctionName, pattern: mustMatchPattern, message: mustMatchMessage } }); return; } } } }; } }); exports.default = _default;