From 88246fca7919b06a00d0564913779db33de4de19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20Beltr=C3=A1n?= Date: Thu, 24 Aug 2023 17:51:34 +0200 Subject: [PATCH] fix(no-await-sync-events): stop reporting `user-event` by default (#803) --- docs/rules/no-await-sync-events.md | 24 +++++++++------ lib/configs/angular.ts | 5 +++- lib/configs/dom.ts | 5 +++- lib/configs/react.ts | 5 +++- lib/rules/no-await-sync-events.ts | 31 +++++++++++--------- tests/lib/rules/no-await-sync-events.test.ts | 21 +++++++++++-- 6 files changed, 62 insertions(+), 29 deletions(-) diff --git a/docs/rules/no-await-sync-events.md b/docs/rules/no-await-sync-events.md index 4c9e0519..8e953882 100644 --- a/docs/rules/no-await-sync-events.md +++ b/docs/rules/no-await-sync-events.md @@ -4,7 +4,7 @@ -Ensure that sync simulated events are not awaited unnecessarily. +Ensure that sync events are not awaited unnecessarily. ## Rule Details @@ -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 @@ -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 = { @@ -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. diff --git a/lib/configs/angular.ts b/lib/configs/angular.ts index 8defb66b..270e4d48 100644 --- a/lib/configs/angular.ts +++ b/lib/configs/angular.ts @@ -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', diff --git a/lib/configs/dom.ts b/lib/configs/dom.ts index 3e5830ac..ed568225 100644 --- a/lib/configs/dom.ts +++ b/lib/configs/dom.ts @@ -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', diff --git a/lib/configs/react.ts b/lib/configs/react.ts index 538b4fc9..275a0c3a 100644 --- a/lib/configs/react.ts +++ b/lib/configs/react.ts @@ -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', diff --git a/lib/rules/no-await-sync-events.ts b/lib/rules/no-await-sync-events.ts index e9fa9b9d..989ddcda 100644 --- a/lib/rules/no-await-sync-events.ts +++ b/lib/rules/no-await-sync-events.ts @@ -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; +type Options = [{ eventModules?: EventModulesOptions }]; export default createTestingLibraryRule({ name: RULE_NAME, @@ -25,9 +29,9 @@ export default createTestingLibraryRule({ 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, }, @@ -42,20 +46,19 @@ export default createTestingLibraryRule({ properties: { eventModules: { 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 @@ -107,10 +110,10 @@ export default createTestingLibraryRule({ 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; } diff --git a/tests/lib/rules/no-await-sync-events.test.ts b/tests/lib/rules/no-await-sync-events.test.ts index 8145138e..a7a93125 100644 --- a/tests/lib/rules/no-await-sync-events.test.ts +++ b/tests/lib/rules/no-await-sync-events.test.ts @@ -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: [ @@ -254,6 +264,7 @@ ruleTester.run(RULE_NAME, rule, { await userEvent.${func}('foo'); }); `, + options: [{ eventModules: ['user-event'] }], errors: [ { line: 4, @@ -277,7 +288,6 @@ ruleTester.run(RULE_NAME, rule, { await fireEvent.${func}('foo'); }); `, - options: [{ eventModules: ['fire-event'] }], errors: [ { line: 4, @@ -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) => ({ @@ -320,6 +329,7 @@ ruleTester.run(RULE_NAME, rule, { await userEvent.keyboard('foo'); }); `, + options: [{ eventModules: ['user-event'] }], errors: [ { line: 4, @@ -343,6 +353,7 @@ ruleTester.run(RULE_NAME, rule, { await userEvent.keyboard('foo', { delay: 0 }); }); `, + options: [{ eventModules: ['user-event'] }], errors: [ { line: 4, @@ -370,6 +381,7 @@ ruleTester.run(RULE_NAME, rule, { await renamedUserEvent.keyboard('foo', { delay: 0 }); }); `, + options: [{ eventModules: ['user-event', 'fire-event'] }], errors: [ { line: 6, @@ -397,6 +409,7 @@ ruleTester.run(RULE_NAME, rule, { await userEvent.type('foo', { delay }); } `, + options: [{ eventModules: ['user-event'] }], errors: [ { line: 3, @@ -414,6 +427,7 @@ ruleTester.run(RULE_NAME, rule, { await userEvent.type('foo', { delay, skipHover }); } `, + options: [{ eventModules: ['user-event'] }], errors: [ { line: 5, @@ -433,6 +447,7 @@ ruleTester.run(RULE_NAME, rule, { await userEvent.type('foo', { delay, skipHover }); } `, + options: [{ eventModules: ['user-event'] }], errors: [ { line: 7,