@depup/eslint-plugin-jest 29.15.1-depup.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +31 -0
  3. package/changes.json +10 -0
  4. package/docs/rules/consistent-test-it.md +96 -0
  5. package/docs/rules/expect-expect.md +152 -0
  6. package/docs/rules/max-expects.md +78 -0
  7. package/docs/rules/max-nested-describe.md +134 -0
  8. package/docs/rules/no-alias-methods.md +57 -0
  9. package/docs/rules/no-commented-out-tests.md +68 -0
  10. package/docs/rules/no-conditional-expect.md +142 -0
  11. package/docs/rules/no-conditional-in-test.md +123 -0
  12. package/docs/rules/no-confusing-set-timeout.md +64 -0
  13. package/docs/rules/no-deprecated-functions.md +59 -0
  14. package/docs/rules/no-disabled-tests.md +72 -0
  15. package/docs/rules/no-done-callback.md +100 -0
  16. package/docs/rules/no-duplicate-hooks.md +79 -0
  17. package/docs/rules/no-error-equal.md +37 -0
  18. package/docs/rules/no-export.md +52 -0
  19. package/docs/rules/no-focused-tests.md +67 -0
  20. package/docs/rules/no-hooks.md +178 -0
  21. package/docs/rules/no-identical-title.md +59 -0
  22. package/docs/rules/no-interpolation-in-snapshots.md +67 -0
  23. package/docs/rules/no-jasmine-globals.md +69 -0
  24. package/docs/rules/no-large-snapshots.md +180 -0
  25. package/docs/rules/no-mocks-import.md +34 -0
  26. package/docs/rules/no-restricted-jest-methods.md +53 -0
  27. package/docs/rules/no-restricted-matchers.md +64 -0
  28. package/docs/rules/no-standalone-expect.md +106 -0
  29. package/docs/rules/no-test-prefixes.md +42 -0
  30. package/docs/rules/no-test-return-statement.md +50 -0
  31. package/docs/rules/no-unnecessary-assertion.md +41 -0
  32. package/docs/rules/no-unneeded-async-expect-function.md +41 -0
  33. package/docs/rules/no-untyped-mock-factory.md +74 -0
  34. package/docs/rules/padding-around-after-all-blocks.md +34 -0
  35. package/docs/rules/padding-around-after-each-blocks.md +38 -0
  36. package/docs/rules/padding-around-all.md +20 -0
  37. package/docs/rules/padding-around-before-all-blocks.md +37 -0
  38. package/docs/rules/padding-around-before-each-blocks.md +38 -0
  39. package/docs/rules/padding-around-describe-blocks.md +42 -0
  40. package/docs/rules/padding-around-expect-groups.md +44 -0
  41. package/docs/rules/padding-around-test-blocks.md +48 -0
  42. package/docs/rules/prefer-called-with.md +39 -0
  43. package/docs/rules/prefer-comparison-matcher.md +62 -0
  44. package/docs/rules/prefer-each.md +58 -0
  45. package/docs/rules/prefer-ending-with-an-expect.md +170 -0
  46. package/docs/rules/prefer-equality-matcher.md +36 -0
  47. package/docs/rules/prefer-expect-assertions.md +230 -0
  48. package/docs/rules/prefer-expect-resolves.md +62 -0
  49. package/docs/rules/prefer-hooks-in-order.md +137 -0
  50. package/docs/rules/prefer-hooks-on-top.md +124 -0
  51. package/docs/rules/prefer-importing-jest-globals.md +85 -0
  52. package/docs/rules/prefer-jest-mocked.md +40 -0
  53. package/docs/rules/prefer-lowercase-title.md +128 -0
  54. package/docs/rules/prefer-mock-promise-shorthand.md +41 -0
  55. package/docs/rules/prefer-mock-return-shorthand.md +50 -0
  56. package/docs/rules/prefer-snapshot-hint.md +192 -0
  57. package/docs/rules/prefer-spy-on.md +46 -0
  58. package/docs/rules/prefer-strict-equal.md +29 -0
  59. package/docs/rules/prefer-to-be.md +63 -0
  60. package/docs/rules/prefer-to-contain.md +53 -0
  61. package/docs/rules/prefer-to-have-been-called-times.md +43 -0
  62. package/docs/rules/prefer-to-have-been-called.md +35 -0
  63. package/docs/rules/prefer-to-have-length.md +41 -0
  64. package/docs/rules/prefer-todo.md +33 -0
  65. package/docs/rules/require-hook.md +187 -0
  66. package/docs/rules/require-to-throw-message.md +43 -0
  67. package/docs/rules/require-top-level-describe.md +86 -0
  68. package/docs/rules/unbound-method.md +61 -0
  69. package/docs/rules/valid-describe-callback.md +69 -0
  70. package/docs/rules/valid-expect-in-promise.md +79 -0
  71. package/docs/rules/valid-expect-with-promise.md +43 -0
  72. package/docs/rules/valid-expect.md +155 -0
  73. package/docs/rules/valid-mock-module-path.md +72 -0
  74. package/docs/rules/valid-title.md +319 -0
  75. package/index.d.ts +28 -0
  76. package/lib/globals.json +15 -0
  77. package/lib/index.js +85 -0
  78. package/lib/rules/consistent-test-it.js +101 -0
  79. package/lib/rules/expect-expect.js +102 -0
  80. package/lib/rules/max-expects.js +70 -0
  81. package/lib/rules/max-nested-describe.js +59 -0
  82. package/lib/rules/no-alias-methods.js +62 -0
  83. package/lib/rules/no-commented-out-tests.js +44 -0
  84. package/lib/rules/no-conditional-expect.js +80 -0
  85. package/lib/rules/no-conditional-in-test.js +63 -0
  86. package/lib/rules/no-confusing-set-timeout.js +61 -0
  87. package/lib/rules/no-deprecated-functions.js +81 -0
  88. package/lib/rules/no-disabled-tests.js +57 -0
  89. package/lib/rules/no-done-callback.js +120 -0
  90. package/lib/rules/no-duplicate-hooks.js +52 -0
  91. package/lib/rules/no-error-equal.js +41 -0
  92. package/lib/rules/no-export.js +61 -0
  93. package/lib/rules/no-focused-tests.js +63 -0
  94. package/lib/rules/no-hooks.js +51 -0
  95. package/lib/rules/no-identical-title.js +73 -0
  96. package/lib/rules/no-interpolation-in-snapshots.js +43 -0
  97. package/lib/rules/no-jasmine-globals.js +140 -0
  98. package/lib/rules/no-large-snapshots.js +100 -0
  99. package/lib/rules/no-mocks-import.js +46 -0
  100. package/lib/rules/no-restricted-jest-methods.js +53 -0
  101. package/lib/rules/no-restricted-matchers.js +60 -0
  102. package/lib/rules/no-standalone-expect.js +114 -0
  103. package/lib/rules/no-test-prefixes.js +48 -0
  104. package/lib/rules/no-test-return-statement.js +62 -0
  105. package/lib/rules/no-unnecessary-assertion.js +87 -0
  106. package/lib/rules/no-unneeded-async-expect-function.js +57 -0
  107. package/lib/rules/no-untyped-mock-factory.js +61 -0
  108. package/lib/rules/padding-around-after-all-blocks.js +17 -0
  109. package/lib/rules/padding-around-after-each-blocks.js +17 -0
  110. package/lib/rules/padding-around-all.js +15 -0
  111. package/lib/rules/padding-around-before-all-blocks.js +17 -0
  112. package/lib/rules/padding-around-before-each-blocks.js +17 -0
  113. package/lib/rules/padding-around-describe-blocks.js +17 -0
  114. package/lib/rules/padding-around-expect-groups.js +21 -0
  115. package/lib/rules/padding-around-test-blocks.js +17 -0
  116. package/lib/rules/prefer-called-with.js +47 -0
  117. package/lib/rules/prefer-comparison-matcher.js +111 -0
  118. package/lib/rules/prefer-each.js +77 -0
  119. package/lib/rules/prefer-ending-with-an-expect.js +102 -0
  120. package/lib/rules/prefer-equality-matcher.js +87 -0
  121. package/lib/rules/prefer-expect-assertions.js +237 -0
  122. package/lib/rules/prefer-expect-resolves.js +47 -0
  123. package/lib/rules/prefer-hooks-in-order.js +67 -0
  124. package/lib/rules/prefer-hooks-on-top.js +41 -0
  125. package/lib/rules/prefer-importing-jest-globals.js +126 -0
  126. package/lib/rules/prefer-jest-mocked.js +66 -0
  127. package/lib/rules/prefer-lowercase-title.js +128 -0
  128. package/lib/rules/prefer-mock-promise-shorthand.js +88 -0
  129. package/lib/rules/prefer-mock-return-shorthand.js +122 -0
  130. package/lib/rules/prefer-snapshot-hint.js +111 -0
  131. package/lib/rules/prefer-spy-on.js +80 -0
  132. package/lib/rules/prefer-strict-equal.js +46 -0
  133. package/lib/rules/prefer-to-be.js +98 -0
  134. package/lib/rules/prefer-to-contain.js +82 -0
  135. package/lib/rules/prefer-to-have-been-called-times.js +70 -0
  136. package/lib/rules/prefer-to-have-been-called.js +59 -0
  137. package/lib/rules/prefer-to-have-length.js +57 -0
  138. package/lib/rules/prefer-todo.js +72 -0
  139. package/lib/rules/require-hook.js +93 -0
  140. package/lib/rules/require-to-throw-message.js +45 -0
  141. package/lib/rules/require-top-level-describe.js +86 -0
  142. package/lib/rules/unbound-method.js +86 -0
  143. package/lib/rules/utils/accessors.js +127 -0
  144. package/lib/rules/utils/ast-utils.js +59 -0
  145. package/lib/rules/utils/detectJestVersion.js +24 -0
  146. package/lib/rules/utils/followTypeAssertionChain.js +10 -0
  147. package/lib/rules/utils/index.js +71 -0
  148. package/lib/rules/utils/misc.js +168 -0
  149. package/lib/rules/utils/padding.js +308 -0
  150. package/lib/rules/utils/parseJestFnCall.js +325 -0
  151. package/lib/rules/utils/ts.js +73 -0
  152. package/lib/rules/valid-describe-callback.js +94 -0
  153. package/lib/rules/valid-expect-in-promise.js +311 -0
  154. package/lib/rules/valid-expect-with-promise.js +54 -0
  155. package/lib/rules/valid-expect.js +302 -0
  156. package/lib/rules/valid-mock-module-path.js +90 -0
  157. package/lib/rules/valid-title.js +252 -0
  158. package/package.json +164 -0
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _utils = require("@typescript-eslint/utils");
8
+ var _utils2 = require("./utils");
9
+ const findNodeObject = node => {
10
+ if ('object' in node) {
11
+ return node.object;
12
+ }
13
+ if (node.callee.type === _utils.AST_NODE_TYPES.MemberExpression) {
14
+ return node.callee.object;
15
+ }
16
+ return null;
17
+ };
18
+ const getJestFnCall = node => {
19
+ if (node.type !== _utils.AST_NODE_TYPES.CallExpression && node.type !== _utils.AST_NODE_TYPES.MemberExpression) {
20
+ return null;
21
+ }
22
+ const obj = findNodeObject(node);
23
+ if (!obj) {
24
+ return null;
25
+ }
26
+ if (obj.type === _utils.AST_NODE_TYPES.Identifier) {
27
+ return node.type === _utils.AST_NODE_TYPES.CallExpression && (0, _utils2.getNodeName)(node.callee) === 'jest.fn' ? node : null;
28
+ }
29
+ return getJestFnCall(obj);
30
+ };
31
+ const getAutoFixMockImplementation = (jestFnCall, context) => {
32
+ const hasMockImplementationAlready = jestFnCall.parent.type === _utils.AST_NODE_TYPES.MemberExpression && jestFnCall.parent.property.type === _utils.AST_NODE_TYPES.Identifier && jestFnCall.parent.property.name === 'mockImplementation';
33
+ if (hasMockImplementationAlready) {
34
+ return '';
35
+ }
36
+ const [arg] = jestFnCall.arguments;
37
+ const argSource = arg && context.sourceCode.getText(arg);
38
+ return argSource ? `.mockImplementation(${argSource})` : '.mockImplementation()';
39
+ };
40
+ var _default = exports.default = (0, _utils2.createRule)({
41
+ name: __filename,
42
+ meta: {
43
+ docs: {
44
+ description: 'Suggest using `jest.spyOn()`'
45
+ },
46
+ messages: {
47
+ useJestSpyOn: 'Use jest.spyOn() instead'
48
+ },
49
+ fixable: 'code',
50
+ schema: [],
51
+ type: 'suggestion'
52
+ },
53
+ defaultOptions: [],
54
+ create(context) {
55
+ return {
56
+ AssignmentExpression(node) {
57
+ const {
58
+ left,
59
+ right
60
+ } = node;
61
+ if (left.type !== _utils.AST_NODE_TYPES.MemberExpression) {
62
+ return;
63
+ }
64
+ const jestFnCall = getJestFnCall(right);
65
+ if (!jestFnCall) {
66
+ return;
67
+ }
68
+ context.report({
69
+ node,
70
+ messageId: 'useJestSpyOn',
71
+ fix(fixer) {
72
+ const leftPropQuote = left.property.type === _utils.AST_NODE_TYPES.Identifier && !left.computed ? "'" : '';
73
+ const mockImplementation = getAutoFixMockImplementation(jestFnCall, context);
74
+ return [fixer.insertTextBefore(left, `jest.spyOn(`), fixer.replaceTextRange([left.object.range[1], left.property.range[0]], `, ${leftPropQuote}`), fixer.replaceTextRange([left.property.range[1], jestFnCall.range[1]], `${leftPropQuote})${mockImplementation}`)];
75
+ }
76
+ });
77
+ }
78
+ };
79
+ }
80
+ });
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _utils = require("./utils");
8
+ var _default = exports.default = (0, _utils.createRule)({
9
+ name: __filename,
10
+ meta: {
11
+ docs: {
12
+ description: 'Suggest using `toStrictEqual()`'
13
+ },
14
+ messages: {
15
+ useToStrictEqual: 'Use `toStrictEqual()` instead',
16
+ suggestReplaceWithStrictEqual: 'Replace with `toStrictEqual()`'
17
+ },
18
+ type: 'suggestion',
19
+ schema: [],
20
+ hasSuggestions: true
21
+ },
22
+ defaultOptions: [],
23
+ create(context) {
24
+ return {
25
+ CallExpression(node) {
26
+ const jestFnCall = (0, _utils.parseJestFnCall)(node, context);
27
+ if (jestFnCall?.type !== 'expect') {
28
+ return;
29
+ }
30
+ const {
31
+ matcher
32
+ } = jestFnCall;
33
+ if ((0, _utils.isSupportedAccessor)(matcher, 'toEqual')) {
34
+ context.report({
35
+ messageId: 'useToStrictEqual',
36
+ node: matcher,
37
+ suggest: [{
38
+ messageId: 'suggestReplaceWithStrictEqual',
39
+ fix: fixer => [(0, _utils.replaceAccessorFixer)(fixer, matcher, _utils.EqualityMatcher.toStrictEqual)]
40
+ }]
41
+ });
42
+ }
43
+ }
44
+ };
45
+ }
46
+ });
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _utils = require("@typescript-eslint/utils");
8
+ var _utils2 = require("./utils");
9
+ const isNullLiteral = node => node.type === _utils.AST_NODE_TYPES.Literal && node.value === null;
10
+
11
+ /**
12
+ * Checks if the given `ParsedEqualityMatcherCall` is a call to one of the equality matchers,
13
+ * with a `null` literal as the sole argument.
14
+ */
15
+ const isNullEqualityMatcher = expectFnCall => isNullLiteral((0, _utils2.getFirstMatcherArg)(expectFnCall));
16
+ const isFirstArgumentIdentifier = (expectFnCall, name) => (0, _utils2.isIdentifier)((0, _utils2.getFirstMatcherArg)(expectFnCall), name);
17
+ const shouldUseToBe = expectFnCall => {
18
+ let firstArg = (0, _utils2.getFirstMatcherArg)(expectFnCall);
19
+ if (firstArg.type === _utils.AST_NODE_TYPES.UnaryExpression && firstArg.operator === '-') {
20
+ firstArg = firstArg.argument;
21
+ }
22
+ if (firstArg.type === _utils.AST_NODE_TYPES.Literal) {
23
+ // regex literals are classed as literals, but they're actually objects
24
+ // which means "toBe" will give different results than other matchers
25
+ return !('regex' in firstArg);
26
+ }
27
+ return firstArg.type === _utils.AST_NODE_TYPES.TemplateLiteral;
28
+ };
29
+ const reportPreferToBe = (context, whatToBe, expectFnCall, func, modifierNode) => {
30
+ context.report({
31
+ messageId: `useToBe${whatToBe}`,
32
+ fix(fixer) {
33
+ const fixes = [(0, _utils2.replaceAccessorFixer)(fixer, expectFnCall.matcher, `toBe${whatToBe}`)];
34
+ if (expectFnCall.args.length && whatToBe !== '') {
35
+ fixes.push((0, _utils2.removeExtraArgumentsFixer)(fixer, context, func, 0));
36
+ }
37
+ if (modifierNode) {
38
+ fixes.push(fixer.removeRange([modifierNode.range[0] - 1, modifierNode.range[1]]));
39
+ }
40
+ return fixes;
41
+ },
42
+ node: expectFnCall.matcher
43
+ });
44
+ };
45
+ var _default = exports.default = (0, _utils2.createRule)({
46
+ name: __filename,
47
+ meta: {
48
+ docs: {
49
+ description: 'Suggest using `toBe()` for primitive literals'
50
+ },
51
+ messages: {
52
+ useToBe: 'Use `toBe` when expecting primitive literals',
53
+ useToBeUndefined: 'Use `toBeUndefined` instead',
54
+ useToBeDefined: 'Use `toBeDefined` instead',
55
+ useToBeNull: 'Use `toBeNull` instead',
56
+ useToBeNaN: 'Use `toBeNaN` instead'
57
+ },
58
+ fixable: 'code',
59
+ type: 'suggestion',
60
+ schema: []
61
+ },
62
+ defaultOptions: [],
63
+ create(context) {
64
+ return {
65
+ CallExpression(node) {
66
+ const jestFnCall = (0, _utils2.parseJestFnCall)(node, context);
67
+ if (jestFnCall?.type !== 'expect') {
68
+ return;
69
+ }
70
+ const matcherName = (0, _utils2.getAccessorValue)(jestFnCall.matcher);
71
+ const notModifier = jestFnCall.modifiers.find(nod => (0, _utils2.getAccessorValue)(nod) === 'not');
72
+ if (notModifier && ['toBeUndefined', 'toBeDefined'].includes(matcherName)) {
73
+ reportPreferToBe(context, matcherName === 'toBeDefined' ? 'Undefined' : 'Defined', jestFnCall, node, notModifier);
74
+ return;
75
+ }
76
+ if (!_utils2.EqualityMatcher.hasOwnProperty(matcherName) || jestFnCall.args.length === 0) {
77
+ return;
78
+ }
79
+ if (isNullEqualityMatcher(jestFnCall)) {
80
+ reportPreferToBe(context, 'Null', jestFnCall, node);
81
+ return;
82
+ }
83
+ if (isFirstArgumentIdentifier(jestFnCall, 'undefined')) {
84
+ const name = notModifier ? 'Defined' : 'Undefined';
85
+ reportPreferToBe(context, name, jestFnCall, node, notModifier);
86
+ return;
87
+ }
88
+ if (isFirstArgumentIdentifier(jestFnCall, 'NaN')) {
89
+ reportPreferToBe(context, 'NaN', jestFnCall, node);
90
+ return;
91
+ }
92
+ if (shouldUseToBe(jestFnCall) && matcherName !== _utils2.EqualityMatcher.toBe) {
93
+ reportPreferToBe(context, '', jestFnCall, node);
94
+ }
95
+ }
96
+ };
97
+ }
98
+ });
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _utils = require("@typescript-eslint/utils");
8
+ var _utils2 = require("./utils");
9
+ /**
10
+ * Checks if the given `node` is a `CallExpression` representing the calling
11
+ * of an `includes`-like method that can be 'fixed' (using `toContain`).
12
+ *
13
+ * @param {CallExpression} node
14
+ *
15
+ * @return {node is FixableIncludesCallExpression}
16
+ */
17
+ const isFixableIncludesCallExpression = node => node.type === _utils.AST_NODE_TYPES.CallExpression && node.callee.type === _utils.AST_NODE_TYPES.MemberExpression && (0, _utils2.isSupportedAccessor)(node.callee.property, 'includes') && (0, _utils2.hasOnlyOneArgument)(node) && node.arguments[0].type !== _utils.AST_NODE_TYPES.SpreadElement;
18
+
19
+ // expect(array.includes(<value>)[not.]{toBe,toEqual}(<boolean>)
20
+ var _default = exports.default = (0, _utils2.createRule)({
21
+ name: __filename,
22
+ meta: {
23
+ docs: {
24
+ description: 'Suggest using `toContain()`'
25
+ },
26
+ messages: {
27
+ useToContain: 'Use toContain() instead'
28
+ },
29
+ fixable: 'code',
30
+ type: 'suggestion',
31
+ schema: []
32
+ },
33
+ defaultOptions: [],
34
+ create(context) {
35
+ return {
36
+ CallExpression(node) {
37
+ const jestFnCall = (0, _utils2.parseJestFnCall)(node, context);
38
+ if (jestFnCall?.type !== 'expect' || jestFnCall.args.length === 0) {
39
+ return;
40
+ }
41
+ const {
42
+ parent: expect
43
+ } = jestFnCall.head.node;
44
+ if (expect.type !== _utils.AST_NODE_TYPES.CallExpression) {
45
+ return;
46
+ }
47
+ const {
48
+ arguments: [includesCall],
49
+ range: [, expectCallEnd]
50
+ } = expect;
51
+ const {
52
+ matcher
53
+ } = jestFnCall;
54
+ const matcherArg = (0, _utils2.getFirstMatcherArg)(jestFnCall);
55
+ if (!includesCall || matcherArg.type === _utils.AST_NODE_TYPES.SpreadElement || !_utils2.EqualityMatcher.hasOwnProperty((0, _utils2.getAccessorValue)(matcher)) || !(0, _utils2.isBooleanLiteral)(matcherArg) || !isFixableIncludesCallExpression(includesCall)) {
56
+ return;
57
+ }
58
+ const hasNot = jestFnCall.modifiers.some(nod => (0, _utils2.getAccessorValue)(nod) === 'not');
59
+ context.report({
60
+ fix(fixer) {
61
+ const {
62
+ sourceCode
63
+ } = context;
64
+
65
+ // we need to negate the expectation if the current expected
66
+ // value is itself negated by the "not" modifier
67
+ const addNotModifier = matcherArg.value === hasNot;
68
+ return [
69
+ // remove the "includes" call entirely
70
+ fixer.removeRange([includesCall.callee.property.range[0] - 1, includesCall.range[1]]),
71
+ // replace the current matcher with "toContain", adding "not" if needed
72
+ fixer.replaceTextRange([expectCallEnd, matcher.parent.range[1]], addNotModifier ? `.${_utils2.ModifierName.not}.toContain` : '.toContain'),
73
+ // replace the matcher argument with the value from the "includes"
74
+ fixer.replaceText(jestFnCall.args[0], sourceCode.getText(includesCall.arguments[0]))];
75
+ },
76
+ messageId: 'useToContain',
77
+ node: matcher
78
+ });
79
+ }
80
+ };
81
+ }
82
+ });
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _utils = require("@typescript-eslint/utils");
8
+ var _utils2 = require("./utils");
9
+ var _default = exports.default = (0, _utils2.createRule)({
10
+ name: __filename,
11
+ meta: {
12
+ fixable: 'code',
13
+ docs: {
14
+ description: 'Suggest using `toHaveBeenCalledTimes()`'
15
+ },
16
+ messages: {
17
+ preferMatcher: 'Prefer `toHaveBeenCalledTimes`'
18
+ },
19
+ type: 'suggestion',
20
+ schema: []
21
+ },
22
+ defaultOptions: [],
23
+ create(context) {
24
+ return {
25
+ CallExpression(node) {
26
+ const jestFnCall = (0, _utils2.parseJestFnCall)(node, context);
27
+ if (jestFnCall?.type !== 'expect') {
28
+ return;
29
+ }
30
+ const {
31
+ parent: expect
32
+ } = jestFnCall.head.node;
33
+ if (expect.type !== _utils.AST_NODE_TYPES.CallExpression) {
34
+ return;
35
+ }
36
+ const {
37
+ matcher
38
+ } = jestFnCall;
39
+ if (!(0, _utils2.isSupportedAccessor)(matcher, 'toHaveLength')) {
40
+ return;
41
+ }
42
+ const [argument] = expect.arguments;
43
+
44
+ // check if the last property in the chain is `calls`
45
+ if (argument.type !== _utils.AST_NODE_TYPES.MemberExpression || !(0, _utils2.isSupportedAccessor)(argument.property, 'calls')) {
46
+ return;
47
+ }
48
+ const {
49
+ object
50
+ } = argument;
51
+
52
+ // check if the second-to-last property in the chain is `mock`
53
+ if (object.type !== _utils.AST_NODE_TYPES.MemberExpression || !(0, _utils2.isSupportedAccessor)(object.property, 'mock')) {
54
+ return;
55
+ }
56
+ context.report({
57
+ messageId: 'preferMatcher',
58
+ node: matcher,
59
+ fix(fixer) {
60
+ return [
61
+ // remove the "mock.calls" accessor chain
62
+ fixer.removeRange([object.property.range[0] - 1, argument.range[1]]),
63
+ // replace the current matcher with "toHaveBeenCalledTimes"
64
+ fixer.replaceTextRange([matcher.parent.object.range[1], matcher.parent.range[1]], '.toHaveBeenCalledTimes')];
65
+ }
66
+ });
67
+ }
68
+ };
69
+ }
70
+ });
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _utils = require("@typescript-eslint/utils");
8
+ var _utils2 = require("./utils");
9
+ var _default = exports.default = (0, _utils2.createRule)({
10
+ name: __filename,
11
+ meta: {
12
+ docs: {
13
+ description: 'Suggest using `toHaveBeenCalled`'
14
+ },
15
+ messages: {
16
+ preferMatcher: 'Use `toHaveBeenCalled`'
17
+ },
18
+ fixable: 'code',
19
+ type: 'suggestion',
20
+ schema: []
21
+ },
22
+ defaultOptions: [],
23
+ create(context) {
24
+ return {
25
+ CallExpression(node) {
26
+ const jestFnCall = (0, _utils2.parseJestFnCall)(node, context);
27
+ if (jestFnCall?.type !== 'expect') {
28
+ return;
29
+ }
30
+ const {
31
+ matcher
32
+ } = jestFnCall;
33
+ if (!['toBeCalledTimes', 'toHaveBeenCalledTimes'].includes((0, _utils2.getAccessorValue)(matcher))) {
34
+ return;
35
+ }
36
+ const arg = (0, _utils2.getFirstMatcherArg)(jestFnCall);
37
+ if (arg.type !== _utils.AST_NODE_TYPES.Literal || arg.value !== 0) {
38
+ return;
39
+ }
40
+ const notModifier = jestFnCall.modifiers.find(nod => (0, _utils2.getAccessorValue)(nod) === 'not');
41
+ context.report({
42
+ messageId: 'preferMatcher',
43
+ node: matcher,
44
+ fix(fixer) {
45
+ let replacementMatcher = '.not.toHaveBeenCalled';
46
+ if (notModifier) {
47
+ replacementMatcher = '.toHaveBeenCalled';
48
+ }
49
+ return [
50
+ // remove all the arguments to the matcher
51
+ fixer.removeRange([jestFnCall.args[0].range[0], jestFnCall.args[jestFnCall.args.length - 1].range[1]]),
52
+ // replace the current matcher with "(.not).toHaveBeenCalled"
53
+ fixer.replaceTextRange([(notModifier || matcher).parent.object.range[1], jestFnCall.matcher.parent.range[1]], replacementMatcher)];
54
+ }
55
+ });
56
+ }
57
+ };
58
+ }
59
+ });
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _utils = require("@typescript-eslint/utils");
8
+ var _utils2 = require("./utils");
9
+ var _default = exports.default = (0, _utils2.createRule)({
10
+ name: __filename,
11
+ meta: {
12
+ docs: {
13
+ description: 'Suggest using `toHaveLength()`'
14
+ },
15
+ messages: {
16
+ useToHaveLength: 'Use toHaveLength() instead'
17
+ },
18
+ fixable: 'code',
19
+ type: 'suggestion',
20
+ schema: []
21
+ },
22
+ defaultOptions: [],
23
+ create(context) {
24
+ return {
25
+ CallExpression(node) {
26
+ const jestFnCall = (0, _utils2.parseJestFnCall)(node, context);
27
+ if (jestFnCall?.type !== 'expect') {
28
+ return;
29
+ }
30
+ const {
31
+ parent: expect
32
+ } = jestFnCall.head.node;
33
+ if (expect.type !== _utils.AST_NODE_TYPES.CallExpression) {
34
+ return;
35
+ }
36
+ const [argument] = expect.arguments;
37
+ const {
38
+ matcher
39
+ } = jestFnCall;
40
+ if (!_utils2.EqualityMatcher.hasOwnProperty((0, _utils2.getAccessorValue)(matcher)) || argument.type !== _utils.AST_NODE_TYPES.MemberExpression || !(0, _utils2.isSupportedAccessor)(argument.property, 'length')) {
41
+ return;
42
+ }
43
+ context.report({
44
+ fix(fixer) {
45
+ return [
46
+ // remove the "length" property accessor
47
+ fixer.removeRange([argument.property.range[0] - 1, argument.range[1]]),
48
+ // replace the current matcher with "toHaveLength"
49
+ fixer.replaceTextRange([matcher.parent.object.range[1], matcher.parent.range[1]], '.toHaveLength')];
50
+ },
51
+ messageId: 'useToHaveLength',
52
+ node: matcher
53
+ });
54
+ }
55
+ };
56
+ }
57
+ });
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _utils = require("@typescript-eslint/utils");
8
+ var _utils2 = require("./utils");
9
+ function isEmptyFunction(node) {
10
+ if (!(0, _utils2.isFunction)(node)) {
11
+ return false;
12
+ }
13
+ return node.body.type === _utils.AST_NODE_TYPES.BlockStatement && !node.body.body.length;
14
+ }
15
+ function createTodoFixer(jestFnCall, fixer) {
16
+ if (jestFnCall.members.length) {
17
+ return (0, _utils2.replaceAccessorFixer)(fixer, jestFnCall.members[0], 'todo');
18
+ }
19
+ return fixer.replaceText(jestFnCall.head.node, `${jestFnCall.head.local}.todo`);
20
+ }
21
+ const isTargetedTestCase = jestFnCall => {
22
+ if (jestFnCall.members.some(s => (0, _utils2.getAccessorValue)(s) !== 'skip')) {
23
+ return false;
24
+ }
25
+
26
+ // todo: we should support this too (needs custom fixer)
27
+ if (jestFnCall.name.startsWith('x')) {
28
+ return false;
29
+ }
30
+ return !jestFnCall.name.startsWith('f');
31
+ };
32
+ var _default = exports.default = (0, _utils2.createRule)({
33
+ name: __filename,
34
+ meta: {
35
+ docs: {
36
+ description: 'Suggest using `test.todo`'
37
+ },
38
+ messages: {
39
+ emptyTest: 'Prefer todo test case over empty test case',
40
+ unimplementedTest: 'Prefer todo test case over unimplemented test case'
41
+ },
42
+ fixable: 'code',
43
+ schema: [],
44
+ type: 'layout'
45
+ },
46
+ defaultOptions: [],
47
+ create(context) {
48
+ return {
49
+ CallExpression(node) {
50
+ const [title, callback] = node.arguments;
51
+ const jestFnCall = (0, _utils2.parseJestFnCall)(node, context);
52
+ if (!title || jestFnCall?.type !== 'test' || !isTargetedTestCase(jestFnCall) || !(0, _utils2.isStringNode)(title)) {
53
+ return;
54
+ }
55
+ if (callback && isEmptyFunction(callback)) {
56
+ context.report({
57
+ messageId: 'emptyTest',
58
+ node,
59
+ fix: fixer => [fixer.removeRange([title.range[1], callback.range[1]]), createTodoFixer(jestFnCall, fixer)]
60
+ });
61
+ }
62
+ if ((0, _utils2.hasOnlyOneArgument)(node)) {
63
+ context.report({
64
+ messageId: 'unimplementedTest',
65
+ node,
66
+ fix: fixer => createTodoFixer(jestFnCall, fixer)
67
+ });
68
+ }
69
+ }
70
+ };
71
+ }
72
+ });
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _utils = require("@typescript-eslint/utils");
8
+ var _utils2 = require("./utils");
9
+ const isJestFnCall = (node, context) => {
10
+ if ((0, _utils2.parseJestFnCall)(node, context)) {
11
+ return true;
12
+ }
13
+ return !!(0, _utils2.getNodeName)(node)?.startsWith('jest.');
14
+ };
15
+ const isNullOrUndefined = node => {
16
+ return node.type === _utils.AST_NODE_TYPES.Literal && node.value === null || (0, _utils2.isIdentifier)(node, 'undefined');
17
+ };
18
+ const shouldBeInHook = (node, context, allowedFunctionCalls = []) => {
19
+ switch (node.type) {
20
+ case _utils.AST_NODE_TYPES.ExpressionStatement:
21
+ return shouldBeInHook(node.expression, context, allowedFunctionCalls);
22
+ case _utils.AST_NODE_TYPES.CallExpression:
23
+ return !(isJestFnCall(node, context) || allowedFunctionCalls.includes((0, _utils2.getNodeName)(node)));
24
+ case _utils.AST_NODE_TYPES.VariableDeclaration:
25
+ {
26
+ if (node.kind === 'const') {
27
+ return false;
28
+ }
29
+ return node.declarations.some(({
30
+ init
31
+ }) => init !== null && !isNullOrUndefined(init));
32
+ }
33
+ default:
34
+ return false;
35
+ }
36
+ };
37
+ var _default = exports.default = (0, _utils2.createRule)({
38
+ name: __filename,
39
+ meta: {
40
+ docs: {
41
+ description: 'Require setup and teardown code to be within a hook'
42
+ },
43
+ messages: {
44
+ useHook: 'This should be done within a hook'
45
+ },
46
+ type: 'suggestion',
47
+ schema: [{
48
+ type: 'object',
49
+ properties: {
50
+ allowedFunctionCalls: {
51
+ type: 'array',
52
+ items: {
53
+ type: 'string'
54
+ }
55
+ }
56
+ },
57
+ additionalProperties: false
58
+ }]
59
+ },
60
+ defaultOptions: [{
61
+ allowedFunctionCalls: []
62
+ }],
63
+ create(context) {
64
+ const {
65
+ allowedFunctionCalls
66
+ } = context.options[0] ?? {};
67
+ const checkBlockBody = body => {
68
+ for (const statement of body) {
69
+ if (shouldBeInHook(statement, context, allowedFunctionCalls)) {
70
+ context.report({
71
+ node: statement,
72
+ messageId: 'useHook'
73
+ });
74
+ }
75
+ }
76
+ };
77
+ return {
78
+ Program(program) {
79
+ checkBlockBody(program.body);
80
+ },
81
+ CallExpression(node) {
82
+ if (!(0, _utils2.isTypeOfJestFnCall)(node, context, ['describe']) || node.arguments.length < 2) {
83
+ return;
84
+ }
85
+ const [, testFn] = node.arguments;
86
+ if (!(0, _utils2.isFunction)(testFn) || testFn.body.type !== _utils.AST_NODE_TYPES.BlockStatement) {
87
+ return;
88
+ }
89
+ checkBlockBody(testFn.body.body);
90
+ }
91
+ };
92
+ }
93
+ });