eslint-plugin-jest 24.4.1 → 24.5.1

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,41 @@
1
+ ## [24.5.1](https://github.com/jest-community/eslint-plugin-jest/compare/v24.5.0...v24.5.1) (2021-10-04)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **prefer-to-be:** don't consider RegExp literals as `toBe`-able ([#922](https://github.com/jest-community/eslint-plugin-jest/issues/922)) ([99b6d42](https://github.com/jest-community/eslint-plugin-jest/commit/99b6d429e697d60645b4bc64ee4ae34d7016118c))
7
+
8
+ # [24.5.0](https://github.com/jest-community/eslint-plugin-jest/compare/v24.4.3...v24.5.0) (2021-09-29)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **no-deprecated-functions:** remove `process.cwd` from resolve paths ([#889](https://github.com/jest-community/eslint-plugin-jest/issues/889)) ([6940488](https://github.com/jest-community/eslint-plugin-jest/commit/6940488d7b5a47577e2823e6d4385b511c5becf4))
14
+ * **no-identical-title:** always consider `.each` titles unique ([#910](https://github.com/jest-community/eslint-plugin-jest/issues/910)) ([a41a40e](https://github.com/jest-community/eslint-plugin-jest/commit/a41a40eafaf1db444ba940cccd2014cb0dc41be9))
15
+
16
+
17
+ ### Features
18
+
19
+ * create `prefer-expect-resolves` rule ([#822](https://github.com/jest-community/eslint-plugin-jest/issues/822)) ([2556020](https://github.com/jest-community/eslint-plugin-jest/commit/2556020a777f9daaf1d362a04e3f990415e82db8))
20
+ * create `prefer-to-be` rule ([#864](https://github.com/jest-community/eslint-plugin-jest/issues/864)) ([3a64aea](https://github.com/jest-community/eslint-plugin-jest/commit/3a64aea5bdc55465f1ef34f1426ae626d6c8a230))
21
+ * **require-top-level-describe:** support enforcing max num of describes ([#912](https://github.com/jest-community/eslint-plugin-jest/issues/912)) ([14a2d13](https://github.com/jest-community/eslint-plugin-jest/commit/14a2d1391c9f6f52509316542f45df35853c9b79))
22
+ * **valid-title:** allow custom matcher messages ([#913](https://github.com/jest-community/eslint-plugin-jest/issues/913)) ([ffc9392](https://github.com/jest-community/eslint-plugin-jest/commit/ffc93921348b0d4a394125f665d2bb09148ea37e))
23
+
24
+ ## [24.4.3](https://github.com/jest-community/eslint-plugin-jest/compare/v24.4.2...v24.4.3) (2021-09-28)
25
+
26
+
27
+ ### Bug Fixes
28
+
29
+ * **valid-expect-in-promise:** support `finally` ([#914](https://github.com/jest-community/eslint-plugin-jest/issues/914)) ([9c89855](https://github.com/jest-community/eslint-plugin-jest/commit/9c89855d23534272230afe6d9e665b8e11ef3075))
30
+ * **valid-expect-in-promise:** support additional test functions ([#915](https://github.com/jest-community/eslint-plugin-jest/issues/915)) ([4798005](https://github.com/jest-community/eslint-plugin-jest/commit/47980058d8d1ff86ee69a376c4edd182d462d594))
31
+
32
+ ## [24.4.2](https://github.com/jest-community/eslint-plugin-jest/compare/v24.4.1...v24.4.2) (2021-09-17)
33
+
34
+
35
+ ### Bug Fixes
36
+
37
+ * use correct property `hasSuggestions` rather than `hasSuggestion` ([#899](https://github.com/jest-community/eslint-plugin-jest/issues/899)) ([dfd2368](https://github.com/jest-community/eslint-plugin-jest/commit/dfd2368d1cb1789b6a95a11be24c36868bb8a819))
38
+
1
39
  ## [24.4.1](https://github.com/jest-community/eslint-plugin-jest/compare/v24.4.0...v24.4.1) (2021-09-17)
2
40
 
3
41
 
package/README.md CHANGED
@@ -59,23 +59,43 @@ doing:
59
59
  This is included in all configs shared by this plugin, so can be omitted if
60
60
  extending them.
61
61
 
62
- The behaviour of some rules (specifically `no-deprecated-functions`) change
63
- depending on the version of `jest` being used.
62
+ ### Jest `version` setting
64
63
 
65
- This setting is detected automatically based off the version of the `jest`
66
- package installed in `node_modules`, but it can also be provided explicitly if
67
- desired:
64
+ The behaviour of some rules (specifically [`no-deprecated-functions`][]) change
65
+ depending on the version of Jest being used.
66
+
67
+ By default, this plugin will attempt to determine to locate Jest using
68
+ `require.resolve`, meaning it will start looking in the closest `node_modules`
69
+ folder to the file being linted and work its way up.
70
+
71
+ Since we cache the automatically determined version, if you're linting
72
+ sub-folders that have different versions of Jest, you may find that the wrong
73
+ version of Jest is considered when linting. You can work around this by
74
+ providing the Jest version explicitly in nested ESLint configs:
68
75
 
69
76
  ```json
70
77
  {
71
78
  "settings": {
72
79
  "jest": {
73
- "version": 26
80
+ "version": 27
74
81
  }
75
82
  }
76
83
  }
77
84
  ```
78
85
 
86
+ To avoid hard-coding a number, you can also fetch it from the installed version
87
+ of Jest if you use a JavaScript config file such as `.eslintrc.js`:
88
+
89
+ ```js
90
+ module.exports = {
91
+ settings: {
92
+ jest: {
93
+ version: require('jest/package.json').version,
94
+ },
95
+ },
96
+ };
97
+ ```
98
+
79
99
  ## Shareable configurations
80
100
 
81
101
  ### Recommended
@@ -130,49 +150,51 @@ installations requiring long-term consistency.
130
150
 
131
151
  <!-- begin base rules list -->
132
152
 
133
- | Rule | Description | Configurations | Fixable |
134
- | ---------------------------------------------------------------------------- | --------------------------------------------------------------- | ---------------- | ------------ |
135
- | [consistent-test-it](docs/rules/consistent-test-it.md) | Have control over `test` and `it` usages | | ![fixable][] |
136
- | [expect-expect](docs/rules/expect-expect.md) | Enforce assertion to be made in a test body | ![recommended][] | |
137
- | [lowercase-name](docs/rules/lowercase-name.md) | Enforce lowercase test names | | ![fixable][] |
138
- | [max-nested-describe](docs/rules/max-nested-describe.md) | Enforces a maximum depth to nested describe calls | | |
139
- | [no-alias-methods](docs/rules/no-alias-methods.md) | Disallow alias methods | ![style][] | ![fixable][] |
140
- | [no-commented-out-tests](docs/rules/no-commented-out-tests.md) | Disallow commented out tests | ![recommended][] | |
141
- | [no-conditional-expect](docs/rules/no-conditional-expect.md) | Prevent calling `expect` conditionally | ![recommended][] | |
142
- | [no-deprecated-functions](docs/rules/no-deprecated-functions.md) | Disallow use of deprecated functions | ![recommended][] | ![fixable][] |
143
- | [no-disabled-tests](docs/rules/no-disabled-tests.md) | Disallow disabled tests | ![recommended][] | |
144
- | [no-done-callback](docs/rules/no-done-callback.md) | Avoid using a callback in asynchronous tests and hooks | ![recommended][] | ![suggest][] |
145
- | [no-duplicate-hooks](docs/rules/no-duplicate-hooks.md) | Disallow duplicate setup and teardown hooks | | |
146
- | [no-export](docs/rules/no-export.md) | Disallow using `exports` in files containing tests | ![recommended][] | |
147
- | [no-focused-tests](docs/rules/no-focused-tests.md) | Disallow focused tests | ![recommended][] | ![suggest][] |
148
- | [no-hooks](docs/rules/no-hooks.md) | Disallow setup and teardown hooks | | |
149
- | [no-identical-title](docs/rules/no-identical-title.md) | Disallow identical titles | ![recommended][] | |
150
- | [no-if](docs/rules/no-if.md) | Disallow conditional logic | | |
151
- | [no-interpolation-in-snapshots](docs/rules/no-interpolation-in-snapshots.md) | Disallow string interpolation inside snapshots | ![recommended][] | |
152
- | [no-jasmine-globals](docs/rules/no-jasmine-globals.md) | Disallow Jasmine globals | ![recommended][] | ![fixable][] |
153
- | [no-jest-import](docs/rules/no-jest-import.md) | Disallow importing Jest | ![recommended][] | |
154
- | [no-large-snapshots](docs/rules/no-large-snapshots.md) | disallow large snapshots | | |
155
- | [no-mocks-import](docs/rules/no-mocks-import.md) | Disallow manually importing from `__mocks__` | ![recommended][] | |
156
- | [no-restricted-matchers](docs/rules/no-restricted-matchers.md) | Disallow specific matchers & modifiers | | |
157
- | [no-standalone-expect](docs/rules/no-standalone-expect.md) | Disallow using `expect` outside of `it` or `test` blocks | ![recommended][] | |
158
- | [no-test-prefixes](docs/rules/no-test-prefixes.md) | Use `.only` and `.skip` over `f` and `x` | ![recommended][] | ![fixable][] |
159
- | [no-test-return-statement](docs/rules/no-test-return-statement.md) | Disallow explicitly returning from tests | | |
160
- | [prefer-called-with](docs/rules/prefer-called-with.md) | Suggest using `toBeCalledWith()` or `toHaveBeenCalledWith()` | | |
161
- | [prefer-expect-assertions](docs/rules/prefer-expect-assertions.md) | Suggest using `expect.assertions()` OR `expect.hasAssertions()` | | ![suggest][] |
162
- | [prefer-hooks-on-top](docs/rules/prefer-hooks-on-top.md) | Suggest having hooks before any test cases | | |
163
- | [prefer-spy-on](docs/rules/prefer-spy-on.md) | Suggest using `jest.spyOn()` | | ![fixable][] |
164
- | [prefer-strict-equal](docs/rules/prefer-strict-equal.md) | Suggest using `toStrictEqual()` | | ![suggest][] |
165
- | [prefer-to-be-null](docs/rules/prefer-to-be-null.md) | Suggest using `toBeNull()` | ![style][] | ![fixable][] |
166
- | [prefer-to-be-undefined](docs/rules/prefer-to-be-undefined.md) | Suggest using `toBeUndefined()` | ![style][] | ![fixable][] |
167
- | [prefer-to-contain](docs/rules/prefer-to-contain.md) | Suggest using `toContain()` | ![style][] | ![fixable][] |
168
- | [prefer-to-have-length](docs/rules/prefer-to-have-length.md) | Suggest using `toHaveLength()` | ![style][] | ![fixable][] |
169
- | [prefer-todo](docs/rules/prefer-todo.md) | Suggest using `test.todo` | | ![fixable][] |
170
- | [require-to-throw-message](docs/rules/require-to-throw-message.md) | Require a message for `toThrow()` | | |
171
- | [require-top-level-describe](docs/rules/require-top-level-describe.md) | Require test cases and hooks to be inside a `describe` block | | |
172
- | [valid-describe](docs/rules/valid-describe.md) | Enforce valid `describe()` callback | ![recommended][] | |
173
- | [valid-expect](docs/rules/valid-expect.md) | Enforce valid `expect()` usage | ![recommended][] | |
174
- | [valid-expect-in-promise](docs/rules/valid-expect-in-promise.md) | Enforce having return statement when testing with promises | ![recommended][] | |
175
- | [valid-title](docs/rules/valid-title.md) | Enforce valid titles | ![recommended][] | ![fixable][] |
153
+ | Rule | Description | Configurations | Fixable |
154
+ | ---------------------------------------------------------------------------- | ------------------------------------------------------------------- | ---------------- | ------------ |
155
+ | [consistent-test-it](docs/rules/consistent-test-it.md) | Have control over `test` and `it` usages | | ![fixable][] |
156
+ | [expect-expect](docs/rules/expect-expect.md) | Enforce assertion to be made in a test body | ![recommended][] | |
157
+ | [lowercase-name](docs/rules/lowercase-name.md) | Enforce lowercase test names | | ![fixable][] |
158
+ | [max-nested-describe](docs/rules/max-nested-describe.md) | Enforces a maximum depth to nested describe calls | | |
159
+ | [no-alias-methods](docs/rules/no-alias-methods.md) | Disallow alias methods | ![style][] | ![fixable][] |
160
+ | [no-commented-out-tests](docs/rules/no-commented-out-tests.md) | Disallow commented out tests | ![recommended][] | |
161
+ | [no-conditional-expect](docs/rules/no-conditional-expect.md) | Prevent calling `expect` conditionally | ![recommended][] | |
162
+ | [no-deprecated-functions](docs/rules/no-deprecated-functions.md) | Disallow use of deprecated functions | ![recommended][] | ![fixable][] |
163
+ | [no-disabled-tests](docs/rules/no-disabled-tests.md) | Disallow disabled tests | ![recommended][] | |
164
+ | [no-done-callback](docs/rules/no-done-callback.md) | Avoid using a callback in asynchronous tests and hooks | ![recommended][] | ![suggest][] |
165
+ | [no-duplicate-hooks](docs/rules/no-duplicate-hooks.md) | Disallow duplicate setup and teardown hooks | | |
166
+ | [no-export](docs/rules/no-export.md) | Disallow using `exports` in files containing tests | ![recommended][] | |
167
+ | [no-focused-tests](docs/rules/no-focused-tests.md) | Disallow focused tests | ![recommended][] | ![suggest][] |
168
+ | [no-hooks](docs/rules/no-hooks.md) | Disallow setup and teardown hooks | | |
169
+ | [no-identical-title](docs/rules/no-identical-title.md) | Disallow identical titles | ![recommended][] | |
170
+ | [no-if](docs/rules/no-if.md) | Disallow conditional logic | | |
171
+ | [no-interpolation-in-snapshots](docs/rules/no-interpolation-in-snapshots.md) | Disallow string interpolation inside snapshots | ![recommended][] | |
172
+ | [no-jasmine-globals](docs/rules/no-jasmine-globals.md) | Disallow Jasmine globals | ![recommended][] | ![fixable][] |
173
+ | [no-jest-import](docs/rules/no-jest-import.md) | Disallow importing Jest | ![recommended][] | |
174
+ | [no-large-snapshots](docs/rules/no-large-snapshots.md) | disallow large snapshots | | |
175
+ | [no-mocks-import](docs/rules/no-mocks-import.md) | Disallow manually importing from `__mocks__` | ![recommended][] | |
176
+ | [no-restricted-matchers](docs/rules/no-restricted-matchers.md) | Disallow specific matchers & modifiers | | |
177
+ | [no-standalone-expect](docs/rules/no-standalone-expect.md) | Disallow using `expect` outside of `it` or `test` blocks | ![recommended][] | |
178
+ | [no-test-prefixes](docs/rules/no-test-prefixes.md) | Use `.only` and `.skip` over `f` and `x` | ![recommended][] | ![fixable][] |
179
+ | [no-test-return-statement](docs/rules/no-test-return-statement.md) | Disallow explicitly returning from tests | | |
180
+ | [prefer-called-with](docs/rules/prefer-called-with.md) | Suggest using `toBeCalledWith()` or `toHaveBeenCalledWith()` | | |
181
+ | [prefer-expect-assertions](docs/rules/prefer-expect-assertions.md) | Suggest using `expect.assertions()` OR `expect.hasAssertions()` | | ![suggest][] |
182
+ | [prefer-expect-resolves](docs/rules/prefer-expect-resolves.md) | Prefer `await expect(...).resolves` over `expect(await ...)` syntax | | ![fixable][] |
183
+ | [prefer-hooks-on-top](docs/rules/prefer-hooks-on-top.md) | Suggest having hooks before any test cases | | |
184
+ | [prefer-spy-on](docs/rules/prefer-spy-on.md) | Suggest using `jest.spyOn()` | | ![fixable][] |
185
+ | [prefer-strict-equal](docs/rules/prefer-strict-equal.md) | Suggest using `toStrictEqual()` | | ![suggest][] |
186
+ | [prefer-to-be](docs/rules/prefer-to-be.md) | Suggest using `toBe()` for primitive literals | | ![fixable][] |
187
+ | [prefer-to-be-null](docs/rules/prefer-to-be-null.md) | Suggest using `toBeNull()` | ![style][] | ![fixable][] |
188
+ | [prefer-to-be-undefined](docs/rules/prefer-to-be-undefined.md) | Suggest using `toBeUndefined()` | ![style][] | ![fixable][] |
189
+ | [prefer-to-contain](docs/rules/prefer-to-contain.md) | Suggest using `toContain()` | ![style][] | ![fixable][] |
190
+ | [prefer-to-have-length](docs/rules/prefer-to-have-length.md) | Suggest using `toHaveLength()` | ![style][] | ![fixable][] |
191
+ | [prefer-todo](docs/rules/prefer-todo.md) | Suggest using `test.todo` | | ![fixable][] |
192
+ | [require-to-throw-message](docs/rules/require-to-throw-message.md) | Require a message for `toThrow()` | | |
193
+ | [require-top-level-describe](docs/rules/require-top-level-describe.md) | Require test cases and hooks to be inside a `describe` block | | |
194
+ | [valid-describe](docs/rules/valid-describe.md) | Enforce valid `describe()` callback | ![recommended][] | |
195
+ | [valid-expect](docs/rules/valid-expect.md) | Enforce valid `expect()` usage | ![recommended][] | |
196
+ | [valid-expect-in-promise](docs/rules/valid-expect-in-promise.md) | Enforce having return statement when testing with promises | ![recommended][] | |
197
+ | [valid-title](docs/rules/valid-title.md) | Enforce valid titles | ![recommended][] | ![fixable][] |
176
198
 
177
199
  <!-- end base rules list -->
178
200
 
@@ -226,3 +248,4 @@ https://github.com/istanbuljs/eslint-plugin-istanbul
226
248
  [suggest]: https://img.shields.io/badge/-suggest-yellow.svg
227
249
  [fixable]: https://img.shields.io/badge/-fixable-green.svg
228
250
  [style]: https://img.shields.io/badge/-style-blue.svg
251
+ [`no-deprecated-functions`]: docs/rules/no-deprecated-functions.md
@@ -116,16 +116,15 @@ describe('foo', () => {
116
116
  });
117
117
  });
118
118
 
119
- describe('foo2', function()) {
120
- describe('bar2', function() {
121
- it('should get something', function() {
119
+ describe('foo2', function () {
120
+ describe('bar2', function () {
121
+ it('should get something', function () {
122
122
  expect(getSomething()).toBe('Something');
123
123
  });
124
124
 
125
- it('should get else', function() {
125
+ it('should get else', function () {
126
126
  expect(getSomething()).toBe('Something');
127
127
  });
128
128
  });
129
129
  });
130
-
131
130
  ```
@@ -8,9 +8,9 @@ assumed to be promises.
8
8
 
9
9
  ## Rule Details
10
10
 
11
- Jest considered a test to have failed if it throws an error, rather than on if
12
- any particular function is called, meaning conditional calls to `expect` could
13
- result in tests silently being skipped.
11
+ Jest only considers a test to have failed if it throws an error, meaning if
12
+ calls to assertion functions like `expect` occur in conditional code such as a
13
+ `catch` statement, tests can end up passing but not actually test anything.
14
14
 
15
15
  Additionally, conditionals tend to make tests more brittle and complex, as they
16
16
  increase the amount of mental thinking needed to understand what is actually
@@ -79,3 +79,57 @@ it('throws an error', async () => {
79
79
  await expect(foo).rejects.toThrow(Error);
80
80
  });
81
81
  ```
82
+
83
+ ### How to catch a thrown error for testing without violating this rule
84
+
85
+ A common situation that comes up with this rule is when wanting to test
86
+ properties on a thrown error, as Jest's `toThrow` matcher only checks the
87
+ `message` property.
88
+
89
+ Most people write something like this:
90
+
91
+ ```typescript
92
+ describe('when the http request fails', () => {
93
+ it('includes the status code in the error', async () => {
94
+ try {
95
+ await makeRequest(url);
96
+ } catch (error) {
97
+ expect(error).toHaveProperty('statusCode', 404);
98
+ }
99
+ });
100
+ });
101
+ ```
102
+
103
+ As stated above, the problem with this is that if `makeRequest()` doesn't throw
104
+ the test will still pass as if the `expect` had been called.
105
+
106
+ While you can use `expect.assertions` & `expect.hasAssertions` for these
107
+ situations, they only work with `expect`.
108
+
109
+ A better way to handle this situation is to introduce a wrapper to handle the
110
+ catching, and otherwise returns a specific "no error thrown" error if nothing is
111
+ thrown by the wrapped function:
112
+
113
+ ```typescript
114
+ class NoErrorThrownError extends Error {}
115
+
116
+ const getError = async <TError>(call: () => unknown): Promise<TError> => {
117
+ try {
118
+ await call();
119
+
120
+ throw new NoErrorThrownError();
121
+ } catch (error: unknown) {
122
+ return error as TError;
123
+ }
124
+ };
125
+
126
+ describe('when the http request fails', () => {
127
+ it('includes the status code in the error', async () => {
128
+ const error = await getError(async () => makeRequest(url));
129
+
130
+ // check that the returned error wasn't that no error was thrown
131
+ expect(error).not.toBeInstanceOf(NoErrorThrownError);
132
+ expect(error).toHaveProperty('statusCode', 404);
133
+ });
134
+ });
135
+ ```
@@ -6,6 +6,11 @@ either been renamed for clarity, or replaced with more powerful APIs.
6
6
  While typically these deprecated functions are kept in the codebase for a number
7
7
  of majors, eventually they are removed completely.
8
8
 
9
+ This rule requires knowing which version of Jest you're using - see
10
+ [this section of the readme](../../README.md#jest-version-setting) for details
11
+ on how that is obtained automatically and how you can explicitly provide a
12
+ version if needed.
13
+
9
14
  ## Rule details
10
15
 
11
16
  This rule warns about calls to deprecated functions, and provides details on
@@ -3,7 +3,7 @@
3
3
  When calling asynchronous code in hooks and tests, `jest` needs to know when the
4
4
  asynchronous work is complete to progress the current run.
5
5
 
6
- Originally the most common pattern to archive this was to use callbacks:
6
+ Originally the most common pattern to achieve this was to use callbacks:
7
7
 
8
8
  ```js
9
9
  test('the data is peanut butter', done => {
@@ -20,11 +20,11 @@ test('the data is peanut butter', done => {
20
20
  });
21
21
  ```
22
22
 
23
- This can be very error prone however, as it requires careful understanding of
23
+ This can be very error-prone however, as it requires careful understanding of
24
24
  how assertions work in tests or otherwise tests won't behave as expected.
25
25
 
26
26
  For example, if the `try/catch` was left out of the above code, the test would
27
- timeout rather than fail. Even with the `try/catch`, forgetting to pass the
27
+ time out rather than fail. Even with the `try/catch`, forgetting to pass the
28
28
  caught error to `done` will result in `jest` believing the test has passed.
29
29
 
30
30
  A more straightforward way to handle asynchronous code is to use Promises:
@@ -8,9 +8,9 @@ trigger this rule.
8
8
 
9
9
  This rule aims to eliminate `expect` statements that will not be executed. An
10
10
  `expect` inside of a `describe` block but outside of a `test` or `it` block or
11
- outside of a `describe` will not execute and therefore will trigger this rule.
12
- It is viable, however, to have an `expect` in a helper function that is called
13
- from within a `test` or `it` block so `expect` statements in a function will not
11
+ outside a `describe` will not execute and therefore will trigger this rule. It
12
+ is viable, however, to have an `expect` in a helper function that is called from
13
+ within a `test` or `it` block so `expect` statements in a function will not
14
14
  trigger this rule.
15
15
 
16
16
  Statements like `expect.hasAssertions()` will NOT trigger this rule since these
@@ -7,8 +7,7 @@ If you are returning Promises then you should update the test to use
7
7
 
8
8
  ## Rule details
9
9
 
10
- This rule triggers a warning if you use a return statement inside of a test
11
- body.
10
+ This rule triggers a warning if you use a return statement inside a test body.
12
11
 
13
12
  ```js
14
13
  /*eslint jest/no-test-return-statement: "error"*/
@@ -0,0 +1,53 @@
1
+ # Prefer `await expect(...).resolves` over `expect(await ...)` syntax (`prefer-expect-resolves`)
2
+
3
+ When working with promises, there are two primary ways you can test the resolved
4
+ value:
5
+
6
+ 1. use the `resolve` modifier on `expect`
7
+ (`await expect(...).resolves.<matcher>` style)
8
+ 2. `await` the promise and assert against its result
9
+ (`expect(await ...).<matcher>` style)
10
+
11
+ While the second style is arguably less dependent on `jest`, if the promise
12
+ rejects it will be treated as a general error, resulting in less predictable
13
+ behaviour and output from `jest`.
14
+
15
+ Additionally, favoring the first style ensures consistency with its `rejects`
16
+ counterpart, as there is no way of "awaiting" a rejection.
17
+
18
+ ## Rule details
19
+
20
+ This rule triggers a warning if an `await` is done within an `expect`, and
21
+ recommends using `resolves` instead.
22
+
23
+ Examples of **incorrect** code for this rule
24
+
25
+ ```js
26
+ it('passes', async () => {
27
+ expect(await someValue()).toBe(true);
28
+ });
29
+
30
+ it('is true', async () => {
31
+ const myPromise = Promise.resolve(true);
32
+
33
+ expect(await myPromise).toBe(true);
34
+ });
35
+ ```
36
+
37
+ Examples of **correct** code for this rule
38
+
39
+ ```js
40
+ it('passes', async () => {
41
+ await expect(someValue()).resolves.toBe(true);
42
+ });
43
+
44
+ it('is true', async () => {
45
+ const myPromise = Promise.resolve(true);
46
+
47
+ await expect(myPromise).resolves.toBe(true);
48
+ });
49
+
50
+ it('errors', async () => {
51
+ await expect(Promise.rejects('oh noes!')).rejects.toThrow('oh noes!');
52
+ });
53
+ ```
@@ -0,0 +1,53 @@
1
+ # Suggest using `toBe()` for primitive literals (`prefer-to-be`)
2
+
3
+ When asserting against primitive literals such as numbers and strings, the
4
+ equality matchers all operate the same, but read slightly differently in code.
5
+
6
+ This rule recommends using the `toBe` matcher in these situations, as it forms
7
+ the most grammatically natural sentence. For `null`, `undefined`, and `NaN` this
8
+ rule recommends using their specific `toBe` matchers, as they give better error
9
+ messages as well.
10
+
11
+ ## Rule details
12
+
13
+ This rule triggers a warning if `toEqual()` or `toStrictEqual()` are used to
14
+ assert a primitive literal value such as numbers, strings, and booleans.
15
+
16
+ The following patterns are considered warnings:
17
+
18
+ ```js
19
+ expect(value).not.toEqual(5);
20
+ expect(getMessage()).toStrictEqual('hello world');
21
+ expect(loadMessage()).resolves.toEqual('hello world');
22
+ ```
23
+
24
+ The following pattern is not warning:
25
+
26
+ ```js
27
+ expect(value).not.toBe(5);
28
+ expect(getMessage()).toBe('hello world');
29
+ expect(loadMessage()).resolves.toBe('hello world');
30
+ expect(didError).not.toBe(true);
31
+
32
+ expect(catchError()).toStrictEqual({ message: 'oh noes!' });
33
+ ```
34
+
35
+ For `null`, `undefined`, and `NaN`, this rule triggers a warning if `toBe` is
36
+ used to assert against those literal values instead of their more specific
37
+ `toBe` counterparts:
38
+
39
+ ```js
40
+ expect(value).not.toBe(undefined);
41
+ expect(getMessage()).toBe(null);
42
+ expect(countMessages()).resolves.not.toBe(NaN);
43
+ ```
44
+
45
+ The following pattern is not warning:
46
+
47
+ ```js
48
+ expect(value).toBeDefined();
49
+ expect(getMessage()).toBeNull();
50
+ expect(countMessages()).resolves.not.toBeNaN();
51
+
52
+ expect(catchError()).toStrictEqual({ message: undefined });
53
+ ```
@@ -47,6 +47,34 @@ describe('test suite', () => {
47
47
  });
48
48
  ```
49
49
 
50
+ You can also enforce a limit on the number of describes allowed at the top-level
51
+ using the `maxNumberOfTopLevelDescribes` option:
52
+
53
+ ```json
54
+ {
55
+ "jest/require-top-level-describe": [
56
+ "error",
57
+ {
58
+ "maxNumberOfTopLevelDescribes": 2
59
+ }
60
+ ]
61
+ }
62
+ ```
63
+
64
+ Examples of **incorrect** code with the above config:
65
+
66
+ ```js
67
+ describe('test suite', () => {
68
+ it('test', () => {});
69
+ });
70
+
71
+ describe('test suite', () => {});
72
+
73
+ describe('test suite', () => {});
74
+ ```
75
+
76
+ This option defaults to `Infinity`, allowing any number of top-level describes.
77
+
50
78
  ## When Not To Use It
51
79
 
52
80
  Don't use this rule on non-jest test files.
@@ -198,17 +198,18 @@ describe('the proper way to handle things', () => {});
198
198
  Defaults: `{}`
199
199
 
200
200
  Allows enforcing that titles must match or must not match a given Regular
201
- Expression. An object can be provided to apply different Regular Expressions to
202
- specific Jest test function groups (`describe`, `test`, and `it`).
201
+ Expression, with an optional message. An object can be provided to apply
202
+ different Regular Expressions (with optional messages) to specific Jest test
203
+ function groups (`describe`, `test`, and `it`).
203
204
 
204
205
  Examples of **incorrect** code when using `mustMatch`:
205
206
 
206
207
  ```js
207
- // with mustMatch: '$that'
208
+ // with mustMatch: '^that'
208
209
  describe('the correct way to do things', () => {});
209
210
  fit('this there!', () => {});
210
211
 
211
- // with mustMatch: { test: '$that' }
212
+ // with mustMatch: { test: '^that' }
212
213
  describe('the tests that will be run', () => {});
213
214
  test('the stuff works', () => {});
214
215
  xtest('errors that are thrown have messages', () => {});
@@ -217,12 +218,39 @@ xtest('errors that are thrown have messages', () => {});
217
218
  Examples of **correct** code when using `mustMatch`:
218
219
 
219
220
  ```js
220
- // with mustMatch: '$that'
221
+ // with mustMatch: '^that'
221
222
  describe('that thing that needs to be done', () => {});
222
223
  fit('that this there!', () => {});
223
224
 
224
- // with mustMatch: { test: '$that' }
225
+ // with mustMatch: { test: '^that' }
225
226
  describe('the tests that will be run', () => {});
226
227
  test('that the stuff works', () => {});
227
228
  xtest('that errors that thrown have messages', () => {});
228
229
  ```
230
+
231
+ Optionally you can provide a custom message to show for a particular matcher by
232
+ using a tuple at any level where you can provide a matcher:
233
+
234
+ ```js
235
+ const prefixes = ['when', 'with', 'without', 'if', 'unless', 'for'];
236
+ const prefixesList = prefixes.join(' - \n');
237
+
238
+ module.exports = {
239
+ rules: {
240
+ 'jest/valid-title': [
241
+ 'error',
242
+ {
243
+ mustNotMatch: ['\\.$', 'Titles should not end with a full-stop'],
244
+ mustMatch: {
245
+ describe: [
246
+ new RegExp(`^(?:[A-Z]|\\b(${prefixes.join('|')})\\b`, 'u').source,
247
+ `Describe titles should either start with a capital letter or one of the following prefixes: ${prefixesList}`,
248
+ ],
249
+ test: [/[^A-Z]/u.source],
250
+ it: /[^A-Z]/u.source,
251
+ },
252
+ },
253
+ ],
254
+ },
255
+ };
256
+ ```
package/lib/index.js CHANGED
@@ -25,7 +25,7 @@ const importDefault = moduleName => // eslint-disable-next-line @typescript-esli
25
25
  interopRequireDefault(require(moduleName)).default;
26
26
 
27
27
  const rulesDir = (0, _path.join)(__dirname, 'rules');
28
- const excludedFiles = ['__tests__', 'utils'];
28
+ const excludedFiles = ['__tests__', 'detectJestVersion', 'utils'];
29
29
  const rules = (0, _fs.readdirSync)(rulesDir).map(rule => (0, _path.parse)(rule).name).filter(rule => !excludedFiles.includes(rule)).reduce((acc, curr) => ({ ...acc,
30
30
  [curr]: importDefault((0, _path.join)(rulesDir, curr))
31
31
  }), {});
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.detectJestVersion = void 0;
7
+ let cachedJestVersion = null;
8
+
9
+ const detectJestVersion = () => {
10
+ if (cachedJestVersion) {
11
+ return cachedJestVersion;
12
+ }
13
+
14
+ try {
15
+ const jestPath = require.resolve('jest/package.json');
16
+
17
+ const jestPackageJson = // eslint-disable-next-line @typescript-eslint/no-require-imports
18
+ require(jestPath);
19
+
20
+ if (jestPackageJson.version) {
21
+ const [majorVersion] = jestPackageJson.version.split('.');
22
+ return cachedJestVersion = parseInt(majorVersion, 10);
23
+ }
24
+ } catch {}
25
+
26
+ throw new Error('Unable to detect Jest version - please ensure jest package is installed, or otherwise set version explicitly');
27
+ };
28
+
29
+ exports.detectJestVersion = detectJestVersion;
@@ -3,39 +3,21 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.default = exports._clearCachedJestVersion = void 0;
6
+ exports.default = void 0;
7
7
 
8
8
  var _experimentalUtils = require("@typescript-eslint/experimental-utils");
9
9
 
10
- var _utils = require("./utils");
11
-
12
- let cachedJestVersion = null;
13
- /** @internal */
10
+ var _detectJestVersion = require("./detectJestVersion");
14
11
 
15
- const _clearCachedJestVersion = () => cachedJestVersion = null;
16
-
17
- exports._clearCachedJestVersion = _clearCachedJestVersion;
12
+ var _utils = require("./utils");
18
13
 
19
- const detectJestVersion = () => {
20
- if (cachedJestVersion) {
21
- return cachedJestVersion;
14
+ const parseJestVersion = rawVersion => {
15
+ if (typeof rawVersion === 'number') {
16
+ return rawVersion;
22
17
  }
23
18
 
24
- try {
25
- const jestPath = require.resolve('jest/package.json', {
26
- paths: [process.cwd()]
27
- });
28
-
29
- const jestPackageJson = // eslint-disable-next-line @typescript-eslint/no-require-imports
30
- require(jestPath);
31
-
32
- if (jestPackageJson.version) {
33
- const [majorVersion] = jestPackageJson.version.split('.');
34
- return cachedJestVersion = parseInt(majorVersion, 10);
35
- }
36
- } catch {}
37
-
38
- throw new Error('Unable to detect Jest version - please ensure jest package is installed, or otherwise set version explicitly');
19
+ const [majorVersion] = rawVersion.split('.');
20
+ return parseInt(majorVersion, 10);
39
21
  };
40
22
 
41
23
  var _default = (0, _utils.createRule)({
@@ -58,7 +40,7 @@ var _default = (0, _utils.createRule)({
58
40
  create(context) {
59
41
  var _context$settings, _context$settings$jes;
60
42
 
61
- const jestVersion = ((_context$settings = context.settings) === null || _context$settings === void 0 ? void 0 : (_context$settings$jes = _context$settings.jest) === null || _context$settings$jes === void 0 ? void 0 : _context$settings$jes.version) || detectJestVersion();
43
+ const jestVersion = parseJestVersion(((_context$settings = context.settings) === null || _context$settings === void 0 ? void 0 : (_context$settings$jes = _context$settings.jest) === null || _context$settings$jes === void 0 ? void 0 : _context$settings$jes.version) || (0, _detectJestVersion.detectJestVersion)());
62
44
  const deprecations = { ...(jestVersion >= 15 && {
63
45
  'jest.resetModuleRegistry': 'jest.resetModules'
64
46
  }),
@@ -41,7 +41,7 @@ var _default = (0, _utils.createRule)({
41
41
  },
42
42
  schema: [],
43
43
  type: 'suggestion',
44
- hasSuggestion: true
44
+ hasSuggestions: true
45
45
  },
46
46
  defaultOptions: [],
47
47
 
@@ -42,7 +42,7 @@ var _default = (0, _utils.createRule)({
42
42
  },
43
43
  schema: [],
44
44
  type: 'suggestion',
45
- hasSuggestion: true
45
+ hasSuggestions: true
46
46
  },
47
47
  defaultOptions: [],
48
48
  create: context => ({
@@ -5,8 +5,6 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.default = void 0;
7
7
 
8
- var _experimentalUtils = require("@typescript-eslint/experimental-utils");
9
-
10
8
  var _utils = require("./utils");
11
9
 
12
10
  const newDescribeContext = () => ({
@@ -35,13 +33,15 @@ var _default = (0, _utils.createRule)({
35
33
  const contexts = [newDescribeContext()];
36
34
  return {
37
35
  CallExpression(node) {
36
+ var _getNodeName;
37
+
38
38
  const currentLayer = contexts[contexts.length - 1];
39
39
 
40
40
  if ((0, _utils.isDescribeCall)(node)) {
41
41
  contexts.push(newDescribeContext());
42
42
  }
43
43
 
44
- if (node.callee.type === _experimentalUtils.AST_NODE_TYPES.TaggedTemplateExpression) {
44
+ if ((_getNodeName = (0, _utils.getNodeName)(node.callee)) !== null && _getNodeName !== void 0 && _getNodeName.endsWith('.each')) {
45
45
  return;
46
46
  }
47
47
 
@@ -39,7 +39,7 @@ var _default = (0, _utils.createRule)({
39
39
  suggestRemovingExtraArguments: 'Remove extra arguments'
40
40
  },
41
41
  type: 'suggestion',
42
- hasSuggestion: true,
42
+ hasSuggestions: true,
43
43
  schema: [{
44
44
  type: 'object',
45
45
  properties: {
@@ -0,0 +1,48 @@
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
+ var _default = (0, _utils.createRule)({
13
+ name: __filename,
14
+ meta: {
15
+ docs: {
16
+ category: 'Best Practices',
17
+ description: 'Prefer `await expect(...).resolves` over `expect(await ...)` syntax',
18
+ recommended: false
19
+ },
20
+ fixable: 'code',
21
+ messages: {
22
+ expectResolves: 'Use `await expect(...).resolves instead.'
23
+ },
24
+ schema: [],
25
+ type: 'suggestion'
26
+ },
27
+ defaultOptions: [],
28
+ create: context => ({
29
+ CallExpression(node) {
30
+ const [awaitNode] = node.arguments;
31
+
32
+ if ((0, _utils.isExpectCall)(node) && (awaitNode === null || awaitNode === void 0 ? void 0 : awaitNode.type) === _experimentalUtils.AST_NODE_TYPES.AwaitExpression) {
33
+ context.report({
34
+ node: node.arguments[0],
35
+ messageId: 'expectResolves',
36
+
37
+ fix(fixer) {
38
+ return [fixer.insertTextBefore(node, 'await '), fixer.removeRange([awaitNode.range[0], awaitNode.argument.range[0]]), fixer.insertTextAfter(node, '.resolves')];
39
+ }
40
+
41
+ });
42
+ }
43
+ }
44
+
45
+ })
46
+ });
47
+
48
+ exports.default = _default;
@@ -22,7 +22,7 @@ var _default = (0, _utils.createRule)({
22
22
  },
23
23
  type: 'suggestion',
24
24
  schema: [],
25
- hasSuggestion: true
25
+ hasSuggestions: true
26
26
  },
27
27
  defaultOptions: [],
28
28
 
@@ -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;
@@ -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`,
@@ -9,7 +9,7 @@ var _experimentalUtils = require("@typescript-eslint/experimental-utils");
9
9
 
10
10
  var _utils = require("./utils");
11
11
 
12
- const isThenOrCatchCall = node => node.type === _experimentalUtils.AST_NODE_TYPES.CallExpression && node.callee.type === _experimentalUtils.AST_NODE_TYPES.MemberExpression && (0, _utils.isSupportedAccessor)(node.callee.property) && ['then', 'catch'].includes((0, _utils.getAccessorValue)(node.callee.property));
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
13
 
14
14
  const isExpectCallPresentInFunction = body => {
15
15
  if (body.type === _experimentalUtils.AST_NODE_TYPES.BlockStatement) {
@@ -58,11 +58,11 @@ const isPromiseReturnedLater = (node, testFunctionBody) => {
58
58
  return lastLineInTestFunc.type === _experimentalUtils.AST_NODE_TYPES.ReturnStatement && lastLineInTestFunc.argument && ('name' in lastLineInTestFunc.argument && lastLineInTestFunc.argument.name === promiseName || !promiseName);
59
59
  };
60
60
 
61
- const isTestFunc = node => node.type === _experimentalUtils.AST_NODE_TYPES.CallExpression && (0, _utils.isSupportedAccessor)(node.callee) && [_utils.TestCaseName.it, _utils.TestCaseName.test].includes((0, _utils.getAccessorValue)(node.callee));
62
-
63
61
  const findTestFunction = node => {
64
62
  while (node) {
65
- if ((0, _utils.isFunction)(node) && node.parent && isTestFunc(node.parent)) {
63
+ var _node$parent;
64
+
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
66
  return node;
67
67
  }
68
68
 
@@ -108,7 +108,7 @@ var _default = (0, _utils.createRule)({
108
108
  create(context) {
109
109
  return {
110
110
  CallExpression(node) {
111
- if (!isThenOrCatchCall(node) || node.parent && node.parent.type === _experimentalUtils.AST_NODE_TYPES.AwaitExpression) {
111
+ if (!isPromiseChainCall(node) || node.parent && node.parent.type === _experimentalUtils.AST_NODE_TYPES.AwaitExpression) {
112
112
  return;
113
113
  }
114
114
 
@@ -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.1",
3
+ "version": "24.5.1",
4
4
  "description": "Eslint rules for Jest",
5
5
  "keywords": [
6
6
  "eslint",
@@ -89,8 +89,8 @@
89
89
  "@commitlint/cli": "^12.0.0",
90
90
  "@commitlint/config-conventional": "^12.0.0",
91
91
  "@schemastore/package": "^0.0.6",
92
- "@semantic-release/changelog": "^5.0.1",
93
- "@semantic-release/git": "^9.0.0",
92
+ "@semantic-release/changelog": "^6.0.0",
93
+ "@semantic-release/git": "^10.0.0",
94
94
  "@types/dedent": "^0.7.0",
95
95
  "@types/jest": "^27.0.0",
96
96
  "@types/node": "^14.0.0",
@@ -111,13 +111,13 @@
111
111
  "husky": "^6.0.0",
112
112
  "is-ci": "^3.0.0",
113
113
  "jest": "^27.0.0",
114
- "jest-runner-eslint": "^0.10.0",
114
+ "jest-runner-eslint": "^0.11.0",
115
115
  "lint-staged": "^10.2.2",
116
116
  "pinst": "^2.0.0",
117
117
  "prettier": "^2.0.5",
118
118
  "resolve-from": "^5.0.0",
119
119
  "rimraf": "^3.0.0",
120
- "semantic-release": "^17.0.7",
120
+ "semantic-release": "^18.0.0",
121
121
  "semver": "^7.3.5",
122
122
  "ts-node": "^9.0.0",
123
123
  "typescript": "^4.4.0"