Skip to content

Commit

Permalink
fix(valid-expect-in-promise): support awaited promises in arguments (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
G-Rath committed Oct 11, 2021
1 parent d90a5dc commit bd2c33c
Show file tree
Hide file tree
Showing 2 changed files with 142 additions and 0 deletions.
100 changes: 100 additions & 0 deletions src/rules/__tests__/valid-expect-in-promise.test.ts
Expand Up @@ -15,6 +15,88 @@ ruleTester.run('valid-expect-in-promise', rule, {
"test('something', () => Promise.resolve().then(() => expect(1).toBe(2)));",
'Promise.resolve().then(() => expect(1).toBe(2))',
'const x = Promise.resolve().then(() => expect(1).toBe(2))',
dedent`
it('is valid', async () => {
const promise = loadNumber().then(number => {
expect(typeof number).toBe('number');
return number + 1;
});
expect(await promise).toBeGreaterThan(1);
});
`,
dedent`
it('is valid', async () => {
const promise = loadNumber().then(number => {
expect(typeof number).toBe('number');
return number + 1;
});
expect(await promise).resolves.toBeGreaterThan(1);
});
`,
dedent`
it('is valid', async () => {
const promise = loadNumber().then(number => {
expect(typeof number).toBe('number');
return number + 1;
});
expect(1).toBeGreaterThan(await promise);
});
`,
dedent`
it('is valid', async () => {
const promise = loadNumber().then(number => {
expect(typeof number).toBe('number');
return number + 1;
});
expect.this.that.is(await promise);
});
`,
dedent`
it('is valid', async () => {
expect(await loadNumber().then(number => {
expect(typeof number).toBe('number');
return number + 1;
})).toBeGreaterThan(1);
});
`,
dedent`
it('is valid', async () => {
const promise = loadNumber().then(number => {
expect(typeof number).toBe('number');
return number + 1;
});
logValue(await promise);
});
`,
dedent`
it('is valid', async () => {
const promise = loadNumber().then(number => {
expect(typeof number).toBe('number');
return 1;
});
expect.assertions(await promise);
});
`,
dedent`
it('is valid', async () => {
await loadNumber().then(number => {
expect(typeof number).toBe('number');
});
});
`,
dedent`
it('it1', () => new Promise((done) => {
test()
Expand Down Expand Up @@ -1369,5 +1451,23 @@ ruleTester.run('valid-expect-in-promise', rule, {
},
],
},
{
code: dedent`
test('that we error on this', () => {
const promise = something().then(value => {
expect(value).toBe('red');
});
log(promise);
});
`,
errors: [
{
messageId: 'expectInFloatingPromise',
line: 2,
column: 9,
},
],
},
],
});
42 changes: 42 additions & 0 deletions src/rules/valid-expect-in-promise.ts
Expand Up @@ -141,6 +141,40 @@ const isPromiseMethodThatUsesValue = (
return isIdentifier(node.argument, name);
};

/**
* Attempts to determine if the runtime value represented by the given `identifier`
* is `await`ed as an argument along the given call expression
*/
const isValueAwaitedInArguments = (
name: string,
call: TSESTree.CallExpression,
): boolean => {
let node: TSESTree.Node = call;

while (node) {
if (node.type === AST_NODE_TYPES.CallExpression) {
for (const argument of node.arguments) {
if (
argument.type === AST_NODE_TYPES.AwaitExpression &&
isIdentifier(argument.argument, name)
) {
return true;
}
}

node = node.callee;
}

if (node.type !== AST_NODE_TYPES.MemberExpression) {
break;
}

node = node.object;
}

return false;
};

/**
* Attempts to determine if the runtime value represented by the given `identifier`
* is `await`ed or `return`ed within the given `body` of statements
Expand All @@ -163,6 +197,14 @@ const isValueAwaitedOrReturned = (
}

if (node.type === AST_NODE_TYPES.ExpressionStatement) {
// it's possible that we're awaiting the value as an argument
if (
node.expression.type === AST_NODE_TYPES.CallExpression &&
isValueAwaitedInArguments(name, node.expression)
) {
return true;
}

if (
node.expression.type === AST_NODE_TYPES.AwaitExpression &&
isPromiseMethodThatUsesValue(node.expression, identifier)
Expand Down

0 comments on commit bd2c33c

Please sign in to comment.