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

fix(no-await-sync-events): stop reporting user-event by default #803

24 changes: 15 additions & 9 deletions docs/rules/no-await-sync-events.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

<!-- end auto-generated rule header -->

Ensure that sync simulated events are not awaited unnecessarily.
Ensure that sync events are not awaited unnecessarily.

## Rule Details

Expand All @@ -22,6 +22,12 @@ Some examples of simulating events not returning any Promise are:

This rule aims to prevent users from waiting for those function calls.

> ⚠️ `fire-event` 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
Expand Down Expand Up @@ -87,13 +93,9 @@ const qux = async () => {

This rule provides the following options:

- `eventModules`: array of strings. The possibilities are: `"fire-event"` and `"user-event"`. Defaults to `["fire-event", "user-event"]`

### `eventModules`

This option gives you more granular control of which event modules you want to report, so you can choose to only report methods from either `fire-event`, `user-event` or both.
- `eventModules`: array of strings. Defines which event module should be linted for sync event methods. The possibilities are: `"fire-event"` and `"user-event"`. Defaults to `["fire-event"]`.

Example:
### Example:

```js
module.exports = {
Expand All @@ -106,7 +108,11 @@ module.exports = {
};
```

## When Not To Use It

- `"fire-event"` option: should be disabled only for those Testing Library packages where fire-event methods are async.
- `"user-event"` option: should be disabled only if using v14 or greater.

## 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-async-events`, 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.
There is another rule `await-async-events`, which is for awaiting async events for `user-event` v14 or `fire-event` only in Testing Library packages with async methods. Please do not confuse with this rule.
5 changes: 4 additions & 1 deletion lib/configs/angular.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ export = {
],
'testing-library/await-async-queries': 'error',
'testing-library/await-async-utils': 'error',
'testing-library/no-await-sync-events': 'error',
'testing-library/no-await-sync-events': [
'error',
{ eventModules: ['fire-event'] },
],
'testing-library/no-await-sync-queries': 'error',
'testing-library/no-container': 'error',
'testing-library/no-debugging-utils': 'warn',
Expand Down
5 changes: 4 additions & 1 deletion lib/configs/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ export = {
],
'testing-library/await-async-queries': 'error',
'testing-library/await-async-utils': 'error',
'testing-library/no-await-sync-events': 'error',
'testing-library/no-await-sync-events': [
'error',
{ eventModules: ['fire-event'] },
],
'testing-library/no-await-sync-queries': 'error',
'testing-library/no-global-regexp-flag-in-query': 'error',
'testing-library/no-node-access': 'error',
Expand Down
5 changes: 4 additions & 1 deletion lib/configs/react.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ export = {
],
'testing-library/await-async-queries': 'error',
'testing-library/await-async-utils': 'error',
'testing-library/no-await-sync-events': 'error',
'testing-library/no-await-sync-events': [
'error',
{ eventModules: ['fire-event'] },
],
'testing-library/no-await-sync-queries': 'error',
'testing-library/no-container': 'error',
'testing-library/no-debugging-utils': 'warn',
Expand Down
31 changes: 17 additions & 14 deletions lib/rules/no-await-sync-events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,17 @@ import {
} from '../node-utils';

const USER_EVENT_ASYNC_EXCEPTIONS: string[] = ['type', 'keyboard'];
const VALID_EVENT_MODULES = ['fire-event', 'user-event'] as const;
const FIRE_EVENT_OPTION = 'fire-event' as const;
const USER_EVENT_OPTION = 'user-event' as const;
const VALID_EVENT_MODULES = [FIRE_EVENT_OPTION, USER_EVENT_OPTION];
const DEFAULT_EVENT_MODULES = [FIRE_EVENT_OPTION];

export const RULE_NAME = 'no-await-sync-events';
export type MessageIds = 'noAwaitSyncEvents';
type Options = [
{ eventModules?: readonly (typeof VALID_EVENT_MODULES)[number][] }
];

type ValidEventModules = (typeof VALID_EVENT_MODULES)[number];
type EventModulesOptions = ReadonlyArray<ValidEventModules>;
type Options = [{ eventModules?: EventModulesOptions }];

export default createTestingLibraryRule<Options, MessageIds>({
name: RULE_NAME,
Expand All @@ -25,9 +29,9 @@ export default createTestingLibraryRule<Options, MessageIds>({
docs: {
description: 'Disallow unnecessary `await` for sync events',
recommendedConfig: {
dom: 'error',
angular: 'error',
react: 'error',
dom: ['error', { eventModules: DEFAULT_EVENT_MODULES }],
angular: ['error', { eventModules: DEFAULT_EVENT_MODULES }],
react: ['error', { eventModules: DEFAULT_EVENT_MODULES }],
vue: false,
marko: false,
},
Expand All @@ -42,20 +46,19 @@ export default createTestingLibraryRule<Options, MessageIds>({
properties: {
eventModules: {
Belco90 marked this conversation as resolved.
Show resolved Hide resolved
type: 'array',
items: { type: 'string', enum: VALID_EVENT_MODULES },
minItems: 1,
items: {
enum: VALID_EVENT_MODULES,
},
default: DEFAULT_EVENT_MODULES,
},
},
additionalProperties: false,
},
],
},
defaultOptions: [{ eventModules: VALID_EVENT_MODULES }],
defaultOptions: [{ eventModules: DEFAULT_EVENT_MODULES }],

create(context, [options], helpers) {
const { eventModules = VALID_EVENT_MODULES } = options;
const { eventModules = DEFAULT_EVENT_MODULES } = options;
let hasDelayDeclarationOrAssignmentGTZero: boolean;

// userEvent.type() and userEvent.keyboard() are exceptions, which returns a
Expand Down Expand Up @@ -107,10 +110,10 @@ export default createTestingLibraryRule<Options, MessageIds>({
return;
}

if (isFireEventMethod && !eventModules.includes('fire-event')) {
if (isFireEventMethod && !eventModules.includes(FIRE_EVENT_OPTION)) {
return;
}
if (isUserEventMethod && !eventModules.includes('user-event')) {
if (isUserEventMethod && !eventModules.includes(USER_EVENT_OPTION)) {
return;
}

Expand Down
21 changes: 18 additions & 3 deletions tests/lib/rules/no-await-sync-events.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,16 @@ ruleTester.run(RULE_NAME, rule, {
`,
options: [{ eventModules: ['fire-event'] }],
})),

// valid tests for user-event with default options (user-event disabled)
...USER_EVENT_SYNC_FUNCTIONS.map((func) => ({
code: `
import userEvent from '@testing-library/user-event';
test('should not report userEvent.${func} by default', async() => {
await userEvent.${func}('foo');
});
`,
})),
],

invalid: [
Expand Down Expand Up @@ -254,6 +264,7 @@ ruleTester.run(RULE_NAME, rule, {
await userEvent.${func}('foo');
});
`,
options: [{ eventModules: ['user-event'] }],
errors: [
{
line: 4,
Expand All @@ -277,7 +288,6 @@ ruleTester.run(RULE_NAME, rule, {
await fireEvent.${func}('foo');
});
`,
options: [{ eventModules: ['fire-event'] }],
errors: [
{
line: 4,
Expand All @@ -289,8 +299,7 @@ ruleTester.run(RULE_NAME, rule, {
} as const)
)
),
// sync userEvent sync methods with await operator are not valid
// when only fire-event set in eventModules

...USER_EVENT_SYNC_FUNCTIONS.map(
(func) =>
({
Expand Down Expand Up @@ -320,6 +329,7 @@ ruleTester.run(RULE_NAME, rule, {
await userEvent.keyboard('foo');
});
`,
options: [{ eventModules: ['user-event'] }],
errors: [
{
line: 4,
Expand All @@ -343,6 +353,7 @@ ruleTester.run(RULE_NAME, rule, {
await userEvent.keyboard('foo', { delay: 0 });
});
`,
options: [{ eventModules: ['user-event'] }],
errors: [
{
line: 4,
Expand Down Expand Up @@ -370,6 +381,7 @@ ruleTester.run(RULE_NAME, rule, {
await renamedUserEvent.keyboard('foo', { delay: 0 });
});
`,
options: [{ eventModules: ['user-event', 'fire-event'] }],
errors: [
{
line: 6,
Expand Down Expand Up @@ -397,6 +409,7 @@ ruleTester.run(RULE_NAME, rule, {
await userEvent.type('foo', { delay });
}
`,
options: [{ eventModules: ['user-event'] }],
errors: [
{
line: 3,
Expand All @@ -414,6 +427,7 @@ ruleTester.run(RULE_NAME, rule, {
await userEvent.type('foo', { delay, skipHover });
}
`,
options: [{ eventModules: ['user-event'] }],
errors: [
{
line: 5,
Expand All @@ -433,6 +447,7 @@ ruleTester.run(RULE_NAME, rule, {
await userEvent.type('foo', { delay, skipHover });
}
`,
options: [{ eventModules: ['user-event'] }],
errors: [
{
line: 7,
Expand Down