Skip to content

feat: add support for resolves/rejects matchers in async queries #64

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jan 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/rules/await-async-query.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ const bar = () => {

// return the promise within a function is correct too!
const findMyButton = () => findByText('my button');

// using a resolves/rejects matcher is also correct
expect(findByTestId('alert')).resolves.toBe('Success');
expect(findByTestId('alert')).rejects.toBe('Error');
```

## Further Reading
Expand Down
22 changes: 21 additions & 1 deletion lib/rules/await-async-query.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,11 @@ module.exports = {
const testingLibraryQueryUsage = [];
return {
[`CallExpression > Identifier[name=${ASYNC_QUERIES_REGEXP}]`](node) {
if (!isAwaited(node.parent.parent) && !isPromiseResolved(node)) {
if (
!isAwaited(node.parent.parent) &&
!isPromiseResolved(node) &&
!hasClosestExpectResolvesRejects(node)
) {
testingLibraryQueryUsage.push(node);
}
},
Expand Down Expand Up @@ -101,3 +105,19 @@ function isPromiseResolved(node) {
// promise.then(...)
return hasAThenProperty(parent);
}

function hasClosestExpectResolvesRejects(node) {
if (!node.parent) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this necessary? Because on L34 the function is called with isAwaited(node.parent.parent), which implies that node.parent cannot be undefined.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. I initially wrote a utility function called findClosestExpectCallthat was supposed to be reusable. At that time the node.parent thing was necessary because the function was recursive. Then I refactored it and indeed, it's not necessary.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to remove the node.parent check and test broke which is, in the end, logical. While isAwaited(node.parent.parent) implies node.parent cannot be undefined, it's very possible that hasClosestExpectResolvesRejects goes beyond node.parent.parent (until the Program node) in case you're not using an expect statement.

return;
}

if (node.type === 'CallExpression' && node.callee.name === 'expect') {
const expectMatcher = node.parent.property;
return (
expectMatcher &&
(expectMatcher.name === 'resolves' || expectMatcher.name === 'rejects')
);
} else {
return hasClosestExpectResolvesRejects(node.parent);
}
}
16 changes: 16 additions & 0 deletions tests/lib/rules/await-async-query.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,22 @@ ruleTester.run('await-async-query', rule, {
}
`,
},

// resolves/rejects matchers are valid
...ASYNC_QUERIES_COMBINATIONS.map(query => ({
code: `test(() => {
expect(${query}("foo")).resolves.toBe("bar")
expect(wrappedQuery(${query}("foo"))).resolves.toBe("bar")
})
`,
})),
...ASYNC_QUERIES_COMBINATIONS.map(query => ({
code: `test(() => {
expect(${query}("foo")).rejects.toBe("bar")
expect(wrappedQuery(${query}("foo"))).rejects.toBe("bar")
})
`,
})),
],

invalid:
Expand Down