Skip to content

Commit 3cc2940

Browse files
thomaslombartBelco90
authored andcommitted
feat(no-dom-import): suggest proper framework to import from (testing-library#14)
* feat: suggest proper framework to import from * fix: report error depending on dom library's name * docs: add docs on no-dom-import framework option * feat: add fixer for no-dom-import when option is specified
1 parent 0e11657 commit 3cc2940

File tree

5 files changed

+134
-18
lines changed

5 files changed

+134
-18
lines changed

docs/rules/no-dom-import.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,20 @@ const { fireEvent } = require('react-testing-library');
5757
const { fireEvent } = require('@testing-library/react');
5858
```
5959

60+
## Options
61+
62+
This rule has an option in case you want to tell the user which framework to use.
63+
64+
### Example
65+
66+
```json
67+
{
68+
"testing-library/no-dom-import": ["error", "react"]
69+
}
70+
```
71+
72+
With the configuration above, if the user imports from `@testing-library/dom` or `dom-testing-library` instead of the used framework, ESLint will tell the user to import from `@testing-library/react` or `react-testing-library`.
73+
6074
## Further Reading
6175

6276
- [Angular Testing Library API](https://testing-library.com/docs/angular-testing-library/api)

lib/index.js

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,27 @@ module.exports = {
2121
},
2222
angular: {
2323
plugins: ['testing-library'],
24-
rules: { ...recommendedRules, 'testing-library/no-debug': 'warn' },
24+
rules: {
25+
...recommendedRules,
26+
'testing-library/no-debug': 'warn',
27+
'testing/library/no-dom-import': ['error', 'angular'],
28+
},
2529
},
2630
react: {
2731
plugins: ['testing-library'],
28-
rules: { ...recommendedRules, 'testing-library/no-debug': 'warn' },
32+
rules: {
33+
...recommendedRules,
34+
'testing-library/no-debug': 'warn',
35+
'testing/library/no-dom-import': ['error', 'react'],
36+
},
2937
},
3038
vue: {
3139
plugins: ['testing-library'],
32-
rules: { ...recommendedRules, 'testing-library/no-debug': 'warn' },
40+
rules: {
41+
...recommendedRules,
42+
'testing-library/no-debug': 'warn',
43+
'testing/library/no-dom-import': ['error', 'vue'],
44+
},
3345
},
3446
},
3547
};

lib/rules/no-dom-import.js

Lines changed: 58 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,35 +18,78 @@ module.exports = {
1818
},
1919
messages: {
2020
noDomImport:
21-
'import from DOM Testing Library is restricted, import from corresponding Testing library framework instead',
21+
'import from DOM Testing Library is restricted, import from corresponding Testing Library framework instead',
22+
noDomImportFramework:
23+
'import from DOM Testing Library is restricted, import from {{module}} instead',
2224
},
23-
fixable: null,
24-
schema: [],
25+
fixable: 'code',
26+
schema: [
27+
{
28+
type: 'string',
29+
},
30+
],
2531
},
2632

2733
create: function(context) {
34+
const framework = context.options[0];
35+
2836
return {
2937
ImportDeclaration(node) {
30-
if (DOM_TESTING_LIBRARY_MODULES.includes(node.source.value)) {
31-
context.report({
32-
node,
33-
messageId: 'noDomImport',
34-
});
38+
const domModuleName = DOM_TESTING_LIBRARY_MODULES.find(
39+
module => module === node.source.value
40+
);
41+
42+
if (domModuleName) {
43+
report(context, node, domModuleName);
3544
}
3645
},
3746

3847
[`CallExpression > Identifier[name="require"]`](node) {
3948
const { arguments: args } = node.parent;
4049

41-
if (
42-
args.some(args => DOM_TESTING_LIBRARY_MODULES.includes(args.value))
43-
) {
44-
context.report({
45-
node,
46-
messageId: 'noDomImport',
47-
});
50+
const literalNodeDomModuleName = args.find(args =>
51+
DOM_TESTING_LIBRARY_MODULES.includes(args.value)
52+
);
53+
54+
if (literalNodeDomModuleName) {
55+
report(context, node, literalNodeDomModuleName.value);
4856
}
4957
},
5058
};
59+
60+
function report(context, node, moduleName) {
61+
if (framework) {
62+
const isRequire = node.name === 'require';
63+
const correctModuleName = moduleName.replace('dom', framework);
64+
context.report({
65+
node,
66+
messageId: 'noDomImportFramework',
67+
data: {
68+
module: correctModuleName,
69+
},
70+
fix(fixer) {
71+
if (isRequire) {
72+
const name = node.parent.arguments[0];
73+
// Replace the module name with the raw module name as we can't predict which punctuation the user is going to use
74+
return fixer.replaceText(
75+
name,
76+
name.raw.replace(moduleName, correctModuleName)
77+
);
78+
} else {
79+
const name = node.source;
80+
return fixer.replaceText(
81+
name,
82+
name.raw.replace(moduleName, correctModuleName)
83+
);
84+
}
85+
},
86+
});
87+
} else {
88+
context.report({
89+
node,
90+
messageId: 'noDomImport',
91+
});
92+
}
93+
}
5194
},
5295
};

tests/__snapshots__/index.test.js.snap

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ Object {
99
"testing-library/await-async-query": "error",
1010
"testing-library/no-await-sync-query": "error",
1111
"testing-library/no-debug": "warn",
12+
"testing/library/no-dom-import": Array [
13+
"error",
14+
"angular",
15+
],
1216
},
1317
}
1418
`;
@@ -22,6 +26,10 @@ Object {
2226
"testing-library/await-async-query": "error",
2327
"testing-library/no-await-sync-query": "error",
2428
"testing-library/no-debug": "warn",
29+
"testing/library/no-dom-import": Array [
30+
"error",
31+
"react",
32+
],
2533
},
2634
}
2735
`;
@@ -47,6 +55,10 @@ Object {
4755
"testing-library/await-async-query": "error",
4856
"testing-library/no-await-sync-query": "error",
4957
"testing-library/no-debug": "warn",
58+
"testing/library/no-dom-import": Array [
59+
"error",
60+
"vue",
61+
],
5062
},
5163
}
5264
`;

