-
Notifications
You must be signed in to change notification settings - Fork 150
Feature/rule no dom import #11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
# Disallow importing from DOM Testing Library | ||
|
||
Ensure that there are no direct imports from `@testing-library/dom` or | ||
`@testing-library/dom` when using some testing library framework | ||
wrapper. | ||
|
||
## Rule Details | ||
|
||
Testing Library framework wrappers as React Testing Library already | ||
re-exports everything from DOM Testing Library so you always have to | ||
import DOM Testing Library utils from corresponding framework wrapper | ||
module to: | ||
|
||
- use proper extended version of some of those methods containing | ||
additional functionality related to specific framework (e.g. | ||
`fireEvent` util) | ||
- avoid importing from extraneous dependencies (similar to | ||
eslint-plugin-import) | ||
|
||
This rule aims to prevent users from import anything directly from | ||
`@testing-library/dom` (or `dom-testing-library`) and it's useful for | ||
new starters or when IDEs autoimport from wrong module. | ||
|
||
Examples of **incorrect** code for this rule: | ||
|
||
```js | ||
import { fireEvent } from 'dom-testing-library'; | ||
``` | ||
|
||
```js | ||
import { fireEvent } from '@testing-library/dom'; | ||
``` | ||
|
||
```js | ||
const { fireEvent } = require('dom-testing-library'); | ||
``` | ||
|
||
```js | ||
const { fireEvent } = require('@testing-library/dom'); | ||
``` | ||
|
||
Examples of **correct** code for this rule: | ||
|
||
```js | ||
import { fireEvent } from 'react-testing-library'; | ||
``` | ||
|
||
```js | ||
import { fireEvent } from '@testing-library/react'; | ||
``` | ||
|
||
```js | ||
const { fireEvent } = require('react-testing-library'); | ||
``` | ||
|
||
```js | ||
const { fireEvent } = require('@testing-library/react'); | ||
``` | ||
|
||
## Further Reading | ||
|
||
- [Angular Testing Library API](https://testing-library.com/docs/angular-testing-library/api) | ||
- [React Testing Library API](https://testing-library.com/docs/react-testing-library/api) | ||
- [Vue Testing Library API](https://testing-library.com/docs/vue-testing-library/api) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
'use strict'; | ||
|
||
const { getDocsUrl } = require('../utils'); | ||
|
||
const DOM_TESTING_LIBRARY_MODULES = [ | ||
'dom-testing-library', | ||
'@testing-library/dom', | ||
]; | ||
|
||
module.exports = { | ||
meta: { | ||
type: 'problem', | ||
docs: { | ||
description: 'Disallow importing from DOM Testing Library', | ||
category: 'Best Practices', | ||
recommended: false, | ||
url: getDocsUrl('no-dom-import'), | ||
}, | ||
messages: { | ||
noDomImport: | ||
'import from DOM Testing Library is restricted, import from corresponding Testing library framework instead', | ||
}, | ||
fixable: null, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be awesome (and I think pretty easy though I'm not sure of that) to include a fix for this rule. I can look into it if you want. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It should, and I was thinking about it but I prefer to leave the fixable for a future version. Every time I think about this one it seems more complex to implement as you don't know which TL framework the query must be imported (don't think about react only, this works for vue and angular). So for fixing the rule we could try to guess from where the user should import it by looking for other "@testing-library/" text and get that suffix to replace it by "@testing-library/dom" but then... what if it's the first import in the file referring to testing library? We can't guess from where we have to reimport. Did you have something else in mind for this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh yes that's true. This is the same problem than above, I have two potential solutions 👆 |
||
schema: [], | ||
}, | ||
|
||
create: function(context) { | ||
return { | ||
ImportDeclaration(node) { | ||
if (DOM_TESTING_LIBRARY_MODULES.includes(node.source.value)) { | ||
context.report({ | ||
node, | ||
messageId: 'noDomImport', | ||
}); | ||
} | ||
}, | ||
|
||
[`CallExpression > Identifier[name="require"]`](node) { | ||
const { arguments: args } = node.parent; | ||
|
||
if ( | ||
args.some(args => DOM_TESTING_LIBRARY_MODULES.includes(args.value)) | ||
) { | ||
context.report({ | ||
node, | ||
messageId: 'noDomImport', | ||
}); | ||
} | ||
}, | ||
}; | ||
}, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
'use strict'; | ||
|
||
const rule = require('../../../lib/rules/no-dom-import'); | ||
const RuleTester = require('eslint').RuleTester; | ||
|
||
const ruleTester = new RuleTester({ | ||
parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, | ||
}); | ||
|
||
ruleTester.run('no-dom-import', rule, { | ||
valid: [ | ||
{ code: 'import { foo } from "foo"' }, | ||
{ code: 'import "foo"' }, | ||
{ code: 'import { fireEvent } from "react-testing-library"' }, | ||
{ code: 'import * as testing from "react-testing-library"' }, | ||
{ code: 'import { fireEvent } from "@testing-library/react"' }, | ||
{ code: 'import * as testing from "@testing-library/react"' }, | ||
{ code: 'import "react-testing-library"' }, | ||
{ code: 'import "@testing-library/react"' }, | ||
{ code: 'const { foo } = require("foo")' }, | ||
{ code: 'require("foo")' }, | ||
{ code: 'require("")' }, | ||
{ code: 'require()' }, | ||
{ code: 'const { fireEvent } = require("react-testing-library")' }, | ||
{ code: 'const { fireEvent } = require("@testing-library/react")' }, | ||
{ code: 'require("react-testing-library")' }, | ||
{ code: 'require("@testing-library/react")' }, | ||
], | ||
invalid: [ | ||
{ | ||
code: 'import { fireEvent } from "dom-testing-library"', | ||
errors: [ | ||
{ | ||
messageId: 'noDomImport', | ||
}, | ||
], | ||
}, | ||
{ | ||
code: 'import * as testing from "dom-testing-library"', | ||
errors: [ | ||
{ | ||
messageId: 'noDomImport', | ||
}, | ||
], | ||
}, | ||
{ | ||
code: 'import { fireEvent } from "@testing-library/dom"', | ||
errors: [ | ||
{ | ||
messageId: 'noDomImport', | ||
}, | ||
], | ||
}, | ||
{ | ||
code: 'import * as testing from "@testing-library/dom"', | ||
errors: [ | ||
{ | ||
messageId: 'noDomImport', | ||
}, | ||
], | ||
}, | ||
{ | ||
code: 'import "dom-testing-library"', | ||
errors: [ | ||
{ | ||
messageId: 'noDomImport', | ||
}, | ||
], | ||
}, | ||
{ | ||
code: 'import "@testing-library/dom"', | ||
errors: [ | ||
{ | ||
messageId: 'noDomImport', | ||
}, | ||
], | ||
}, | ||
{ | ||
code: 'const { fireEvent } = require("dom-testing-library")', | ||
errors: [ | ||
{ | ||
messageId: 'noDomImport', | ||
}, | ||
], | ||
}, | ||
{ | ||
code: 'const { fireEvent } = require("@testing-library/dom")', | ||
errors: [ | ||
{ | ||
messageId: 'noDomImport', | ||
}, | ||
], | ||
}, | ||
{ | ||
code: 'require("dom-testing-library")', | ||
errors: [ | ||
{ | ||
messageId: 'noDomImport', | ||
}, | ||
], | ||
}, | ||
{ | ||
code: 'require("@testing-library/dom")', | ||
errors: [ | ||
{ | ||
messageId: 'noDomImport', | ||
}, | ||
], | ||
}, | ||
], | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error message sounds unclear to me. What do you think?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I'm not convinced at all about it but I can't suggest which module should be imported. What I can do is to check which DTL module is used (
dom-testing-library
or@testing-library/dom
) and say something likeimport directly from '@testing-library/dom' is restricted, instead you must import from '@testing-library/<framework>'
by literally leaving that<framework>
placeholderThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I would think of something like that too. I see two solutions possible to that:
However, this would require the user to add the framework to each rule where we need to differentiate the frameworks.
And in
generateRecommendedRules
:Where
rule
would be a higher-order function that returns the proper rule configuration based on the framework:What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤦♂️ of course, didn't think about context options! Really interesting approach, I'd go for something intermediate between those 2 options: we add the framework option to this rule through context.options (so you receive it within
create
function through context arg and pass it to report message, as this seems the standard instead of doing the whole workaround to pass it to the rule function itself) and then we setup automatically this option on each recommended framework config (so it's automatically setup for those frameworks but everyone can customize it when adding the rule manually).So I'm gonna merge this one (as I wanted to improve the error message but obviously it's gonna be way better when the rule options are received so I'm gonna skip that for now) and create an issue for you to handle that separately, but only for receiving the context.options and improve the message with proper framework setup, nothing yet about fixable. Are you happy with that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Brilliant!