diff --git a/lib/detect-testing-library-utils.ts b/lib/detect-testing-library-utils.ts index cf5ca51f..31b4c809 100644 --- a/lib/detect-testing-library-utils.ts +++ b/lib/detect-testing-library-utils.ts @@ -2,6 +2,7 @@ import { TSESLint, TSESTree } from '@typescript-eslint/experimental-utils'; export type TestingLibrarySettings = { 'testing-library/module'?: string; + 'testing-library/file-name'?: string; }; export type TestingLibraryContext< @@ -25,9 +26,12 @@ export type EnhancedRuleCreate< export type DetectionHelpers = { getIsTestingLibraryImported: () => boolean; + getIsValidFileName: () => boolean; canReportErrors: () => boolean; }; +const DEFAULT_FILE_NAME_PATTERN = '^.*\\.(test|spec)\\.[jt]sx?$'; + /** * Enhances a given rule `create` with helpers to detect Testing Library utils. */ @@ -45,6 +49,9 @@ export function detectTestingLibraryUtils< // Init options based on shared ESLint settings const customModule = context.settings['testing-library/module']; + const fileNamePattern = + context.settings['testing-library/file-name'] ?? + DEFAULT_FILE_NAME_PATTERN; // Helpers for Testing Library detection. const helpers: DetectionHelpers = { @@ -68,11 +75,21 @@ export function detectTestingLibraryUtils< return isImportingTestingLibraryModule || isImportingCustomModule; }, + /** + * Gets if name of the file being analyzed is valid or not. + * + * This is based on "testing-library/file-name" setting. + */ + getIsValidFileName() { + const fileName = context.getFilename(); + return !!fileName.match(fileNamePattern); + }, + /** * Wraps all conditions that must be met to report rules. */ canReportErrors() { - return this.getIsTestingLibraryImported(); + return this.getIsTestingLibraryImported() && this.getIsValidFileName(); }, }; diff --git a/tests/create-testing-library-rule.test.ts b/tests/create-testing-library-rule.test.ts index 14977c72..1073b370 100644 --- a/tests/create-testing-library-rule.test.ts +++ b/tests/create-testing-library-rule.test.ts @@ -1,11 +1,7 @@ import { createRuleTester } from './lib/test-utils'; import rule, { RULE_NAME } from './fake-rule'; -const ruleTester = createRuleTester({ - ecmaFeatures: { - jsx: true, - }, -}); +const ruleTester = createRuleTester(); ruleTester.run(RULE_NAME, rule, { valid: [ @@ -38,6 +34,15 @@ ruleTester.run(RULE_NAME, rule, { 'testing-library/module': 'test-utils', }, }, + { + code: ` + // case: import module forced to be reported but not matching file name + import { foo } from 'report-me' + `, + settings: { + 'testing-library/file-name': 'testing-library\\.js', + }, + }, ], invalid: [ { @@ -47,6 +52,25 @@ ruleTester.run(RULE_NAME, rule, { `, errors: [{ line: 3, column: 7, messageId: 'fakeError' }], }, + { + filename: 'MyComponent.spec.js', + code: ` + // case: import module forced to be reported but from .spec.js named file + import { foo } from 'report-me' + `, + errors: [{ line: 3, column: 7, messageId: 'fakeError' }], + }, + { + filename: 'MyComponent.testing-library.js', + code: ` + // case: import module forced to be reported with custom file name + import { foo } from 'report-me' + `, + settings: { + 'testing-library/file-name': 'testing-library\\.js', + }, + errors: [{ line: 3, column: 7, messageId: 'fakeError' }], + }, { code: ` // case: render imported from any module by default (aggressive reporting) diff --git a/tests/lib/rules/consistent-data-testid.test.ts b/tests/lib/rules/consistent-data-testid.test.ts index cb7ba92c..dd0892f2 100644 --- a/tests/lib/rules/consistent-data-testid.test.ts +++ b/tests/lib/rules/consistent-data-testid.test.ts @@ -1,11 +1,7 @@ import { createRuleTester } from '../test-utils'; import rule, { RULE_NAME } from '../../../lib/rules/consistent-data-testid'; -const ruleTester = createRuleTester({ - ecmaFeatures: { - jsx: true, - }, -}); +const ruleTester = createRuleTester(); ruleTester.run(RULE_NAME, rule, { valid: [ diff --git a/tests/lib/rules/no-container.test.ts b/tests/lib/rules/no-container.test.ts index a6a6116a..4823e9a6 100644 --- a/tests/lib/rules/no-container.test.ts +++ b/tests/lib/rules/no-container.test.ts @@ -1,11 +1,7 @@ import { createRuleTester } from '../test-utils'; import rule, { RULE_NAME } from '../../../lib/rules/no-container'; -const ruleTester = createRuleTester({ - ecmaFeatures: { - jsx: true, - }, -}); +const ruleTester = createRuleTester(); ruleTester.run(RULE_NAME, rule, { valid: [ diff --git a/tests/lib/rules/no-debug.test.ts b/tests/lib/rules/no-debug.test.ts index ad175bac..3e26b414 100644 --- a/tests/lib/rules/no-debug.test.ts +++ b/tests/lib/rules/no-debug.test.ts @@ -1,11 +1,7 @@ import { createRuleTester } from '../test-utils'; import rule, { RULE_NAME } from '../../../lib/rules/no-debug'; -const ruleTester = createRuleTester({ - ecmaFeatures: { - jsx: true, - }, -}); +const ruleTester = createRuleTester(); ruleTester.run(RULE_NAME, rule, { valid: [ diff --git a/tests/lib/rules/no-multiple-assertions-wait-for.test.ts b/tests/lib/rules/no-multiple-assertions-wait-for.test.ts index 44bef16a..4398662a 100644 --- a/tests/lib/rules/no-multiple-assertions-wait-for.test.ts +++ b/tests/lib/rules/no-multiple-assertions-wait-for.test.ts @@ -3,11 +3,7 @@ import rule, { RULE_NAME, } from '../../../lib/rules/no-multiple-assertions-wait-for'; -const ruleTester = createRuleTester({ - ecmaFeatures: { - jsx: true, - }, -}); +const ruleTester = createRuleTester(); ruleTester.run(RULE_NAME, rule, { valid: [ diff --git a/tests/lib/rules/no-node-access.test.ts b/tests/lib/rules/no-node-access.test.ts index 9c7cbcc4..83dcf04c 100644 --- a/tests/lib/rules/no-node-access.test.ts +++ b/tests/lib/rules/no-node-access.test.ts @@ -1,11 +1,7 @@ import { createRuleTester } from '../test-utils'; import rule, { RULE_NAME } from '../../../lib/rules/no-node-access'; -const ruleTester = createRuleTester({ - ecmaFeatures: { - jsx: true, - }, -}); +const ruleTester = createRuleTester(); ruleTester.run(RULE_NAME, rule, { valid: [ @@ -51,18 +47,16 @@ ruleTester.run(RULE_NAME, rule, { within(signinModal).getByPlaceholderText('Username'); `, }, - /*{ - // TODO: this one should be valid indeed. Rule implementation must be improved - // to track where the nodes are coming from. This one wasn't reported before - // just because this code is not importing TL module, but that's a really - // brittle check. Instead, this one shouldn't be reported since `children` - // it's just a property not related to a node + { code: ` const Component = props => { return
{props.children}
} `, - },*/ + settings: { + 'testing-library/file-name': 'testing-library\\.js', + }, + }, { code: ` // case: importing custom module diff --git a/tests/lib/rules/no-render-in-setup.test.ts b/tests/lib/rules/no-render-in-setup.test.ts index 3c8fbc57..1cf9867a 100644 --- a/tests/lib/rules/no-render-in-setup.test.ts +++ b/tests/lib/rules/no-render-in-setup.test.ts @@ -2,11 +2,7 @@ import { createRuleTester } from '../test-utils'; import { TESTING_FRAMEWORK_SETUP_HOOKS } from '../../../lib/utils'; import rule, { RULE_NAME } from '../../../lib/rules/no-render-in-setup'; -const ruleTester = createRuleTester({ - ecmaFeatures: { - jsx: true, - }, -}); +const ruleTester = createRuleTester(); ruleTester.run(RULE_NAME, rule, { valid: [ diff --git a/tests/lib/rules/no-side-effects-wait-for.test.ts b/tests/lib/rules/no-side-effects-wait-for.test.ts index 664e5db2..6b3fadf2 100644 --- a/tests/lib/rules/no-side-effects-wait-for.test.ts +++ b/tests/lib/rules/no-side-effects-wait-for.test.ts @@ -1,11 +1,7 @@ import { createRuleTester } from '../test-utils'; import rule, { RULE_NAME } from '../../../lib/rules/no-side-effects-wait-for'; -const ruleTester = createRuleTester({ - ecmaFeatures: { - jsx: true, - }, -}); +const ruleTester = createRuleTester(); ruleTester.run(RULE_NAME, rule, { valid: [ diff --git a/tests/lib/rules/prefer-find-by.test.ts b/tests/lib/rules/prefer-find-by.test.ts index eacef738..da743103 100644 --- a/tests/lib/rules/prefer-find-by.test.ts +++ b/tests/lib/rules/prefer-find-by.test.ts @@ -14,11 +14,7 @@ import rule, { MessageIds, } from '../../../lib/rules/prefer-find-by'; -const ruleTester = createRuleTester({ - ecmaFeatures: { - jsx: true, - }, -}); +const ruleTester = createRuleTester(); function buildFindByMethod(queryMethod: string) { return `${getFindByQueryVariant(queryMethod)}${queryMethod.split('By')[1]}`; diff --git a/tests/lib/rules/render-result-naming-convention.test.ts b/tests/lib/rules/render-result-naming-convention.test.ts index 3e34c524..7f51163c 100644 --- a/tests/lib/rules/render-result-naming-convention.test.ts +++ b/tests/lib/rules/render-result-naming-convention.test.ts @@ -3,11 +3,7 @@ import rule, { RULE_NAME, } from '../../../lib/rules/render-result-naming-convention'; -const ruleTester = createRuleTester({ - ecmaFeatures: { - jsx: true, - }, -}); +const ruleTester = createRuleTester(); ruleTester.run(RULE_NAME, rule, { valid: [ diff --git a/tests/lib/test-utils.ts b/tests/lib/test-utils.ts index 391f5844..88c1b778 100644 --- a/tests/lib/test-utils.ts +++ b/tests/lib/test-utils.ts @@ -1,14 +1,49 @@ import { resolve } from 'path'; import { TSESLint } from '@typescript-eslint/experimental-utils'; +const DEFAULT_TEST_CASE_CONFIG = { + filename: 'MyComponent.test.js', +}; + +class TestingLibraryRuleTester extends TSESLint.RuleTester { + run>( + ruleName: string, + rule: TSESLint.RuleModule, + tests: TSESLint.RunTests + ): void { + const { valid, invalid } = tests; + + const finalValid = valid.map((testCase) => { + if (typeof testCase === 'string') { + return { + ...DEFAULT_TEST_CASE_CONFIG, + code: testCase, + }; + } + + return { ...DEFAULT_TEST_CASE_CONFIG, ...testCase }; + }); + const finalInvalid = invalid.map((testCase) => ({ + ...DEFAULT_TEST_CASE_CONFIG, + ...testCase, + })); + + super.run(ruleName, rule, { valid: finalValid, invalid: finalInvalid }); + } +} + export const createRuleTester = ( parserOptions: Partial = {} -): TSESLint.RuleTester => - new TSESLint.RuleTester({ +): TSESLint.RuleTester => { + return new TestingLibraryRuleTester({ parser: resolve('./node_modules/@typescript-eslint/parser'), parserOptions: { ecmaVersion: 2018, sourceType: 'module', + ecmaFeatures: { + jsx: true, + }, ...parserOptions, }, }); +}; diff --git a/tsconfig.json b/tsconfig.json index cddbaa37..1856daa6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "es5", + "target": "es6", "module": "commonjs", "moduleResolution": "node", "esModuleInterop": true,