From 321270d6234b68a8f7dcd6cc169dcc21b7e316bb Mon Sep 17 00:00:00 2001
From: neriyarden <neriyarden@gmail.com>
Date: Sat, 27 Jul 2024 16:30:51 +0300
Subject: [PATCH 1/6] feat(await-async-utils): add autofix for direct usage

---
 lib/rules/await-async-utils.ts            |   8 +
 tests/lib/rules/await-async-utils.test.ts | 186 +++++++++++++++++++++-
 2 files changed, 192 insertions(+), 2 deletions(-)

diff --git a/lib/rules/await-async-utils.ts b/lib/rules/await-async-utils.ts
index a52e96d0..39d0a497 100644
--- a/lib/rules/await-async-utils.ts
+++ b/lib/rules/await-async-utils.ts
@@ -7,6 +7,7 @@ import {
 	getFunctionName,
 	getInnermostReturningFunction,
 	getVariableReferences,
+	isMemberExpression,
 	isObjectPattern,
 	isPromiseHandled,
 	isProperty,
@@ -36,6 +37,7 @@ export default createTestingLibraryRule<Options, MessageIds>({
 				'Promise returned from {{ name }} wrapper over async util must be handled',
 		},
 		schema: [],
+		fixable: 'code',
 	},
 	defaultOptions: [],
 
@@ -149,6 +151,12 @@ export default createTestingLibraryRule<Options, MessageIds>({
 							data: {
 								name: node.name,
 							},
+							fix: (fixer) => {
+								if (isMemberExpression(node.parent)) {
+									return fixer.insertTextBefore(node.parent, 'await ');
+								}
+								return fixer.insertTextBefore(node, 'await ');
+							},
 						});
 					}
 				} else {
diff --git a/tests/lib/rules/await-async-utils.test.ts b/tests/lib/rules/await-async-utils.test.ts
index e91517b3..ddd79c7b 100644
--- a/tests/lib/rules/await-async-utils.test.ts
+++ b/tests/lib/rules/await-async-utils.test.ts
@@ -346,6 +346,13 @@ ruleTester.run(RULE_NAME, rule, {
 							data: { name: asyncUtil },
 						},
 					],
+					output: `
+        import { ${asyncUtil} } from '${testingFramework}';
+        test('${asyncUtil} util not waited is invalid', () => {
+          doSomethingElse();
+          await ${asyncUtil}(() => getByLabelText('email'));
+        });
+      `,
 				} as const)
 		),
 		...ASYNC_UTILS.map(
@@ -366,6 +373,13 @@ ruleTester.run(RULE_NAME, rule, {
 							data: { name: asyncUtil },
 						},
 					],
+					output: `
+        import { ${asyncUtil} } from '${testingFramework}';
+        test('${asyncUtil} util not waited is invalid', () => {
+          doSomethingElse();
+          const el = await ${asyncUtil}(() => getByLabelText('email'));
+        });
+      `,
 				} as const)
 		),
 		...ASYNC_UTILS.map(
@@ -386,6 +400,13 @@ ruleTester.run(RULE_NAME, rule, {
 							data: { name: asyncUtil },
 						},
 					],
+					output: `
+        import * as asyncUtil from '${testingFramework}';
+        test('asyncUtil.${asyncUtil} util not handled is invalid', () => {
+          doSomethingElse();
+          await asyncUtil.${asyncUtil}(() => getByLabelText('email'));
+        });
+      `,
 				} as const)
 		),
 		...ASYNC_UTILS.map(
@@ -406,6 +427,13 @@ ruleTester.run(RULE_NAME, rule, {
 							data: { name: asyncUtil },
 						},
 					],
+					output: `
+        import { ${asyncUtil} } from '${testingFramework}';
+        test('${asyncUtil} util promise saved not handled is invalid', () => {
+          doSomethingElse();
+          const aPromise = await ${asyncUtil}(() => getByLabelText('email'));
+        });
+      `,
 				} as const)
 		),
 		...ASYNC_UTILS.map(
@@ -433,6 +461,14 @@ ruleTester.run(RULE_NAME, rule, {
 							data: { name: asyncUtil },
 						},
 					],
+					output: `
+        import { ${asyncUtil} } from '${testingFramework}';
+        test('several ${asyncUtil} utils not handled are invalid', () => {
+          const aPromise = ${asyncUtil}(() => getByLabelText('username'));
+          doSomethingElse(aPromise);
+          await ${asyncUtil}(() => getByLabelText('email'));
+        });
+      `,
 				} as const)
 		),
 		...ASYNC_UTILS.map(
@@ -460,6 +496,14 @@ ruleTester.run(RULE_NAME, rule, {
 							data: { name: asyncUtil },
 						},
 					],
+					output: `
+        import { ${asyncUtil} } from '${testingFramework}';
+        test('unhandled expression that evaluates to promise is invalid', () => {
+          const aPromise = ${asyncUtil}(() => getByLabelText('username'));
+          doSomethingElse(aPromise);
+          await ${asyncUtil}(() => getByLabelText('email'));
+        });
+      `,
 				} as const)
 		),
 		...ASYNC_UTILS.map(
@@ -485,6 +529,18 @@ ruleTester.run(RULE_NAME, rule, {
 							data: { name: 'waitForSomethingAsync' },
 						},
 					],
+					output: `
+        import { ${asyncUtil}, render } from '${testingFramework}';
+        
+        function waitForSomethingAsync() {
+          return ${asyncUtil}(() => somethingAsync())
+        }
+
+        test('unhandled promise from function wrapping ${asyncUtil} util is invalid', async () => {
+          render()
+          await waitForSomethingAsync()
+        });
+      `,
 				} as const)
 		),
 		...ASYNC_UTILS.map(
@@ -507,6 +563,15 @@ ruleTester.run(RULE_NAME, rule, {
 							data: { name: asyncUtil },
 						},
 					],
+					output: `
+        import { ${asyncUtil} } from 'some-other-library';
+        test(
+        'aggressive reporting - util "${asyncUtil}" which is not related to testing library is invalid',
+        async () => {
+          doSomethingElse();
+          await ${asyncUtil}();
+        });
+      `,
 				} as const)
 		),
 		...ASYNC_UTILS.map(
@@ -532,6 +597,18 @@ ruleTester.run(RULE_NAME, rule, {
 							data: { name: 'waitForSomethingAsync' },
 						},
 					],
+					output: `
+        import { ${asyncUtil}, render } from '${testingFramework}';
+        
+        function waitForSomethingAsync() {
+          return ${asyncUtil}(() => somethingAsync())
+        }
+
+        test('unhandled promise from function wrapping ${asyncUtil} util is invalid', async () => {
+          render()
+          const el = await waitForSomethingAsync()
+        });
+      `,
 				} as const)
 		),
 
