Skip to content

Commit

Permalink
feat(await-fire-event): rename to await-async-event + add support f…
Browse files Browse the repository at this point in the history
…or `user-event` (#652)

BREAKING CHANGE: `await-fire-event` is now called `await-async-event`
  • Loading branch information
skovy authored and MichaelDeBoey committed Oct 4, 2022
1 parent c76a7bf commit b4ce9bb
Show file tree
Hide file tree
Showing 15 changed files with 1,082 additions and 540 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,9 +206,9 @@ To enable this configuration use the `extends` property in your

| Name | Description | 🔧 | Included in configurations |
| ------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------- | --- | ---------------------------------------------------------------------------------- |
| [`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][] |
| [`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][] |
| [`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][] |
| [`await-fire-event`](./docs/rules/await-fire-event.md) | Enforce promises from `fireEvent` methods to be handled | | ![vue-badge][] ![marko-badge][] |
| [`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 | | |
| [`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][] |
Expand Down
143 changes: 143 additions & 0 deletions docs/rules/await-async-event.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
# Enforce promises from async event methods are handled (`testing-library/await-async-event`)

Ensure that promises returned by `userEvent` (v14+) async methods or `fireEvent` (only Vue and Marko) async methods are handled properly.

## Rule Details

This rule aims to prevent users from forgetting to handle promise returned from async event
methods.

> ⚠️ `fireEvent` methods are async only on following Testing Library packages:
>
> - `@testing-library/vue` (supported by this plugin)
> - `@testing-library/svelte` (not supported yet by this plugin)
> - `@marko/testing-library` (supported by this plugin)
Examples of **incorrect** code for this rule:

```js
fireEvent.click(getByText('Click me'));

fireEvent.focus(getByLabelText('username'));
fireEvent.blur(getByLabelText('username'));

// wrap a fireEvent method within a function...
function triggerEvent() {
return fireEvent.click(button);
}
triggerEvent(); // ...but not handling promise from it is incorrect too
```

```js
userEvent.click(getByText('Click me'));
userEvent.tripleClick(getByText('Click me'));
userEvent.keyboard('foo');

// wrap a userEvent method within a function...
function triggerEvent() {
return userEvent.click(button);
}
triggerEvent(); // ...but not handling promise from it is incorrect too
```

Examples of **correct** code for this rule:

```js
// `await` operator is correct
await fireEvent.focus(getByLabelText('username'));
await fireEvent.blur(getByLabelText('username'));

// `then` method is correct
fireEvent.click(getByText('Click me')).then(() => {
// ...
});

// return the promise within a function is correct too!
const clickMeArrowFn = () => fireEvent.click(getByText('Click me'));

// wrap a fireEvent method within a function...
function triggerEvent() {
return fireEvent.click(button);
}
await triggerEvent(); // ...and handling promise from it is correct also

// using `Promise.all` or `Promise.allSettled` with an array of promises is valid
await Promise.all([
fireEvent.focus(getByLabelText('username')),
fireEvent.blur(getByLabelText('username')),
]);
```

```js
// `await` operator is correct
await userEvent.click(getByText('Click me'));
await userEvent.tripleClick(getByText('Click me'));

// `then` method is correct
userEvent.keyboard('foo').then(() => {
// ...
});

// return the promise within a function is correct too!
const clickMeArrowFn = () => userEvent.click(getByText('Click me'));

// wrap a userEvent method within a function...
function triggerEvent() {
return userEvent.click(button);
}
await triggerEvent(); // ...and handling promise from it is correct also

// using `Promise.all` or `Promise.allSettled` with an array of promises is valid
await Promise.all([
userEvent.click(getByText('Click me'));
userEvent.tripleClick(getByText('Click me'));
]);
```

## Options

- `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.

## Example

```json
{
"testing-library/await-async-event": [
2,
{
"eventModule": "userEvent"
}
]
}
```

```json
{
"testing-library/await-async-event": [
2,
{
"eventModule": "fireEvent"
}
]
}
```

```json
{
"testing-library/await-async-event": [
2,
{
"eventModule": ["fireEvent", "userEvent"]
}
]
}
```

## When Not To Use It

- `userEvent` is below v14, before all event methods are async
- `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.

## Further Reading

- [Vue Testing Library fireEvent](https://testing-library.com/docs/vue-testing-library/api#fireevent)
66 changes: 0 additions & 66 deletions docs/rules/await-fire-event.md

This file was deleted.

3 changes: 1 addition & 2 deletions docs/rules/no-await-sync-events.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,5 +105,4 @@ Example:
## Notes

- 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.
- There is another rule `await-fire-event`, which is only in Vue Testing
Library. Please do not confuse with this rule.
- 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.
4 changes: 4 additions & 0 deletions lib/configs/angular.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
export = {
plugins: ['testing-library'],
rules: {
'testing-library/await-async-event': [
'error',
{ eventModule: 'userEvent' },
],
'testing-library/await-async-query': 'error',
'testing-library/await-async-utils': 'error',
'testing-library/no-await-sync-query': 'error',
Expand Down
4 changes: 4 additions & 0 deletions lib/configs/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
export = {
plugins: ['testing-library'],
rules: {
'testing-library/await-async-event': [
'error',
{ eventModule: 'userEvent' },
],
'testing-library/await-async-query': 'error',
'testing-library/await-async-utils': 'error',
'testing-library/no-await-sync-query': 'error',
Expand Down
5 changes: 4 additions & 1 deletion lib/configs/marko.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@
export = {
plugins: ['testing-library'],
rules: {
'testing-library/await-async-event': [
'error',
{ eventModule: ['fireEvent', 'userEvent'] },
],
'testing-library/await-async-query': 'error',
'testing-library/await-async-utils': 'error',
'testing-library/await-fire-event': 'error',
'testing-library/no-await-sync-query': 'error',
'testing-library/no-container': 'error',
'testing-library/no-debugging-utils': 'error',
Expand Down
4 changes: 4 additions & 0 deletions lib/configs/react.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
export = {
plugins: ['testing-library'],
rules: {
'testing-library/await-async-event': [
'error',
{ eventModule: 'userEvent' },
],
'testing-library/await-async-query': 'error',
'testing-library/await-async-utils': 'error',
'testing-library/no-await-sync-query': 'error',
Expand Down
5 changes: 4 additions & 1 deletion lib/configs/vue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@
export = {
plugins: ['testing-library'],
rules: {
'testing-library/await-async-event': [
'error',
{ eventModule: ['fireEvent', 'userEvent'] },
],
'testing-library/await-async-query': 'error',
'testing-library/await-async-utils': 'error',
'testing-library/await-fire-event': 'error',
'testing-library/no-await-sync-query': 'error',
'testing-library/no-container': 'error',
'testing-library/no-debugging-utils': 'error',
Expand Down
2 changes: 1 addition & 1 deletion lib/node-utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ export function isPromiseHandled(nodeIdentifier: TSESTree.Identifier): boolean {
}

export function getVariableReferences(
context: TSESLint.RuleContext<string, []>,
context: TSESLint.RuleContext<string, unknown[]>,
node: TSESTree.Node
): TSESLint.Scope.Reference[] {
if (ASTUtils.isVariableDeclarator(node)) {
Expand Down

0 comments on commit b4ce9bb

Please sign in to comment.