Skip to content

Commit 9e70b34

Browse files
committed
feat: add prefer-partially-checked rule
Suggest toBePartiallyChecked() instead of toHaveAttribute('aria-checked', 'mixed'). Fills a pre-existing gap—the matcher has been in jest-dom for years without a corresponding lint rule.
1 parent b468260 commit 9e70b34

File tree

3 files changed

+155
-0
lines changed

3 files changed

+155
-0
lines changed
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Prefer toBePartiallyChecked over checking attributes (`jest-dom/prefer-partially-checked`)
2+
3+
💼 This rule is enabled in the ✅ `recommended` config.
4+
5+
🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).
6+
7+
<!-- end auto-generated rule header -->
8+
9+
## Rule Details
10+
11+
This rule suggests using `toBePartiallyChecked()` instead of checking for the
12+
`"mixed"` value of the `aria-checked` attribute with `.toHaveAttribute()` or
13+
`.toHaveProperty()`. This improves readability and ensures you are using the
14+
semantically correct jest-dom matcher.
15+
16+
Examples of **incorrect** code for this rule:
17+
18+
```js
19+
expect(element).toHaveAttribute("aria-checked", "mixed");
20+
expect(element).toHaveProperty("aria-checked", "mixed");
21+
expect(element).not.toHaveAttribute("aria-checked", "mixed");
22+
```
23+
24+
Examples of **correct** code for this rule:
25+
26+
```js
27+
expect(element).toBePartiallyChecked();
28+
29+
expect(element).not.toBePartiallyChecked();
30+
31+
expect(element).toHaveAttribute("aria-checked", "true");
32+
expect(element).toHaveAttribute("aria-checked", "false");
33+
```
34+
35+
## When Not To Use It
36+
37+
Don't use this rule if you:
38+
39+
- don't use `jest-dom`
40+
- want to allow `.toHaveAttribute()` and `.toHaveProperty()` for `aria-checked="mixed"`
41+
42+
## Further Reading
43+
44+
- [jest-dom `toBePartiallyChecked`](https://github.com/testing-library/jest-dom#tobepartiallychecked)
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/**
2+
* @fileoverview prefer toBePartiallyChecked over checking aria-checked for mixed state
3+
*/
4+
5+
import { RuleTester } from "eslint";
6+
import * as rule from "../../../rules/prefer-partially-checked";
7+
8+
const ruleTester = new RuleTester({
9+
parserOptions: { ecmaVersion: 2015, sourceType: "module" },
10+
});
11+
12+
ruleTester.run("prefer-partially-checked", rule, {
13+
valid: [
14+
`expect(element).toBePartiallyChecked()`,
15+
`expect(element).not.toBePartiallyChecked()`,
16+
`const el = screen.getByText("foo"); expect(el).toBePartiallyChecked()`,
17+
`const el = screen.getByText("foo"); expect(el).toHaveAttribute("aria-checked", "true")`,
18+
`const el = screen.getByText("foo"); expect(el).toHaveAttribute("aria-checked", "false")`,
19+
`const el = screen.getByText("foo"); expect(el).toHaveAttribute("aria-checked")`,
20+
`const el = screen.getByText("foo"); expect(el).toHaveProperty("aria-checked", "true")`,
21+
`const el = screen.getByText("foo"); expect(el).not.toHaveAttribute("aria-checked")`,
22+
`const el = foo.bar(); expect(el).toHaveAttribute("aria-checked", "mixed")`,
23+
`const el = foo.bar(); expect(el).toHaveProperty("aria-checked", "mixed")`,
24+
],
25+
invalid: [
26+
{
27+
code: `const el = screen.getByText("foo"); expect(el).toHaveAttribute("aria-checked", "mixed")`,
28+
errors: [
29+
{
30+
message: `Use toBePartiallyChecked() instead of toHaveAttribute('aria-checked', 'mixed')`,
31+
},
32+
],
33+
output: `const el = screen.getByText("foo"); expect(el).toBePartiallyChecked()`,
34+
},
35+
{
36+
code: `const el = screen.getByText("foo"); expect(el).toHaveProperty("aria-checked", "mixed")`,
37+
errors: [
38+
{
39+
message: `Use toBePartiallyChecked() instead of toHaveProperty('aria-checked', 'mixed')`,
40+
},
41+
],
42+
output: `const el = screen.getByText("foo"); expect(el).toBePartiallyChecked()`,
43+
},
44+
{
45+
code: `expect(getByText("foo")).toHaveAttribute("aria-checked", "mixed")`,
46+
errors: [
47+
{
48+
message: `Use toBePartiallyChecked() instead of toHaveAttribute('aria-checked', 'mixed')`,
49+
},
50+
],
51+
output: `expect(getByText("foo")).toBePartiallyChecked()`,
52+
},
53+
{
54+
code: `expect(getByRole("checkbox")).toHaveAttribute("aria-checked", "mixed")`,
55+
errors: [
56+
{
57+
message: `Use toBePartiallyChecked() instead of toHaveAttribute('aria-checked', 'mixed')`,
58+
},
59+
],
60+
output: `expect(getByRole("checkbox")).toBePartiallyChecked()`,
61+
},
62+
{
63+
code: `const el = screen.getByText("foo"); expect(el).not.toHaveAttribute("aria-checked", "mixed")`,
64+
errors: [
65+
{
66+
message: `Use not.toBePartiallyChecked() instead of not.toHaveAttribute('aria-checked', 'mixed')`,
67+
},
68+
],
69+
output: `const el = screen.getByText("foo"); expect(el).not.toBePartiallyChecked()`,
70+
},
71+
{
72+
code: `const el = screen.getByText("foo"); expect(el).not.toHaveProperty("aria-checked", "mixed")`,
73+
errors: [
74+
{
75+
message: `Use not.toBePartiallyChecked() instead of not.toHaveProperty('aria-checked', 'mixed')`,
76+
},
77+
],
78+
output: `const el = screen.getByText("foo"); expect(el).not.toBePartiallyChecked()`,
79+
},
80+
{
81+
code: `expect(getByText("foo")).not.toHaveAttribute("aria-checked", "mixed")`,
82+
errors: [
83+
{
84+
message: `Use not.toBePartiallyChecked() instead of not.toHaveAttribute('aria-checked', 'mixed')`,
85+
},
86+
],
87+
output: `expect(getByText("foo")).not.toBePartiallyChecked()`,
88+
},
89+
],
90+
});
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* @fileoverview prefer toBePartiallyChecked over checking aria-checked for mixed state
3+
*/
4+
5+
import createPartiallyBannedAttributeRule from "../createPartiallyBannedAttributeRule";
6+
7+
export const meta = {
8+
docs: {
9+
description:
10+
"prefer toBePartiallyChecked over checking aria-checked attribute for mixed state",
11+
category: "Best Practices",
12+
recommended: true,
13+
url: "prefer-partially-checked",
14+
},
15+
fixable: "code",
16+
};
17+
18+
export const create = createPartiallyBannedAttributeRule({
19+
preferred: "toBePartiallyChecked",
20+
attribute: "aria-checked",
21+
});

0 commit comments

Comments
 (0)