Skip to content

Commit

Permalink
fix: better handling of template literals (#79)
Browse files Browse the repository at this point in the history
  • Loading branch information
benmonro authored Sep 1, 2020
1 parent 7547a54 commit d639db4
Show file tree
Hide file tree
Showing 3 changed files with 179 additions and 158 deletions.
21 changes: 18 additions & 3 deletions src/__tests__/lib/rules/prefer-to-have-style.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const errors = [
{ message: "Use toHaveStyle instead of asserting on element style" },
];
const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2015 } });
ruleTester.run("prefer-to-have-attribute", rule, {
ruleTester.run("prefer-to-have-style", rule, {
valid: [
`expect(el).toHaveStyle({foo:"bar"})`,
`expect(el.style).toMatchSnapshot()`,
Expand All @@ -23,6 +23,16 @@ ruleTester.run("prefer-to-have-attribute", rule, {
errors,
output: `expect(el).not.toHaveStyle({foo:"bar"})`,
},
{
code: "expect(el.style.backgroundImage).toBe(`url(${foo})`)",
errors,
output: "expect(el).toHaveStyle({backgroundImage:`url(${foo})`})",
},
{
code: "expect(el.style.backgroundImage).not.toBe(`url(${foo})`)",
errors,
output: "expect(el).not.toHaveStyle({backgroundImage:`url(${foo})`})",
},
{
code: `expect(el.style).toHaveProperty("background-color", "green")`,
errors,
Expand All @@ -39,9 +49,14 @@ ruleTester.run("prefer-to-have-attribute", rule, {
output: `expect(screen.getByTestId("foo")).toHaveStyle({scrollSnapType: "x mandatory"})`,
},
{
code: `expect(screen.getByTestId("foo").style["scroll-snap-type"]).not.toBe("x mandatory")`,
code: 'expect(el.style["scroll-snap-type"]).toBe(`${x} mandatory`)',
errors,
output: "expect(el).toHaveStyle({scrollSnapType: `${x} mandatory`})",
},
{
code: `expect(el.style["scroll-snap-type"]).not.toBe("x mandatory")`,
errors,
output: `expect(screen.getByTestId("foo")).not.toHaveStyle({scrollSnapType: "x mandatory"})`,
output: `expect(el).not.toHaveStyle({scrollSnapType: "x mandatory"})`,
},
{
code: `expect(el.style).toContain("background-color")`,
Expand Down
295 changes: 147 additions & 148 deletions src/rules/prefer-empty.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,157 +13,156 @@ export const meta = {
fixable: "code", // or "code" or "whitespace"
};

export const create = (context) => ({
[`BinaryExpression[left.property.name='innerHTML'][right.value=''][parent.callee.name='expect'][parent.parent.property.name=/toBe$|to(Strict)?Equal/]`](
node
) {
context.report({
node,
message: "Use toBeEmptyDOMElement instead of checking inner html.",
fix: (fixer) => [
fixer.removeRange([node.left.object.range[1], node.range[1]]),
fixer.replaceText(
node.parent.parent.property,
Boolean(node.parent.parent.parent.arguments[0].value) ===
node.operator.startsWith("=") // binary expression XNOR matcher boolean
? "toBeEmptyDOMElement"
: "not.toBeEmptyDOMElement"
),
fixer.remove(node.parent.parent.parent.arguments[0]),
],
});
},
[`BinaryExpression[left.property.name='firstChild'][right.value=null][parent.callee.name='expect'][parent.parent.property.name=/toBe$|to(Strict)?Equal/]`](
node
) {
context.report({
node,
message: "Use toBeEmptyDOMElement instead of checking inner html.",
fix: (fixer) => [
fixer.removeRange([node.left.object.range[1], node.range[1]]),
fixer.replaceText(
node.parent.parent.property,
Boolean(node.parent.parent.parent.arguments[0].value) ===
node.operator.startsWith("=") // binary expression XNOR matcher boolean
? "toBeEmptyDOMElement"
: "not.toBeEmptyDOMElement"
),
fixer.remove(node.parent.parent.parent.arguments[0]),
],
});
},
[`MemberExpression[property.name = 'innerHTML'][parent.callee.name = 'expect'][parent.parent.property.name = /toBe$|to(Strict)?Equal/]`](
node
) {
const args = node.parent.parent.parent.arguments[0];
if (isNonEmptyStringOrTemplateLiteral(args)) {
return;
}
export const create = (context) => {
function isNonEmptyStringOrTemplateLiteral(node) {
return !['""', "''", "``", "null"].includes(
context.getSourceCode().getText(node)
);
}

context.report({
node,
message: "Use toBeEmptyDOMElement instead of checking inner html.",
fix: (fixer) => [
fixer.removeRange([node.object.range[1], node.property.range[1]]),
fixer.replaceText(node.parent.parent.property, "toBeEmptyDOMElement"),
fixer.remove(node.parent.parent.parent.arguments[0]),
],
});
},
return {
[`BinaryExpression[left.property.name='innerHTML'][right.value=''][parent.callee.name='expect'][parent.parent.property.name=/toBe$|to(Strict)?Equal/]`](
node
) {
context.report({
node,
message: "Use toBeEmptyDOMElement instead of checking inner html.",
fix: (fixer) => [
fixer.removeRange([node.left.object.range[1], node.range[1]]),
fixer.replaceText(
node.parent.parent.property,
Boolean(node.parent.parent.parent.arguments[0].value) ===
node.operator.startsWith("=") // binary expression XNOR matcher boolean
? "toBeEmptyDOMElement"
: "not.toBeEmptyDOMElement"
),
fixer.remove(node.parent.parent.parent.arguments[0]),
],
});
},
[`BinaryExpression[left.property.name='firstChild'][right.value=null][parent.callee.name='expect'][parent.parent.property.name=/toBe$|to(Strict)?Equal/]`](
node
) {
context.report({
node,
message: "Use toBeEmptyDOMElement instead of checking inner html.",
fix: (fixer) => [
fixer.removeRange([node.left.object.range[1], node.range[1]]),
fixer.replaceText(
node.parent.parent.property,
Boolean(node.parent.parent.parent.arguments[0].value) ===
node.operator.startsWith("=") // binary expression XNOR matcher boolean
? "toBeEmptyDOMElement"
: "not.toBeEmptyDOMElement"
),
fixer.remove(node.parent.parent.parent.arguments[0]),
],
});
},
[`MemberExpression[property.name = 'innerHTML'][parent.callee.name = 'expect'][parent.parent.property.name = /toBe$|to(Strict)?Equal/]`](
node
) {
const args = node.parent.parent.parent.arguments[0];

[`MemberExpression[property.name='innerHTML'][parent.parent.property.name='not'][parent.parent.parent.property.name=/toBe$|to(Strict)?Equal$/][parent.parent.object.callee.name='expect']`](
node
) {
const args = node.parent.parent.parent.parent.arguments[0];
if (isNonEmptyStringOrTemplateLiteral(args)) {
return;
}
if (isNonEmptyStringOrTemplateLiteral(args)) {
return;
}

context.report({
node,
message: "Use toBeEmptyDOMElement instead of checking inner html.",
fix: (fixer) => [
fixer.removeRange([node.object.range[1], node.property.range[1]]),
fixer.replaceText(
node.parent.parent.parent.property,
"toBeEmptyDOMElement"
),
fixer.remove(node.parent.parent.parent.parent.arguments[0]),
],
});
},
[`MemberExpression[property.name = 'firstChild'][parent.callee.name = 'expect'][parent.parent.property.name = /toBeNull$/]`](
node
) {
context.report({
node,
message: "Use toBeEmptyDOMElement instead of checking inner html.",
fix: (fixer) => [
fixer.removeRange([node.object.range[1], node.property.range[1]]),
fixer.replaceText(node.parent.parent.property, "toBeEmptyDOMElement"),
],
});
},
[`MemberExpression[property.name='firstChild'][parent.parent.property.name='not'][parent.parent.parent.property.name=/toBe$|to(Strict)?Equal$/][parent.parent.object.callee.name='expect']`](
node
) {
if (node.parent.parent.parent.parent.arguments[0].value !== null) {
return;
}
context.report({
node,
message: "Use toBeEmptyDOMElement instead of checking inner html.",
fix: (fixer) => [
fixer.removeRange([node.object.range[1], node.property.range[1]]),
fixer.replaceText(node.parent.parent.property, "toBeEmptyDOMElement"),
fixer.remove(node.parent.parent.parent.arguments[0]),
],
});
},

context.report({
node,
message: "Use toBeEmptyDOMElement instead of checking inner html.",
fix: (fixer) => [
fixer.removeRange([node.object.range[1], node.property.range[1]]),
fixer.replaceText(
node.parent.parent.parent.property,
"toBeEmptyDOMElement"
),
fixer.remove(node.parent.parent.parent.parent.arguments[0]),
],
});
},
[`MemberExpression[property.name='firstChild'][parent.parent.property.name='not'][parent.parent.parent.property.name=/toBeNull$/][parent.parent.object.callee.name='expect']`](
node
) {
context.report({
node,
message: "Use toBeEmptyDOMElement instead of checking inner html.",
fix: (fixer) => [
fixer.removeRange([node.object.range[1], node.property.range[1]]),
fixer.replaceText(
node.parent.parent.parent.property,
"toBeEmptyDOMElement"
),
],
});
},
[`MemberExpression[property.name = 'firstChild'][parent.callee.name = 'expect'][parent.parent.property.name = /toBe$|to(Strict)?Equal/]`](
node
) {
if (node.parent.parent.parent.arguments[0].value !== null) {
return;
}
[`MemberExpression[property.name='innerHTML'][parent.parent.property.name='not'][parent.parent.parent.property.name=/toBe$|to(Strict)?Equal$/][parent.parent.object.callee.name='expect']`](
node
) {
const args = node.parent.parent.parent.parent.arguments[0];
if (isNonEmptyStringOrTemplateLiteral(args)) {
return;
}

context.report({
node,
message: "Use toBeEmptyDOMElement instead of checking inner html.",
fix: (fixer) => [
fixer.removeRange([node.object.range[1], node.property.range[1]]),
fixer.replaceText(node.parent.parent.property, "toBeEmptyDOMElement"),
fixer.remove(node.parent.parent.parent.arguments[0]),
],
});
},
});
context.report({
node,
message: "Use toBeEmptyDOMElement instead of checking inner html.",
fix: (fixer) => [
fixer.removeRange([node.object.range[1], node.property.range[1]]),
fixer.replaceText(
node.parent.parent.parent.property,
"toBeEmptyDOMElement"
),
fixer.remove(node.parent.parent.parent.parent.arguments[0]),
],
});
},
[`MemberExpression[property.name = 'firstChild'][parent.callee.name = 'expect'][parent.parent.property.name = /toBeNull$/]`](
node
) {
context.report({
node,
message: "Use toBeEmptyDOMElement instead of checking inner html.",
fix: (fixer) => [
fixer.removeRange([node.object.range[1], node.property.range[1]]),
fixer.replaceText(node.parent.parent.property, "toBeEmptyDOMElement"),
],
});
},
[`MemberExpression[property.name='firstChild'][parent.parent.property.name='not'][parent.parent.parent.property.name=/toBe$|to(Strict)?Equal$/][parent.parent.object.callee.name='expect']`](
node
) {
if (node.parent.parent.parent.parent.arguments[0].value !== null) {
return;
}

function isNonEmptyStringOrTemplateLiteral(node) {
return (
!(node.type === "Literal" || node.type === "TemplateLiteral") ||
node.value ||
node.name ||
(node?.quasis?.length > 0 &&
!(node?.quasis?.length === 1 && node?.quasis[0]?.value?.raw === ""))
);
}
context.report({
node,
message: "Use toBeEmptyDOMElement instead of checking inner html.",
fix: (fixer) => [
fixer.removeRange([node.object.range[1], node.property.range[1]]),
fixer.replaceText(
node.parent.parent.parent.property,
"toBeEmptyDOMElement"
),
fixer.remove(node.parent.parent.parent.parent.arguments[0]),
],
});
},
[`MemberExpression[property.name='firstChild'][parent.parent.property.name='not'][parent.parent.parent.property.name=/toBeNull$/][parent.parent.object.callee.name='expect']`](
node
) {
context.report({
node,
message: "Use toBeEmptyDOMElement instead of checking inner html.",
fix: (fixer) => [
fixer.removeRange([node.object.range[1], node.property.range[1]]),
fixer.replaceText(
node.parent.parent.parent.property,
"toBeEmptyDOMElement"
),
],
});
},
[`MemberExpression[property.name = 'firstChild'][parent.callee.name = 'expect'][parent.parent.property.name = /toBe$|to(Strict)?Equal/]`](
node
) {
if (node.parent.parent.parent.arguments[0].value !== null) {
return;
}

context.report({
node,
message: "Use toBeEmptyDOMElement instead of checking inner html.",
fix: (fixer) => [
fixer.removeRange([node.object.range[1], node.property.range[1]]),
fixer.replaceText(node.parent.parent.property, "toBeEmptyDOMElement"),
fixer.remove(node.parent.parent.parent.arguments[0]),
],
});
},
};
};
Loading

0 comments on commit d639db4

Please sign in to comment.