11import { TSESLint , TSESTree } from '@typescript-eslint/experimental-utils' ;
2+ import { isLiteral } from './node-utils' ;
23
34export type TestingLibrarySettings = {
45 'testing-library/module' ?: string ;
5- 'testing-library/file-name ' ?: string ;
6+ 'testing-library/filename-pattern ' ?: string ;
67} ;
78
89export type TestingLibraryContext <
@@ -24,13 +25,20 @@ export type EnhancedRuleCreate<
2425 detectionHelpers : Readonly < DetectionHelpers >
2526) => TRuleListener ;
2627
28+ type ModuleImportation =
29+ | TSESTree . ImportDeclaration
30+ | TSESTree . CallExpression
31+ | null ;
32+
2733export type DetectionHelpers = {
34+ getTestingLibraryImportNode : ( ) => ModuleImportation ;
35+ getCustomModuleImportNode : ( ) => ModuleImportation ;
2836 getIsTestingLibraryImported : ( ) => boolean ;
29- getIsValidFileName : ( ) => boolean ;
37+ getIsValidFilename : ( ) => boolean ;
3038 canReportErrors : ( ) => boolean ;
3139} ;
3240
33- const DEFAULT_FILE_NAME_PATTERN = '^.*\\.(test|spec)\\.[jt]sx?$' ;
41+ const DEFAULT_FILENAME_PATTERN = '^.*\\.(test|spec)\\.[jt]sx?$' ;
3442
3543/**
3644 * Enhances a given rule `create` with helpers to detect Testing Library utils.
@@ -44,17 +52,23 @@ export function detectTestingLibraryUtils<
4452 context : TestingLibraryContext < TOptions , TMessageIds > ,
4553 optionsWithDefault : Readonly < TOptions >
4654 ) : TSESLint . RuleListener => {
47- let isImportingTestingLibraryModule = false ;
48- let isImportingCustomModule = false ;
55+ let importedTestingLibraryNode : ModuleImportation = null ;
56+ let importedCustomModuleNode : ModuleImportation = null ;
4957
5058 // Init options based on shared ESLint settings
5159 const customModule = context . settings [ 'testing-library/module' ] ;
52- const fileNamePattern =
53- context . settings [ 'testing-library/file-name ' ] ??
54- DEFAULT_FILE_NAME_PATTERN ;
60+ const filenamePattern =
61+ context . settings [ 'testing-library/filename-pattern ' ] ??
62+ DEFAULT_FILENAME_PATTERN ;
5563
5664 // Helpers for Testing Library detection.
5765 const helpers : DetectionHelpers = {
66+ getTestingLibraryImportNode ( ) {
67+ return importedTestingLibraryNode ;
68+ } ,
69+ getCustomModuleImportNode ( ) {
70+ return importedCustomModuleNode ;
71+ } ,
5872 /**
5973 * Gets if Testing Library is considered as imported or not.
6074 *
@@ -72,50 +86,85 @@ export function detectTestingLibraryUtils<
7286 return true ;
7387 }
7488
75- return isImportingTestingLibraryModule || isImportingCustomModule ;
89+ return ! ! importedTestingLibraryNode || ! ! importedCustomModuleNode ;
7690 } ,
7791
7892 /**
79- * Gets if name of the file being analyzed is valid or not.
93+ * Gets if filename being analyzed is valid or not.
8094 *
81- * This is based on "testing-library/file-name " setting.
95+ * This is based on "testing-library/filename-pattern " setting.
8296 */
83- getIsValidFileName ( ) {
97+ getIsValidFilename ( ) {
8498 const fileName = context . getFilename ( ) ;
85- return ! ! fileName . match ( fileNamePattern ) ;
99+ return ! ! fileName . match ( filenamePattern ) ;
86100 } ,
87101
88102 /**
89103 * Wraps all conditions that must be met to report rules.
90104 */
91105 canReportErrors ( ) {
92- return this . getIsTestingLibraryImported ( ) && this . getIsValidFileName ( ) ;
106+ return this . getIsTestingLibraryImported ( ) && this . getIsValidFilename ( ) ;
93107 } ,
94108 } ;
95109
96110 // Instructions for Testing Library detection.
97111 const detectionInstructions : TSESLint . RuleListener = {
98112 /**
99113 * This ImportDeclaration rule listener will check if Testing Library related
100- * modules are loaded . Since imports happen first thing in a file, it's
114+ * modules are imported . Since imports happen first thing in a file, it's
101115 * safe to use `isImportingTestingLibraryModule` and `isImportingCustomModule`
102116 * since they will have corresponding value already updated when reporting other
103117 * parts of the file.
104118 */
105119 ImportDeclaration ( node : TSESTree . ImportDeclaration ) {
106- if ( ! isImportingTestingLibraryModule ) {
107- // check only if testing library import not found yet so we avoid
108- // to override isImportingTestingLibraryModule after it's found
109- isImportingTestingLibraryModule = / t e s t i n g - l i b r a r y / g. test (
110- node . source . value as string
111- ) ;
120+ // check only if testing library import not found yet so we avoid
121+ // to override importedTestingLibraryNode after it's found
122+ if (
123+ ! importedTestingLibraryNode &&
124+ / t e s t i n g - l i b r a r y / g. test ( node . source . value as string )
125+ ) {
126+ importedTestingLibraryNode = node ;
127+ }
128+
129+ // check only if custom module import not found yet so we avoid
130+ // to override importedCustomModuleNode after it's found
131+ if (
132+ ! importedCustomModuleNode &&
133+ String ( node . source . value ) . endsWith ( customModule )
134+ ) {
135+ importedCustomModuleNode = node ;
136+ }
137+ } ,
138+
139+ // Check if Testing Library related modules are loaded with required.
140+ [ `CallExpression > Identifier[name="require"]` ] (
141+ node : TSESTree . Identifier
142+ ) {
143+ const callExpression = node . parent as TSESTree . CallExpression ;
144+ const { arguments : args } = callExpression ;
145+
146+ if (
147+ ! importedTestingLibraryNode &&
148+ args . some (
149+ ( arg ) =>
150+ isLiteral ( arg ) &&
151+ typeof arg . value === 'string' &&
152+ / t e s t i n g - l i b r a r y / g. test ( arg . value )
153+ )
154+ ) {
155+ importedTestingLibraryNode = callExpression ;
112156 }
113157
114- if ( ! isImportingCustomModule ) {
115- // check only if custom module import not found yet so we avoid
116- // to override isImportingCustomModule after it's found
117- const importName = String ( node . source . value ) ;
118- isImportingCustomModule = importName . endsWith ( customModule ) ;
158+ if (
159+ ! importedCustomModuleNode &&
160+ args . some (
161+ ( arg ) =>
162+ isLiteral ( arg ) &&
163+ typeof arg . value === 'string' &&
164+ arg . value . endsWith ( customModule )
165+ )
166+ ) {
167+ importedCustomModuleNode = callExpression ;
119168 }
120169 } ,
121170 } ;
0 commit comments