eslint-plugin-jest 29.6.0 → 29.7.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
|
@@ -396,6 +396,7 @@ Manually fixable by
|
|
|
396
396
|
|
|
397
397
|
| Name | Description | 💼 | ⚠️ | 🔧 | 💡 |
|
|
398
398
|
| :----------------------------------------------------------------- | :----------------------------------------------------------- | :-- | :-- | :-- | :-- |
|
|
399
|
+
| [no-error-equal](docs/rules/no-error-equal.md) | Disallow using equality matchers on error types | | | | |
|
|
399
400
|
| [no-unnecessary-assertion](docs/rules/no-unnecessary-assertion.md) | Disallow unnecessary assertions based on types | | | | |
|
|
400
401
|
| [unbound-method](docs/rules/unbound-method.md) | Enforce unbound methods are called with their expected scope | | | | |
|
|
401
402
|
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Disallow using equality matchers on error types (`no-error-equal`)
|
|
2
|
+
|
|
3
|
+
💭 This rule requires
|
|
4
|
+
[type information](https://typescript-eslint.io/linting/typed-linting).
|
|
5
|
+
|
|
6
|
+
<!-- end auto-generated rule header -->
|
|
7
|
+
|
|
8
|
+
When comparing errors, `toEqual` and `toStrictEqual` will only compare the
|
|
9
|
+
`message` properties, meaning tests can pass even if the errors are of different
|
|
10
|
+
types.
|
|
11
|
+
|
|
12
|
+
Instead, it is better to use `toThrow` which does check the error type along
|
|
13
|
+
with its message.
|
|
14
|
+
|
|
15
|
+
## Rule details
|
|
16
|
+
|
|
17
|
+
This rule warns when `toEqual` and `toStrictEqual` is used with an `Error` type.
|
|
18
|
+
|
|
19
|
+
The following patterns are considered warnings:
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
expect(new AggregateError([], expect.any(String))).toEqual(
|
|
23
|
+
new Error(expect.any(String)),
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
expect(new Error('hello world')).toStrictEqual('hello sunshine');
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The following patterns are not considered warnings:
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
expect(() => throw new AggregateError([], expect.any(String))).toThrow(new Error(expect.any(String)));
|
|
33
|
+
|
|
34
|
+
expect(() => throw new Error('hello world')).toThrow('hello sunshine');
|
|
35
|
+
```
|
|
@@ -0,0 +1,111 @@
|
|
|
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
|
+
let SymbolFlags;
|
|
10
|
+
let TypeFlags;
|
|
11
|
+
function isSymbolFromDefaultLibrary(program, symbol) {
|
|
12
|
+
/* istanbul ignore next */
|
|
13
|
+
const declarations = symbol.getDeclarations() ?? [];
|
|
14
|
+
for (const declaration of declarations) {
|
|
15
|
+
const sourceFile = declaration.getSourceFile();
|
|
16
|
+
|
|
17
|
+
/* istanbul ignore else */
|
|
18
|
+
if (program.isSourceFileDefaultLibrary(sourceFile)) {
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/* istanbul ignore next */
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
function isBuiltinSymbolLike(program, type, symbolName) {
|
|
27
|
+
return isBuiltinSymbolLikeRecurser(program, type, subType => {
|
|
28
|
+
const symbol = subType.getSymbol();
|
|
29
|
+
if (!symbol) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
const actualSymbolName = symbol.getName();
|
|
33
|
+
if (actualSymbolName === symbolName && isSymbolFromDefaultLibrary(program, symbol)) {
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
function isBuiltinSymbolLikeRecurser(program, type, predicate) {
|
|
40
|
+
if (type.isIntersection()) {
|
|
41
|
+
return type.types.some(t => isBuiltinSymbolLikeRecurser(program, t, predicate));
|
|
42
|
+
}
|
|
43
|
+
if (type.isUnion()) {
|
|
44
|
+
return type.types.every(t => isBuiltinSymbolLikeRecurser(program, t, predicate));
|
|
45
|
+
}
|
|
46
|
+
if (isTypeParameter(type)) {
|
|
47
|
+
const t = type.getConstraint();
|
|
48
|
+
if (t) {
|
|
49
|
+
return isBuiltinSymbolLikeRecurser(program, t, predicate);
|
|
50
|
+
}
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
const predicateResult = predicate(type);
|
|
54
|
+
if (typeof predicateResult === 'boolean') {
|
|
55
|
+
return predicateResult;
|
|
56
|
+
}
|
|
57
|
+
const symbol = type.getSymbol();
|
|
58
|
+
|
|
59
|
+
/* istanbul ignore next */
|
|
60
|
+
if (symbol && symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) {
|
|
61
|
+
const checker = program.getTypeChecker();
|
|
62
|
+
for (const baseType of checker.getBaseTypes(type)) {
|
|
63
|
+
if (isBuiltinSymbolLikeRecurser(program, baseType, predicate)) {
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
function isTypeParameter(type) {
|
|
71
|
+
return (type.flags & TypeFlags.TypeParameter) !== 0;
|
|
72
|
+
}
|
|
73
|
+
var _default = exports.default = (0, _utils2.createRule)({
|
|
74
|
+
name: __filename,
|
|
75
|
+
meta: {
|
|
76
|
+
docs: {
|
|
77
|
+
description: 'Disallow using equality matchers on error types',
|
|
78
|
+
requiresTypeChecking: true
|
|
79
|
+
},
|
|
80
|
+
messages: {
|
|
81
|
+
equalError: 'Avoid using equality matchers to check errors'
|
|
82
|
+
},
|
|
83
|
+
type: 'suggestion',
|
|
84
|
+
schema: []
|
|
85
|
+
},
|
|
86
|
+
defaultOptions: [],
|
|
87
|
+
create(context) {
|
|
88
|
+
const services = _utils.ESLintUtils.getParserServices(context);
|
|
89
|
+
|
|
90
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
91
|
+
({
|
|
92
|
+
TypeFlags,
|
|
93
|
+
SymbolFlags
|
|
94
|
+
} = require('typescript'));
|
|
95
|
+
return {
|
|
96
|
+
CallExpression(node) {
|
|
97
|
+
const jestFnCall = (0, _utils2.parseJestFnCall)(node, context);
|
|
98
|
+
if (jestFnCall?.type !== 'expect' || jestFnCall.head.node.parent.type !== _utils.AST_NODE_TYPES.CallExpression || !['toEqual', 'toStrictEqual'].includes((0, _utils2.getAccessorValue)(jestFnCall.matcher))) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
const [argument] = jestFnCall.head.node.parent.arguments;
|
|
102
|
+
if (isBuiltinSymbolLike(services.program, services.getTypeAtLocation(argument), 'Error')) {
|
|
103
|
+
context.report({
|
|
104
|
+
messageId: 'equalError',
|
|
105
|
+
node
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
});
|
|
@@ -61,6 +61,11 @@ var _default = exports.default = (0, _utils2.createRule)({
|
|
|
61
61
|
if (!['toBeNull', 'toBeDefined', 'toBeUndefined'].includes(matcherName)) {
|
|
62
62
|
return;
|
|
63
63
|
}
|
|
64
|
+
|
|
65
|
+
// todo: we should support resolving promise types
|
|
66
|
+
if (jestFnCall.modifiers.some(nod => (0, _utils2.getAccessorValue)(nod) !== 'not')) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
64
69
|
const [argument] = jestFnCall.head.node.parent.arguments;
|
|
65
70
|
const isNullable = canBe(services.getTypeAtLocation(argument), matcherName === 'toBeNull' ? TypeFlags.Null : TypeFlags.Undefined);
|
|
66
71
|
if (!isNullable) {
|