Skip to content
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

feat(rules): added new rule for prefer-to-have-style #76

Merged
merged 6 commits into from
Aug 31, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 12 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,21 +95,22 @@ module.exports = {

## Supported Rules

✔️ indicates that a rule is recommended for all users.
👍 indicates that a rule is recommended for all users.

🛠 indicates that a rule is fixable.
🔧 indicates that a rule is fixable.

<!-- __BEGIN AUTOGENERATED TABLE__ -->

| Name | ✔️ | 🛠 | Description |
| 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-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-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 |

<!-- __END AUTOGENERATED TABLE__ -->

Expand Down Expand Up @@ -156,6 +157,7 @@ Thanks goes to these people ([emoji key][emojis]):

<!-- markdownlint-enable -->
<!-- prettier-ignore-end -->

<!-- ALL-CONTRIBUTORS-LIST:END -->

This project follows the [all-contributors][all-contributors] specification.
Expand Down
6 changes: 3 additions & 3 deletions build/generate-readme-table.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ const expectedTableLines = Object.keys(rules)
lines.push(
[
`[${ruleId}](https://github.com/testing-library/eslint-plugin-jest-dom/blob/master/docs/rules/${ruleId}.md)`,
rule.meta.docs.recommended ? "✔️" : "",
rule.meta.fixable ? "🛠" : "",
rule.meta.docs.recommended ? "👍" : "",
rule.meta.fixable ? "🔧" : "",
rule.meta.docs.description,
].join(" | ")
);

return lines;
},
["Name | ✔️ | 🛠 | Description", "----- | ----- | ----- | -----"]
["Name | 👍 | 🔧 | Description", "----- | ----- | ----- | -----"]
)
.join("\n");

Expand Down
39 changes: 39 additions & 0 deletions docs/rules/prefer-to-have-style.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# prefer toHaveProperty over checking element.style (prefer-to-have-style)

This rule is an autofixable rule that reports usages of checking element.style in expect statements in preference of using the jest-dom
`toHaveStyle` matcher.

## Rule Details

Examples of **incorrect** code for this rule:

```js
expect(el.style.foo).toBe("bar");
expect(el.style.foo).not.toBe("bar");
expect(el.style).toHaveProperty("background-color", "green");
expect(screen.getByTestId("foo").style["scroll-snap-type"]).toBe("x mandatory");
expect(el.style).toContain("background-color");
expect(el.style).not.toContain("background-color");
expect(el).toHaveAttribute(
"style",
"background-color: green; border-width: 10px; color: blue;"
);
```

Examples of **correct** code for this rule:

```js
expect(el).toHaveStyle({ foo: "bar" });
expect(el.style).toMatchSnapshot();
expect(el.style).toEqual(foo);
```

## When Not To Use It

If you don't care about using built in matchers for checking style on dom
elements.

## Further Reading