@@ -555,6 +632,15 @@ ruleTester.run(RULE_NAME, rule, {
 							data: { name: asyncUtil },
 						},
 					],
+					output: `
+        import * as asyncUtils from 'some-other-library';
+        test(
+        'aggressive reporting - util "asyncUtils.${asyncUtil}" which is not related to testing library is invalid',
+        async () => {
+          doSomethingElse();
+          await asyncUtils.${asyncUtil}();
+        });
+      `,
 				} as const)
 		),
 		...ASYNC_UTILS.map(
@@ -563,11 +649,11 @@ ruleTester.run(RULE_NAME, rule, {
 					code: `
         function setup() {
           const utils = render(<MyComponent />);
-        
+
           const waitForAsyncUtil = () => {
             return ${asyncUtil}(screen.queryByTestId('my-test-id'));
           };
-        
+
           return { waitForAsyncUtil, ...utils };
         }
 
@@ -584,6 +670,22 @@ ruleTester.run(RULE_NAME, rule, {
 							data: { name: 'waitForAsyncUtil' },
 						},
 					],
+					output: `
+        function setup() {
+          const utils = render(<MyComponent />);
+
+          const waitForAsyncUtil = () => {
+            return ${asyncUtil}(screen.queryByTestId('my-test-id'));
+          };
+
+          return { waitForAsyncUtil, ...utils };
+        }
+
+        test('unhandled promise from destructed property of async function wrapper is invalid', () => {
+          const { user, waitForAsyncUtil } = setup();
+          await waitForAsyncUtil();
+        });
+      `,
 				} as const)
 		),
 		...ASYNC_UTILS.map(
@@ -614,6 +716,23 @@ ruleTester.run(RULE_NAME, rule, {
 							data: { name: 'myAlias' },
 						},
 					],
+					output: `
+        function setup() {
+          const utils = render(<MyComponent />);
+        
+          const waitForAsyncUtil = () => {
+            return ${asyncUtil}(screen.queryByTestId('my-test-id'));
+          };
+        
+          return { waitForAsyncUtil, ...utils };
+        }
+
+        test('unhandled promise from destructed property of async function wrapper is invalid', () => {
+          const { user, waitForAsyncUtil } = setup();
+          const myAlias = waitForAsyncUtil;
+          await myAlias();
+        });
+      `,
 				} as const)
 		),
 		...ASYNC_UTILS.map(
@@ -643,6 +762,22 @@ ruleTester.run(RULE_NAME, rule, {
 							data: { name: 'waitForAsyncUtil' },
 						},
 					],
+					output: `
+        function setup() {
+          const utils = render(<MyComponent />);
+        
+          const waitForAsyncUtil = () => {
+            return ${asyncUtil}(screen.queryByTestId('my-test-id'));
+          };
+        
+          return { waitForAsyncUtil, ...utils };
+        }
+
+        test('unhandled promise from destructed property of async function wrapper is invalid', () => {
+          const { ...clone } = setup();
+          await clone.waitForAsyncUtil();
+        });
+      `,
 				} as const)
 		),
 		...ASYNC_UTILS.map(
@@ -672,6 +807,22 @@ ruleTester.run(RULE_NAME, rule, {
 							data: { name: 'myAlias' },
 						},
 					],
+					output: `
+        function setup() {
+          const utils = render(<MyComponent />);
+        
+          const waitForAsyncUtil = () => {
+            return ${asyncUtil}(screen.queryByTestId('my-test-id'));
+          };
+        
+          return { waitForAsyncUtil, ...utils };
+        }
+
+        test('unhandled promise from destructed property of async function wrapper is invalid', () => {
+          const { waitForAsyncUtil: myAlias } = setup();
+          await myAlias();
+        });
+      `,
 				} as const)
 		),
 		...ASYNC_UTILS.map(
@@ -700,6 +851,21 @@ ruleTester.run(RULE_NAME, rule, {
 							data: { name: 'waitForAsyncUtil' },
 						},
 					],
+					output: `
+        function setup() {
+          const utils = render(<MyComponent />);
+        
+          const waitForAsyncUtil = () => {
+            return ${asyncUtil}(screen.queryByTestId('my-test-id'));
+          };
+        
+          return { waitForAsyncUtil, ...utils };
+        }
+
+        test('unhandled promise from destructed property of async function wrapper is invalid', () => {
+          await setup().waitForAsyncUtil();
+        });
+      `,
 				} as const)
 		),
 		...ASYNC_UTILS.map(
@@ -729,6 +895,22 @@ ruleTester.run(RULE_NAME, rule, {
 							data: { name: 'myAlias' },
 						},
 					],
+					output: `
+        function setup() {
+          const utils = render(<MyComponent />);
+        
+          const waitForAsyncUtil = () => {
+            return ${asyncUtil}(screen.queryByTestId('my-test-id'));
+          };
+        
+          return { waitForAsyncUtil, ...utils };
+        }
+
+        test('unhandled promise from destructed property of async function wrapper is invalid', () => {
+          const myAlias = setup().waitForAsyncUtil;
+          await myAlias();
+        });
+      `,
 				} as const)
 		),
 	]),

From fd46b968d5955b107a5c084a79a0722353c61536 Mon Sep 17 00:00:00 2001
From: neriyarden <neriyarden@gmail.com>
Date: Sat, 27 Jul 2024 16:46:20 +0300
Subject: [PATCH 2/6] feat(await-async-utils): add autofix for references

---
 lib/rules/await-async-utils.ts            | 3 +++
 tests/lib/rules/await-async-utils.test.ts | 4 ++--
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/lib/rules/await-async-utils.ts b/lib/rules/await-async-utils.ts
index 39d0a497..84bbe222 100644
--- a/lib/rules/await-async-utils.ts
+++ b/lib/rules/await-async-utils.ts
@@ -169,6 +169,9 @@ export default createTestingLibraryRule<Options, MessageIds>({
 								data: {
 									name: node.name,
 								},
+								fix: (fixer) => {
+									return fixer.insertTextBefore(referenceNode, 'await ');
+								},
 							});
 							return;
 						}
diff --git a/tests/lib/rules/await-async-utils.test.ts b/tests/lib/rules/await-async-utils.test.ts
index ddd79c7b..c389cf70 100644
--- a/tests/lib/rules/await-async-utils.test.ts
+++ b/tests/lib/rules/await-async-utils.test.ts
@@ -465,7 +465,7 @@ ruleTester.run(RULE_NAME, rule, {
         import { ${asyncUtil} } from '${testingFramework}';
         test('several ${asyncUtil} utils not handled are invalid', () => {
           const aPromise = ${asyncUtil}(() => getByLabelText('username'));
-          doSomethingElse(aPromise);
+          doSomethingElse(await aPromise);
           await ${asyncUtil}(() => getByLabelText('email'));
         });
       `,
@@ -500,7 +500,7 @@ ruleTester.run(RULE_NAME, rule, {
         import { ${asyncUtil} } from '${testingFramework}';
         test('unhandled expression that evaluates to promise is invalid', () => {
           const aPromise = ${asyncUtil}(() => getByLabelText('username'));
-          doSomethingElse(aPromise);
+          doSomethingElse(await aPromise);
           await ${asyncUtil}(() => getByLabelText('email'));
         });
       `,

From d841dc6e7735122c89baa16ca949ef65dd37a9a9 Mon Sep 17 00:00:00 2001
From: neriyarden <neriyarden@gmail.com>
Date: Sat, 27 Jul 2024 16:47:15 +0300
Subject: [PATCH 3/6] docs(await-async-utils): generate docs

---
 README.md                       | 2 +-
 docs/rules/await-async-utils.md | 2 ++
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 2199abdd..a6424952 100644
--- a/README.md
+++ b/README.md
@@ -206,7 +206,7 @@ module.exports = {
 | :------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------- | :------------------------------------------------------------------ | :-- |
 | [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-vue][] |                                                                     | 🔧  |
 | [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-vue][] |                                                                     |     |
-| [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-vue][] |                                                                     |     |
+| [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-vue][] |                                                                     | 🔧  |
 | [consistent-data-testid](docs/rules/consistent-data-testid.md)                   | Ensures consistent usage of `data-testid`                                                    |                                                                                    |                                                                     |     |
 | [no-await-sync-events](docs/rules/no-await-sync-events.md)                       | Disallow unnecessary `await` for sync events                                                 | ![badge-angular][] ![badge-dom][] ![badge-react][]                                 |                                                                     |     |
 | [no-await-sync-queries](docs/rules/no-await-sync-queries.md)                     | Disallow unnecessary `await` for sync queries                                                | ![badge-angular][] ![badge-dom][] ![badge-marko][] ![badge-react][] ![badge-vue][] |                                                                     |     |
diff --git a/docs/rules/await-async-utils.md b/docs/rules/await-async-utils.md
index b5433d83..47a6facf 100644
--- a/docs/rules/await-async-utils.md
+++ b/docs/rules/await-async-utils.md
@@ -2,6 +2,8 @@
 
 💼 This rule is enabled in the following configs: `angular`, `dom`, `marko`, `react`, `vue`.
 
+🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).
+
 <!-- end auto-generated rule header -->
 
 Ensure that promises returned by async utils are handled properly.

From 9717acd1a736a24d7dbe4e5b3b0169de912c3244 Mon Sep 17 00:00:00 2001
From: neriyarden <neriyarden@gmail.com>
Date: Sat, 27 Jul 2024 16:30:51 +0300
Subject: [PATCH 4/6] feat(await-async-utils): add autofix for direct usage

---
 lib/rules/await-async-utils.ts            |   8 +
 tests/lib/rules/await-async-utils.test.ts | 182 ++++++++++++++++++++++
 2 files changed, 190 insertions(+)

diff --git a/lib/rules/await-async-utils.ts b/lib/rules/await-async-utils.ts
index a52e96d0..39d0a497 100644
--- a/lib/rules/await-async-utils.ts
+++ b/lib/rules/await-async-utils.ts
@@ -7,6 +7,7 @@ import {
 	getFunctionName,
 	getInnermostReturningFunction,
 	getVariableReferences,
+	isMemberExpression,
 	isObjectPattern,
 	isPromiseHandled,
 	isProperty,
@@ -36,6 +37,7 @@ export default createTestingLibraryRule<Options, MessageIds>({
 				'Promise returned from {{ name }} wrapper over async util must be handled',
 		},
 		schema: [],
+		fixable: 'code',
 	},
 	defaultOptions: [],
 
@@ -149,6 +151,12 @@ export default createTestingLibraryRule<Options, MessageIds>({
 							data: {
 								name: node.name,
 							},
+							fix: (fixer) => {
+								if (isMemberExpression(node.parent)) {
+									return fixer.insertTextBefore(node.parent, 'await ');
+								}
+								return fixer.insertTextBefore(node, 'await ');
+							},
 						});
 					}
 				} else {
diff --git a/tests/lib/rules/await-async-utils.test.ts b/tests/lib/rules/await-async-utils.test.ts
index fc538079..99e4b542 100644
--- a/tests/lib/rules/await-async-utils.test.ts
+++ b/tests/lib/rules/await-async-utils.test.ts
@@ -347,6 +347,13 @@ ruleTester.run(RULE_NAME, rule, {
 							data: { name: asyncUtil },
 						},
 					],
+					output: `
+        import { ${asyncUtil} } from '${testingFramework}';
+        test('${asyncUtil} util not waited is invalid', () => {
+          doSomethingElse();
+          await ${asyncUtil}(() => getByLabelText('email'));
+        });
+      `,
 				}) as const
 		),
 		...ASYNC_UTILS.map(
@@ -367,6 +374,13 @@ ruleTester.run(RULE_NAME, rule, {
 							data: { name: asyncUtil },
 						},
 					],
+					output: `
+        import { ${asyncUtil} } from '${testingFramework}';
+        test('${asyncUtil} util not waited is invalid', () => {
+          doSomethingElse();
+          const el = await ${asyncUtil}(() => getByLabelText('email'));
+        });
+      `,
 				}) as const
 		),
 		...ASYNC_UTILS.map(
@@ -387,6 +401,13 @@ ruleTester.run(RULE_NAME, rule, {
 							data: { name: asyncUtil },
 						},
 					],
+					output: `
+        import * as asyncUtil from '${testingFramework}';
+        test('asyncUtil.${asyncUtil} util not handled is invalid', () => {
+          doSomethingElse();
+          await asyncUtil.${asyncUtil}(() => getByLabelText('email'));
+        });
+      `,
 				}) as const
 		),
 		...ASYNC_UTILS.map(
@@ -407,6 +428,13 @@ ruleTester.run(RULE_NAME, rule, {
 							data: { name: asyncUtil },
 						},
 					],
+					output: `
+        import { ${asyncUtil} } from '${testingFramework}';
+        test('${asyncUtil} util promise saved not handled is invalid', () => {
+          doSomethingElse();
+          const aPromise = await ${asyncUtil}(() => getByLabelText('email'));
+        });
+      `,
 				}) as const
 		),
 		...ASYNC_UTILS.map(
@@ -434,6 +462,14 @@ ruleTester.run(RULE_NAME, rule, {
 							data: { name: asyncUtil },
 						},
 					],
+					output: `
+        import { ${asyncUtil} } from '${testingFramework}';
+        test('several ${asyncUtil} utils not handled are invalid', () => {
+          const aPromise = ${asyncUtil}(() => getByLabelText('username'));
+          doSomethingElse(aPromise);
+          await ${asyncUtil}(() => getByLabelText('email'));
+        });
+      `,
 				}) as const
 		),
 		...ASYNC_UTILS.map(
@@ -461,6 +497,14 @@ ruleTester.run(RULE_NAME, rule, {
 							data: { name: asyncUtil },
 						},
 					],
+					output: `
+        import { ${asyncUtil} } from '${testingFramework}';
+        test('unhandled expression that evaluates to promise is invalid', () => {
+          const aPromise = ${asyncUtil}(() => getByLabelText('username'));
+          doSomethingElse(aPromise);
+          await ${asyncUtil}(() => getByLabelText('email'));
+        });
+      `,
 				}) as const
 		),
 		...ASYNC_UTILS.map(
@@ -486,6 +530,18 @@ ruleTester.run(RULE_NAME, rule, {
 							data: { name: 'waitForSomethingAsync' },
 						},
 					],
+					output: `
+        import { ${asyncUtil}, render } from '${testingFramework}';
+
+        function waitForSomethingAsync() {
+          return ${asyncUtil}(() => somethingAsync())
+        }
+
+        test('unhandled promise from function wrapping ${asyncUtil} util is invalid', async () => {
+          render()
+          await waitForSomethingAsync()
+        });
+      `,
 				}) as const
 		),
 		...ASYNC_UTILS.map(
@@ -508,6 +564,15 @@ ruleTester.run(RULE_NAME, rule, {
 							data: { name: asyncUtil },
 						},
 					],
+					output: `
+        import { ${asyncUtil} } from 'some-other-library';
+        test(
+        'aggressive reporting - util "${asyncUtil}" which is not related to testing library is invalid',
+        async () => {
+          doSomethingElse();
+          await ${asyncUtil}();
+        });
+      `,
 				}) as const
 		),
 		...ASYNC_UTILS.map(
@@ -533,6 +598,18 @@ ruleTester.run(RULE_NAME, rule, {
 							data: { name: 'waitForSomethingAsync' },
 						},
 					],
+					output: `
+        import { ${asyncUtil}, render } from '${testingFramework}';
+
+        function waitForSomethingAsync() {
+          return ${asyncUtil}(() => somethingAsync())
+        }
+
+        test('unhandled promise from function wrapping ${asyncUtil} util is invalid', async () => {
+          render()
+          const el = await waitForSomethingAsync()
+        });
+      `,
 				}) as const
 		),
 
@@ -556,6 +633,15 @@ ruleTester.run(RULE_NAME, rule, {
 							data: { name: asyncUtil },
 						},
 					],
+					output: `
+        import * as asyncUtils from 'some-other-library';
+        test(
+        'aggressive reporting - util "asyncUtils.${asyncUtil}" which is not related to testing library is invalid',
+        async () => {
+          doSomethingElse();
+          await asyncUtils.${asyncUtil}();
+        });
+      `,
 				}) as const
 		),
 		...ASYNC_UTILS.map(
@@ -586,6 +672,22 @@ ruleTester.run(RULE_NAME, rule, {
 							data: { name: 'waitForAsyncUtil' },
 						},
 					],
+					output: `
+        function setup() {
+          const utils = render(<MyComponent />);
+
+          const waitForAsyncUtil = () => {
+            return ${asyncUtil}(screen.queryByTestId('my-test-id'));
+          };
+
+          return { waitForAsyncUtil, ...utils };
+        }
+
+        test('unhandled promise from destructed property of async function wrapper is invalid', () => {
+          const { user, waitForAsyncUtil } = setup();
+          await waitForAsyncUtil();
+        });
+      `,
 				}) as const
 		),
 		...ASYNC_UTILS.map(
@@ -617,6 +719,23 @@ ruleTester.run(RULE_NAME, rule, {
 							data: { name: 'myAlias' },
 						},
 					],
+					output: `
+        function setup() {
+          const utils = render(<MyComponent />);
+
+          const waitForAsyncUtil = () => {
+            return ${asyncUtil}(screen.queryByTestId('my-test-id'));
+          };
+
+          return { waitForAsyncUtil, ...utils };
+        }
+
+        test('unhandled promise from destructed property of async function wrapper is invalid', () => {
+          const { user, waitForAsyncUtil } = setup();
+          const myAlias = waitForAsyncUtil;
+          await myAlias();
+        });
+      `,
 				}) as const
 		),
 		...ASYNC_UTILS.map(
@@ -647,6 +766,22 @@ ruleTester.run(RULE_NAME, rule, {
 							data: { name: 'waitForAsyncUtil' },
 						},
 					],
+					output: `
+        function setup() {
+          const utils = render(<MyComponent />);
+
+          const waitForAsyncUtil = () => {
+            return ${asyncUtil}(screen.queryByTestId('my-test-id'));
+          };
+
+          return { waitForAsyncUtil, ...utils };
+        }
+
+        test('unhandled promise from destructed property of async function wrapper is invalid', () => {
+          const { ...clone } = setup();
+          await clone.waitForAsyncUtil();
+        });
+      `,
 				}) as const
 		),
 		...ASYNC_UTILS.map(
@@ -677,6 +812,22 @@ ruleTester.run(RULE_NAME, rule, {
 							data: { name: 'myAlias' },
 						},
 					],
+					output: `
+        function setup() {
+          const utils = render(<MyComponent />);
+
+          const waitForAsyncUtil = () => {
+            return ${asyncUtil}(screen.queryByTestId('my-test-id'));
+          };
+
+          return { waitForAsyncUtil, ...utils };
+        }
+
+        test('unhandled promise from destructed property of async function wrapper is invalid', () => {
+          const { waitForAsyncUtil: myAlias } = setup();
+          await myAlias();
+        });
+      `,
 				}) as const
 		),
 		...ASYNC_UTILS.map(
@@ -706,6 +857,21 @@ ruleTester.run(RULE_NAME, rule, {
 							data: { name: 'waitForAsyncUtil' },
 						},
 					],
+					output: `
+        function setup() {
+          const utils = render(<MyComponent />);
+
+          const waitForAsyncUtil = () => {
+            return ${asyncUtil}(screen.queryByTestId('my-test-id'));
+          };
+
+          return { waitForAsyncUtil, ...utils };
+        }
+
+        test('unhandled promise from destructed property of async function wrapper is invalid', () => {
+          await setup().waitForAsyncUtil();
+        });
+      `,
 				}) as const
 		),
 		...ASYNC_UTILS.map(
@@ -736,6 +902,22 @@ ruleTester.run(RULE_NAME, rule, {
 							data: { name: 'myAlias' },
 						},
 					],
+					output: `
+        function setup() {
+          const utils = render(<MyComponent />);
+
+          const waitForAsyncUtil = () => {
+            return ${asyncUtil}(screen.queryByTestId('my-test-id'));
+          };
+
+          return { waitForAsyncUtil, ...utils };
+        }
+
+        test('unhandled promise from destructed property of async function wrapper is invalid', () => {
+          const myAlias = setup().waitForAsyncUtil;
+          await myAlias();
+        });
+      `,
 				}) as const
 		),
 	]),

From 224d165641da531e77d651e8d450c0cfb3c89af4 Mon Sep 17 00:00:00 2001
From: neriyarden <neriyarden@gmail.com>
Date: Sat, 27 Jul 2024 16:46:20 +0300
Subject: [PATCH 5/6] feat(await-async-utils): add autofix for references

---
 lib/rules/await-async-utils.ts            | 3 +++
 tests/lib/rules/await-async-utils.test.ts | 4 ++--
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/lib/rules/await-async-utils.ts b/lib/rules/await-async-utils.ts
index 39d0a497..84bbe222 100644
--- a/lib/rules/await-async-utils.ts
+++ b/lib/rules/await-async-utils.ts
@@ -169,6 +169,9 @@ export default createTestingLibraryRule<Options, MessageIds>({
 								data: {
 									name: node.name,
 								},
+								fix: (fixer) => {
+									return fixer.insertTextBefore(referenceNode, 'await ');
+								},
 							});
 							return;
 						}
diff --git a/tests/lib/rules/await-async-utils.test.ts b/tests/lib/rules/await-async-utils.test.ts
index 99e4b542..7f4f8768 100644
--- a/tests/lib/rules/await-async-utils.test.ts
+++ b/tests/lib/rules/await-async-utils.test.ts
@@ -466,7 +466,7 @@ ruleTester.run(RULE_NAME, rule, {
         import { ${asyncUtil} } from '${testingFramework}';
         test('several ${asyncUtil} utils not handled are invalid', () => {
           const aPromise = ${asyncUtil}(() => getByLabelText('username'));
-          doSomethingElse(aPromise);
+          doSomethingElse(await aPromise);
           await ${asyncUtil}(() => getByLabelText('email'));
         });
       `,
@@ -501,7 +501,7 @@ ruleTester.run(RULE_NAME, rule, {
         import { ${asyncUtil} } from '${testingFramework}';
         test('unhandled expression that evaluates to promise is invalid', () => {
           const aPromise = ${asyncUtil}(() => getByLabelText('username'));
-          doSomethingElse(aPromise);
+          doSomethingElse(await aPromise);
           await ${asyncUtil}(() => getByLabelText('email'));
         });
       `,

From a48bcc23f0d1fd0c3aa1fc2094b7d4e321c8dcfb Mon Sep 17 00:00:00 2001
From: neriyarden <neriyarden@gmail.com>
Date: Sat, 27 Jul 2024 16:47:15 +0300
Subject: [PATCH 6/6] docs(await-async-utils): generate docs

---
 README.md                       | 2 +-
 docs/rules/await-async-utils.md | 2 ++
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 0fa46197..3b55524f 100644
--- a/README.md
+++ b/README.md
@@ -296,7 +296,7 @@ module.exports = [
 | :------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------- | :------------------------------------------------------------------ | :-- |
 | [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-vue][] |                                                                     | 🔧  |
 | [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-vue][] |                                                                     |     |
-| [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-vue][] |                                                                     |     |
+| [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-vue][] |                                                                     | 🔧  |
 | [consistent-data-testid](docs/rules/consistent-data-testid.md)                   | Ensures consistent usage of `data-testid`                                                    |                                                                                    |                                                                     |     |
 | [no-await-sync-events](docs/rules/no-await-sync-events.md)                       | Disallow unnecessary `await` for sync events                                                 | ![badge-angular][] ![badge-dom][] ![badge-react][]                                 |                                                                     |     |
 | [no-await-sync-queries](docs/rules/no-await-sync-queries.md)                     | Disallow unnecessary `await` for sync queries                                                | ![badge-angular][] ![badge-dom][] ![badge-marko][] ![badge-react][] ![badge-vue][] |                                                                     |     |
diff --git a/docs/rules/await-async-utils.md b/docs/rules/await-async-utils.md
index b5433d83..47a6facf 100644
--- a/docs/rules/await-async-utils.md
+++ b/docs/rules/await-async-utils.md
@@ -2,6 +2,8 @@
 
 💼 This rule is enabled in the following configs: `angular`, `dom`, `marko`, `react`, `vue`.
 
+🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).
+
 <!-- end auto-generated rule header -->
 
 Ensure that promises returned by async utils are handled properly.