securityos/node_modules/eslint-plugin-jest-dom/dist/rules/prefer-to-have-style.js

166 lines
9.3 KiB
JavaScript
Raw Permalink Normal View History

2024-09-06 15:32:35 +00:00
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.meta = exports.create = void 0;
/**
* @fileoverview prefer toHaveStyle over checking element style
* @author Ben Monro
*/
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
const camelCase = str => str.replace(/-([a-z])/g, c => c[1].toUpperCase());
const meta = {
docs: {
category: "Best Practices",
url: "prefer-to-have-style",
description: "prefer toHaveStyle over checking element style",
recommended: true
},
fixable: "code"
};
exports.meta = meta;
const create = context => {
function getReplacementObjectProperty(styleName) {
if (styleName.type === "Literal") {
return camelCase(styleName.value);
}
return `[${context.getSourceCode().getText(styleName)}]`;
}
function getReplacementStyleParam(styleName, styleValue) {
return styleName.type === "Literal" ? `{${camelCase(styleName.value)}: ${context.getSourceCode().getText(styleValue)}}` : `${context.getSourceCode().getText(styleName).slice(0, -1)}: ${styleValue.type === "TemplateLiteral" ? context.getSourceCode().getText(styleValue).substring(1) : `${styleValue.value}\``}`;
}
return {
//expect(el.style.foo).toBe("bar");
[`MemberExpression[property.name=style][parent.computed=false][parent.parent.parent.property.name=/toBe$|to(Strict)?Equal/][parent.parent.parent.parent.arguments.0.type=/(Template)?Literal/][parent.parent.callee.name=expect]`](node) {
const styleName = node.parent.property;
const [styleValue] = node.parent.parent.parent.parent.arguments;
const matcher = node.parent.parent.parent.property;
context.report({
node: node.property,
message: "Use toHaveStyle instead of asserting on element style",
fix(fixer) {
return [fixer.removeRange([node.object.range[1], styleName.range[1]]), fixer.replaceText(matcher, "toHaveStyle"), fixer.replaceText(styleValue, `{${styleName.name}:${context.getSourceCode().getText(styleValue)}}`)];
}
});
},
//expect(el.style.foo).not.toBe("bar");
[`MemberExpression[property.name=style][parent.computed=false][parent.parent.parent.property.name=not][parent.parent.parent.parent.property.name=/toBe$|to(Strict)?Equal/][parent.parent.parent.parent.parent.arguments.0.type=/(Template)?Literal$/][parent.parent.callee.name=expect]`](node) {
const styleName = node.parent.property;
const styleValue = node.parent.parent.parent.parent.parent.arguments[0];
const matcher = node.parent.parent.parent.parent.property;
context.report({
node: node.property,
message: "Use toHaveStyle instead of asserting on element style",
fix(fixer) {
return [fixer.removeRange([node.object.range[1], styleName.range[1]]), fixer.replaceText(matcher, "toHaveStyle"), fixer.replaceText(styleValue, `{${styleName.name}:${context.getSourceCode().getText(styleValue)}}`)];
}
});
},
// expect(el.style).toContain("foo-bar")
[`MemberExpression[property.name=style][parent.parent.property.name=toContain][parent.parent.parent.arguments.0.type=/(Template)?Literal$/][parent.callee.name=expect]`](node) {
const [styleName] = node.parent.parent.parent.arguments;
const matcher = node.parent.parent.property;
context.report({
node: node.property,
message: "Use toHaveStyle instead of asserting on element style",
fix(fixer) {
return [fixer.removeRange([node.object.range[1], node.property.range[1]]), fixer.replaceText(matcher, "toHaveStyle"), fixer.replaceText(styleName, styleName.type === "Literal" ? `{${camelCase(styleName.value)}: expect.anything()}` : context.getSourceCode().getText(styleName))];
}
});
},
// expect(el.style).not.toContain("foo-bar")
[`MemberExpression[property.name=style][parent.parent.property.name=not][parent.parent.parent.property.name=toContain][parent.parent.parent.parent.arguments.0.type=/(Template)?Literal$/]`](node) {
const [styleName] = node.parent.parent.parent.parent.arguments;
const matcher = node.parent.parent.parent.property;
context.report({
node: node.property,
message: "Use toHaveStyle instead of asserting on element style",
fix(fixer) {
return [fixer.removeRange([node.object.range[1], node.property.range[1]]), fixer.replaceText(matcher, "toHaveStyle"), fixer.replaceText(styleName, styleName.type === "Literal" ? `{${camelCase(styleName.value)}: expect.anything()}` : context.getSourceCode().getText(styleName))];
}
});
},
//expect(el).toHaveAttribute("style", "foo: bar");
[`CallExpression[callee.property.name=toHaveAttribute][arguments.0.value=style][arguments.1][callee.object.callee.name=expect]`](node) {
context.report({
node: node.arguments[0],
message: "Use toHaveStyle instead of asserting on element style",
fix(fixer) {
return [fixer.replaceText(node.callee.property, "toHaveStyle"), fixer.removeRange([node.arguments[0].range[0], node.arguments[1].range[0]])];
}
});
},
//expect(el.style["foo-bar"]).toBe("baz")
[`MemberExpression[property.name=style][parent.computed=true][parent.parent.parent.property.name=/toBe$|to(Strict)?Equal/][parent.parent.parent.parent.arguments.0.type=/((Template)?Literal|Identifier)/][parent.parent.callee.name=expect]`](node) {
const styleName = node.parent.property;
const [styleValue] = node.parent.parent.parent.parent.arguments;
const matcher = node.parent.parent.parent.property;
const startOfStyleMemberExpression = node.object.range[1];
const endOfStyleMemberExpression = node.parent.parent.arguments[0].range[1];
let fix = null;
if (typeof styleValue.value !== "number" && !(styleValue.value instanceof RegExp)) {
fix = fixer => {
return [fixer.removeRange([startOfStyleMemberExpression, endOfStyleMemberExpression]), fixer.replaceText(matcher, "toHaveStyle"), fixer.replaceText(styleValue, typeof styleName.value === "number" ? `{${getReplacementObjectProperty(styleValue)}: expect.anything()}` : getReplacementStyleParam(styleName, styleValue))];
};
}
context.report({
node: node.property,
message: "Use toHaveStyle instead of asserting on element style",
fix
});
},
//expect(el.style["foo-bar"]).not.toBe("baz")
[`MemberExpression[property.name=style][parent.computed=true][parent.parent.parent.property.name=not][parent.parent.parent.parent.parent.callee.property.name=/toBe$|to(Strict)?Equal/][parent.parent.parent.parent.parent.arguments.0.type=/(Template)?Literal/][parent.parent.callee.name=expect]`](node) {
const styleName = node.parent.property;
const [styleValue] = node.parent.parent.parent.parent.parent.arguments;
const matcher = node.parent.parent.parent.parent.property;
const endOfStyleMemberExpression = node.parent.parent.arguments[0].range[1];
let fix = null;
if (typeof styleName.value !== "number") {
fix = fixer => {
return [fixer.removeRange([node.object.range[1], endOfStyleMemberExpression]), fixer.replaceText(matcher, "toHaveStyle"), fixer.replaceText(styleValue, getReplacementStyleParam(styleName, styleValue))];
};
}
context.report({
node: node.property,
message: "Use toHaveStyle instead of asserting on element style",
fix
});
},
//expect(foo.style).toHaveProperty("foo", "bar")
[`MemberExpression[property.name=style][parent.parent.property.name=toHaveProperty][parent.callee.name=expect]`](node) {
const [styleName, styleValue] = node.parent.parent.parent.arguments;
const matcher = node.parent.parent.property;
context.report({
node: node.property,
message: "Use toHaveStyle instead of asserting on element style",
fix(fixer) {
if (!styleValue || !["Literal", "TemplateLiteral"].includes(styleValue.type)) {
return null;
}
return [fixer.removeRange([node.object.range[1], node.property.range[1]]), fixer.replaceText(matcher, "toHaveStyle"), fixer.replaceTextRange([styleName.range[0], styleValue.range[1]], `{${camelCase(styleName.value)}: ${context.getSourceCode().getText(styleValue)}}`)];
}
});
},
//expect(foo.style).not.toHaveProperty("foo", "bar")
[`MemberExpression[property.name=style][parent.parent.property.name=not][parent.parent.parent.property.name=toHaveProperty][parent.callee.name=expect]`](node) {
const [styleName, styleValue] = node.parent.parent.parent.parent.arguments;
const matcher = node.parent.parent.parent.property;
context.report({
node: node.property,
message: "Use toHaveStyle instead of asserting on element style",
fix(fixer) {
if (!styleValue || !["Literal", "TemplateLiteral"].includes(styleValue.type)) {
return null;
}
return [fixer.removeRange([node.object.range[1], node.property.range[1]]), fixer.replaceText(matcher, "toHaveStyle"), fixer.replaceTextRange([styleName.range[0], styleValue.range[1]], `{${camelCase(styleName.value)}: ${context.getSourceCode().getText(styleValue)}}`)];
}
});
}
};
};
exports.create = create;