eslint-plugin-jest 24.4.3 → 24.6.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.
@@ -9,96 +9,204 @@ var _experimentalUtils = require("@typescript-eslint/experimental-utils");
9
9
 
10
10
  var _utils = require("./utils");
11
11
 
12
- const isPromiseChainCall = node => node.type === _experimentalUtils.AST_NODE_TYPES.CallExpression && node.callee.type === _experimentalUtils.AST_NODE_TYPES.MemberExpression && (0, _utils.isSupportedAccessor)(node.callee.property) && ['then', 'catch', 'finally'].includes((0, _utils.getAccessorValue)(node.callee.property));
13
-
14
- const isExpectCallPresentInFunction = body => {
15
- if (body.type === _experimentalUtils.AST_NODE_TYPES.BlockStatement) {
16
- return body.body.find(line => {
17
- if (line.type === _experimentalUtils.AST_NODE_TYPES.ExpressionStatement) {
18
- return isFullExpectCall(line.expression);
19
- }
12
+ const isPromiseChainCall = node => {
13
+ if (node.type === _experimentalUtils.AST_NODE_TYPES.CallExpression && node.callee.type === _experimentalUtils.AST_NODE_TYPES.MemberExpression && (0, _utils.isSupportedAccessor)(node.callee.property)) {
14
+ // promise methods should have at least 1 argument
15
+ if (node.arguments.length === 0) {
16
+ return false;
17
+ }
20
18
 
21
- if (line.type === _experimentalUtils.AST_NODE_TYPES.ReturnStatement && line.argument) {
22
- return isFullExpectCall(line.argument);
23
- }
19
+ switch ((0, _utils.getAccessorValue)(node.callee.property)) {
20
+ case 'then':
21
+ return node.arguments.length < 3;
24
22
 
25
- return false;
26
- });
23
+ case 'catch':
24
+ case 'finally':
25
+ return node.arguments.length < 2;
26
+ }
27
27
  }
28
28
 
29
- return isFullExpectCall(body);
29
+ return false;
30
30
  };
31
31
 
32
- const isFullExpectCall = expression => expression.type === _experimentalUtils.AST_NODE_TYPES.CallExpression && (0, _utils.isExpectMember)(expression.callee);
32
+ const findTopMostCallExpression = node => {
33
+ let topMostCallExpression = node;
34
+ let {
35
+ parent
36
+ } = node;
33
37
 
34
- const reportReturnRequired = (context, node) => {
35
- context.report({
36
- loc: {
37
- end: {
38
- column: node.loc.end.column,
39
- line: node.loc.end.line
40
- },
41
- start: node.loc.start
42
- },
43
- messageId: 'returnPromise',
44
- node
45
- });
38
+ while (parent) {
39
+ if (parent.type === _experimentalUtils.AST_NODE_TYPES.CallExpression) {
40
+ topMostCallExpression = parent;
41
+ parent = parent.parent;
42
+ continue;
43
+ }
44
+
45
+ if (parent.type !== _experimentalUtils.AST_NODE_TYPES.MemberExpression) {
46
+ break;
47
+ }
48
+
49
+ parent = parent.parent;
50
+ }
51
+
52
+ return topMostCallExpression;
46
53
  };
47
54
 
48
- const isPromiseReturnedLater = (node, testFunctionBody) => {
49
- let promiseName;
55
+ const isTestCaseCallWithCallbackArg = node => {
56
+ if (!(0, _utils.isTestCaseCall)(node)) {
57
+ return false;
58
+ }
59
+
60
+ const isJestEach = (0, _utils.getNodeName)(node).endsWith('.each');
50
61
 
51
- if (node.type === _experimentalUtils.AST_NODE_TYPES.ExpressionStatement && node.expression.type === _experimentalUtils.AST_NODE_TYPES.CallExpression && node.expression.callee.type === _experimentalUtils.AST_NODE_TYPES.MemberExpression && (0, _utils.isSupportedAccessor)(node.expression.callee.object)) {
52
- promiseName = (0, _utils.getAccessorValue)(node.expression.callee.object);
53
- } else if (node.type === _experimentalUtils.AST_NODE_TYPES.VariableDeclarator && node.id.type === _experimentalUtils.AST_NODE_TYPES.Identifier) {
54
- promiseName = node.id.name;
62
+ if (isJestEach && node.callee.type !== _experimentalUtils.AST_NODE_TYPES.TaggedTemplateExpression) {
63
+ // isJestEach but not a TaggedTemplateExpression, so this must be
64
+ // the `jest.each([])()` syntax which this rule doesn't support due
65
+ // to its complexity (see jest-community/eslint-plugin-jest#710)
66
+ // so we return true to trigger bailout
67
+ return true;
55
68
  }
56
69
 
57
- const lastLineInTestFunc = testFunctionBody[testFunctionBody.length - 1];
58
- return lastLineInTestFunc.type === _experimentalUtils.AST_NODE_TYPES.ReturnStatement && lastLineInTestFunc.argument && ('name' in lastLineInTestFunc.argument && lastLineInTestFunc.argument.name === promiseName || !promiseName);
70
+ if (isJestEach || node.arguments.length >= 2) {
71
+ const [, callback] = node.arguments;
72
+ const callbackArgIndex = Number(isJestEach);
73
+ return callback && (0, _utils.isFunction)(callback) && callback.params.length === 1 + callbackArgIndex;
74
+ }
75
+
76
+ return false;
59
77
  };
60
78
 
61
- const findTestFunction = node => {
62
- while (node) {
63
- var _node$parent;
79
+ const isPromiseMethodThatUsesValue = (node, identifier) => {
80
+ const {
81
+ name
82
+ } = identifier;
83
+
84
+ if (node.argument === null) {
85
+ return false;
86
+ }
87
+
88
+ if (node.argument.type === _experimentalUtils.AST_NODE_TYPES.CallExpression && node.argument.arguments.length > 0) {
89
+ const nodeName = (0, _utils.getNodeName)(node.argument);
90
+
91
+ if (['Promise.all', 'Promise.allSettled'].includes(nodeName)) {
92
+ const [firstArg] = node.argument.arguments;
64
93
 
65
- if ((0, _utils.isFunction)(node) && ((_node$parent = node.parent) === null || _node$parent === void 0 ? void 0 : _node$parent.type) === _experimentalUtils.AST_NODE_TYPES.CallExpression && (0, _utils.isTestCaseCall)(node.parent)) {
66
- return node;
94
+ if (firstArg.type === _experimentalUtils.AST_NODE_TYPES.ArrayExpression && firstArg.elements.some(nod => (0, _utils.isIdentifier)(nod, name))) {
95
+ return true;
96
+ }
67
97
  }
68
98
 
69
- node = node.parent;
99
+ if (['Promise.resolve', 'Promise.reject'].includes(nodeName) && node.argument.arguments.length === 1) {
100
+ return (0, _utils.isIdentifier)(node.argument.arguments[0], name);
101
+ }
70
102
  }
71
103
 
72
- return null;
104
+ return node.argument.type === _experimentalUtils.AST_NODE_TYPES.Identifier && (0, _utils.isIdentifier)(node.argument, name);
73
105
  };
106
+ /**
107
+ * Attempts to determine if the runtime value represented by the given `identifier`
108
+ * is `await`ed or `return`ed within the given `body` of statements
109
+ */
74
110
 
75
- const isParentThenOrPromiseReturned = (node, testFunctionBody) => node.type === _experimentalUtils.AST_NODE_TYPES.ReturnStatement || isPromiseReturnedLater(node, testFunctionBody);
76
111
 
77
- const verifyExpectWithReturn = (promiseCallbacks, node, context, testFunctionBody) => {
78
- promiseCallbacks.some(promiseCallback => {
79
- if (promiseCallback && (0, _utils.isFunction)(promiseCallback)) {
80
- if (isExpectCallPresentInFunction(promiseCallback.body) && node.parent.parent && !isParentThenOrPromiseReturned(node.parent.parent, testFunctionBody)) {
81
- reportReturnRequired(context, node.parent.parent);
82
- return true;
112
+ const isValueAwaitedOrReturned = (identifier, body) => {
113
+ const {
114
+ name
115
+ } = identifier;
116
+
117
+ for (const node of body) {
118
+ // skip all nodes that are before this identifier, because they'd probably
119
+ // be affecting a different runtime value (e.g. due to reassignment)
120
+ if (node.range[0] <= identifier.range[0]) {
121
+ continue;
122
+ }
123
+
124
+ if (node.type === _experimentalUtils.AST_NODE_TYPES.ReturnStatement) {
125
+ return isPromiseMethodThatUsesValue(node, identifier);
126
+ }
127
+
128
+ if (node.type === _experimentalUtils.AST_NODE_TYPES.ExpressionStatement) {
129
+ if (node.expression.type === _experimentalUtils.AST_NODE_TYPES.AwaitExpression) {
130
+ return isPromiseMethodThatUsesValue(node.expression, identifier);
131
+ } // (re)assignment changes the runtime value, so if we've not found an
132
+ // await or return already we act as if we've reached the end of the body
133
+
134
+
135
+ if (node.expression.type === _experimentalUtils.AST_NODE_TYPES.AssignmentExpression) {
136
+ var _getNodeName;
137
+
138
+ // unless we're assigning to the same identifier, in which case
139
+ // we might be chaining off the existing promise value
140
+ if ((0, _utils.isIdentifier)(node.expression.left, name) && (_getNodeName = (0, _utils.getNodeName)(node.expression.right)) !== null && _getNodeName !== void 0 && _getNodeName.startsWith(`${name}.`) && isPromiseChainCall(node.expression.right)) {
141
+ continue;
142
+ }
143
+
144
+ break;
83
145
  }
84
146
  }
85
147
 
86
- return false;
87
- });
148
+ if (node.type === _experimentalUtils.AST_NODE_TYPES.BlockStatement && isValueAwaitedOrReturned(identifier, node.body)) {
149
+ return true;
150
+ }
151
+ }
152
+
153
+ return false;
88
154
  };
89
155
 
90
- const isHavingAsyncCallBackParam = testFunction => testFunction.params[0] && testFunction.params[0].type === _experimentalUtils.AST_NODE_TYPES.Identifier;
156
+ const findFirstBlockBodyUp = node => {
157
+ let parent = node;
158
+
159
+ while (parent) {
160
+ if (parent.type === _experimentalUtils.AST_NODE_TYPES.BlockStatement) {
161
+ return parent.body;
162
+ }
163
+
164
+ parent = parent.parent;
165
+ }
166
+ /* istanbul ignore next */
167
+
168
+
169
+ throw new Error(`Could not find BlockStatement - please file a github issue at https://github.com/jest-community/eslint-plugin-jest`);
170
+ };
171
+
172
+ const isDirectlyWithinTestCaseCall = node => {
173
+ let parent = node;
174
+
175
+ while (parent) {
176
+ if ((0, _utils.isFunction)(parent)) {
177
+ var _parent;
178
+
179
+ parent = parent.parent;
180
+ return !!(((_parent = parent) === null || _parent === void 0 ? void 0 : _parent.type) === _experimentalUtils.AST_NODE_TYPES.CallExpression && (0, _utils.isTestCaseCall)(parent));
181
+ }
182
+
183
+ parent = parent.parent;
184
+ }
185
+
186
+ return false;
187
+ };
188
+
189
+ const isVariableAwaitedOrReturned = variable => {
190
+ const body = findFirstBlockBodyUp(variable); // it's pretty much impossible for us to track destructuring assignments,
191
+ // so we return true to bailout gracefully
192
+
193
+ if (!(0, _utils.isIdentifier)(variable.id)) {
194
+ return true;
195
+ }
196
+
197
+ return isValueAwaitedOrReturned(variable.id, body);
198
+ };
91
199
 
92
200
  var _default = (0, _utils.createRule)({
93
201
  name: __filename,
94
202
  meta: {
95
203
  docs: {
96
204
  category: 'Best Practices',
97
- description: 'Enforce having return statement when testing with promises',
205
+ description: 'Ensure promises that have expectations in their chain are valid',
98
206
  recommended: 'error'
99
207
  },
100
208
  messages: {
101
- returnPromise: 'Promise should be returned to test its fulfillment or rejection'
209
+ expectInFloatingPromise: "This promise should either be returned or awaited to ensure the expects in it's chain are called"
102
210
  },
103
211
  type: 'suggestion',
104
212
  schema: []
@@ -106,30 +214,106 @@ var _default = (0, _utils.createRule)({
106
214
  defaultOptions: [],
107
215
 
108
216
  create(context) {
217
+ let inTestCaseWithDoneCallback = false; // an array of booleans representing each promise chain we enter, with the
218
+ // boolean value representing if we think a given chain contains an expect
219
+ // in it's body.
220
+ //
221
+ // since we only care about the inner-most chain, we represent the state in
222
+ // reverse with the inner-most being the first item, as that makes it
223
+ // slightly less code to assign to by not needing to know the length
224
+
225
+ const chains = [];
109
226
  return {
110
227
  CallExpression(node) {
111
- if (!isPromiseChainCall(node) || node.parent && node.parent.type === _experimentalUtils.AST_NODE_TYPES.AwaitExpression) {
228
+ // there are too many ways that the done argument could be used with
229
+ // promises that contain expect that would make the promise safe for us
230
+ if (isTestCaseCallWithCallbackArg(node)) {
231
+ inTestCaseWithDoneCallback = true;
112
232
  return;
113
- }
233
+ } // if this call expression is a promise chain, add it to the stack with
234
+ // value of "false", as we assume there are no expect calls initially
114
235
 
115
- const testFunction = findTestFunction(node);
116
236
 
117
- if (testFunction && !isHavingAsyncCallBackParam(testFunction)) {
118
- const {
119
- body
120
- } = testFunction;
237
+ if (isPromiseChainCall(node)) {
238
+ chains.unshift(false);
239
+ return;
240
+ } // if we're within a promise chain, and this call expression looks like
241
+ // an expect call, mark the deepest chain as having an expect call
121
242
 
122
- if (body.type !== _experimentalUtils.AST_NODE_TYPES.BlockStatement) {
123
- return;
243
+
244
+ if (chains.length > 0 && (0, _utils.isExpectCall)(node)) {
245
+ chains[0] = true;
246
+ }
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, _utils.isTestCaseCall)(node)) {
255
+ inTestCaseWithDoneCallback = false;
124
256
  }
125
257
 
126
- const testFunctionBody = body.body; // then block can have two args, fulfillment & rejection
127
- // then block can have one args, fulfillment
128
- // catch block can have one args, rejection
129
- // ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
258
+ return;
259
+ }
260
+
261
+ if (!isPromiseChainCall(node)) {
262
+ return;
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
+
266
+
267
+ const hasExpectCall = chains.shift(); // if the promise chain we're exiting doesn't contain an expect,
268
+ // then we don't need to check it for anything
269
+
270
+ if (!hasExpectCall) {
271
+ return;
272
+ }
273
+
274
+ const {
275
+ parent
276
+ } = findTopMostCallExpression(node); // if we don't have a parent (which is technically impossible at runtime)
277
+ // or our parent is not directly within the test case, we stop checking
278
+ // because we're most likely in the body of a function being defined
279
+ // within the test, which we can't track
130
280
 
131
- verifyExpectWithReturn(node.arguments.slice(0, 2), node.callee, context, testFunctionBody);
281
+ if (!parent || !isDirectlyWithinTestCaseCall(parent)) {
282
+ return;
132
283
  }
284
+
285
+ switch (parent.type) {
286
+ case _experimentalUtils.AST_NODE_TYPES.VariableDeclarator:
287
+ {
288
+ if (isVariableAwaitedOrReturned(parent)) {
289
+ return;
290
+ }
291
+
292
+ break;
293
+ }
294
+
295
+ case _experimentalUtils.AST_NODE_TYPES.AssignmentExpression:
296
+ {
297
+ if (parent.left.type === _experimentalUtils.AST_NODE_TYPES.Identifier && isValueAwaitedOrReturned(parent.left, findFirstBlockBodyUp(parent))) {
298
+ return;
299
+ }
300
+
301
+ break;
302
+ }
303
+
304
+ case _experimentalUtils.AST_NODE_TYPES.ExpressionStatement:
305
+ break;
306
+
307
+ case _experimentalUtils.AST_NODE_TYPES.ReturnStatement:
308
+ case _experimentalUtils.AST_NODE_TYPES.AwaitExpression:
309
+ default:
310
+ return;
311
+ }
312
+
313
+ context.report({
314
+ messageId: 'expectInFloatingPromise',
315
+ node: parent
316
+ });
133
317
  }
134
318
 
135
319
  };
@@ -26,19 +26,25 @@ const getPromiseCallExpressionNode = node => {
26
26
  node = node.parent;
27
27
  }
28
28
 
29
- if (node.type === _experimentalUtils.AST_NODE_TYPES.CallExpression && node.callee && node.callee.type === _experimentalUtils.AST_NODE_TYPES.MemberExpression && (0, _utils.isSupportedAccessor)(node.callee.object) && (0, _utils.getAccessorValue)(node.callee.object) === 'Promise' && node.parent) {
29
+ if (node.type === _experimentalUtils.AST_NODE_TYPES.CallExpression && node.callee.type === _experimentalUtils.AST_NODE_TYPES.MemberExpression && (0, _utils.isSupportedAccessor)(node.callee.object) && (0, _utils.getAccessorValue)(node.callee.object) === 'Promise' && node.parent) {
30
30
  return node;
31
31
  }
32
32
 
33
33
  return null;
34
34
  };
35
35
 
36
- const findPromiseCallExpressionNode = node => node.parent && node.parent.parent && [_experimentalUtils.AST_NODE_TYPES.CallExpression, _experimentalUtils.AST_NODE_TYPES.ArrayExpression].includes(node.parent.type) ? getPromiseCallExpressionNode(node.parent) : null;
36
+ const findPromiseCallExpressionNode = node => {
37
+ var _node$parent;
38
+
39
+ return (_node$parent = node.parent) !== null && _node$parent !== void 0 && _node$parent.parent && [_experimentalUtils.AST_NODE_TYPES.CallExpression, _experimentalUtils.AST_NODE_TYPES.ArrayExpression].includes(node.parent.type) ? getPromiseCallExpressionNode(node.parent) : null;
40
+ };
37
41
 
38
42
  const getParentIfThenified = node => {
39
- const grandParentNode = node.parent && node.parent.parent;
43
+ var _node$parent2;
44
+
45
+ const grandParentNode = (_node$parent2 = node.parent) === null || _node$parent2 === void 0 ? void 0 : _node$parent2.parent;
40
46
 
41
- if (grandParentNode && grandParentNode.type === _experimentalUtils.AST_NODE_TYPES.CallExpression && grandParentNode.callee && (0, _utils.isExpectMember)(grandParentNode.callee) && ['then', 'catch'].includes((0, _utils.getAccessorValue)(grandParentNode.callee.property)) && grandParentNode.parent) {
47
+ if (grandParentNode && grandParentNode.type === _experimentalUtils.AST_NODE_TYPES.CallExpression && (0, _utils.isExpectMember)(grandParentNode.callee) && ['then', 'catch'].includes((0, _utils.getAccessorValue)(grandParentNode.callee.property)) && grandParentNode.parent) {
42
48
  // Just in case `then`s are chained look one above.
43
49
  return getParentIfThenified(grandParentNode);
44
50
  }
@@ -25,23 +25,38 @@ const doesBinaryExpressionContainStringNode = binaryExp => {
25
25
 
26
26
  const quoteStringValue = node => node.type === _experimentalUtils.AST_NODE_TYPES.TemplateLiteral ? `\`${node.quasis[0].value.raw}\`` : node.raw;
27
27
 
28
+ const compileMatcherPattern = matcherMaybeWithMessage => {
29
+ const [matcher, message] = Array.isArray(matcherMaybeWithMessage) ? matcherMaybeWithMessage : [matcherMaybeWithMessage];
30
+ return [new RegExp(matcher, 'u'), message];
31
+ };
32
+
28
33
  const compileMatcherPatterns = matchers => {
29
- if (typeof matchers === 'string') {
30
- const matcher = new RegExp(matchers, 'u');
34
+ if (typeof matchers === 'string' || Array.isArray(matchers)) {
35
+ const compiledMatcher = compileMatcherPattern(matchers);
31
36
  return {
32
- describe: matcher,
33
- test: matcher,
34
- it: matcher
37
+ describe: compiledMatcher,
38
+ test: compiledMatcher,
39
+ it: compiledMatcher
35
40
  };
36
41
  }
37
42
 
38
43
  return {
39
- describe: matchers.describe ? new RegExp(matchers.describe, 'u') : null,
40
- test: matchers.test ? new RegExp(matchers.test, 'u') : null,
41
- it: matchers.it ? new RegExp(matchers.it, 'u') : null
44
+ describe: matchers.describe ? compileMatcherPattern(matchers.describe) : null,
45
+ test: matchers.test ? compileMatcherPattern(matchers.test) : null,
46
+ it: matchers.it ? compileMatcherPattern(matchers.it) : null
42
47
  };
43
48
  };
44
49
 
50
+ const MatcherAndMessageSchema = {
51
+ type: 'array',
52
+ items: {
53
+ type: 'string'
54
+ },
55
+ minItems: 1,
56
+ maxItems: 2,
57
+ additionalItems: false
58
+ };
59
+
45
60
  var _default = (0, _utils.createRule)({
46
61
  name: __filename,
47
62
  meta: {
@@ -57,7 +72,9 @@ var _default = (0, _utils.createRule)({
57
72
  accidentalSpace: 'should not have leading or trailing spaces',
58
73
  disallowedWord: '"{{ word }}" is not allowed in test titles.',
59
74
  mustNotMatch: '{{ jestFunctionName }} should not match {{ pattern }}',
60
- mustMatch: '{{ jestFunctionName }} should match {{ pattern }}'
75
+ mustMatch: '{{ jestFunctionName }} should match {{ pattern }}',
76
+ mustNotMatchCustom: '{{ message }}',
77
+ mustMatchCustom: '{{ message }}'
61
78
  },
62
79
  type: 'suggestion',
63
80
  schema: [{
@@ -72,43 +89,22 @@ var _default = (0, _utils.createRule)({
72
89
  items: {
73
90
  type: 'string'
74
91
  }
75
- },
76
- mustNotMatch: {
92
+ }
93
+ },
94
+ patternProperties: {
95
+ [/^must(?:Not)?Match$/u.source]: {
77
96
  oneOf: [{
78
97
  type: 'string'
79
- }, {
98
+ }, MatcherAndMessageSchema, {
80
99
  type: 'object',
81
- properties: {
82
- describe: {
83
- type: 'string'
84
- },
85
- test: {
86
- type: 'string'
87
- },
88
- it: {
89
- type: 'string'
90
- }
100
+ propertyNames: {
101
+ enum: ['describe', 'test', 'it']
91
102
  },
92
- additionalProperties: false
93
- }]
94
- },
95
- mustMatch: {
96
- oneOf: [{
97
- type: 'string'
98
- }, {
99
- type: 'object',
100
- properties: {
101
- describe: {
103
+ additionalProperties: {
104
+ oneOf: [{
102
105
  type: 'string'
103
- },
104
- test: {
105
- type: 'string'
106
- },
107
- it: {
108
- type: 'string'
109
- }
110
- },
111
- additionalProperties: false
106
+ }, MatcherAndMessageSchema]
107
+ }
112
108
  }]
113
109
  }
114
110
  },
@@ -132,6 +128,8 @@ var _default = (0, _utils.createRule)({
132
128
  const mustMatchPatterns = compileMatcherPatterns(mustMatch !== null && mustMatch !== void 0 ? mustMatch : {});
133
129
  return {
134
130
  CallExpression(node) {
131
+ var _mustNotMatchPatterns, _mustMatchPatterns$je;
132
+
135
133
  if (!(0, _utils.isDescribeCall)(node) && !(0, _utils.isTestCaseCall)(node)) {
136
134
  return;
137
135
  }
@@ -205,32 +203,34 @@ var _default = (0, _utils.createRule)({
205
203
  }
206
204
 
207
205
  const [jestFunctionName] = nodeName.split('.');
208
- const mustNotMatchPattern = mustNotMatchPatterns[jestFunctionName];
206
+ const [mustNotMatchPattern, mustNotMatchMessage] = (_mustNotMatchPatterns = mustNotMatchPatterns[jestFunctionName]) !== null && _mustNotMatchPatterns !== void 0 ? _mustNotMatchPatterns : [];
209
207
 
210
208
  if (mustNotMatchPattern) {
211
209
  if (mustNotMatchPattern.test(title)) {
212
210
  context.report({
213
- messageId: 'mustNotMatch',
211
+ messageId: mustNotMatchMessage ? 'mustNotMatchCustom' : 'mustNotMatch',
214
212
  node: argument,
215
213
  data: {
216
214
  jestFunctionName,
217
- pattern: mustNotMatchPattern
215
+ pattern: mustNotMatchPattern,
216
+ message: mustNotMatchMessage
218
217
  }
219
218
  });
220
219
  return;
221
220
  }
222
221
  }
223
222
 
224
- const mustMatchPattern = mustMatchPatterns[jestFunctionName];
223
+ const [mustMatchPattern, mustMatchMessage] = (_mustMatchPatterns$je = mustMatchPatterns[jestFunctionName]) !== null && _mustMatchPatterns$je !== void 0 ? _mustMatchPatterns$je : [];
225
224
 
226
225
  if (mustMatchPattern) {
227
226
  if (!mustMatchPattern.test(title)) {
228
227
  context.report({
229
- messageId: 'mustMatch',
228
+ messageId: mustMatchMessage ? 'mustMatchCustom' : 'mustMatch',
230
229
  node: argument,
231
230
  data: {
232
231
  jestFunctionName,
233
- pattern: mustMatchPattern
232
+ pattern: mustMatchPattern,
233
+ message: mustMatchMessage
234
234
  }
235
235
  });
236
236
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-jest",
3
- "version": "24.4.3",
3
+ "version": "24.6.0",
4
4
  "description": "Eslint rules for Jest",
5
5
  "keywords": [
6
6
  "eslint",