Skip to content

Commit 01165d9

Browse files
y-hsgwBelco90
andauthored
feat(await-async-queries): add auto-fix (#1077)
Closes #914 Co-authored-by: Mario Beltrán <[email protected]>
1 parent 7ab7356 commit 01165d9

File tree

4 files changed

+259
-84
lines changed

4 files changed

+259
-84
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ module.exports = [
325325
| Name                            | Description | 💼 | ⚠️ | 🔧 |
326326
| :------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------ | :-- |
327327
| [await-async-events](docs/rules/await-async-events.md) | Enforce promises from async event methods are handled | ![badge-angular][] ![badge-dom][] ![badge-marko][] ![badge-react][] ![badge-svelte][] ![badge-vue][] | | 🔧 |
328-
| [await-async-queries](docs/rules/await-async-queries.md) | Enforce promises from async queries to be handled | ![badge-angular][] ![badge-dom][] ![badge-marko][] ![badge-react][] ![badge-svelte][] ![badge-vue][] | | |
328+
| [await-async-queries](docs/rules/await-async-queries.md) | Enforce promises from async queries to be handled | ![badge-angular][] ![badge-dom][] ![badge-marko][] ![badge-react][] ![badge-svelte][] ![badge-vue][] | | 🔧 |
329329
| [await-async-utils](docs/rules/await-async-utils.md) | Enforce promises from async utils to be awaited properly | ![badge-angular][] ![badge-dom][] ![badge-marko][] ![badge-react][] ![badge-svelte][] ![badge-vue][] | | |
330330
| [consistent-data-testid](docs/rules/consistent-data-testid.md) | Ensures consistent usage of `data-testid` | | | |
331331
| [no-await-sync-events](docs/rules/no-await-sync-events.md) | Disallow unnecessary `await` for sync events | ![badge-angular][] ![badge-dom][] ![badge-react][] | | |

docs/rules/await-async-queries.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
💼 This rule is enabled in the following configs: `angular`, `dom`, `marko`, `react`, `svelte`, `vue`.
44

5+
🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).
6+
57
<!-- end auto-generated rule header -->
68

79
Ensure that promises returned by async queries are handled properly.

lib/rules/await-async-queries.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ import { ASTUtils } from '@typescript-eslint/utils';
33
import { createTestingLibraryRule } from '../create-testing-library-rule';
44
import {
55
findClosestCallExpressionNode,
6+
findClosestFunctionExpressionNode,
67
getDeepestIdentifierNode,
78
getFunctionName,
89
getInnermostReturningFunction,
910
getVariableReferences,
11+
isMemberExpression,
1012
isPromiseHandled,
1113
} from '../node-utils';
1214

@@ -37,6 +39,7 @@ export default createTestingLibraryRule<Options, MessageIds>({
3739
asyncQueryWrapper:
3840
'promise returned from `{{ name }}` wrapper over async query must be handled',
3941
},
42+
fixable: 'code',
4043
schema: [],
4144
},
4245
defaultOptions: [],
@@ -85,6 +88,18 @@ export default createTestingLibraryRule<Options, MessageIds>({
8588
node: identifierNode,
8689
messageId: 'awaitAsyncQuery',
8790
data: { name: identifierNode.name },
91+
fix: (fixer) => {
92+
if (
93+
isMemberExpression(identifierNode.parent) &&
94+
ASTUtils.isIdentifier(identifierNode.parent.object)
95+
) {
96+
return fixer.insertTextBefore(
97+
identifierNode.parent,
98+
'await '
99+
);
100+
}
101+
return fixer.insertTextBefore(identifierNode, 'await ');
102+
},
88103
});
89104
return;
90105
}
@@ -102,6 +117,10 @@ export default createTestingLibraryRule<Options, MessageIds>({
102117
node: identifierNode,
103118
messageId: 'awaitAsyncQuery',
104119
data: { name: identifierNode.name },
120+
fix: (fixer) =>
121+
references.map((ref) =>
122+
fixer.insertTextBefore(ref.identifier, 'await ')
123+
),
105124
});
106125
return;
107126
}
@@ -115,6 +134,51 @@ export default createTestingLibraryRule<Options, MessageIds>({
115134
node: identifierNode,
116135
messageId: 'asyncQueryWrapper',
117136
data: { name: identifierNode.name },
137+
fix: (fixer) => {
138+
const functionExpression =
139+
findClosestFunctionExpressionNode(node);
140+
141+
if (!functionExpression) return null;
142+
143+
let IdentifierNodeFixer;
144+
if (isMemberExpression(identifierNode.parent)) {
145+
/**
146+
* If the wrapper is a property of an object,
147+
* add 'await' before the object, e.g.:
148+
* const obj = { wrapper: () => screen.findByText(/foo/i) };
149+
* await obj.wrapper();
150+
*/
151+
IdentifierNodeFixer = fixer.insertTextBefore(
152+
identifierNode.parent,
153+
'await '
154+
);
155+
} else {
156+
/**
157+
* Add 'await' before the wrapper function, e.g.:
158+
* const wrapper = () => screen.findByText(/foo/i);
159+
* await wrapper();
160+
*/
161+
IdentifierNodeFixer = fixer.insertTextBefore(
162+
identifierNode,
163+
'await '
164+
);
165+
}
166+
167+
const ruleFixes = [IdentifierNodeFixer];
168+
if (!functionExpression.async) {
169+
/**
170+
* Mutate the actual node so if other nodes exist in this
171+
* function expression body they don't also try to fix it.
172+
*/
173+
functionExpression.async = true;
174+
175+
ruleFixes.push(
176+
fixer.insertTextBefore(functionExpression, 'async ')
177+
);
178+
}
179+
180+
return ruleFixes;
181+
},
118182
});
119183
}
120184
},

0 commit comments

Comments
 (0)