Skip to content

Commit bee49db

Browse files
committed
feat: make prefer-find-by rule fixable
1 parent 7a9af37 commit bee49db

File tree

3 files changed

+22
-16
lines changed

3 files changed

+22
-16
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ To enable this configuration use the `extends` property in your
145145
| [no-manual-cleanup](docs/rules/no-manual-cleanup.md) | Disallow the use of `cleanup` | | |
146146
| [no-wait-for-empty-callback](docs/rules/no-wait-for-empty-callback.md) | Disallow empty callbacks for `waitFor` and `waitForElementToBeRemoved` | | |
147147
| [prefer-explicit-assert](docs/rules/prefer-explicit-assert.md) | Suggest using explicit assertions rather than just `getBy*` queries | | |
148-
| [prefer-find-by](docs/rules/prefer-find-by.md) | Suggest using `findBy*` methods instead of the `waitFor` + `getBy` queries | ![recommended-badge][] ![angular-badge][] ![react-badge][] ![vue-badge][] | |
148+
| [prefer-find-by](docs/rules/prefer-find-by.md) | Suggest using `findBy*` methods instead of the `waitFor` + `getBy` queries | ![recommended-badge][] ![angular-badge][] ![react-badge][] ![vue-badge][] | ![fixable-badge][] |
149149
| [prefer-presence-queries](docs/rules/prefer-presence-queries.md) | Enforce specific queries when checking element is present or not | | |
150150
| [prefer-screen-queries](docs/rules/prefer-screen-queries.md) | Suggest using screen while using queries | | |
151151
| [prefer-wait-for](docs/rules/prefer-wait-for.md) | Use `waitFor` instead of deprecated wait methods | | ![fixable-badge][] |

lib/rules/prefer-find-by.ts

+14-6
Original file line numberDiff line numberDiff line change
@@ -26,23 +26,26 @@ export default ESLintUtils.RuleCreator(getDocsUrl)<Options, MessageIds>({
2626
messages: {
2727
preferFindBy: 'Prefer {{queryVariant}}{{queryMethod}} method over using await {{fullQuery}}'
2828
},
29-
fixable: null,
29+
fixable: 'code',
3030
schema: []
3131
},
3232
defaultOptions: [],
3333

3434
create(context) {
35+
const sourceCode = context.getSourceCode();
3536

36-
function reportInvalidUsage(node: TSESTree.CallExpression, { queryVariant, queryMethod, fullQuery }: { queryVariant: string, queryMethod: string, fullQuery: string}) {
37+
function reportInvalidUsage(node: TSESTree.CallExpression, { queryVariant, queryMethod, fullQuery, callArguments, caller }: { queryVariant: string, queryMethod: string, fullQuery: string, callArguments: TSESTree.Expression[], caller?: string }) {
3738
context.report({
3839
node,
3940
messageId: "preferFindBy",
4041
data: { queryVariant, queryMethod, fullQuery },
42+
fix(fixer) {
43+
const newCode = `${caller ? `${caller}.` : ''}${queryVariant}${queryMethod}(${callArguments.map((node) => sourceCode.getText(node)).join(', ')})`
44+
return fixer.replaceText(node, newCode)
45+
}
4146
});
4247
}
4348

44-
const sourceCode = context.getSourceCode();
45-
4649
return {
4750
'AwaitExpression > CallExpression'(node: TSESTree.CallExpression) {
4851
if (!isIdentifier(node.callee) || !WAIT_METHODS.includes(node.callee.name)) {
@@ -61,10 +64,14 @@ export default ESLintUtils.RuleCreator(getDocsUrl)<Options, MessageIds>({
6164
if (isMemberExpression(argument.body.callee) && isIdentifier(argument.body.callee.property) && isIdentifier(argument.body.callee.object) && SYNC_QUERIES_COMBINATIONS.includes(argument.body.callee.property.name)) {
6265
// shape of () => screen.getByText
6366
const queryMethod = argument.body.callee.property.name
67+
const caller = argument.body.callee.object.name
68+
6469
reportInvalidUsage(node, {
6570
queryMethod: queryMethod.split('By')[1],
6671
queryVariant: getFindByQueryVariant(queryMethod),
67-
fullQuery: sourceCode.getText(node)
72+
fullQuery: sourceCode.getText(node),
73+
callArguments: argument.body.arguments,
74+
caller,
6875
})
6976
return
7077
}
@@ -74,7 +81,8 @@ export default ESLintUtils.RuleCreator(getDocsUrl)<Options, MessageIds>({
7481
reportInvalidUsage(node, {
7582
queryMethod: queryMethod.split('By')[1],
7683
queryVariant: getFindByQueryVariant(queryMethod),
77-
fullQuery: sourceCode.getText(node)
84+
fullQuery: sourceCode.getText(node),
85+
callArguments: argument.body.arguments,
7886
})
7987
return
8088
}

tests/lib/rules/prefer-find-by.test.ts

+7-9
Original file line numberDiff line numberDiff line change
@@ -58,31 +58,29 @@ ruleTester.run(RULE_NAME, rule, {
5858
...WAIT_METHODS.reduce((acc: InvalidTestCase<'preferFindBy', []>[], waitMethod) => acc
5959
.concat(
6060
SYNC_QUERIES_COMBINATIONS.map((queryMethod: string) => ({
61-
code: `
62-
const submitButton = await ${waitMethod}(() => ${queryMethod}('foo', { name: 'baz' }))
63-
`,
61+
code: `const submitButton = await ${waitMethod}(() => ${queryMethod}('foo', { name: 'baz' }))`,
6462
errors: [{
6563
messageId: 'preferFindBy',
6664
data: {
6765
queryVariant: queryMethod.includes('All') ? 'findAllBy': 'findBy',
6866
queryMethod: queryMethod.split('By')[1],
6967
fullQuery: `${waitMethod}(() => ${queryMethod}('foo', { name: 'baz' }))`,
70-
}
71-
}]
68+
},
69+
}],
70+
output: `const submitButton = await ${queryMethod.includes('All') ? 'findAllBy': 'findBy'}${queryMethod.split('By')[1]}('foo', { name: 'baz' })`
7271
}))
7372
).concat(
7473
SYNC_QUERIES_COMBINATIONS.map((queryMethod: string) => ({
75-
code: `
76-
const submitButton = await ${waitMethod}(() => screen.${queryMethod}('foo', { name: 'baz' }))
77-
`,
74+
code: `const submitButton = await ${waitMethod}(() => screen.${queryMethod}('foo', { name: 'baz' }))`,
7875
errors: [{
7976
messageId: 'preferFindBy',
8077
data: {
8178
queryVariant: queryMethod.includes('All') ? 'findAllBy': 'findBy',
8279
queryMethod: queryMethod.split('By')[1],
8380
fullQuery: `${waitMethod}(() => screen.${queryMethod}('foo', { name: 'baz' }))`,
8481
}
85-
}]
82+
}],
83+
output: `const submitButton = await screen.${queryMethod.includes('All') ? 'findAllBy': 'findBy'}${queryMethod.split('By')[1]}('foo', { name: 'baz' })`
8684
}))
8785
),
8886
[])

0 commit comments

Comments
 (0)