Skip to content

Commit 5ae02ab

Browse files
committed
fix(valid-expect-in-promise): bailout if done callback is present
1 parent d1516eb commit 5ae02ab

File tree

2 files changed

+85
-24
lines changed

2 files changed

+85
-24
lines changed

src/rules/__tests__/valid-expect-in-promise.test.ts

Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,15 @@ const ruleTester = new TSESLint.RuleTester({
1212

1313
ruleTester.run('valid-expect-in-promise', rule, {
1414
valid: [
15-
// todo: done callback
16-
// dedent`
17-
// it('it1', () => new Promise((done) => {
18-
// test()
19-
// .then(() => {
20-
// expect(someThing).toEqual(true);
21-
// done();
22-
// });
23-
// }));
24-
// `,
15+
dedent`
16+
it('it1', () => new Promise((done) => {
17+
test()
18+
.then(() => {
19+
expect(someThing).toEqual(true);
20+
done();
21+
});
22+
}));
23+
`,
2524
dedent`
2625
it('passes', () => {
2726
Promise.resolve().then(() => {
@@ -280,20 +279,30 @@ ruleTester.run('valid-expect-in-promise', rule, {
280279
}))
281280
`,
282281
'it("it1", () => somePromise.then(() => expect(someThing).toEqual(true)))',
283-
// todo: done callback
284-
// dedent`
285-
// it('promise test with done', (done) => {
286-
// const promise = getPromise();
287-
// promise.then(() => expect(someThing).toEqual(true));
288-
// });
289-
// `,
290-
// todo: done callback
291-
// dedent`
292-
// it('name of done param does not matter', (nameDoesNotMatter) => {
293-
// const promise = getPromise();
294-
// promise.then(() => expect(someThing).toEqual(true));
295-
// });
296-
// `,
282+
dedent`
283+
it('promise test with done', (done) => {
284+
const promise = getPromise();
285+
promise.then(() => expect(someThing).toEqual(true));
286+
});
287+
`,
288+
dedent`
289+
it('name of done param does not matter', (nameDoesNotMatter) => {
290+
const promise = getPromise();
291+
promise.then(() => expect(someThing).toEqual(true));
292+
});
293+
`,
294+
dedent`
295+
it.each([])('name of done param does not matter', (nameDoesNotMatter) => {
296+
const promise = getPromise();
297+
promise.then(() => expect(someThing).toEqual(true));
298+
});
299+
`,
300+
dedent`
301+
it.each\`\`('name of done param does not matter', ({}, nameDoesNotMatter) => {
302+
const promise = getPromise();
303+
promise.then(() => expect(someThing).toEqual(true));
304+
});
305+
`,
297306
],
298307
invalid: [
299308
{

src/rules/valid-expect-in-promise.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ import {
77
KnownCallExpression,
88
createRule,
99
getAccessorValue,
10+
getNodeName,
1011
isExpectCall,
12+
isFunction,
1113
isSupportedAccessor,
1214
isTestCaseCall,
1315
} from './utils';
@@ -76,6 +78,41 @@ const findTopOfBodyNode = (
7678
return null;
7779
};
7880

81+
const isTestCaseCallWithCallbackArg = (
82+
node: TSESTree.CallExpression,
83+
): boolean => {
84+
if (!isTestCaseCall(node)) {
85+
return false;
86+
}
87+
88+
const isJestEach = getNodeName(node).endsWith('.each');
89+
90+
if (
91+
isJestEach &&
92+
node.callee.type !== AST_NODE_TYPES.TaggedTemplateExpression
93+
) {
94+
// isJestEach but not a TaggedTemplateExpression, so this must be
95+
// the `jest.each([])()` syntax which this rule doesn't support due
96+
// to its complexity (see jest-community/eslint-plugin-jest#710)
97+
// so we return true to trigger bailout
98+
return true;
99+
}
100+
101+
if (isJestEach || node.arguments.length >= 2) {
102+
const [, callback] = node.arguments;
103+
104+
const callbackArgIndex = Number(isJestEach);
105+
106+
return (
107+
callback &&
108+
isFunction(callback) &&
109+
callback.params.length === 1 + callbackArgIndex
110+
);
111+
}
112+
113+
return false;
114+
};
115+
79116
export default createRule<unknown[], MessageIds>({
80117
name: __filename,
81118
meta: {
@@ -93,11 +130,18 @@ export default createRule<unknown[], MessageIds>({
93130
},
94131
defaultOptions: [],
95132
create(context) {
133+
let inTestCaseWithDoneCallback = false;
96134
let inPromiseChain = false;
97135
let hasExpectCall = false;
98136

99137
return {
100138
CallExpression(node) {
139+
if (isTestCaseCallWithCallbackArg(node)) {
140+
inTestCaseWithDoneCallback = true;
141+
142+
return;
143+
}
144+
101145
if (isThenOrCatchCall(node)) {
102146
inPromiseChain = true;
103147

@@ -115,6 +159,14 @@ export default createRule<unknown[], MessageIds>({
115159
}
116160
},
117161
'CallExpression:exit'(node: TSESTree.CallExpression) {
162+
if (inTestCaseWithDoneCallback) {
163+
if (isTestCaseCall(node)) {
164+
inTestCaseWithDoneCallback = false;
165+
}
166+
167+
return;
168+
}
169+
118170
if (isThenOrCatchCall(node)) {
119171
inPromiseChain = false;
120172

0 commit comments

Comments
 (0)