prefer-equality-matcher.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.default = void 0;
  6. var _utils = require("@typescript-eslint/utils");
  7. var _utils2 = require("./utils");
  8. var _default = (0, _utils2.createRule)({
  9. name: __filename,
  10. meta: {
  11. docs: {
  12. category: 'Best Practices',
  13. description: 'Suggest using the built-in equality matchers',
  14. recommended: false,
  15. suggestion: true
  16. },
  17. messages: {
  18. useEqualityMatcher: 'Prefer using one of the equality matchers instead',
  19. suggestEqualityMatcher: 'Use `{{ equalityMatcher }}`'
  20. },
  21. hasSuggestions: true,
  22. type: 'suggestion',
  23. schema: []
  24. },
  25. defaultOptions: [],
  26. create(context) {
  27. return {
  28. CallExpression(node) {
  29. const jestFnCall = (0, _utils2.parseJestFnCall)(node, context);
  30. if ((jestFnCall === null || jestFnCall === void 0 ? void 0 : jestFnCall.type) !== 'expect' || jestFnCall.args.length === 0) {
  31. return;
  32. }
  33. const {
  34. parent: expect
  35. } = jestFnCall.head.node;
  36. if ((expect === null || expect === void 0 ? void 0 : expect.type) !== _utils.AST_NODE_TYPES.CallExpression) {
  37. return;
  38. }
  39. const {
  40. arguments: [comparison],
  41. range: [, expectCallEnd]
  42. } = expect;
  43. const {
  44. matcher
  45. } = jestFnCall;
  46. const matcherArg = (0, _utils2.getFirstMatcherArg)(jestFnCall);
  47. if ((comparison === null || comparison === void 0 ? void 0 : comparison.type) !== _utils.AST_NODE_TYPES.BinaryExpression || comparison.operator !== '===' && comparison.operator !== '!==' || !_utils2.EqualityMatcher.hasOwnProperty((0, _utils2.getAccessorValue)(matcher)) || !(0, _utils2.isBooleanLiteral)(matcherArg)) {
  48. return;
  49. }
  50. const matcherValue = matcherArg.value;
  51. const [modifier] = jestFnCall.modifiers;
  52. const hasNot = jestFnCall.modifiers.some(nod => (0, _utils2.getAccessorValue)(nod) === 'not'); // we need to negate the expectation if the current expected
  53. // value is itself negated by the "not" modifier
  54. const addNotModifier = (comparison.operator === '!==' ? !matcherValue : matcherValue) === hasNot;
  55. const buildFixer = equalityMatcher => fixer => {
  56. const sourceCode = context.getSourceCode(); // preserve the existing modifier if it's not a negation
  57. let modifierText = modifier && (0, _utils2.getAccessorValue)(modifier) !== 'not' ? `.${(0, _utils2.getAccessorValue)(modifier)}` : '';
  58. if (addNotModifier) {
  59. modifierText += `.${_utils2.ModifierName.not}`;
  60. }
  61. return [// replace the comparison argument with the left-hand side of the comparison
  62. fixer.replaceText(comparison, sourceCode.getText(comparison.left)), // replace the current matcher & modifier with the preferred matcher
  63. fixer.replaceTextRange([expectCallEnd, matcher.parent.range[1]], `${modifierText}.${equalityMatcher}`), // replace the matcher argument with the right-hand side of the comparison
  64. fixer.replaceText(matcherArg, sourceCode.getText(comparison.right))];
  65. };
  66. context.report({
  67. messageId: 'useEqualityMatcher',
  68. suggest: ['toBe', 'toEqual', 'toStrictEqual'].map(equalityMatcher => ({
  69. messageId: 'suggestEqualityMatcher',
  70. data: {
  71. equalityMatcher
  72. },
  73. fix: buildFixer(equalityMatcher)
  74. })),
  75. node: matcher
  76. });
  77. }
  78. };
  79. }
  80. });
  81. exports.default = _default;