eslint-plugin-jest 28.12.0 → 28.13.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.
package/README.md
CHANGED
|
@@ -363,6 +363,7 @@ Manually fixable by
|
|
|
363
363
|
| [prefer-called-with](docs/rules/prefer-called-with.md) | Suggest using `toBeCalledWith()` or `toHaveBeenCalledWith()` | | | | |
|
|
364
364
|
| [prefer-comparison-matcher](docs/rules/prefer-comparison-matcher.md) | Suggest using the built-in comparison matchers | | | 🔧 | |
|
|
365
365
|
| [prefer-each](docs/rules/prefer-each.md) | Prefer using `.each` rather than manual loops | | | | |
|
|
366
|
+
| [prefer-ending-with-an-expect](docs/rules/prefer-ending-with-an-expect.md) | Prefer having the last statement in a test be an assertion | | | | |
|
|
366
367
|
| [prefer-equality-matcher](docs/rules/prefer-equality-matcher.md) | Suggest using the built-in equality matchers | | | | 💡 |
|
|
367
368
|
| [prefer-expect-assertions](docs/rules/prefer-expect-assertions.md) | Suggest using `expect.assertions()` OR `expect.hasAssertions()` | | | | 💡 |
|
|
368
369
|
| [prefer-expect-resolves](docs/rules/prefer-expect-resolves.md) | Prefer `await expect(...).resolves` over `expect(await ...)` syntax | | | 🔧 | |
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# Prefer having the last statement in a test be an assertion (`prefer-ending-with-an-expect`)
|
|
2
|
+
|
|
3
|
+
<!-- end auto-generated rule header -->
|
|
4
|
+
|
|
5
|
+
Prefer ending tests with an `expect` assertion.
|
|
6
|
+
|
|
7
|
+
## Rule details
|
|
8
|
+
|
|
9
|
+
This rule triggers when a test body does not end with an `expect` call, which
|
|
10
|
+
can indicate an unfinished test.
|
|
11
|
+
|
|
12
|
+
Examples of **incorrect** code for this rule:
|
|
13
|
+
|
|
14
|
+
```js
|
|
15
|
+
it('lets me change the selected option', () => {
|
|
16
|
+
const container = render(MySelect, {
|
|
17
|
+
props: { options: [1, 2, 3], selected: 1 },
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
expect(container).toBeDefined();
|
|
21
|
+
expect(container.toHTML()).toContain('<option value="1" selected>');
|
|
22
|
+
|
|
23
|
+
container.setProp('selected', 2);
|
|
24
|
+
});
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Examples of **correct** code for this rule:
|
|
28
|
+
|
|
29
|
+
```js
|
|
30
|
+
it('lets me change the selected option', () => {
|
|
31
|
+
const container = render(MySelect, {
|
|
32
|
+
props: { options: [1, 2, 3], selected: 1 },
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
expect(container).toBeDefined();
|
|
36
|
+
expect(container.toHTML()).toContain('<option value="1" selected>');
|
|
37
|
+
|
|
38
|
+
container.setProp('selected', 2);
|
|
39
|
+
|
|
40
|
+
expect(container.toHTML()).not.toContain('<option value="1" selected>');
|
|
41
|
+
expect(container.toHTML()).toContain('<option value="2" selected>');
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Options
|
|
46
|
+
|
|
47
|
+
```json
|
|
48
|
+
{
|
|
49
|
+
"jest/prefer-ending-with-an-expect": [
|
|
50
|
+
"error",
|
|
51
|
+
{
|
|
52
|
+
"assertFunctionNames": ["expect"],
|
|
53
|
+
"additionalTestBlockFunctions": []
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### `assertFunctionNames`
|
|
60
|
+
|
|
61
|
+
This array option specifies the names of functions that should be considered to
|
|
62
|
+
be asserting functions. Function names can use wildcards i.e `request.*.expect`,
|
|
63
|
+
`request.**.expect`, `request.*.expect*`
|
|
64
|
+
|
|
65
|
+
Examples of **incorrect** code for the `{ "assertFunctionNames": ["expect"] }`
|
|
66
|
+
option:
|
|
67
|
+
|
|
68
|
+
```js
|
|
69
|
+
/* eslint jest/prefer-ending-with-an-expect: ["error", { "assertFunctionNames": ["expect"] }] */
|
|
70
|
+
|
|
71
|
+
import { expectSaga } from 'redux-saga-test-plan';
|
|
72
|
+
import { addSaga } from '../src/sagas';
|
|
73
|
+
|
|
74
|
+
test('returns sum', () => {
|
|
75
|
+
expectSaga(addSaga, 1, 1).returns(2).run();
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Examples of **correct** code for the
|
|
80
|
+
`{ "assertFunctionNames": ["expect", "expectSaga"] }` option:
|
|
81
|
+
|
|
82
|
+
```js
|
|
83
|
+
/* eslint jest/prefer-ending-with-an-expect: ["error", { "assertFunctionNames": ["expect", "expectSaga"] }] */
|
|
84
|
+
|
|
85
|
+
import { expectSaga } from 'redux-saga-test-plan';
|
|
86
|
+
import { addSaga } from '../src/sagas';
|
|
87
|
+
|
|
88
|
+
test('returns sum', () => {
|
|
89
|
+
expectSaga(addSaga, 1, 1).returns(2).run();
|
|
90
|
+
});
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Since the string is compiled into a regular expression, you'll need to escape
|
|
94
|
+
special characters such as `$` with a double backslash:
|
|
95
|
+
|
|
96
|
+
```js
|
|
97
|
+
/* eslint jest/prefer-ending-with-an-expect: ["error", { "assertFunctionNames": ["expect\\$"] }] */
|
|
98
|
+
|
|
99
|
+
it('is money-like', () => {
|
|
100
|
+
expect$(1.0);
|
|
101
|
+
});
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Examples of **correct** code for working with the HTTP assertions library
|
|
105
|
+
[SuperTest](https://www.npmjs.com/package/supertest) with the
|
|
106
|
+
`{ "assertFunctionNames": ["expect", "request.**.expect"] }` option:
|
|
107
|
+
|
|
108
|
+
```js
|
|
109
|
+
/* eslint jest/prefer-ending-with-an-expect: ["error", { "assertFunctionNames": ["expect", "request.**.expect"] }] */
|
|
110
|
+
const request = require('supertest');
|
|
111
|
+
const express = require('express');
|
|
112
|
+
|
|
113
|
+
const app = express();
|
|
114
|
+
|
|
115
|
+
describe('GET /user', function () {
|
|
116
|
+
it('responds with json', function (done) {
|
|
117
|
+
doSomething();
|
|
118
|
+
|
|
119
|
+
request(app).get('/user').expect('Content-Type', /json/).expect(200, done);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### `additionalTestBlockFunctions`
|
|
125
|
+
|
|
126
|
+
This array can be used to specify the names of functions that should also be
|
|
127
|
+
treated as test blocks:
|
|
128
|
+
|
|
129
|
+
```json
|
|
130
|
+
{
|
|
131
|
+
"rules": {
|
|
132
|
+
"jest/prefer-ending-with-an-expect": [
|
|
133
|
+
"error",
|
|
134
|
+
{ "additionalTestBlockFunctions": ["each.test"] }
|
|
135
|
+
]
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
The following is _correct_ when using the above configuration:
|
|
141
|
+
|
|
142
|
+
```js
|
|
143
|
+
each([
|
|
144
|
+
[2, 3],
|
|
145
|
+
[1, 3],
|
|
146
|
+
]).test(
|
|
147
|
+
'the selection can change from %d to %d',
|
|
148
|
+
(firstSelection, secondSelection) => {
|
|
149
|
+
const container = render(MySelect, {
|
|
150
|
+
props: { options: [1, 2, 3], selected: firstSelection },
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
expect(container).toBeDefined();
|
|
154
|
+
expect(container.toHTML()).toContain(
|
|
155
|
+
`<option value="${firstSelection}" selected>`,
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
container.setProp('selected', secondSelection);
|
|
159
|
+
|
|
160
|
+
expect(container.toHTML()).not.toContain(
|
|
161
|
+
`<option value="${firstSelection}" selected>`,
|
|
162
|
+
);
|
|
163
|
+
expect(container.toHTML()).toContain(
|
|
164
|
+
`<option value="${secondSelection}" selected>`,
|
|
165
|
+
);
|
|
166
|
+
},
|
|
167
|
+
);
|
|
168
|
+
```
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _utils = require("@typescript-eslint/utils");
|
|
8
|
+
var _utils2 = require("./utils");
|
|
9
|
+
/*
|
|
10
|
+
* This implementation is adapted from eslint-plugin-jasmine.
|
|
11
|
+
* MIT license, Remco Haszing.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Checks if node names returned by getNodeName matches any of the given star patterns
|
|
16
|
+
* Pattern examples:
|
|
17
|
+
* request.*.expect
|
|
18
|
+
* request.**.expect
|
|
19
|
+
* request.**.expect*
|
|
20
|
+
*/
|
|
21
|
+
function matchesAssertFunctionName(nodeName, patterns) {
|
|
22
|
+
return patterns.some(p => new RegExp(`^${p.split('.').map(x => {
|
|
23
|
+
if (x === '**') {
|
|
24
|
+
return '[a-z\\d\\.]*';
|
|
25
|
+
}
|
|
26
|
+
return x.replace(/\*/gu, '[a-z\\d]*');
|
|
27
|
+
}).join('\\.')}(\\.|$)`, 'ui').test(nodeName));
|
|
28
|
+
}
|
|
29
|
+
var _default = exports.default = (0, _utils2.createRule)({
|
|
30
|
+
name: __filename,
|
|
31
|
+
meta: {
|
|
32
|
+
docs: {
|
|
33
|
+
description: 'Prefer having the last statement in a test be an assertion'
|
|
34
|
+
},
|
|
35
|
+
messages: {
|
|
36
|
+
mustEndWithExpect: 'Tests should end with an assertion'
|
|
37
|
+
},
|
|
38
|
+
schema: [{
|
|
39
|
+
type: 'object',
|
|
40
|
+
properties: {
|
|
41
|
+
assertFunctionNames: {
|
|
42
|
+
type: 'array',
|
|
43
|
+
items: {
|
|
44
|
+
type: 'string'
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
additionalTestBlockFunctions: {
|
|
48
|
+
type: 'array',
|
|
49
|
+
items: {
|
|
50
|
+
type: 'string'
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
additionalProperties: false
|
|
55
|
+
}],
|
|
56
|
+
type: 'suggestion'
|
|
57
|
+
},
|
|
58
|
+
defaultOptions: [{
|
|
59
|
+
assertFunctionNames: ['expect'],
|
|
60
|
+
additionalTestBlockFunctions: []
|
|
61
|
+
}],
|
|
62
|
+
create(context, [{
|
|
63
|
+
assertFunctionNames = ['expect'],
|
|
64
|
+
additionalTestBlockFunctions = []
|
|
65
|
+
}]) {
|
|
66
|
+
function getLastStatement(fn) {
|
|
67
|
+
if (fn.body.type === _utils.AST_NODE_TYPES.BlockStatement) {
|
|
68
|
+
if (fn.body.body.length === 0) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
const lastStatement = fn.body.body[fn.body.body.length - 1];
|
|
72
|
+
if (lastStatement.type === _utils.AST_NODE_TYPES.ExpressionStatement) {
|
|
73
|
+
return lastStatement.expression;
|
|
74
|
+
}
|
|
75
|
+
return lastStatement;
|
|
76
|
+
}
|
|
77
|
+
return fn.body;
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
CallExpression(node) {
|
|
81
|
+
const name = (0, _utils2.getNodeName)(node.callee) ?? '';
|
|
82
|
+
if (!(0, _utils2.isTypeOfJestFnCall)(node, context, ['test']) && !additionalTestBlockFunctions.includes(name)) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
if (node.arguments.length < 2 || !(0, _utils2.isFunction)(node.arguments[1])) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const lastStatement = getLastStatement(node.arguments[1]);
|
|
89
|
+
if (lastStatement?.type === _utils.AST_NODE_TYPES.CallExpression && ((0, _utils2.isTypeOfJestFnCall)(lastStatement, context, ['expect']) || matchesAssertFunctionName((0, _utils2.getNodeName)(lastStatement.callee), assertFunctionNames))) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
context.report({
|
|
93
|
+
messageId: 'mustEndWithExpect',
|
|
94
|
+
node: node.callee
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
});
|