Skip to content

Commit 1fee973

Browse files
authored
fix(no-conditional-expect): check for expects in catchs on promises (#819)
1 parent 040c605 commit 1fee973

File tree

3 files changed

+145
-1
lines changed

3 files changed

+145
-1
lines changed

docs/rules/no-conditional-expect.md

+11
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

+106
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

@@ -584,3 +585,108 @@ ruleTester.run('catch conditions', rule, {
584585
},
585586
],
586587
});
588+
589+
ruleTester.run('promises', rule, {
590+
valid: [
591+
`
592+
it('works', async () => {
593+
try {
594+
await Promise.resolve().then(() => {
595+
throw new Error('oh noes!');
596+
});
597+
} catch {
598+
// ignore errors
599+
} finally {
600+
expect(something).toHaveBeenCalled();
601+
}
602+
});
603+
`,
604+
`
605+
it('works', async () => {
606+
await doSomething().catch(error => error);
607+
608+
expect(error).toBeInstanceOf(Error);
609+
});
610+
`,
611+
`
612+
it('works', async () => {
613+
try {
614+
await Promise.resolve().then(() => {
615+
throw new Error('oh noes!');
616+
});
617+
} catch {
618+
// ignore errors
619+
}
620+
621+
expect(something).toHaveBeenCalled();
622+
});
623+
`,
624+
],
625+
invalid: [
626+
{
627+
code: dedent`
628+
it('works', async () => {
629+
await Promise.resolve()
630+
.then(() => { throw new Error('oh noes!'); })
631+
.catch(error => expect(error).toBeInstanceOf(Error));
632+
});
633+
`,
634+
errors: [{ messageId: 'conditionalExpect' }],
635+
},
636+
{
637+
code: dedent`
638+
it('works', async () => {
639+
await Promise.resolve()
640+
.then(() => { throw new Error('oh noes!'); })
641+
.catch(error => expect(error).toBeInstanceOf(Error))
642+
.then(() => { throw new Error('oh noes!'); })
643+
.catch(error => expect(error).toBeInstanceOf(Error))
644+
.then(() => { throw new Error('oh noes!'); })
645+
.catch(error => expect(error).toBeInstanceOf(Error));
646+
});
647+
`,
648+
errors: [{ messageId: 'conditionalExpect' }],
649+
},
650+
{
651+
code: dedent`
652+
it('works', async () => {
653+
await Promise.resolve()
654+
.catch(error => expect(error).toBeInstanceOf(Error))
655+
.catch(error => expect(error).toBeInstanceOf(Error))
656+
.catch(error => expect(error).toBeInstanceOf(Error));
657+
});
658+
`,
659+
errors: [{ messageId: 'conditionalExpect' }],
660+
},
661+
{
662+
code: dedent`
663+
it('works', async () => {
664+
await Promise.resolve()
665+
.catch(error => expect(error).toBeInstanceOf(Error))
666+
.then(() => { throw new Error('oh noes!'); })
667+
.then(() => { throw new Error('oh noes!'); })
668+
.then(() => { throw new Error('oh noes!'); });
669+
});
670+
`,
671+
errors: [{ messageId: 'conditionalExpect' }],
672+
},
673+
{
674+
code: dedent`
675+
it('works', async () => {
676+
await somePromise
677+
.then(() => { throw new Error('oh noes!'); })
678+
.catch(error => expect(error).toBeInstanceOf(Error));
679+
});
680+
`,
681+
errors: [{ messageId: 'conditionalExpect' }],
682+
},
683+
{
684+
code: dedent`
685+
it('works', async () => {
686+
await somePromise.catch(error => expect(error).toBeInstanceOf(Error));
687+
});
688+
`,
689+
errors: [{ messageId: 'conditionalExpect' }],
690+
},
691+
],
692+
});

src/rules/no-conditional-expect.ts

+28-1
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)