Skip to content

Commit

Permalink
feat(rule): add data-testid rule (#56)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
tknickman authored and Belco90 committed Jan 13, 2020
1 parent 856851e commit 6fd2e24
Show file tree
Hide file tree
Showing 5 changed files with 400 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ To enable this configuration use the `extends` property in your
| [no-dom-import](docs/rules/no-dom-import.md) | Disallow importing from DOM Testing Library | ![angular-badge][] ![react-badge][] ![vue-badge][] | ![fixable-badge][] |
| [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][] | |
| [prefer-explicit-assert](docs/rules/prefer-explicit-assert.md) | Suggest using explicit assertions rather than just `getBy*` queries | | |
| [consistent-data-testid](docs/rules/consistent-data-testid.md) | Ensure `data-testid` values match a provided regex. | | |

[build-badge]: https://img.shields.io/travis/Belco90/eslint-plugin-testing-library?style=flat-square
[build-url]: https://travis-ci.org/belco90/eslint-plugin-testing-library
Expand Down
43 changes: 43 additions & 0 deletions docs/rules/consistent-data-testid.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Enforces consistent naming for the data-testid attribute (consistent-data-testid)

Ensure `data-testid` values match a provided regex. This rule is un-opinionated, and requires configuration.

## Rule Details

> Assuming the rule has been configured with the following regex: `^TestId(\_\_[A-Z]*)?$`
Examples of **incorrect** code for this rule:

```js
const foo = props => <div data-testid="my-test-id">...</div>;
const foo = props => <div data-testid="myTestId">...</div>;
const foo = props => <div data-testid="TestIdEXAMPLE">...</div>;
```

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

```js
const foo = props => <div data-testid="TestId__EXAMPLE">...</div>;
const bar = props => <div data-testid="TestId">...</div>;
const baz = props => <div>...</div>;
```

## Options

| Option | Required | Default | Details | Example |
| ----------------- | -------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------- |
| `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]_?)+)_\$` |
| `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` |

## Example

```json
{
"testing-library/data-testid": [
2,
{
"testIdPattern": "^TestId(__[A-Z]*)?$"
}
]
}
```
1 change: 1 addition & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
const rules = {
'await-async-query': require('./rules/await-async-query'),
'await-fire-event': require('./rules/await-fire-event'),
'consistent-data-testid': require('./rules/consistent-data-testid'),
'no-await-sync-query': require('./rules/no-await-sync-query'),
'no-debug': require('./rules/no-debug'),
'no-dom-import': require('./rules/no-dom-import'),
Expand Down
75 changes: 75 additions & 0 deletions lib/rules/consistent-data-testid.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
'use strict';

const FILENAME_PLACEHOLDER = '{fileName}';

module.exports = {
meta: {
docs: {
description: 'Ensures consistent usage of `data-testid`',
category: 'Best Practices',
recommended: false,
},
messages: {
invalidTestId: '`{{attr}}` "{{value}}" should match `{{regex}}`',
},
fixable: null,
schema: [
{
type: 'object',
default: {},
additionalProperties: false,
required: ['testIdPattern'],
properties: {
testIdPattern: {
type: 'string',
},
testIdAttribute: {
type: 'string',
default: 'data-testid',
},
},
},
],
},

create: function(context) {
const { options, getFilename } = context;
const { testIdPattern, testIdAttribute: attr } = options[0];

function getFileNameData() {
const splitPath = getFilename().split('/');
const fileNameWithExtension = splitPath.pop();
const parent = splitPath.pop();
const fileName = fileNameWithExtension.split('.').shift();

return {
fileName: fileName === 'index' ? parent : fileName,
};
}

function getTestIdValidator({ fileName }) {
return new RegExp(testIdPattern.replace(FILENAME_PLACEHOLDER, fileName));
}

return {
[`JSXIdentifier[name=${attr}]`]: node => {
const value =
node && node.parent && node.parent.value && node.parent.value.value;
const { fileName } = getFileNameData();
const regex = getTestIdValidator({ fileName });

if (value && !regex.test(value)) {
context.report({
node,
messageId: 'invalidTestId',
data: {
attr,
value,
regex,
},
});
}
},
};
},
};

0 comments on commit 6fd2e24

Please sign in to comment.