prefer-to-contain.js 3.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  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. 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') && (0, _utils.hasOnlyOneArgument)(node); // expect(array.includes(<value>)[not.]{toBe,toEqual}(<boolean>)
  31. var _default = (0, _utils.createRule)({
  32. name: __filename,
  33. meta: {
  34. docs: {
  35. category: 'Best Practices',
  36. description: 'Suggest using `toContain()`',
  37. recommended: false
  38. },
  39. messages: {
  40. useToContain: 'Use toContain() instead'
  41. },
  42. fixable: 'code',
  43. type: 'suggestion',
  44. schema: []
  45. },
  46. defaultOptions: [],
  47. create(context) {
  48. return {
  49. CallExpression(node) {
  50. if (!(0, _utils.isExpectCall)(node)) {
  51. return;
  52. }
  53. const {
  54. expect: {
  55. arguments: [includesCall],
  56. range: [, expectCallEnd]
  57. },
  58. matcher,
  59. modifier
  60. } = (0, _utils.parseExpectCall)(node);
  61. if (!matcher || !includesCall || modifier && modifier.name !== _utils.ModifierName.not || !isBooleanEqualityMatcher(matcher) || !isFixableIncludesCallExpression(includesCall)) {
  62. return;
  63. }
  64. context.report({
  65. fix(fixer) {
  66. const sourceCode = context.getSourceCode(); // we need to negate the expectation if the current expected
  67. // value is itself negated by the "not" modifier
  68. const addNotModifier = (0, _utils.followTypeAssertionChain)(matcher.arguments[0]).value === !!modifier;
  69. return [// remove the "includes" call entirely
  70. fixer.removeRange([includesCall.callee.property.range[0] - 1, includesCall.range[1]]), // replace the current matcher with "toContain", adding "not" if needed
  71. fixer.replaceTextRange([expectCallEnd, matcher.node.range[1]], addNotModifier ? `.${_utils.ModifierName.not}.toContain` : '.toContain'), // replace the matcher argument with the value from the "includes"
  72. fixer.replaceText(matcher.arguments[0], sourceCode.getText(includesCall.arguments[0]))];
  73. },
  74. messageId: 'useToContain',
  75. node: (modifier || matcher).node.property
  76. });
  77. }
  78. };
  79. }
  80. });
  81. exports.default = _default;