Skip to content

Commit

Permalink
fix(valid-expect-in-promise): allow expect.resolve & expect.reject (
Browse files Browse the repository at this point in the history
#948)

We purposely don't check if the `expect` is `await`ed or returned, as
that is the role of the `valid-expect` rule.

Fixes #947
  • Loading branch information
G-Rath committed Oct 14, 2021
1 parent f783d9d commit 71b7e17
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 5 deletions.
84 changes: 84 additions & 0 deletions src/rules/__tests__/valid-expect-in-promise.test.ts
Expand Up @@ -15,6 +15,50 @@ 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', () => {
const promise = loadNumber().then(number => {
expect(typeof number).toBe('number');
return number + 1;
});
expect(promise).resolves.toBe(1);
});
`,
dedent`
it('is valid', () => {
const promise = loadNumber().then(number => {
expect(typeof number).toBe('number');
return number + 1;
});
expect(promise).resolves.not.toBe(2);
});
`,
dedent`
it('is valid', () => {
const promise = loadNumber().then(number => {
expect(typeof number).toBe('number');
return number + 1;
});
expect(promise).rejects.toBe(1);
});
`,
dedent`
it('is valid', () => {
const promise = loadNumber().then(number => {
expect(typeof number).toBe('number');
return number + 1;
});
expect(promise).rejects.not.toBe(2);
});
`,
dedent`
it('is valid', async () => {
const promise = loadNumber().then(number => {
Expand Down Expand Up @@ -1469,5 +1513,45 @@ ruleTester.run('valid-expect-in-promise', rule, {
},
],
},
{
code: dedent`
it('is valid', async () => {
const promise = loadNumber().then(number => {
expect(typeof number).toBe('number');
return number + 1;
});
expect(promise).toBeInstanceOf(Promise);
});
`,
errors: [
{
messageId: 'expectInFloatingPromise',
line: 2,
column: 9,
},
],
},
{
code: dedent`
it('is valid', async () => {
const promise = loadNumber().then(number => {
expect(typeof number).toBe('number');
return number + 1;
});
expect(anotherPromise).resolves.toBe(1);
});
`,
errors: [
{
messageId: 'expectInFloatingPromise',
line: 2,
column: 9,
},
],
},
],
});
50 changes: 45 additions & 5 deletions src/rules/valid-expect-in-promise.ts
Expand Up @@ -4,6 +4,7 @@ import {
} from '@typescript-eslint/experimental-utils';
import {
KnownCallExpression,
ModifierName,
createRule,
getAccessorValue,
getNodeName,
Expand All @@ -12,6 +13,7 @@ import {
isIdentifier,
isSupportedAccessor,
isTestCaseCall,
parseExpectCall,
} from './utils';

type PromiseChainCallExpression = KnownCallExpression<
Expand Down Expand Up @@ -175,6 +177,28 @@ const isValueAwaitedInArguments = (
return false;
};

const getLeftMostCallExpression = (
call: TSESTree.CallExpression,
): TSESTree.CallExpression => {
let leftMostCallExpression: TSESTree.CallExpression = call;
let node: TSESTree.Node = call;

while (node) {
if (node.type === AST_NODE_TYPES.CallExpression) {
leftMostCallExpression = node;
node = node.callee;
}

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

node = node.object;
}

return leftMostCallExpression;
};

/**
* 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 @@ -198,11 +222,27 @@ 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.CallExpression) {
if (isValueAwaitedInArguments(name, node.expression)) {
return true;
}

const leftMostCall = getLeftMostCallExpression(node.expression);

if (
isExpectCall(leftMostCall) &&
leftMostCall.arguments.length > 0 &&
isIdentifier(leftMostCall.arguments[0], name)
) {
const { modifier } = parseExpectCall(leftMostCall);

if (
modifier?.name === ModifierName.resolves ||
modifier?.name === ModifierName.rejects
) {
return true;
}
}
}

if (
Expand Down

0 comments on commit 71b7e17

Please sign in to comment.