mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-25 14:25:02 +00:00
197 lines
5.4 KiB
JavaScript
197 lines
5.4 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
/**
|
|
* Function-to-Test Coverage Analyzer (Simple Version)
|
|
*
|
|
* Analyzes which functions have unit tests
|
|
*/
|
|
|
|
import fs from "fs";
|
|
import path from "path";
|
|
import { fileURLToPath } from "url";
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = path.dirname(__filename);
|
|
|
|
// Recursively find files
|
|
function findFiles(dir, pattern, ignore = []) {
|
|
let results = [];
|
|
try {
|
|
const files = fs.readdirSync(dir);
|
|
|
|
for (const file of files) {
|
|
const filepath = path.join(dir, file);
|
|
const stat = fs.statSync(filepath);
|
|
const relPath = path.relative(process.cwd(), filepath);
|
|
|
|
// Skip ignored patterns
|
|
if (ignore.some(ign => relPath.includes(ign))) continue;
|
|
|
|
if (stat.isDirectory()) {
|
|
results = results.concat(findFiles(filepath, pattern, ignore));
|
|
} else if (pattern.test(file)) {
|
|
results.push(filepath);
|
|
}
|
|
}
|
|
} catch (e) {}
|
|
return results;
|
|
}
|
|
|
|
// Extract exported functions
|
|
function extractFunctions(content, file) {
|
|
const functions = [];
|
|
const lines = content.split("\n");
|
|
|
|
lines.forEach((line, index) => {
|
|
const lineNum = index + 1;
|
|
|
|
// export function name() or export const name = () =>
|
|
const namedMatch = line.match(/export\s+(?:function|const|async\s+function|async\s+const)\s+(\w+)/);
|
|
if (namedMatch) {
|
|
functions.push({
|
|
name: namedMatch[1],
|
|
file,
|
|
type: "export",
|
|
line: lineNum,
|
|
});
|
|
}
|
|
});
|
|
|
|
return functions;
|
|
}
|
|
|
|
// Extract test function calls
|
|
function extractTestedFunctions(content) {
|
|
const functions = new Set();
|
|
|
|
// Match function calls in test context
|
|
const matches = content.match(/\b([a-zA-Z_$][a-zA-Z0-9_$]*)\s*\(/g) || [];
|
|
matches.forEach(match => {
|
|
const name = match.replace(/\s*\($/, "");
|
|
const helpers = ["it", "test", "describe", "expect", "beforeEach", "afterEach", "beforeAll", "afterAll", "jest", "vi", "assert", "eq", "ok", "throws", "render", "screen", "fireEvent", "userEvent", "waitFor"];
|
|
if (!helpers.includes(name)) {
|
|
functions.add(name);
|
|
}
|
|
});
|
|
|
|
return functions;
|
|
}
|
|
|
|
// Main analysis
|
|
function analyze() {
|
|
const ignore = ["node_modules", ".next", "build", "dist"];
|
|
|
|
console.log("\n" + "=".repeat(70));
|
|
console.log("FUNCTION-TO-TEST COVERAGE ANALYSIS");
|
|
console.log("=".repeat(70) + "\n");
|
|
|
|
// Find source files
|
|
const srcFiles = findFiles("src", /\.(ts|tsx)$/, ignore)
|
|
.concat(findFiles("packages", /\.(ts|tsx)$/, ignore))
|
|
.concat(findFiles("dbal/ts", /\.(ts|tsx)$/, ignore))
|
|
.filter(f => !f.includes(".test.") && !f.includes(".spec."));
|
|
|
|
// Find test files
|
|
const testFiles = findFiles(".", /\.(test|spec)\.(ts|tsx)$/, ignore);
|
|
|
|
// Extract all functions
|
|
const allFunctions = new Map();
|
|
srcFiles.forEach(file => {
|
|
try {
|
|
const content = fs.readFileSync(file, "utf-8");
|
|
const funcs = extractFunctions(content, path.relative(process.cwd(), file));
|
|
funcs.forEach(f => {
|
|
const key = `${f.name}`;
|
|
if (!allFunctions.has(key)) {
|
|
allFunctions.set(key, []);
|
|
}
|
|
allFunctions.get(key).push(f);
|
|
});
|
|
} catch (e) {}
|
|
});
|
|
|
|
// Extract tested functions
|
|
const testedFunctions = new Set();
|
|
testFiles.forEach(file => {
|
|
try {
|
|
const content = fs.readFileSync(file, "utf-8");
|
|
const tested = extractTestedFunctions(content);
|
|
tested.forEach(f => testedFunctions.add(f));
|
|
} catch (e) {}
|
|
});
|
|
|
|
// Calculate coverage
|
|
const untested = [];
|
|
const tested = [];
|
|
|
|
allFunctions.forEach((defs, name) => {
|
|
if (testedFunctions.has(name)) {
|
|
tested.push({ name, defs });
|
|
} else {
|
|
untested.push({ name, defs });
|
|
}
|
|
});
|
|
|
|
const coverage = (tested.length / allFunctions.size * 100).toFixed(1);
|
|
|
|
console.log(`📊 Summary:`);
|
|
console.log(` Total Functions: ${allFunctions.size}`);
|
|
console.log(` Tested: ${tested.length}`);
|
|
console.log(` Untested: ${untested.length}`);
|
|
console.log(` Coverage: ${coverage}%`);
|
|
|
|
if (untested.length > 0) {
|
|
console.log(`\n${"─".repeat(70)}`);
|
|
console.log(`❌ UNTESTED FUNCTIONS (${untested.length}):`);
|
|
console.log(`${"─".repeat(70)}`);
|
|
|
|
const grouped = new Map();
|
|
untested.forEach(({ name, defs }) => {
|
|
defs.forEach(def => {
|
|
if (!grouped.has(def.file)) {
|
|
grouped.set(def.file, []);
|
|
}
|
|
grouped.get(def.file).push({ name, ...def });
|
|
});
|
|
});
|
|
|
|
grouped.forEach((funcs, file) => {
|
|
console.log(`\n📄 ${file}`);
|
|
funcs.slice(0, 10).forEach(f => {
|
|
console.log(` • ${f.name} [line ${f.line}]`);
|
|
});
|
|
if (funcs.length > 10) {
|
|
console.log(` ... and ${funcs.length - 10} more`);
|
|
}
|
|
});
|
|
}
|
|
|
|
if (tested.length > 0) {
|
|
console.log(`\n${"─".repeat(70)}`);
|
|
console.log(`✅ TESTED FUNCTIONS (${tested.length}):`);
|
|
console.log(`${"─".repeat(70)}`);
|
|
tested.slice(0, 15).forEach(({ name }) => {
|
|
console.log(` ✓ ${name}`);
|
|
});
|
|
if (tested.length > 15) {
|
|
console.log(` ... and ${tested.length - 15} more`);
|
|
}
|
|
}
|
|
|
|
// Generate action items
|
|
if (untested.length > 0) {
|
|
console.log(`\n${"─".repeat(70)}`);
|
|
console.log(`🎯 TODO - CREATE TESTS FOR:`);
|
|
console.log(`${"─".repeat(70)}`);
|
|
|
|
untested.forEach(({ name, defs }) => {
|
|
const file = defs[0].file;
|
|
console.log(`- [ ] Test ${name} (in ${file})`);
|
|
});
|
|
}
|
|
|
|
console.log("\n" + "=".repeat(70) + "\n");
|
|
}
|
|
|
|
analyze();
|