tests/lib/rules/no-dom-import.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,30 @@ ruleTester.run('no-dom-import', rule, {
3434
messageId: 'noDomImport',
3535
},
3636
],
37+
output: 'import { fireEvent } from "dom-testing-library"',
38+
},
39+
{
40+
code: 'import { fireEvent } from "dom-testing-library"',
41+
options: ['react'],
42+
errors: [
43+
{
44+
message:
45+
'import from DOM Testing Library is restricted, import from react-testing-library instead',
46+
},
47+
],
48+
output: `import { fireEvent } from "react-testing-library"`,
49+
},
50+
// Single quote or double quotes should not be replaced
51+
{
52+
code: `import { fireEvent } from 'dom-testing-library'`,
53+
options: ['react'],
54+
errors: [
55+
{
56+
message:
57+
'import from DOM Testing Library is restricted, import from react-testing-library instead',
58+
},
59+
],
60+
output: `import { fireEvent } from 'react-testing-library'`,
3761
},
3862
{
3963
code: 'import * as testing from "dom-testing-library"',
@@ -91,6 +115,17 @@ ruleTester.run('no-dom-import', rule, {
91115
},
92116
],
93117
},
118+
{
119+
code: 'const { fireEvent } = require("@testing-library/dom")',
120+
options: ['vue'],
121+
errors: [
122+
{
123+
message:
124+
'import from DOM Testing Library is restricted, import from @testing-library/vue instead',
125+
},
126+
],
127+
output: 'const { fireEvent } = require("@testing-library/vue")',
128+
},
94129
{
95130
code: 'require("dom-testing-library")',
96131
errors: [

0 commit comments

Comments
 (0)