import { getCallName } from "../utils.js";import { isTestLike } from "../utils.js";import { unwrapExpression } from "../utils.js"; const testRegistrars = new Set(["it", "test"]); const isFunction = (node) => node?.type === "ArrowFunctionExpression" || node?.type === "FunctionExpression" || node?.type === "FunctionDeclaration"; const isTestRegistrationCall = (node) => { const callee = unwrapExpression(node.callee); if (callee?.type === "Identifier") return testRegistrars.has(callee.name); if (callee?.type !== "MemberExpression") return false; const object = unwrapExpression(callee.object); return object?.type === "Identifier" && testRegistrars.has(object.name);}; export default { meta: { type: "problem", docs: { description: "Disallow conditional expect calls inside tests.", }, }, create(context) { if (!isTestLike(context.filename)) return {}; const testCallbacks = new WeakSet(); let testDepth = 0; let conditionalDepth = 0; const enterFunction = (node) => { if (testCallbacks.has(node)) testDepth++; }; const exitFunction = (node) => { if (testCallbacks.has(node)) testDepth--; }; const enterConditional = () => { if (testDepth > 0) conditionalDepth++; }; const exitConditional = () => { if (testDepth > 0) conditionalDepth--; }; return { CallExpression(node) { if (isTestRegistrationCall(node)) { const callback = node.arguments.find(isFunction); if (callback) testCallbacks.add(callback); } if (testDepth === 0 || conditionalDepth === 0) return; const name = getCallName(node.callee); if (name !== "expect") return; context.report({ node, message: "Avoid conditional expect calls; split the test or assert both branches explicitly.", }); }, FunctionDeclaration: enterFunction, "FunctionDeclaration:exit": exitFunction, FunctionExpression: enterFunction, "FunctionExpression:exit": exitFunction, ArrowFunctionExpression: enterFunction, "ArrowFunctionExpression:exit": exitFunction, IfStatement: enterConditional, "IfStatement:exit": exitConditional, ConditionalExpression: enterConditional, "ConditionalExpression:exit": exitConditional, LogicalExpression: enterConditional, "LogicalExpression:exit": exitConditional, SwitchCase: enterConditional, "SwitchCase:exit": exitConditional, }; },};
import { getCallName } from "../utils.js";
import { isTestLike } from "../utils.js";
import { unwrapExpression } from "../utils.js";
const testRegistrars = new Set(["it", "test"]);
const isFunction = (node) =>
node?.type === "ArrowFunctionExpression" ||
node?.type === "FunctionExpression" ||
node?.type === "FunctionDeclaration";
const isTestRegistrationCall = (node) => {
const callee = unwrapExpression(node.callee);
if (callee?.type === "Identifier") return testRegistrars.has(callee.name);
if (callee?.type !== "MemberExpression") return false;
const object = unwrapExpression(callee.object);
return object?.type === "Identifier" && testRegistrars.has(object.name);
};
export default {
meta: {
type: "problem",
docs: {
description: "Disallow conditional expect calls inside tests.",
},
},
create(context) {
if (!isTestLike(context.filename)) return {};
const testCallbacks = new WeakSet();
let testDepth = 0;
let conditionalDepth = 0;
const enterFunction = (node) => {
if (testCallbacks.has(node)) testDepth++;
};
const exitFunction = (node) => {
if (testCallbacks.has(node)) testDepth--;
};
const enterConditional = () => {
if (testDepth > 0) conditionalDepth++;
};
const exitConditional = () => {
if (testDepth > 0) conditionalDepth--;
};
return {
CallExpression(node) {
if (isTestRegistrationCall(node)) {
const callback = node.arguments.find(isFunction);
if (callback) testCallbacks.add(callback);
}
if (testDepth === 0 || conditionalDepth === 0) return;
const name = getCallName(node.callee);
if (name !== "expect") return;
context.report({
node,
message: "Avoid conditional expect calls; split the test or assert both branches explicitly.",
});
},
FunctionDeclaration: enterFunction,
"FunctionDeclaration:exit": exitFunction,
FunctionExpression: enterFunction,
"FunctionExpression:exit": exitFunction,
ArrowFunctionExpression: enterFunction,
"ArrowFunctionExpression:exit": exitFunction,
IfStatement: enterConditional,
"IfStatement:exit": exitConditional,
ConditionalExpression: enterConditional,
"ConditionalExpression:exit": exitConditional,
LogicalExpression: enterConditional,
"LogicalExpression:exit": exitConditional,
SwitchCase: enterConditional,
"SwitchCase:exit": exitConditional,
};
},
};