Skip to content

Commit 0ae1f25

Browse files
authored
fix(no-dom-imports): false negatives with several testing library imports (#657)
1 parent e262701 commit 0ae1f25

File tree

4 files changed

+46
-25
lines changed

4 files changed

+46
-25
lines changed

docs/rules/no-dom-import.md

+5
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ import { fireEvent } from 'dom-testing-library';
3131
import { fireEvent } from '@testing-library/dom';
3232
```
3333

34+
```js
35+
import { render } from '@testing-library/react'; // Okay, no error
36+
import { screen } from '@testing-library/dom'; // Error, unnecessary import from @testing-library/dom
37+
```
38+
3439
```js
3540
const { fireEvent } = require('dom-testing-library');
3641
```

lib/create-testing-library-rule/detect-testing-library-utils.ts

+16-12
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ export type EnhancedRuleCreate<
5454

5555
// Helpers methods
5656
type GetTestingLibraryImportNodeFn = () => ImportModuleNode | null;
57+
type GetTestingLibraryImportNodesFn = () => ImportModuleNode[];
5758
type GetCustomModuleImportNodeFn = () => ImportModuleNode | null;
5859
type GetTestingLibraryImportNameFn = () => string | undefined;
5960
type GetCustomModuleImportNameFn = () => string | undefined;
@@ -95,6 +96,7 @@ type IsNodeComingFromTestingLibraryFn = (
9596

9697
export interface DetectionHelpers {
9798
getTestingLibraryImportNode: GetTestingLibraryImportNodeFn;
99+
getAllTestingLibraryImportNodes: GetTestingLibraryImportNodesFn;
98100
getCustomModuleImportNode: GetCustomModuleImportNodeFn;
99101
getTestingLibraryImportName: GetTestingLibraryImportNameFn;
100102
getCustomModuleImportName: GetCustomModuleImportNameFn;
@@ -158,7 +160,7 @@ export function detectTestingLibraryUtils<
158160
context: TestingLibraryContext<TOptions, TMessageIds>,
159161
optionsWithDefault: Readonly<TOptions>
160162
): TSESLint.RuleListener => {
161-
let importedTestingLibraryNode: ImportModuleNode | null = null;
163+
const importedTestingLibraryNodes: ImportModuleNode[] = [];
162164
let importedCustomModuleNode: ImportModuleNode | null = null;
163165
let importedUserEventLibraryNode: ImportModuleNode | null = null;
164166
let importedReactDomTestUtilsNode: ImportModuleNode | null = null;
@@ -299,15 +301,20 @@ export function detectTestingLibraryUtils<
299301

300302
// Helpers for Testing Library detection.
301303
const getTestingLibraryImportNode: GetTestingLibraryImportNodeFn = () => {
302-
return importedTestingLibraryNode;
304+
return importedTestingLibraryNodes[0];
303305
};
304306

307+
const getAllTestingLibraryImportNodes: GetTestingLibraryImportNodesFn =
308+
() => {
309+
return importedTestingLibraryNodes;
310+
};
311+
305312
const getCustomModuleImportNode: GetCustomModuleImportNodeFn = () => {
306313
return importedCustomModuleNode;
307314
};
308315

309316
const getTestingLibraryImportName: GetTestingLibraryImportNameFn = () => {
310-
return getImportModuleName(importedTestingLibraryNode);
317+
return getImportModuleName(importedTestingLibraryNodes[0]);
311318
};
312319

313320
const getCustomModuleImportName: GetCustomModuleImportNameFn = () => {
@@ -331,7 +338,7 @@ export function detectTestingLibraryUtils<
331338
isStrict = false
332339
) => {
333340
const isSomeModuleImported =
334-
!!importedTestingLibraryNode || !!importedCustomModuleNode;
341+
importedTestingLibraryNodes.length !== 0 || !!importedCustomModuleNode;
335342

336343
return (
337344
(!isStrict && isAggressiveModuleReportingEnabled()) ||
@@ -945,6 +952,7 @@ export function detectTestingLibraryUtils<
945952

946953
const helpers: DetectionHelpers = {
947954
getTestingLibraryImportNode,
955+
getAllTestingLibraryImportNodes,
948956
getCustomModuleImportNode,
949957
getTestingLibraryImportName,
950958
getCustomModuleImportName,
@@ -989,12 +997,9 @@ export function detectTestingLibraryUtils<
989997
return;
990998
}
991999
// check only if testing library import not found yet so we avoid
992-
// to override importedTestingLibraryNode after it's found
993-
if (
994-
!importedTestingLibraryNode &&
995-
/testing-library/g.test(node.source.value)
996-
) {
997-
importedTestingLibraryNode = node;
1000+
// to override importedTestingLibraryNodes after it's found
1001+
if (/testing-library/g.test(node.source.value)) {
1002+
importedTestingLibraryNodes.push(node);
9981003
}
9991004

10001005
// check only if custom module import not found yet so we avoid
@@ -1035,15 +1040,14 @@ export function detectTestingLibraryUtils<
10351040
const { arguments: args } = callExpression;
10361041

10371042
if (
1038-
!importedTestingLibraryNode &&
10391043
args.some(
10401044
(arg) =>
10411045
isLiteral(arg) &&
10421046
typeof arg.value === 'string' &&
10431047
/testing-library/g.test(arg.value)
10441048
)
10451049
) {
1046-
importedTestingLibraryNode = callExpression;
1050+
importedTestingLibraryNodes.push(callExpression);
10471051
}
10481052

10491053
const customModule = getCustomModule();

lib/rules/no-dom-import.ts

+13-13
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { TSESTree } from '@typescript-eslint/utils';
22

33
import { createTestingLibraryRule } from '../create-testing-library-rule';
4-
import { isCallExpression } from '../node-utils';
4+
import { isCallExpression, getImportModuleName } from '../node-utils';
55

66
export const RULE_NAME = 'no-dom-import';
77
export type MessageIds = 'noDomImport' | 'noDomImportFramework';
@@ -84,22 +84,22 @@ export default createTestingLibraryRule<Options, MessageIds>({
8484

8585
return {
8686
'Program:exit'() {
87-
const importName = helpers.getTestingLibraryImportName();
88-
const importNode = helpers.getTestingLibraryImportNode();
87+
let importName: string | undefined;
88+
const allImportNodes = helpers.getAllTestingLibraryImportNodes();
8989

90-
if (!importNode) {
91-
return;
92-
}
90+
allImportNodes.forEach((importNode) => {
91+
importName = getImportModuleName(importNode);
9392

94-
const domModuleName = DOM_TESTING_LIBRARY_MODULES.find(
95-
(module) => module === importName
96-
);
93+
const domModuleName = DOM_TESTING_LIBRARY_MODULES.find(
94+
(module) => module === importName
95+
);
9796

98-
if (!domModuleName) {
99-
return;
100-
}
97+
if (!domModuleName) {
98+
return;
99+
}
101100

102-
report(importNode, domModuleName);
101+
report(importNode, domModuleName);
102+
});
103103
},
104104
};
105105
},

tests/lib/rules/no-dom-import.test.ts

+12
Original file line numberDiff line numberDiff line change
@@ -203,5 +203,17 @@ ruleTester.run(RULE_NAME, rule, {
203203
code: 'require("@testing-library/dom")',
204204
errors: [{ messageId: 'noDomImport' }],
205205
},
206+
{
207+
code: `
208+
require("@testing-library/dom");
209+
require("@testing-library/react");`,
210+
errors: [{ line: 2, messageId: 'noDomImport' }],
211+
},
212+
{
213+
code: `
214+
import { render } from '@testing-library/react';
215+
import { screen } from '@testing-library/dom';`,
216+
errors: [{ line: 3, messageId: 'noDomImport' }],
217+
},
206218
],
207219
});

0 commit comments

Comments
 (0)