From ace7c3fa8e63c6faab7aaf79bb7ae5466b94efe9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20Beltr=C3=A1n=20Alarc=C3=B3n?= Date: Wed, 3 Mar 2021 20:10:54 +0100 Subject: [PATCH 01/12] refactor(shared-settings): rename utils-module Rename testing-library/module to testing-library/utils-module --- lib/detect-testing-library-utils.ts | 6 +- tests/create-testing-library-rule.test.ts | 62 +++++++------- tests/lib/rules/await-async-query.test.ts | 2 +- tests/lib/rules/await-fire-event.test.ts | 10 +-- tests/lib/rules/no-await-sync-query.test.ts | 6 +- tests/lib/rules/no-dom-import.test.ts | 12 +-- tests/lib/rules/no-manual-cleanup.test.ts | 12 +-- tests/lib/rules/no-node-access.test.ts | 2 +- .../rules/no-promise-in-fire-event.test.ts | 2 +- .../lib/rules/prefer-explicit-assert.test.ts | 4 +- .../lib/rules/prefer-presence-queries.test.ts | 8 +- tests/lib/rules/prefer-user-event.test.ts | 20 ++--- tests/lib/rules/prefer-wait-for.test.ts | 80 +++++++++---------- 13 files changed, 113 insertions(+), 113 deletions(-) diff --git a/lib/detect-testing-library-utils.ts b/lib/detect-testing-library-utils.ts index 7d04d424..d24f8fce 100644 --- a/lib/detect-testing-library-utils.ts +++ b/lib/detect-testing-library-utils.ts @@ -23,7 +23,7 @@ import { } from './utils'; export type TestingLibrarySettings = { - 'testing-library/module'?: string; + 'testing-library/utils-module'?: string; 'testing-library/filename-pattern'?: string; 'testing-library/custom-renders'?: string[]; }; @@ -95,7 +95,7 @@ export function detectTestingLibraryUtils< let importedCustomModuleNode: ImportModuleNode | null = null; // Init options based on shared ESLint settings - const customModule = context.settings['testing-library/module']; + const customModule = context.settings['testing-library/utils-module']; const filenamePattern = context.settings['testing-library/filename-pattern'] ?? DEFAULT_FILENAME_PATTERN; @@ -150,7 +150,7 @@ export function detectTestingLibraryUtils< * custom modules. * * However, there is a setting to customize the module where TL utils can - * be imported from: "testing-library/module". If this setting is enabled, + * be imported from: "testing-library/utils-module". If this setting is enabled, * then this method will return `true` ONLY IF a testing-library package * or custom module are imported. */ diff --git a/tests/create-testing-library-rule.test.ts b/tests/create-testing-library-rule.test.ts index 5d820df7..3ad15022 100644 --- a/tests/create-testing-library-rule.test.ts +++ b/tests/create-testing-library-rule.test.ts @@ -30,7 +30,7 @@ ruleTester.run(RULE_NAME, rule, { const utils = render(); `, settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, }, { @@ -41,7 +41,7 @@ ruleTester.run(RULE_NAME, rule, { const utils = render(); `, settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, }, { @@ -51,7 +51,7 @@ ruleTester.run(RULE_NAME, rule, { import { foo } from 'report-me' `, settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, }, { @@ -61,7 +61,7 @@ ruleTester.run(RULE_NAME, rule, { const { foo } = require('report-me') `, settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, }, { @@ -112,7 +112,7 @@ ruleTester.run(RULE_NAME, rule, { // Test Cases for all settings mixed { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', 'testing-library/filename-pattern': 'testing-library\\.js', }, code: ` @@ -126,7 +126,7 @@ ruleTester.run(RULE_NAME, rule, { }, { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', 'testing-library/filename-pattern': 'testing-library\\.js', }, filename: 'MyComponent.testing-library.js', @@ -174,7 +174,7 @@ ruleTester.run(RULE_NAME, rule, { }, { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` // case: built-in "getBy*" query not reported because custom module not imported @@ -184,7 +184,7 @@ ruleTester.run(RULE_NAME, rule, { }, { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` // case: built-in "queryBy*" query not reported because custom module not imported @@ -194,7 +194,7 @@ ruleTester.run(RULE_NAME, rule, { }, { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` // case: built-in "findBy*" query not reported because custom module not imported @@ -231,7 +231,7 @@ ruleTester.run(RULE_NAME, rule, { }, { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` import * as tl from 'test-utils' @@ -240,7 +240,7 @@ ruleTester.run(RULE_NAME, rule, { `, }, { - settings: { 'testing-library/module': 'test-utils' }, + settings: { 'testing-library/utils-module': 'test-utils' }, code: ` // case: aggressive render enabled, but module disabled - not coming from TL import { render } from 'somewhere-else' @@ -346,7 +346,7 @@ ruleTester.run(RULE_NAME, rule, { const utils = render(); `, settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, errors: [ { @@ -366,7 +366,7 @@ ruleTester.run(RULE_NAME, rule, { const utils = render(); `, settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, errors: [ { @@ -387,7 +387,7 @@ ruleTester.run(RULE_NAME, rule, { const utils = render(); `, settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, errors: [ { @@ -408,7 +408,7 @@ ruleTester.run(RULE_NAME, rule, { const utils = render(); `, settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, errors: [ { @@ -420,7 +420,7 @@ ruleTester.run(RULE_NAME, rule, { }, { settings: { - 'testing-library/module': 'custom-module-forced-report', + 'testing-library/utils-module': 'custom-module-forced-report', }, code: ` // case: import custom module forced to be reported with custom module setting @@ -432,7 +432,7 @@ ruleTester.run(RULE_NAME, rule, { // Test Cases for all settings mixed { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', 'testing-library/filename-pattern': 'testing-library\\.js', }, filename: 'MyComponent.testing-library.js', @@ -589,7 +589,7 @@ ruleTester.run(RULE_NAME, rule, { }, { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` // case: built-in "getBy*" query reported with custom module + Testing Library package import @@ -601,7 +601,7 @@ ruleTester.run(RULE_NAME, rule, { { filename: 'MyComponent.spec.js', settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` // case: built-in "queryBy*" query reported with custom module + Testing Library package import @@ -613,7 +613,7 @@ ruleTester.run(RULE_NAME, rule, { { filename: 'MyComponent.spec.js', settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` // case: built-in "findBy*" query reported with custom module + Testing Library package import @@ -624,7 +624,7 @@ ruleTester.run(RULE_NAME, rule, { }, { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` // case: built-in "getBy*" query reported with custom module + custom module import @@ -636,7 +636,7 @@ ruleTester.run(RULE_NAME, rule, { { filename: 'MyComponent.spec.js', settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` // case: built-in "queryBy*" query reported with custom module + custom module import @@ -648,7 +648,7 @@ ruleTester.run(RULE_NAME, rule, { { filename: 'MyComponent.spec.js', settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` // case: built-in "queryBy*" query reported with custom module + custom module import @@ -660,7 +660,7 @@ ruleTester.run(RULE_NAME, rule, { { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` // case: custom "getBy*" query reported with custom module + Testing Library package import @@ -672,7 +672,7 @@ ruleTester.run(RULE_NAME, rule, { { filename: 'MyComponent.spec.js', settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` // case: custom "queryBy*" query reported with custom module + Testing Library package import @@ -684,7 +684,7 @@ ruleTester.run(RULE_NAME, rule, { { filename: 'MyComponent.spec.js', settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` // case: custom "findBy*" query reported with custom module + Testing Library package import @@ -695,7 +695,7 @@ ruleTester.run(RULE_NAME, rule, { }, { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` // case: custom "getBy*" query reported with custom module + custom module import @@ -707,7 +707,7 @@ ruleTester.run(RULE_NAME, rule, { { filename: 'MyComponent.spec.js', settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` // case: custom "queryBy*" query reported with custom module + custom module import @@ -719,7 +719,7 @@ ruleTester.run(RULE_NAME, rule, { { filename: 'MyComponent.spec.js', settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` // case: custom "findBy*" query reported with custom module + custom module import @@ -730,7 +730,7 @@ ruleTester.run(RULE_NAME, rule, { }, { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` import * as tl from 'test-utils' @@ -744,7 +744,7 @@ ruleTester.run(RULE_NAME, rule, { filename: 'MyComponent.custom-suffix.js', settings: { 'testing-library/custom-renders': ['customRender', 'renderWithRedux'], - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', 'testing-library/filename-pattern': 'custom-suffix\\.js', }, code: ` diff --git a/tests/lib/rules/await-async-query.test.ts b/tests/lib/rules/await-async-query.test.ts index 3c00a59a..a13a14ab 100644 --- a/tests/lib/rules/await-async-query.test.ts +++ b/tests/lib/rules/await-async-query.test.ts @@ -207,7 +207,7 @@ ruleTester.run(RULE_NAME, rule, { // unresolved async queries with aggressive reporting opted-out are valid ...ALL_ASYNC_COMBINATIONS_TO_TEST.map((query) => ({ - settings: { 'testing-library/module': 'test-utils' }, + settings: { 'testing-library/utils-module': 'test-utils' }, code: ` import { render } from "another-library" diff --git a/tests/lib/rules/await-fire-event.test.ts b/tests/lib/rules/await-fire-event.test.ts index 2b62147a..e6a4f8fc 100644 --- a/tests/lib/rules/await-fire-event.test.ts +++ b/tests/lib/rules/await-fire-event.test.ts @@ -101,7 +101,7 @@ ruleTester.run(RULE_NAME, rule, { })), ...COMMON_FIRE_EVENT_METHODS.map((fireEventMethod) => ({ settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` import { fireEvent } from 'somewhere-else' @@ -112,7 +112,7 @@ ruleTester.run(RULE_NAME, rule, { })), ...COMMON_FIRE_EVENT_METHODS.map((fireEventMethod) => ({ settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` import { fireEvent } from 'test-utils' @@ -215,7 +215,7 @@ ruleTester.run(RULE_NAME, rule, { })), ...COMMON_FIRE_EVENT_METHODS.map((fireEventMethod) => ({ settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` import { fireEvent } from '@testing-library/vue' @@ -234,7 +234,7 @@ ruleTester.run(RULE_NAME, rule, { })), ...COMMON_FIRE_EVENT_METHODS.map((fireEventMethod) => ({ settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` import { fireEvent } from 'test-utils' @@ -255,7 +255,7 @@ ruleTester.run(RULE_NAME, rule, { })), ...COMMON_FIRE_EVENT_METHODS.map((fireEventMethod) => ({ settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` import { fireEvent } from '@testing-library/vue' diff --git a/tests/lib/rules/no-await-sync-query.test.ts b/tests/lib/rules/no-await-sync-query.test.ts index cb31c4a6..14e17630 100644 --- a/tests/lib/rules/no-await-sync-query.test.ts +++ b/tests/lib/rules/no-await-sync-query.test.ts @@ -59,7 +59,7 @@ ruleTester.run(RULE_NAME, rule, { // sync query awaited but not related to custom module is invalid but not reported { - settings: { 'testing-library/module': 'test-utils' }, + settings: { 'testing-library/utils-module': 'test-utils' }, code: ` import { screen } from 'somewhere-else' () => { @@ -194,7 +194,7 @@ ruleTester.run(RULE_NAME, rule, { // sync query awaited and related to testing library module // with custom module setting is not valid { - settings: { 'testing-library/module': 'test-utils' }, + settings: { 'testing-library/utils-module': 'test-utils' }, code: ` import { screen } from '@testing-library/react' () => { @@ -205,7 +205,7 @@ ruleTester.run(RULE_NAME, rule, { }, // sync query awaited and related to custom module is not valid { - settings: { 'testing-library/module': 'test-utils' }, + settings: { 'testing-library/utils-module': 'test-utils' }, code: ` import { screen } from 'test-utils' () => { diff --git a/tests/lib/rules/no-dom-import.test.ts b/tests/lib/rules/no-dom-import.test.ts index 01ba26c1..b2c354c1 100644 --- a/tests/lib/rules/no-dom-import.test.ts +++ b/tests/lib/rules/no-dom-import.test.ts @@ -23,7 +23,7 @@ ruleTester.run(RULE_NAME, rule, { 'require("@testing-library/react")', { code: 'import { fireEvent } from "test-utils"', - settings: { 'testing-library/module': 'test-utils' }, + settings: { 'testing-library/utils-module': 'test-utils' }, }, { code: 'import { fireEvent } from "dom-testing-library"', @@ -70,7 +70,7 @@ ruleTester.run(RULE_NAME, rule, { }, { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` // case: dom-testing-library imported with custom module setting @@ -122,7 +122,7 @@ ruleTester.run(RULE_NAME, rule, { }, { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` // case: dom-testing-library wildcard imported with custom module setting @@ -144,7 +144,7 @@ ruleTester.run(RULE_NAME, rule, { }, { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` // case: @testing-library/dom imported with custom module setting @@ -190,7 +190,7 @@ ruleTester.run(RULE_NAME, rule, { }, { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` // case: dom-testing-library required with custom module setting @@ -225,7 +225,7 @@ ruleTester.run(RULE_NAME, rule, { }, { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` // case: @testing-library/dom required with custom module setting diff --git a/tests/lib/rules/no-manual-cleanup.test.ts b/tests/lib/rules/no-manual-cleanup.test.ts index f50e036c..3c681b27 100644 --- a/tests/lib/rules/no-manual-cleanup.test.ts +++ b/tests/lib/rules/no-manual-cleanup.test.ts @@ -77,7 +77,7 @@ ruleTester.run(RULE_NAME, rule, { ...ALL_TESTING_LIBRARIES_WITH_CLEANUP.map((lib) => ({ // official testing-library packages should be reported with custom module setting settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: `import { cleanup, render } from "${lib}"`, errors: [ @@ -90,7 +90,7 @@ ruleTester.run(RULE_NAME, rule, { })), { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` import { render, cleanup } from 'test-utils' @@ -109,7 +109,7 @@ ruleTester.run(RULE_NAME, rule, { })), { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` import { cleanup as myCustomCleanup } from 'test-utils' @@ -128,7 +128,7 @@ ruleTester.run(RULE_NAME, rule, { })), { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` import utils, { cleanup } from 'test-utils' @@ -150,7 +150,7 @@ ruleTester.run(RULE_NAME, rule, { })), { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` import utils from 'test-utils' @@ -183,7 +183,7 @@ ruleTester.run(RULE_NAME, rule, { })), { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` const { render, cleanup } = require('test-utils') diff --git a/tests/lib/rules/no-node-access.test.ts b/tests/lib/rules/no-node-access.test.ts index d8693ee4..c8a9e3f7 100644 --- a/tests/lib/rules/no-node-access.test.ts +++ b/tests/lib/rules/no-node-access.test.ts @@ -64,7 +64,7 @@ ruleTester.run(RULE_NAME, rule, { expect(closestButton).toBeInTheDocument(); `, settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, }, ], diff --git a/tests/lib/rules/no-promise-in-fire-event.test.ts b/tests/lib/rules/no-promise-in-fire-event.test.ts index 99eff5cc..66023be2 100644 --- a/tests/lib/rules/no-promise-in-fire-event.test.ts +++ b/tests/lib/rules/no-promise-in-fire-event.test.ts @@ -41,7 +41,7 @@ ruleTester.run(RULE_NAME, rule, { }, { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: `// invalid usage but aggressive reporting opted-out import { fireEvent } from 'somewhere-else' diff --git a/tests/lib/rules/prefer-explicit-assert.test.ts b/tests/lib/rules/prefer-explicit-assert.test.ts index db8c664f..d5834739 100644 --- a/tests/lib/rules/prefer-explicit-assert.test.ts +++ b/tests/lib/rules/prefer-explicit-assert.test.ts @@ -11,7 +11,7 @@ ruleTester.run(RULE_NAME, rule, { ...COMBINED_QUERIES_METHODS.map((queryMethod) => ({ code: `get${queryMethod}('Hello')`, settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, })), ...COMBINED_QUERIES_METHODS.map((queryMethod) => ({ @@ -153,7 +153,7 @@ ruleTester.run(RULE_NAME, rule, { })), ...COMBINED_QUERIES_METHODS.map((queryMethod) => ({ settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` import "test-utils" diff --git a/tests/lib/rules/prefer-presence-queries.test.ts b/tests/lib/rules/prefer-presence-queries.test.ts index 637d1aec..e4497b1b 100644 --- a/tests/lib/rules/prefer-presence-queries.test.ts +++ b/tests/lib/rules/prefer-presence-queries.test.ts @@ -52,7 +52,7 @@ ruleTester.run(RULE_NAME, rule, { `expect(getElement('foo')).not.toBeInTheDocument()`, { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` // case: invalid presence assert but not reported because custom module is not imported @@ -61,7 +61,7 @@ ruleTester.run(RULE_NAME, rule, { }, { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` // case: invalid absence assert but not reported because custom module is not imported @@ -664,7 +664,7 @@ ruleTester.run(RULE_NAME, rule, { }, { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` // case: asserting presence incorrectly importing custom module @@ -675,7 +675,7 @@ ruleTester.run(RULE_NAME, rule, { }, { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` // case: asserting absence incorrectly importing custom module diff --git a/tests/lib/rules/prefer-user-event.test.ts b/tests/lib/rules/prefer-user-event.test.ts index 171c1968..f3c3bab8 100644 --- a/tests/lib/rules/prefer-user-event.test.ts +++ b/tests/lib/rules/prefer-user-event.test.ts @@ -105,7 +105,7 @@ ruleTester.run(RULE_NAME, rule, { })), { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` import { screen } from 'test-utils' @@ -114,7 +114,7 @@ ruleTester.run(RULE_NAME, rule, { }, { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` import { render } from 'test-utils' @@ -124,7 +124,7 @@ ruleTester.run(RULE_NAME, rule, { }, ...UserEventMethods.map((userEventMethod) => ({ settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` import userEvent from 'test-utils' @@ -134,7 +134,7 @@ ruleTester.run(RULE_NAME, rule, { })), ...Object.keys(MappingToUserEvent).map((fireEventMethod: string) => ({ settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` // fireEvent method used but not imported from TL related module @@ -145,7 +145,7 @@ ruleTester.run(RULE_NAME, rule, { })), ...Object.keys(MappingToUserEvent).map((fireEventMethod: string) => ({ settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` import { fireEvent } from 'test-utils' @@ -156,7 +156,7 @@ ruleTester.run(RULE_NAME, rule, { })), ...Object.keys(MappingToUserEvent).map((fireEventMethod: string) => ({ settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` import { fireEvent as fireEventAliased } from 'test-utils' @@ -167,7 +167,7 @@ ruleTester.run(RULE_NAME, rule, { })), ...Object.keys(MappingToUserEvent).map((fireEventMethod: string) => ({ settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` import * as dom from 'test-utils' @@ -231,7 +231,7 @@ ruleTester.run(RULE_NAME, rule, { ), ...Object.keys(MappingToUserEvent).map((fireEventMethod: string) => ({ settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` import * as dom from 'test-utils' @@ -241,7 +241,7 @@ ruleTester.run(RULE_NAME, rule, { })), ...Object.keys(MappingToUserEvent).map((fireEventMethod: string) => ({ settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` import { fireEvent } from 'test-utils' @@ -260,7 +260,7 @@ ruleTester.run(RULE_NAME, rule, { })), ...Object.keys(MappingToUserEvent).map((fireEventMethod: string) => ({ settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: ` import { fireEvent as fireEventAliased } from 'test-utils' diff --git a/tests/lib/rules/prefer-wait-for.test.ts b/tests/lib/rules/prefer-wait-for.test.ts index 4e7ea7a3..e905c328 100644 --- a/tests/lib/rules/prefer-wait-for.test.ts +++ b/tests/lib/rules/prefer-wait-for.test.ts @@ -22,7 +22,7 @@ ruleTester.run(RULE_NAME, rule, { })), { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: `import { waitFor, render } from 'test-utils'; @@ -32,7 +32,7 @@ ruleTester.run(RULE_NAME, rule, { }, { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: `const { waitFor, render } = require('test-utils'); @@ -56,7 +56,7 @@ ruleTester.run(RULE_NAME, rule, { })), { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: `import { waitForElementToBeRemoved, render } from 'test-utils'; @@ -66,7 +66,7 @@ ruleTester.run(RULE_NAME, rule, { }, { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: `const { waitForElementToBeRemoved, render } = require('test-utils'); @@ -90,7 +90,7 @@ ruleTester.run(RULE_NAME, rule, { })), { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: `import * as testingLibrary from 'test-utils'; @@ -100,7 +100,7 @@ ruleTester.run(RULE_NAME, rule, { }, { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: `const testingLibrary = require('test-utils'); @@ -126,7 +126,7 @@ ruleTester.run(RULE_NAME, rule, { })), { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: `import { render } from 'test-utils'; import { waitForSomethingElse } from 'other-module'; @@ -137,7 +137,7 @@ ruleTester.run(RULE_NAME, rule, { }, { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: `const { render } = require('test-utils'); const { waitForSomethingElse } = require('other-module'); @@ -162,7 +162,7 @@ ruleTester.run(RULE_NAME, rule, { })), { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: `import * as testingLibrary from 'test-utils'; @@ -172,7 +172,7 @@ ruleTester.run(RULE_NAME, rule, { }, { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: `const testingLibrary = require('test-utils'); @@ -295,7 +295,7 @@ ruleTester.run(RULE_NAME, rule, { })), { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: `import { wait, render } from 'test-utils'; @@ -322,7 +322,7 @@ ruleTester.run(RULE_NAME, rule, { }, { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: `const { wait, render } = require('test-utils'); @@ -389,7 +389,7 @@ ruleTester.run(RULE_NAME, rule, { })), { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: `import * as testingLibrary from 'test-utils'; @@ -411,7 +411,7 @@ ruleTester.run(RULE_NAME, rule, { }, { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: `const testingLibrary = require('test-utils'); @@ -473,7 +473,7 @@ ruleTester.run(RULE_NAME, rule, { })), { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: `import * as testingLibrary from 'test-utils'; @@ -495,7 +495,7 @@ ruleTester.run(RULE_NAME, rule, { }, { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: `const testingLibrary = require('test-utils'); @@ -565,7 +565,7 @@ ruleTester.run(RULE_NAME, rule, { })), { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: `import { render, wait } from 'test-utils' @@ -592,7 +592,7 @@ ruleTester.run(RULE_NAME, rule, { }, { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: `const { render, wait } = require('test-utils'); @@ -677,7 +677,7 @@ ruleTester.run(RULE_NAME, rule, { })), { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: `import { render, wait, screen } from "test-utils"; @@ -708,7 +708,7 @@ ruleTester.run(RULE_NAME, rule, { }, { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: `const { render, wait, screen } = require('test-utils'); @@ -787,7 +787,7 @@ ruleTester.run(RULE_NAME, rule, { })), { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: `import { render, waitForElement, screen } from 'test-utils' @@ -814,7 +814,7 @@ ruleTester.run(RULE_NAME, rule, { }, { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: `const { render, waitForElement, screen } = require('test-utils'); @@ -897,7 +897,7 @@ ruleTester.run(RULE_NAME, rule, { })), { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: `import { waitForElement } from 'test-utils'; @@ -928,7 +928,7 @@ ruleTester.run(RULE_NAME, rule, { }, { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: `const { waitForElement } = require('test-utils'); @@ -1007,7 +1007,7 @@ ruleTester.run(RULE_NAME, rule, { })), { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: `import { waitForDomChange } from 'test-utils'; @@ -1034,7 +1034,7 @@ ruleTester.run(RULE_NAME, rule, { }, { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: `const { waitForDomChange } = require('test-utils'); @@ -1109,7 +1109,7 @@ ruleTester.run(RULE_NAME, rule, { })), { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: `import { waitForDomChange } from 'test-utils'; @@ -1136,7 +1136,7 @@ ruleTester.run(RULE_NAME, rule, { }, { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: `const { waitForDomChange } = require('test-utils'); @@ -1211,7 +1211,7 @@ ruleTester.run(RULE_NAME, rule, { })), { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: `import { waitForDomChange } from 'test-utils'; @@ -1238,7 +1238,7 @@ ruleTester.run(RULE_NAME, rule, { }, { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: `const { waitForDomChange } = require('test-utils'); @@ -1359,7 +1359,7 @@ ruleTester.run(RULE_NAME, rule, { })), { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: `import { waitForDomChange, wait, waitForElement } from 'test-utils'; import userEvent from '@testing-library/user-event'; @@ -1409,7 +1409,7 @@ ruleTester.run(RULE_NAME, rule, { }, { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: `const { waitForDomChange, wait, waitForElement } = require('test-utils'); const userEvent = require('@testing-library/user-event'); @@ -1549,7 +1549,7 @@ ruleTester.run(RULE_NAME, rule, { })), { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: `import { render, waitForDomChange, wait, waitForElement } from 'test-utils'; @@ -1597,7 +1597,7 @@ ruleTester.run(RULE_NAME, rule, { }, { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: `const { render, waitForDomChange, wait, waitForElement } = require('test-utils'); @@ -1735,7 +1735,7 @@ ruleTester.run(RULE_NAME, rule, { })), { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: `import { waitForDomChange, wait, render, waitForElement } from 'test-utils'; @@ -1783,7 +1783,7 @@ ruleTester.run(RULE_NAME, rule, { }, { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: `const { waitForDomChange, wait, render, waitForElement } = require('test-utils'); @@ -1931,7 +1931,7 @@ ruleTester.run(RULE_NAME, rule, { })), { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: `import { waitForDomChange, @@ -1984,7 +1984,7 @@ ruleTester.run(RULE_NAME, rule, { }, { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, code: `const { waitForDomChange, @@ -2091,7 +2091,7 @@ ruleTester.run(RULE_NAME, rule, { })), { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, // if already importing waitFor then it's not imported twice code: `import { wait, waitFor, render } from 'test-utils'; @@ -2121,7 +2121,7 @@ ruleTester.run(RULE_NAME, rule, { }, { settings: { - 'testing-library/module': 'test-utils', + 'testing-library/utils-module': 'test-utils', }, // if already importing waitFor then it's not imported twice code: `const { wait, waitFor, render } = require('test-utils'); From 51b5d0bdcefa6967be85b786ee265fc1399535d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20Beltr=C3=A1n=20Alarc=C3=B3n?= Date: Wed, 3 Mar 2021 20:41:01 +0100 Subject: [PATCH 02/12] refactor(detection-helpers): improve fn type definitions --- lib/detect-testing-library-utils.ts | 131 ++++++++++++++++------------ 1 file changed, 75 insertions(+), 56 deletions(-) diff --git a/lib/detect-testing-library-utils.ts b/lib/detect-testing-library-utils.ts index d24f8fce..88811a2c 100644 --- a/lib/detect-testing-library-utils.ts +++ b/lib/detect-testing-library-utils.ts @@ -47,32 +47,54 @@ export type EnhancedRuleCreate< detectionHelpers: Readonly ) => TRuleListener; -export type DetectionHelpers = { - getTestingLibraryImportNode: () => ImportModuleNode | null; - getCustomModuleImportNode: () => ImportModuleNode | null; - getTestingLibraryImportName: () => string | undefined; - getCustomModuleImportName: () => string | undefined; - isTestingLibraryImported: () => boolean; - isValidFilename: () => boolean; - isGetQueryVariant: (node: TSESTree.Identifier) => boolean; - isQueryQueryVariant: (node: TSESTree.Identifier) => boolean; - isFindQueryVariant: (node: TSESTree.Identifier) => boolean; - isSyncQuery: (node: TSESTree.Identifier) => boolean; - isAsyncQuery: (node: TSESTree.Identifier) => boolean; - isCustomQuery: (node: TSESTree.Identifier) => boolean; - isAsyncUtil: (node: TSESTree.Identifier) => boolean; - isFireEventMethod: (node: TSESTree.Identifier) => boolean; - isRenderUtil: (node: TSESTree.Node) => boolean; - isPresenceAssert: (node: TSESTree.MemberExpression) => boolean; - isAbsenceAssert: (node: TSESTree.MemberExpression) => boolean; - canReportErrors: () => boolean; - findImportedUtilSpecifier: ( - specifierName: string - ) => TSESTree.ImportClause | TSESTree.Identifier | undefined; - isNodeComingFromTestingLibrary: ( - node: TSESTree.MemberExpression | TSESTree.Identifier - ) => boolean; -}; +// Helpers methods +type GetTestingLibraryImportNodeFn = () => ImportModuleNode | null; +type GetCustomModuleImportNodeFn = () => ImportModuleNode | null; +type GetTestingLibraryImportNameFn = () => string | undefined; +type GetCustomModuleImportNameFn = () => string | undefined; +type IsTestingLibraryImportedFn = () => boolean; +type IsValidFilenameFn = () => boolean; +type IsGetQueryVariantFn = (node: TSESTree.Identifier) => boolean; +type IsQueryQueryVariantFn = (node: TSESTree.Identifier) => boolean; +type IsFindQueryVariantFn = (node: TSESTree.Identifier) => boolean; +type IsSyncQueryFn = (node: TSESTree.Identifier) => boolean; +type IsAsyncQueryFn = (node: TSESTree.Identifier) => boolean; +type IsCustomQueryFn = (node: TSESTree.Identifier) => boolean; +type IsAsyncUtilFn = (node: TSESTree.Identifier) => boolean; +type IsFireEventMethodFn = (node: TSESTree.Identifier) => boolean; +type IsRenderUtilFn = (node: TSESTree.Node) => boolean; +type IsPresenceAssertFn = (node: TSESTree.MemberExpression) => boolean; +type IsAbsenceAssertFn = (node: TSESTree.MemberExpression) => boolean; +type CanReportErrorsFn = () => boolean; +type FindImportedUtilSpecifierFn = ( + specifierName: string +) => TSESTree.ImportClause | TSESTree.Identifier | undefined; +type IsNodeComingFromTestingLibraryFn = ( + node: TSESTree.MemberExpression | TSESTree.Identifier +) => boolean; + +export interface DetectionHelpers { + getTestingLibraryImportNode: GetTestingLibraryImportNodeFn; + getCustomModuleImportNode: GetCustomModuleImportNodeFn; + getTestingLibraryImportName: GetTestingLibraryImportNameFn; + getCustomModuleImportName: GetCustomModuleImportNameFn; + isTestingLibraryImported: IsTestingLibraryImportedFn; + isValidFilename: IsValidFilenameFn; + isGetQueryVariant: IsGetQueryVariantFn; + isQueryQueryVariant: IsQueryQueryVariantFn; + isFindQueryVariant: IsFindQueryVariantFn; + isSyncQuery: IsSyncQueryFn; + isAsyncQuery: IsAsyncQueryFn; + isCustomQuery: IsCustomQueryFn; + isAsyncUtil: IsAsyncUtilFn; + isFireEventMethod: IsFireEventMethodFn; + isRenderUtil: IsRenderUtilFn; + isPresenceAssert: IsPresenceAssertFn; + isAbsenceAssert: IsAbsenceAssertFn; + canReportErrors: CanReportErrorsFn; + findImportedUtilSpecifier: FindImportedUtilSpecifierFn; + isNodeComingFromTestingLibrary: IsNodeComingFromTestingLibraryFn; +} const DEFAULT_FILENAME_PATTERN = '^.*\\.(test|spec)\\.[jt]sx?$'; @@ -126,21 +148,22 @@ export function detectTestingLibraryUtils< !Array.isArray(customRenders) || customRenders.length === 0; // Helpers for Testing Library detection. - const getTestingLibraryImportNode: DetectionHelpers['getTestingLibraryImportNode'] = () => { + const getTestingLibraryImportNode: GetTestingLibraryImportNodeFn = () => { return importedTestingLibraryNode; }; - const getCustomModuleImportNode: DetectionHelpers['getCustomModuleImportNode'] = () => { + const getCustomModuleImportNode: GetCustomModuleImportNodeFn = () => { return importedCustomModuleNode; }; - const getTestingLibraryImportName: DetectionHelpers['getTestingLibraryImportName'] = () => { + const getTestingLibraryImportName: GetTestingLibraryImportNameFn = () => { return getImportModuleName(importedTestingLibraryNode); }; - const getCustomModuleImportName: DetectionHelpers['getCustomModuleImportName'] = () => { + const getCustomModuleImportName: GetCustomModuleImportNameFn = () => { return getImportModuleName(importedCustomModuleNode); }; + /** * Determines whether Testing Library utils are imported or not for * current file being analyzed. @@ -154,19 +177,19 @@ export function detectTestingLibraryUtils< * then this method will return `true` ONLY IF a testing-library package * or custom module are imported. */ - const isTestingLibraryImported: DetectionHelpers['isTestingLibraryImported'] = () => { - if (isAggressiveModuleReportingEnabled()) { - return true; - } - - return !!importedTestingLibraryNode || !!importedCustomModuleNode; + const isTestingLibraryImported: IsTestingLibraryImportedFn = () => { + return ( + isAggressiveModuleReportingEnabled() || + !!importedTestingLibraryNode || + !!importedCustomModuleNode + ); }; /** * Determines whether filename is valid or not for current file * being analyzed based on "testing-library/filename-pattern" setting. */ - const isValidFilename: DetectionHelpers['isValidFilename'] = () => { + const isValidFilename: IsValidFilenameFn = () => { const fileName = context.getFilename(); return !!fileName.match(filenamePattern); }; @@ -174,43 +197,39 @@ export function detectTestingLibraryUtils< /** * Determines whether a given node is `get*` query variant or not. */ - const isGetQueryVariant: DetectionHelpers['isGetQueryVariant'] = (node) => { + const isGetQueryVariant: IsGetQueryVariantFn = (node) => { return /^get(All)?By.+$/.test(node.name); }; /** * Determines whether a given node is `query*` query variant or not. */ - const isQueryQueryVariant: DetectionHelpers['isQueryQueryVariant'] = ( - node - ) => { + const isQueryQueryVariant: IsQueryQueryVariantFn = (node) => { return /^query(All)?By.+$/.test(node.name); }; /** * Determines whether a given node is `find*` query variant or not. */ - const isFindQueryVariant: DetectionHelpers['isFindQueryVariant'] = ( - node - ) => { + const isFindQueryVariant: IsFindQueryVariantFn = (node) => { return /^find(All)?By.+$/.test(node.name); }; /** * Determines whether a given node is sync query or not. */ - const isSyncQuery: DetectionHelpers['isSyncQuery'] = (node) => { + const isSyncQuery: IsSyncQueryFn = (node) => { return isGetQueryVariant(node) || isQueryQueryVariant(node); }; /** * Determines whether a given node is async query or not. */ - const isAsyncQuery: DetectionHelpers['isAsyncQuery'] = (node) => { + const isAsyncQuery: IsAsyncQueryFn = (node) => { return isFindQueryVariant(node); }; - const isCustomQuery: DetectionHelpers['isCustomQuery'] = (node) => { + const isCustomQuery: IsCustomQueryFn = (node) => { return ( (isSyncQuery(node) || isAsyncQuery(node)) && !ALL_QUERIES_COMBINATIONS.includes(node.name) @@ -220,14 +239,14 @@ export function detectTestingLibraryUtils< /** * Determines whether a given node is async util or not. */ - const isAsyncUtil: DetectionHelpers['isAsyncUtil'] = (node) => { + const isAsyncUtil: IsAsyncUtilFn = (node) => { return ASYNC_UTILS.includes(node.name); }; /** * Determines whether a given node is fireEvent method or not */ - const isFireEventMethod: DetectionHelpers['isFireEventMethod'] = (node) => { + const isFireEventMethod: IsFireEventMethodFn = (node) => { const fireEventUtil = findImportedUtilSpecifier(FIRE_EVENT_NAME); let fireEventUtilName: string | undefined; @@ -293,7 +312,7 @@ export function detectTestingLibraryUtils< * Testing Library. Otherwise, it means `custom-module` has been set up, so * only those nodes coming from Testing Library will be considered as valid. */ - const isRenderUtil: DetectionHelpers['isRenderUtil'] = (node) => { + const isRenderUtil: IsRenderUtilFn = (node) => { const identifier = getIdentifierNode(node); if (!identifier) { @@ -325,7 +344,7 @@ export function detectTestingLibraryUtils< * - expect(element).toBeInTheDocument() * - expect(element).not.toBeNull() */ - const isPresenceAssert: DetectionHelpers['isPresenceAssert'] = (node) => { + const isPresenceAssert: IsPresenceAssertFn = (node) => { const { matcher, isNegated } = getAssertNodeInfo(node); if (!matcher) { @@ -344,7 +363,7 @@ export function detectTestingLibraryUtils< * - expect(element).toBeNull() * - expect(element).not.toBeInTheDocument() */ - const isAbsenceAssert: DetectionHelpers['isAbsenceAssert'] = (node) => { + const isAbsenceAssert: IsAbsenceAssertFn = (node) => { const { matcher, isNegated } = getAssertNodeInfo(node); if (!matcher) { @@ -360,7 +379,7 @@ export function detectTestingLibraryUtils< * Gets a string and verifies if it was imported/required by Testing Library * related module. */ - const findImportedUtilSpecifier: DetectionHelpers['findImportedUtilSpecifier'] = ( + const findImportedUtilSpecifier: FindImportedUtilSpecifierFn = ( specifierName ) => { const node = getCustomModuleImportNode() ?? getTestingLibraryImportNode(); @@ -398,14 +417,14 @@ export function detectTestingLibraryUtils< /** * Determines if file inspected meets all conditions to be reported by rules or not. */ - const canReportErrors: DetectionHelpers['canReportErrors'] = () => { + const canReportErrors: CanReportErrorsFn = () => { return isTestingLibraryImported() && isValidFilename(); }; /** * Takes a MemberExpression or an Identifier and verifies if its name comes from the import in TL * @param node a MemberExpression (in "foo.property" it would be property) or an Identifier */ - const isNodeComingFromTestingLibrary: DetectionHelpers['isNodeComingFromTestingLibrary'] = ( + const isNodeComingFromTestingLibrary: IsNodeComingFromTestingLibraryFn = ( node ) => { let identifierName: string | undefined; @@ -423,7 +442,7 @@ export function detectTestingLibraryUtils< return !!findImportedUtilSpecifier(identifierName); }; - const helpers = { + const helpers: DetectionHelpers = { getTestingLibraryImportNode, getCustomModuleImportNode, getTestingLibraryImportName, From 46016d53eb24dc297dc34d663b58332278acf5ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20Beltr=C3=A1n=20Alarc=C3=B3n?= Date: Thu, 4 Mar 2021 20:42:15 +0100 Subject: [PATCH 03/12] test(filename-pattern): simplify settings patterns --- tests/lib/rules/no-await-sync-query.test.ts | 2 +- tests/lib/rules/no-dom-import.test.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/lib/rules/no-await-sync-query.test.ts b/tests/lib/rules/no-await-sync-query.test.ts index 14e17630..2bef1ab9 100644 --- a/tests/lib/rules/no-await-sync-query.test.ts +++ b/tests/lib/rules/no-await-sync-query.test.ts @@ -69,7 +69,7 @@ ruleTester.run(RULE_NAME, rule, { }, // sync query awaited but not matching filename pattern is invalid but not reported { - settings: { 'testing-library/filename-pattern': '^.*\\.(nope)\\.js$' }, + settings: { 'testing-library/filename-pattern': 'nope\\.js' }, code: ` () => { const element = await getByRole('button') diff --git a/tests/lib/rules/no-dom-import.test.ts b/tests/lib/rules/no-dom-import.test.ts index b2c354c1..d459f9e7 100644 --- a/tests/lib/rules/no-dom-import.test.ts +++ b/tests/lib/rules/no-dom-import.test.ts @@ -31,7 +31,7 @@ ruleTester.run(RULE_NAME, rule, { }, { code: 'import { fireEvent } from "dom-testing-library"', - settings: { 'testing-library/filename-pattern': '^.*\\.(nope)\\.js$' }, + settings: { 'testing-library/filename-pattern': 'nope\\.js' }, }, { code: 'const { fireEvent } = require("dom-testing-library")', @@ -39,7 +39,7 @@ ruleTester.run(RULE_NAME, rule, { }, { code: 'const { fireEvent } = require("dom-testing-library")', - settings: { 'testing-library/filename-pattern': '^.*\\.(nope)\\.js$' }, + settings: { 'testing-library/filename-pattern': 'nope\\.js' }, }, { code: 'import { fireEvent } from "@testing-library/dom"', @@ -47,7 +47,7 @@ ruleTester.run(RULE_NAME, rule, { }, { code: 'import { fireEvent } from "@testing-library/dom"', - settings: { 'testing-library/filename-pattern': '^.*\\.(nope)\\.js$' }, + settings: { 'testing-library/filename-pattern': 'nope\\.js' }, }, { code: 'const { fireEvent } = require("@testing-library/dom")', @@ -55,7 +55,7 @@ ruleTester.run(RULE_NAME, rule, { }, { code: 'const { fireEvent } = require("@testing-library/dom")', - settings: { 'testing-library/filename-pattern': '^.*\\.(nope)\\.js$' }, + settings: { 'testing-library/filename-pattern': 'nope\\.js' }, }, ], invalid: [ From 330c0a0b8115e84162a9367368b102750ea40fbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20Beltr=C3=A1n=20Alarc=C3=B3n?= Date: Thu, 4 Mar 2021 21:19:29 +0100 Subject: [PATCH 04/12] fix: check member expression properly within isRenderUtil helper --- lib/detect-testing-library-utils.ts | 13 ++++++++++- tests/create-testing-library-rule.test.ts | 27 +++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/lib/detect-testing-library-utils.ts b/lib/detect-testing-library-utils.ts index 88811a2c..9f9d891f 100644 --- a/lib/detect-testing-library-utils.ts +++ b/lib/detect-testing-library-utils.ts @@ -331,9 +331,20 @@ export function detectTestingLibraryUtils< return false; } + const referenceNode = (function () { + if (isMemberExpression(node)) { + return node; + } + + if (node.parent && isMemberExpression(node.parent)) { + return node.parent; + } + return identifier; + })(); + return ( isAggressiveModuleReportingEnabled() || - isNodeComingFromTestingLibrary(identifier) + isNodeComingFromTestingLibrary(referenceNode) ); }; diff --git a/tests/create-testing-library-rule.test.ts b/tests/create-testing-library-rule.test.ts index 3ad15022..0c77726d 100644 --- a/tests/create-testing-library-rule.test.ts +++ b/tests/create-testing-library-rule.test.ts @@ -257,6 +257,19 @@ ruleTester.run(RULE_NAME, rule, { const utils = render() `, }, + + // Test Cases for all settings mixed + { + settings: { 'testing-library/utils-module': 'test-utils' }, + code: ` + // case: aggressive module disabled and render coming from non-related module + import * as somethingElse from '@somewhere/else' + import { render } from '@testing-library/react' + + // somethingElse.render is not coming from any module related to TL + const utils = somethingElse.render() + `, + }, ], invalid: [ // Test Cases for Imports & Filename @@ -512,6 +525,19 @@ ruleTester.run(RULE_NAME, rule, { `, errors: [{ line: 5, column: 21, messageId: 'renderError' }], }, + { + settings: { 'testing-library/utils-module': 'test-utils' }, + code: ` + // case: aggressive module disabled and render wildcard-imported from related module + import * as rtl from '@testing-library/react' + + const utils = rtl.render() + `, + errors: [ + { line: 5, column: 21, messageId: 'fakeError' }, + { line: 5, column: 25, messageId: 'renderError' }, + ], + }, // Test Cases for presence/absence assertions { @@ -740,6 +766,7 @@ ruleTester.run(RULE_NAME, rule, { }, // Test Cases for all settings mixed + { filename: 'MyComponent.custom-suffix.js', settings: { From c3859eda4c86285e5b0d8d6b0da93e3b40b42657 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20Beltr=C3=A1n=20Alarc=C3=B3n?= Date: Sun, 7 Mar 2021 13:19:24 +0100 Subject: [PATCH 05/12] test: improve create-testing-library-rule test cases --- tests/create-testing-library-rule.test.ts | 188 ++++++++++++++++------ 1 file changed, 138 insertions(+), 50 deletions(-) diff --git a/tests/create-testing-library-rule.test.ts b/tests/create-testing-library-rule.test.ts index 0c77726d..68ec397a 100644 --- a/tests/create-testing-library-rule.test.ts +++ b/tests/create-testing-library-rule.test.ts @@ -108,35 +108,22 @@ ruleTester.run(RULE_NAME, rule, { const utils = customRender() `, }, - - // Test Cases for all settings mixed { - settings: { - 'testing-library/utils-module': 'test-utils', - 'testing-library/filename-pattern': 'testing-library\\.js', - }, + settings: { 'testing-library/utils-module': 'test-utils' }, code: ` - // case: matching custom settings partially - module but not filename - import { render } from 'test-utils' - import { somethingElse } from 'another-module' - const foo = require('bar') + // case: aggressive render enabled, but module disabled - not coming from TL + import { render } from 'somewhere-else' - const utils = render(); + const utils = render() `, }, { - settings: { - 'testing-library/utils-module': 'test-utils', - 'testing-library/filename-pattern': 'testing-library\\.js', - }, - filename: 'MyComponent.testing-library.js', + filename: 'file.not.matching.js', code: ` - // case: matching custom settings partially - filename but not module - import { render } from 'other-utils' - import { somethingElse } from 'another-module' - const foo = require('bar') + // case: aggressive render and module enabled, but file name not matching + import { render } from '@testing-library/react' - const utils = render(); + const utils = render() `, }, @@ -160,18 +147,36 @@ ruleTester.run(RULE_NAME, rule, { getSomeElement('button') `, }, + { + code: ` + // case: custom method not matching "getBy*" variant pattern using within + within(container).getSomeElement('button') + `, + }, { code: ` // case: custom method not matching "queryBy*" variant pattern querySomeElement('button') `, }, + { + code: ` + // case: custom method not matching "queryBy*" variant pattern using within + within(container).querySomeElement('button') + `, + }, { code: ` // case: custom method not matching "findBy*" variant pattern findSomeElement('button') `, }, + { + code: ` + // case: custom method not matching "findBy*" variant pattern using within + within(container).findSomeElement('button') + `, + }, { settings: { 'testing-library/utils-module': 'test-utils', @@ -182,6 +187,16 @@ ruleTester.run(RULE_NAME, rule, { getByRole('button') `, }, + { + settings: { + 'testing-library/utils-module': 'test-utils', + }, + code: ` + // case: built-in "getBy*" query not reported because custom module not imported using within + import { render } from 'other-module' + within(container).getByRole('button') + `, + }, { settings: { 'testing-library/utils-module': 'test-utils', @@ -192,6 +207,16 @@ ruleTester.run(RULE_NAME, rule, { queryByRole('button') `, }, + { + settings: { + 'testing-library/utils-module': 'test-utils', + }, + code: ` + // case: built-in "queryBy*" query not reported because custom module not imported using within + import { render } from 'other-module' + within(container).queryByRole('button') + `, + }, { settings: { 'testing-library/utils-module': 'test-utils', @@ -202,6 +227,16 @@ ruleTester.run(RULE_NAME, rule, { findByRole('button') `, }, + { + settings: { + 'testing-library/utils-module': 'test-utils', + }, + code: ` + // case: built-in "findBy*" query not reported because custom module not imported using within + import { render } from 'other-module' + within(container).findByRole('button') + `, + }, { settings: { 'testing-library/filename-pattern': 'testing-library\\.js', @@ -229,6 +264,8 @@ ruleTester.run(RULE_NAME, rule, { findByRole('button') `, }, + + // Test Cases for async utils { settings: { 'testing-library/utils-module': 'test-utils', @@ -239,26 +276,37 @@ ruleTester.run(RULE_NAME, rule, { obj.tl.waitFor(() => {}) `, }, + + // Test Cases for all settings mixed { - settings: { 'testing-library/utils-module': 'test-utils' }, + settings: { + 'testing-library/utils-module': 'test-utils', + 'testing-library/filename-pattern': 'testing-library\\.js', + }, code: ` - // case: aggressive render enabled, but module disabled - not coming from TL - import { render } from 'somewhere-else' + // case: matching custom settings partially - module but not filename + import { render } from 'test-utils' + import { somethingElse } from 'another-module' + const foo = require('bar') - const utils = render() + const utils = render(); `, }, { - filename: 'file.not.matching.js', + settings: { + 'testing-library/utils-module': 'test-utils', + 'testing-library/filename-pattern': 'testing-library\\.js', + }, + filename: 'MyComponent.testing-library.js', code: ` - // case: aggressive render and module enabled, but file name not matching - import { render } from '@testing-library/react' + // case: matching custom settings partially - filename but not module + import { render } from 'other-utils' + import { somethingElse } from 'another-module' + const foo = require('bar') - const utils = render() + const utils = render(); `, }, - - // Test Cases for all settings mixed { settings: { 'testing-library/utils-module': 'test-utils' }, code: ` @@ -442,24 +490,6 @@ ruleTester.run(RULE_NAME, rule, { errors: [{ line: 3, column: 7, messageId: 'fakeError' }], }, - // Test Cases for all settings mixed - { - settings: { - 'testing-library/utils-module': 'test-utils', - 'testing-library/filename-pattern': 'testing-library\\.js', - }, - filename: 'MyComponent.testing-library.js', - code: ` - // case: matching all custom settings - import { render } from 'test-utils' - import { somethingElse } from 'another-module' - const foo = require('bar') - - const utils = render(); - `, - errors: [{ line: 7, column: 21, messageId: 'renderError' }], - }, - // Test Cases for renders { code: ` @@ -577,6 +607,13 @@ ruleTester.run(RULE_NAME, rule, { `, errors: [{ line: 3, column: 7, messageId: 'getByError' }], }, + { + code: ` + // case: built-in "getBy*" query reported without import using within (aggressive reporting) + within(container).getByRole('button') + `, + errors: [{ line: 3, column: 25, messageId: 'getByError' }], + }, { code: ` // case: built-in "queryBy*" query reported without import (aggressive reporting) @@ -584,6 +621,13 @@ ruleTester.run(RULE_NAME, rule, { `, errors: [{ line: 3, column: 7, messageId: 'queryByError' }], }, + { + code: ` + // case: built-in "queryBy*" query reported without import using within (aggressive reporting) + within(container).queryByRole('button') + `, + errors: [{ line: 3, column: 25, messageId: 'queryByError' }], + }, { code: ` // case: built-in "findBy*" query reported without import (aggressive reporting) @@ -591,6 +635,13 @@ ruleTester.run(RULE_NAME, rule, { `, errors: [{ line: 3, column: 7, messageId: 'findByError' }], }, + { + code: ` + // case: built-in "findBy*" query reported without import using within (aggressive reporting) + within(container).findByRole('button') + `, + errors: [{ line: 3, column: 25, messageId: 'findByError' }], + }, { filename: 'MyComponent.spec.js', code: ` @@ -599,6 +650,14 @@ ruleTester.run(RULE_NAME, rule, { `, errors: [{ line: 3, column: 7, messageId: 'customQueryError' }], }, + { + filename: 'MyComponent.spec.js', + code: ` + // case: custom "getBy*" query reported without import using within (aggressive reporting) + within(container).getByIcon('search') + `, + errors: [{ line: 3, column: 25, messageId: 'customQueryError' }], + }, { code: ` // case: custom "queryBy*" query reported without import (aggressive reporting) @@ -606,6 +665,13 @@ ruleTester.run(RULE_NAME, rule, { `, errors: [{ line: 3, column: 7, messageId: 'customQueryError' }], }, + { + code: ` + // case: custom "queryBy*" query reported without import using within (aggressive reporting) + within(container).queryByIcon('search') + `, + errors: [{ line: 3, column: 25, messageId: 'customQueryError' }], + }, { code: ` // case: custom "findBy*" query reported without import (aggressive reporting) @@ -613,6 +679,13 @@ ruleTester.run(RULE_NAME, rule, { `, errors: [{ line: 3, column: 7, messageId: 'customQueryError' }], }, + { + code: ` + // case: custom "findBy*" query reported without import using within (aggressive reporting) + within(container).findByIcon('search') + `, + errors: [{ line: 3, column: 25, messageId: 'customQueryError' }], + }, { settings: { 'testing-library/utils-module': 'test-utils', @@ -766,7 +839,6 @@ ruleTester.run(RULE_NAME, rule, { }, // Test Cases for all settings mixed - { filename: 'MyComponent.custom-suffix.js', settings: { @@ -786,5 +858,21 @@ ruleTester.run(RULE_NAME, rule, { { line: 6, column: 18, messageId: 'getByError' }, ], }, + { + settings: { + 'testing-library/utils-module': 'test-utils', + 'testing-library/filename-pattern': 'testing-library\\.js', + }, + filename: 'MyComponent.testing-library.js', + code: ` + // case: matching all custom settings + import { render } from 'test-utils' + import { somethingElse } from 'another-module' + const foo = require('bar') + + const utils = render(); + `, + errors: [{ line: 7, column: 21, messageId: 'renderError' }], + }, ], }); From 1a7db9de1b2cf5b89c6ba3ad44bd5c469ea52984 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20Beltr=C3=A1n=20Alarc=C3=B3n?= Date: Sun, 7 Mar 2021 14:20:54 +0100 Subject: [PATCH 06/12] refactor: check if coming from Testing Library within isAsyncUtil --- lib/detect-testing-library-utils.ts | 50 ++++++++++++++++--- lib/rules/await-async-utils.ts | 11 ----- lib/rules/no-wait-for-snapshot.ts | 3 +- tests/lib/rules/await-async-utils.test.ts | 48 +++++++++++++----- tests/lib/rules/no-wait-for-snapshot.test.ts | 52 +++++++++++++++----- 5 files changed, 119 insertions(+), 45 deletions(-) diff --git a/lib/detect-testing-library-utils.ts b/lib/detect-testing-library-utils.ts index 9f9d891f..9a34abba 100644 --- a/lib/detect-testing-library-utils.ts +++ b/lib/detect-testing-library-utils.ts @@ -60,7 +60,7 @@ type IsFindQueryVariantFn = (node: TSESTree.Identifier) => boolean; type IsSyncQueryFn = (node: TSESTree.Identifier) => boolean; type IsAsyncQueryFn = (node: TSESTree.Identifier) => boolean; type IsCustomQueryFn = (node: TSESTree.Identifier) => boolean; -type IsAsyncUtilFn = (node: TSESTree.Identifier) => boolean; +type IsAsyncUtilFn = (node: TSESTree.Node) => boolean; type IsFireEventMethodFn = (node: TSESTree.Identifier) => boolean; type IsRenderUtilFn = (node: TSESTree.Node) => boolean; type IsPresenceAssertFn = (node: TSESTree.MemberExpression) => boolean; @@ -237,10 +237,44 @@ export function detectTestingLibraryUtils< }; /** - * Determines whether a given node is async util or not. + * Determines whether a given node is a valid async util or not. + * + * A node will be interpreted as a valid async util based on two conditions: + * the name matches with some Testing Library async util, and the node is + * coming from Testing Library module. + * + * The latter depends on Aggressive + * module reporting: if enabled, then it doesn't matter from + * where the given node was imported from as it will be considered part of + * Testing Library. Otherwise, it means `custom-module` has been set up, so + * only those nodes coming from Testing Library will be considered as valid. */ const isAsyncUtil: IsAsyncUtilFn = (node) => { - return ASYNC_UTILS.includes(node.name); + const identifierNode = getIdentifierNode(node); + + if (!identifierNode) { + return false; + } + + if (!ASYNC_UTILS.includes(identifierNode.name)) { + return false; + } + + const referenceNode = (function () { + if (isMemberExpression(node)) { + return node; + } + + if (node.parent && isMemberExpression(node.parent)) { + return node.parent; + } + return identifierNode; + })(); + + return ( + isAggressiveModuleReportingEnabled() || + isNodeComingFromTestingLibrary(referenceNode) + ); }; /** @@ -313,18 +347,18 @@ export function detectTestingLibraryUtils< * only those nodes coming from Testing Library will be considered as valid. */ const isRenderUtil: IsRenderUtilFn = (node) => { - const identifier = getIdentifierNode(node); + const identifierNode = getIdentifierNode(node); - if (!identifier) { + if (!identifierNode) { return false; } const isNameMatching = (function () { if (isAggressiveRenderReportingEnabled()) { - return identifier.name.toLowerCase().includes(RENDER_NAME); + return identifierNode.name.toLowerCase().includes(RENDER_NAME); } - return [RENDER_NAME, ...customRenders].includes(identifier.name); + return [RENDER_NAME, ...customRenders].includes(identifierNode.name); })(); if (!isNameMatching) { @@ -339,7 +373,7 @@ export function detectTestingLibraryUtils< if (node.parent && isMemberExpression(node.parent)) { return node.parent; } - return identifier; + return identifierNode; })(); return ( diff --git a/lib/rules/await-async-utils.ts b/lib/rules/await-async-utils.ts index c23a8877..27e8033b 100644 --- a/lib/rules/await-async-utils.ts +++ b/lib/rules/await-async-utils.ts @@ -4,7 +4,6 @@ import { getFunctionName, getInnermostReturningFunction, getVariableReferences, - isMemberExpression, isPromiseHandled, } from '../node-utils'; import { createTestingLibraryRule } from '../create-testing-library-rule'; @@ -46,16 +45,6 @@ export default createTestingLibraryRule({ return { 'CallExpression Identifier'(node: TSESTree.Identifier) { if (helpers.isAsyncUtil(node)) { - if ( - !helpers.isNodeComingFromTestingLibrary(node) && - !( - isMemberExpression(node.parent) && - helpers.isNodeComingFromTestingLibrary(node.parent) - ) - ) { - return; - } - // detect async query used within wrapper function for later analysis detectAsyncUtilWrapper(node); diff --git a/lib/rules/no-wait-for-snapshot.ts b/lib/rules/no-wait-for-snapshot.ts index 723dfb2a..2963e0e7 100644 --- a/lib/rules/no-wait-for-snapshot.ts +++ b/lib/rules/no-wait-for-snapshot.ts @@ -37,7 +37,6 @@ export default createTestingLibraryRule({ const callExpression = findClosestCallExpressionNode(n); if ( ASTUtils.isIdentifier(callExpression.callee) && - helpers.isNodeComingFromTestingLibrary(callExpression.callee) && helpers.isAsyncUtil(callExpression.callee) ) { return callExpression.callee; @@ -45,7 +44,7 @@ export default createTestingLibraryRule({ if ( isMemberExpression(callExpression.callee) && ASTUtils.isIdentifier(callExpression.callee.property) && - helpers.isNodeComingFromTestingLibrary(callExpression.callee) + helpers.isAsyncUtil(callExpression.callee.property) ) { return callExpression.callee.property; } diff --git a/tests/lib/rules/await-async-utils.test.ts b/tests/lib/rules/await-async-utils.test.ts index 506f800f..b99565d6 100644 --- a/tests/lib/rules/await-async-utils.test.ts +++ b/tests/lib/rules/await-async-utils.test.ts @@ -103,18 +103,28 @@ ruleTester.run(RULE_NAME, rule, { `, })), ...ASYNC_UTILS.map((asyncUtil) => ({ + settings: { + 'testing-library/utils-module': 'test-utils', + }, code: ` import { ${asyncUtil} } from 'some-other-library'; - test('util "${asyncUtil}" which is not related to testing library is valid', async () => { + test( + 'aggressive reporting disabled - util "${asyncUtil}" which is not related to testing library is valid', + async () => { doSomethingElse(); ${asyncUtil}(); }); `, })), ...ASYNC_UTILS.map((asyncUtil) => ({ + settings: { + 'testing-library/utils-module': 'test-utils', + }, code: ` import * as asyncUtils from 'some-other-library'; - test('util "asyncUtils.${asyncUtil}" which is not related to testing library is valid', async () => { + test( + 'aggressive reporting disabled - util "asyncUtils.${asyncUtil}" which is not related to testing library is valid', + async () => { doSomethingElse(); asyncUtils.${asyncUtil}(); }); @@ -174,16 +184,6 @@ ruleTester.run(RULE_NAME, rule, { }); `, }, - ...ASYNC_UTILS.map((asyncUtil) => ({ - code: ` - import { ${asyncUtil} } from '@somewhere/else'; - test('util unhandled but not related to testing library is valid', async () => { - doSomethingElse(); - ${asyncUtil}('not related to testing library') - waitForNotRelatedToTestingLibrary() - }); - `, - })), ...ASYNC_UTILS.map((asyncUtil) => ({ code: ` import { ${asyncUtil} } from '@testing-library/dom'; @@ -301,5 +301,29 @@ ruleTester.run(RULE_NAME, rule, { `, errors: [{ messageId: 'asyncUtilWrapper', line: 10, column: 11 }], })), + ...ASYNC_UTILS.map((asyncUtil) => ({ + code: ` + import { ${asyncUtil} } from 'some-other-library'; + test( + 'aggressive reporting - util "${asyncUtil}" which is not related to testing library is invalid', + async () => { + doSomethingElse(); + ${asyncUtil}(); + }); + `, + errors: [{ line: 7, column: 11, messageId: 'awaitAsyncUtil' }], + })), + ...ASYNC_UTILS.map((asyncUtil) => ({ + code: ` + import * as asyncUtils from 'some-other-library'; + test( + 'aggressive reporting - util "asyncUtils.${asyncUtil}" which is not related to testing library is invalid', + async () => { + doSomethingElse(); + asyncUtils.${asyncUtil}(); + }); + `, + errors: [{ line: 7, column: 22, messageId: 'awaitAsyncUtil' }], + })), ], }); diff --git a/tests/lib/rules/no-wait-for-snapshot.test.ts b/tests/lib/rules/no-wait-for-snapshot.test.ts index 70e44c03..200584a4 100644 --- a/tests/lib/rules/no-wait-for-snapshot.test.ts +++ b/tests/lib/rules/no-wait-for-snapshot.test.ts @@ -51,73 +51,101 @@ ruleTester.run(RULE_NAME, rule, { `, })), ...ASYNC_UTILS.map((asyncUtil) => ({ + settings: { + 'testing-library/utils-module': 'test-utils', + }, code: ` import { ${asyncUtil} } from 'some-other-library'; - test('snapshot calls within ${asyncUtil} are not valid', async () => { + test('aggressive reporting disabled - snapshot calls within ${asyncUtil} not related to Testing Library are valid', async () => { await ${asyncUtil}(() => expect(foo).toMatchSnapshot()); }); `, })), ...ASYNC_UTILS.map((asyncUtil) => ({ + settings: { + 'testing-library/utils-module': 'test-utils', + }, code: ` import { ${asyncUtil} } from 'some-other-library'; - test('snapshot calls within ${asyncUtil} are not valid', async () => { + test('(alt) aggressive reporting disabled - snapshot calls within ${asyncUtil} not related to Testing Library are valid', async () => { await ${asyncUtil}(() => { - expect(foo).toMatchSnapshot() + // this alt version doesn't return from callback passed to async util + expect(foo).toMatchSnapshot() }); }); `, })), ...ASYNC_UTILS.map((asyncUtil) => ({ + settings: { + 'testing-library/utils-module': 'test-utils', + }, code: ` import * as asyncUtils from 'some-other-library'; - test('snapshot calls within ${asyncUtil} are not valid', async () => { + test('aggressive reporting disabled - snapshot calls within ${asyncUtil} from wildcard import not related to Testing Library are valid', async () => { await asyncUtils.${asyncUtil}(() => expect(foo).toMatchSnapshot()); }); `, })), ...ASYNC_UTILS.map((asyncUtil) => ({ + settings: { + 'testing-library/utils-module': 'test-utils', + }, code: ` import * as asyncUtils from 'some-other-library'; - test('snapshot calls within ${asyncUtil} are not valid', async () => { + test('(alt) aggressive reporting disabled - snapshot calls within ${asyncUtil} from wildcard import not related to Testing Library are valid', async () => { await asyncUtils.${asyncUtil}(() => { - expect(foo).toMatchSnapshot() + // this alt version doesn't return from callback passed to async util + expect(foo).toMatchSnapshot() }); }); `, })), ...ASYNC_UTILS.map((asyncUtil) => ({ + settings: { + 'testing-library/utils-module': 'test-utils', + }, code: ` import { ${asyncUtil} } from 'some-other-library'; - test('snapshot calls within ${asyncUtil} are not valid', async () => { + test('aggressive reporting disabled - inline snapshot calls within ${asyncUtil} import not related to Testing Library are valid', async () => { await ${asyncUtil}(() => expect(foo).toMatchInlineSnapshot()); }); `, })), ...ASYNC_UTILS.map((asyncUtil) => ({ + settings: { + 'testing-library/utils-module': 'test-utils', + }, code: ` import { ${asyncUtil} } from 'some-other-library'; - test('snapshot calls within ${asyncUtil} are not valid', async () => { + test('(alt) aggressive reporting disabled - inline snapshot calls within ${asyncUtil} import not related to Testing Library are valid', async () => { await ${asyncUtil}(() => { - expect(foo).toMatchInlineSnapshot() + // this alt version doesn't return from callback passed to async util + expect(foo).toMatchInlineSnapshot() }); }); `, })), ...ASYNC_UTILS.map((asyncUtil) => ({ + settings: { + 'testing-library/utils-module': 'test-utils', + }, code: ` import * as asyncUtils from 'some-other-library'; - test('snapshot calls within ${asyncUtil} are not valid', async () => { + test('aggressive reporting disabled - inline snapshot calls within ${asyncUtil} from wildcard import not related to Testing Library are valid', async () => { await asyncUtils.${asyncUtil}(() => expect(foo).toMatchInlineSnapshot()); }); `, })), ...ASYNC_UTILS.map((asyncUtil) => ({ + settings: { + 'testing-library/utils-module': 'test-utils', + }, code: ` import * as asyncUtils from 'some-other-library'; - test('snapshot calls within ${asyncUtil} are not valid', async () => { + test('(alt) aggressive reporting disabled - inline snapshot calls within ${asyncUtil} from wildcard import not related to Testing Library are valid', async () => { await asyncUtils.${asyncUtil}(() => { - expect(foo).toMatchInlineSnapshot() + // this alt version doesn't return from callback passed to async util + expect(foo).toMatchInlineSnapshot() }); }); `, From 33267dc483ee058171873e0203d39d970c8f39a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20Beltr=C3=A1n=20Alarc=C3=B3n?= Date: Sun, 7 Mar 2021 14:29:29 +0100 Subject: [PATCH 07/12] refactor: extract common method for determining if node is TL util --- lib/detect-testing-library-utils.ts | 91 +++++++++++++---------------- 1 file changed, 39 insertions(+), 52 deletions(-) diff --git a/lib/detect-testing-library-utils.ts b/lib/detect-testing-library-utils.ts index 9a34abba..605f3165 100644 --- a/lib/detect-testing-library-utils.ts +++ b/lib/detect-testing-library-utils.ts @@ -123,6 +123,41 @@ export function detectTestingLibraryUtils< DEFAULT_FILENAME_PATTERN; const customRenders = context.settings['testing-library/custom-renders']; + /** + * Small method to extract common checks to determine whether a node is + * related to Testing Library or not. + */ + function isTestingLibraryUtil( + node: TSESTree.Node, + isUtilCallback: (identifierNode: TSESTree.Identifier) => boolean + ): boolean { + const identifierNode = getIdentifierNode(node); + + if (!identifierNode) { + return false; + } + + if (!isUtilCallback(identifierNode)) { + return false; + } + + const referenceNode = (function () { + if (isMemberExpression(node)) { + return node; + } + + if (node.parent && isMemberExpression(node.parent)) { + return node.parent; + } + return identifierNode; + })(); + + return ( + isAggressiveModuleReportingEnabled() || + isNodeComingFromTestingLibrary(referenceNode) + ); + } + /** * Determines whether aggressive module reporting is enabled or not. * @@ -250,30 +285,8 @@ export function detectTestingLibraryUtils< * only those nodes coming from Testing Library will be considered as valid. */ const isAsyncUtil: IsAsyncUtilFn = (node) => { - const identifierNode = getIdentifierNode(node); - - if (!identifierNode) { - return false; - } - - if (!ASYNC_UTILS.includes(identifierNode.name)) { - return false; - } - - const referenceNode = (function () { - if (isMemberExpression(node)) { - return node; - } - - if (node.parent && isMemberExpression(node.parent)) { - return node.parent; - } - return identifierNode; - })(); - - return ( - isAggressiveModuleReportingEnabled() || - isNodeComingFromTestingLibrary(referenceNode) + return isTestingLibraryUtil(node, (identifierNode) => + ASYNC_UTILS.includes(identifierNode.name) ); }; @@ -347,39 +360,13 @@ export function detectTestingLibraryUtils< * only those nodes coming from Testing Library will be considered as valid. */ const isRenderUtil: IsRenderUtilFn = (node) => { - const identifierNode = getIdentifierNode(node); - - if (!identifierNode) { - return false; - } - - const isNameMatching = (function () { + return isTestingLibraryUtil(node, (identifierNode) => { if (isAggressiveRenderReportingEnabled()) { return identifierNode.name.toLowerCase().includes(RENDER_NAME); } return [RENDER_NAME, ...customRenders].includes(identifierNode.name); - })(); - - if (!isNameMatching) { - return false; - } - - const referenceNode = (function () { - if (isMemberExpression(node)) { - return node; - } - - if (node.parent && isMemberExpression(node.parent)) { - return node.parent; - } - return identifierNode; - })(); - - return ( - isAggressiveModuleReportingEnabled() || - isNodeComingFromTestingLibrary(referenceNode) - ); + }); }; /** From e98cfa007ea9a0bc137a6304f7187bcbf401fdae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20Beltr=C3=A1n=20Alarc=C3=B3n?= Date: Sun, 7 Mar 2021 17:33:04 +0100 Subject: [PATCH 08/12] refactor: improve TL util node detection from identifier --- lib/detect-testing-library-utils.ts | 33 ++++------- lib/node-utils.ts | 62 ++++++++++++++++++++ lib/rules/render-result-naming-convention.ts | 10 +++- 3 files changed, 80 insertions(+), 25 deletions(-) diff --git a/lib/detect-testing-library-utils.ts b/lib/detect-testing-library-utils.ts index 605f3165..8519539f 100644 --- a/lib/detect-testing-library-utils.ts +++ b/lib/detect-testing-library-utils.ts @@ -5,8 +5,9 @@ import { } from '@typescript-eslint/experimental-utils'; import { getAssertNodeInfo, - getIdentifierNode, getImportModuleName, + getPropertyIdentifierNode, + getReferenceNode, ImportModuleNode, isImportDeclaration, isImportNamespaceSpecifier, @@ -60,9 +61,9 @@ type IsFindQueryVariantFn = (node: TSESTree.Identifier) => boolean; type IsSyncQueryFn = (node: TSESTree.Identifier) => boolean; type IsAsyncQueryFn = (node: TSESTree.Identifier) => boolean; type IsCustomQueryFn = (node: TSESTree.Identifier) => boolean; -type IsAsyncUtilFn = (node: TSESTree.Node) => boolean; +type IsAsyncUtilFn = (node: TSESTree.Identifier) => boolean; type IsFireEventMethodFn = (node: TSESTree.Identifier) => boolean; -type IsRenderUtilFn = (node: TSESTree.Node) => boolean; +type IsRenderUtilFn = (node: TSESTree.Identifier) => boolean; type IsPresenceAssertFn = (node: TSESTree.MemberExpression) => boolean; type IsAbsenceAssertFn = (node: TSESTree.MemberExpression) => boolean; type CanReportErrorsFn = () => boolean; @@ -128,33 +129,19 @@ export function detectTestingLibraryUtils< * related to Testing Library or not. */ function isTestingLibraryUtil( - node: TSESTree.Node, + node: TSESTree.Identifier, isUtilCallback: (identifierNode: TSESTree.Identifier) => boolean ): boolean { - const identifierNode = getIdentifierNode(node); - - if (!identifierNode) { - return false; - } - - if (!isUtilCallback(identifierNode)) { + if (!isUtilCallback(node)) { return false; } - const referenceNode = (function () { - if (isMemberExpression(node)) { - return node; - } - - if (node.parent && isMemberExpression(node.parent)) { - return node.parent; - } - return identifierNode; - })(); + const referenceNode = getReferenceNode(node); + const referenceNodeIdentifier = getPropertyIdentifierNode(referenceNode); return ( isAggressiveModuleReportingEnabled() || - isNodeComingFromTestingLibrary(referenceNode) + isNodeComingFromTestingLibrary(referenceNodeIdentifier) ); } @@ -468,7 +455,7 @@ export function detectTestingLibraryUtils< } if (!identifierName) { - return; + return false; } return !!findImportedUtilSpecifier(identifierName); diff --git a/lib/node-utils.ts b/lib/node-utils.ts index 148bc8ef..c9a9f676 100644 --- a/lib/node-utils.ts +++ b/lib/node-utils.ts @@ -363,6 +363,45 @@ export function getFunctionReturnStatementNode( return null; } +/** + * Gets the property identifier node of a given property node. + * + * Not to be confused with {@link getIdentifierNode} + * + * An example: + * Having `const a = rtl.within('foo').getByRole('button')`: + * if we call `getPropertyIdentifierNode` with `rtl` property node, + * it will return `rtl` identifier node + */ +export function getPropertyIdentifierNode( + node: TSESTree.Node +): TSESTree.Identifier | null { + if (ASTUtils.isIdentifier(node)) { + return node; + } + + if (isMemberExpression(node)) { + return getPropertyIdentifierNode(node.object); + } + + if (isCallExpression(node)) { + return getPropertyIdentifierNode(node.callee); + } + + return null; +} + +/** + * Gets the deepest identifier node from a given node. + * + * Opposite of {@link getReferenceNode} + * + * An example: + * Having `const a = rtl.within('foo').getByRole('button')`: + * if we call `getIdentifierNode` with `rtl` node, + * it will return `getByRole` identifier + */ +// TODO: rename to getDeepestIdentifierNode export function getIdentifierNode( node: TSESTree.Node ): TSESTree.Identifier | null { @@ -381,6 +420,29 @@ export function getIdentifierNode( return null; } +/** + * Gets the farthest node from a given node. + * + * Opposite of {@link getIdentifierNode} + + * An example: + * Having `const a = rtl.within('foo').getByRole('button')`: + * if we call `getReferenceNode` with `getByRole` identifier, + * it will return `rtl` node + */ +export function getReferenceNode( + node: + | TSESTree.CallExpression + | TSESTree.MemberExpression + | TSESTree.Identifier +): TSESTree.CallExpression | TSESTree.MemberExpression | TSESTree.Identifier { + if (isMemberExpression(node.parent) || isCallExpression(node.parent)) { + return getReferenceNode(node.parent); + } + + return node; +} + export function getFunctionName( node: | TSESTree.FunctionDeclaration diff --git a/lib/rules/render-result-naming-convention.ts b/lib/rules/render-result-naming-convention.ts index 3d4bb171..9a866cdd 100644 --- a/lib/rules/render-result-naming-convention.ts +++ b/lib/rules/render-result-naming-convention.ts @@ -1,5 +1,5 @@ import { createTestingLibraryRule } from '../create-testing-library-rule'; -import { isObjectPattern } from '../node-utils'; +import { getIdentifierNode, isObjectPattern } from '../node-utils'; import { ASTUtils } from '@typescript-eslint/experimental-utils'; export const RULE_NAME = 'render-result-naming-convention'; @@ -32,7 +32,13 @@ export default createTestingLibraryRule({ create(context, _, helpers) { return { VariableDeclarator(node) { - if (!helpers.isRenderUtil(node.init)) { + const initIdentifierNode = getIdentifierNode(node.init); + + if (!initIdentifierNode) { + return; + } + + if (!helpers.isRenderUtil(initIdentifierNode)) { return; } From 4c5f358b0e3d02f98f8cfbc17695df61c4cee4c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20Beltr=C3=A1n=20Alarc=C3=B3n?= Date: Sun, 7 Mar 2021 17:41:07 +0100 Subject: [PATCH 09/12] refactor: rename getIdentifierNode to clarify its behavior --- lib/node-utils.ts | 17 +++++++++-------- lib/rules/no-promise-in-fire-event.ts | 4 ++-- lib/rules/render-result-naming-convention.ts | 4 ++-- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/lib/node-utils.ts b/lib/node-utils.ts index c9a9f676..2fcd4e75 100644 --- a/lib/node-utils.ts +++ b/lib/node-utils.ts @@ -151,7 +151,7 @@ export function isCallExpressionCallee( node: TSESTree.CallExpression, identifier: TSESTree.Identifier ): boolean { - const nodeInnerIdentifier = getIdentifierNode(node); + const nodeInnerIdentifier = getDeepestIdentifierNode(node); if (nodeInnerIdentifier) { return nodeInnerIdentifier.name === identifier.name; @@ -366,7 +366,7 @@ export function getFunctionReturnStatementNode( /** * Gets the property identifier node of a given property node. * - * Not to be confused with {@link getIdentifierNode} + * Not to be confused with {@link getDeepestIdentifierNode} * * An example: * Having `const a = rtl.within('foo').getByRole('button')`: @@ -398,11 +398,10 @@ export function getPropertyIdentifierNode( * * An example: * Having `const a = rtl.within('foo').getByRole('button')`: - * if we call `getIdentifierNode` with `rtl` node, + * if we call `getDeepestIdentifierNode` with `rtl` node, * it will return `getByRole` identifier */ -// TODO: rename to getDeepestIdentifierNode -export function getIdentifierNode( +export function getDeepestIdentifierNode( node: TSESTree.Node ): TSESTree.Identifier | null { if (ASTUtils.isIdentifier(node)) { @@ -414,7 +413,7 @@ export function getIdentifierNode( } if (isCallExpression(node)) { - return getIdentifierNode(node.callee); + return getDeepestIdentifierNode(node.callee); } return null; @@ -423,7 +422,7 @@ export function getIdentifierNode( /** * Gets the farthest node from a given node. * - * Opposite of {@link getIdentifierNode} + * Opposite of {@link getDeepestIdentifierNode} * An example: * Having `const a = rtl.within('foo').getByRole('button')`: @@ -611,7 +610,9 @@ export function getInnermostReturningFunction( return; } - const returnStatementIdentifier = getIdentifierNode(returnStatementNode); + const returnStatementIdentifier = getDeepestIdentifierNode( + returnStatementNode + ); if (returnStatementIdentifier?.name !== node.name) { return; diff --git a/lib/rules/no-promise-in-fire-event.ts b/lib/rules/no-promise-in-fire-event.ts index e83500fe..40be4ece 100644 --- a/lib/rules/no-promise-in-fire-event.ts +++ b/lib/rules/no-promise-in-fire-event.ts @@ -2,7 +2,7 @@ import { ASTUtils, TSESTree } from '@typescript-eslint/experimental-utils'; import { createTestingLibraryRule } from '../create-testing-library-rule'; import { findClosestCallExpressionNode, - getIdentifierNode, + getDeepestIdentifierNode, isCallExpression, isNewExpression, isPromiseIdentifier, @@ -50,7 +50,7 @@ export default createTestingLibraryRule({ } if (isCallExpression(node)) { - const domElementIdentifier = getIdentifierNode(node); + const domElementIdentifier = getDeepestIdentifierNode(node); if ( helpers.isAsyncQuery(domElementIdentifier) || diff --git a/lib/rules/render-result-naming-convention.ts b/lib/rules/render-result-naming-convention.ts index 9a866cdd..f68e5170 100644 --- a/lib/rules/render-result-naming-convention.ts +++ b/lib/rules/render-result-naming-convention.ts @@ -1,5 +1,5 @@ import { createTestingLibraryRule } from '../create-testing-library-rule'; -import { getIdentifierNode, isObjectPattern } from '../node-utils'; +import { getDeepestIdentifierNode, isObjectPattern } from '../node-utils'; import { ASTUtils } from '@typescript-eslint/experimental-utils'; export const RULE_NAME = 'render-result-naming-convention'; @@ -32,7 +32,7 @@ export default createTestingLibraryRule({ create(context, _, helpers) { return { VariableDeclarator(node) { - const initIdentifierNode = getIdentifierNode(node.init); + const initIdentifierNode = getDeepestIdentifierNode(node.init); if (!initIdentifierNode) { return; From 1ed9912f1169a318a491c1c1b4c565dce0fd6a3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20Beltr=C3=A1n=20Alarc=C3=B3n?= Date: Sun, 7 Mar 2021 17:50:41 +0100 Subject: [PATCH 10/12] fix: improve check for determining if node coming from TL --- lib/detect-testing-library-utils.ts | 9 ++------ tests/create-testing-library-rule.test.ts | 28 ++++++++++++++--------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/lib/detect-testing-library-utils.ts b/lib/detect-testing-library-utils.ts index 8519539f..806234a5 100644 --- a/lib/detect-testing-library-utils.ts +++ b/lib/detect-testing-library-utils.ts @@ -446,13 +446,8 @@ export function detectTestingLibraryUtils< const isNodeComingFromTestingLibrary: IsNodeComingFromTestingLibraryFn = ( node ) => { - let identifierName: string | undefined; - - if (ASTUtils.isIdentifier(node)) { - identifierName = node.name; - } else if (ASTUtils.isIdentifier(node.object)) { - identifierName = node.object.name; - } + const identifierName: string | undefined = getPropertyIdentifierNode(node) + ?.name; if (!identifierName) { return false; diff --git a/tests/create-testing-library-rule.test.ts b/tests/create-testing-library-rule.test.ts index 68ec397a..2b9ae0ec 100644 --- a/tests/create-testing-library-rule.test.ts +++ b/tests/create-testing-library-rule.test.ts @@ -265,17 +265,7 @@ ruleTester.run(RULE_NAME, rule, { `, }, - // Test Cases for async utils - { - settings: { - 'testing-library/utils-module': 'test-utils', - }, - code: ` - import * as tl from 'test-utils' - const obj = { tl } - obj.tl.waitFor(() => {}) - `, - }, + // TODO: Test Cases for async utils // Test Cases for all settings mixed { @@ -599,6 +589,22 @@ ruleTester.run(RULE_NAME, rule, { errors: [{ line: 3, column: 7, messageId: 'absenceAssertError' }], }, + // Test Cases for async utils + { + settings: { + 'testing-library/utils-module': 'test-utils', + }, + code: ` + // case: object property shadowed name is checked correctly + import * as tl from 'test-utils' + const obj = { tl } + + obj.module.waitFor(() => {}) + `, + // TODO: column will be different when async utils errors are implemented properly + errors: [{ line: 6, column: 9, messageId: 'fakeError' }], + }, + // Test Cases for Queries and Aggressive Queries Reporting { code: ` From a378605012780f7c4a4a1cd23b036892de1091a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20Beltr=C3=A1n=20Alarc=C3=B3n?= Date: Sun, 7 Mar 2021 18:14:41 +0100 Subject: [PATCH 11/12] test: add async util test cases to fake rule --- lib/detect-testing-library-utils.ts | 6 +- tests/create-testing-library-rule.test.ts | 117 ++++++++++++++++++---- tests/fake-rule.ts | 20 ++-- 3 files changed, 109 insertions(+), 34 deletions(-) diff --git a/lib/detect-testing-library-utils.ts b/lib/detect-testing-library-utils.ts index 806234a5..7f493207 100644 --- a/lib/detect-testing-library-utils.ts +++ b/lib/detect-testing-library-utils.ts @@ -447,11 +447,7 @@ export function detectTestingLibraryUtils< node ) => { const identifierName: string | undefined = getPropertyIdentifierNode(node) - ?.name; - - if (!identifierName) { - return false; - } + .name; return !!findImportedUtilSpecifier(identifierName); }; diff --git a/tests/create-testing-library-rule.test.ts b/tests/create-testing-library-rule.test.ts index 2b9ae0ec..68c119cb 100644 --- a/tests/create-testing-library-rule.test.ts +++ b/tests/create-testing-library-rule.test.ts @@ -265,7 +265,28 @@ ruleTester.run(RULE_NAME, rule, { `, }, - // TODO: Test Cases for async utils + // Test Cases for async utils + { + settings: { + 'testing-library/utils-module': 'test-utils', + }, + code: ` + import { waitFor } from 'some-other-library'; + test( + 'aggressive reporting disabled - util waitFor not related to testing library is valid', + () => { waitFor() } + ); + `, + }, + { + filename: 'file.not.matching.js', + code: ` + // case: waitFor util found, but file name not matching + import { waitFor } from '@testing-library/react' + + waitFor() + `, + }, // Test Cases for all settings mixed { @@ -497,10 +518,7 @@ ruleTester.run(RULE_NAME, rule, { const utils = rtl.render() `, - errors: [ - { line: 5, column: 21, messageId: 'fakeError' }, - { line: 5, column: 25, messageId: 'renderError' }, - ], + errors: [{ line: 5, column: 25, messageId: 'renderError' }], }, { code: ` @@ -553,10 +571,7 @@ ruleTester.run(RULE_NAME, rule, { const utils = rtl.render() `, - errors: [ - { line: 5, column: 21, messageId: 'fakeError' }, - { line: 5, column: 25, messageId: 'renderError' }, - ], + errors: [{ line: 5, column: 25, messageId: 'renderError' }], }, // Test Cases for presence/absence assertions @@ -590,19 +605,80 @@ ruleTester.run(RULE_NAME, rule, { }, // Test Cases for async utils + { + code: ` + import { waitFor } from 'test-utils'; + test( + 'aggressive reporting enabled - util waitFor reported no matter where is coming from', + () => { waitFor() } + ); + `, + errors: [ + { + line: 5, + column: 19, + messageId: 'asyncUtilError', + data: { utilName: 'waitFor' }, + }, + ], + }, { settings: { 'testing-library/utils-module': 'test-utils', }, code: ` - // case: object property shadowed name is checked correctly + import { waitFor } from 'test-utils'; + test( + 'aggressive reporting disabled - util waitFor related to testing library', + () => { waitFor() } + ); + `, + errors: [ + { + line: 5, + column: 19, + messageId: 'asyncUtilError', + data: { utilName: 'waitFor' }, + }, + ], + }, + { + settings: { + 'testing-library/utils-module': 'test-utils', + }, + code: ` + // case: waitFor from object property shadowed name is checked correctly import * as tl from 'test-utils' const obj = { tl } obj.module.waitFor(() => {}) `, - // TODO: column will be different when async utils errors are implemented properly - errors: [{ line: 6, column: 9, messageId: 'fakeError' }], + errors: [ + { + line: 6, + column: 20, + messageId: 'asyncUtilError', + data: { utilName: 'waitFor' }, + }, + ], + }, + { + settings: { + 'testing-library/utils-module': 'test-utils', + }, + code: ` + // case: aggressive reporting disabled - waitFor from wildcard import related to TL + import * as tl from 'test-utils' + tl.waitFor(() => {}) + `, + errors: [ + { + line: 4, + column: 12, + messageId: 'asyncUtilError', + data: { utilName: 'waitFor' }, + }, + ], }, // Test Cases for Queries and Aggressive Queries Reporting @@ -833,16 +909,6 @@ ruleTester.run(RULE_NAME, rule, { `, errors: [{ line: 4, column: 7, messageId: 'customQueryError' }], }, - { - settings: { - 'testing-library/utils-module': 'test-utils', - }, - code: ` - import * as tl from 'test-utils' - tl.waitFor(() => {}) - `, - errors: [{ line: 3, column: 9, messageId: 'fakeError' }], - }, // Test Cases for all settings mixed { @@ -858,10 +924,17 @@ ruleTester.run(RULE_NAME, rule, { const { getByRole } = renderWithRedux() const el = getByRole('button') + waitFor(() => {}) `, errors: [ { line: 5, column: 29, messageId: 'renderError' }, { line: 6, column: 18, messageId: 'getByError' }, + { + line: 7, + column: 7, + messageId: 'asyncUtilError', + data: { utilName: 'waitFor' }, + }, ], }, { diff --git a/tests/fake-rule.ts b/tests/fake-rule.ts index 0851d6b9..ece0ccbd 100644 --- a/tests/fake-rule.ts +++ b/tests/fake-rule.ts @@ -10,6 +10,7 @@ type Options = []; type MessageIds = | 'fakeError' | 'renderError' + | 'asyncUtilError' | 'getByError' | 'queryByError' | 'findByError' @@ -29,6 +30,8 @@ export default createTestingLibraryRule({ messages: { fakeError: 'fake error reported', renderError: 'some error related to render util reported', + asyncUtilError: + 'some error related to {{ utilName }} async util reported', getByError: 'some error related to getBy reported', queryByError: 'some error related to queryBy reported', findByError: 'some error related to findBy reported', @@ -47,11 +50,20 @@ export default createTestingLibraryRule({ return context.report({ node, messageId: 'renderError' }); } + // force async utils to be reported + if (helpers.isAsyncUtil(node)) { + return context.report({ + node, + messageId: 'asyncUtilError', + data: { utilName: node.name }, + }); + } + + // force queries to be reported if (helpers.isCustomQuery(node)) { return context.report({ node, messageId: 'customQueryError' }); } - // force queries to be reported if (helpers.isGetQueryVariant(node)) { return context.report({ node, messageId: 'getByError' }); } @@ -87,12 +99,6 @@ export default createTestingLibraryRule({ return { 'CallExpression Identifier': reportCallExpressionIdentifier, MemberExpression: reportMemberExpression, - 'CallExpression > MemberExpression'(node: TSESTree.MemberExpression) { - if (!helpers.isNodeComingFromTestingLibrary(node)) { - return; - } - context.report({ node, messageId: 'fakeError' }); - }, ImportDeclaration: reportImportDeclaration, 'Program:exit'() { const importNode = helpers.getCustomModuleImportNode(); From acaf3df2f89847fef56dbe0046b694e656dd785a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20Beltr=C3=A1n=20Alarc=C3=B3n?= Date: Mon, 8 Mar 2021 16:32:17 +0100 Subject: [PATCH 12/12] docs: format jsdoc --- lib/detect-testing-library-utils.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/detect-testing-library-utils.ts b/lib/detect-testing-library-utils.ts index 7f493207..8b4e0cc9 100644 --- a/lib/detect-testing-library-utils.ts +++ b/lib/detect-testing-library-utils.ts @@ -265,11 +265,11 @@ export function detectTestingLibraryUtils< * the name matches with some Testing Library async util, and the node is * coming from Testing Library module. * - * The latter depends on Aggressive - * module reporting: if enabled, then it doesn't matter from - * where the given node was imported from as it will be considered part of - * Testing Library. Otherwise, it means `custom-module` has been set up, so - * only those nodes coming from Testing Library will be considered as valid. + * The latter depends on Aggressive module reporting: + * if enabled, then it doesn't matter from where the given node was imported + * from as it will be considered part of Testing Library. + * Otherwise, it means `custom-module` has been set up, so only those nodes + * coming from Testing Library will be considered as valid. */ const isAsyncUtil: IsAsyncUtilFn = (node) => { return isTestingLibraryUtil(node, (identifierNode) =>