qa360 2.2.7 → 2.2.9
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.
Potentially problematic release.
This version of qa360 might be problematic. Click here for more details.
- package/cli/dist/core/pack-v2/migrator.d.ts +1 -0
- package/cli/dist/core/pack-v2/migrator.js +25 -0
- package/cli/package.json +1 -1
- package/package.json +1 -1
- package/cli/dist/core/core/coverage/analyzer.d.ts +0 -101
- package/cli/dist/core/core/coverage/analyzer.js +0 -415
- package/cli/dist/core/core/coverage/collector.d.ts +0 -74
- package/cli/dist/core/core/coverage/collector.js +0 -459
- package/cli/dist/core/core/coverage/config.d.ts +0 -37
- package/cli/dist/core/core/coverage/config.js +0 -156
- package/cli/dist/core/core/coverage/index.d.ts +0 -11
- package/cli/dist/core/core/coverage/index.js +0 -15
- package/cli/dist/core/core/coverage/types.d.ts +0 -267
- package/cli/dist/core/core/coverage/types.js +0 -6
- package/cli/dist/core/core/coverage/vault.d.ts +0 -95
- package/cli/dist/core/core/coverage/vault.js +0 -405
- package/cli/dist/core/schemas/pack.schema.json +0 -236
- package/cli/dist/schemas/pack.schema.json +0 -236
|
@@ -1,459 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Coverage Collector
|
|
3
|
-
*
|
|
4
|
-
* Collects coverage data from various test sources.
|
|
5
|
-
* Supports Jest, Vitest, Cypress, Playwright, and LCOV formats.
|
|
6
|
-
*/
|
|
7
|
-
import { createDefaultCoverageConfig } from './config.js';
|
|
8
|
-
/**
|
|
9
|
-
* Coverage Collector class
|
|
10
|
-
*/
|
|
11
|
-
export class CoverageCollector {
|
|
12
|
-
config;
|
|
13
|
-
constructor(config) {
|
|
14
|
-
this.config = { ...createDefaultCoverageConfig(), ...config };
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Collect coverage from a file path (auto-detect format)
|
|
18
|
-
*/
|
|
19
|
-
async collectFromFile(filePath) {
|
|
20
|
-
const format = this.detectFormat(filePath);
|
|
21
|
-
switch (format) {
|
|
22
|
-
case 'lcov':
|
|
23
|
-
return this.collectFromLcov(filePath);
|
|
24
|
-
case 'json':
|
|
25
|
-
return this.collectFromJson(filePath);
|
|
26
|
-
case 'istanbul':
|
|
27
|
-
return this.collectFromIstanbul(filePath);
|
|
28
|
-
default:
|
|
29
|
-
return null;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Collect coverage from LCOV format
|
|
34
|
-
*/
|
|
35
|
-
async collectFromLcov(filePath) {
|
|
36
|
-
try {
|
|
37
|
-
const fs = await import('node:fs/promises');
|
|
38
|
-
const content = await fs.readFile(filePath, 'utf-8');
|
|
39
|
-
const records = this.parseLcov(content);
|
|
40
|
-
if (records.length === 0) {
|
|
41
|
-
return null;
|
|
42
|
-
}
|
|
43
|
-
const files = {};
|
|
44
|
-
for (const record of records) {
|
|
45
|
-
files[record.file] = this.convertLcovToFileCoverage(record);
|
|
46
|
-
}
|
|
47
|
-
return {
|
|
48
|
-
source: 'lcov',
|
|
49
|
-
gate: 'unknown',
|
|
50
|
-
files,
|
|
51
|
-
metrics: this.calculateMetrics(files),
|
|
52
|
-
format: 'lcov',
|
|
53
|
-
timestamp: Date.now()
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
catch (error) {
|
|
57
|
-
return null;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* Collect coverage from JSON format (Vitest/Istanbul)
|
|
62
|
-
*/
|
|
63
|
-
async collectFromJson(filePath) {
|
|
64
|
-
try {
|
|
65
|
-
const fs = await import('node:fs/promises');
|
|
66
|
-
const content = await fs.readFile(filePath, 'utf-8');
|
|
67
|
-
const data = JSON.parse(content);
|
|
68
|
-
const files = {};
|
|
69
|
-
// Handle Vitest format
|
|
70
|
-
if (data.result && typeof data.result === 'object') {
|
|
71
|
-
for (const [filePath, coverageData] of Object.entries(data.result)) {
|
|
72
|
-
if (typeof coverageData === 'object' && coverageData !== null) {
|
|
73
|
-
files[filePath] = this.convertVitestToFileCoverage(filePath, coverageData);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
return {
|
|
77
|
-
source: 'vitest',
|
|
78
|
-
gate: 'unknown',
|
|
79
|
-
files,
|
|
80
|
-
metrics: this.calculateMetrics(files),
|
|
81
|
-
format: 'json',
|
|
82
|
-
timestamp: Date.now()
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
// Handle Istanbul format
|
|
86
|
-
if (typeof data === 'object' && !data.result) {
|
|
87
|
-
for (const [filePath, coverageData] of Object.entries(data)) {
|
|
88
|
-
if (typeof coverageData === 'object' && coverageData !== null) {
|
|
89
|
-
files[filePath] = this.convertIstanbulToFileCoverage(filePath, coverageData);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
return {
|
|
93
|
-
source: 'istanbul',
|
|
94
|
-
gate: 'unknown',
|
|
95
|
-
files,
|
|
96
|
-
metrics: this.calculateMetrics(files),
|
|
97
|
-
format: 'istanbul',
|
|
98
|
-
timestamp: Date.now()
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
return null;
|
|
102
|
-
}
|
|
103
|
-
catch (error) {
|
|
104
|
-
return null;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* Collect coverage from Istanbul format
|
|
109
|
-
*/
|
|
110
|
-
async collectFromIstanbul(filePath) {
|
|
111
|
-
return this.collectFromJson(filePath);
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* Parse LCOV format content
|
|
115
|
-
*/
|
|
116
|
-
parseLcov(content) {
|
|
117
|
-
const records = [];
|
|
118
|
-
const lines = content.split('\n');
|
|
119
|
-
let currentRecord = null;
|
|
120
|
-
for (const line of lines) {
|
|
121
|
-
if (line.startsWith('SF:')) {
|
|
122
|
-
// Start of new file record
|
|
123
|
-
currentRecord = {
|
|
124
|
-
file: line.substring(3),
|
|
125
|
-
lines: {},
|
|
126
|
-
branches: {},
|
|
127
|
-
functions: {}
|
|
128
|
-
};
|
|
129
|
-
records.push(currentRecord);
|
|
130
|
-
}
|
|
131
|
-
else if (currentRecord) {
|
|
132
|
-
if (line.startsWith('DA:')) {
|
|
133
|
-
// Line data: DA:<line>,<count>
|
|
134
|
-
const [, lineNum, count] = line.match(/^DA:(\d+),(\d+)$/) || [];
|
|
135
|
-
if (lineNum) {
|
|
136
|
-
currentRecord.lines[parseInt(lineNum, 10)] = parseInt(count, 10);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
else if (line.startsWith('BRDA:')) {
|
|
140
|
-
// Branch data: BRDA:<line>,<block>,<branch>,<count>
|
|
141
|
-
const parts = line.substring(5).split(',');
|
|
142
|
-
if (parts.length >= 4) {
|
|
143
|
-
const lineNum = parts[0];
|
|
144
|
-
const branchId = `${lineNum}-${parts[1]}-${parts[2]}`;
|
|
145
|
-
const count = parts[3] === '-' ? 0 : parseInt(parts[3], 10);
|
|
146
|
-
if (!currentRecord.branches[lineNum]) {
|
|
147
|
-
currentRecord.branches[lineNum] = [];
|
|
148
|
-
}
|
|
149
|
-
currentRecord.branches[lineNum].push(count);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
else if (line.startsWith('FN:')) {
|
|
153
|
-
// Function data: FN:<line>,<function name>
|
|
154
|
-
const parts = line.substring(3).split(',');
|
|
155
|
-
if (parts.length >= 2) {
|
|
156
|
-
currentRecord.functions[parts[1]] = 0;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
else if (line.startsWith('FNDA:')) {
|
|
160
|
-
// Function execution data: FNDA:<count>,<function name>
|
|
161
|
-
const parts = line.substring(5).split(',');
|
|
162
|
-
if (parts.length >= 2) {
|
|
163
|
-
currentRecord.functions[parts[1]] = parseInt(parts[0], 10);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
return records;
|
|
169
|
-
}
|
|
170
|
-
/**
|
|
171
|
-
* Convert LCOV record to FileCoverage
|
|
172
|
-
*/
|
|
173
|
-
convertLcovToFileCoverage(record) {
|
|
174
|
-
const lineNumbers = Object.keys(record.lines).map(Number);
|
|
175
|
-
const totalLines = lineNumbers.length > 0 ? Math.max(...lineNumbers) : 0;
|
|
176
|
-
const coveredLines = Object.values(record.lines).filter(c => c > 0).length;
|
|
177
|
-
// Count branches
|
|
178
|
-
let totalBranches = 0;
|
|
179
|
-
let coveredBranches = 0;
|
|
180
|
-
const branchesByLine = {};
|
|
181
|
-
for (const [lineNum, branchCounts] of Object.entries(record.branches)) {
|
|
182
|
-
const line = parseInt(lineNum, 10);
|
|
183
|
-
const branches = branchCounts;
|
|
184
|
-
totalBranches += branches.length;
|
|
185
|
-
coveredBranches += branches.filter(c => c > 0).length;
|
|
186
|
-
branchesByLine[line] = branches.map((count, idx) => ({
|
|
187
|
-
index: idx,
|
|
188
|
-
count,
|
|
189
|
-
location: { start: { line, column: 0 }, end: { line, column: 0 } }
|
|
190
|
-
}));
|
|
191
|
-
}
|
|
192
|
-
// Count functions
|
|
193
|
-
const totalFunctions = Object.keys(record.functions).length;
|
|
194
|
-
const coveredFunctions = Object.values(record.functions).filter(f => f > 0).length;
|
|
195
|
-
// Uncovered lines
|
|
196
|
-
const uncoveredLines = Object.entries(record.lines)
|
|
197
|
-
.filter(([, count]) => count === 0)
|
|
198
|
-
.map(([line]) => parseInt(line, 10));
|
|
199
|
-
const partiallyCoveredLines = Object.entries(record.lines)
|
|
200
|
-
.filter(([, count]) => count > 0 && count < 10) // Low execution count
|
|
201
|
-
.map(([line]) => parseInt(line, 10));
|
|
202
|
-
return {
|
|
203
|
-
path: record.file,
|
|
204
|
-
totalLines,
|
|
205
|
-
coveredLines,
|
|
206
|
-
lineCoverage: totalLines > 0 ? (coveredLines / totalLines) * 100 : 0,
|
|
207
|
-
totalBranches,
|
|
208
|
-
coveredBranches,
|
|
209
|
-
branchCoverage: totalBranches > 0 ? (coveredBranches / totalBranches) * 100 : 0,
|
|
210
|
-
totalFunctions,
|
|
211
|
-
coveredFunctions,
|
|
212
|
-
functionCoverage: totalFunctions > 0 ? (coveredFunctions / totalFunctions) * 100 : 0,
|
|
213
|
-
totalStatements: totalLines, // Approximation
|
|
214
|
-
coveredStatements: coveredLines,
|
|
215
|
-
statementCoverage: totalLines > 0 ? (coveredLines / totalLines) * 100 : 0,
|
|
216
|
-
uncoveredLines,
|
|
217
|
-
partiallyCoveredLines,
|
|
218
|
-
branchesByLine
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
/**
|
|
222
|
-
* Convert Vitest coverage data to FileCoverage
|
|
223
|
-
*/
|
|
224
|
-
convertVitestToFileCoverage(filePath, data) {
|
|
225
|
-
const s = data.s || { total: 0, covered: 0, pct: 0 };
|
|
226
|
-
const b = data.b || {};
|
|
227
|
-
const f = data.f || {};
|
|
228
|
-
const stmt = data.statementMap || {};
|
|
229
|
-
const branchMap = data.branchMap || {};
|
|
230
|
-
// Calculate line coverage from statement map
|
|
231
|
-
const lines = new Set();
|
|
232
|
-
const coveredLines = new Set();
|
|
233
|
-
for (const [idx, stmtInfo] of Object.entries(stmt)) {
|
|
234
|
-
if (stmtInfo && typeof stmtInfo === 'object') {
|
|
235
|
-
const start = stmtInfo.start;
|
|
236
|
-
const end = stmtInfo.end;
|
|
237
|
-
if (start && end) {
|
|
238
|
-
for (let i = start.line; i <= end.line; i++) {
|
|
239
|
-
lines.add(i);
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
const count = s[parseInt(idx)];
|
|
244
|
-
if (count > 0 && stmtInfo) {
|
|
245
|
-
const start = stmtInfo.start;
|
|
246
|
-
const end = stmtInfo.end;
|
|
247
|
-
if (start && end) {
|
|
248
|
-
for (let i = start.line; i <= end.line; i++) {
|
|
249
|
-
coveredLines.add(i);
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
// Calculate branch coverage
|
|
255
|
-
let totalBranches = 0;
|
|
256
|
-
let coveredBranches = 0;
|
|
257
|
-
const branchesByLine = {};
|
|
258
|
-
for (const [idx, branches] of Object.entries(b)) {
|
|
259
|
-
if (Array.isArray(branches)) {
|
|
260
|
-
const branchInfo = branchMap[parseInt(idx)];
|
|
261
|
-
const line = branchInfo?.line || 0;
|
|
262
|
-
totalBranches += branches.length;
|
|
263
|
-
coveredBranches += branches.filter((c) => c > 0).length;
|
|
264
|
-
branchesByLine[line] = branches.map((count, idx) => ({
|
|
265
|
-
index: idx,
|
|
266
|
-
count,
|
|
267
|
-
location: { start: { line, column: 0 }, end: { line, column: 0 } }
|
|
268
|
-
}));
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
// Calculate function coverage
|
|
272
|
-
const totalFunctions = Object.keys(f).length;
|
|
273
|
-
const coveredFunctions = Object.values(f).filter((c) => typeof c === 'number' && c > 0).length;
|
|
274
|
-
const totalLines = lines.size;
|
|
275
|
-
const coveredLinesCount = coveredLines.size;
|
|
276
|
-
const uncoveredLines = Array.from(lines).filter(l => !coveredLines.has(l));
|
|
277
|
-
return {
|
|
278
|
-
path: filePath,
|
|
279
|
-
totalLines,
|
|
280
|
-
coveredLines: coveredLinesCount,
|
|
281
|
-
lineCoverage: s.pct || 0,
|
|
282
|
-
totalBranches,
|
|
283
|
-
coveredBranches,
|
|
284
|
-
branchCoverage: totalBranches > 0 ? (coveredBranches / totalBranches) * 100 : 0,
|
|
285
|
-
totalFunctions,
|
|
286
|
-
coveredFunctions,
|
|
287
|
-
functionCoverage: totalFunctions > 0 ? (coveredFunctions / totalFunctions) * 100 : 0,
|
|
288
|
-
totalStatements: s.total || 0,
|
|
289
|
-
coveredStatements: s.covered || 0,
|
|
290
|
-
statementCoverage: s.pct || 0,
|
|
291
|
-
uncoveredLines,
|
|
292
|
-
partiallyCoveredLines: [],
|
|
293
|
-
branchesByLine
|
|
294
|
-
};
|
|
295
|
-
}
|
|
296
|
-
/**
|
|
297
|
-
* Convert Istanbul coverage data to FileCoverage
|
|
298
|
-
*/
|
|
299
|
-
convertIstanbulToFileCoverage(filePath, data) {
|
|
300
|
-
return this.convertVitestToFileCoverage(filePath, data);
|
|
301
|
-
}
|
|
302
|
-
/**
|
|
303
|
-
* Calculate aggregate metrics from file coverage data
|
|
304
|
-
*/
|
|
305
|
-
calculateMetrics(files) {
|
|
306
|
-
const fileArray = Object.values(files);
|
|
307
|
-
const threshold = this.config.thresholds.line || 80;
|
|
308
|
-
if (fileArray.length === 0) {
|
|
309
|
-
return {
|
|
310
|
-
lineCoverage: 0,
|
|
311
|
-
branchCoverage: 0,
|
|
312
|
-
functionCoverage: 0,
|
|
313
|
-
statementCoverage: 0,
|
|
314
|
-
totalFiles: 0,
|
|
315
|
-
filesWithCoverage: 0,
|
|
316
|
-
filesMeetingThreshold: 0,
|
|
317
|
-
totalLines: 0,
|
|
318
|
-
coveredLines: 0,
|
|
319
|
-
timestamp: Date.now()
|
|
320
|
-
};
|
|
321
|
-
}
|
|
322
|
-
let totalLines = 0;
|
|
323
|
-
let coveredLines = 0;
|
|
324
|
-
let totalBranches = 0;
|
|
325
|
-
let coveredBranches = 0;
|
|
326
|
-
let totalFunctions = 0;
|
|
327
|
-
let coveredFunctions = 0;
|
|
328
|
-
let totalStatements = 0;
|
|
329
|
-
let coveredStatements = 0;
|
|
330
|
-
let filesMeetingThreshold = 0;
|
|
331
|
-
for (const file of fileArray) {
|
|
332
|
-
totalLines += file.totalLines;
|
|
333
|
-
coveredLines += file.coveredLines;
|
|
334
|
-
totalBranches += file.totalBranches;
|
|
335
|
-
coveredBranches += file.coveredBranches;
|
|
336
|
-
totalFunctions += file.totalFunctions;
|
|
337
|
-
coveredFunctions += file.coveredFunctions;
|
|
338
|
-
totalStatements += file.totalStatements;
|
|
339
|
-
coveredStatements += file.coveredStatements;
|
|
340
|
-
if (file.lineCoverage >= threshold) {
|
|
341
|
-
filesMeetingThreshold++;
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
return {
|
|
345
|
-
lineCoverage: totalLines > 0 ? (coveredLines / totalLines) * 100 : 0,
|
|
346
|
-
branchCoverage: totalBranches > 0 ? (coveredBranches / totalBranches) * 100 : 0,
|
|
347
|
-
functionCoverage: totalFunctions > 0 ? (coveredFunctions / totalFunctions) * 100 : 0,
|
|
348
|
-
statementCoverage: totalStatements > 0 ? (coveredStatements / totalStatements) * 100 : 0,
|
|
349
|
-
totalFiles: fileArray.length,
|
|
350
|
-
filesWithCoverage: fileArray.filter(f => f.totalLines > 0).length,
|
|
351
|
-
filesMeetingThreshold,
|
|
352
|
-
totalLines,
|
|
353
|
-
coveredLines,
|
|
354
|
-
timestamp: Date.now()
|
|
355
|
-
};
|
|
356
|
-
}
|
|
357
|
-
/**
|
|
358
|
-
* Detect coverage format from file path
|
|
359
|
-
*/
|
|
360
|
-
detectFormat(filePath) {
|
|
361
|
-
if (filePath.endsWith('.json')) {
|
|
362
|
-
return 'json';
|
|
363
|
-
}
|
|
364
|
-
if (filePath.endsWith('.lcov') || filePath.endsWith('.info')) {
|
|
365
|
-
return 'lcov';
|
|
366
|
-
}
|
|
367
|
-
if (filePath.includes('coverage') && filePath.endsWith('.json')) {
|
|
368
|
-
return 'istanbul';
|
|
369
|
-
}
|
|
370
|
-
return 'unknown';
|
|
371
|
-
}
|
|
372
|
-
/**
|
|
373
|
-
* Filter files based on include/exclude patterns
|
|
374
|
-
*/
|
|
375
|
-
filterFiles(files) {
|
|
376
|
-
const filtered = {};
|
|
377
|
-
const isIncluded = (path) => {
|
|
378
|
-
// Check exclude patterns first
|
|
379
|
-
for (const pattern of this.config.exclude) {
|
|
380
|
-
if (this.matchPattern(path, pattern)) {
|
|
381
|
-
return false;
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
// Check include patterns
|
|
385
|
-
if (this.config.include.length === 0) {
|
|
386
|
-
return true; // No include filters means all
|
|
387
|
-
}
|
|
388
|
-
for (const pattern of this.config.include) {
|
|
389
|
-
if (this.matchPattern(path, pattern)) {
|
|
390
|
-
return true;
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
return false;
|
|
394
|
-
};
|
|
395
|
-
for (const [path, coverage] of Object.entries(files)) {
|
|
396
|
-
if (isIncluded(path)) {
|
|
397
|
-
filtered[path] = coverage;
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
return filtered;
|
|
401
|
-
}
|
|
402
|
-
/**
|
|
403
|
-
* Match path against glob pattern
|
|
404
|
-
*/
|
|
405
|
-
matchPattern(path, pattern) {
|
|
406
|
-
const regex = new RegExp('^' + pattern.replace(/\*/g, '.*').replace(/\?/g, '.').replace(/\//g, '\\/') + '$');
|
|
407
|
-
return regex.test(path);
|
|
408
|
-
}
|
|
409
|
-
/**
|
|
410
|
-
* Merge multiple coverage results
|
|
411
|
-
*/
|
|
412
|
-
mergeCoverageResults(results) {
|
|
413
|
-
const mergedFiles = {};
|
|
414
|
-
for (const result of results) {
|
|
415
|
-
for (const [path, file] of Object.entries(result.files)) {
|
|
416
|
-
if (!mergedFiles[path]) {
|
|
417
|
-
mergedFiles[path] = { ...file };
|
|
418
|
-
}
|
|
419
|
-
else {
|
|
420
|
-
// Merge coverage data
|
|
421
|
-
const existing = mergedFiles[path];
|
|
422
|
-
// For lines, take the max execution count
|
|
423
|
-
const uncoveredLines = new Set([
|
|
424
|
-
...existing.uncoveredLines,
|
|
425
|
-
...file.uncoveredLines
|
|
426
|
-
]);
|
|
427
|
-
// Update totals (approximate merging)
|
|
428
|
-
mergedFiles[path] = {
|
|
429
|
-
...existing,
|
|
430
|
-
uncoveredLines: Array.from(uncoveredLines),
|
|
431
|
-
lineCoverage: Math.max(existing.lineCoverage, file.lineCoverage),
|
|
432
|
-
branchCoverage: Math.max(existing.branchCoverage, file.branchCoverage),
|
|
433
|
-
functionCoverage: Math.max(existing.functionCoverage, file.functionCoverage)
|
|
434
|
-
};
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
return {
|
|
439
|
-
source: 'custom',
|
|
440
|
-
gate: 'merged',
|
|
441
|
-
files: mergedFiles,
|
|
442
|
-
metrics: this.calculateMetrics(mergedFiles),
|
|
443
|
-
format: 'json',
|
|
444
|
-
timestamp: Date.now()
|
|
445
|
-
};
|
|
446
|
-
}
|
|
447
|
-
/**
|
|
448
|
-
* Update configuration
|
|
449
|
-
*/
|
|
450
|
-
updateConfig(updates) {
|
|
451
|
-
this.config = { ...this.config, ...updates };
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
/**
|
|
455
|
-
* Create a coverage collector with default config
|
|
456
|
-
*/
|
|
457
|
-
export function createCoverageCollector(config) {
|
|
458
|
-
return new CoverageCollector(config);
|
|
459
|
-
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Coverage Configuration
|
|
3
|
-
*
|
|
4
|
-
* Default configuration and factory functions for coverage analytics.
|
|
5
|
-
*/
|
|
6
|
-
import type { CoverageConfig, CoverageThreshold } from './types.js';
|
|
7
|
-
/**
|
|
8
|
-
* Create default coverage configuration
|
|
9
|
-
*/
|
|
10
|
-
export declare function createDefaultCoverageConfig(): CoverageConfig;
|
|
11
|
-
/**
|
|
12
|
-
* Create strict coverage configuration (enterprise/production)
|
|
13
|
-
*/
|
|
14
|
-
export declare function createStrictCoverageConfig(): CoverageConfig;
|
|
15
|
-
/**
|
|
16
|
-
* Create relaxed coverage configuration (development/prototyping)
|
|
17
|
-
*/
|
|
18
|
-
export declare function createRelaxedCoverageConfig(): CoverageConfig;
|
|
19
|
-
/**
|
|
20
|
-
* Parse coverage threshold from string format
|
|
21
|
-
* Supported formats:
|
|
22
|
-
* - "80" (line coverage only)
|
|
23
|
-
* - "line:80,branch:70"
|
|
24
|
-
* - "{line:80,branch:70,function:75}"
|
|
25
|
-
*/
|
|
26
|
-
export declare function parseCoverageThreshold(input: string): CoverageThreshold;
|
|
27
|
-
/**
|
|
28
|
-
* Format coverage threshold as string
|
|
29
|
-
*/
|
|
30
|
-
export declare function formatCoverageThreshold(threshold: CoverageThreshold): string;
|
|
31
|
-
/**
|
|
32
|
-
* Validate coverage configuration
|
|
33
|
-
*/
|
|
34
|
-
export declare function validateCoverageConfig(config: CoverageConfig): {
|
|
35
|
-
valid: boolean;
|
|
36
|
-
errors: string[];
|
|
37
|
-
};
|
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Coverage Configuration
|
|
3
|
-
*
|
|
4
|
-
* Default configuration and factory functions for coverage analytics.
|
|
5
|
-
*/
|
|
6
|
-
/**
|
|
7
|
-
* Create default coverage configuration
|
|
8
|
-
*/
|
|
9
|
-
export function createDefaultCoverageConfig() {
|
|
10
|
-
return {
|
|
11
|
-
enabled: true,
|
|
12
|
-
thresholds: {
|
|
13
|
-
line: 80,
|
|
14
|
-
branch: 70,
|
|
15
|
-
function: 75,
|
|
16
|
-
statement: 80,
|
|
17
|
-
scope: 'global',
|
|
18
|
-
files: {}
|
|
19
|
-
},
|
|
20
|
-
failOnThreshold: false,
|
|
21
|
-
include: [
|
|
22
|
-
'src/**/*.ts',
|
|
23
|
-
'src/**/*.js',
|
|
24
|
-
'lib/**/*.ts',
|
|
25
|
-
'lib/**/*.js'
|
|
26
|
-
],
|
|
27
|
-
exclude: [
|
|
28
|
-
'**/*.test.ts',
|
|
29
|
-
'**/*.test.js',
|
|
30
|
-
'**/*.spec.ts',
|
|
31
|
-
'**/*.spec.js',
|
|
32
|
-
'**/node_modules/**',
|
|
33
|
-
'**/dist/**',
|
|
34
|
-
'**/build/**',
|
|
35
|
-
'**/*.d.ts',
|
|
36
|
-
'**/*.config.*',
|
|
37
|
-
'**/mock*/**',
|
|
38
|
-
'**/__tests__/**',
|
|
39
|
-
'**/__mocks__/**'
|
|
40
|
-
],
|
|
41
|
-
reportFormats: ['json', 'lcov'],
|
|
42
|
-
sources: ['jest', 'vitest', 'cypress', 'playwright', 'istanbul', 'lcov'],
|
|
43
|
-
storeInVault: true,
|
|
44
|
-
trackTrends: true
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Create strict coverage configuration (enterprise/production)
|
|
49
|
-
*/
|
|
50
|
-
export function createStrictCoverageConfig() {
|
|
51
|
-
return {
|
|
52
|
-
...createDefaultCoverageConfig(),
|
|
53
|
-
thresholds: {
|
|
54
|
-
line: 90,
|
|
55
|
-
branch: 85,
|
|
56
|
-
function: 88,
|
|
57
|
-
statement: 90,
|
|
58
|
-
scope: 'per-file',
|
|
59
|
-
files: {}
|
|
60
|
-
},
|
|
61
|
-
failOnThreshold: true
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Create relaxed coverage configuration (development/prototyping)
|
|
66
|
-
*/
|
|
67
|
-
export function createRelaxedCoverageConfig() {
|
|
68
|
-
return {
|
|
69
|
-
...createDefaultCoverageConfig(),
|
|
70
|
-
thresholds: {
|
|
71
|
-
line: 60,
|
|
72
|
-
branch: 50,
|
|
73
|
-
function: 55,
|
|
74
|
-
statement: 60,
|
|
75
|
-
scope: 'global',
|
|
76
|
-
files: {}
|
|
77
|
-
},
|
|
78
|
-
failOnThreshold: false
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* Parse coverage threshold from string format
|
|
83
|
-
* Supported formats:
|
|
84
|
-
* - "80" (line coverage only)
|
|
85
|
-
* - "line:80,branch:70"
|
|
86
|
-
* - "{line:80,branch:70,function:75}"
|
|
87
|
-
*/
|
|
88
|
-
export function parseCoverageThreshold(input) {
|
|
89
|
-
// JSON format
|
|
90
|
-
if (input.startsWith('{')) {
|
|
91
|
-
try {
|
|
92
|
-
return JSON.parse(input);
|
|
93
|
-
}
|
|
94
|
-
catch {
|
|
95
|
-
return createDefaultCoverageConfig().thresholds;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
// Key:value format
|
|
99
|
-
if (input.includes(':')) {
|
|
100
|
-
const result = {};
|
|
101
|
-
for (const part of input.split(',')) {
|
|
102
|
-
const [key, value] = part.split(':');
|
|
103
|
-
const numValue = parseInt(value, 10);
|
|
104
|
-
if (!isNaN(numValue) && ['line', 'branch', 'function', 'statement'].includes(key)) {
|
|
105
|
-
result[key] = numValue;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
return Object.keys(result).length > 0 ? result : createDefaultCoverageConfig().thresholds;
|
|
109
|
-
}
|
|
110
|
-
// Simple number (line coverage)
|
|
111
|
-
const numValue = parseInt(input, 10);
|
|
112
|
-
if (!isNaN(numValue)) {
|
|
113
|
-
return { line: numValue };
|
|
114
|
-
}
|
|
115
|
-
return createDefaultCoverageConfig().thresholds;
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* Format coverage threshold as string
|
|
119
|
-
*/
|
|
120
|
-
export function formatCoverageThreshold(threshold) {
|
|
121
|
-
const parts = [];
|
|
122
|
-
if (threshold.line !== undefined)
|
|
123
|
-
parts.push(`line:${threshold.line}`);
|
|
124
|
-
if (threshold.branch !== undefined)
|
|
125
|
-
parts.push(`branch:${threshold.branch}`);
|
|
126
|
-
if (threshold.function !== undefined)
|
|
127
|
-
parts.push(`function:${threshold.function}`);
|
|
128
|
-
if (threshold.statement !== undefined)
|
|
129
|
-
parts.push(`statement:${threshold.statement}`);
|
|
130
|
-
return parts.join(',') || 'line:80';
|
|
131
|
-
}
|
|
132
|
-
/**
|
|
133
|
-
* Validate coverage configuration
|
|
134
|
-
*/
|
|
135
|
-
export function validateCoverageConfig(config) {
|
|
136
|
-
const errors = [];
|
|
137
|
-
if (config.thresholds.line !== undefined && (config.thresholds.line < 0 || config.thresholds.line > 100)) {
|
|
138
|
-
errors.push('Line coverage threshold must be between 0 and 100');
|
|
139
|
-
}
|
|
140
|
-
if (config.thresholds.branch !== undefined && (config.thresholds.branch < 0 || config.thresholds.branch > 100)) {
|
|
141
|
-
errors.push('Branch coverage threshold must be between 0 and 100');
|
|
142
|
-
}
|
|
143
|
-
if (config.thresholds.function !== undefined && (config.thresholds.function < 0 || config.thresholds.function > 100)) {
|
|
144
|
-
errors.push('Function coverage threshold must be between 0 and 100');
|
|
145
|
-
}
|
|
146
|
-
if (config.thresholds.statement !== undefined && (config.thresholds.statement < 0 || config.thresholds.statement > 100)) {
|
|
147
|
-
errors.push('Statement coverage threshold must be between 0 and 100');
|
|
148
|
-
}
|
|
149
|
-
if (config.include.length === 0) {
|
|
150
|
-
errors.push('At least one include pattern is required');
|
|
151
|
-
}
|
|
152
|
-
return {
|
|
153
|
-
valid: errors.length === 0,
|
|
154
|
-
errors
|
|
155
|
-
};
|
|
156
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* QA360 Coverage Analytics Module
|
|
3
|
-
*
|
|
4
|
-
* Exports all coverage analytics functionality.
|
|
5
|
-
*/
|
|
6
|
-
export type { CoverageType, CoverageSource, FileCoverage, BranchInfo, CoverageMetrics, CoverageThreshold, CoverageResult, CoverageTrend, CoverageGap, CoverageComparison, CoverageConfig, TestCoverageMap, CoverageReport } from './types.js';
|
|
7
|
-
export { createDefaultCoverageConfig, createStrictCoverageConfig, createRelaxedCoverageConfig, parseCoverageThreshold, formatCoverageThreshold, validateCoverageConfig } from './config.js';
|
|
8
|
-
export { CoverageCollector, createCoverageCollector } from './collector.js';
|
|
9
|
-
export { CoverageAnalyzer, createCoverageAnalyzer } from './analyzer.js';
|
|
10
|
-
export { CoverageVault } from './vault.js';
|
|
11
|
-
export { createDefaultCoverageConfig as createCoverageConfig } from './config.js';
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* QA360 Coverage Analytics Module
|
|
3
|
-
*
|
|
4
|
-
* Exports all coverage analytics functionality.
|
|
5
|
-
*/
|
|
6
|
-
// Config
|
|
7
|
-
export { createDefaultCoverageConfig, createStrictCoverageConfig, createRelaxedCoverageConfig, parseCoverageThreshold, formatCoverageThreshold, validateCoverageConfig } from './config.js';
|
|
8
|
-
// Collector
|
|
9
|
-
export { CoverageCollector, createCoverageCollector } from './collector.js';
|
|
10
|
-
// Analyzer
|
|
11
|
-
export { CoverageAnalyzer, createCoverageAnalyzer } from './analyzer.js';
|
|
12
|
-
// Vault integration
|
|
13
|
-
export { CoverageVault } from './vault.js';
|
|
14
|
-
// Re-exports for convenience
|
|
15
|
-
export { createDefaultCoverageConfig as createCoverageConfig } from './config.js';
|