diff --git a/README.md b/README.md index 1461a975..e14a4485 100644 --- a/README.md +++ b/README.md @@ -120,6 +120,7 @@ To enable this configuration use the `extends` property in your | [await-async-query](docs/rules/await-async-query.md) | Enforce async queries to have proper `await` | ![recommended][] | | | [no-await-sync-query](docs/rules/no-await-sync-query.md) | Disallow unnecessary `await` for sync queries | ![recommended][] | | | [no-debug](docs/rules/no-debug.md) | Disallow the use of `debug` | | ![angular][] ![react][] ![vue][] | +| [no-dom-import](docs/rules/no-dom-import.md) | Disallow importing from DOM Testing Library | | ![angular][] ![react][] ![vue][] | [recommended]: https://img.shields.io/badge/recommended-lightgrey?style=flat-square [angular]: https://img.shields.io/badge/-Angular-black?style=flat-square&logo=angular&logoColor=white&labelColor=DD0031&color=black diff --git a/docs/rules/no-dom-import.md b/docs/rules/no-dom-import.md new file mode 100644 index 00000000..0237c9a9 --- /dev/null +++ b/docs/rules/no-dom-import.md @@ -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) diff --git a/lib/rules/no-dom-import.js b/lib/rules/no-dom-import.js new file mode 100644 index 00000000..1f73ac96 --- /dev/null +++ b/lib/rules/no-dom-import.js @@ -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, + 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', + }); + } + }, + }; + }, +}; diff --git a/tests/lib/rules/no-dom-import.js b/tests/lib/rules/no-dom-import.js new file mode 100644 index 00000000..9bebc4c8 --- /dev/null +++ b/tests/lib/rules/no-dom-import.js @@ -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', + }, + ], + }, + ], +});