-
Notifications
You must be signed in to change notification settings - Fork 40
fix(rules): only report errors for prefer-to-have-value on valid DTL queries #122
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
e77f6b9
5676c99
94aad94
d20d049
932bfda
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,14 +15,34 @@ import * as rule from "../../../rules/prefer-to-have-value"; | |
// Tests | ||
//------------------------------------------------------------------------------ | ||
|
||
const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2015 } }); | ||
const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2020 } }); | ||
|
||
const errors = [{ messageId: "use-to-have-value" }]; | ||
ruleTester.run("prefer-empty", rule, { | ||
ruleTester.run("prefer-to-have-value", rule, { | ||
valid: [ | ||
`expect(element).toHaveValue('foo')`, | ||
`expect(element.value).toBeGreaterThan(2);`, | ||
`expect(element.value).toBeLessThan(2);`, | ||
|
||
`const element = document.getElementById('asdfasf'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. for now this is fine, but it would be nice to at some point support document queries as well as RTL. :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, my initial idea was to create a list of all the DOM api query functions but then it occurred to me that you can also get a reference to a dom element via a lot of other keys like |
||
expect(element.value).toEqual('foo');`, | ||
|
||
`let element; | ||
element = someOtherFunction(); | ||
expect(element.value).toStrictEqual('foo');`, | ||
|
||
`const element = { value: 'foo' }; | ||
expect(element.value).toBe('foo');`, | ||
|
||
`const element = document.getElementById('asdfasf'); | ||
expect(element.value).not.toEqual('foo');`, | ||
|
||
`let element; | ||
element = someOtherFunction(); | ||
expect(element.value).not.toStrictEqual('foo');`, | ||
|
||
`const element = { value: 'foo' }; | ||
expect(element.value).not.toBe('foo');`, | ||
], | ||
invalid: [ | ||
{ | ||
|
@@ -45,35 +65,159 @@ ruleTester.run("prefer-empty", rule, { | |
errors, | ||
output: `expect(element).not.toHaveValue("foo")`, | ||
}, | ||
//========================================================================== | ||
{ | ||
code: `expect(element.value).toBe('foo')`, | ||
code: `expect(screen.getByRole("textbox").value).toEqual("foo")`, | ||
errors, | ||
output: `expect(element).toHaveValue('foo')`, | ||
output: `expect(screen.getByRole("textbox")).toHaveValue("foo")`, | ||
}, | ||
{ | ||
code: `expect(element.value).toEqual('foo')`, | ||
code: `expect(screen.queryByRole("dropdown").value).toEqual("foo")`, | ||
errors, | ||
output: `expect(element).toHaveValue('foo')`, | ||
output: `expect(screen.queryByRole("dropdown")).toHaveValue("foo")`, | ||
}, | ||
{ | ||
code: `expect(element.value).toStrictEqual('foo')`, | ||
code: `async function x() { expect((await screen.findByRole("textbox")).value).toEqual("foo") }`, | ||
errors, | ||
output: `expect(element).toHaveValue('foo')`, | ||
output: `async function x() { expect((await screen.findByRole("textbox"))).toHaveValue("foo") }`, | ||
}, | ||
{ | ||
code: `expect(element.value).not.toBe('foo')`, | ||
code: `const element = screen.getByRole("textbox"); expect(element.value).toBe("foo");`, | ||
errors, | ||
output: `expect(element).not.toHaveValue('foo')`, | ||
output: `const element = screen.getByRole("textbox"); expect(element).toHaveValue("foo");`, | ||
}, | ||
{ | ||
code: `expect(element.value).not.toEqual('foo')`, | ||
code: `expect(screen.getByRole("textbox").value).not.toEqual("foo")`, | ||
errors, | ||
output: `expect(element).not.toHaveValue('foo')`, | ||
output: `expect(screen.getByRole("textbox")).not.toHaveValue("foo")`, | ||
}, | ||
{ | ||
code: `expect(element.value).not.toStrictEqual('foo')`, | ||
code: `expect(screen.queryByRole("dropdown").value).not.toEqual("foo")`, | ||
errors, | ||
output: `expect(element).not.toHaveValue('foo')`, | ||
output: `expect(screen.queryByRole("dropdown")).not.toHaveValue("foo")`, | ||
}, | ||
{ | ||
code: `async function x() { expect((await screen.getByRole("textbox")).value).not.toEqual("foo") }`, | ||
errors, | ||
output: `async function x() { expect((await screen.getByRole("textbox"))).not.toHaveValue("foo") }`, | ||
}, | ||
{ | ||
code: `const element = screen.getByRole("textbox"); expect(element.value).not.toBe("foo");`, | ||
errors, | ||
output: `const element = screen.getByRole("textbox"); expect(element).not.toHaveValue("foo");`, | ||
}, | ||
//========================================================================== | ||
{ | ||
code: `expect(screen.getByTestId('bananas').value).toEqual('foo')`, | ||
errors: [ | ||
{ | ||
...errors[0], | ||
suggestions: [ | ||
{ | ||
desc: "Replace toEqual with toHaveValue", | ||
output: `expect(screen.getByTestId('bananas')).toHaveValue('foo')`, | ||
}, | ||
], | ||
}, | ||
], | ||
}, | ||
{ | ||
code: `expect(screen.queryByTestId('bananas').value).toBe('foo')`, | ||
errors: [ | ||
{ | ||
...errors[0], | ||
suggestions: [ | ||
{ | ||
desc: "Replace toBe with toHaveValue", | ||
output: `expect(screen.queryByTestId('bananas')).toHaveValue('foo')`, | ||
}, | ||
], | ||
}, | ||
], | ||
}, | ||
{ | ||
code: `async function x() { expect((await screen.findByTestId("bananas")).value).toStrictEqual("foo") }`, | ||
errors: [ | ||
{ | ||
...errors[0], | ||
suggestions: [ | ||
{ | ||
desc: "Replace toStrictEqual with toHaveValue", | ||
output: `async function x() { expect((await screen.findByTestId("bananas"))).toHaveValue("foo") }`, | ||
}, | ||
], | ||
}, | ||
], | ||
}, | ||
{ | ||
code: `let element; element = screen.getByTestId('bananas'); expect(element.value).toEqual('foo');`, | ||
errors: [ | ||
{ | ||
...errors[0], | ||
suggestions: [ | ||
{ | ||
desc: "Replace toEqual with toHaveValue", | ||
output: `let element; element = screen.getByTestId('bananas'); expect(element).toHaveValue('foo');`, | ||
}, | ||
], | ||
}, | ||
], | ||
}, | ||
{ | ||
code: `expect(screen.getByTestId('bananas').value).not.toEqual('foo')`, | ||
errors: [ | ||
{ | ||
...errors[0], | ||
suggestions: [ | ||
{ | ||
desc: "Replace toEqual with toHaveValue", | ||
output: `expect(screen.getByTestId('bananas')).not.toHaveValue('foo')`, | ||
}, | ||
], | ||
}, | ||
], | ||
}, | ||
{ | ||
code: `expect(screen.queryByTestId('bananas').value).not.toBe('foo')`, | ||
errors: [ | ||
{ | ||
...errors[0], | ||
suggestions: [ | ||
{ | ||
desc: "Replace toBe with toHaveValue", | ||
output: `expect(screen.queryByTestId('bananas')).not.toHaveValue('foo')`, | ||
}, | ||
], | ||
}, | ||
], | ||
}, | ||
{ | ||
code: `async function x() { expect((await screen.findByTestId("bananas")).value).not.toStrictEqual("foo") }`, | ||
errors: [ | ||
{ | ||
...errors[0], | ||
suggestions: [ | ||
{ | ||
desc: "Replace toStrictEqual with toHaveValue", | ||
output: `async function x() { expect((await screen.findByTestId("bananas"))).not.toHaveValue("foo") }`, | ||
}, | ||
], | ||
}, | ||
], | ||
}, | ||
{ | ||
code: `let element; element = screen.getByTestId('bananas'); expect(element.value).not.toEqual('foo');`, | ||
errors: [ | ||
{ | ||
...errors[0], | ||
suggestions: [ | ||
{ | ||
desc: "Replace toEqual with toHaveValue", | ||
output: `let element; element = screen.getByTestId('bananas'); expect(element).not.toHaveValue('foo');`, | ||
}, | ||
], | ||
}, | ||
], | ||
}, | ||
], | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
/** | ||
* Gets the inner relevant node (CallExpression, Identity, et al.) given a generic expression node | ||
* await someAsyncFunc() => someAsyncFunc() | ||
* someElement as HTMLDivElement => someElement | ||
* | ||
* @param {Object} expression - An expression node | ||
* @returns {Object} - A node | ||
*/ | ||
export function getInnerNodeFrom(expression) { | ||
return expression.type === "TSAsExpression" | ||
? getInnerNodeFrom(expression.expression) | ||
: expression.type === "AwaitExpression" | ||
? getInnerNodeFrom(expression.argument) | ||
: expression; | ||
} | ||
|
||
/** | ||
* Get the node corresponding to the latest assignment to a variable named `identifierName` | ||
* | ||
* @param {Object} context - Context for a rule | ||
* @param {String} identifierName - Name of an identifier | ||
* @returns {Object} - A node, possibly undefined | ||
*/ | ||
export function getAssignmentForIdentifier(context, identifierName) { | ||
const variable = context.getScope().set.get(identifierName); | ||
|
||
if (!variable) return; | ||
const init = variable.defs[0].node.init; | ||
|
||
let assignmentNode; | ||
if (init) { | ||
// let foo = bar; | ||
assignmentNode = getInnerNodeFrom(init); | ||
} else { | ||
// let foo; | ||
// foo = bar; | ||
const assignmentRef = variable.references | ||
.reverse() | ||
.find((ref) => !!ref.writeExpr); | ||
if (!assignmentRef) { | ||
return; | ||
} | ||
assignmentNode = getInnerNodeFrom(assignmentRef.writeExpr); | ||
} | ||
return assignmentNode; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wow thanks, that's what I get for copy pasta. :)