Skip to content

Commit 6fd2e24

Browse files
tknickmanBelco90
authored andcommitted
feat(rule): add data-testid rule (#56)
* feat(rule): add data-testid rule * chore(consistent-data-testid): address pr feedback * feat(testid): support configurable attribute * chore(consistent-data-testid): update docs * chore(consistent-data-testid): fix coverage
1 parent 856851e commit 6fd2e24

File tree

5 files changed

+400
-0
lines changed

5 files changed

+400
-0
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ To enable this configuration use the `extends` property in your
138138
| [no-dom-import](docs/rules/no-dom-import.md) | Disallow importing from DOM Testing Library | ![angular-badge][] ![react-badge][] ![vue-badge][] | ![fixable-badge][] |
139139
| [prefer-expect-query-by](docs/rules/prefer-expect-query-by.md) | Disallow the use of `expect(getBy*)` | ![recommended-badge][] ![angular-badge][] ![react-badge][] ![vue-badge][] | |
140140
| [prefer-explicit-assert](docs/rules/prefer-explicit-assert.md) | Suggest using explicit assertions rather than just `getBy*` queries | | |
141+
| [consistent-data-testid](docs/rules/consistent-data-testid.md) | Ensure `data-testid` values match a provided regex. | | |
141142

142143
[build-badge]: https://img.shields.io/travis/Belco90/eslint-plugin-testing-library?style=flat-square
143144
[build-url]: https://travis-ci.org/belco90/eslint-plugin-testing-library

docs/rules/consistent-data-testid.md

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Enforces consistent naming for the data-testid attribute (consistent-data-testid)
2+
3+
Ensure `data-testid` values match a provided regex. This rule is un-opinionated, and requires configuration.
4+
5+
## Rule Details
6+
7+
> Assuming the rule has been configured with the following regex: `^TestId(\_\_[A-Z]*)?$`
8+
9+
Examples of **incorrect** code for this rule:
10+
11+
```js
12+
const foo = props => <div data-testid="my-test-id">...</div>;
13+
const foo = props => <div data-testid="myTestId">...</div>;
14+
const foo = props => <div data-testid="TestIdEXAMPLE">...</div>;
15+
```
16+
17+
Examples of **correct** code for this rule:
18+
19+
```js
20+
const foo = props => <div data-testid="TestId__EXAMPLE">...</div>;
21+
const bar = props => <div data-testid="TestId">...</div>;
22+
const baz = props => <div>...</div>;
23+
```
24+
25+
## Options
26+
27+
| Option | Required | Default | Details | Example |
28+
| ----------------- | -------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------- |
29+
| `testIdPattern` | Yes | None | A regex used to validate the format of the `data-testid` value. `{fileName}` can optionally be used as a placeholder and will be substituted with the name of the file OR the name of the files parent directory in the case when the file name is `index.js` | `^{fileName}(\_\_([A-Z]+[a-z]_?)+)_\$` |
30+
| `testIdAttribute` | No | `data-testid` | A string used to specify the attribute used for querying by ID. This is only required if data-testid has been explicitly overridden in the [RTL configuration](https://testing-library.com/docs/dom-testing-library/api-queries#overriding-data-testid) | `data-my-test-attribute` |
31+
32+
## Example
33+
34+
```json
35+
{
36+
"testing-library/data-testid": [
37+
2,
38+
{
39+
"testIdPattern": "^TestId(__[A-Z]*)?$"
40+
}
41+
]
42+
}
43+
```

lib/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
const rules = {
44
'await-async-query': require('./rules/await-async-query'),
55
'await-fire-event': require('./rules/await-fire-event'),
6+
'consistent-data-testid': require('./rules/consistent-data-testid'),
67
'no-await-sync-query': require('./rules/no-await-sync-query'),
78
'no-debug': require('./rules/no-debug'),
89
'no-dom-import': require('./rules/no-dom-import'),

lib/rules/consistent-data-testid.js

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
'use strict';
2+
3+
const FILENAME_PLACEHOLDER = '{fileName}';
4+
5+
module.exports = {
6+
meta: {
7+
docs: {
8+
description: 'Ensures consistent usage of `data-testid`',
9+
category: 'Best Practices',
10+
recommended: false,
11+
},
12+
messages: {
13+
invalidTestId: '`{{attr}}` "{{value}}" should match `{{regex}}`',
14+
},
15+
fixable: null,
16+
schema: [
17+
{
18+
type: 'object',
19+
default: {},
20+
additionalProperties: false,
21+
required: ['testIdPattern'],
22+
properties: {
23+
testIdPattern: {
24+
type: 'string',
25+
},
26+
testIdAttribute: {
27+
type: 'string',
28+
default: 'data-testid',
29+
},
30+
},
31+
},
32+
],
33+
},
34+
35+
create: function(context) {
36+
const { options, getFilename } = context;
37+
const { testIdPattern, testIdAttribute: attr } = options[0];
38+
39+
function getFileNameData() {
40+
const splitPath = getFilename().split('/');
41+
const fileNameWithExtension = splitPath.pop();
42+
const parent = splitPath.pop();
43+
const fileName = fileNameWithExtension.split('.').shift();
44+
45+
return {
46+
fileName: fileName === 'index' ? parent : fileName,
47+
};
48+
}
49+
50+
function getTestIdValidator({ fileName }) {
51+
return new RegExp(testIdPattern.replace(FILENAME_PLACEHOLDER, fileName));
52+
}
53+
54+
return {
55+
[`JSXIdentifier[name=${attr}]`]: node => {
56+
const value =
57+
node && node.parent && node.parent.value && node.parent.value.value;
58+
const { fileName } = getFileNameData();
59+
const regex = getTestIdValidator({ fileName });
60+
61+
if (value && !regex.test(value)) {
62+
context.report({
63+
node,
64+
messageId: 'invalidTestId',
65+
data: {
66+
attr,
67+
value,
68+
regex,
69+
},
70+
});
71+
}
72+
},
73+
};
74+
},
75+
};

0 commit comments

Comments
 (0)