eslint-plugin-jest 25.0.0-next.4 → 25.0.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 (36) hide show
  1. package/CHANGELOG.md +138 -0
  2. package/README.md +71 -49
  3. package/docs/rules/max-nested-describe.md +4 -5
  4. package/docs/rules/no-conditional-expect.md +57 -3
  5. package/docs/rules/no-deprecated-functions.md +5 -0
  6. package/docs/rules/no-done-callback.md +3 -3
  7. package/docs/rules/no-standalone-expect.md +3 -3
  8. package/docs/rules/no-test-return-statement.md +1 -2
  9. package/docs/rules/prefer-expect-resolves.md +53 -0
  10. package/docs/rules/{lowercase-name.md → prefer-lowercase-title.md} +1 -1
  11. package/docs/rules/prefer-to-be.md +53 -0
  12. package/docs/rules/require-hook.md +149 -0
  13. package/docs/rules/require-top-level-describe.md +28 -0
  14. package/docs/rules/{valid-describe.md → valid-describe-callback.md} +1 -1
  15. package/docs/rules/valid-expect-in-promise.md +55 -14
  16. package/docs/rules/valid-title.md +30 -2
  17. package/lib/index.js +2 -3
  18. package/lib/rules/detectJestVersion.js +29 -0
  19. package/lib/rules/no-deprecated-functions.js +9 -27
  20. package/lib/rules/no-identical-title.js +3 -3
  21. package/lib/rules/no-large-snapshots.js +1 -3
  22. package/lib/rules/prefer-expect-resolves.js +48 -0
  23. package/lib/rules/{lowercase-name.js → prefer-lowercase-title.js} +20 -1
  24. package/lib/rules/prefer-to-be.js +129 -0
  25. package/lib/rules/require-hook.js +87 -0
  26. package/lib/rules/require-top-level-describe.js +40 -6
  27. package/lib/rules/utils.js +6 -4
  28. package/lib/rules/{valid-describe.js → valid-describe-callback.js} +0 -0
  29. package/lib/rules/valid-expect-in-promise.js +252 -68
  30. package/lib/rules/valid-expect.js +10 -4
  31. package/lib/rules/valid-title.js +47 -47
  32. package/package.json +5 -5
  33. package/docs/rules/prefer-to-be-null.md +0 -33
  34. package/docs/rules/prefer-to-be-undefined.md +0 -33
  35. package/lib/rules/prefer-to-be-null.js +0 -67
  36. package/lib/rules/prefer-to-be-undefined.js +0 -67