- [jest-dom toHaveStyle](https://github.com/testing-library/jest-dom#tohavestyle)
- [ElementCSSInlineStyle.style](https://developer.mozilla.org/en-US/docs/Web/API/ElementCSSInlineStyle/style)
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"requireindex": "^1.2.0"
},
"devDependencies": {
"jest-extended": "^0.11.5",
"kcd-scripts": "^6.0.0"
},
"peerDependencies": {
Expand Down
85 changes: 0 additions & 85 deletions src/__tests__/__snapshots__/index.test.js.snap

This file was deleted.

11 changes: 10 additions & 1 deletion src/__tests__/index.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
import { rules, generateRecommendedConfig } from "../index";
import "jest-extended";

it("should have all the rules", () => {
expect(rules).toMatchSnapshot();
expect(Object.keys(rules).length).toBeGreaterThan(0);
});

it.each(Object.keys(rules))("%s should export required fields", (ruleName) => {
const rule = rules[ruleName];
expect(rule).toHaveProperty("create", expect.any(Function));
expect(rule.meta.docs.url).not.toBeEmpty();
expect(rule.meta.docs.category).toBe("jest-dom");
expect(rule.meta.docs.description).not.toBeEmpty();
});

it("should have a recommended config with recommended rules", () => {
Expand Down
3 changes: 3 additions & 0 deletions src/__tests__/lib/rules/.eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{"rules":{
"no-template-curly-in-string":"off"
}}
62 changes: 62 additions & 0 deletions src/__tests__/lib/rules/prefer-to-have-style.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { RuleTester } from "eslint";
import * as rule from "../../../rules/prefer-to-have-style";

const errors = [
{ message: "Use toHaveStyle instead of asserting on element style" },
];
const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2015 } });
ruleTester.run("prefer-to-have-attribute", rule, {
valid: [
`expect(el).toHaveStyle({foo:"bar"})`,
`expect(el.style).toMatchSnapshot()`,
`expect(el.style).toEqual(foo)`,
`expect(el).toHaveAttribute("style")`,
],
invalid: [
{
code: `expect(el.style.foo).toBe("bar")`,
errors,
output: `expect(el).toHaveStyle({foo:"bar"})`,
},
{
code: `expect(el.style.foo).not.toBe("bar")`,
errors,
output: `expect(el).not.toHaveStyle({foo:"bar"})`,
},
{
code: `expect(el.style).toHaveProperty("background-color", "green")`,
errors,
output: `expect(el).toHaveStyle({backgroundColor: "green"})`,
},
{
code: `expect(el.style).not.toHaveProperty("background-color", "green")`,
errors,
output: `expect(el).not.toHaveStyle({backgroundColor: "green"})`,
},
{
code: `expect(screen.getByTestId("foo").style["scroll-snap-type"]).toBe("x mandatory")`,
errors,
output: `expect(screen.getByTestId("foo")).toHaveStyle({scrollSnapType: "x mandatory"})`,
},
{
code: `expect(screen.getByTestId("foo").style["scroll-snap-type"]).not.toBe("x mandatory")`,
errors,
output: `expect(screen.getByTestId("foo")).not.toHaveStyle({scrollSnapType: "x mandatory"})`,
},
{
code: `expect(el.style).toContain("background-color")`,
errors,
output: `expect(el).toHaveStyle({backgroundColor: expect.anything()})`,
},
{
code: `expect(el.style).not.toContain("background-color")`,
errors,
output: `expect(el).not.toHaveStyle({backgroundColor: expect.anything()})`,
},
{
code: `expect(el).toHaveAttribute("style", "background-color: green; border-width: 10px; color: blue;")`,
errors,
output: `expect(el).toHaveStyle("background-color: green; border-width: 10px; color: blue;")`,
},
],
});
4 changes: 2 additions & 2 deletions src/createBannedAttributeRule.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export default ({ preferred, negatedPreferred, attributes }) => (context) => {
node
) {
const {
arguments: [{ property, property: { name } = {} }],
arguments: [{ object, property, property: { name } = {} }],
} = node.callee.object;
const matcher = node.callee.property.name;
const matcherArg = node.arguments.length && node.arguments[0].value;
Expand All @@ -58,7 +58,7 @@ export default ({ preferred, negatedPreferred, attributes }) => (context) => {
node,
message: `Use ${correctFunction}() instead of checking .${name} directly`,
fix: (fixer) => [
fixer.removeRange([property.range[0] - 1, property.range[1]]),
fixer.removeRange([object.range[1], property.range[1]]),
fixer.replaceTextRange(
[node.callee.property.range[0], node.range[1]],
`${correctFunction}()`
Expand Down
16 changes: 8 additions & 8 deletions src/rules/prefer-empty.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const create = (context) => ({
node,
message: "Use toBeEmptyDOMElement instead of checking inner html.",
fix: (fixer) => [
fixer.removeRange([node.left.property.range[0] - 1, node.range[1]]),
fixer.removeRange([node.left.object.range[1], node.range[1]]),
fixer.replaceText(
node.parent.parent.property,
Boolean(node.parent.parent.parent.arguments[0].value) ===
Expand All @@ -40,7 +40,7 @@ export const create = (context) => ({
node,
message: "Use toBeEmptyDOMElement instead of checking inner html.",
fix: (fixer) => [
fixer.removeRange([node.left.property.range[0] - 1, node.range[1]]),
fixer.removeRange([node.left.object.range[1], node.range[1]]),
fixer.replaceText(
node.parent.parent.property,
Boolean(node.parent.parent.parent.arguments[0].value) ===
Expand All @@ -64,7 +64,7 @@ export const create = (context) => ({
node,
message: "Use toBeEmptyDOMElement instead of checking inner html.",
fix: (fixer) => [
fixer.removeRange([node.property.range[0] - 1, node.property.range[1]]),
fixer.removeRange([node.object.range[1], node.property.range[1]]),
fixer.replaceText(node.parent.parent.property, "toBeEmptyDOMElement"),
fixer.remove(node.parent.parent.parent.arguments[0]),
],
Expand All @@ -83,7 +83,7 @@ export const create = (context) => ({
node,
message: "Use toBeEmptyDOMElement instead of checking inner html.",
fix: (fixer) => [
fixer.removeRange([node.property.range[0] - 1, node.property.range[1]]),
fixer.removeRange([node.object.range[1], node.property.range[1]]),
fixer.replaceText(
node.parent.parent.parent.property,
"toBeEmptyDOMElement"
Expand All @@ -99,7 +99,7 @@ export const create = (context) => ({
node,
message: "Use toBeEmptyDOMElement instead of checking inner html.",
fix: (fixer) => [
fixer.removeRange([node.property.range[0] - 1, node.property.range[1]]),
fixer.removeRange([node.object.range[1], node.property.range[1]]),
fixer.replaceText(node.parent.parent.property, "toBeEmptyDOMElement"),
],
});
Expand All @@ -115,7 +115,7 @@ export const create = (context) => ({
node,
message: "Use toBeEmptyDOMElement instead of checking inner html.",
fix: (fixer) => [
fixer.removeRange([node.property.range[0] - 1, node.property.range[1]]),
fixer.removeRange([node.object.range[1], node.property.range[1]]),
fixer.replaceText(
node.parent.parent.parent.property,
"toBeEmptyDOMElement"
Expand All @@ -131,7 +131,7 @@ export const create = (context) => ({
node,
message: "Use toBeEmptyDOMElement instead of checking inner html.",
fix: (fixer) => [
fixer.removeRange([node.property.range[0] - 1, node.property.range[1]]),
fixer.removeRange([node.object.range[1], node.property.range[1]]),
fixer.replaceText(
node.parent.parent.parent.property,
"toBeEmptyDOMElement"
Expand All @@ -150,7 +150,7 @@ export const create = (context) => ({
node,
message: "Use toBeEmptyDOMElement instead of checking inner html.",
fix: (fixer) => [
fixer.removeRange([node.property.range[0] - 1, node.property.range[1]]),
fixer.removeRange([node.object.range[1], node.property.range[1]]),
fixer.replaceText(node.parent.parent.property, "toBeEmptyDOMElement"),
fixer.remove(node.parent.parent.parent.arguments[0]),
],
Expand Down
1 change: 1 addition & 0 deletions src/rules/prefer-focus.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const variantsOfDoc = [

export const meta = {
docs: {
url: "prefer-focus",
description: "prefer toHaveFocus over checking document.activeElement",
category: "jest-dom",
recommended: true,
Expand Down
4 changes: 3 additions & 1 deletion src/rules/prefer-to-have-attribute.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@

export const meta = {
docs: {
category: "jest-dom",
description:
"prefer toHaveAttribute over checking getAttribute/hasAttribute ",
url: "prefer-to-have-attribute",
recommended: true,
},
fixable: "code",
Expand Down Expand Up @@ -107,7 +109,7 @@ export const create = (context) => ({
message: "Invalid matcher for getAttribute",
});
},
[`CallExpression[callee.property.name='hasAttribute'][parent.callee.name='expect'][parent.parent.property.name=/toBe$|to(Striclty)?Equal/]`](
[`CallExpression[callee.property.name='hasAttribute'][parent.callee.name='expect'][parent.parent.property.name=/toBe$|to(Strict)?Equal/]`](
node
) {
if (typeof node.parent.parent.parent.arguments[0].value === "boolean") {
Expand Down
Loading