Skip to content

Commit 407bc9c

Browse files
committed
refactor: include matcher and modifiers in parsed expect fn call
1 parent 28439b5 commit 407bc9c

15 files changed

+52
-86
lines changed

src/rules/no-alias-methods.ts

+2-9
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,7 @@ export default createRule({
4545
return;
4646
}
4747

48-
const matcher = jestFnCall.members[jestFnCall.members.length - 1];
49-
50-
// if (!matcher) {
51-
// return;
52-
// }
48+
const { matcher } = jestFnCall;
5349

5450
const alias = getAccessorValue(matcher);
5551

@@ -58,10 +54,7 @@ export default createRule({
5854

5955
context.report({
6056
messageId: 'replaceAlias',
61-
data: {
62-
alias,
63-
canonical,
64-
},
57+
data: { alias, canonical },
6558
node: matcher,
6659
fix: fixer => [replaceAccessorFixer(fixer, matcher, canonical)],
6760
});

src/rules/no-interpolation-in-snapshots.ts

+1-3
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,11 @@ export default createRule({
2525
return;
2626
}
2727

28-
const matcher = jestFnCall.members[jestFnCall.members.length - 1];
29-
3028
if (
3129
[
3230
'toMatchInlineSnapshot',
3331
'toThrowErrorMatchingInlineSnapshot',
34-
].includes(getAccessorValue(matcher))
32+
].includes(getAccessorValue(jestFnCall.matcher))
3533
) {
3634
// Check all since the optional 'propertyMatchers' argument might be present
3735
jestFnCall.args.forEach(argument => {

src/rules/no-large-snapshots.ts

+1-3
Original file line numberDiff line numberDiff line change
@@ -118,13 +118,11 @@ export default createRule<[RuleOptions], MessageId>({
118118
return;
119119
}
120120

121-
const matcher = jestFnCall.members[jestFnCall.members.length - 1];
122-
123121
if (
124122
[
125123
'toMatchInlineSnapshot',
126124
'toThrowErrorMatchingInlineSnapshot',
127-
].includes(getAccessorValue(matcher)) &&
125+
].includes(getAccessorValue(jestFnCall.matcher)) &&
128126
jestFnCall.args.length
129127
) {
130128
reportOnViolation(context, jestFnCall.args[0], {

src/rules/prefer-called-with.ts

+2-6
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,11 @@ export default createRule({
2525
return;
2626
}
2727

28-
const matcher = jestFnCall.members[jestFnCall.members.length - 1];
29-
30-
if (
31-
!matcher ||
32-
jestFnCall.members.some(nod => getAccessorValue(nod) === 'not')
33-
) {
28+
if (jestFnCall.modifiers.some(nod => getAccessorValue(nod) === 'not')) {
3429
return;
3530
}
3631

32+
const { matcher } = jestFnCall;
3733
const matcherName = getAccessorValue(matcher);
3834

3935
if (['toBeCalled', 'toHaveBeenCalled'].includes(matcherName)) {

src/rules/prefer-comparison-matcher.ts

+4-5
Original file line numberDiff line numberDiff line change
@@ -88,11 +88,10 @@ export default createRule({
8888
range: [, expectCallEnd],
8989
} = expect;
9090

91-
const matcher = jestFnCall.members[jestFnCall.members.length - 1];
91+
const { matcher } = jestFnCall;
9292
const matcherArg = getFirstMatcherArg(jestFnCall);
9393

9494
if (
95-
!matcher ||
9695
comparison?.type !== AST_NODE_TYPES.BinaryExpression ||
9796
isComparingToString(comparison) ||
9897
!EqualityMatcher.hasOwnProperty(getAccessorValue(matcher)) ||
@@ -101,8 +100,8 @@ export default createRule({
101100
return;
102101
}
103102

104-
const [modifier] = jestFnCall.members;
105-
const hasNot = jestFnCall.members.some(
103+
const [modifier] = jestFnCall.modifiers;
104+
const hasNot = jestFnCall.modifiers.some(
106105
nod => getAccessorValue(nod) === 'not',
107106
);
108107

@@ -121,7 +120,7 @@ export default createRule({
121120

122121
// preserve the existing modifier if it's not a negation
123122
const modifierText =
124-
modifier !== matcher && getAccessorValue(modifier) !== 'not'
123+
modifier && getAccessorValue(modifier) !== 'not'
125124
? `.${getAccessorValue(modifier)}`
126125
: '';
127126

src/rules/prefer-equality-matcher.ts

+4-5
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,10 @@ export default createRule({
4747
range: [, expectCallEnd],
4848
} = expect;
4949

50-
const matcher = jestFnCall.members[jestFnCall.members.length - 1];
50+
const { matcher } = jestFnCall;
5151
const matcherArg = getFirstMatcherArg(jestFnCall);
5252

5353
if (
54-
!matcher ||
5554
comparison?.type !== AST_NODE_TYPES.BinaryExpression ||
5655
(comparison.operator !== '===' && comparison.operator !== '!==') ||
5756
!EqualityMatcher.hasOwnProperty(getAccessorValue(matcher)) ||
@@ -62,8 +61,8 @@ export default createRule({
6261

6362
const matcherValue = matcherArg.value;
6463

65-
const [modifier] = jestFnCall.members;
66-
const hasNot = jestFnCall.members.some(
64+
const [modifier] = jestFnCall.modifiers;
65+
const hasNot = jestFnCall.modifiers.some(
6766
nod => getAccessorValue(nod) === 'not',
6867
);
6968

@@ -80,7 +79,7 @@ export default createRule({
8079

8180
// preserve the existing modifier if it's not a negation
8281
let modifierText =
83-
modifier !== matcher && getAccessorValue(modifier) !== 'not'
82+
modifier && getAccessorValue(modifier) !== 'not'
8483
? `.${getAccessorValue(modifier)}`
8584
: '';
8685

src/rules/prefer-snapshot-hint.ts

+5-13
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,12 @@ const snapshotMatchers = ['toMatchSnapshot', 'toThrowErrorMatchingSnapshot'];
1212
const snapshotMatcherNames = snapshotMatchers;
1313

1414
const isSnapshotMatcherWithoutHint = (expectFnCall: ParsedExpectFnCall) => {
15-
if (!expectFnCall.args || expectFnCall.args.length === 0) {
15+
if (expectFnCall.args.length === 0) {
1616
return true;
1717
}
1818

19-
const matcher = expectFnCall.members[expectFnCall.members.length - 1];
20-
2119
// this matcher only supports one argument which is the hint
22-
if (!isSupportedAccessor(matcher, 'toMatchSnapshot')) {
20+
if (!isSupportedAccessor(expectFnCall.matcher, 'toMatchSnapshot')) {
2321
return expectFnCall.args.length !== 1;
2422
}
2523

@@ -67,12 +65,9 @@ export default createRule<[('always' | 'multi')?], keyof typeof messages>({
6765
const reportSnapshotMatchersWithoutHints = () => {
6866
for (const snapshotMatcher of snapshotMatchers) {
6967
if (isSnapshotMatcherWithoutHint(snapshotMatcher)) {
70-
const matcher =
71-
snapshotMatcher.members[snapshotMatcher.members.length - 1];
72-
7368
context.report({
7469
messageId: 'missingHint',
75-
node: matcher,
70+
node: snapshotMatcher.matcher,
7671
});
7772
}
7873
}
@@ -126,12 +121,9 @@ export default createRule<[('always' | 'multi')?], keyof typeof messages>({
126121
return;
127122
}
128123

129-
const matcher = jestFnCall.members[jestFnCall.members.length - 1];
124+
const matcherName = getAccessorValue(jestFnCall.matcher);
130125

131-
if (
132-
!matcher ||
133-
!snapshotMatcherNames.includes(getAccessorValue(matcher))
134-
) {
126+
if (!snapshotMatcherNames.includes(matcherName)) {
135127
return;
136128
}
137129

src/rules/prefer-strict-equal.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@ export default createRule({
3333
return;
3434
}
3535

36-
const matcher = jestFnCall.members[jestFnCall.members.length - 1];
36+
const { matcher } = jestFnCall;
3737

38-
if (matcher && isSupportedAccessor(matcher, 'toEqual')) {
38+
if (isSupportedAccessor(matcher, 'toEqual')) {
3939
context.report({
4040
messageId: 'useToStrictEqual',
4141
node: matcher,

src/rules/prefer-to-be.ts

+6-8
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,12 @@ const reportPreferToBe = (
5353
expectFnCall: ParsedExpectFnCall,
5454
modifierNode?: AccessorNode,
5555
) => {
56-
const matcher = expectFnCall.members[expectFnCall.members.length - 1];
57-
5856
context.report({
5957
messageId: `useToBe${whatToBe}`,
6058
fix(fixer) {
61-
const fixes = [replaceAccessorFixer(fixer, matcher, `toBe${whatToBe}`)];
59+
const fixes = [
60+
replaceAccessorFixer(fixer, expectFnCall.matcher, `toBe${whatToBe}`),
61+
];
6262

6363
if (expectFnCall.args?.length && whatToBe !== '') {
6464
fixes.push(fixer.remove(expectFnCall.args[0]));
@@ -72,7 +72,7 @@ const reportPreferToBe = (
7272

7373
return fixes;
7474
},
75-
node: matcher,
75+
node: expectFnCall.matcher,
7676
});
7777
};
7878

@@ -105,10 +105,8 @@ export default createRule({
105105
return;
106106
}
107107

108-
const matcher = jestFnCall.members[jestFnCall.members.length - 1];
109-
110-
const matcherName = getAccessorValue(matcher);
111-
const notModifier = jestFnCall.members.find(
108+
const matcherName = getAccessorValue(jestFnCall.matcher);
109+
const notModifier = jestFnCall.modifiers.find(
112110
nod => getAccessorValue(nod) === 'not',
113111
);
114112

src/rules/prefer-to-contain.ts

+6-10
Original file line numberDiff line numberDiff line change
@@ -70,24 +70,20 @@ export default createRule({
7070
range: [, expectCallEnd],
7171
} = expect;
7272

73-
const matcher = jestFnCall.members[jestFnCall.members.length - 1];
74-
const arg = getFirstMatcherArg(jestFnCall);
73+
const { matcher } = jestFnCall;
74+
const matcherArg = getFirstMatcherArg(jestFnCall);
7575

7676
if (
77-
!matcher ||
7877
!includesCall ||
79-
arg.type === AST_NODE_TYPES.SpreadElement ||
80-
jestFnCall.members
81-
.slice(0, -1)
82-
.some(nod => getAccessorValue(nod) !== 'not') ||
78+
matcherArg.type === AST_NODE_TYPES.SpreadElement ||
8379
!EqualityMatcher.hasOwnProperty(getAccessorValue(matcher)) ||
84-
!isBooleanLiteral(arg) ||
80+
!isBooleanLiteral(matcherArg) ||
8581
!isFixableIncludesCallExpression(includesCall)
8682
) {
8783
return;
8884
}
8985

90-
const hasNot = jestFnCall.members.some(
86+
const hasNot = jestFnCall.modifiers.some(
9187
nod => getAccessorValue(nod) === 'not',
9288
);
9389

@@ -97,7 +93,7 @@ export default createRule({
9793

9894
// we need to negate the expectation if the current expected
9995
// value is itself negated by the "not" modifier
100-
const addNotModifier = arg.value === hasNot;
96+
const addNotModifier = matcherArg.value === hasNot;
10197

10298
return [
10399
// remove the "includes" call entirely

src/rules/prefer-to-have-length.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,9 @@ export default createRule({
3939
}
4040

4141
const [argument] = expect.arguments;
42-
const matcher = jestFnCall.members[jestFnCall.members.length - 1];
42+
const { matcher } = jestFnCall;
4343

4444
if (
45-
!matcher ||
4645
!EqualityMatcher.hasOwnProperty(getAccessorValue(matcher)) ||
4746
argument?.type !== AST_NODE_TYPES.MemberExpression ||
4847
!isSupportedAccessor(argument.property, 'length')

src/rules/require-to-throw-message.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,18 @@ export default createRule({
2424
return;
2525
}
2626

27-
const matcher = jestFnCall.members[jestFnCall.members.length - 1];
27+
const { matcher } = jestFnCall;
28+
const matcherName = getAccessorValue(matcher);
2829

2930
if (
30-
matcher &&
3131
jestFnCall.args.length === 0 &&
32-
['toThrow', 'toThrowError'].includes(getAccessorValue(matcher)) &&
33-
!jestFnCall.members.some(nod => getAccessorValue(nod) === 'not')
32+
['toThrow', 'toThrowError'].includes(matcherName) &&
33+
!jestFnCall.modifiers.some(nod => getAccessorValue(nod) === 'not')
3434
) {
3535
// Look for `toThrow` calls with no arguments.
3636
context.report({
3737
messageId: 'addErrorMessage',
38-
data: { matcherName: getAccessorValue(matcher) },
38+
data: { matcherName },
3939
node: matcher,
4040
});
4141
}

src/rules/unbound-method.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,8 @@ export default createRule<Options, MessageIds>({
9292
context,
9393
);
9494

95-
if (jestFnCall?.type === 'expect' && jestFnCall.members.length > 0) {
96-
const matcher = jestFnCall.members[jestFnCall.members.length - 1];
95+
if (jestFnCall?.type === 'expect') {
96+
const { matcher } = jestFnCall;
9797

9898
if (!toThrowMatchers.includes(getAccessorValue(matcher))) {
9999
return;

src/rules/utils/parseJestFnCall.ts

+7-5
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,10 @@ interface ParsedGeneralJestFnCall extends BaseParsedJestFnCall {
9393
type: Exclude<JestFnType, 'expect'>;
9494
}
9595

96-
export interface ParsedExpectFnCall extends BaseParsedJestFnCall {
96+
export interface ParsedExpectFnCall
97+
extends BaseParsedJestFnCall,
98+
ModifiersAndMatcher {
9799
type: 'expect';
98-
args: TSESTree.CallExpression['arguments'];
99100
}
100101

101102
export type ParsedJestFnCall = ParsedGeneralJestFnCall | ParsedExpectFnCall;
@@ -302,7 +303,8 @@ type KnownMemberExpressionProperty<Specifics extends string = string> =
302303
interface ModifiersAndMatcher {
303304
modifiers: KnownMemberExpressionProperty[];
304305
matcher: KnownMemberExpressionProperty;
305-
matcherArgs: TSESTree.CallExpression['arguments'];
306+
/** The arguments that are being passed to the `matcher` */
307+
args: TSESTree.CallExpression['arguments'];
306308
}
307309

308310
const findModifiersAndMatcher = (
@@ -319,7 +321,7 @@ const findModifiersAndMatcher = (
319321
) {
320322
return {
321323
matcher: member,
322-
matcherArgs: member.parent.parent.arguments,
324+
args: member.parent.parent.arguments,
323325
modifiers,
324326
};
325327
}
@@ -372,7 +374,7 @@ const parseJestExpectCall = (
372374
return {
373375
...typelessParsedJestFnCall,
374376
type: 'expect',
375-
args: modifiersAndMatcher.matcherArgs,
377+
...modifiersAndMatcher,
376378
};
377379
};
378380

src/rules/valid-expect.ts

+4-8
Original file line numberDiff line numberDiff line change
@@ -301,13 +301,11 @@ export default createRule<[Options], MessageIds>({
301301
});
302302
}
303303

304-
const matcher = jestFnCall.members[jestFnCall.members.length - 1];
304+
const { matcher } = jestFnCall;
305305

306-
const parentNode = matcher.parent?.parent;
306+
const parentNode = matcher.parent.parent;
307307
const shouldBeAwaited =
308-
jestFnCall.members
309-
.slice(0, -1)
310-
.some(nod => getAccessorValue(nod) !== 'not') ||
308+
jestFnCall.modifiers.some(nod => getAccessorValue(nod) !== 'not') ||
311309
asyncMatchers.includes(getAccessorValue(matcher));
312310

313311
if (!parentNode?.parent || !shouldBeAwaited) {
@@ -338,9 +336,7 @@ export default createRule<[Options], MessageIds>({
338336
) {
339337
context.report({
340338
loc: finalNode.loc,
341-
data: {
342-
orReturned,
343-
},
339+
data: { orReturned },
344340
messageId:
345341
finalNode === targetNode
346342
? 'asyncMustBeAwaited'

0 commit comments

Comments
 (0)