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`: string, or 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' },
Belco90 marked this conversation as resolved.
Show resolved Hide resolved
],
'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' },
Belco90 marked this conversation as resolved.
Show resolved Hide resolved
],
'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' },
Belco90 marked this conversation as resolved.
Show resolved Hide resolved
],
'testing-library/no-await-sync-queries': 'error',
'testing-library/no-container': 'error',
'testing-library/no-debugging-utils': 'warn',
Expand Down
54 changes: 39 additions & 15 deletions lib/rules/no-await-sync-events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,28 @@ 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];

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> | ValidEventModules;
type Options = [{ eventModules?: EventModulesOptions }];

function getEnabledEventModules(
eventModulesOption?: EventModulesOptions | undefined
): ReadonlyArray<ValidEventModules> {
if (typeof eventModulesOption === 'undefined') {
// This should match the default option
return [FIRE_EVENT_OPTION];
} else if (typeof eventModulesOption === 'string') {
return [eventModulesOption];
}
return eventModulesOption;
}

export default createTestingLibraryRule<Options, MessageIds>({
name: RULE_NAME,
Expand All @@ -25,9 +40,9 @@ export default createTestingLibraryRule<Options, MessageIds>({
docs: {
description: 'Disallow unnecessary `await` for sync events',
recommendedConfig: {
dom: 'error',
angular: 'error',
react: 'error',
dom: ['error', { eventModules: 'fire-event' }],
angular: ['error', { eventModules: 'fire-event' }],
react: ['error', { eventModules: 'fire-event' }],
Belco90 marked this conversation as resolved.
Show resolved Hide resolved
vue: false,
marko: false,
},
Expand All @@ -41,18 +56,22 @@ export default createTestingLibraryRule<Options, MessageIds>({
type: 'object',
properties: {
eventModules: {
Belco90 marked this conversation as resolved.
Show resolved Hide resolved
type: 'array',
minItems: 1,
items: {
enum: VALID_EVENT_MODULES,
},
default: FIRE_EVENT_OPTION,
Belco90 marked this conversation as resolved.
Show resolved Hide resolved
oneOf: [
{ type: 'string', enum: VALID_EVENT_MODULES },
Belco90 marked this conversation as resolved.
Show resolved Hide resolved
{
type: 'array',
itmes: { type: 'string', enum: VALID_EVENT_MODULES },
minItems: 1,
},
],
},
},
additionalProperties: false,
},
],
},
defaultOptions: [{ eventModules: VALID_EVENT_MODULES }],
defaultOptions: [{ eventModules: FIRE_EVENT_OPTION }],
Belco90 marked this conversation as resolved.
Show resolved Hide resolved

create(context, [options], helpers) {
const { eventModules = VALID_EVENT_MODULES } = options;
Belco90 marked this conversation as resolved.
Show resolved Hide resolved
Expand Down Expand Up @@ -90,6 +109,11 @@ export default createTestingLibraryRule<Options, MessageIds>({
},
'AwaitExpression > CallExpression'(node: TSESTree.CallExpression) {
const simulateEventFunctionIdentifier = getDeepestIdentifierNode(node);
const enabledEventModules = getEnabledEventModules(eventModules);
const isFireEventEnabled =
enabledEventModules.includes(FIRE_EVENT_OPTION);
const isUserEventEnabled =
enabledEventModules.includes(USER_EVENT_OPTION);
Belco90 marked this conversation as resolved.
Show resolved Hide resolved
Belco90 marked this conversation as resolved.
Show resolved Hide resolved

if (!simulateEventFunctionIdentifier) {
return;
Expand All @@ -107,10 +131,10 @@ export default createTestingLibraryRule<Options, MessageIds>({
return;
}

if (isFireEventMethod && !eventModules.includes('fire-event')) {
if (isFireEventMethod && !isFireEventEnabled) {
Belco90 marked this conversation as resolved.
Show resolved Hide resolved
return;
}
if (isUserEventMethod && !eventModules.includes('user-event')) {
if (isUserEventMethod && !isUserEventEnabled) {
Belco90 marked this conversation as resolved.
Show resolved Hide resolved
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' }],
Belco90 marked this conversation as resolved.
Show resolved Hide resolved
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