Skip to content

Commit bd2c33c

Browse files
authored
fix(valid-expect-in-promise): support awaited promises in arguments (#936)
1 parent d90a5dc commit bd2c33c

File tree

2 files changed

+142
-0
lines changed

2 files changed

+142
-0
lines changed

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

+100
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,88 @@ ruleTester.run('valid-expect-in-promise', rule, {
1515
"test('something', () => Promise.resolve().then(() => expect(1).toBe(2)));",
1616
'Promise.resolve().then(() => expect(1).toBe(2))',
1717
'const x = Promise.resolve().then(() => expect(1).toBe(2))',
18+
dedent`
19+
it('is valid', async () => {
20+
const promise = loadNumber().then(number => {
21+
expect(typeof number).toBe('number');
22+
23+
return number + 1;
24+
});
25+
26+
expect(await promise).toBeGreaterThan(1);
27+
});
28+
`,
29+
dedent`
30+
it('is valid', async () => {
31+
const promise = loadNumber().then(number => {
32+
expect(typeof number).toBe('number');
33+
34+
return number + 1;
35+
});
36+
37+
expect(await promise).resolves.toBeGreaterThan(1);
38+
});
39+
`,
40+
dedent`
41+
it('is valid', async () => {
42+
const promise = loadNumber().then(number => {
43+
expect(typeof number).toBe('number');
44+
45+
return number + 1;
46+
});
47+
48+
expect(1).toBeGreaterThan(await promise);
49+
});
50+
`,
51+
dedent`
52+
it('is valid', async () => {
53+
const promise = loadNumber().then(number => {
54+
expect(typeof number).toBe('number');
55+
56+
return number + 1;
57+
});
58+
59+
expect.this.that.is(await promise);
60+
});
61+
`,
62+
dedent`
63+
it('is valid', async () => {
64+
expect(await loadNumber().then(number => {
65+
expect(typeof number).toBe('number');
66+
67+
return number + 1;
68+
})).toBeGreaterThan(1);
69+
});
70+
`,
71+
dedent`
72+
it('is valid', async () => {
73+
const promise = loadNumber().then(number => {
74+
expect(typeof number).toBe('number');
75+
76+
return number + 1;
77+
});
78+
79+
logValue(await promise);
80+
});
81+
`,
82+
dedent`
83+
it('is valid', async () => {
84+
const promise = loadNumber().then(number => {
85+
expect(typeof number).toBe('number');
86+
87+
return 1;
88+
});
89+
90+
expect.assertions(await promise);
91+
});
92+
`,
93+
dedent`
94+
it('is valid', async () => {
95+
await loadNumber().then(number => {
96+
expect(typeof number).toBe('number');
97+
});
98+
});
99+
`,
18100
dedent`
19101
it('it1', () => new Promise((done) => {
20102
test()
@@ -1369,5 +1451,23 @@ ruleTester.run('valid-expect-in-promise', rule, {
13691451
},
13701452
],
13711453
},
1454+
{
1455+
code: dedent`
1456+
test('that we error on this', () => {
1457+
const promise = something().then(value => {
1458+
expect(value).toBe('red');
1459+
});
1460+
1461+
log(promise);
1462+
});
1463+
`,
1464+
errors: [
1465+
{
1466+
messageId: 'expectInFloatingPromise',
1467+
line: 2,
1468+
column: 9,
1469+
},
1470+
],
1471+
},
13721472
],
13731473
});

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

+42
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,40 @@ const isPromiseMethodThatUsesValue = (
141141
return isIdentifier(node.argument, name);
142142
};
143143

144+
/**
145+
* Attempts to determine if the runtime value represented by the given `identifier`
146+
* is `await`ed as an argument along the given call expression
147+
*/
148+
const isValueAwaitedInArguments = (
149+
name: string,
150+
call: TSESTree.CallExpression,
151+
): boolean => {
152+
let node: TSESTree.Node = call;
153+
154+
while (node) {
155+
if (node.type === AST_NODE_TYPES.CallExpression) {
156+
for (const argument of node.arguments) {
157+
if (
158+
argument.type === AST_NODE_TYPES.AwaitExpression &&
159+
isIdentifier(argument.argument, name)
160+
) {
161+
return true;
162+
}
163+
}
164+
165+
node = node.callee;
166+
}
167+
168+
if (node.type !== AST_NODE_TYPES.MemberExpression) {
169+
break;
170+
}
171+
172+
node = node.object;
173+
}
174+
175+
return false;
176+
};
177+
144178
/**
145179
* Attempts to determine if the runtime value represented by the given `identifier`
146180
* is `await`ed or `return`ed within the given `body` of statements
@@ -163,6 +197,14 @@ const isValueAwaitedOrReturned = (
163197
}
164198

165199
if (node.type === AST_NODE_TYPES.ExpressionStatement) {
200+
// it's possible that we're awaiting the value as an argument
201+
if (
202+
node.expression.type === AST_NODE_TYPES.CallExpression &&
203+
isValueAwaitedInArguments(name, node.expression)
204+
) {
205+
return true;
206+
}
207+
166208
if (
167209
node.expression.type === AST_NODE_TYPES.AwaitExpression &&
168210
isPromiseMethodThatUsesValue(node.expression, identifier)

0 commit comments

Comments
 (0)