Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
1 change: 1 addition & 0 deletions docs/rules/await-async-queries.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ problems in the tests. The promise will be considered as handled when:
- chaining the `then` method
- chaining `resolves` or `rejects` from jest
- chaining `toResolve()` or `toReject()` from [jest-extended](https://github.com/jest-community/jest-extended#promise)
- chaining jasmine [async matchers](https://jasmine.github.io/api/edge/async-matchers.html)
- it's returned from a function (in this case, that particular function will be analyzed by this rule too)

Examples of **incorrect** code for this rule:
Expand Down
1 change: 1 addition & 0 deletions docs/rules/await-async-utils.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ problems in the tests. The promise will be considered as handled when:
- chaining the `then` method
- chaining `resolves` or `rejects` from jest
- chaining `toResolve()` or `toReject()` from [jest-extended](https://github.com/jest-community/jest-extended#promise)
- chaining jasmine [async matchers](https://jasmine.github.io/api/edge/async-matchers.html)
- it's returned from a function (in this case, that particular function will be analyzed by this rule too)

Examples of **incorrect** code for this rule:
Expand Down
22 changes: 14 additions & 8 deletions lib/node-utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ export function isPromiseHandled(nodeIdentifier: TSESTree.Identifier): boolean {
isReturnStatement(node.parent)
)
return true;
if (hasClosestExpectResolvesRejects(node.parent)) return true;
if (hasClosestExpectHandlesPromise(node.parent)) return true;
if (hasChainedThen(node)) return true;
if (isPromisesArrayResolved(node)) return true;
});
Expand Down Expand Up @@ -525,21 +525,27 @@ const matcherNamesHandlePromise = [
'rejects',
'toResolve',
'toReject',
'toBeRejected',
'toBeRejectedWith',
'toBeRejectedWithError',
'toBePending',
'toBeResolved',
'toBeResolvedTo',
];

/**
* Determines whether a node belongs to an async assertion
* fulfilled by `resolves` or `rejects` properties or
* by `toResolve` or `toReject` jest-extended matchers
*
* Determines whether a node belongs to an async assertion that is fulfilled by:
* - `resolves` or `rejects` properties
* - `toResolve` or `toReject` jest-extended matchers
* - jasmine async matchers
*/
export function hasClosestExpectResolvesRejects(node: TSESTree.Node): boolean {
export function hasClosestExpectHandlesPromise(node: TSESTree.Node): boolean {
if (
isCallExpression(node) &&
ASTUtils.isIdentifier(node.callee) &&
node.parent &&
isMemberExpression(node.parent) &&
node.callee.name === 'expect'
['expect', 'expectAsync'].includes(node.callee.name)
) {
const expectMatcher = node.parent.property;
return (
Expand All @@ -552,7 +558,7 @@ export function hasClosestExpectResolvesRejects(node: TSESTree.Node): boolean {
return false;
}

return hasClosestExpectResolvesRejects(node.parent);
return hasClosestExpectHandlesPromise(node.parent);
}

/**
Expand Down
18 changes: 18 additions & 0 deletions tests/lib/rules/await-async-queries.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,24 @@ ruleTester.run(RULE_NAME, rule, {
`
),

// jasmine async matchers are valid
...createTestCase(
(query) => `
expectAsync(${query}("foo")).toBeRejected()
expectAsync(wrappedQuery(${query}("foo"))).toBeRejected()
expectAsync(${query}("foo")).toBeRejectedWith("bar")
expectAsync(wrappedQuery(${query}("foo"))).toBeRejectedWith("bar")
expectAsync(${query}("foo")).toBeRejectedWithError("bar")
expectAsync(wrappedQuery(${query}("foo"))).toBeRejectedWithError("bar")
expectAsync(${query}("foo")).toBePending()
expectAsync(wrappedQuery(${query}("foo"))).toBePending()
expectAsync(${query}("foo")).toBeResolved()
expectAsync(wrappedQuery(${query}("foo"))).toBeResolved()
expectAsync(${query}("foo")).toBeResolvedTo("bar")
expectAsync(wrappedQuery(${query}("foo"))).toBeResolvedTo("bar")
`
),

// unresolved async queries with aggressive reporting opted-out are valid
...ALL_ASYNC_COMBINATIONS_TO_TEST.map((query) => ({
settings: { 'testing-library/utils-module': 'test-utils' },
Expand Down
14 changes: 14 additions & 0 deletions tests/lib/rules/await-async-utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,20 @@ ruleTester.run(RULE_NAME, rule, {
doSomethingElse();
expect(${asyncUtil}(() => getByLabelText('email'))).toResolve();
});
`,
})),
...ASYNC_UTILS.map((asyncUtil) => ({
code: `
import { ${asyncUtil} } from '${testingFramework}';
test('${asyncUtil} util expect chained with jasmine async matchers are valid', () => {
doSomethingElse();
expectAsync(${asyncUtil}(() => getByLabelText('email'))).toBeRejected();
expectAsync(${asyncUtil}(() => getByLabelText('email'))).toBeRejectedWith("bar");
expectAsync(${asyncUtil}(() => getByLabelText('email'))).toBeRejectedWithError("bar");
expectAsync(${asyncUtil}(() => getByLabelText('email'))).toBeResolved();
expectAsync(${asyncUtil}(() => getByLabelText('email'))).toBeResolvedTo("bar");
expectAsync(${asyncUtil}(() => getByLabelText('email'))).toBePending();
});
`,
})),
...ASYNC_UTILS.map((asyncUtil) => ({
Expand Down