Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: rename await-fire-event to await-async-event and support user-event #652

Merged
merged 1 commit into from
Oct 2, 2022
Merged
Show file tree
Hide file tree
Changes from all 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
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
Loading