@@ -0,0 +1,129 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+
8
+ var _experimentalUtils = require("@typescript-eslint/experimental-utils");
9
+
10
+ var _utils = require("./utils");
11
+
12
+ const isNullLiteral = node => node.type === _experimentalUtils.AST_NODE_TYPES.Literal && node.value === null;
13
+ /**
14
+ * Checks if the given `ParsedEqualityMatcherCall` is a call to one of the equality matchers,
15
+ * with a `null` literal as the sole argument.
16
+ */
17
+
18
+
19
+ const isNullEqualityMatcher = matcher => isNullLiteral(getFirstArgument(matcher));
20
+
21
+ const isFirstArgumentIdentifier = (matcher, name) => (0, _utils.isIdentifier)(getFirstArgument(matcher), name);
22
+
23
+ const isPrimitiveLiteral = matcher => {
24
+ const firstArg = getFirstArgument(matcher);
25
+ return firstArg.type === _experimentalUtils.AST_NODE_TYPES.Literal && !('regex' in firstArg);
26
+ };
27
+
28
+ const getFirstArgument = matcher => {
29
+ return (0, _utils.followTypeAssertionChain)(matcher.arguments[0]);
30
+ };
31
+
32
+ const reportPreferToBe = (context, whatToBe, matcher, modifier) => {
33
+ const modifierNode = (modifier === null || modifier === void 0 ? void 0 : modifier.negation) || (modifier === null || modifier === void 0 ? void 0 : modifier.node);
34
+ context.report({
35
+ messageId: `useToBe${whatToBe}`,
36
+
37
+ fix(fixer) {
38
+ var _matcher$arguments;
39
+
40
+ const fixes = [fixer.replaceText(matcher.node.property, `toBe${whatToBe}`)];
41
+
42
+ if ((_matcher$arguments = matcher.arguments) !== null && _matcher$arguments !== void 0 && _matcher$arguments.length && whatToBe !== '') {
43
+ fixes.push(fixer.remove(matcher.arguments[0]));
44
+ }
45
+
46
+ if (modifierNode) {
47
+ fixes.push(fixer.removeRange([modifierNode.property.range[0] - 1, modifierNode.property.range[1]]));
48
+ }
49
+
50
+ return fixes;
51
+ },
52
+
53
+ node: matcher.node.property
54
+ });
55
+ };
56
+
57
+ var _default = (0, _utils.createRule)({
58
+ name: __filename,
59
+ meta: {
60
+ docs: {
61
+ category: 'Best Practices',
62
+ description: 'Suggest using `toBe()` for primitive literals',
63
+ recommended: false
64
+ },
65
+ messages: {
66
+ useToBe: 'Use `toBe` when expecting primitive literals',
67
+ useToBeUndefined: 'Use `toBeUndefined` instead',
68
+ useToBeDefined: 'Use `toBeDefined` instead',
69
+ useToBeNull: 'Use `toBeNull` instead',
70
+ useToBeNaN: 'Use `toBeNaN` instead'
71
+ },
72
+ fixable: 'code',
73
+ type: 'suggestion',
74
+ schema: []
75
+ },
76
+ defaultOptions: [],
77
+
78
+ create(context) {
79
+ return {
80
+ CallExpression(node) {
81
+ if (!(0, _utils.isExpectCall)(node)) {
82
+ return;
83
+ }
84
+
85
+ const {
86
+ matcher,
87
+ modifier
88
+ } = (0, _utils.parseExpectCall)(node);
89
+
90
+ if (!matcher) {
91
+ return;
92
+ }
93
+
94
+ if (((modifier === null || modifier === void 0 ? void 0 : modifier.name) === _utils.ModifierName.not || modifier !== null && modifier !== void 0 && modifier.negation) && ['toBeUndefined', 'toBeDefined'].includes(matcher.name)) {
95
+ reportPreferToBe(context, matcher.name === 'toBeDefined' ? 'Undefined' : 'Defined', matcher, modifier);
96
+ return;
97
+ }
98
+
99
+ if (!(0, _utils.isParsedEqualityMatcherCall)(matcher)) {
100
+ return;
101
+ }
102
+
103
+ if (isNullEqualityMatcher(matcher)) {
104
+ reportPreferToBe(context, 'Null', matcher);
105
+ return;
106
+ }
107
+
108
+ if (isFirstArgumentIdentifier(matcher, 'undefined')) {
109
+ const name = (modifier === null || modifier === void 0 ? void 0 : modifier.name) === _utils.ModifierName.not || modifier !== null && modifier !== void 0 && modifier.negation ? 'Defined' : 'Undefined';
110
+ reportPreferToBe(context, name, matcher, modifier);
111
+ return;
112
+ }
113
+
114
+ if (isFirstArgumentIdentifier(matcher, 'NaN')) {
115
+ reportPreferToBe(context, 'NaN', matcher);
116
+ return;
117
+ }
118
+
119
+ if (isPrimitiveLiteral(matcher) && matcher.name !== _utils.EqualityMatcher.toBe) {
120
+ reportPreferToBe(context, '', matcher);
121
+ }
122
+ }
123
+
124
+ };
125
+ }
126
+
127
+ });
128
+
129
+ exports.default = _default;
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+
8
+ var _experimentalUtils = require("@typescript-eslint/experimental-utils");
9
+
10
+ var _utils = require("./utils");
11
+
12
+ const isJestFnCall = node => {
13
+ var _getNodeName;
14
+
15
+ if ((0, _utils.isDescribeCall)(node) || (0, _utils.isTestCaseCall)(node) || (0, _utils.isHook)(node)) {
16
+ return true;
17
+ }
18
+
19
+ return !!((_getNodeName = (0, _utils.getNodeName)(node)) !== null && _getNodeName !== void 0 && _getNodeName.startsWith('jest.'));
20
+ };
21
+
22
+ const shouldBeInHook = node => {
23
+ switch (node.type) {
24
+ case _experimentalUtils.AST_NODE_TYPES.ExpressionStatement:
25
+ return shouldBeInHook(node.expression);
26
+
27
+ case _experimentalUtils.AST_NODE_TYPES.CallExpression:
28
+ return !isJestFnCall(node);
29
+
30
+ default:
31
+ return false;
32
+ }
33
+ };
34
+
35
+ var _default = (0, _utils.createRule)({
36
+ name: __filename,
37
+ meta: {
38
+ docs: {
39
+ category: 'Best Practices',
40
+ description: 'Require setup and teardown code to be within a hook',
41
+ recommended: false
42
+ },
43
+ messages: {
44
+ useHook: 'This should be done within a hook'
45
+ },
46
+ type: 'suggestion',
47
+ schema: []
48
+ },
49
+ defaultOptions: [],
50
+
51
+ create(context) {
52
+ const checkBlockBody = body => {
53
+ for (const statement of body) {
54
+ if (shouldBeInHook(statement)) {
55
+ context.report({
56
+ node: statement,
57
+ messageId: 'useHook'
58
+ });
59
+ }
60
+ }
61
+ };
62
+
63
+ return {
64
+ Program(program) {
65
+ checkBlockBody(program.body);
66
+ },
67
+
68
+ CallExpression(node) {
69
+ if (!(0, _utils.isDescribeCall)(node) || node.arguments.length < 2) {
70
+ return;
71
+ }
72
+
73
+ const [, testFn] = node.arguments;
74
+
75
+ if (!(0, _utils.isFunction)(testFn) || testFn.body.type !== _experimentalUtils.AST_NODE_TYPES.BlockStatement) {
76
+ return;
77
+ }
78
+
79
+ checkBlockBody(testFn.body.body);
80
+ }
81
+
82
+ };
83
+ }
84
+
85
+ });
86
+
87
+ exports.default = _default;
@@ -7,6 +7,12 @@ exports.default = void 0;
7
7
 
