Skip to content

Commit b4ce9bb

Browse files
skovyMichaelDeBoey
authored andcommitted
feat(await-fire-event): rename to await-async-event + add support for user-event (#652)
BREAKING CHANGE: `await-fire-event` is now called `await-async-event`
1 parent c76a7bf commit b4ce9bb

15 files changed

+1082
-540
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -206,9 +206,9 @@ To enable this configuration use the `extends` property in your
206206

207207
| Name | Description | 🔧 | Included in configurations |
208208
| ------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------- | --- | ---------------------------------------------------------------------------------- |
209+
| [`await-async-event`](./docs/rules/await-async-event.md) | Enforce promises from async event methods are handled | | ![dom-badge][] ![angular-badge][] ![react-badge][] ![vue-badge][] ![marko-badge][] |
209210
| [`await-async-query`](./docs/rules/await-async-query.md) | Enforce promises from async queries to be handled | | ![dom-badge][] ![angular-badge][] ![react-badge][] ![vue-badge][] ![marko-badge][] |
210211
| [`await-async-utils`](./docs/rules/await-async-utils.md) | Enforce promises from async utils to be awaited properly | | ![dom-badge][] ![angular-badge][] ![react-badge][] ![vue-badge][] ![marko-badge][] |
211-
| [`await-fire-event`](./docs/rules/await-fire-event.md) | Enforce promises from `fireEvent` methods to be handled | | ![vue-badge][] ![marko-badge][] |
212212
| [`consistent-data-testid`](./docs/rules/consistent-data-testid.md) | Ensures consistent usage of `data-testid` | | |
213213
| [`no-await-sync-events`](./docs/rules/no-await-sync-events.md) | Disallow unnecessary `await` for sync events | | |
214214
| [`no-await-sync-query`](./docs/rules/no-await-sync-query.md) | Disallow unnecessary `await` for sync queries | | ![dom-badge][] ![angular-badge][] ![react-badge][] ![vue-badge][] ![marko-badge][] |

docs/rules/await-async-event.md

+143
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
# Enforce promises from async event methods are handled (`testing-library/await-async-event`)
2+
3+
Ensure that promises returned by `userEvent` (v14+) async methods or `fireEvent` (only Vue and Marko) async methods are handled properly.
4+
5+
## Rule Details
6+
7+
This rule aims to prevent users from forgetting to handle promise returned from async event
8+
methods.
9+
10+
> ⚠️ `fireEvent` methods are async only on following Testing Library packages:
11+
>
12+
> - `@testing-library/vue` (supported by this plugin)
13+
> - `@testing-library/svelte` (not supported yet by this plugin)
14+
> - `@marko/testing-library` (supported by this plugin)
15+
16+
Examples of **incorrect** code for this rule:
17+
18+
```js
19+
fireEvent.click(getByText('Click me'));
20+
21+
fireEvent.focus(getByLabelText('username'));
22+
fireEvent.blur(getByLabelText('username'));
23+
24+
// wrap a fireEvent method within a function...
25+
function triggerEvent() {
26+
return fireEvent.click(button);
27+
}
28+
triggerEvent(); // ...but not handling promise from it is incorrect too
29+
```
30+
31+
```js
32+
userEvent.click(getByText('Click me'));
33+
userEvent.tripleClick(getByText('Click me'));
34+
userEvent.keyboard('foo');
35+
36+
// wrap a userEvent method within a function...
37+
function triggerEvent() {
38+
return userEvent.click(button);
39+
}
40+
triggerEvent(); // ...but not handling promise from it is incorrect too
41+
```
42+
43+
Examples of **correct** code for this rule:
44+
45+
```js
46+
// `await` operator is correct
47+
await fireEvent.focus(getByLabelText('username'));
48+
await fireEvent.blur(getByLabelText('username'));
49+
50+
// `then` method is correct
51+
fireEvent.click(getByText('Click me')).then(() => {
52+
// ...
53+
});
54+
55+
// return the promise within a function is correct too!
56+
const clickMeArrowFn = () => fireEvent.click(getByText('Click me'));
57+
58+
// wrap a fireEvent method within a function...
59+
function triggerEvent() {
60+
return fireEvent.click(button);
61+
}
62+
await triggerEvent(); // ...and handling promise from it is correct also
63+
64+
// using `Promise.all` or `Promise.allSettled` with an array of promises is valid
65+
await Promise.all([
66+
fireEvent.focus(getByLabelText('username')),
67+
fireEvent.blur(getByLabelText('username')),
68+
]);
69+
```
70+
71+
```js
72+
// `await` operator is correct
73+
await userEvent.click(getByText('Click me'));
74+
await userEvent.tripleClick(getByText('Click me'));
75+
76+
// `then` method is correct
77+
userEvent.keyboard('foo').then(() => {
78+
// ...
79+
});
80+
81+
// return the promise within a function is correct too!
82+
const clickMeArrowFn = () => userEvent.click(getByText('Click me'));
83+
84+
// wrap a userEvent method within a function...
85+
function triggerEvent() {
86+
return userEvent.click(button);
87+
}
88+
await triggerEvent(); // ...and handling promise from it is correct also
89+
90+
// using `Promise.all` or `Promise.allSettled` with an array of promises is valid
91+
await Promise.all([
92+
userEvent.click(getByText('Click me'));
93+
userEvent.tripleClick(getByText('Click me'));
94+
]);
95+
```
96+
97+
## Options
98+
99+
- `eventModule`: `string` or `string[]`. Which event module should be linted for async event methods. Defaults to `userEvent` which should be used after v14. `fireEvent` should only be used with frameworks that have async fire event methods.
100+
101+
## Example
102+
103+
```json
104+
{
105+
"testing-library/await-async-event": [
106+
2,
107+
{
108+
"eventModule": "userEvent"
109+
}
110+
]
111+
}
112+
```
113+
114+
```json
115+
{
116+
"testing-library/await-async-event": [
117+
2,
118+
{
119+
"eventModule": "fireEvent"
120+
}
121+
]
122+
}
123+
```
124+
125+
```json
126+
{
127+
"testing-library/await-async-event": [
128+
2,
129+
{
130+
"eventModule": ["fireEvent", "userEvent"]
131+
}
132+
]
133+
}
134+
```
135+
136+
## When Not To Use It
137+
138+
- `userEvent` is below v14, before all event methods are async
139+
- `fireEvent` methods are sync for most Testing Library packages. If you are not using Testing Library package with async events, you shouldn't use this rule.
140+
141+
## Further Reading
142+
143+
- [Vue Testing Library fireEvent](https://testing-library.com/docs/vue-testing-library/api#fireevent)

docs/rules/await-fire-event.md

-66
This file was deleted.

docs/rules/no-await-sync-events.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -105,5 +105,4 @@ Example:
105105
## Notes
106106

107107
- Since `user-event` v14 all its methods are async, so you should disable reporting them by setting the `eventModules` to just `"fire-event"` so `user-event` methods are not reported.
108-
- There is another rule `await-fire-event`, which is only in Vue Testing
109-
Library. Please do not confuse with this rule.
108+
- There is another rule `await-async-event`, which is for awaiting async events for `user-event` v14 or `fire-event` only in Vue Testing Library. Please do not confuse with this rule.

lib/configs/angular.ts

+4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
export = {
66
plugins: ['testing-library'],
77
rules: {
8+
'testing-library/await-async-event': [
9+
'error',
10+
{ eventModule: 'userEvent' },
11+
],
812
'testing-library/await-async-query': 'error',
913
'testing-library/await-async-utils': 'error',
1014
'testing-library/no-await-sync-query': 'error',

lib/configs/dom.ts

+4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
export = {
66
plugins: ['testing-library'],
77
rules: {
8+
'testing-library/await-async-event': [
9+
'error',
10+
{ eventModule: 'userEvent' },
11+
],
812
'testing-library/await-async-query': 'error',
913
'testing-library/await-async-utils': 'error',
1014
'testing-library/no-await-sync-query': 'error',

lib/configs/marko.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@
55
export = {
66
plugins: ['testing-library'],
77
rules: {
8+
'testing-library/await-async-event': [
9+
'error',
10+
{ eventModule: ['fireEvent', 'userEvent'] },
11+
],
812
'testing-library/await-async-query': 'error',
913
'testing-library/await-async-utils': 'error',
10-
'testing-library/await-fire-event': 'error',
1114
'testing-library/no-await-sync-query': 'error',
1215
'testing-library/no-container': 'error',
1316
'testing-library/no-debugging-utils': 'error',

lib/configs/react.ts

+4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
export = {
66
plugins: ['testing-library'],
77
rules: {
8+
'testing-library/await-async-event': [
9+
'error',
10+
{ eventModule: 'userEvent' },
11+
],
812
'testing-library/await-async-query': 'error',
913
'testing-library/await-async-utils': 'error',
1014
'testing-library/no-await-sync-query': 'error',

lib/configs/vue.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@
55
export = {
66
plugins: ['testing-library'],
77
rules: {
8+
'testing-library/await-async-event': [
9+
'error',
10+
{ eventModule: ['fireEvent', 'userEvent'] },
11+
],
812
'testing-library/await-async-query': 'error',
913
'testing-library/await-async-utils': 'error',
10-
'testing-library/await-fire-event': 'error',
1114
'testing-library/no-await-sync-query': 'error',
1215
'testing-library/no-container': 'error',
1316
'testing-library/no-debugging-utils': 'error',

lib/node-utils/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ export function isPromiseHandled(nodeIdentifier: TSESTree.Identifier): boolean {
234234
}
235235

236236
export function getVariableReferences(
237-
context: TSESLint.RuleContext<string, []>,
237+
context: TSESLint.RuleContext<string, unknown[]>,
238238
node: TSESTree.Node
239239
): TSESLint.Scope.Reference[] {
240240
if (ASTUtils.isVariableDeclarator(node)) {

0 commit comments

Comments
 (0)