prefer-comparison-matcher.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  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. const isString = node => {
  23. return (0, _utils.isStringNode)(node) || node.type === _experimentalUtils.AST_NODE_TYPES.TemplateLiteral;
  24. };
  25. const isComparingToString = expression => {
  26. return isString(expression.left) || isString(expression.right);
  27. };
  28. const invertOperator = operator => {
  29. switch (operator) {
  30. case '>':
  31. return '<=';
  32. case '<':
  33. return '>=';
  34. case '>=':
  35. return '<';
  36. case '<=':
  37. return '>';
  38. }
  39. return null;
  40. };
  41. const determineMatcher = (operator, negated) => {
  42. const op = negated ? invertOperator(operator) : operator;
  43. switch (op) {
  44. case '>':
  45. return 'toBeGreaterThan';
  46. case '<':
  47. return 'toBeLessThan';
  48. case '>=':
  49. return 'toBeGreaterThanOrEqual';
  50. case '<=':
  51. return 'toBeLessThanOrEqual';
  52. }
  53. return null;
  54. };
  55. var _default = (0, _utils.createRule)({
  56. name: __filename,
  57. meta: {
  58. docs: {
  59. category: 'Best Practices',
  60. description: 'Suggest using the built-in comparison matchers',
  61. recommended: false
  62. },
  63. messages: {
  64. useToBeComparison: 'Prefer using `{{ preferredMatcher }}` instead'
  65. },
  66. fixable: 'code',
  67. type: 'suggestion',
  68. schema: []
  69. },
  70. defaultOptions: [],
  71. create(context) {
  72. return {
  73. CallExpression(node) {
  74. if (!(0, _utils.isExpectCall)(node)) {
  75. return;
  76. }
  77. const {
  78. expect: {
  79. arguments: [comparison],
  80. range: [, expectCallEnd]
  81. },
  82. matcher,
  83. modifier
  84. } = (0, _utils.parseExpectCall)(node);
  85. if (!matcher || (comparison === null || comparison === void 0 ? void 0 : comparison.type) !== _experimentalUtils.AST_NODE_TYPES.BinaryExpression || isComparingToString(comparison) || !isBooleanEqualityMatcher(matcher)) {
  86. return;
  87. }
  88. const preferredMatcher = determineMatcher(comparison.operator, (0, _utils.followTypeAssertionChain)(matcher.arguments[0]).value === !!modifier);
  89. if (!preferredMatcher) {
  90. return;
  91. }
  92. context.report({
  93. fix(fixer) {
  94. const sourceCode = context.getSourceCode();
  95. return [// replace the comparison argument with the left-hand side of the comparison
  96. fixer.replaceText(comparison, sourceCode.getText(comparison.left)), // replace the current matcher & modifier with the preferred matcher
  97. fixer.replaceTextRange([expectCallEnd, matcher.node.range[1]], `.${preferredMatcher}`), // replace the matcher argument with the right-hand side of the comparison
  98. fixer.replaceText(matcher.arguments[0], sourceCode.getText(comparison.right))];
  99. },
  100. messageId: 'useToBeComparison',
  101. data: {
  102. preferredMatcher
  103. },
  104. node: (modifier || matcher).node.property
  105. });
  106. }
  107. };
  108. }
  109. });
  110. exports.default = _default;