added cli-covarage tool and fixed more tests
This commit is contained in:
parent
b63e58784c
commit
b845a8eade
57 changed files with 11917 additions and 295 deletions
288
tools/coverage-cli/src/processor.ts
Normal file
288
tools/coverage-cli/src/processor.ts
Normal file
|
|
@ -0,0 +1,288 @@
|
|||
import type {
|
||||
CoverageConfig,
|
||||
CoverageReport,
|
||||
PackageCoverage,
|
||||
CoverageMetric,
|
||||
FileCoverage
|
||||
} from './types';
|
||||
|
||||
export class CoverageProcessor {
|
||||
constructor(private config: CoverageConfig) {}
|
||||
|
||||
process(rawCoverage: any, testResults: any[]): CoverageReport {
|
||||
// Use the package information from raw coverage if available
|
||||
const packages = rawCoverage.packages
|
||||
? this.processPackagesCoverage(rawCoverage.packages)
|
||||
: this.groupByPackage(rawCoverage.files, testResults);
|
||||
|
||||
const overall = this.calculateOverallCoverage(packages);
|
||||
|
||||
return {
|
||||
timestamp: new Date().toISOString(),
|
||||
packages,
|
||||
overall,
|
||||
config: this.config,
|
||||
};
|
||||
}
|
||||
|
||||
private processPackagesCoverage(packagesData: any): PackageCoverage[] {
|
||||
const packages: PackageCoverage[] = [];
|
||||
|
||||
for (const [packageName, packageData] of Object.entries(packagesData)) {
|
||||
if (!packageData || typeof packageData !== 'object') continue;
|
||||
|
||||
const pkg: PackageCoverage = {
|
||||
name: packageName,
|
||||
path: '', // Will be set from files if available
|
||||
lines: this.createMetricFromRaw(packageData.lines),
|
||||
functions: this.createMetricFromRaw(packageData.functions),
|
||||
branches: this.createMetricFromRaw(packageData.branches),
|
||||
statements: this.createMetricFromRaw(packageData.lines), // Often same as lines
|
||||
files: [],
|
||||
};
|
||||
|
||||
// Process files if available
|
||||
if (packageData.files && Array.isArray(packageData.files)) {
|
||||
for (const file of packageData.files) {
|
||||
const fileCoverage = this.processFile(file);
|
||||
pkg.files.push(fileCoverage);
|
||||
|
||||
// Set package path from first file if not set
|
||||
if (!pkg.path && file.path) {
|
||||
pkg.path = this.getPackagePath(file.path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Only include packages that have files with coverage data
|
||||
if (pkg.files.length > 0) {
|
||||
packages.push(pkg);
|
||||
}
|
||||
}
|
||||
|
||||
return packages;
|
||||
}
|
||||
|
||||
private createMetricFromRaw(rawMetric: any): CoverageMetric {
|
||||
if (!rawMetric || typeof rawMetric !== 'object') {
|
||||
return this.createEmptyMetric();
|
||||
}
|
||||
|
||||
const total = rawMetric.found || 0;
|
||||
const covered = rawMetric.hit || 0;
|
||||
|
||||
return {
|
||||
total,
|
||||
covered,
|
||||
skipped: 0,
|
||||
percentage: total > 0 ? (covered / total) * 100 : 100,
|
||||
};
|
||||
}
|
||||
|
||||
private groupByPackage(files: any[], testResults: any[]): PackageCoverage[] {
|
||||
const packageMap = new Map<string, PackageCoverage>();
|
||||
|
||||
// Group files by package
|
||||
for (const file of files) {
|
||||
const packageName = this.getPackageFromPath(file.path);
|
||||
|
||||
if (!packageMap.has(packageName)) {
|
||||
packageMap.set(packageName, {
|
||||
name: packageName,
|
||||
path: this.getPackagePath(file.path),
|
||||
lines: this.createEmptyMetric(),
|
||||
functions: this.createEmptyMetric(),
|
||||
branches: this.createEmptyMetric(),
|
||||
statements: this.createEmptyMetric(),
|
||||
files: [],
|
||||
});
|
||||
}
|
||||
|
||||
const pkg = packageMap.get(packageName)!;
|
||||
const fileCoverage = this.processFile(file);
|
||||
|
||||
pkg.files.push(fileCoverage);
|
||||
this.addMetrics(pkg.lines, fileCoverage.lines);
|
||||
this.addMetrics(pkg.functions, fileCoverage.functions);
|
||||
this.addMetrics(pkg.branches, fileCoverage.branches);
|
||||
this.addMetrics(pkg.statements, fileCoverage.statements);
|
||||
}
|
||||
|
||||
// Calculate percentages for each package
|
||||
const packages = Array.from(packageMap.values());
|
||||
for (const pkg of packages) {
|
||||
this.calculatePercentage(pkg.lines);
|
||||
this.calculatePercentage(pkg.functions);
|
||||
this.calculatePercentage(pkg.branches);
|
||||
this.calculatePercentage(pkg.statements);
|
||||
}
|
||||
|
||||
return packages;
|
||||
}
|
||||
|
||||
private processFile(file: any): FileCoverage {
|
||||
const lines = this.createMetric(file.lines.found, file.lines.hit);
|
||||
const functions = this.createMetric(file.functions.found, file.functions.hit);
|
||||
const branches = this.createMetric(file.branches.found, file.branches.hit);
|
||||
// Statements often equal lines in simple coverage tools
|
||||
const statements = this.createMetric(file.lines.found, file.lines.hit);
|
||||
|
||||
return {
|
||||
path: file.path,
|
||||
lines,
|
||||
functions,
|
||||
branches,
|
||||
statements,
|
||||
};
|
||||
}
|
||||
|
||||
private createEmptyMetric(): CoverageMetric {
|
||||
return {
|
||||
total: 0,
|
||||
covered: 0,
|
||||
skipped: 0,
|
||||
percentage: 0,
|
||||
};
|
||||
}
|
||||
|
||||
private createMetric(total: number, covered: number): CoverageMetric {
|
||||
return {
|
||||
total,
|
||||
covered,
|
||||
skipped: 0,
|
||||
percentage: total > 0 ? (covered / total) * 100 : 100,
|
||||
};
|
||||
}
|
||||
|
||||
private addMetrics(target: CoverageMetric, source: CoverageMetric): void {
|
||||
target.total += source.total;
|
||||
target.covered += source.covered;
|
||||
target.skipped += source.skipped;
|
||||
}
|
||||
|
||||
private calculatePercentage(metric: CoverageMetric): void {
|
||||
metric.percentage = metric.total > 0 ? (metric.covered / metric.total) * 100 : 100;
|
||||
}
|
||||
|
||||
private calculateOverallCoverage(packages: PackageCoverage[]): CoverageReport['overall'] {
|
||||
const overall = {
|
||||
lines: this.createEmptyMetric(),
|
||||
functions: this.createEmptyMetric(),
|
||||
branches: this.createEmptyMetric(),
|
||||
statements: this.createEmptyMetric(),
|
||||
};
|
||||
|
||||
for (const pkg of packages) {
|
||||
this.addMetrics(overall.lines, pkg.lines);
|
||||
this.addMetrics(overall.functions, pkg.functions);
|
||||
this.addMetrics(overall.branches, pkg.branches);
|
||||
this.addMetrics(overall.statements, pkg.statements);
|
||||
}
|
||||
|
||||
this.calculatePercentage(overall.lines);
|
||||
this.calculatePercentage(overall.functions);
|
||||
this.calculatePercentage(overall.branches);
|
||||
this.calculatePercentage(overall.statements);
|
||||
|
||||
return overall;
|
||||
}
|
||||
|
||||
private getPackageFromPath(filePath: string): string {
|
||||
const normalizedPath = filePath.replace(/\\/g, '/');
|
||||
|
||||
// Try to extract package name from path
|
||||
const patterns = [
|
||||
/packages\/([^/]+)\//,
|
||||
/apps\/stock\/([^/]+)\//,
|
||||
/apps\/([^/]+)\//,
|
||||
/libs\/core\/([^/]+)\//,
|
||||
/libs\/data\/([^/]+)\//,
|
||||
/libs\/services\/([^/]+)\//,
|
||||
/libs\/([^/]+)\//,
|
||||
/tools\/([^/]+)\//,
|
||||
/@stock-bot\/([^/]+)\//,
|
||||
];
|
||||
|
||||
for (const pattern of patterns) {
|
||||
const match = normalizedPath.match(pattern);
|
||||
if (match) {
|
||||
return `@stock-bot/${match[1]}`;
|
||||
}
|
||||
}
|
||||
|
||||
// Default to root
|
||||
return 'root';
|
||||
}
|
||||
|
||||
private getPackagePath(filePath: string): string {
|
||||
const normalizedPath = filePath.replace(/\\/g, '/');
|
||||
|
||||
// Extract package root path
|
||||
const patterns = [
|
||||
/(.*\/packages\/[^/]+)\//,
|
||||
/(.*\/apps\/stock\/[^/]+)\//,
|
||||
/(.*\/apps\/[^/]+)\//,
|
||||
/(.*\/libs\/core\/[^/]+)\//,
|
||||
/(.*\/libs\/data\/[^/]+)\//,
|
||||
/(.*\/libs\/services\/[^/]+)\//,
|
||||
/(.*\/libs\/[^/]+)\//,
|
||||
/(.*\/tools\/[^/]+)\//,
|
||||
];
|
||||
|
||||
for (const pattern of patterns) {
|
||||
const match = normalizedPath.match(pattern);
|
||||
if (match) {
|
||||
return match[1];
|
||||
}
|
||||
}
|
||||
|
||||
// Default to workspace root
|
||||
return this.config.workspaceRoot || process.cwd();
|
||||
}
|
||||
|
||||
checkThresholds(report: CoverageReport): {
|
||||
passed: boolean;
|
||||
failures: Array<{ metric: string; expected: number; actual: number }>;
|
||||
} {
|
||||
const failures: Array<{ metric: string; expected: number; actual: number }> = [];
|
||||
const { thresholds } = this.config;
|
||||
const { overall } = report;
|
||||
|
||||
if (thresholds.lines !== undefined && overall.lines.percentage < thresholds.lines) {
|
||||
failures.push({
|
||||
metric: 'lines',
|
||||
expected: thresholds.lines,
|
||||
actual: overall.lines.percentage,
|
||||
});
|
||||
}
|
||||
|
||||
if (thresholds.functions !== undefined && overall.functions.percentage < thresholds.functions) {
|
||||
failures.push({
|
||||
metric: 'functions',
|
||||
expected: thresholds.functions,
|
||||
actual: overall.functions.percentage,
|
||||
});
|
||||
}
|
||||
|
||||
if (thresholds.branches !== undefined && overall.branches.percentage < thresholds.branches) {
|
||||
failures.push({
|
||||
metric: 'branches',
|
||||
expected: thresholds.branches,
|
||||
actual: overall.branches.percentage,
|
||||
});
|
||||
}
|
||||
|
||||
if (thresholds.statements !== undefined && overall.statements.percentage < thresholds.statements) {
|
||||
failures.push({
|
||||
metric: 'statements',
|
||||
expected: thresholds.statements,
|
||||
actual: overall.statements.percentage,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
passed: failures.length === 0,
|
||||
failures,
|
||||
};
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue