@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,73 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.isBuiltinSymbolLike = isBuiltinSymbolLike;
7
+ let SymbolFlags;
8
+ let TypeFlags;
9
+ function isSymbolFromDefaultLibrary(program, symbol) {
10
+ /* istanbul ignore next */
11
+ const declarations = symbol.getDeclarations() ?? [];
12
+ for (const declaration of declarations) {
13
+ const sourceFile = declaration.getSourceFile();
14
+
15
+ /* istanbul ignore else */
16
+ if (program.isSourceFileDefaultLibrary(sourceFile)) {
17
+ return true;
18
+ }
19
+ }
20
+
21
+ /* istanbul ignore next */
22
+ return false;
23
+ }
24
+ function isBuiltinSymbolLike(program, type, symbolName) {
25
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
26
+ ({
27
+ TypeFlags,
28
+ SymbolFlags
29
+ } = require('typescript'));
30
+ return isBuiltinSymbolLikeRecurser(program, type, subType => {
31
+ const symbol = subType.getSymbol();
32
+ if (!symbol) {
33
+ return false;
34
+ }
35
+ const actualSymbolName = symbol.getName();
36
+ if (actualSymbolName === symbolName && isSymbolFromDefaultLibrary(program, symbol)) {
37
+ return true;
38
+ }
39
+ return null;
40
+ });
41
+ }
42
+ function isBuiltinSymbolLikeRecurser(program, type, predicate) {
43
+ if (type.isIntersection()) {
44
+ return type.types.some(t => isBuiltinSymbolLikeRecurser(program, t, predicate));
45
+ }
46
+ if (type.isUnion()) {
47
+ return type.types.every(t => isBuiltinSymbolLikeRecurser(program, t, predicate));
48
+ }
49
+ if (isTypeParameter(type)) {
50
+ const t = type.getConstraint();
51
+ if (t) {
52
+ return isBuiltinSymbolLikeRecurser(program, t, predicate);
53
+ }
54
+ return false;
55
+ }
56
+ const predicateResult = predicate(type);
57
+ if (typeof predicateResult === 'boolean') {
58
+ return predicateResult;
59
+ }
60
+ const symbol = type.getSymbol();
61
+ if (symbol && symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) {
62
+ const checker = program.getTypeChecker();
63
+ for (const baseType of checker.getBaseTypes(type)) {
64
+ if (isBuiltinSymbolLikeRecurser(program, baseType, predicate)) {
65
+ return true;
66
+ }
67
+ }
68
+ }
69
+ return false;
70
+ }
71
+ function isTypeParameter(type) {
72
+ return (type.flags & TypeFlags.TypeParameter) !== 0;
73
+ }
@@ -0,0 +1,94 @@
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 paramsLocation = params => {
10
+ const [first] = params;
11
+ const last = params[params.length - 1];
12
+ return {
13
+ start: first.loc.start,
14
+ end: last.loc.end
15
+ };
16
+ };
17
+ var _default = exports.default = (0, _utils2.createRule)({
18
+ name: __filename,
19
+ meta: {
20
+ type: 'problem',
21
+ docs: {
22
+ description: 'Enforce valid `describe()` callback'
23
+ },
24
+ messages: {
25
+ nameAndCallback: 'Describe requires name and callback arguments',
26
+ secondArgumentMustBeFunction: 'Second argument must be function',
27
+ noAsyncDescribeCallback: 'No async describe callback',
28
+ unexpectedDescribeArgument: 'Unexpected argument(s) in describe callback',
29
+ unexpectedReturnInDescribe: 'Unexpected return statement in describe callback'
30
+ },
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 !== 'describe') {
39
+ return;
40
+ }
41
+ if (node.arguments.length < 1) {
42
+ context.report({
43
+ messageId: 'nameAndCallback',
44
+ loc: node.loc
45
+ });
46
+ return;
47
+ }
48
+ const [, callback] = node.arguments;
49
+ if (!callback) {
50
+ context.report({
51
+ messageId: 'nameAndCallback',
52
+ loc: paramsLocation(node.arguments)
53
+ });
54
+ return;
55
+ }
56
+ if (!(0, _utils2.isFunction)(callback)) {
57
+ context.report({
58
+ messageId: 'secondArgumentMustBeFunction',
59
+ loc: paramsLocation(node.arguments)
60
+ });
61
+ return;
62
+ }
63
+ if (callback.async) {
64
+ context.report({
65
+ messageId: 'noAsyncDescribeCallback',
66
+ node: callback
67
+ });
68
+ }
69
+ if (jestFnCall.members.every(s => (0, _utils2.getAccessorValue)(s) !== 'each') && callback.params.length) {
70
+ context.report({
71
+ messageId: 'unexpectedDescribeArgument',
72
+ loc: paramsLocation(callback.params)
73
+ });
74
+ }
75
+ if (callback.body.type === _utils.AST_NODE_TYPES.CallExpression) {
76
+ context.report({
77
+ messageId: 'unexpectedReturnInDescribe',
78
+ node: callback
79
+ });
80
+ }
81
+ if (callback.body.type === _utils.AST_NODE_TYPES.BlockStatement) {
82
+ callback.body.body.forEach(node => {
83
+ if (node.type === _utils.AST_NODE_TYPES.ReturnStatement) {
84
+ context.report({
85
+ messageId: 'unexpectedReturnInDescribe',
86
+ node
87
+ });
88
+ }
89
+ });
90
+ }
91
+ }
92
+ };
93
+ }
94
+ });
@@ -0,0 +1,311 @@
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 isPromiseChainCall = node => {
10
+ if (node.type === _utils.AST_NODE_TYPES.CallExpression && node.callee.type === _utils.AST_NODE_TYPES.MemberExpression && (0, _utils2.isSupportedAccessor)(node.callee.property)) {
11
+ // promise methods should have at least 1 argument
12
+ if (node.arguments.length === 0) {
13
+ return false;
14
+ }
15
+ switch ((0, _utils2.getAccessorValue)(node.callee.property)) {
16
+ case 'then':
17
+ return node.arguments.length < 3;
18
+ case 'catch':
19
+ case 'finally':
20
+ return node.arguments.length < 2;
21
+ }
22
+ }
23
+ return false;
24
+ };
25
+ const isTestCaseCallWithCallbackArg = (node, context) => {
26
+ const jestCallFn = (0, _utils2.parseJestFnCall)(node, context);
27
+ if (jestCallFn?.type !== 'test') {
28
+ return false;
29
+ }
30
+ const isJestEach = jestCallFn.members.some(s => (0, _utils2.getAccessorValue)(s) === 'each');
31
+ if (isJestEach && node.callee.type !== _utils.AST_NODE_TYPES.TaggedTemplateExpression) {
32
+ // isJestEach but not a TaggedTemplateExpression, so this must be
33
+ // the `jest.each([])()` syntax which this rule doesn't support due
34
+ // to its complexity (see jest-community/eslint-plugin-jest#710)
35
+ // so we return true to trigger bailout
36
+ return true;
37
+ }
38
+ const [, callback] = node.arguments;
39
+ const callbackArgIndex = Number(isJestEach);
40
+ return callback && (0, _utils2.isFunction)(callback) && callback.params.length === 1 + callbackArgIndex;
41
+ };
42
+ const isPromiseMethodThatUsesValue = (node, identifier) => {
43
+ const {
44
+ name
45
+ } = identifier;
46
+ if (node.argument === null) {
47
+ return false;
48
+ }
49
+ if (node.argument.type === _utils.AST_NODE_TYPES.CallExpression && node.argument.arguments.length > 0) {
50
+ const nodeName = (0, _utils2.getNodeName)(node.argument);
51
+ if (['Promise.all', 'Promise.allSettled'].includes(nodeName)) {
52
+ const [firstArg] = node.argument.arguments;
53
+ if (firstArg.type === _utils.AST_NODE_TYPES.ArrayExpression && firstArg.elements.some(nod => nod && (0, _utils2.isIdentifier)(nod, name))) {
54
+ return true;
55
+ }
56
+ }
57
+ if (['Promise.resolve', 'Promise.reject'].includes(nodeName) && node.argument.arguments.length === 1) {
58
+ return (0, _utils2.isIdentifier)(node.argument.arguments[0], name);
59
+ }
60
+ }
61
+ return (0, _utils2.isIdentifier)(node.argument, name);
62
+ };
63
+
64
+ /**
65
+ * Attempts to determine if the runtime value represented by the given `identifier`
66
+ * is `await`ed within the given array of elements
67
+ */
68
+ const isValueAwaitedInElements = (name, elements) => {
69
+ for (const element of elements) {
70
+ if (element?.type === _utils.AST_NODE_TYPES.AwaitExpression && (0, _utils2.isIdentifier)(element.argument, name)) {
71
+ return true;
72
+ }
73
+ if (element?.type === _utils.AST_NODE_TYPES.ArrayExpression && isValueAwaitedInElements(name, element.elements)) {
74
+ return true;
75
+ }
76
+ }
77
+ return false;
78
+ };
79
+
80
+ /**
81
+ * Attempts to determine if the runtime value represented by the given `identifier`
82
+ * is `await`ed as an argument along the given call expression
83
+ */
84
+ const isValueAwaitedInArguments = (name, call) => {
85
+ let node = call;
86
+ while (node) {
87
+ if (node.type === _utils.AST_NODE_TYPES.CallExpression) {
88
+ if (isValueAwaitedInElements(name, node.arguments)) {
89
+ return true;
90
+ }
91
+ node = node.callee;
92
+ }
93
+ if (node.type !== _utils.AST_NODE_TYPES.MemberExpression) {
94
+ break;
95
+ }
96
+ node = node.object;
97
+ }
98
+ return false;
99
+ };
100
+ const getLeftMostCallExpression = call => {
101
+ let leftMostCallExpression = call;
102
+ let node = call;
103
+ while (node) {
104
+ if (node.type === _utils.AST_NODE_TYPES.CallExpression) {
105
+ leftMostCallExpression = node;
106
+ node = node.callee;
107
+ }
108
+ if (node.type !== _utils.AST_NODE_TYPES.MemberExpression) {
109
+ break;
110
+ }
111
+ node = node.object;
112
+ }
113
+ return leftMostCallExpression;
114
+ };
115
+
116
+ /**
117
+ * Attempts to determine if the runtime value represented by the given `identifier`
118
+ * is `await`ed or `return`ed within the given `body` of statements
119
+ */
120
+ const isValueAwaitedOrReturned = (identifier, body, context) => {
121
+ const {
122
+ name
123
+ } = identifier;
124
+ for (const node of body) {
125
+ // skip all nodes that are before this identifier, because they'd probably
126
+ // be affecting a different runtime value (e.g. due to reassignment)
127
+ if (node.range[0] <= identifier.range[0]) {
128
+ continue;
129
+ }
130
+ if (node.type === _utils.AST_NODE_TYPES.ReturnStatement) {
131
+ return isPromiseMethodThatUsesValue(node, identifier);
132
+ }
133
+ if (node.type === _utils.AST_NODE_TYPES.ExpressionStatement) {
134
+ // it's possible that we're awaiting the value as an argument
135
+ if (node.expression.type === _utils.AST_NODE_TYPES.CallExpression) {
136
+ if (isValueAwaitedInArguments(name, node.expression)) {
137
+ return true;
138
+ }
139
+ const leftMostCall = getLeftMostCallExpression(node.expression);
140
+ const jestFnCall = (0, _utils2.parseJestFnCall)(node.expression, context);
141
+ if (jestFnCall?.type === 'expect' && leftMostCall.arguments.length > 0 && (0, _utils2.isIdentifier)(leftMostCall.arguments[0], name)) {
142
+ if (jestFnCall.members.some(m => {
143
+ const v = (0, _utils2.getAccessorValue)(m);
144
+ return v === _utils2.ModifierName.resolves || v === _utils2.ModifierName.rejects;
145
+ })) {
146
+ return true;
147
+ }
148
+ }
149
+ }
150
+ if (node.expression.type === _utils.AST_NODE_TYPES.AwaitExpression && isPromiseMethodThatUsesValue(node.expression, identifier)) {
151
+ return true;
152
+ }
153
+
154
+ // (re)assignment changes the runtime value, so if we've not found an
155
+ // await or return already we act as if we've reached the end of the body
156
+ if (node.expression.type === _utils.AST_NODE_TYPES.AssignmentExpression) {
157
+ // unless we're assigning to the same identifier, in which case
158
+ // we might be chaining off the existing promise value
159
+ if ((0, _utils2.isIdentifier)(node.expression.left, name) && (0, _utils2.getNodeName)(node.expression.right)?.startsWith(`${name}.`) && isPromiseChainCall(node.expression.right)) {
160
+ continue;
161
+ }
162
+ break;
163
+ }
164
+ }
165
+ if (node.type === _utils.AST_NODE_TYPES.BlockStatement && isValueAwaitedOrReturned(identifier, node.body, context)) {
166
+ return true;
167
+ }
168
+ }
169
+ return false;
170
+ };
171
+ const findFirstBlockBodyUp = node => {
172
+ let parent = node;
173
+ while (parent) {
174
+ if (parent.type === _utils.AST_NODE_TYPES.BlockStatement) {
175
+ return parent.body;
176
+ }
177
+ parent = parent.parent;
178
+ }
179
+
180
+ /* istanbul ignore next */
181
+ throw new Error(`Could not find BlockStatement - please file a github issue at https://github.com/jest-community/eslint-plugin-jest`);
182
+ };
183
+ const isDirectlyWithinTestCaseCall = (node, context) => {
184
+ let parent = node;
185
+ while (parent) {
186
+ if ((0, _utils2.isFunction)(parent)) {
187
+ parent = parent.parent;
188
+ return parent.type === _utils.AST_NODE_TYPES.CallExpression && (0, _utils2.isTypeOfJestFnCall)(parent, context, ['test']);
189
+ }
190
+ parent = parent.parent;
191
+ }
192
+ return false;
193
+ };
194
+ const isVariableAwaitedOrReturned = (variable, context) => {
195
+ const body = findFirstBlockBodyUp(variable);
196
+
197
+ // it's pretty much impossible for us to track destructuring assignments,
198
+ // so we return true to bailout gracefully
199
+ if (!(0, _utils2.isIdentifier)(variable.id)) {
200
+ return true;
201
+ }
202
+ return isValueAwaitedOrReturned(variable.id, body, context);
203
+ };
204
+ var _default = exports.default = (0, _utils2.createRule)({
205
+ name: __filename,
206
+ meta: {
207
+ docs: {
208
+ description: 'Require promises that have expectations in their chain to be valid'
209
+ },
210
+ messages: {
211
+ expectInFloatingPromise: 'This promise should either be returned or awaited to ensure the expects in its chain are called'
212
+ },
213
+ type: 'suggestion',
214
+ schema: []
215
+ },
216
+ defaultOptions: [],
217
+ create(context) {
218
+ let inTestCaseWithDoneCallback = false;
219
+ // an array of booleans representing each promise chain we enter, with the
220
+ // boolean value representing if we think a given chain contains an expect
221
+ // in it's body.
222
+ //
223
+ // since we only care about the inner-most chain, we represent the state in
224
+ // reverse with the inner-most being the first item, as that makes it
225
+ // slightly less code to assign to by not needing to know the length
226
+ const chains = [];
227
+ return {
228
+ CallExpression(node) {
229
+ // there are too many ways that the done argument could be used with
230
+ // promises that contain expect that would make the promise safe for us
231
+ if (isTestCaseCallWithCallbackArg(node, context)) {
232
+ inTestCaseWithDoneCallback = true;
233
+ return;
234
+ }
235
+
236
+ // if this call expression is a promise chain, add it to the stack with
237
+ // value of "false", as we assume there are no expect calls initially
238
+ if (isPromiseChainCall(node)) {
239
+ chains.unshift(false);
240
+ return;
241
+ }
242
+
243
+ // if we're within a promise chain, and this call expression looks like
244
+ // an expect call, mark the deepest chain as having an expect call
245
+ if (chains.length > 0 && (0, _utils2.isTypeOfJestFnCall)(node, context, ['expect'])) {
246
+ chains[0] = true;
247
+ }
248
+ },
249
+ 'CallExpression:exit'(node) {
250
+ // there are too many ways that the "done" argument could be used to
251
+ // make promises containing expects safe in a test for us to be able to
252
+ // accurately check, so we just bail out completely if it's present
253
+ if (inTestCaseWithDoneCallback) {
254
+ if ((0, _utils2.isTypeOfJestFnCall)(node, context, ['test'])) {
255
+ inTestCaseWithDoneCallback = false;
256
+ }
257
+ return;
258
+ }
259
+ if (!isPromiseChainCall(node)) {
260
+ return;
261
+ }
262
+
263
+ // since we're exiting this call expression (which is a promise chain)
264
+ // we remove it from the stack of chains, since we're unwinding
265
+ const hasExpectCall = chains.shift();
266
+
267
+ // if the promise chain we're exiting doesn't contain an expect,
268
+ // then we don't need to check it for anything
269
+ if (!hasExpectCall) {
270
+ return;
271
+ }
272
+ const {
273
+ parent
274
+ } = (0, _utils2.findTopMostCallExpression)(node);
275
+
276
+ // if our parent is not directly within the test case, we stop checking
277
+ // because we're most likely in the body of a function being defined
278
+ // within the test, which we can't track
279
+ if (!isDirectlyWithinTestCaseCall(parent, context)) {
280
+ return;
281
+ }
282
+ switch (parent.type) {
283
+ case _utils.AST_NODE_TYPES.VariableDeclarator:
284
+ {
285
+ if (isVariableAwaitedOrReturned(parent, context)) {
286
+ return;
287
+ }
288
+ break;
289
+ }
290
+ case _utils.AST_NODE_TYPES.AssignmentExpression:
291
+ {
292
+ if (parent.left.type === _utils.AST_NODE_TYPES.Identifier && isValueAwaitedOrReturned(parent.left, findFirstBlockBodyUp(parent), context)) {
293
+ return;
294
+ }
295
+ break;
296
+ }
297
+ case _utils.AST_NODE_TYPES.ExpressionStatement:
298
+ break;
299
+ case _utils.AST_NODE_TYPES.ReturnStatement:
300
+ case _utils.AST_NODE_TYPES.AwaitExpression:
301
+ default:
302
+ return;
303
+ }
304
+ context.report({
305
+ messageId: 'expectInFloatingPromise',
306
+ node: parent
307
+ });
308
+ }
309
+ };
310
+ }
311
+ });
@@ -0,0 +1,54 @@
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: 'Require that `resolve` and `reject` modifiers are present (and only) for promise-like types',
14
+ requiresTypeChecking: true
15
+ },
16
+ messages: {
17
+ poorlyExpectedPromise: 'Subject is a promise so resolve or reject should be used',
18
+ unneededRejectResolve: 'Subject is not a promise so {{ modifier }} is not needed'
19
+ },
20
+ type: 'suggestion',
21
+ schema: []
22
+ },
23
+ defaultOptions: [],
24
+ create(context) {
25
+ const services = _utils.ESLintUtils.getParserServices(context);
26
+ return {
27
+ CallExpression(node) {
28
+ const jestFnCall = (0, _utils2.parseJestFnCall)(node, context);
29
+ if (jestFnCall?.type !== 'expect' || jestFnCall.head.node.parent.type !== _utils.AST_NODE_TYPES.CallExpression) {
30
+ return;
31
+ }
32
+ const [argument] = jestFnCall.head.node.parent.arguments;
33
+ const isPromiseLike = (0, _utils2.isBuiltinSymbolLike)(services.program, services.getTypeAtLocation(argument), 'Promise');
34
+ const promiseModifier = jestFnCall.modifiers.find(nod => (0, _utils2.getAccessorValue)(nod) !== 'not');
35
+ if (isPromiseLike && !promiseModifier) {
36
+ context.report({
37
+ messageId: 'poorlyExpectedPromise',
38
+ node
39
+ });
40
+ return;
41
+ }
42
+ if (!isPromiseLike && promiseModifier) {
43
+ context.report({
44
+ messageId: 'unneededRejectResolve',
45
+ data: {
46
+ modifier: (0, _utils2.getAccessorValue)(promiseModifier)
47
+ },
48
+ node: promiseModifier
49
+ });
50
+ }
51
+ }
52
+ };
53
+ }
54
+ });