@nahisaho/musubix-core 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/musubix.js +18 -0
- package/dist/__tests__/index.test.d.ts +2 -0
- package/dist/__tests__/index.test.d.ts.map +1 -0
- package/dist/__tests__/index.test.js +27 -0
- package/dist/__tests__/index.test.js.map +1 -0
- package/dist/auth/auth-manager.d.ts +320 -0
- package/dist/auth/auth-manager.d.ts.map +1 -0
- package/dist/auth/auth-manager.js +580 -0
- package/dist/auth/auth-manager.js.map +1 -0
- package/dist/cli/base.d.ts +58 -0
- package/dist/cli/base.d.ts.map +1 -0
- package/dist/cli/base.js +93 -0
- package/dist/cli/base.js.map +1 -0
- package/dist/cli/commands/help.d.ts +17 -0
- package/dist/cli/commands/help.d.ts.map +1 -0
- package/dist/cli/commands/help.js +228 -0
- package/dist/cli/commands/help.js.map +1 -0
- package/dist/cli/commands/index.d.ts +14 -0
- package/dist/cli/commands/index.d.ts.map +1 -0
- package/dist/cli/commands/index.js +25 -0
- package/dist/cli/commands/index.js.map +1 -0
- package/dist/cli/commands/init.d.ts +38 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +258 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/index.d.ts +9 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +9 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/codegen/coding-standards.d.ts +250 -0
- package/dist/codegen/coding-standards.d.ts.map +1 -0
- package/dist/codegen/coding-standards.js +976 -0
- package/dist/codegen/coding-standards.js.map +1 -0
- package/dist/codegen/coverage-reporter.d.ts +264 -0
- package/dist/codegen/coverage-reporter.d.ts.map +1 -0
- package/dist/codegen/coverage-reporter.js +697 -0
- package/dist/codegen/coverage-reporter.js.map +1 -0
- package/dist/codegen/dependency-analyzer.d.ts +271 -0
- package/dist/codegen/dependency-analyzer.d.ts.map +1 -0
- package/dist/codegen/dependency-analyzer.js +661 -0
- package/dist/codegen/dependency-analyzer.js.map +1 -0
- package/dist/codegen/generator.d.ts +275 -0
- package/dist/codegen/generator.d.ts.map +1 -0
- package/dist/codegen/generator.js +781 -0
- package/dist/codegen/generator.js.map +1 -0
- package/dist/codegen/index.d.ts +18 -0
- package/dist/codegen/index.d.ts.map +1 -0
- package/dist/codegen/index.js +27 -0
- package/dist/codegen/index.js.map +1 -0
- package/dist/codegen/integration-test-generator.d.ts +312 -0
- package/dist/codegen/integration-test-generator.d.ts.map +1 -0
- package/dist/codegen/integration-test-generator.js +765 -0
- package/dist/codegen/integration-test-generator.js.map +1 -0
- package/dist/codegen/pattern-conformance.d.ts +309 -0
- package/dist/codegen/pattern-conformance.d.ts.map +1 -0
- package/dist/codegen/pattern-conformance.js +590 -0
- package/dist/codegen/pattern-conformance.js.map +1 -0
- package/dist/codegen/quality-metrics.d.ts +235 -0
- package/dist/codegen/quality-metrics.d.ts.map +1 -0
- package/dist/codegen/quality-metrics.js +439 -0
- package/dist/codegen/quality-metrics.js.map +1 -0
- package/dist/codegen/security-scanner.d.ts +179 -0
- package/dist/codegen/security-scanner.d.ts.map +1 -0
- package/dist/codegen/security-scanner.js +495 -0
- package/dist/codegen/security-scanner.js.map +1 -0
- package/dist/codegen/static-analyzer.d.ts +188 -0
- package/dist/codegen/static-analyzer.d.ts.map +1 -0
- package/dist/codegen/static-analyzer.js +490 -0
- package/dist/codegen/static-analyzer.js.map +1 -0
- package/dist/codegen/unit-test-generator.d.ts +289 -0
- package/dist/codegen/unit-test-generator.d.ts.map +1 -0
- package/dist/codegen/unit-test-generator.js +634 -0
- package/dist/codegen/unit-test-generator.js.map +1 -0
- package/dist/design/adr-generator.d.ts +227 -0
- package/dist/design/adr-generator.d.ts.map +1 -0
- package/dist/design/adr-generator.js +423 -0
- package/dist/design/adr-generator.js.map +1 -0
- package/dist/design/c4-generator.d.ts +267 -0
- package/dist/design/c4-generator.d.ts.map +1 -0
- package/dist/design/c4-generator.js +453 -0
- package/dist/design/c4-generator.js.map +1 -0
- package/dist/design/framework-optimizer.d.ts +190 -0
- package/dist/design/framework-optimizer.d.ts.map +1 -0
- package/dist/design/framework-optimizer.js +589 -0
- package/dist/design/framework-optimizer.js.map +1 -0
- package/dist/design/index.d.ts +12 -0
- package/dist/design/index.d.ts.map +1 -0
- package/dist/design/index.js +13 -0
- package/dist/design/index.js.map +1 -0
- package/dist/design/pattern-detector.d.ts +270 -0
- package/dist/design/pattern-detector.d.ts.map +1 -0
- package/dist/design/pattern-detector.js +621 -0
- package/dist/design/pattern-detector.js.map +1 -0
- package/dist/design/solid-validator.d.ts +188 -0
- package/dist/design/solid-validator.d.ts.map +1 -0
- package/dist/design/solid-validator.js +579 -0
- package/dist/design/solid-validator.js.map +1 -0
- package/dist/error/data-persistence.d.ts +311 -0
- package/dist/error/data-persistence.d.ts.map +1 -0
- package/dist/error/data-persistence.js +586 -0
- package/dist/error/data-persistence.js.map +1 -0
- package/dist/error/graceful-degradation.d.ts +309 -0
- package/dist/error/graceful-degradation.d.ts.map +1 -0
- package/dist/error/graceful-degradation.js +510 -0
- package/dist/error/graceful-degradation.js.map +1 -0
- package/dist/error/index.d.ts +11 -0
- package/dist/error/index.d.ts.map +1 -0
- package/dist/error/index.js +19 -0
- package/dist/error/index.js.map +1 -0
- package/dist/explanation/explanation-generator.d.ts +228 -0
- package/dist/explanation/explanation-generator.d.ts.map +1 -0
- package/dist/explanation/explanation-generator.js +662 -0
- package/dist/explanation/explanation-generator.js.map +1 -0
- package/dist/explanation/index.d.ts +11 -0
- package/dist/explanation/index.d.ts.map +1 -0
- package/dist/explanation/index.js +19 -0
- package/dist/explanation/index.js.map +1 -0
- package/dist/explanation/reasoning-chain.d.ts +314 -0
- package/dist/explanation/reasoning-chain.d.ts.map +1 -0
- package/dist/explanation/reasoning-chain.js +414 -0
- package/dist/explanation/reasoning-chain.js.map +1 -0
- package/dist/explanation/visual-explanation.d.ts +315 -0
- package/dist/explanation/visual-explanation.d.ts.map +1 -0
- package/dist/explanation/visual-explanation.js +667 -0
- package/dist/explanation/visual-explanation.js.map +1 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +47 -0
- package/dist/index.js.map +1 -0
- package/dist/requirements/decomposer.d.ts +235 -0
- package/dist/requirements/decomposer.d.ts.map +1 -0
- package/dist/requirements/decomposer.js +587 -0
- package/dist/requirements/decomposer.js.map +1 -0
- package/dist/requirements/related-finder.d.ts +261 -0
- package/dist/requirements/related-finder.d.ts.map +1 -0
- package/dist/requirements/related-finder.js +629 -0
- package/dist/requirements/related-finder.js.map +1 -0
- package/dist/traceability/impact.d.ts +196 -0
- package/dist/traceability/impact.d.ts.map +1 -0
- package/dist/traceability/impact.js +438 -0
- package/dist/traceability/impact.js.map +1 -0
- package/dist/traceability/index.d.ts +9 -0
- package/dist/traceability/index.d.ts.map +1 -0
- package/dist/traceability/index.js +10 -0
- package/dist/traceability/index.js.map +1 -0
- package/dist/traceability/manager.d.ts +266 -0
- package/dist/traceability/manager.d.ts.map +1 -0
- package/dist/traceability/manager.js +412 -0
- package/dist/traceability/manager.js.map +1 -0
- package/dist/types/common.d.ts +294 -0
- package/dist/types/common.d.ts.map +1 -0
- package/dist/types/common.js +15 -0
- package/dist/types/common.js.map +1 -0
- package/dist/types/ears.d.ts +158 -0
- package/dist/types/ears.d.ts.map +1 -0
- package/dist/types/ears.js +33 -0
- package/dist/types/ears.js.map +1 -0
- package/dist/types/errors.d.ts +176 -0
- package/dist/types/errors.d.ts.map +1 -0
- package/dist/types/errors.js +55 -0
- package/dist/types/errors.js.map +1 -0
- package/dist/types/index.d.ts +10 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +10 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/data-protector.d.ts +122 -0
- package/dist/utils/data-protector.d.ts.map +1 -0
- package/dist/utils/data-protector.js +275 -0
- package/dist/utils/data-protector.js.map +1 -0
- package/dist/utils/error-handler.d.ts +101 -0
- package/dist/utils/error-handler.d.ts.map +1 -0
- package/dist/utils/error-handler.js +324 -0
- package/dist/utils/error-handler.js.map +1 -0
- package/dist/utils/i18n-manager.d.ts +259 -0
- package/dist/utils/i18n-manager.d.ts.map +1 -0
- package/dist/utils/i18n-manager.js +554 -0
- package/dist/utils/i18n-manager.js.map +1 -0
- package/dist/utils/index.d.ts +10 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +10 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/logger.d.ts +120 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +237 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/performance-profiler.d.ts +251 -0
- package/dist/utils/performance-profiler.d.ts.map +1 -0
- package/dist/utils/performance-profiler.js +458 -0
- package/dist/utils/performance-profiler.js.map +1 -0
- package/dist/utils/scalability-optimizer.d.ts +294 -0
- package/dist/utils/scalability-optimizer.d.ts.map +1 -0
- package/dist/utils/scalability-optimizer.js +606 -0
- package/dist/utils/scalability-optimizer.js.map +1 -0
- package/dist/utils/structured-logger.d.ts +294 -0
- package/dist/utils/structured-logger.d.ts.map +1 -0
- package/dist/utils/structured-logger.js +630 -0
- package/dist/utils/structured-logger.js.map +1 -0
- package/dist/utils/version-compatibility.d.ts +217 -0
- package/dist/utils/version-compatibility.d.ts.map +1 -0
- package/dist/utils/version-compatibility.js +443 -0
- package/dist/utils/version-compatibility.js.map +1 -0
- package/dist/validators/ears-validator.d.ts +182 -0
- package/dist/validators/ears-validator.d.ts.map +1 -0
- package/dist/validators/ears-validator.js +357 -0
- package/dist/validators/ears-validator.js.map +1 -0
- package/dist/validators/index.d.ts +8 -0
- package/dist/validators/index.d.ts.map +1 -0
- package/dist/validators/index.js +9 -0
- package/dist/validators/index.js.map +1 -0
- package/dist/version.d.ts +8 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +8 -0
- package/dist/version.js.map +1 -0
- package/package.json +100 -0
|
@@ -0,0 +1,697 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Coverage Reporter
|
|
3
|
+
*
|
|
4
|
+
* Generates and reports test coverage metrics
|
|
5
|
+
*
|
|
6
|
+
* @packageDocumentation
|
|
7
|
+
* @module codegen/coverage-reporter
|
|
8
|
+
*
|
|
9
|
+
* @see REQ-QUA-002 - Coverage Reporting
|
|
10
|
+
* @see Article VII - Quality Assurance Standards
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Default configuration
|
|
14
|
+
*/
|
|
15
|
+
export const DEFAULT_REPORTER_CONFIG = {
|
|
16
|
+
projectName: 'Project',
|
|
17
|
+
thresholds: [
|
|
18
|
+
{ metric: 'statements', pass: 80, warn: 60 },
|
|
19
|
+
{ metric: 'branches', pass: 80, warn: 60 },
|
|
20
|
+
{ metric: 'functions', pass: 80, warn: 60 },
|
|
21
|
+
{ metric: 'lines', pass: 80, warn: 60 },
|
|
22
|
+
],
|
|
23
|
+
formats: ['text', 'json'],
|
|
24
|
+
outputDir: 'coverage',
|
|
25
|
+
includeFileDetails: true,
|
|
26
|
+
showUncoveredLines: true,
|
|
27
|
+
trackTrends: false,
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Coverage Reporter
|
|
31
|
+
*/
|
|
32
|
+
export class CoverageReporter {
|
|
33
|
+
config;
|
|
34
|
+
constructor(config) {
|
|
35
|
+
this.config = { ...DEFAULT_REPORTER_CONFIG, ...config };
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Generate coverage report from raw data
|
|
39
|
+
*/
|
|
40
|
+
generateReport(coverageData) {
|
|
41
|
+
const summary = this.calculateSummary(coverageData);
|
|
42
|
+
const thresholdResults = this.checkThresholds(summary);
|
|
43
|
+
const recommendations = this.generateRecommendations(coverageData, summary, thresholdResults);
|
|
44
|
+
const trends = this.config.trackTrends
|
|
45
|
+
? this.calculateTrends(summary)
|
|
46
|
+
: undefined;
|
|
47
|
+
return {
|
|
48
|
+
timestamp: new Date(),
|
|
49
|
+
projectName: this.config.projectName,
|
|
50
|
+
summary,
|
|
51
|
+
files: coverageData,
|
|
52
|
+
thresholdResults,
|
|
53
|
+
trends,
|
|
54
|
+
recommendations,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Format report
|
|
59
|
+
*/
|
|
60
|
+
formatReport(report, format) {
|
|
61
|
+
switch (format) {
|
|
62
|
+
case 'text':
|
|
63
|
+
return this.formatText(report);
|
|
64
|
+
case 'json':
|
|
65
|
+
return this.formatJson(report);
|
|
66
|
+
case 'html':
|
|
67
|
+
return this.formatHtml(report);
|
|
68
|
+
case 'lcov':
|
|
69
|
+
return this.formatLcov(report);
|
|
70
|
+
case 'cobertura':
|
|
71
|
+
return this.formatCobertura(report);
|
|
72
|
+
case 'markdown':
|
|
73
|
+
return this.formatMarkdown(report);
|
|
74
|
+
default:
|
|
75
|
+
return this.formatText(report);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Get all formatted reports
|
|
80
|
+
*/
|
|
81
|
+
getAllFormattedReports(report) {
|
|
82
|
+
const reports = new Map();
|
|
83
|
+
for (const format of this.config.formats) {
|
|
84
|
+
reports.set(format, this.formatReport(report, format));
|
|
85
|
+
}
|
|
86
|
+
return reports;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Parse coverage from various formats
|
|
90
|
+
*/
|
|
91
|
+
parseCoverage(data, format) {
|
|
92
|
+
switch (format) {
|
|
93
|
+
case 'istanbul':
|
|
94
|
+
return this.parseIstanbul(data);
|
|
95
|
+
case 'lcov':
|
|
96
|
+
return this.parseLcov(data);
|
|
97
|
+
case 'cobertura':
|
|
98
|
+
return this.parseCobertura(data);
|
|
99
|
+
default:
|
|
100
|
+
throw new Error(`Unknown format: ${format}`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Calculate summary
|
|
105
|
+
*/
|
|
106
|
+
calculateSummary(files) {
|
|
107
|
+
const totals = {
|
|
108
|
+
statements: { total: 0, covered: 0, skipped: 0 },
|
|
109
|
+
branches: { total: 0, covered: 0, skipped: 0 },
|
|
110
|
+
functions: { total: 0, covered: 0, skipped: 0 },
|
|
111
|
+
lines: { total: 0, covered: 0, skipped: 0 },
|
|
112
|
+
};
|
|
113
|
+
let coveredFiles = 0;
|
|
114
|
+
for (const file of files) {
|
|
115
|
+
// Aggregate metrics
|
|
116
|
+
totals.statements.total += file.statements.total;
|
|
117
|
+
totals.statements.covered += file.statements.covered;
|
|
118
|
+
totals.statements.skipped += file.statements.skipped;
|
|
119
|
+
totals.branches.total += file.branches.total;
|
|
120
|
+
totals.branches.covered += file.branches.covered;
|
|
121
|
+
totals.branches.skipped += file.branches.skipped;
|
|
122
|
+
totals.functions.total += file.functions.total;
|
|
123
|
+
totals.functions.covered += file.functions.covered;
|
|
124
|
+
totals.functions.skipped += file.functions.skipped;
|
|
125
|
+
totals.lines.total += file.lines.total;
|
|
126
|
+
totals.lines.covered += file.lines.covered;
|
|
127
|
+
totals.lines.skipped += file.lines.skipped;
|
|
128
|
+
// Check if file is covered
|
|
129
|
+
if (file.statements.percentage > 0) {
|
|
130
|
+
coveredFiles++;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
const statements = this.createMetric(totals.statements);
|
|
134
|
+
const branches = this.createMetric(totals.branches);
|
|
135
|
+
const functions = this.createMetric(totals.functions);
|
|
136
|
+
const lines = this.createMetric(totals.lines);
|
|
137
|
+
const overallPercentage = (statements.percentage +
|
|
138
|
+
branches.percentage +
|
|
139
|
+
functions.percentage +
|
|
140
|
+
lines.percentage) / 4;
|
|
141
|
+
return {
|
|
142
|
+
totalFiles: files.length,
|
|
143
|
+
coveredFiles,
|
|
144
|
+
statements,
|
|
145
|
+
branches,
|
|
146
|
+
functions,
|
|
147
|
+
lines,
|
|
148
|
+
overallPercentage: Math.round(overallPercentage * 100) / 100,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Create metric from totals
|
|
153
|
+
*/
|
|
154
|
+
createMetric(totals) {
|
|
155
|
+
const percentage = totals.total > 0
|
|
156
|
+
? (totals.covered / totals.total) * 100
|
|
157
|
+
: 0;
|
|
158
|
+
return {
|
|
159
|
+
total: totals.total,
|
|
160
|
+
covered: totals.covered,
|
|
161
|
+
skipped: totals.skipped,
|
|
162
|
+
percentage: Math.round(percentage * 100) / 100,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Check thresholds
|
|
167
|
+
*/
|
|
168
|
+
checkThresholds(summary) {
|
|
169
|
+
const results = [];
|
|
170
|
+
for (const threshold of this.config.thresholds) {
|
|
171
|
+
const current = summary[threshold.metric].percentage;
|
|
172
|
+
let status;
|
|
173
|
+
let gap;
|
|
174
|
+
if (current >= threshold.pass) {
|
|
175
|
+
status = 'pass';
|
|
176
|
+
}
|
|
177
|
+
else if (current >= threshold.warn) {
|
|
178
|
+
status = 'warn';
|
|
179
|
+
gap = threshold.pass - current;
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
status = 'fail';
|
|
183
|
+
gap = threshold.pass - current;
|
|
184
|
+
}
|
|
185
|
+
results.push({
|
|
186
|
+
metric: threshold.metric,
|
|
187
|
+
current,
|
|
188
|
+
threshold,
|
|
189
|
+
status,
|
|
190
|
+
gap,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
return results;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Calculate trends
|
|
197
|
+
*/
|
|
198
|
+
calculateTrends(current) {
|
|
199
|
+
const trends = [];
|
|
200
|
+
const previous = this.config.previousCoverage;
|
|
201
|
+
const metrics = ['statements', 'branches', 'functions', 'lines'];
|
|
202
|
+
for (const metric of metrics) {
|
|
203
|
+
const currentValue = current[metric].percentage;
|
|
204
|
+
const previousValue = previous ? previous[metric].percentage : currentValue;
|
|
205
|
+
const change = currentValue - previousValue;
|
|
206
|
+
trends.push({
|
|
207
|
+
date: new Date(),
|
|
208
|
+
metric,
|
|
209
|
+
percentage: currentValue,
|
|
210
|
+
change: Math.round(change * 100) / 100,
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
return trends;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Generate recommendations
|
|
217
|
+
*/
|
|
218
|
+
generateRecommendations(files, summary, thresholds) {
|
|
219
|
+
const recommendations = [];
|
|
220
|
+
// Threshold-based recommendations
|
|
221
|
+
const failingMetrics = thresholds.filter((t) => t.status === 'fail');
|
|
222
|
+
if (failingMetrics.length > 0) {
|
|
223
|
+
for (const metric of failingMetrics) {
|
|
224
|
+
recommendations.push(`Increase ${metric.metric} coverage by ${metric.gap?.toFixed(1)}% to meet threshold`);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
// File-based recommendations
|
|
228
|
+
const lowCoverageFiles = files
|
|
229
|
+
.filter((f) => f.statements.percentage < 50)
|
|
230
|
+
.sort((a, b) => a.statements.percentage - b.statements.percentage)
|
|
231
|
+
.slice(0, 5);
|
|
232
|
+
if (lowCoverageFiles.length > 0) {
|
|
233
|
+
recommendations.push(`Focus on improving coverage in: ${lowCoverageFiles.map((f) => f.path).join(', ')}`);
|
|
234
|
+
}
|
|
235
|
+
// Branch-based recommendations
|
|
236
|
+
if (summary.branches.percentage < summary.statements.percentage - 20) {
|
|
237
|
+
recommendations.push('Branch coverage is significantly lower than statement coverage - add more conditional tests');
|
|
238
|
+
}
|
|
239
|
+
// Function-based recommendations
|
|
240
|
+
const filesWithUncoveredFunctions = files.filter((f) => f.uncoveredFunctions.length > 0);
|
|
241
|
+
if (filesWithUncoveredFunctions.length > 0) {
|
|
242
|
+
recommendations.push(`${filesWithUncoveredFunctions.length} files have uncovered functions - add tests for them`);
|
|
243
|
+
}
|
|
244
|
+
// Overall recommendations
|
|
245
|
+
if (summary.overallPercentage >= 80) {
|
|
246
|
+
recommendations.push('Coverage is good! Consider adding edge case tests');
|
|
247
|
+
}
|
|
248
|
+
else if (summary.overallPercentage >= 60) {
|
|
249
|
+
recommendations.push('Coverage is moderate. Prioritize critical paths');
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
recommendations.push('Coverage is low. Start with unit tests for core functionality');
|
|
253
|
+
}
|
|
254
|
+
return recommendations;
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Format as text
|
|
258
|
+
*/
|
|
259
|
+
formatText(report) {
|
|
260
|
+
const lines = [];
|
|
261
|
+
const width = 80;
|
|
262
|
+
lines.push('='.repeat(width));
|
|
263
|
+
lines.push(`COVERAGE REPORT - ${report.projectName}`);
|
|
264
|
+
lines.push(`Generated: ${report.timestamp.toISOString()}`);
|
|
265
|
+
lines.push('='.repeat(width));
|
|
266
|
+
lines.push('');
|
|
267
|
+
// Summary
|
|
268
|
+
lines.push('SUMMARY');
|
|
269
|
+
lines.push('-'.repeat(width));
|
|
270
|
+
lines.push(`Files: ${report.summary.coveredFiles}/${report.summary.totalFiles}`);
|
|
271
|
+
lines.push(`Statements: ${this.formatMetricBar(report.summary.statements)}`);
|
|
272
|
+
lines.push(`Branches: ${this.formatMetricBar(report.summary.branches)}`);
|
|
273
|
+
lines.push(`Functions: ${this.formatMetricBar(report.summary.functions)}`);
|
|
274
|
+
lines.push(`Lines: ${this.formatMetricBar(report.summary.lines)}`);
|
|
275
|
+
lines.push('');
|
|
276
|
+
lines.push(`Overall: ${report.summary.overallPercentage}%`);
|
|
277
|
+
lines.push('');
|
|
278
|
+
// Threshold results
|
|
279
|
+
lines.push('THRESHOLD STATUS');
|
|
280
|
+
lines.push('-'.repeat(width));
|
|
281
|
+
for (const result of report.thresholdResults) {
|
|
282
|
+
const icon = result.status === 'pass' ? '✓' :
|
|
283
|
+
result.status === 'warn' ? '⚠' : '✗';
|
|
284
|
+
const status = result.status.toUpperCase().padEnd(4);
|
|
285
|
+
lines.push(`${icon} ${result.metric.padEnd(12)} ${result.current.toFixed(1)}% ${status}`);
|
|
286
|
+
}
|
|
287
|
+
lines.push('');
|
|
288
|
+
// Trends
|
|
289
|
+
if (report.trends) {
|
|
290
|
+
lines.push('TRENDS');
|
|
291
|
+
lines.push('-'.repeat(width));
|
|
292
|
+
for (const trend of report.trends) {
|
|
293
|
+
const arrow = trend.change > 0 ? '↑' : trend.change < 0 ? '↓' : '→';
|
|
294
|
+
const change = trend.change !== 0 ? `${trend.change > 0 ? '+' : ''}${trend.change.toFixed(1)}%` : '';
|
|
295
|
+
lines.push(`${trend.metric.padEnd(12)} ${trend.percentage.toFixed(1)}% ${arrow} ${change}`);
|
|
296
|
+
}
|
|
297
|
+
lines.push('');
|
|
298
|
+
}
|
|
299
|
+
// File details
|
|
300
|
+
if (this.config.includeFileDetails) {
|
|
301
|
+
lines.push('FILE COVERAGE');
|
|
302
|
+
lines.push('-'.repeat(width));
|
|
303
|
+
for (const file of report.files.slice(0, 20)) {
|
|
304
|
+
const pct = file.statements.percentage.toFixed(1).padStart(5);
|
|
305
|
+
lines.push(`${pct}% ${file.path}`);
|
|
306
|
+
if (this.config.showUncoveredLines && file.uncoveredLines.length > 0) {
|
|
307
|
+
const uncovered = file.uncoveredLines.slice(0, 10).join(', ');
|
|
308
|
+
const more = file.uncoveredLines.length > 10
|
|
309
|
+
? ` (+${file.uncoveredLines.length - 10} more)`
|
|
310
|
+
: '';
|
|
311
|
+
lines.push(` Uncovered lines: ${uncovered}${more}`);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
if (report.files.length > 20) {
|
|
315
|
+
lines.push(`... and ${report.files.length - 20} more files`);
|
|
316
|
+
}
|
|
317
|
+
lines.push('');
|
|
318
|
+
}
|
|
319
|
+
// Recommendations
|
|
320
|
+
lines.push('RECOMMENDATIONS');
|
|
321
|
+
lines.push('-'.repeat(width));
|
|
322
|
+
for (const rec of report.recommendations) {
|
|
323
|
+
lines.push(`• ${rec}`);
|
|
324
|
+
}
|
|
325
|
+
lines.push('');
|
|
326
|
+
lines.push('='.repeat(width));
|
|
327
|
+
return lines.join('\n');
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Format metric as bar
|
|
331
|
+
*/
|
|
332
|
+
formatMetricBar(metric) {
|
|
333
|
+
const barWidth = 30;
|
|
334
|
+
const filled = Math.round((metric.percentage / 100) * barWidth);
|
|
335
|
+
const bar = '█'.repeat(filled) + '░'.repeat(barWidth - filled);
|
|
336
|
+
return `${bar} ${metric.covered}/${metric.total} (${metric.percentage}%)`;
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Format as JSON
|
|
340
|
+
*/
|
|
341
|
+
formatJson(report) {
|
|
342
|
+
return JSON.stringify(report, null, 2);
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Format as HTML
|
|
346
|
+
*/
|
|
347
|
+
formatHtml(report) {
|
|
348
|
+
const statusColor = (status) => {
|
|
349
|
+
switch (status) {
|
|
350
|
+
case 'pass': return '#4caf50';
|
|
351
|
+
case 'warn': return '#ff9800';
|
|
352
|
+
case 'fail': return '#f44336';
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
return `<!DOCTYPE html>
|
|
356
|
+
<html>
|
|
357
|
+
<head>
|
|
358
|
+
<title>Coverage Report - ${report.projectName}</title>
|
|
359
|
+
<style>
|
|
360
|
+
body { font-family: -apple-system, BlinkMacSystemFont, sans-serif; margin: 20px; }
|
|
361
|
+
h1 { color: #333; }
|
|
362
|
+
.summary { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; margin: 20px 0; }
|
|
363
|
+
.metric { background: #f5f5f5; padding: 20px; border-radius: 8px; }
|
|
364
|
+
.metric h3 { margin: 0 0 10px 0; }
|
|
365
|
+
.metric .value { font-size: 2em; font-weight: bold; }
|
|
366
|
+
.progress { background: #e0e0e0; height: 8px; border-radius: 4px; margin-top: 10px; }
|
|
367
|
+
.progress-bar { height: 100%; border-radius: 4px; }
|
|
368
|
+
.threshold { display: flex; align-items: center; padding: 10px; border-radius: 4px; margin: 5px 0; }
|
|
369
|
+
.threshold.pass { background: #e8f5e9; }
|
|
370
|
+
.threshold.warn { background: #fff3e0; }
|
|
371
|
+
.threshold.fail { background: #ffebee; }
|
|
372
|
+
table { width: 100%; border-collapse: collapse; margin: 20px 0; }
|
|
373
|
+
th, td { text-align: left; padding: 10px; border-bottom: 1px solid #ddd; }
|
|
374
|
+
th { background: #f5f5f5; }
|
|
375
|
+
.recommendation { padding: 10px; background: #e3f2fd; margin: 5px 0; border-radius: 4px; }
|
|
376
|
+
</style>
|
|
377
|
+
</head>
|
|
378
|
+
<body>
|
|
379
|
+
<h1>Coverage Report - ${report.projectName}</h1>
|
|
380
|
+
<p>Generated: ${report.timestamp.toISOString()}</p>
|
|
381
|
+
|
|
382
|
+
<div class="summary">
|
|
383
|
+
<div class="metric">
|
|
384
|
+
<h3>Statements</h3>
|
|
385
|
+
<div class="value">${report.summary.statements.percentage}%</div>
|
|
386
|
+
<div class="progress"><div class="progress-bar" style="width: ${report.summary.statements.percentage}%; background: #2196f3;"></div></div>
|
|
387
|
+
</div>
|
|
388
|
+
<div class="metric">
|
|
389
|
+
<h3>Branches</h3>
|
|
390
|
+
<div class="value">${report.summary.branches.percentage}%</div>
|
|
391
|
+
<div class="progress"><div class="progress-bar" style="width: ${report.summary.branches.percentage}%; background: #9c27b0;"></div></div>
|
|
392
|
+
</div>
|
|
393
|
+
<div class="metric">
|
|
394
|
+
<h3>Functions</h3>
|
|
395
|
+
<div class="value">${report.summary.functions.percentage}%</div>
|
|
396
|
+
<div class="progress"><div class="progress-bar" style="width: ${report.summary.functions.percentage}%; background: #ff9800;"></div></div>
|
|
397
|
+
</div>
|
|
398
|
+
<div class="metric">
|
|
399
|
+
<h3>Lines</h3>
|
|
400
|
+
<div class="value">${report.summary.lines.percentage}%</div>
|
|
401
|
+
<div class="progress"><div class="progress-bar" style="width: ${report.summary.lines.percentage}%; background: #4caf50;"></div></div>
|
|
402
|
+
</div>
|
|
403
|
+
</div>
|
|
404
|
+
|
|
405
|
+
<h2>Threshold Status</h2>
|
|
406
|
+
${report.thresholdResults.map((r) => `
|
|
407
|
+
<div class="threshold ${r.status}">
|
|
408
|
+
<strong>${r.metric}</strong>: ${r.current}%
|
|
409
|
+
(threshold: ${r.threshold.pass}%) -
|
|
410
|
+
<span style="color: ${statusColor(r.status)}">${r.status.toUpperCase()}</span>
|
|
411
|
+
${r.gap ? ` (${r.gap.toFixed(1)}% to pass)` : ''}
|
|
412
|
+
</div>
|
|
413
|
+
`).join('')}
|
|
414
|
+
|
|
415
|
+
<h2>File Coverage</h2>
|
|
416
|
+
<table>
|
|
417
|
+
<thead>
|
|
418
|
+
<tr>
|
|
419
|
+
<th>File</th>
|
|
420
|
+
<th>Statements</th>
|
|
421
|
+
<th>Branches</th>
|
|
422
|
+
<th>Functions</th>
|
|
423
|
+
<th>Lines</th>
|
|
424
|
+
</tr>
|
|
425
|
+
</thead>
|
|
426
|
+
<tbody>
|
|
427
|
+
${report.files.map((f) => `
|
|
428
|
+
<tr>
|
|
429
|
+
<td>${f.path}</td>
|
|
430
|
+
<td>${f.statements.percentage}%</td>
|
|
431
|
+
<td>${f.branches.percentage}%</td>
|
|
432
|
+
<td>${f.functions.percentage}%</td>
|
|
433
|
+
<td>${f.lines.percentage}%</td>
|
|
434
|
+
</tr>
|
|
435
|
+
`).join('')}
|
|
436
|
+
</tbody>
|
|
437
|
+
</table>
|
|
438
|
+
|
|
439
|
+
<h2>Recommendations</h2>
|
|
440
|
+
${report.recommendations.map((r) => `
|
|
441
|
+
<div class="recommendation">${r}</div>
|
|
442
|
+
`).join('')}
|
|
443
|
+
</body>
|
|
444
|
+
</html>`;
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* Format as LCOV
|
|
448
|
+
*/
|
|
449
|
+
formatLcov(report) {
|
|
450
|
+
const lines = [];
|
|
451
|
+
for (const file of report.files) {
|
|
452
|
+
lines.push(`TN:`);
|
|
453
|
+
lines.push(`SF:${file.path}`);
|
|
454
|
+
// Function coverage
|
|
455
|
+
lines.push(`FNF:${file.functions.total}`);
|
|
456
|
+
lines.push(`FNH:${file.functions.covered}`);
|
|
457
|
+
// Line coverage
|
|
458
|
+
lines.push(`LF:${file.lines.total}`);
|
|
459
|
+
lines.push(`LH:${file.lines.covered}`);
|
|
460
|
+
// Branch coverage
|
|
461
|
+
lines.push(`BRF:${file.branches.total}`);
|
|
462
|
+
lines.push(`BRH:${file.branches.covered}`);
|
|
463
|
+
lines.push('end_of_record');
|
|
464
|
+
}
|
|
465
|
+
return lines.join('\n');
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Format as Cobertura XML
|
|
469
|
+
*/
|
|
470
|
+
formatCobertura(report) {
|
|
471
|
+
const lineRate = report.summary.lines.percentage / 100;
|
|
472
|
+
const branchRate = report.summary.branches.percentage / 100;
|
|
473
|
+
return `<?xml version="1.0" ?>
|
|
474
|
+
<!DOCTYPE coverage SYSTEM "http://cobertura.sourceforge.net/xml/coverage-04.dtd">
|
|
475
|
+
<coverage version="1.0" timestamp="${report.timestamp.getTime()}" lines-valid="${report.summary.lines.total}" lines-covered="${report.summary.lines.covered}" line-rate="${lineRate.toFixed(4)}" branches-valid="${report.summary.branches.total}" branches-covered="${report.summary.branches.covered}" branch-rate="${branchRate.toFixed(4)}">
|
|
476
|
+
<packages>
|
|
477
|
+
<package name="${report.projectName}" line-rate="${lineRate.toFixed(4)}" branch-rate="${branchRate.toFixed(4)}">
|
|
478
|
+
<classes>
|
|
479
|
+
${report.files.map((f) => ` <class name="${f.path}" filename="${f.path}" line-rate="${(f.lines.percentage / 100).toFixed(4)}" branch-rate="${(f.branches.percentage / 100).toFixed(4)}">
|
|
480
|
+
<lines>
|
|
481
|
+
${Array.from({ length: f.lines.total }, (_, i) => ` <line number="${i + 1}" hits="${f.uncoveredLines.includes(i + 1) ? 0 : 1}"/>`).join('\n')}
|
|
482
|
+
</lines>
|
|
483
|
+
</class>`).join('\n')}
|
|
484
|
+
</classes>
|
|
485
|
+
</package>
|
|
486
|
+
</packages>
|
|
487
|
+
</coverage>`;
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* Format as Markdown
|
|
491
|
+
*/
|
|
492
|
+
formatMarkdown(report) {
|
|
493
|
+
const lines = [];
|
|
494
|
+
lines.push(`# Coverage Report - ${report.projectName}`);
|
|
495
|
+
lines.push('');
|
|
496
|
+
lines.push(`> Generated: ${report.timestamp.toISOString()}`);
|
|
497
|
+
lines.push('');
|
|
498
|
+
// Summary
|
|
499
|
+
lines.push('## Summary');
|
|
500
|
+
lines.push('');
|
|
501
|
+
lines.push('| Metric | Covered | Total | Percentage |');
|
|
502
|
+
lines.push('|--------|---------|-------|------------|');
|
|
503
|
+
lines.push(`| Statements | ${report.summary.statements.covered} | ${report.summary.statements.total} | ${report.summary.statements.percentage}% |`);
|
|
504
|
+
lines.push(`| Branches | ${report.summary.branches.covered} | ${report.summary.branches.total} | ${report.summary.branches.percentage}% |`);
|
|
505
|
+
lines.push(`| Functions | ${report.summary.functions.covered} | ${report.summary.functions.total} | ${report.summary.functions.percentage}% |`);
|
|
506
|
+
lines.push(`| Lines | ${report.summary.lines.covered} | ${report.summary.lines.total} | ${report.summary.lines.percentage}% |`);
|
|
507
|
+
lines.push('');
|
|
508
|
+
lines.push(`**Overall Coverage: ${report.summary.overallPercentage}%**`);
|
|
509
|
+
lines.push('');
|
|
510
|
+
// Thresholds
|
|
511
|
+
lines.push('## Threshold Status');
|
|
512
|
+
lines.push('');
|
|
513
|
+
for (const result of report.thresholdResults) {
|
|
514
|
+
const icon = result.status === 'pass' ? '✅' :
|
|
515
|
+
result.status === 'warn' ? '⚠️' : '❌';
|
|
516
|
+
lines.push(`- ${icon} **${result.metric}**: ${result.current}% (threshold: ${result.threshold.pass}%)`);
|
|
517
|
+
}
|
|
518
|
+
lines.push('');
|
|
519
|
+
// Trends
|
|
520
|
+
if (report.trends) {
|
|
521
|
+
lines.push('## Trends');
|
|
522
|
+
lines.push('');
|
|
523
|
+
lines.push('| Metric | Current | Change |');
|
|
524
|
+
lines.push('|--------|---------|--------|');
|
|
525
|
+
for (const trend of report.trends) {
|
|
526
|
+
const arrow = trend.change > 0 ? '📈' : trend.change < 0 ? '📉' : '➡️';
|
|
527
|
+
const change = trend.change !== 0
|
|
528
|
+
? `${trend.change > 0 ? '+' : ''}${trend.change.toFixed(1)}%`
|
|
529
|
+
: 'No change';
|
|
530
|
+
lines.push(`| ${trend.metric} | ${trend.percentage}% | ${arrow} ${change} |`);
|
|
531
|
+
}
|
|
532
|
+
lines.push('');
|
|
533
|
+
}
|
|
534
|
+
// File Coverage
|
|
535
|
+
if (this.config.includeFileDetails) {
|
|
536
|
+
lines.push('## File Coverage');
|
|
537
|
+
lines.push('');
|
|
538
|
+
lines.push('| File | Statements | Branches | Functions | Lines |');
|
|
539
|
+
lines.push('|------|------------|----------|-----------|-------|');
|
|
540
|
+
for (const file of report.files) {
|
|
541
|
+
lines.push(`| ${file.path} | ${file.statements.percentage}% | ${file.branches.percentage}% | ${file.functions.percentage}% | ${file.lines.percentage}% |`);
|
|
542
|
+
}
|
|
543
|
+
lines.push('');
|
|
544
|
+
}
|
|
545
|
+
// Recommendations
|
|
546
|
+
lines.push('## Recommendations');
|
|
547
|
+
lines.push('');
|
|
548
|
+
for (const rec of report.recommendations) {
|
|
549
|
+
lines.push(`- ${rec}`);
|
|
550
|
+
}
|
|
551
|
+
return lines.join('\n');
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* Parse Istanbul JSON coverage
|
|
555
|
+
*/
|
|
556
|
+
parseIstanbul(data) {
|
|
557
|
+
const json = JSON.parse(data);
|
|
558
|
+
const files = [];
|
|
559
|
+
for (const [path, coverage] of Object.entries(json)) {
|
|
560
|
+
const cov = coverage;
|
|
561
|
+
files.push(this.parseIstanbulFile(path, cov));
|
|
562
|
+
}
|
|
563
|
+
return files;
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* Parse Istanbul file coverage
|
|
567
|
+
*/
|
|
568
|
+
parseIstanbulFile(path, coverage) {
|
|
569
|
+
const s = coverage.s;
|
|
570
|
+
const b = coverage.b;
|
|
571
|
+
const f = coverage.f;
|
|
572
|
+
const stmtTotal = Object.keys(s).length;
|
|
573
|
+
const stmtCovered = Object.values(s).filter((v) => v > 0).length;
|
|
574
|
+
const branchTotal = Object.values(b).flat().length;
|
|
575
|
+
const branchCovered = Object.values(b).flat().filter((v) => v > 0).length;
|
|
576
|
+
const fnTotal = Object.keys(f).length;
|
|
577
|
+
const fnCovered = Object.values(f).filter((v) => v > 0).length;
|
|
578
|
+
// Calculate line coverage from statement map
|
|
579
|
+
const statementMap = coverage.statementMap;
|
|
580
|
+
const lines = new Set();
|
|
581
|
+
const coveredLines = new Set();
|
|
582
|
+
for (const [key, value] of Object.entries(statementMap)) {
|
|
583
|
+
const line = value.start.line;
|
|
584
|
+
lines.add(line);
|
|
585
|
+
if (s[key] > 0) {
|
|
586
|
+
coveredLines.add(line);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
return {
|
|
590
|
+
path,
|
|
591
|
+
statements: {
|
|
592
|
+
total: stmtTotal,
|
|
593
|
+
covered: stmtCovered,
|
|
594
|
+
skipped: 0,
|
|
595
|
+
percentage: stmtTotal > 0 ? Math.round((stmtCovered / stmtTotal) * 10000) / 100 : 0,
|
|
596
|
+
},
|
|
597
|
+
branches: {
|
|
598
|
+
total: branchTotal,
|
|
599
|
+
covered: branchCovered,
|
|
600
|
+
skipped: 0,
|
|
601
|
+
percentage: branchTotal > 0 ? Math.round((branchCovered / branchTotal) * 10000) / 100 : 0,
|
|
602
|
+
},
|
|
603
|
+
functions: {
|
|
604
|
+
total: fnTotal,
|
|
605
|
+
covered: fnCovered,
|
|
606
|
+
skipped: 0,
|
|
607
|
+
percentage: fnTotal > 0 ? Math.round((fnCovered / fnTotal) * 10000) / 100 : 0,
|
|
608
|
+
},
|
|
609
|
+
lines: {
|
|
610
|
+
total: lines.size,
|
|
611
|
+
covered: coveredLines.size,
|
|
612
|
+
skipped: 0,
|
|
613
|
+
percentage: lines.size > 0 ? Math.round((coveredLines.size / lines.size) * 10000) / 100 : 0,
|
|
614
|
+
},
|
|
615
|
+
uncoveredLines: [...lines].filter((l) => !coveredLines.has(l)),
|
|
616
|
+
uncoveredBranches: [],
|
|
617
|
+
uncoveredFunctions: [],
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
/**
|
|
621
|
+
* Parse LCOV format
|
|
622
|
+
*/
|
|
623
|
+
parseLcov(data) {
|
|
624
|
+
const files = [];
|
|
625
|
+
const records = data.split('end_of_record');
|
|
626
|
+
for (const record of records) {
|
|
627
|
+
if (!record.trim())
|
|
628
|
+
continue;
|
|
629
|
+
const lines = record.trim().split('\n');
|
|
630
|
+
let path = '';
|
|
631
|
+
let linesTotal = 0, linesCovered = 0;
|
|
632
|
+
let fnTotal = 0, fnCovered = 0;
|
|
633
|
+
let branchTotal = 0, branchCovered = 0;
|
|
634
|
+
for (const line of lines) {
|
|
635
|
+
if (line.startsWith('SF:'))
|
|
636
|
+
path = line.substring(3);
|
|
637
|
+
else if (line.startsWith('LF:'))
|
|
638
|
+
linesTotal = parseInt(line.substring(3));
|
|
639
|
+
else if (line.startsWith('LH:'))
|
|
640
|
+
linesCovered = parseInt(line.substring(3));
|
|
641
|
+
else if (line.startsWith('FNF:'))
|
|
642
|
+
fnTotal = parseInt(line.substring(4));
|
|
643
|
+
else if (line.startsWith('FNH:'))
|
|
644
|
+
fnCovered = parseInt(line.substring(4));
|
|
645
|
+
else if (line.startsWith('BRF:'))
|
|
646
|
+
branchTotal = parseInt(line.substring(4));
|
|
647
|
+
else if (line.startsWith('BRH:'))
|
|
648
|
+
branchCovered = parseInt(line.substring(4));
|
|
649
|
+
}
|
|
650
|
+
if (path) {
|
|
651
|
+
files.push({
|
|
652
|
+
path,
|
|
653
|
+
statements: { total: linesTotal, covered: linesCovered, skipped: 0, percentage: linesTotal > 0 ? Math.round((linesCovered / linesTotal) * 10000) / 100 : 0 },
|
|
654
|
+
branches: { total: branchTotal, covered: branchCovered, skipped: 0, percentage: branchTotal > 0 ? Math.round((branchCovered / branchTotal) * 10000) / 100 : 0 },
|
|
655
|
+
functions: { total: fnTotal, covered: fnCovered, skipped: 0, percentage: fnTotal > 0 ? Math.round((fnCovered / fnTotal) * 10000) / 100 : 0 },
|
|
656
|
+
lines: { total: linesTotal, covered: linesCovered, skipped: 0, percentage: linesTotal > 0 ? Math.round((linesCovered / linesTotal) * 10000) / 100 : 0 },
|
|
657
|
+
uncoveredLines: [],
|
|
658
|
+
uncoveredBranches: [],
|
|
659
|
+
uncoveredFunctions: [],
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
return files;
|
|
664
|
+
}
|
|
665
|
+
/**
|
|
666
|
+
* Parse Cobertura XML
|
|
667
|
+
*/
|
|
668
|
+
parseCobertura(data) {
|
|
669
|
+
// Simple XML parsing for Cobertura format
|
|
670
|
+
const files = [];
|
|
671
|
+
const classRegex = /<class name="([^"]+)" filename="([^"]+)" line-rate="([^"]+)" branch-rate="([^"]+)"/g;
|
|
672
|
+
let match;
|
|
673
|
+
while ((match = classRegex.exec(data)) !== null) {
|
|
674
|
+
const path = match[2];
|
|
675
|
+
const lineRate = parseFloat(match[3]) * 100;
|
|
676
|
+
const branchRate = parseFloat(match[4]) * 100;
|
|
677
|
+
files.push({
|
|
678
|
+
path,
|
|
679
|
+
statements: { total: 100, covered: Math.round(lineRate), skipped: 0, percentage: lineRate },
|
|
680
|
+
branches: { total: 100, covered: Math.round(branchRate), skipped: 0, percentage: branchRate },
|
|
681
|
+
functions: { total: 100, covered: Math.round(lineRate), skipped: 0, percentage: lineRate },
|
|
682
|
+
lines: { total: 100, covered: Math.round(lineRate), skipped: 0, percentage: lineRate },
|
|
683
|
+
uncoveredLines: [],
|
|
684
|
+
uncoveredBranches: [],
|
|
685
|
+
uncoveredFunctions: [],
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
return files;
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* Create coverage reporter instance
|
|
693
|
+
*/
|
|
694
|
+
export function createCoverageReporter(config) {
|
|
695
|
+
return new CoverageReporter(config);
|
|
696
|
+
}
|
|
697
|
+
//# sourceMappingURL=coverage-reporter.js.map
|