Skip to content

Commit d639db4

Browse files
authored
fix: better handling of template literals (#79)
1 parent 7547a54 commit d639db4

File tree

3 files changed

+179
-158
lines changed

3 files changed

+179
-158
lines changed

src/__tests__/lib/rules/prefer-to-have-style.js

+18-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const errors = [
55
{ message: "Use toHaveStyle instead of asserting on element style" },
66
];
77
const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2015 } });
8-
ruleTester.run("prefer-to-have-attribute", rule, {
8+
ruleTester.run("prefer-to-have-style", rule, {
99
valid: [
1010
`expect(el).toHaveStyle({foo:"bar"})`,
1111
`expect(el.style).toMatchSnapshot()`,
@@ -23,6 +23,16 @@ ruleTester.run("prefer-to-have-attribute", rule, {
2323
errors,
2424
output: `expect(el).not.toHaveStyle({foo:"bar"})`,
2525
},
26+
{
27+
code: "expect(el.style.backgroundImage).toBe(`url(${foo})`)",
28+
errors,
29+
output: "expect(el).toHaveStyle({backgroundImage:`url(${foo})`})",
30+
},
31+
{
32+
code: "expect(el.style.backgroundImage).not.toBe(`url(${foo})`)",
33+
errors,
34+
output: "expect(el).not.toHaveStyle({backgroundImage:`url(${foo})`})",
35+
},
2636
{
2737
code: `expect(el.style).toHaveProperty("background-color", "green")`,
2838
errors,
@@ -39,9 +49,14 @@ ruleTester.run("prefer-to-have-attribute", rule, {
3949
output: `expect(screen.getByTestId("foo")).toHaveStyle({scrollSnapType: "x mandatory"})`,
4050
},
4151
{
42-
code: `expect(screen.getByTestId("foo").style["scroll-snap-type"]).not.toBe("x mandatory")`,
52+
code: 'expect(el.style["scroll-snap-type"]).toBe(`${x} mandatory`)',
53+
errors,
54+
output: "expect(el).toHaveStyle({scrollSnapType: `${x} mandatory`})",
55+
},
56+
{
57+
code: `expect(el.style["scroll-snap-type"]).not.toBe("x mandatory")`,
4358
errors,
44-
output: `expect(screen.getByTestId("foo")).not.toHaveStyle({scrollSnapType: "x mandatory"})`,
59+
output: `expect(el).not.toHaveStyle({scrollSnapType: "x mandatory"})`,
4560
},
4661
{
4762
code: `expect(el.style).toContain("background-color")`,

src/rules/prefer-empty.js

+147-148
Original file line numberDiff line numberDiff line change
@@ -13,157 +13,156 @@ export const meta = {
1313
fixable: "code", // or "code" or "whitespace"
1414
};
1515

16-
export const create = (context) => ({
17-
[`BinaryExpression[left.property.name='innerHTML'][right.value=''][parent.callee.name='expect'][parent.parent.property.name=/toBe$|to(Strict)?Equal/]`](
18-
node
19-
) {
20-
context.report({
21-
node,
22-
message: "Use toBeEmptyDOMElement instead of checking inner html.",
23-
fix: (fixer) => [
24-
fixer.removeRange([node.left.object.range[1], node.range[1]]),
25-
fixer.replaceText(
26-
node.parent.parent.property,
27-
Boolean(node.parent.parent.parent.arguments[0].value) ===
28-
node.operator.startsWith("=") // binary expression XNOR matcher boolean
29-
? "toBeEmptyDOMElement"
30-
: "not.toBeEmptyDOMElement"
31-
),
32-
fixer.remove(node.parent.parent.parent.arguments[0]),
33-
],
34-
});
35-
},
36-
[`BinaryExpression[left.property.name='firstChild'][right.value=null][parent.callee.name='expect'][parent.parent.property.name=/toBe$|to(Strict)?Equal/]`](
37-
node
38-
) {
39-
context.report({
40-
node,
41-
message: "Use toBeEmptyDOMElement instead of checking inner html.",
42-
fix: (fixer) => [
43-
fixer.removeRange([node.left.object.range[1], node.range[1]]),
44-
fixer.replaceText(
45-
node.parent.parent.property,
46-
Boolean(node.parent.parent.parent.arguments[0].value) ===
47-
node.operator.startsWith("=") // binary expression XNOR matcher boolean
48-
? "toBeEmptyDOMElement"
49-
: "not.toBeEmptyDOMElement"
50-
),
51-
fixer.remove(node.parent.parent.parent.arguments[0]),
52-
],
53-
});
54-
},
55-
[`MemberExpression[property.name = 'innerHTML'][parent.callee.name = 'expect'][parent.parent.property.name = /toBe$|to(Strict)?Equal/]`](
56-
node
57-
) {
58-
const args = node.parent.parent.parent.arguments[0];
59-
if (isNonEmptyStringOrTemplateLiteral(args)) {
60-
return;
61-
}
16+
export const create = (context) => {
17+
function isNonEmptyStringOrTemplateLiteral(node) {
18+
return !['""', "''", "``", "null"].includes(
19+
context.getSourceCode().getText(node)
20+
);
21+
}
6222

63-
context.report({
64-
node,
65-
message: "Use toBeEmptyDOMElement instead of checking inner html.",
66-
fix: (fixer) => [
67-
fixer.removeRange([node.object.range[1], node.property.range[1]]),
68-
fixer.replaceText(node.parent.parent.property, "toBeEmptyDOMElement"),
69-
fixer.remove(node.parent.parent.parent.arguments[0]),
70-
],
71-
});
72-
},
23+
return {
24+
[`BinaryExpression[left.property.name='innerHTML'][right.value=''][parent.callee.name='expect'][parent.parent.property.name=/toBe$|to(Strict)?Equal/]`](
25+
node
26+
) {
27+
context.report({
28+
node,
29+
message: "Use toBeEmptyDOMElement instead of checking inner html.",
30+
fix: (fixer) => [
31+
fixer.removeRange([node.left.object.range[1], node.range[1]]),
32+
fixer.replaceText(
33+
node.parent.parent.property,
34+
Boolean(node.parent.parent.parent.arguments[0].value) ===
35+
node.operator.startsWith("=") // binary expression XNOR matcher boolean
36+
? "toBeEmptyDOMElement"
37+
: "not.toBeEmptyDOMElement"
38+
),
39+
fixer.remove(node.parent.parent.parent.arguments[0]),
40+
],
41+
});
42+
},
43+
[`BinaryExpression[left.property.name='firstChild'][right.value=null][parent.callee.name='expect'][parent.parent.property.name=/toBe$|to(Strict)?Equal/]`](
44+
node
45+
) {
46+
context.report({
47+
node,
48+
message: "Use toBeEmptyDOMElement instead of checking inner html.",
49+
fix: (fixer) => [
50+
fixer.removeRange([node.left.object.range[1], node.range[1]]),
51+
fixer.replaceText(
52+
node.parent.parent.property,
53+
Boolean(node.parent.parent.parent.arguments[0].value) ===
54+
node.operator.startsWith("=") // binary expression XNOR matcher boolean
55+
? "toBeEmptyDOMElement"
56+
: "not.toBeEmptyDOMElement"
57+
),
58+
fixer.remove(node.parent.parent.parent.arguments[0]),
59+
],
60+
});
61+
},
62+
[`MemberExpression[property.name = 'innerHTML'][parent.callee.name = 'expect'][parent.parent.property.name = /toBe$|to(Strict)?Equal/]`](
63+
node
64+
) {
65+
const args = node.parent.parent.parent.arguments[0];
7366

74-
[`MemberExpression[property.name='innerHTML'][parent.parent.property.name='not'][parent.parent.parent.property.name=/toBe$|to(Strict)?Equal$/][parent.parent.object.callee.name='expect']`](
75-
node
76-
) {
77-
const args = node.parent.parent.parent.parent.arguments[0];
78-
if (isNonEmptyStringOrTemplateLiteral(args)) {
79-
return;
80-
}
67+
if (isNonEmptyStringOrTemplateLiteral(args)) {
68+
return;
69+
}
8170

82-
context.report({
83-
node,
84-
message: "Use toBeEmptyDOMElement instead of checking inner html.",
85-
fix: (fixer) => [
86-
fixer.removeRange([node.object.range[1], node.property.range[1]]),
87-
fixer.replaceText(
88-
node.parent.parent.parent.property,
89-
"toBeEmptyDOMElement"
90-
),
91-
fixer.remove(node.parent.parent.parent.parent.arguments[0]),
92-
],
93-
});
94-
},
95-
[`MemberExpression[property.name = 'firstChild'][parent.callee.name = 'expect'][parent.parent.property.name = /toBeNull$/]`](
96-
node
97-
) {
98-
context.report({
99-
node,
100-
message: "Use toBeEmptyDOMElement instead of checking inner html.",
101-
fix: (fixer) => [
102-
fixer.removeRange([node.object.range[1], node.property.range[1]]),
103-
fixer.replaceText(node.parent.parent.property, "toBeEmptyDOMElement"),
104-
],
105-
});
106-
},
107-
[`MemberExpression[property.name='firstChild'][parent.parent.property.name='not'][parent.parent.parent.property.name=/toBe$|to(Strict)?Equal$/][parent.parent.object.callee.name='expect']`](
108-
node
109-
) {
110-
if (node.parent.parent.parent.parent.arguments[0].value !== null) {
111-
return;
112-
}
71+
context.report({
72+
node,
73+
message: "Use toBeEmptyDOMElement instead of checking inner html.",
74+
fix: (fixer) => [
75+
fixer.removeRange([node.object.range[1], node.property.range[1]]),
76+
fixer.replaceText(node.parent.parent.property, "toBeEmptyDOMElement"),
77+
fixer.remove(node.parent.parent.parent.arguments[0]),
78+
],
79+
});
80+
},
11381

114-
context.report({
115-
node,
116-
message: "Use toBeEmptyDOMElement instead of checking inner html.",
117-
fix: (fixer) => [
118-
fixer.removeRange([node.object.range[1], node.property.range[1]]),
119-
fixer.replaceText(
120-
node.parent.parent.parent.property,
121-
"toBeEmptyDOMElement"
122-
),
123-
fixer.remove(node.parent.parent.parent.parent.arguments[0]),
124-
],
125-
});
126-
},
127-
[`MemberExpression[property.name='firstChild'][parent.parent.property.name='not'][parent.parent.parent.property.name=/toBeNull$/][parent.parent.object.callee.name='expect']`](
128-
node
129-
) {
130-
context.report({
131-
node,
132-
message: "Use toBeEmptyDOMElement instead of checking inner html.",
133-
fix: (fixer) => [
134-
fixer.removeRange([node.object.range[1], node.property.range[1]]),
135-
fixer.replaceText(
136-
node.parent.parent.parent.property,
137-
"toBeEmptyDOMElement"
138-
),
139-
],
140-
});
141-
},
142-
[`MemberExpression[property.name = 'firstChild'][parent.callee.name = 'expect'][parent.parent.property.name = /toBe$|to(Strict)?Equal/]`](
143-
node
144-
) {
145-
if (node.parent.parent.parent.arguments[0].value !== null) {
146-
return;
147-
}
82+
[`MemberExpression[property.name='innerHTML'][parent.parent.property.name='not'][parent.parent.parent.property.name=/toBe$|to(Strict)?Equal$/][parent.parent.object.callee.name='expect']`](
83+
node
84+
) {
85+
const args = node.parent.parent.parent.parent.arguments[0];
86+
if (isNonEmptyStringOrTemplateLiteral(args)) {
87+
return;
88+
}
14889

149-
context.report({
150-
node,
151-
message: "Use toBeEmptyDOMElement instead of checking inner html.",
152-
fix: (fixer) => [
153-
fixer.removeRange([node.object.range[1], node.property.range[1]]),
154-
fixer.replaceText(node.parent.parent.property, "toBeEmptyDOMElement"),
155-
fixer.remove(node.parent.parent.parent.arguments[0]),
156-
],
157-
});
158-
},
159-
});
90+
context.report({
91+
node,
92+
message: "Use toBeEmptyDOMElement instead of checking inner html.",
93+
fix: (fixer) => [
94+
fixer.removeRange([node.object.range[1], node.property.range[1]]),
95+
fixer.replaceText(
96+
node.parent.parent.parent.property,
97+
"toBeEmptyDOMElement"
98+
),
99+
fixer.remove(node.parent.parent.parent.parent.arguments[0]),
100+
],
101+
});
102+
},
103+
[`MemberExpression[property.name = 'firstChild'][parent.callee.name = 'expect'][parent.parent.property.name = /toBeNull$/]`](
104+
node
105+
) {
106+
context.report({
107+
node,
108+
message: "Use toBeEmptyDOMElement instead of checking inner html.",
109+
fix: (fixer) => [
110+
fixer.removeRange([node.object.range[1], node.property.range[1]]),
111+
fixer.replaceText(node.parent.parent.property, "toBeEmptyDOMElement"),
112+
],
113+
});
114+
},
115+
[`MemberExpression[property.name='firstChild'][parent.parent.property.name='not'][parent.parent.parent.property.name=/toBe$|to(Strict)?Equal$/][parent.parent.object.callee.name='expect']`](
116+
node
117+
) {
118+
if (node.parent.parent.parent.parent.arguments[0].value !== null) {
119+
return;
120+
}
160121

161-
function isNonEmptyStringOrTemplateLiteral(node) {
162-
return (
163-
!(node.type === "Literal" || node.type === "TemplateLiteral") ||
164-
node.value ||
165-
node.name ||
166-
(node?.quasis?.length > 0 &&
167-
!(node?.quasis?.length === 1 && node?.quasis[0]?.value?.raw === ""))
168-
);
169-
}
122+
context.report({
123+
node,
124+
message: "Use toBeEmptyDOMElement instead of checking inner html.",
125+
fix: (fixer) => [
126+
fixer.removeRange([node.object.range[1], node.property.range[1]]),
127+
fixer.replaceText(
128+
node.parent.parent.parent.property,
129+
"toBeEmptyDOMElement"
130+
),
131+
fixer.remove(node.parent.parent.parent.parent.arguments[0]),
132+
],
133+
});
134+
},
135+
[`MemberExpression[property.name='firstChild'][parent.parent.property.name='not'][parent.parent.parent.property.name=/toBeNull$/][parent.parent.object.callee.name='expect']`](
136+
node
137+
) {
138+
context.report({
139+
node,
140+
message: "Use toBeEmptyDOMElement instead of checking inner html.",
141+
fix: (fixer) => [
142+
fixer.removeRange([node.object.range[1], node.property.range[1]]),
143+
fixer.replaceText(
144+
node.parent.parent.parent.property,
145+
"toBeEmptyDOMElement"
146+
),
147+
],
148+
});
149+
},
150+
[`MemberExpression[property.name = 'firstChild'][parent.callee.name = 'expect'][parent.parent.property.name = /toBe$|to(Strict)?Equal/]`](
151+
node
152+
) {
153+
if (node.parent.parent.parent.arguments[0].value !== null) {
154+
return;
155+
}
156+
157+
context.report({
158+
node,
159+
message: "Use toBeEmptyDOMElement instead of checking inner html.",
160+
fix: (fixer) => [
161+
fixer.removeRange([node.object.range[1], node.property.range[1]]),
162+
fixer.replaceText(node.parent.parent.property, "toBeEmptyDOMElement"),
163+
fixer.remove(node.parent.parent.parent.arguments[0]),
164+
],
165+
});
166+
},
167+
};
168+
};

0 commit comments

Comments
 (0)