Skip to content

Commit 0d136bb

Browse files
feat: Add auto fix capability to onClick rule when data-test-id attribute is missing (#8)
* feat: Add auto fix capability to onClick rule when data-test-id attribute is missing (#1) * feat: Add auto fix capability to onClick rule when data-test-id attribute is missing. * cleaned up parserOptionsMapper from onClick rule and used the generic parserOptionMapper instead * removed unused code Co-authored-by: Boima Konuwa <[email protected]> * fix: Allow multipart jsx node names Co-authored-by: Boima Konuwa <[email protected]> Co-authored-by: Chris Garcia <[email protected]>
1 parent 73bc12d commit 0d136bb

File tree

5 files changed

+120
-16
lines changed

5 files changed

+120
-16
lines changed

lib/rules/onClick.js

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ module.exports = {
2121
recommended: true,
2222
url: 'https://github.com/davidcalhoun/eslint-plugin-test-selectors/tree/master/docs/rules/onClick.md'
2323
},
24-
fixable: null,
24+
fixable: 'code',
2525
schema: defaultRuleSchema
2626
},
2727

@@ -42,7 +42,20 @@ module.exports = {
4242

4343
context.report({
4444
node,
45-
message: getError(errors.onClick.message, testAttribute)
45+
message: getError(errors.onClick.message, testAttribute),
46+
fix: function fix(fixer) {
47+
const { nanoid } = require('nanoid');
48+
const suggestedId = nanoid();
49+
const namePositionEnd = node.name.range[1];
50+
const attributeText = `${ testAttribute }="${ suggestedId }"`;
51+
const start = namePositionEnd - 1;
52+
const end = start + 1;
53+
54+
return fixer.insertTextAfterRange(
55+
[start, end],
56+
` ${ attributeText }`
57+
);
58+
},
4659
});
4760
}
4861
};

package-lock.json

Lines changed: 42 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,14 @@
2222
},
2323
"dependencies": {
2424
"jsx-ast-utils": "^3.2.0",
25+
"nanoid": "^3.1.18",
2526
"requireindex": "^1.2.0"
2627
},
2728
"devDependencies": {
2829
"babel-eslint": "^10.1.0",
2930
"eslint": "^7.16.0",
30-
"mocha": "^8.2.1"
31+
"mocha": "^8.2.1",
32+
"mock-require": "^3.0.3"
3133
},
3234
"engines": {
3335
"node": ">=0.10.0"

tests/lib/rules/onClick.js

Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,21 @@ const {
99
errors
1010
} = require('../../../lib/constants');
1111
const { getError } = require('../../../lib/utils');
12+
const nanoidMock = require('mock-require');
1213

1314
const { onClick } = errors;
1415

1516
const onClickError = getError(onClick.message, defaults.testAttribute);
1617

18+
nanoidMock('nanoid', {
19+
nanoid: function () {
20+
return 'AbYK0YPm2OWYiMaFPKLbp';
21+
},
22+
});
23+
24+
const id = require('nanoid');
25+
const suggestedId = id.nanoid();
26+
1727
const ruleTester = new RuleTester();
1828
ruleTester.run('onClick', rule, {
1929
valid: [
@@ -31,12 +41,46 @@ ruleTester.run('onClick', rule, {
3141
].map(parserOptionsMapper),
3242

3343
invalid: [
34-
{ code: '<div onClick={ this.handleClick } />', errors: [onClickError] },
35-
{ code: '<div onClick={ this.handleClick }>foo</div>', errors: [onClickError] },
36-
{ code: '<Bar onClick={ this.handleClick } />', errors: [onClickError] },
37-
{ code: '<Bar onClick={ this.handleClick }>foo</Bar>', errors: [onClickError] },
38-
{ code: '<Bar onClick={ () => handleClick() }>foo</Bar>', errors: [onClickError] },
39-
{ code: '<Bar onClick={ () => handleClick() } disabled={ foo }>foo</Bar>', errors: [onClickError] },
40-
{ code: '<Bar onClick={ () => handleClick() } readonly={ foo }>foo</Bar>', errors: [onClickError] }
44+
{
45+
code: '<div onClick={ this.handleClick } />',
46+
errors: [onClickError],
47+
output: `<div data-test-id="${ suggestedId }" onClick={ this.handleClick } />`,
48+
},
49+
{
50+
code: '<div onClick={ this.handleClick }>foo</div>',
51+
errors: [onClickError],
52+
output: `<div data-test-id="${ suggestedId }" onClick={ this.handleClick }>foo</div>`,
53+
},
54+
{
55+
code: '<Bar onClick={ this.handleClick } />',
56+
errors: [onClickError],
57+
output: `<Bar data-test-id="${ suggestedId }" onClick={ this.handleClick } />`,
58+
},
59+
{
60+
code: '<Bar onClick={ this.handleClick }>foo</Bar>',
61+
errors: [onClickError],
62+
output: `<Bar data-test-id="${ suggestedId }" onClick={ this.handleClick }>foo</Bar>`,
63+
},
64+
{
65+
code: '<Bar onClick={ () => handleClick() }>foo</Bar>',
66+
errors: [onClickError],
67+
output: `<Bar data-test-id="${ suggestedId }" onClick={ () => handleClick() }>foo</Bar>`,
68+
},
69+
{
70+
code: '<Bar onClick={ () => handleClick() } disabled={ foo }>foo</Bar>',
71+
errors: [onClickError],
72+
output: `<Bar data-test-id="${ suggestedId }" onClick={ () => handleClick() } disabled={ foo }>foo</Bar>`,
73+
},
74+
{
75+
code: '<Bar onClick={ () => handleClick() } readonly={ foo }>foo</Bar>',
76+
errors: [onClickError],
77+
output: `<Bar data-test-id="${ suggestedId }" onClick={ () => handleClick() } readonly={ foo }>foo</Bar>`,
78+
},
79+
{
80+
code: '<Foo.Bar onClick={ () => handleClick() } readonly={ foo }>foo</Foo.Bar>',
81+
errors: [onClickError],
82+
output:
83+
`<Foo.Bar data-test-id="${ suggestedId }" onClick={ () => handleClick() } readonly={ foo }>foo</Foo.Bar>`,
84+
},
4185
].map(parserOptionsMapper)
4286
});

tests/parserOptionsMapper.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,23 @@ const defaultParserOptions = {
88
module.exports = function({
99
code,
1010
errors,
11+
output,
1112
options = [],
1213
parserOptions = {},
1314
}) {
14-
return {
15+
const defaultOptions = {
1516
code,
1617
errors,
1718
options,
1819
parserOptions: {
1920
...defaultParserOptions,
2021
...parserOptions,
2122
}
22-
};
23+
}
24+
25+
if (output) {
26+
defaultOptions.output = output;
27+
}
28+
29+
return defaultOptions;
2330
}

0 commit comments

Comments
 (0)