From 4d35d54369859fa5f591ec9c8712976454ee368e Mon Sep 17 00:00:00 2001 From: Anton Niklasson Date: Sun, 15 Nov 2020 14:10:24 +0100 Subject: [PATCH 01/16] add a first rough draft of prefer-in-document --- src/__tests__/lib/rules/prefer-in-document.js | 70 +++++++++++++++++++ src/rules/prefer-in-document.js | 63 +++++++++++++++++ 2 files changed, 133 insertions(+) create mode 100644 src/__tests__/lib/rules/prefer-in-document.js create mode 100644 src/rules/prefer-in-document.js diff --git a/src/__tests__/lib/rules/prefer-in-document.js b/src/__tests__/lib/rules/prefer-in-document.js new file mode 100644 index 0000000..2e8d0fb --- /dev/null +++ b/src/__tests__/lib/rules/prefer-in-document.js @@ -0,0 +1,70 @@ +/* eslint-disable no-template-curly-in-string */ +/** + * @fileoverview Prefer toBeInTheDocument over querying and asserting length. + * @author Anton Niklasson + */ + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +import { RuleTester } from "eslint"; +import * as rule from "../../../rules/prefer-in-document"; + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +const queryStrats = [ + "ByLabelText", + "ByPlaceholderText", + "ByText", + "ByAltText", + "ByTitle", + "ByDisplayValue", + "ByRole", + "ByTestId", +]; +const queries = [ + "get", + "getAll", + "query", + "queryAll", + "find", + "findAll", +].reduce((acc, q) => { + return [...acc, ...queryStrats.map((qs) => q + qs)]; +}, []); + +const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2015 } }); +ruleTester.run("prefer-in-document", rule, { + valid: [ + ...queries.map((q) => `expect(screen.${q}('foo')).toBeInTheDocument()`), + { + code: `expect(screen.notAQuery('foo-bar')).toHaveLength(1)`, + errors: [ + { + messageId: "useDocument", + }, + ], + }, + { + code: `expect(screen.getByText('foo-bar')).toHaveLength(2)`, + errors: [ + { + messageId: "useDocument", + }, + ], + }, + ], + invalid: [ + ...queries.map((q) => ({ + code: `expect(screen.${q}('foo')).toHaveLength(1)`, + errors: [ + { + messageId: "useDocument", + }, + ], + })), + ], +}); diff --git a/src/rules/prefer-in-document.js b/src/rules/prefer-in-document.js new file mode 100644 index 0000000..93eef60 --- /dev/null +++ b/src/rules/prefer-in-document.js @@ -0,0 +1,63 @@ +/** + * @fileoverview prefer toBeInTheDocument over checking getAttribute/hasAttribute + * @author Anton Niklasson + */ + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ + +export const meta = { + type: "suggestion", + docs: { + category: "jest-dom", + description: + "Prefer .toBeInTheDocument() in favor of checking the length of the result using .toHaveLength(1)", + url: "prefer-in-document", + recommended: false, + }, + messages: { + useDocument: `Prefer .toBeInTheDocument() in favor of .toHaveLength(1)`, + }, +}; + +/* eslint-disable no-console */ + +const queryStrats = [ + "ByLabelText", + "ByPlaceholderText", + "ByText", + "ByAltText", + "ByTitle", + "ByDisplayValue", + "ByRole", + "ByTestId", +]; +const queries = [ + "get", + "getAll", + "query", + "queryAll", + "find", + "findAll", +].reduce((acc, q) => { + return [...acc, ...queryStrats.map((qs) => q + qs)]; +}, []); + +export const create = (context) => { + return { + [`ExpressionStatement[expression.arguments.0.value=1][expression.callee.object.callee.name='expect'][expression.callee.property.name='toHaveLength']`]( + node + ) { + const screenQuery = + node.expression.callee.object.arguments[0].callee.property.name; + + if (queries.includes(screenQuery)) { + context.report({ + node: node.expression.callee.property, + messageId: "useDocument", + }); + } + }, + }; +}; From 86b23c75d7be9115766fb6d3b57cb3d5e2b4cfb1 Mon Sep 17 00:00:00 2001 From: Anton Niklasson Date: Sun, 15 Nov 2020 14:34:23 +0100 Subject: [PATCH 02/16] add docs for the new rule --- README.md | 23 +++++++++++++---------- docs/rules/prefer-in-document.md | 27 +++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 10 deletions(-) create mode 100644 docs/rules/prefer-in-document.md diff --git a/README.md b/README.md index d8e7826..8049f2c 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ + - [Installation](#installation) - [Usage](#usage) - [Recommended Configuration](#recommended-configuration) @@ -101,16 +102,17 @@ module.exports = { -| Name | 👍 | 🔧 | Description | -| ---------------------------------------------------------------------------------------------------------------------------------------------- | --- | --- | -------------------------------------------------------------- | -| [prefer-checked](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-checked.md) | 👍 | 🔧 | prefer toBeChecked over checking attributes | -| [prefer-empty](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-empty.md) | 👍 | 🔧 | Prefer toBeEmpty over checking innerHTML | -| [prefer-enabled-disabled](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-enabled-disabled.md) | 👍 | 🔧 | prefer toBeDisabled or toBeEnabled over checking attributes | -| [prefer-focus](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-focus.md) | 👍 | 🔧 | prefer toHaveFocus over checking document.activeElement | -| [prefer-required](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-required.md) | 👍 | 🔧 | prefer toBeRequired over checking properties | -| [prefer-to-have-attribute](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-to-have-attribute.md) | 👍 | 🔧 | prefer toHaveAttribute over checking getAttribute/hasAttribute | -| [prefer-to-have-style](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-to-have-style.md) | 👍 | 🔧 | prefer toHaveStyle over checking element style | -| [prefer-to-have-text-content](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-to-have-text-content.md) | 👍 | 🔧 | Prefer toHaveTextContent over checking element.textContent | +| Name | 👍 | 🔧 | Description | +| ---------------------------------------------------------------------------------------------------------------------------------------------- | --- | --- | ---------------------------------------------------------------------------------- | +| [prefer-checked](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-checked.md) | 👍 | 🔧 | prefer toBeChecked over checking attributes | +| [prefer-empty](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-empty.md) | 👍 | 🔧 | Prefer toBeEmpty over checking innerHTML | +| [prefer-enabled-disabled](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-enabled-disabled.md) | 👍 | 🔧 | prefer toBeDisabled or toBeEnabled over checking attributes | +| [prefer-focus](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-focus.md) | 👍 | 🔧 | prefer toHaveFocus over checking document.activeElement | +| [prefer-required](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-required.md) | 👍 | 🔧 | prefer toBeRequired over checking properties | +| [prefer-to-have-attribute](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-to-have-attribute.md) | 👍 | 🔧 | prefer toHaveAttribute over checking getAttribute/hasAttribute | +| [prefer-to-have-style](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-to-have-style.md) | 👍 | 🔧 | prefer toHaveStyle over checking element style | +| [prefer-to-have-text-content](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-to-have-text-content.md) | 👍 | 🔧 | Prefer toHaveTextContent over checking element.textContent | +| [prefer-in-document](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-in-document.md) | | | Prefer asserting DOM node presence with .toBeInTheDocument() over .toHaveLength(1) | @@ -158,6 +160,7 @@ Thanks goes to these people ([emoji key][emojis]): + This project follows the [all-contributors][all-contributors] specification. diff --git a/docs/rules/prefer-in-document.md b/docs/rules/prefer-in-document.md new file mode 100644 index 0000000..af99637 --- /dev/null +++ b/docs/rules/prefer-in-document.md @@ -0,0 +1,27 @@ +# Prefer asserting DOM node presence with .toBeInTheDocument over .toHaveLength (prefer-in-document) + +## Rule Details + +This rule enforces checking existance of DOM nodes using `.toBeInTheDocument()` +instead of asserting on the length of a query result using `.toHaveLength(1)`. + +Examples of **incorrect** code for this rule: + +```js +expect(screen.queryByText("foo")).toHaveLength(1); +``` + +Examples of **correct** code for this rule: + +```js +expect(screen.queryByText("foo")).toBeInTheDocument(); +``` + +## When Not To Use It + +Don't use this rule if you don't care about the added readability and +improvements that `toBeInTheDocument` offers to your expects. + +## Further Reading + +- [Docs on toBeInTheDocument](https://github.com/testing-library/jest-dom#tobeinthedocument) From 88cd02c6c4a80674f88f02f54da6fdffb3f13144 Mon Sep 17 00:00:00 2001 From: Anton Niklasson Date: Sun, 15 Nov 2020 19:51:54 +0100 Subject: [PATCH 03/16] make the rule fixable --- README.md | 3 +- docs/rules/prefer-in-document.md | 2 +- src/__tests__/lib/rules/prefer-in-document.js | 23 +---------- src/queryNames.js | 27 +++++++++++++ src/rules/prefer-in-document.js | 39 +++++++------------ 5 files changed, 45 insertions(+), 49 deletions(-) create mode 100644 src/queryNames.js diff --git a/README.md b/README.md index 8049f2c..9a3eb06 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,6 @@ - - [Installation](#installation) - [Usage](#usage) - [Recommended Configuration](#recommended-configuration) @@ -112,7 +111,7 @@ module.exports = { | [prefer-to-have-attribute](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-to-have-attribute.md) | 👍 | 🔧 | prefer toHaveAttribute over checking getAttribute/hasAttribute | | [prefer-to-have-style](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-to-have-style.md) | 👍 | 🔧 | prefer toHaveStyle over checking element style | | [prefer-to-have-text-content](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-to-have-text-content.md) | 👍 | 🔧 | Prefer toHaveTextContent over checking element.textContent | -| [prefer-in-document](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-in-document.md) | | | Prefer asserting DOM node presence with .toBeInTheDocument() over .toHaveLength(1) | +| [prefer-in-document](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-in-document.md) | 👍 | 🔧 | Prefer asserting DOM node presence with .toBeInTheDocument() over .toHaveLength(1) | diff --git a/docs/rules/prefer-in-document.md b/docs/rules/prefer-in-document.md index af99637..84d6a4e 100644 --- a/docs/rules/prefer-in-document.md +++ b/docs/rules/prefer-in-document.md @@ -1,4 +1,4 @@ -# Prefer asserting DOM node presence with .toBeInTheDocument over .toHaveLength (prefer-in-document) +# Prefer .toBeInTheDocument in favor of .toHaveLength(1) (prefer-in-document) ## Rule Details diff --git a/src/__tests__/lib/rules/prefer-in-document.js b/src/__tests__/lib/rules/prefer-in-document.js index 2e8d0fb..6e87edc 100644 --- a/src/__tests__/lib/rules/prefer-in-document.js +++ b/src/__tests__/lib/rules/prefer-in-document.js @@ -9,33 +9,13 @@ //------------------------------------------------------------------------------ import { RuleTester } from "eslint"; +import { queries } from "../../../queryNames"; import * as rule from "../../../rules/prefer-in-document"; //------------------------------------------------------------------------------ // Tests //------------------------------------------------------------------------------ -const queryStrats = [ - "ByLabelText", - "ByPlaceholderText", - "ByText", - "ByAltText", - "ByTitle", - "ByDisplayValue", - "ByRole", - "ByTestId", -]; -const queries = [ - "get", - "getAll", - "query", - "queryAll", - "find", - "findAll", -].reduce((acc, q) => { - return [...acc, ...queryStrats.map((qs) => q + qs)]; -}, []); - const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2015 } }); ruleTester.run("prefer-in-document", rule, { valid: [ @@ -65,6 +45,7 @@ ruleTester.run("prefer-in-document", rule, { messageId: "useDocument", }, ], + output: `expect(screen.${q}('foo')).toBeInTheDocument()`, })), ], }); diff --git a/src/queryNames.js b/src/queryNames.js new file mode 100644 index 0000000..ba5b55f --- /dev/null +++ b/src/queryNames.js @@ -0,0 +1,27 @@ +function constructQueries() { + const strategies = [ + "LabelText", + "PlaceholderText", + "Text", + "AltText", + "Title", + "DisplayValue", + "Role", + "TestId", + ]; + + const by = [ + "getBy", + "getAllBy", + "queryBy", + "queryAllBy", + "findBy", + "findAllBy", + ]; + + return by.reduce((acc, q) => { + return [...acc, ...strategies.map((qs) => q + qs)]; + }, []); +} + +export const queries = constructQueries(); diff --git a/src/rules/prefer-in-document.js b/src/rules/prefer-in-document.js index 93eef60..11add9d 100644 --- a/src/rules/prefer-in-document.js +++ b/src/rules/prefer-in-document.js @@ -3,6 +3,8 @@ * @author Anton Niklasson */ +import { queries } from "../queryNames"; + //------------------------------------------------------------------------------ // Rule Definition //------------------------------------------------------------------------------ @@ -16,6 +18,7 @@ export const meta = { url: "prefer-in-document", recommended: false, }, + fixable: "code", messages: { useDocument: `Prefer .toBeInTheDocument() in favor of .toHaveLength(1)`, }, @@ -23,39 +26,25 @@ export const meta = { /* eslint-disable no-console */ -const queryStrats = [ - "ByLabelText", - "ByPlaceholderText", - "ByText", - "ByAltText", - "ByTitle", - "ByDisplayValue", - "ByRole", - "ByTestId", -]; -const queries = [ - "get", - "getAll", - "query", - "queryAll", - "find", - "findAll", -].reduce((acc, q) => { - return [...acc, ...queryStrats.map((qs) => q + qs)]; -}, []); - export const create = (context) => { return { - [`ExpressionStatement[expression.arguments.0.value=1][expression.callee.object.callee.name='expect'][expression.callee.property.name='toHaveLength']`]( + [`CallExpression[callee.object.callee.name='expect'][callee.property.name='toHaveLength'][arguments.0.value=1]`]( node ) { - const screenQuery = - node.expression.callee.object.arguments[0].callee.property.name; + const screenQuery = node.callee.object.arguments[0].callee.property.name; + const toHaveLengthNode = node.callee.property; if (queries.includes(screenQuery)) { context.report({ - node: node.expression.callee.property, + node: node.callee, messageId: "useDocument", + loc: toHaveLengthNode.loc, + fix(fixer) { + return [ + fixer.replaceText(toHaveLengthNode, "toBeInTheDocument"), + fixer.remove(node.arguments[0]), + ]; + }, }); } }, From 8dccaa83284b7f53db171b3fc8c050583d0c9605 Mon Sep 17 00:00:00 2001 From: Anton Niklasson Date: Wed, 18 Nov 2020 08:10:22 +0100 Subject: [PATCH 04/16] support destructed query calls --- src/__tests__/lib/rules/prefer-in-document.js | 32 ++++++++++++++----- src/rules/prefer-in-document.js | 5 +-- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/__tests__/lib/rules/prefer-in-document.js b/src/__tests__/lib/rules/prefer-in-document.js index 6e87edc..cfd6802 100644 --- a/src/__tests__/lib/rules/prefer-in-document.js +++ b/src/__tests__/lib/rules/prefer-in-document.js @@ -19,7 +19,13 @@ import * as rule from "../../../rules/prefer-in-document"; const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2015 } }); ruleTester.run("prefer-in-document", rule, { valid: [ - ...queries.map((q) => `expect(screen.${q}('foo')).toBeInTheDocument()`), + ...queries + .map((q) => [ + `expect(screen.${q}('foo')).toBeInTheDocument()`, + `expect(${q}('foo')).toBeInTheDocument()`, + `expect(wrapper.${q}('foo')).toBeInTheDocument()`, + ]) + .flat(), { code: `expect(screen.notAQuery('foo-bar')).toHaveLength(1)`, errors: [ @@ -38,14 +44,24 @@ ruleTester.run("prefer-in-document", rule, { }, ], invalid: [ - ...queries.map((q) => ({ - code: `expect(screen.${q}('foo')).toHaveLength(1)`, - errors: [ + ...queries + .map((q) => [ { - messageId: "useDocument", + code: `expect(screen.${q}('foo')).toHaveLength(1)`, + errors: 1, + output: `expect(screen.${q}('foo')).toBeInTheDocument()`, }, - ], - output: `expect(screen.${q}('foo')).toBeInTheDocument()`, - })), + { + code: `expect(${q}('foo')).toHaveLength(1)`, + errors: 1, + output: `expect(${q}('foo')).toBeInTheDocument()`, + }, + { + code: `expect(wrapper.${q}('foo')).toHaveLength(1)`, + errors: 1, + output: `expect(wrapper.${q}('foo')).toBeInTheDocument()`, + }, + ]) + .flat(), ], }); diff --git a/src/rules/prefer-in-document.js b/src/rules/prefer-in-document.js index 11add9d..ff244c7 100644 --- a/src/rules/prefer-in-document.js +++ b/src/rules/prefer-in-document.js @@ -31,10 +31,11 @@ export const create = (context) => { [`CallExpression[callee.object.callee.name='expect'][callee.property.name='toHaveLength'][arguments.0.value=1]`]( node ) { - const screenQuery = node.callee.object.arguments[0].callee.property.name; + const queryNode = node.callee.object.arguments[0].callee; + const query = queryNode.name || queryNode.property.name; const toHaveLengthNode = node.callee.property; - if (queries.includes(screenQuery)) { + if (queries.includes(query)) { context.report({ node: node.callee, messageId: "useDocument", From 4bf9fc1fece45172cd0422fc085b1034d4e981a9 Mon Sep 17 00:00:00 2001 From: Anton Niklasson Date: Wed, 18 Nov 2020 17:48:53 +0100 Subject: [PATCH 05/16] pull in query names from @testing-library/dom --- package.json | 4 ++- src/__tests__/lib/rules/prefer-in-document.js | 6 ++--- src/queryNames.js | 27 ------------------- src/rules/prefer-in-document.js | 10 ++----- 4 files changed, 8 insertions(+), 39 deletions(-) delete mode 100644 src/queryNames.js diff --git a/package.json b/package.json index 3e5396d..b974ae1 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "requireindex": "^1.2.0" }, "devDependencies": { + "@testing-library/dom": "^7.27.1", "eslint": "6.8", "jest-extended": "^0.11.5", "kcd-scripts": "6.5.1" @@ -53,7 +54,8 @@ "extends": "./node_modules/kcd-scripts/eslint.js", "rules": { "babel/quotes": "off", - "max-lines-per-function": "off" + "max-lines-per-function": "off", + "testing-library/no-dom-import": "off" } }, "eslintIgnore": [ diff --git a/src/__tests__/lib/rules/prefer-in-document.js b/src/__tests__/lib/rules/prefer-in-document.js index cfd6802..3bb0648 100644 --- a/src/__tests__/lib/rules/prefer-in-document.js +++ b/src/__tests__/lib/rules/prefer-in-document.js @@ -9,7 +9,7 @@ //------------------------------------------------------------------------------ import { RuleTester } from "eslint"; -import { queries } from "../../../queryNames"; +import { queries } from "@testing-library/dom"; import * as rule from "../../../rules/prefer-in-document"; //------------------------------------------------------------------------------ @@ -19,7 +19,7 @@ import * as rule from "../../../rules/prefer-in-document"; const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2015 } }); ruleTester.run("prefer-in-document", rule, { valid: [ - ...queries + ...Object.keys(queries) .map((q) => [ `expect(screen.${q}('foo')).toBeInTheDocument()`, `expect(${q}('foo')).toBeInTheDocument()`, @@ -44,7 +44,7 @@ ruleTester.run("prefer-in-document", rule, { }, ], invalid: [ - ...queries + ...Object.keys(queries) .map((q) => [ { code: `expect(screen.${q}('foo')).toHaveLength(1)`, diff --git a/src/queryNames.js b/src/queryNames.js deleted file mode 100644 index ba5b55f..0000000 --- a/src/queryNames.js +++ /dev/null @@ -1,27 +0,0 @@ -function constructQueries() { - const strategies = [ - "LabelText", - "PlaceholderText", - "Text", - "AltText", - "Title", - "DisplayValue", - "Role", - "TestId", - ]; - - const by = [ - "getBy", - "getAllBy", - "queryBy", - "queryAllBy", - "findBy", - "findAllBy", - ]; - - return by.reduce((acc, q) => { - return [...acc, ...strategies.map((qs) => q + qs)]; - }, []); -} - -export const queries = constructQueries(); diff --git a/src/rules/prefer-in-document.js b/src/rules/prefer-in-document.js index ff244c7..3d24158 100644 --- a/src/rules/prefer-in-document.js +++ b/src/rules/prefer-in-document.js @@ -3,11 +3,7 @@ * @author Anton Niklasson */ -import { queries } from "../queryNames"; - -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ +import { queries } from "@testing-library/dom"; export const meta = { type: "suggestion", @@ -24,8 +20,6 @@ export const meta = { }, }; -/* eslint-disable no-console */ - export const create = (context) => { return { [`CallExpression[callee.object.callee.name='expect'][callee.property.name='toHaveLength'][arguments.0.value=1]`]( @@ -35,7 +29,7 @@ export const create = (context) => { const query = queryNode.name || queryNode.property.name; const toHaveLengthNode = node.callee.property; - if (queries.includes(query)) { + if (Object.keys(queries).includes(query)) { context.report({ node: node.callee, messageId: "useDocument", From 6e9ccd2c7467daa1a74634a8769fe65857719101 Mon Sep 17 00:00:00 2001 From: Anton Niklasson Date: Wed, 18 Nov 2020 17:49:03 +0100 Subject: [PATCH 06/16] npm run generate-readme-table --- README.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 9a3eb06..f30c4c2 100644 --- a/README.md +++ b/README.md @@ -101,17 +101,17 @@ module.exports = { -| Name | 👍 | 🔧 | Description | -| ---------------------------------------------------------------------------------------------------------------------------------------------- | --- | --- | ---------------------------------------------------------------------------------- | -| [prefer-checked](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-checked.md) | 👍 | 🔧 | prefer toBeChecked over checking attributes | -| [prefer-empty](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-empty.md) | 👍 | 🔧 | Prefer toBeEmpty over checking innerHTML | -| [prefer-enabled-disabled](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-enabled-disabled.md) | 👍 | 🔧 | prefer toBeDisabled or toBeEnabled over checking attributes | -| [prefer-focus](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-focus.md) | 👍 | 🔧 | prefer toHaveFocus over checking document.activeElement | -| [prefer-required](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-required.md) | 👍 | 🔧 | prefer toBeRequired over checking properties | -| [prefer-to-have-attribute](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-to-have-attribute.md) | 👍 | 🔧 | prefer toHaveAttribute over checking getAttribute/hasAttribute | -| [prefer-to-have-style](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-to-have-style.md) | 👍 | 🔧 | prefer toHaveStyle over checking element style | -| [prefer-to-have-text-content](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-to-have-text-content.md) | 👍 | 🔧 | Prefer toHaveTextContent over checking element.textContent | -| [prefer-in-document](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-in-document.md) | 👍 | 🔧 | Prefer asserting DOM node presence with .toBeInTheDocument() over .toHaveLength(1) | +| Name | 👍 | 🔧 | Description | +| ---------------------------------------------------------------------------------------------------------------------------------------------- | --- | --- | ------------------------------------------------------------------------------------------------ | +| [prefer-checked](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-checked.md) | 👍 | 🔧 | prefer toBeChecked over checking attributes | +| [prefer-empty](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-empty.md) | 👍 | 🔧 | Prefer toBeEmpty over checking innerHTML | +| [prefer-enabled-disabled](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-enabled-disabled.md) | 👍 | 🔧 | prefer toBeDisabled or toBeEnabled over checking attributes | +| [prefer-focus](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-focus.md) | 👍 | 🔧 | prefer toHaveFocus over checking document.activeElement | +| [prefer-in-document](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-in-document.md) | | 🔧 | Prefer .toBeInTheDocument() in favor of checking the length of the result using .toHaveLength(1) | +| [prefer-required](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-required.md) | 👍 | 🔧 | prefer toBeRequired over checking properties | +| [prefer-to-have-attribute](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-to-have-attribute.md) | 👍 | 🔧 | prefer toHaveAttribute over checking getAttribute/hasAttribute | +| [prefer-to-have-style](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-to-have-style.md) | 👍 | 🔧 | prefer toHaveStyle over checking element style | +| [prefer-to-have-text-content](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-to-have-text-content.md) | 👍 | 🔧 | Prefer toHaveTextContent over checking element.textContent | From 9336aaea5a0bdd3a0062db1dbabccf0d77867592 Mon Sep 17 00:00:00 2001 From: Anton Niklasson Date: Wed, 18 Nov 2020 18:01:16 +0100 Subject: [PATCH 07/16] all valid cases should be strings --- src/__tests__/lib/rules/prefer-in-document.js | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/src/__tests__/lib/rules/prefer-in-document.js b/src/__tests__/lib/rules/prefer-in-document.js index 3bb0648..e418a78 100644 --- a/src/__tests__/lib/rules/prefer-in-document.js +++ b/src/__tests__/lib/rules/prefer-in-document.js @@ -26,22 +26,8 @@ ruleTester.run("prefer-in-document", rule, { `expect(wrapper.${q}('foo')).toBeInTheDocument()`, ]) .flat(), - { - code: `expect(screen.notAQuery('foo-bar')).toHaveLength(1)`, - errors: [ - { - messageId: "useDocument", - }, - ], - }, - { - code: `expect(screen.getByText('foo-bar')).toHaveLength(2)`, - errors: [ - { - messageId: "useDocument", - }, - ], - }, + `expect(screen.notAQuery('foo-bar')).toHaveLength(1)`, + `expect(screen.getByText('foo-bar')).toHaveLength(2)`, ], invalid: [ ...Object.keys(queries) From 525b16c571551ce17947f76fea4c839c890d6159 Mon Sep 17 00:00:00 2001 From: Anton Niklasson Date: Thu, 19 Nov 2020 22:46:43 +0100 Subject: [PATCH 08/16] split queries by variant, add invalid cases for query* --- src/__tests__/lib/rules/prefer-in-document.js | 73 ++++++++++++++----- src/queries.js | 9 +++ src/rules/prefer-in-document.js | 4 +- 3 files changed, 65 insertions(+), 21 deletions(-) create mode 100644 src/queries.js diff --git a/src/__tests__/lib/rules/prefer-in-document.js b/src/__tests__/lib/rules/prefer-in-document.js index e418a78..3b6c4c8 100644 --- a/src/__tests__/lib/rules/prefer-in-document.js +++ b/src/__tests__/lib/rules/prefer-in-document.js @@ -1,4 +1,3 @@ -/* eslint-disable no-template-curly-in-string */ /** * @fileoverview Prefer toBeInTheDocument over querying and asserting length. * @author Anton Niklasson @@ -9,17 +8,29 @@ //------------------------------------------------------------------------------ import { RuleTester } from "eslint"; -import { queries } from "@testing-library/dom"; +import { queries, queriesByVariant } from "../../../queries"; import * as rule from "../../../rules/prefer-in-document"; //------------------------------------------------------------------------------ // Tests //------------------------------------------------------------------------------ +function invalidCase(code, output) { + return { + code, + output, + errors: [ + { + messageId: "use-document", + }, + ], + }; +} + const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2015 } }); ruleTester.run("prefer-in-document", rule, { valid: [ - ...Object.keys(queries) + ...queries .map((q) => [ `expect(screen.${q}('foo')).toBeInTheDocument()`, `expect(${q}('foo')).toBeInTheDocument()`, @@ -30,23 +41,47 @@ ruleTester.run("prefer-in-document", rule, { `expect(screen.getByText('foo-bar')).toHaveLength(2)`, ], invalid: [ - ...Object.keys(queries) + // Invalid cases that applies to all variants + ...queries + .map((q) => [ + invalidCase( + `expect(screen.${q}('foo')).toHaveLength(1)`, + `expect(screen.${q}('foo')).toBeInTheDocument()` + ), + invalidCase( + `expect(${q}('foo')).toHaveLength(1)`, + `expect(${q}('foo')).toBeInTheDocument()` + ), + invalidCase( + `expect(wrapper.${q}('foo')).toHaveLength(1)`, + `expect(wrapper.${q}('foo')).toBeInTheDocument()` + ), + ]) + .flat(), + + // Invalid cases that applies to queryBy* and queryAllBy* + ...queriesByVariant.query .map((q) => [ - { - code: `expect(screen.${q}('foo')).toHaveLength(1)`, - errors: 1, - output: `expect(screen.${q}('foo')).toBeInTheDocument()`, - }, - { - code: `expect(${q}('foo')).toHaveLength(1)`, - errors: 1, - output: `expect(${q}('foo')).toBeInTheDocument()`, - }, - { - code: `expect(wrapper.${q}('foo')).toHaveLength(1)`, - errors: 1, - output: `expect(wrapper.${q}('foo')).toBeInTheDocument()`, - }, + invalidCase( + `expect(${q}('foo')).toHaveLength(0)`, + `expect(${q}('foo')).not.toBeInTheDocument()` + ), + invalidCase( + `expect(${q}('foo')).toBeNull()`, + `expect(${q}('foo')).not.toBeInTheDocument()` + ), + invalidCase( + `expect(${q}('foo')).not.toBeNull()`, + `expect(${q}('foo')).toBeInTheDocument()` + ), + invalidCase( + `expect(${q}('foo')).toBeDefined()`, + `expect(${q}('foo')).toBeInTheDocument()` + ), + invalidCase( + `expect(${q}('foo')).not.toBeDefined()`, + `expect(${q}('foo')).not.toBeInTheDocument()` + ), ]) .flat(), ], diff --git a/src/queries.js b/src/queries.js new file mode 100644 index 0000000..fecca5a --- /dev/null +++ b/src/queries.js @@ -0,0 +1,9 @@ +import { queries as allQueries } from "@testing-library/dom"; + +export const queries = Object.keys(allQueries); + +export const queriesByVariant = { + query: queries.filter((q) => q.startsWith("query")), + get: queries.filter((q) => q.startsWith("get")), + find: queries.filter((q) => q.startsWith("find")), +}; diff --git a/src/rules/prefer-in-document.js b/src/rules/prefer-in-document.js index 3d24158..5c6bf1c 100644 --- a/src/rules/prefer-in-document.js +++ b/src/rules/prefer-in-document.js @@ -16,7 +16,7 @@ export const meta = { }, fixable: "code", messages: { - useDocument: `Prefer .toBeInTheDocument() in favor of .toHaveLength(1)`, + "use-document": `Prefer .toBeInTheDocument() in favor of .toHaveLength(1)`, }, }; @@ -32,7 +32,7 @@ export const create = (context) => { if (Object.keys(queries).includes(query)) { context.report({ node: node.callee, - messageId: "useDocument", + messageId: "use-document", loc: toHaveLengthNode.loc, fix(fixer) { return [ From bd8d2a153e40cecea0ac1cf14401c5dc79d7414d Mon Sep 17 00:00:00 2001 From: Anton Niklasson Date: Sun, 22 Nov 2020 20:49:44 +0100 Subject: [PATCH 09/16] implement the code to pass the failing tests --- src/rules/prefer-in-document.js | 100 +++++++++++++++++++++++++++----- 1 file changed, 84 insertions(+), 16 deletions(-) diff --git a/src/rules/prefer-in-document.js b/src/rules/prefer-in-document.js index 5c6bf1c..54f904a 100644 --- a/src/rules/prefer-in-document.js +++ b/src/rules/prefer-in-document.js @@ -3,7 +3,7 @@ * @author Anton Niklasson */ -import { queries } from "@testing-library/dom"; +import { queries } from "../queries"; export const meta = { type: "suggestion", @@ -20,26 +20,94 @@ export const meta = { }, }; +function isAntonymMatcher(matcherNode, matcherArguments) { + return ( + matcherNode.name === "toBeNull" || + (matcherNode.name === "toHaveLength" && matcherArguments[0].value === 0) + ); +} + +function check( + context, + // eslint-disable-next-line no-unused-vars + { queryNode, matcherNode, matcherArguments, negatedMatcher } +) { + const query = queryNode.name || queryNode.property.name; + + // toHaveLength should only be invalid if the argument is 1 + if (matcherNode.name === "toHaveLength" && matcherArguments[0].value > 1) { + return; + } + + if (queries.includes(query)) { + context.report({ + node: matcherNode, + messageId: "use-document", + loc: matcherNode.loc, + fix(fixer) { + const operations = []; + + // Flip the .not if neccessary + if (isAntonymMatcher(matcherNode, matcherArguments)) { + if (negatedMatcher) { + operations.push( + fixer.removeRange([matcherNode.start - 5, matcherNode.start - 1]) + ); + } else { + operations.push(fixer.insertTextBefore(matcherNode, "not.")); + } + } + + // Replace the actual matcher + operations.push(fixer.replaceText(matcherNode, "toBeInTheDocument")); + + // Remove any arguments in the matcher + for (const argument of matcherArguments) { + operations.push(fixer.remove(argument)); + } + + return operations; + }, + }); + } +} + export const create = (context) => { + const alternativeMatchers = /(toHaveLength|toBeDefined|toBeNull)/; + return { - [`CallExpression[callee.object.callee.name='expect'][callee.property.name='toHaveLength'][arguments.0.value=1]`]( + // Grabbing expect().not. + [`CallExpression[callee.object.object.callee.name='expect'][callee.object.property.name='not'][callee.property.name=${alternativeMatchers}]`]( + node + ) { + const queryNode = node.callee.object.object.arguments[0].callee; + const matcherNode = node.callee.property; + const matcherArguments = node.arguments; + + if (queryNode && matcherNode) { + check(context, { + negatedMatcher: true, + queryNode, + matcherNode, + matcherArguments, + }); + } + }, + + // Grabbing expect(). + [`CallExpression[callee.object.callee.name='expect'][callee.property.name=${alternativeMatchers}]`]( node ) { const queryNode = node.callee.object.arguments[0].callee; - const query = queryNode.name || queryNode.property.name; - const toHaveLengthNode = node.callee.property; - - if (Object.keys(queries).includes(query)) { - context.report({ - node: node.callee, - messageId: "use-document", - loc: toHaveLengthNode.loc, - fix(fixer) { - return [ - fixer.replaceText(toHaveLengthNode, "toBeInTheDocument"), - fixer.remove(node.arguments[0]), - ]; - }, + const matcherNode = node.callee.property; + const matcherArguments = node.arguments; + + if (queryNode && matcherNode) { + check(context, { + negatedMatcher: false, + queryNode, + matcherNode, + matcherArguments, }); } }, From 0fad7d5f86604732607c0ad143990ab98d579e01 Mon Sep 17 00:00:00 2001 From: Anton Niklasson Date: Sun, 22 Nov 2020 20:55:49 +0100 Subject: [PATCH 10/16] update the rule docs --- docs/rules/prefer-in-document.md | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/docs/rules/prefer-in-document.md b/docs/rules/prefer-in-document.md index 84d6a4e..394a390 100644 --- a/docs/rules/prefer-in-document.md +++ b/docs/rules/prefer-in-document.md @@ -2,19 +2,32 @@ ## Rule Details -This rule enforces checking existance of DOM nodes using `.toBeInTheDocument()` -instead of asserting on the length of a query result using `.toHaveLength(1)`. +This rule enforces checking existance of DOM nodes using `.toBeInTheDocument()`. +The rule prefers that matcher over various existance checks such as `.toHaveLength(1)`, `.not.toBeNull()` and +similar. Examples of **incorrect** code for this rule: ```js expect(screen.queryByText("foo")).toHaveLength(1); +expect(queryByText("foo")).toHaveLength(1); +expect(wrapper.queryByText("foo")).toHaveLength(1); +expect(queryByText("foo")).toHaveLength(0); +expect(queryByText("foo")).toBeNull(); +expect(queryByText("foo")).not.toBeNull(); +expect(queryByText("foo")).toBeDefined(); +expect(queryByText("foo")).not.toBeDefined(); ``` Examples of **correct** code for this rule: ```js expect(screen.queryByText("foo")).toBeInTheDocument(); +expect(screen.queryByText("foo")).toBeInTheDocument(); +expect(queryByText("foo")).toBeInTheDocument()`; +expect(wrapper.queryAllByTestId('foo')).toBeInTheDocument()`; +expect(screen.getAllByLabel("foo-bar")).toHaveLength(2)`; +expect(notAQuery('foo-bar')).toHaveLength(1)`; ``` ## When Not To Use It From 4e2bc4eb26f663163ab2efcee022b9ca07859153 Mon Sep 17 00:00:00 2001 From: Anton Niklasson Date: Mon, 23 Nov 2020 10:23:33 +0100 Subject: [PATCH 11/16] get rid of Array.flat() to support node 10 --- src/__tests__/lib/rules/prefer-in-document.js | 108 +++++++++--------- 1 file changed, 52 insertions(+), 56 deletions(-) diff --git a/src/__tests__/lib/rules/prefer-in-document.js b/src/__tests__/lib/rules/prefer-in-document.js index 3b6c4c8..917a619 100644 --- a/src/__tests__/lib/rules/prefer-in-document.js +++ b/src/__tests__/lib/rules/prefer-in-document.js @@ -27,62 +27,58 @@ function invalidCase(code, output) { }; } +const valid = [ + ...queries.map((q) => [ + `expect(screen.${q}('foo')).toBeInTheDocument()`, + `expect(${q}('foo')).toBeInTheDocument()`, + `expect(wrapper.${q}('foo')).toBeInTheDocument()`, + ]), + `expect(screen.notAQuery('foo-bar')).toHaveLength(1)`, + `expect(screen.getByText('foo-bar')).toHaveLength(2)`, +]; +const invalid = [ + // Invalid cases that applies to all variants + ...queries.map((q) => [ + invalidCase( + `expect(screen.${q}('foo')).toHaveLength(1)`, + `expect(screen.${q}('foo')).toBeInTheDocument()` + ), + invalidCase( + `expect(${q}('foo')).toHaveLength(1)`, + `expect(${q}('foo')).toBeInTheDocument()` + ), + invalidCase( + `expect(wrapper.${q}('foo')).toHaveLength(1)`, + `expect(wrapper.${q}('foo')).toBeInTheDocument()` + ), + ]), + // Invalid cases that applies to queryBy* and queryAllBy* + ...queriesByVariant.query.map((q) => [ + invalidCase( + `expect(${q}('foo')).toHaveLength(0)`, + `expect(${q}('foo')).not.toBeInTheDocument()` + ), + invalidCase( + `expect(${q}('foo')).toBeNull()`, + `expect(${q}('foo')).not.toBeInTheDocument()` + ), + invalidCase( + `expect(${q}('foo')).not.toBeNull()`, + `expect(${q}('foo')).toBeInTheDocument()` + ), + invalidCase( + `expect(${q}('foo')).toBeDefined()`, + `expect(${q}('foo')).toBeInTheDocument()` + ), + invalidCase( + `expect(${q}('foo')).not.toBeDefined()`, + `expect(${q}('foo')).not.toBeInTheDocument()` + ), + ]), +]; + const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2015 } }); ruleTester.run("prefer-in-document", rule, { - valid: [ - ...queries - .map((q) => [ - `expect(screen.${q}('foo')).toBeInTheDocument()`, - `expect(${q}('foo')).toBeInTheDocument()`, - `expect(wrapper.${q}('foo')).toBeInTheDocument()`, - ]) - .flat(), - `expect(screen.notAQuery('foo-bar')).toHaveLength(1)`, - `expect(screen.getByText('foo-bar')).toHaveLength(2)`, - ], - invalid: [ - // Invalid cases that applies to all variants - ...queries - .map((q) => [ - invalidCase( - `expect(screen.${q}('foo')).toHaveLength(1)`, - `expect(screen.${q}('foo')).toBeInTheDocument()` - ), - invalidCase( - `expect(${q}('foo')).toHaveLength(1)`, - `expect(${q}('foo')).toBeInTheDocument()` - ), - invalidCase( - `expect(wrapper.${q}('foo')).toHaveLength(1)`, - `expect(wrapper.${q}('foo')).toBeInTheDocument()` - ), - ]) - .flat(), - - // Invalid cases that applies to queryBy* and queryAllBy* - ...queriesByVariant.query - .map((q) => [ - invalidCase( - `expect(${q}('foo')).toHaveLength(0)`, - `expect(${q}('foo')).not.toBeInTheDocument()` - ), - invalidCase( - `expect(${q}('foo')).toBeNull()`, - `expect(${q}('foo')).not.toBeInTheDocument()` - ), - invalidCase( - `expect(${q}('foo')).not.toBeNull()`, - `expect(${q}('foo')).toBeInTheDocument()` - ), - invalidCase( - `expect(${q}('foo')).toBeDefined()`, - `expect(${q}('foo')).toBeInTheDocument()` - ), - invalidCase( - `expect(${q}('foo')).not.toBeDefined()`, - `expect(${q}('foo')).not.toBeInTheDocument()` - ), - ]) - .flat(), - ], + valid: [].concat(...valid), + invalid: [].concat(...invalid), }); From 60e64a7c4d237606d1a722c5bca19c3c0a9892a5 Mon Sep 17 00:00:00 2001 From: Anton Niklasson Date: Mon, 23 Nov 2020 13:59:36 +0100 Subject: [PATCH 12/16] remove unnecessary if statement to get 100% coverage --- src/rules/prefer-in-document.js | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/rules/prefer-in-document.js b/src/rules/prefer-in-document.js index 54f904a..595d4f8 100644 --- a/src/rules/prefer-in-document.js +++ b/src/rules/prefer-in-document.js @@ -84,14 +84,12 @@ export const create = (context) => { const matcherNode = node.callee.property; const matcherArguments = node.arguments; - if (queryNode && matcherNode) { - check(context, { - negatedMatcher: true, - queryNode, - matcherNode, - matcherArguments, - }); - } + check(context, { + negatedMatcher: true, + queryNode, + matcherNode, + matcherArguments, + }); }, // Grabbing expect(). @@ -102,14 +100,12 @@ export const create = (context) => { const matcherNode = node.callee.property; const matcherArguments = node.arguments; - if (queryNode && matcherNode) { - check(context, { - negatedMatcher: false, - queryNode, - matcherNode, - matcherArguments, - }); - } + check(context, { + negatedMatcher: false, + queryNode, + matcherNode, + matcherArguments, + }); }, }; }; From 246d7762a475f7323e99e686f4b32c72cb6b632f Mon Sep 17 00:00:00 2001 From: Anton Niklasson Date: Mon, 23 Nov 2020 16:50:06 +0100 Subject: [PATCH 13/16] use node.range[0] instead of node.start for eslint@7 --- src/rules/prefer-in-document.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/rules/prefer-in-document.js b/src/rules/prefer-in-document.js index 595d4f8..db12a2e 100644 --- a/src/rules/prefer-in-document.js +++ b/src/rules/prefer-in-document.js @@ -51,7 +51,10 @@ function check( if (isAntonymMatcher(matcherNode, matcherArguments)) { if (negatedMatcher) { operations.push( - fixer.removeRange([matcherNode.start - 5, matcherNode.start - 1]) + fixer.removeRange([ + matcherNode.range[0] - 5, + matcherNode.range[0] - 1, + ]) ); } else { operations.push(fixer.insertTextBefore(matcherNode, "not.")); From 0d4244a6bbb546adc7269c8aa1543b5797810e95 Mon Sep 17 00:00:00 2001 From: Ben Monro Date: Mon, 23 Nov 2020 11:25:42 -0800 Subject: [PATCH 14/16] updated readme --- README.md | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 10cdf01..7a1c681 100644 --- a/README.md +++ b/README.md @@ -100,19 +100,17 @@ module.exports = { 🔧 indicates that a rule is fixable. - -| Name | 👍 | 🔧 | Description | -| ---------------------------------------------------------------------------------------------------------------------------------------------- | --- | --- | ------------------------------------------------------------------------------------------------ | -| [prefer-checked](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-checked.md) | 👍 | 🔧 | prefer toBeChecked over checking attributes | -| [prefer-empty](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-empty.md) | 👍 | 🔧 | Prefer toBeEmpty over checking innerHTML | -| [prefer-enabled-disabled](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-enabled-disabled.md) | 👍 | 🔧 | prefer toBeDisabled or toBeEnabled over checking attributes | -| [prefer-focus](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-focus.md) | 👍 | 🔧 | prefer toHaveFocus over checking document.activeElement | -| [prefer-in-document](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-in-document.md) | | 🔧 | Prefer .toBeInTheDocument() in favor of checking the length of the result using .toHaveLength(1) | -| [prefer-required](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-required.md) | 👍 | 🔧 | prefer toBeRequired over checking properties | -| [prefer-to-have-attribute](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-to-have-attribute.md) | 👍 | 🔧 | prefer toHaveAttribute over checking getAttribute/hasAttribute | -| [prefer-to-have-style](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-to-have-style.md) | 👍 | 🔧 | prefer toHaveStyle over checking element style | -| [prefer-to-have-text-content](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-to-have-text-content.md) | 👍 | 🔧 | Prefer toHaveTextContent over checking element.textContent | - +Name | 👍 | 🔧 | Description +----- | ----- | ----- | ----- +[prefer-checked](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-checked.md) | 👍 | 🔧 | prefer toBeChecked over checking attributes +[prefer-empty](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-empty.md) | 👍 | 🔧 | Prefer toBeEmpty over checking innerHTML +[prefer-enabled-disabled](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-enabled-disabled.md) | 👍 | 🔧 | prefer toBeDisabled or toBeEnabled over checking attributes +[prefer-focus](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-focus.md) | 👍 | 🔧 | prefer toHaveFocus over checking document.activeElement +[prefer-in-document](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-in-document.md) | | 🔧 | Prefer .toBeInTheDocument() in favor of checking the length of the result using .toHaveLength(1) +[prefer-required](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-required.md) | 👍 | 🔧 | prefer toBeRequired over checking properties +[prefer-to-have-attribute](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-to-have-attribute.md) | 👍 | 🔧 | prefer toHaveAttribute over checking getAttribute/hasAttribute +[prefer-to-have-style](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-to-have-style.md) | 👍 | 🔧 | prefer toHaveStyle over checking element style +[prefer-to-have-text-content](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/prefer-to-have-text-content.md) | 👍 | 🔧 | Prefer toHaveTextContent over checking element.textContent ## Issues From 11dd5e7e45764f5998cbd36d013ea8353b0b07d1 Mon Sep 17 00:00:00 2001 From: Anton Niklasson Date: Mon, 23 Nov 2020 21:55:54 +0100 Subject: [PATCH 15/16] update the rule description and reported message --- src/rules/prefer-in-document.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/rules/prefer-in-document.js b/src/rules/prefer-in-document.js index db12a2e..fd06149 100644 --- a/src/rules/prefer-in-document.js +++ b/src/rules/prefer-in-document.js @@ -10,13 +10,13 @@ export const meta = { docs: { category: "jest-dom", description: - "Prefer .toBeInTheDocument() in favor of checking the length of the result using .toHaveLength(1)", + "Prefer .toBeInTheDocument() for asserting the existence of a DOM node", url: "prefer-in-document", recommended: false, }, fixable: "code", messages: { - "use-document": `Prefer .toBeInTheDocument() in favor of .toHaveLength(1)`, + "use-document": `Prefer .toBeInTheDocument() for asserting DOM node existence`, }, }; @@ -29,12 +29,11 @@ function isAntonymMatcher(matcherNode, matcherArguments) { function check( context, - // eslint-disable-next-line no-unused-vars { queryNode, matcherNode, matcherArguments, negatedMatcher } ) { const query = queryNode.name || queryNode.property.name; - // toHaveLength should only be invalid if the argument is 1 + // toHaveLength() is only invalid with 0 or 1 if (matcherNode.name === "toHaveLength" && matcherArguments[0].value > 1) { return; } From 3d2333eca58dd01c8dad07f9eb430f0dae5426e9 Mon Sep 17 00:00:00 2001 From: Ben Monro Date: Mon, 23 Nov 2020 17:03:36 -0800 Subject: [PATCH 16/16] move DTL to deps --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 84be56e..d083277 100644 --- a/package.json +++ b/package.json @@ -39,10 +39,10 @@ }, "dependencies": { "@babel/runtime": "^7.9.6", + "@testing-library/dom": "^7.28.1", "requireindex": "^1.2.0" }, "devDependencies": { - "@testing-library/dom": "^7.27.1", "eslint": "7.14", "jest-extended": "^0.11.5", "kcd-scripts": "6.5.1"