eslint-plugin-jest 25.2.4 → 25.3.3

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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,33 @@
1
+ ## [25.3.3](https://github.com/jest-community/eslint-plugin-jest/compare/v25.3.2...v25.3.3) (2021-12-30)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **prefer-to-contain:** support square bracket accessors ([#1009](https://github.com/jest-community/eslint-plugin-jest/issues/1009)) ([73984a7](https://github.com/jest-community/eslint-plugin-jest/commit/73984a79f790986a17116589a587506bcc10efc0))
7
+ * **prefer-to-have-length:** support square bracket accessors ([#1010](https://github.com/jest-community/eslint-plugin-jest/issues/1010)) ([9e70f55](https://github.com/jest-community/eslint-plugin-jest/commit/9e70f550e341432f69a1cd334c19df87513ea906))
8
+
9
+ ## [25.3.2](https://github.com/jest-community/eslint-plugin-jest/compare/v25.3.1...v25.3.2) (2021-12-27)
10
+
11
+
12
+ ### Bug Fixes
13
+
14
+ * **no-large-snapshots:** only count size of template string for inline snapshots ([#1005](https://github.com/jest-community/eslint-plugin-jest/issues/1005)) ([5bea38f](https://github.com/jest-community/eslint-plugin-jest/commit/5bea38f9773ab686f08a7cc25247a782d50aa5ed))
15
+ * **prefer-hooks-on-top:** improve message & docs ([#999](https://github.com/jest-community/eslint-plugin-jest/issues/999)) ([f9e7ae2](https://github.com/jest-community/eslint-plugin-jest/commit/f9e7ae29233daad7bfea2230bea7266659299328))
16
+
17
+ ## [25.3.1](https://github.com/jest-community/eslint-plugin-jest/compare/v25.3.0...v25.3.1) (2021-12-27)
18
+
19
+
20
+ ### Bug Fixes
21
+
22
+ * **prefer-to-be:** support template literals ([#1006](https://github.com/jest-community/eslint-plugin-jest/issues/1006)) ([aa428e6](https://github.com/jest-community/eslint-plugin-jest/commit/aa428e6598d5f7b259d3cec1bc505989a0fe9885))
23
+
24
+ # [25.3.0](https://github.com/jest-community/eslint-plugin-jest/compare/v25.2.4...v25.3.0) (2021-11-23)
25
+
26
+
27
+ ### Features
28
+
29
+ * **require-hook:** add `allowedFunctionCalls` setting ([#983](https://github.com/jest-community/eslint-plugin-jest/issues/983)) ([9d9336a](https://github.com/jest-community/eslint-plugin-jest/commit/9d9336a7624c53c0bb3ee899b8cc336a0b3349cb))
30
+
1
31
  ## [25.2.4](https://github.com/jest-community/eslint-plugin-jest/compare/v25.2.3...v25.2.4) (2021-11-08)
2
32
 
3
33
 
package/README.md CHANGED
@@ -128,7 +128,7 @@ config file:
128
128
  ```
129
129
 
130
130
  See
131
- [ESLint documentation](http://eslint.org/docs/user-guide/configuring#extending-configuration-files)
131
+ [ESLint documentation](https://eslint.org/docs/user-guide/configuring/configuration-files#extending-configuration-files)
132
132
  for more information about extending configuration files.
133
133
 
134
134
  ### All
@@ -1,6 +1,10 @@
1
1
  # Suggest having hooks before any test cases (`prefer-hooks-on-top`)
2
2
 
3
- All hooks should be defined before the start of the tests
3
+ While hooks can be setup anywhere in a test file, they are always called in a
4
+ specific order which means it can be confusing if they're intermixed with test
5
+ cases.
6
+
7
+ This rule helps to ensure that hooks are always defined before test cases.
4
8
 
5
9
  ## Rule Details
6
10
 
@@ -11,47 +15,51 @@ Examples of **incorrect** code for this rule
11
15
 
12
16
  describe('foo', () => {
13
17
  beforeEach(() => {
14
- //some hook code
15
- });
16
- test('bar', () => {
17
- some_fn();
18
+ seedMyDatabase();
18
19
  });
19
- beforeAll(() => {
20
- //some hook code
21
- });
22
- test('bar', () => {
23
- some_fn();
20
+
21
+ it('accepts this input', () => {
22
+ // ...
24
23
  });
25
- });
26
24
 
27
- // Nested describe scenario
28
- describe('foo', () => {
29
25
  beforeAll(() => {
30
- //some hook code
26
+ createMyDatabase();
31
27
  });
32
- test('bar', () => {
33
- some_fn();
28
+
29
+ it('returns that value', () => {
30
+ // ...
34
31
  });
35
- describe('inner_foo', () => {
32
+
33
+ describe('when the database has specific values', () => {
34
+ const specificValue = '...';
35
+
36
36
  beforeEach(() => {
37
- //some hook code
37
+ seedMyDatabase(specificValue);
38
38
  });
39
- test('inner bar', () => {
40
- some_fn();
39
+
40
+ it('accepts that input', () => {
41
+ // ...
41
42
  });
42
- test('inner bar', () => {
43
- some_fn();
43
+
44
+ it('throws an error', () => {
45
+ // ...
44
46
  });
45
- beforeAll(() => {
46
- //some hook code
47
+
48
+ afterEach(() => {
49
+ clearLogger();
47
50
  });
48
- afterAll(() => {
49
- //some hook code
51
+ beforeEach(() => {
52
+ mockLogger();
50
53
  });
51
- test('inner bar', () => {
52
- some_fn();
54
+
55
+ it('logs a message', () => {
56
+ // ...
53
57
  });
54
58
  });
59
+
60
+ afterAll(() => {
61
+ removeMyDatabase();
62
+ });
55
63
  });
56
64
  ```
57
65
 
@@ -61,35 +69,51 @@ Examples of **correct** code for this rule
61
69
  /* eslint jest/prefer-hooks-on-top: "error" */
62
70
 
63
71
  describe('foo', () => {
64
- beforeEach(() => {
65
- //some hook code
72
+ beforeAll(() => {
73
+ createMyDatabase();
66
74
  });
67
75
 
68
- // Not affected by rule
69
- someSetup();
70
-
71
- afterEach(() => {
72
- //some hook code
76
+ beforeEach(() => {
77
+ seedMyDatabase();
73
78
  });
74
- test('bar', () => {
75
- some_fn();
79
+
80
+ afterAll(() => {
81
+ clearMyDatabase();
76
82
  });
77
- });
78
83
 
79
- // Nested describe scenario
80
- describe('foo', () => {
81
- beforeEach(() => {
82
- //some hook code
84
+ it('accepts this input', () => {
85
+ // ...
83
86
  });
84
- test('bar', () => {
85
- some_fn();
87
+
88
+ it('returns that value', () => {
89
+ // ...
86
90
  });
87
- describe('inner_foo', () => {
91
+
92
+ describe('when the database has specific values', () => {
93
+ const specificValue = '...';
94
+
88
95
  beforeEach(() => {
89
- //some hook code
96
+ seedMyDatabase(specificValue);
90
97
  });
91
- test('inner bar', () => {
92
- some_fn();
98
+
99
+ beforeEach(() => {
100
+ mockLogger();
101
+ });
102
+
103
+ afterEach(() => {
104
+ clearLogger();
105
+ });
106
+
107
+ it('accepts that input', () => {
108
+ // ...
109
+ });
110
+
111
+ it('throws an error', () => {
112
+ // ...
113
+ });
114
+
115
+ it('logs a message', () => {
116
+ // ...
93
117
  });
94
118
  });
95
119
  });
@@ -148,3 +148,40 @@ afterEach(() => {
148
148
  clearCityDatabase();
149
149
  });
150
150
  ```
151
+
152
+ ## Options
153
+
154
+ If there are methods that you want to call outside of hooks and tests, you can
155
+ mark them as allowed using the `allowedFunctionCalls` option.
156
+
157
+ ```json
158
+ {
159
+ "jest/require-hook": [
160
+ "error",
161
+ {
162
+ "allowedFunctionCalls": ["enableAutoDestroy"]
163
+ }
164
+ ]
165
+ }
166
+ ```
167
+
168
+ Examples of **correct** code when using
169
+ `{ "allowedFunctionCalls": ["enableAutoDestroy"] }` option:
170
+
171
+ ```js
172
+ /* eslint jest/require-hook: ["error", { "allowedFunctionCalls": ["enableAutoDestroy"] }] */
173
+
174
+ import { enableAutoDestroy, mount } from '@vue/test-utils';
175
+ import { initDatabase, tearDownDatabase } from './databaseUtils';
176
+
177
+ enableAutoDestroy(afterEach);
178
+
179
+ beforeEach(initDatabase);
180
+ afterEach(tearDownDatabase);
181
+
182
+ describe('Foo', () => {
183
+ test('always returns 42', () => {
184
+ expect(global.getAnswer()).toBe(42);
185
+ });
186
+ });
187
+ ```
@@ -100,6 +100,8 @@ var _default = (0, _utils.createRule)({
100
100
 
101
101
  return {
102
102
  CallExpression(node) {
103
+ var _matcher$arguments;
104
+
103
105
  if (!(0, _utils.isExpectCall)(node)) {
104
106
  return;
105
107
  }
@@ -112,10 +114,10 @@ var _default = (0, _utils.createRule)({
112
114
  return;
113
115
  }
114
116
 
115
- if (['toMatchInlineSnapshot', 'toThrowErrorMatchingInlineSnapshot'].includes(matcher.name)) {
117
+ if (['toMatchInlineSnapshot', 'toThrowErrorMatchingInlineSnapshot'].includes(matcher.name) && (_matcher$arguments = matcher.arguments) !== null && _matcher$arguments !== void 0 && _matcher$arguments.length) {
116
118
  var _options$inlineMaxSiz;
117
119
 
118
- reportOnViolation(context, matcher.node.parent, { ...options,
120
+ reportOnViolation(context, matcher.arguments[0], { ...options,
119
121
  maxSize: (_options$inlineMaxSiz = options.inlineMaxSize) !== null && _options$inlineMaxSiz !== void 0 ? _options$inlineMaxSiz : options.maxSize
120
122
  });
121
123
  }
@@ -16,7 +16,7 @@ var _default = (0, _utils.createRule)({
16
16
  recommended: false
17
17
  },
18
18
  messages: {
19
- noHookOnTop: 'Move all hooks before test cases'
19
+ noHookOnTop: 'Hooks should come before test cases'
20
20
  },
21
21
  schema: [],
22
22
  type: 'suggestion'
@@ -20,9 +20,16 @@ const isNullEqualityMatcher = matcher => isNullLiteral(getFirstArgument(matcher)
20
20
 
21
21
  const isFirstArgumentIdentifier = (matcher, name) => (0, _utils.isIdentifier)(getFirstArgument(matcher), name);
22
22
 
23
- const isPrimitiveLiteral = matcher => {
23
+ const shouldUseToBe = matcher => {
24
24
  const firstArg = getFirstArgument(matcher);
25
- return firstArg.type === _experimentalUtils.AST_NODE_TYPES.Literal && !('regex' in firstArg);
25
+
26
+ if (firstArg.type === _experimentalUtils.AST_NODE_TYPES.Literal) {
27
+ // regex literals are classed as literals, but they're actually objects
28
+ // which means "toBe" will give different results than other matchers
29
+ return !('regex' in firstArg);
30
+ }
31
+
32
+ return firstArg.type === _experimentalUtils.AST_NODE_TYPES.TemplateLiteral;
26
33
  };
27
34
 
28
35
  const getFirstArgument = matcher => {
@@ -116,7 +123,7 @@ var _default = (0, _utils.createRule)({
116
123
  return;
117
124
  }
118
125
 
119
- if (isPrimitiveLiteral(matcher) && matcher.name !== _utils.EqualityMatcher.toBe) {
126
+ if (shouldUseToBe(matcher) && matcher.name !== _utils.EqualityMatcher.toBe) {
120
127
  reportPreferToBe(context, '', matcher);
121
128
  }
122
129
  }
@@ -32,52 +32,8 @@ const isBooleanEqualityMatcher = matcher => (0, _utils.isParsedEqualityMatcherCa
32
32
  * @param {CallExpression} node
33
33
  *
34
34
  * @return {node is FixableIncludesCallExpression}
35
- *
36
- * @todo support `['includes']()` syntax (remove last property.type check to begin)
37
- * @todo break out into `isMethodCall<Name extends string>(node: TSESTree.Node, method: Name)` util-fn
38
- */
39
- 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') && node.callee.property.type === _experimentalUtils.AST_NODE_TYPES.Identifier && (0, _utils.hasOnlyOneArgument)(node);
40
-
41
- const buildToContainFuncExpectation = negated => negated ? `${_utils.ModifierName.not}.toContain` : 'toContain';
42
- /**
43
- * Finds the first `.` character token between the `object` & `property` of the given `member` expression.
44
- *
45
- * @param {TSESTree.MemberExpression} member
46
- * @param {SourceCode} sourceCode
47
- *
48
- * @return {Token | null}
49
35
  */
50
-
51
-
52
- const findPropertyDotToken = (member, sourceCode) => sourceCode.getFirstTokenBetween(member.object, member.property, token => token.value === '.');
53
-
54
- const getNegationFixes = (node, modifier, matcher, sourceCode, fixer, fileName) => {
55
- const [containArg] = node.arguments;
56
- const negationPropertyDot = findPropertyDotToken(modifier.node, sourceCode);
57
- const toContainFunc = buildToContainFuncExpectation((0, _utils.followTypeAssertionChain)(matcher.arguments[0]).value);
58
- /* istanbul ignore if */
59
-
60
- if (negationPropertyDot === null) {
61
- throw new Error(`Unexpected null when attempting to fix ${fileName} - please file a github issue at https://github.com/jest-community/eslint-plugin-jest`);
62
- }
63
-
64
- return [fixer.remove(negationPropertyDot), fixer.remove(modifier.node.property), fixer.replaceText(matcher.node.property, toContainFunc), fixer.replaceText(matcher.arguments[0], sourceCode.getText(containArg))];
65
- };
66
-
67
- const getCommonFixes = (node, sourceCode, fileName) => {
68
- const [containArg] = node.arguments;
69
- const includesCallee = node.callee;
70
- const propertyDot = findPropertyDotToken(includesCallee, sourceCode);
71
- const closingParenthesis = sourceCode.getTokenAfter(containArg);
72
- const openParenthesis = sourceCode.getTokenBefore(containArg);
73
- /* istanbul ignore if */
74
-
75
- if (propertyDot === null || closingParenthesis === null || openParenthesis === null) {
76
- throw new Error(`Unexpected null when attempting to fix ${fileName} - please file a github issue at https://github.com/jest-community/eslint-plugin-jest`);
77
- }
78
-
79
- return [containArg, includesCallee.property, propertyDot, closingParenthesis, openParenthesis];
80
- }; // expect(array.includes(<value>)[not.]{toBe,toEqual}(<boolean>)
36
+ 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>)
81
37
 
82
38
 
83
39
  var _default = (0, _utils.createRule)({
@@ -106,7 +62,8 @@ var _default = (0, _utils.createRule)({
106
62
 
107
63
  const {
108
64
  expect: {
109
- arguments: [includesCall]
65
+ arguments: [includesCall],
66
+ range: [, expectCallEnd]
110
67
  },
111
68
  matcher,
112
69
  modifier
@@ -118,19 +75,14 @@ var _default = (0, _utils.createRule)({
118
75
 
119
76
  context.report({
120
77
  fix(fixer) {
121
- const sourceCode = context.getSourceCode();
122
- const fileName = context.getFilename();
123
- const fixArr = getCommonFixes(includesCall, sourceCode, fileName).map(target => fixer.remove(target));
124
-
125
- if (modifier) {
126
- return getNegationFixes(includesCall, modifier, matcher, sourceCode, fixer, fileName).concat(fixArr);
127
- }
128
-
129
- const toContainFunc = buildToContainFuncExpectation(!(0, _utils.followTypeAssertionChain)(matcher.arguments[0]).value);
130
- const [containArg] = includesCall.arguments;
131
- fixArr.push(fixer.replaceText(matcher.node.property, toContainFunc));
132
- fixArr.push(fixer.replaceText(matcher.arguments[0], sourceCode.getText(containArg)));
133
- return fixArr;
78
+ const sourceCode = context.getSourceCode(); // we need to negate the expectation if the current expected
79
+ // value is itself negated by the "not" modifier
80
+
81
+ const addNotModifier = (0, _utils.followTypeAssertionChain)(matcher.arguments[0]).value === !!modifier;
82
+ return [// remove the "includes" call entirely
83
+ fixer.removeRange([includesCall.callee.property.range[0] - 1, includesCall.range[1]]), // replace the current matcher with "toContain", adding "not" if needed
84
+ fixer.replaceTextRange([expectCallEnd, matcher.node.range[1]], addNotModifier ? `.${_utils.ModifierName.not}.toContain` : '.toContain'), // replace the matcher argument with the value from the "includes"
85
+ fixer.replaceText(matcher.arguments[0], sourceCode.getText(includesCall.arguments[0]))];
134
86
  },
135
87
 
136
88
  messageId: 'useToContain',
@@ -40,20 +40,15 @@ var _default = (0, _utils.createRule)({
40
40
  matcher
41
41
  } = (0, _utils.parseExpectCall)(node);
42
42
 
43
- if (!matcher || !(0, _utils.isParsedEqualityMatcherCall)(matcher) || (argument === null || argument === void 0 ? void 0 : argument.type) !== _experimentalUtils.AST_NODE_TYPES.MemberExpression || !(0, _utils.isSupportedAccessor)(argument.property, 'length') || argument.property.type !== _experimentalUtils.AST_NODE_TYPES.Identifier) {
43
+ if (!matcher || !(0, _utils.isParsedEqualityMatcherCall)(matcher) || (argument === null || argument === void 0 ? void 0 : argument.type) !== _experimentalUtils.AST_NODE_TYPES.MemberExpression || !(0, _utils.isSupportedAccessor)(argument.property, 'length')) {
44
44
  return;
45
45
  }
46
46
 
47
47
  context.report({
48
48
  fix(fixer) {
49
- const propertyDot = context.getSourceCode().getFirstTokenBetween(argument.object, argument.property, token => token.value === '.');
50
- /* istanbul ignore if */
51
-
52
- if (propertyDot === null) {
53
- throw new Error(`Unexpected null when attempting to fix ${context.getFilename()} - please file a github issue at https://github.com/jest-community/eslint-plugin-jest`);
54
- }
55
-
56
- return [fixer.remove(propertyDot), fixer.remove(argument.property), fixer.replaceText(matcher.node.property, 'toHaveLength')];
49
+ return [// remove the "length" property accessor
50
+ fixer.removeRange([argument.property.range[0] - 1, argument.range[1]]), // replace the current matcher with "toHaveLength"
51
+ fixer.replaceTextRange([matcher.node.object.range[1], matcher.node.range[1]], '.toHaveLength')];
57
52
  },
58
53
 
59
54
  messageId: 'useToHaveLength',
@@ -23,13 +23,13 @@ const isNullOrUndefined = node => {
23
23
  return node.type === _experimentalUtils.AST_NODE_TYPES.Literal && node.value === null || (0, _utils.isIdentifier)(node, 'undefined');
24
24
  };
25
25
 
26
- const shouldBeInHook = node => {
26
+ const shouldBeInHook = (node, allowedFunctionCalls = []) => {
27
27
  switch (node.type) {
28
28
  case _experimentalUtils.AST_NODE_TYPES.ExpressionStatement:
29
- return shouldBeInHook(node.expression);
29
+ return shouldBeInHook(node.expression, allowedFunctionCalls);
30
30
 
31
31
  case _experimentalUtils.AST_NODE_TYPES.CallExpression:
32
- return !isJestFnCall(node);
32
+ return !(isJestFnCall(node) || allowedFunctionCalls.includes((0, _utils.getNodeName)(node)));
33
33
 
34
34
  case _experimentalUtils.AST_NODE_TYPES.VariableDeclaration:
35
35
  {
@@ -59,14 +59,33 @@ var _default = (0, _utils.createRule)({
59
59
  useHook: 'This should be done within a hook'
60
60
  },
61
61
  type: 'suggestion',
62
- schema: []
62
+ schema: [{
63
+ type: 'object',
64
+ properties: {
65
+ allowedFunctionCalls: {
66
+ type: 'array',
67
+ items: {
68
+ type: 'string'
69
+ }
70
+ }
71
+ },
72
+ additionalProperties: false
73
+ }]
63
74
  },
64
- defaultOptions: [],
75
+ defaultOptions: [{
76
+ allowedFunctionCalls: []
77
+ }],
65
78
 
66
79
  create(context) {
80
+ var _context$options$;
81
+
82
+ const {
83
+ allowedFunctionCalls
84
+ } = (_context$options$ = context.options[0]) !== null && _context$options$ !== void 0 ? _context$options$ : {};
85
+
67
86
  const checkBlockBody = body => {
68
87
  for (const statement of body) {
69
- if (shouldBeInHook(statement)) {
88
+ if (shouldBeInHook(statement, allowedFunctionCalls)) {
70
89
  context.report({
71
90
  node: statement,
72
91
  messageId: 'useHook'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-jest",
3
- "version": "25.2.4",
3
+ "version": "25.3.3",
4
4
  "description": "Eslint rules for Jest",
5
5
  "keywords": [
6
6
  "eslint",
@@ -89,8 +89,8 @@
89
89
  "@babel/core": "^7.4.4",
90
90
  "@babel/preset-env": "^7.4.4",
91
91
  "@babel/preset-typescript": "^7.3.3",
92
- "@commitlint/cli": "^13.1.0",
93
- "@commitlint/config-conventional": "^13.1.0",
92
+ "@commitlint/cli": "^16.0.0",
93
+ "@commitlint/config-conventional": "^16.0.0",
94
94
  "@schemastore/package": "^0.0.6",
95
95
  "@semantic-release/changelog": "^6.0.0",
96
96
  "@semantic-release/git": "^10.0.0",
@@ -115,7 +115,7 @@
115
115
  "is-ci": "^3.0.0",
116
116
  "jest": "^27.0.0",
117
117
  "jest-runner-eslint": "^1.0.0",
118
- "lint-staged": "^11.1.2",
118
+ "lint-staged": "^12.0.0",
119
119
  "pinst": "^2.0.0",
120
120
  "prettier": "^2.0.5",
121
121
  "rimraf": "^3.0.0",
@@ -159,5 +159,5 @@
159
159
  "resolutions": {
160
160
  "@typescript-eslint/experimental-utils": "^5.0.0"
161
161
  },
162
- "packageManager": "yarn@3.1.0"
162
+ "packageManager": "yarn@3.1.1"
163
163
  }