8
8
  var _utils = require("./utils");
9
9
 
10
+ const messages = {
11
+ tooManyDescribes: 'There should not be more than {{ max }} describe{{ s }} at the top level',
12
+ unexpectedTestCase: 'All test cases must be wrapped in a describe block.',
13
+ unexpectedHook: 'All hooks must be wrapped in a describe block.'
14
+ };
15
+
10
16
  var _default = (0, _utils.createRule)({
11
17
  name: __filename,
12
18
  meta: {
@@ -15,21 +21,49 @@ var _default = (0, _utils.createRule)({
15
21
  description: 'Require test cases and hooks to be inside a `describe` block',
16
22
  recommended: false
17
23
  },
18
- messages: {
19
- unexpectedTestCase: 'All test cases must be wrapped in a describe block.',
20
- unexpectedHook: 'All hooks must be wrapped in a describe block.'
21
- },
24
+ messages,
22
25
  type: 'suggestion',
23
- schema: []
26
+ schema: [{
27
+ type: 'object',
28
+ properties: {
29
+ maxNumberOfTopLevelDescribes: {
30
+ type: 'number',
31
+ minimum: 1
32
+ }
33
+ },
34
+ additionalProperties: false
35
+ }]
24
36
  },
25
- defaultOptions: [],
37
+ defaultOptions: [{}],
26
38
 
27
39
  create(context) {
40
+ var _context$options$;
41
+
42
+ const {
43
+ maxNumberOfTopLevelDescribes = Infinity
44
+ } = (_context$options$ = context.options[0]) !== null && _context$options$ !== void 0 ? _context$options$ : {};
45
+ let numberOfTopLevelDescribeBlocks = 0;
28
46
  let numberOfDescribeBlocks = 0;
29
47
  return {
30
48
  CallExpression(node) {
31
49
  if ((0, _utils.isDescribeCall)(node)) {
32
50
  numberOfDescribeBlocks++;
51
+
52
+ if (numberOfDescribeBlocks === 1) {
53
+ numberOfTopLevelDescribeBlocks++;
54
+
55
+ if (numberOfTopLevelDescribeBlocks > maxNumberOfTopLevelDescribes) {
56
+ context.report({
57
+ node,
58
+ messageId: 'tooManyDescribes',
59
+ data: {
60
+ max: maxNumberOfTopLevelDescribes,
61
+ s: maxNumberOfTopLevelDescribes === 1 ? '' : 's'
62
+ }
63
+ });
64
+ }
65
+ }
66
+
33
67
  return;
34
68
  }
35
69
 
@@ -4,7 +4,7 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.getNodeName = getNodeName;
7
- exports.scopeHasLocalReference = exports.isDescribeCall = exports.isTestCaseCall = exports.getTestCallExpressionsFromDeclaredVariables = exports.isHook = exports.isFunction = exports.TestCaseProperty = exports.DescribeProperty = exports.HookName = exports.TestCaseName = exports.DescribeAlias = exports.parseExpectCall = exports.isParsedEqualityMatcherCall = exports.EqualityMatcher = exports.ModifierName = exports.isExpectMember = exports.isExpectCall = exports.getAccessorValue = exports.isSupportedAccessor = exports.hasOnlyOneArgument = exports.getStringValue = exports.isStringNode = exports.followTypeAssertionChain = exports.createRule = void 0;
7
+ exports.scopeHasLocalReference = exports.isDescribeCall = exports.isTestCaseCall = exports.getTestCallExpressionsFromDeclaredVariables = exports.isHook = exports.isFunction = exports.TestCaseProperty = exports.DescribeProperty = exports.HookName = exports.TestCaseName = exports.DescribeAlias = exports.parseExpectCall = exports.isParsedEqualityMatcherCall = exports.EqualityMatcher = exports.ModifierName = exports.isExpectMember = exports.isExpectCall = exports.getAccessorValue = exports.isSupportedAccessor = exports.isIdentifier = exports.hasOnlyOneArgument = exports.getStringValue = exports.isStringNode = exports.followTypeAssertionChain = exports.createRule = void 0;
8
8
 
9
9
  var _path = require("path");
10
10
 
@@ -151,6 +151,8 @@ const isIdentifier = (node, name) => node.type === _experimentalUtils.AST_NODE_T
151
151
  */
152
152
 
153
153
 
154
+ exports.isIdentifier = isIdentifier;
155
+
154
156
  const isSupportedAccessor = (node, value) => isIdentifier(node, value) || isStringNode(node, value);
155
157
  /**
156
158
  * Gets the value of the given `AccessorNode`,
@@ -231,7 +233,7 @@ const reparseAsMatcher = parsedMember => ({ ...parsedMember,
231
233
  *
232
234
  * If this matcher isn't called, this will be `null`.
233
235
  */
234
- arguments: parsedMember.node.parent && parsedMember.node.parent.type === _experimentalUtils.AST_NODE_TYPES.CallExpression ? parsedMember.node.parent.arguments : null
236
+ arguments: parsedMember.node.parent.type === _experimentalUtils.AST_NODE_TYPES.CallExpression ? parsedMember.node.parent.arguments : null
235
237
  });
236
238
  /**
237
239
  * Re-parses the given `parsedMember` as a `ParsedExpectModifier`.
@@ -258,7 +260,7 @@ const reparseMemberAsModifier = parsedMember => {
258
260
  throw new Error(`modifier name must be either "${ModifierName.resolves}" or "${ModifierName.rejects}" (got "${parsedMember.name}")`);
259
261
  }
260
262
 
261
- const negation = parsedMember.node.parent && isExpectMember(parsedMember.node.parent, ModifierName.not) ? parsedMember.node.parent : undefined;
263
+ const negation = isExpectMember(parsedMember.node.parent, ModifierName.not) ? parsedMember.node.parent : undefined;
262
264
  return { ...parsedMember,
263
265
  negation
264
266
  };
@@ -295,7 +297,7 @@ const parseExpectCall = expect => {
295
297
  const modifier = expectation.modifier = reparseMemberAsModifier(parsedMember);
296
298
  const memberNode = modifier.negation || modifier.node;
297
299
 
298
- if (!memberNode.parent || !isExpectMember(memberNode.parent)) {
300
+ if (!isExpectMember(memberNode.parent)) {
299
301
  return expectation;
300
302
  }
301
303