Skip to content

Commit 0950a96

Browse files
authored
fix(no-restricted-matchers): allow restricting negated resolves and rejects modifiers (#1142)
1 parent 7c5e662 commit 0950a96

File tree

2 files changed

+129
-44
lines changed

2 files changed

+129
-44
lines changed

src/rules/__tests__/no-restricted-matchers.test.ts

+76
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,66 @@ ruleTester.run('no-restricted-matchers', rule, {
9999
},
100100
],
101101
},
102+
{
103+
code: 'expect(a).resolves.toBe(b)',
104+
options: [{ resolves: null }],
105+
errors: [
106+
{
107+
messageId: 'restrictedChain',
108+
data: {
109+
message: null,
110+
chain: 'resolves',
111+
},
112+
column: 11,
113+
line: 1,
114+
},
115+
],
116+
},
117+
{
118+
code: 'expect(a).resolves.not.toBe(b)',
119+
options: [{ not: null }],
120+
errors: [
121+
{
122+
messageId: 'restrictedChain',
123+
data: {
124+
message: null,
125+
chain: 'not',
126+
},
127+
column: 20,
128+
line: 1,
129+
},
130+
],
131+
},
132+
{
133+
code: 'expect(a).resolves.not.toBe(b)',
134+
options: [{ resolves: null }],
135+
errors: [
136+
{
137+
messageId: 'restrictedChain',
138+
data: {
139+
message: null,
140+
chain: 'resolves',
141+
},
142+
column: 11,
143+
line: 1,
144+
},
145+
],
146+
},
147+
{
148+
code: 'expect(a).resolves.not.toBe(b)',
149+
options: [{ 'resolves.not': null }],
150+
errors: [
151+
{
152+
messageId: 'restrictedChain',
153+
data: {
154+
message: null,
155+
chain: 'resolves.not',
156+
},
157+
column: 11,
158+
line: 1,
159+
},
160+
],
161+
},
102162
{
103163
code: 'expect(a).not.toBe(b)',
104164
options: [{ 'not.toBe': null }],
@@ -115,6 +175,22 @@ ruleTester.run('no-restricted-matchers', rule, {
115175
},
116176
],
117177
},
178+
{
179+
code: 'expect(a).resolves.not.toBe(b)',
180+
options: [{ 'resolves.not.toBe': null }],
181+
errors: [
182+
{
183+
messageId: 'restrictedChain',
184+
data: {
185+
message: null,
186+
chain: 'resolves.not.toBe',
187+
},
188+
endColumn: 28,
189+
column: 11,
190+
line: 1,
191+
},
192+
],
193+
},
118194
{
119195
code: 'expect(a).toBe(b)',
120196
options: [{ toBe: 'Prefer `toStrictEqual` instead' }],

src/rules/no-restricted-matchers.ts

+53-44
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { TSESTree } from '@typescript-eslint/utils';
12
import { createRule, isExpectCall, parseExpectCall } from './utils';
23

34
export default createRule<
@@ -27,6 +28,25 @@ export default createRule<
2728
},
2829
defaultOptions: [{}],
2930
create(context, [restrictedChains]) {
31+
const reportIfRestricted = (
32+
loc: TSESTree.SourceLocation,
33+
chain: string,
34+
): boolean => {
35+
if (!(chain in restrictedChains)) {
36+
return false;
37+
}
38+
39+
const message = restrictedChains[chain];
40+
41+
context.report({
42+
messageId: message ? 'restrictedChainWithMessage' : 'restrictedChain',
43+
data: { message, chain },
44+
loc,
45+
});
46+
47+
return true;
48+
};
49+
3050
return {
3151
CallExpression(node) {
3252
if (!isExpectCall(node)) {
@@ -35,61 +55,50 @@ export default createRule<
3555

3656
const { matcher, modifier } = parseExpectCall(node);
3757

38-
if (matcher) {
39-
const chain = matcher.name;
40-
41-
if (chain in restrictedChains) {
42-
const message = restrictedChains[chain];
43-
44-
context.report({
45-
messageId: message
46-
? 'restrictedChainWithMessage'
47-
: 'restrictedChain',
48-
data: { message, chain },
49-
node: matcher.node.property,
50-
});
51-
52-
return;
53-
}
58+
if (
59+
matcher &&
60+
reportIfRestricted(matcher.node.property.loc, matcher.name)
61+
) {
62+
return;
5463
}
5564

5665
if (modifier) {
57-
const chain = modifier.name;
58-
59-
if (chain in restrictedChains) {
60-
const message = restrictedChains[chain];
61-
62-
context.report({
63-
messageId: message
64-
? 'restrictedChainWithMessage'
65-
: 'restrictedChain',
66-
data: { message, chain },
67-
node: modifier.node.property,
68-
});
69-
66+
if (reportIfRestricted(modifier.node.property.loc, modifier.name)) {
7067
return;
7168
}
69+
70+
if (modifier.negation) {
71+
if (
72+
reportIfRestricted(modifier.negation.property.loc, 'not') ||
73+
reportIfRestricted(
74+
{
75+
start: modifier.node.property.loc.start,
76+
end: modifier.negation.property.loc.end,
77+
},
78+
`${modifier.name}.not`,
79+
)
80+
) {
81+
return;
82+
}
83+
}
7284
}
7385

7486
if (matcher && modifier) {
75-
const chain = `${modifier.name}.${matcher.name}`;
87+
let chain: string = modifier.name;
7688

77-
if (chain in restrictedChains) {
78-
const message = restrictedChains[chain];
89+
if (modifier.negation) {
90+
chain += '.not';
91+
}
7992

80-
context.report({
81-
messageId: message
82-
? 'restrictedChainWithMessage'
83-
: 'restrictedChain',
84-
data: { message, chain },
85-
loc: {
86-
start: modifier.node.property.loc.start,
87-
end: matcher.node.property.loc.end,
88-
},
89-
});
93+
chain += `.${matcher.name}`;
9094

91-
return;
92-
}
95+
reportIfRestricted(
96+
{
97+
start: modifier.node.property.loc.start,
98+
end: matcher.node.property.loc.end,
99+
},
100+
chain,
101+
);
93102
}
94103
},
95104
};

0 commit comments

Comments
 (0)