prefer-to-contain.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.default = void 0;
  6. var _experimentalUtils = require("@typescript-eslint/experimental-utils");
  7. var _utils = require("./utils");
  8. const isBooleanLiteral = node => node.type === _experimentalUtils.AST_NODE_TYPES.Literal && typeof node.value === 'boolean';
  9. /**
  10. * Checks if the given `ParsedExpectMatcher` is a call to one of the equality matchers,
  11. * with a boolean literal as the sole argument.
  12. *
  13. * @example javascript
  14. * toBe(true);
  15. * toEqual(false);
  16. *
  17. * @param {ParsedExpectMatcher} matcher
  18. *
  19. * @return {matcher is ParsedBooleanEqualityMatcher}
  20. */
  21. const isBooleanEqualityMatcher = matcher => (0, _utils.isParsedEqualityMatcherCall)(matcher) && isBooleanLiteral((0, _utils.followTypeAssertionChain)(matcher.arguments[0]));
  22. /**
  23. * Checks if the given `node` is a `CallExpression` representing the calling
  24. * of an `includes`-like method that can be 'fixed' (using `toContain`).
  25. *
  26. * @param {CallExpression} node
  27. *
  28. * @return {node is FixableIncludesCallExpression}
  29. *
  30. * @todo support `['includes']()` syntax (remove last property.type check to begin)
  31. * @todo break out into `isMethodCall<Name extends string>(node: TSESTree.Node, method: Name)` util-fn
  32. */
  33. const isFixableIncludesCallExpression = node => node.type === _experimentalUtils.AST_NODE_TYPES.CallExpression && node.callee.type === _experimentalUtils.AST_NODE_TYPES.MemberExpression && (0, _utils.isSupportedAccessor)(node.callee.property, 'includes') && node.callee.property.type === _experimentalUtils.AST_NODE_TYPES.Identifier && (0, _utils.hasOnlyOneArgument)(node);
  34. const buildToContainFuncExpectation = negated => negated ? `${_utils.ModifierName.not}.toContain` : 'toContain';
  35. /**
  36. * Finds the first `.` character token between the `object` & `property` of the given `member` expression.
  37. *
  38. * @param {TSESTree.MemberExpression} member
  39. * @param {SourceCode} sourceCode
  40. *
  41. * @return {Token | null}
  42. */
  43. const findPropertyDotToken = (member, sourceCode) => sourceCode.getFirstTokenBetween(member.object, member.property, token => token.value === '.');
  44. const getNegationFixes = (node, modifier, matcher, sourceCode, fixer, fileName) => {
  45. const [containArg] = node.arguments;
  46. const negationPropertyDot = findPropertyDotToken(modifier.node, sourceCode);
  47. const toContainFunc = buildToContainFuncExpectation((0, _utils.followTypeAssertionChain)(matcher.arguments[0]).value);
  48. /* istanbul ignore if */
  49. if (negationPropertyDot === null) {
  50. throw new Error(`Unexpected null when attempting to fix ${fileName} - please file a github issue at https://github.com/jest-community/eslint-plugin-jest`);
  51. }
  52. return [fixer.remove(negationPropertyDot), fixer.remove(modifier.node.property), fixer.replaceText(matcher.node.property, toContainFunc), fixer.replaceText(matcher.arguments[0], sourceCode.getText(containArg))];
  53. };
  54. const getCommonFixes = (node, sourceCode, fileName) => {
  55. const [containArg] = node.arguments;
  56. const includesCallee = node.callee;
  57. const propertyDot = findPropertyDotToken(includesCallee, sourceCode);
  58. const closingParenthesis = sourceCode.getTokenAfter(containArg);
  59. const openParenthesis = sourceCode.getTokenBefore(containArg);
  60. /* istanbul ignore if */
  61. if (propertyDot === null || closingParenthesis === null || openParenthesis === null) {
  62. throw new Error(`Unexpected null when attempting to fix ${fileName} - please file a github issue at https://github.com/jest-community/eslint-plugin-jest`);
  63. }
  64. return [containArg, includesCallee.property, propertyDot, closingParenthesis, openParenthesis];
  65. }; // expect(array.includes(<value>)[not.]{toBe,toEqual}(<boolean>)
  66. var _default = (0, _utils.createRule)({
  67. name: __filename,
  68. meta: {
  69. docs: {
  70. category: 'Best Practices',
  71. description: 'Suggest using `toContain()`',
  72. recommended: false
  73. },
  74. messages: {
  75. useToContain: 'Use toContain() instead'
  76. },
  77. fixable: 'code',
  78. type: 'suggestion',
  79. schema: []
  80. },
  81. defaultOptions: [],
  82. create(context) {
  83. return {
  84. CallExpression(node) {
  85. if (!(0, _utils.isExpectCall)(node)) {
  86. return;
  87. }
  88. const {
  89. expect: {
  90. arguments: [includesCall]
  91. },
  92. matcher,
  93. modifier
  94. } = (0, _utils.parseExpectCall)(node);
  95. if (!matcher || !includesCall || modifier && modifier.name !== _utils.ModifierName.not || !isBooleanEqualityMatcher(matcher) || !isFixableIncludesCallExpression(includesCall)) {
  96. return;
  97. }
  98. context.report({
  99. fix(fixer) {
  100. const sourceCode = context.getSourceCode();
  101. const fileName = context.getFilename();
  102. const fixArr = getCommonFixes(includesCall, sourceCode, fileName).map(target => fixer.remove(target));
  103. if (modifier) {
  104. return getNegationFixes(includesCall, modifier, matcher, sourceCode, fixer, fileName).concat(fixArr);
  105. }
  106. const toContainFunc = buildToContainFuncExpectation(!(0, _utils.followTypeAssertionChain)(matcher.arguments[0]).value);
  107. const [containArg] = includesCall.arguments;
  108. fixArr.push(fixer.replaceText(matcher.node.property, toContainFunc));
  109. fixArr.push(fixer.replaceText(matcher.arguments[0], sourceCode.getText(containArg)));
  110. return fixArr;
  111. },
  112. messageId: 'useToContain',
  113. node: (modifier || matcher).node.property
  114. });
  115. }
  116. };
  117. }
  118. });
  119. exports.default = _default;