Skip to content

Commit 1f052e2

Browse files
committed
fix(no-conditional-expect): check for expects in catchs on promises
1 parent 72fe0c6 commit 1f052e2

File tree

3 files changed

+145
-1
lines changed

3 files changed

+145
-1
lines changed

docs/rules/no-conditional-expect.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
This rule prevents the use of `expect` in conditional blocks, such as `if`s &
44
`catch`s.
55

6+
This includes using `expect` in callbacks to functions named `catch`, which are
7+
assumed to be promises.
8+
69
## Rule Details
710

811
Jest considered a test to have failed if it throws an error, rather than on if
@@ -37,6 +40,10 @@ it('baz', async () => {
3740
expect(err).toMatchObject({ code: 'MODULE_NOT_FOUND' });
3841
}
3942
});
43+
44+
it('throws an error', async () => {
45+
await foo().catch(error => expect(error).toBeInstanceOf(error));
46+
});
4047
```
4148

4249
The following patterns are not warnings:
@@ -67,4 +74,8 @@ it('validates the request', () => {
6774
expect(validRequest).toHaveBeenCalledWith(request);
6875
}
6976
});
77+
78+
it('throws an error', async () => {
79+
await expect(foo).rejects.toThrow(Error)
80+
});
7081
```

src/rules/__tests__/no-conditional-expect.test.ts

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { TSESLint } from '@typescript-eslint/experimental-utils';
2+
import dedent from 'dedent';
23
import resolveFrom from 'resolve-from';
34
import rule from '../no-conditional-expect';
45

@@ -519,3 +520,108 @@ ruleTester.run('catch conditions', rule, {
519520
},
520521
],
521522
});
523+
524+
ruleTester.run('promises', rule, {
525+
valid: [
526+
`
527+
it('works', async () => {
528+
try {
529+
await Promise.resolve().then(() => {
530+
throw new Error('oh noes!');
531+
});
532+
} catch {
533+
// ignore errors
534+
} finally {
535+
expect(something).toHaveBeenCalled();
536+
}
537+
});
538+
`,
539+
`
540+
it('works', async () => {
541+
await doSomething().catch(error => error);
542+
543+
expect(error).toBeInstanceOf(Error);
544+
});
545+
`,
546+
`
547+
it('works', async () => {
548+
try {
549+
await Promise.resolve().then(() => {
550+
throw new Error('oh noes!');
551+
});
552+
} catch {
553+
// ignore errors
554+
}
555+
556+
expect(something).toHaveBeenCalled();
557+
});
558+
`,
559+
],
560+
invalid: [
561+
{
562+
code: dedent`
563+
it('works', async () => {
564+
await Promise.resolve()
565+
.then(() => { throw new Error('oh noes!'); })
566+
.catch(error => expect(error).toBeInstanceOf(Error));
567+
});
568+
`,
569+
errors: [{ messageId: 'conditionalExpect' }],
570+
},
571+
{
572+
code: dedent`
573+
it('works', async () => {
574+
await Promise.resolve()
575+
.then(() => { throw new Error('oh noes!'); })
576+
.catch(error => expect(error).toBeInstanceOf(Error))
577+
.then(() => { throw new Error('oh noes!'); })
578+
.catch(error => expect(error).toBeInstanceOf(Error))
579+
.then(() => { throw new Error('oh noes!'); })
580+
.catch(error => expect(error).toBeInstanceOf(Error));
581+
});
582+
`,
583+
errors: [{ messageId: 'conditionalExpect' }],
584+
},
585+
{
586+
code: dedent`
587+
it('works', async () => {
588+
await Promise.resolve()
589+
.catch(error => expect(error).toBeInstanceOf(Error))
590+
.catch(error => expect(error).toBeInstanceOf(Error))
591+
.catch(error => expect(error).toBeInstanceOf(Error));
592+
});
593+
`,
594+
errors: [{ messageId: 'conditionalExpect' }],
595+
},
596+
{
597+
code: dedent`
598+
it('works', async () => {
599+
await Promise.resolve()
600+
.catch(error => expect(error).toBeInstanceOf(Error))
601+
.then(() => { throw new Error('oh noes!'); })
602+
.then(() => { throw new Error('oh noes!'); })
603+
.then(() => { throw new Error('oh noes!'); });
604+
});
605+
`,
606+
errors: [{ messageId: 'conditionalExpect' }],
607+
},
608+
{
609+
code: dedent`
610+
it('works', async () => {
611+
await somePromise
612+
.then(() => { throw new Error('oh noes!'); })
613+
.catch(error => expect(error).toBeInstanceOf(Error));
614+
});
615+
`,
616+
errors: [{ messageId: 'conditionalExpect' }],
617+
},
618+
{
619+
code: dedent`
620+
it('works', async () => {
621+
await somePromise.catch(error => expect(error).toBeInstanceOf(Error));
622+
});
623+
`,
624+
errors: [{ messageId: 'conditionalExpect' }],
625+
},
626+
],
627+
});

src/rules/no-conditional-expect.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,22 @@
1-
import { TSESTree } from '@typescript-eslint/experimental-utils';
21
import {
2+
AST_NODE_TYPES,
3+
TSESTree,
4+
} from '@typescript-eslint/experimental-utils';
5+
import {
6+
KnownCallExpression,
37
createRule,
48
getTestCallExpressionsFromDeclaredVariables,
59
isExpectCall,
10+
isSupportedAccessor,
611
isTestCaseCall,
712
} from './utils';
813

14+
const isCatchCall = (
15+
node: TSESTree.CallExpression,
16+
): node is KnownCallExpression<'catch'> =>
17+
node.callee.type === AST_NODE_TYPES.MemberExpression &&
18+
isSupportedAccessor(node.callee.property, 'catch');
19+
920
export default createRule({
1021
name: __filename,
1122
meta: {
@@ -24,6 +35,7 @@ export default createRule({
2435
create(context) {
2536
let conditionalDepth = 0;
2637
let inTestCase = false;
38+
let inPromiseCatch = false;
2739

2840
const increaseConditionalDepth = () => inTestCase && conditionalDepth++;
2941
const decreaseConditionalDepth = () => inTestCase && conditionalDepth--;
@@ -44,17 +56,32 @@ export default createRule({
4456
inTestCase = true;
4557
}
4658

59+
if (isCatchCall(node)) {
60+
inPromiseCatch = true;
61+
}
62+
4763
if (inTestCase && isExpectCall(node) && conditionalDepth > 0) {
4864
context.report({
4965
messageId: 'conditionalExpect',
5066
node,
5167
});
5268
}
69+
70+
if (inPromiseCatch && isExpectCall(node)) {
71+
context.report({
72+
messageId: 'conditionalExpect',
73+
node,
74+
});
75+
}
5376
},
5477
'CallExpression:exit'(node) {
5578
if (isTestCaseCall(node)) {
5679
inTestCase = false;
5780
}
81+
82+
if (isCatchCall(node)) {
83+
inPromiseCatch = false;
84+
}
5885
},
5986
CatchClause: increaseConditionalDepth,
6087
'CatchClause:exit': decreaseConditionalDepth,

0 commit comments

Comments
 (0)