0
# Coverage Utilities
1
2
Coverage utilities process and manipulate coverage objects including merging, summarization, format conversion, and derived metric calculation. These functions work with the raw coverage data structures generated by instrumented code.
3
4
## Capabilities
5
6
### Coverage Processing
7
8
Process coverage objects to add derived information and calculate metrics.
9
10
```javascript { .api }
11
const utils = {
12
/**
13
* Adds line coverage information to all file coverage objects
14
* @param {Object} coverage - Coverage object containing file coverage data
15
*/
16
addDerivedInfo(coverage: Object): void;
17
18
/**
19
* Adds line coverage information to a single file coverage object
20
* @param {Object} fileCoverage - Single file coverage object
21
*/
22
addDerivedInfoForFile(fileCoverage: Object): void;
23
24
/**
25
* Removes line coverage information from coverage objects
26
* @param {Object} coverage - Coverage object to clean
27
*/
28
removeDerivedInfo(coverage: Object): void;
29
30
/**
31
* Returns a blank summary metrics object
32
* @returns {Object} Empty summary with zero counts
33
*/
34
blankSummary(): Object;
35
36
/**
37
* Returns summary metrics for a single file coverage object
38
* @param {Object} fileCoverage - File coverage object
39
* @returns {Object} Summary metrics (totals, covered, skipped, percentages)
40
*/
41
summarizeFileCoverage(fileCoverage: Object): Object;
42
43
/**
44
* Returns summary metrics for entire coverage object
45
* @param {Object} coverage - Complete coverage object
46
* @returns {Object} Aggregated summary metrics
47
*/
48
summarizeCoverage(coverage: Object): Object;
49
50
/**
51
* Merges two file coverage objects
52
* @param {Object} first - First file coverage object
53
* @param {Object} second - Second file coverage object
54
* @returns {Object} Merged file coverage object
55
*/
56
mergeFileCoverage(first: Object, second: Object): Object;
57
58
/**
59
* Merges multiple summary metrics objects
60
* @param {...Object} summaries - Summary objects to merge
61
* @returns {Object} Merged summary metrics
62
*/
63
mergeSummaryObjects(...summaries: Object[]): Object;
64
65
/**
66
* Converts coverage object to YUI Test coverage format
67
* @param {Object} coverage - Istanbul coverage object
68
* @returns {Object} YUI Test coverage format
69
*/
70
toYUICoverage(coverage: Object): Object;
71
72
/**
73
* Increments hit counts on items marked as ignored/skipped
74
* @param {Object} cov - File coverage object to process
75
* @returns {Object} New file coverage object with incremented ignored totals
76
*/
77
incrementIgnoredTotals(cov: Object): Object;
78
};
79
```
80
81
**Usage Examples:**
82
83
```javascript
84
const { utils } = require('istanbul');
85
86
// Process coverage data after collection
87
const collector = new Collector();
88
collector.add(global.__coverage__);
89
const coverage = collector.getFinalCoverage();
90
91
// Add line coverage information (derived from statements)
92
utils.addDerivedInfo(coverage);
93
94
// Get summary metrics
95
const summary = utils.summarizeCoverage(coverage);
96
console.log('Overall coverage:', summary);
97
98
// Get summary for individual files
99
Object.keys(coverage).forEach(filename => {
100
const fileSummary = utils.summarizeFileCoverage(coverage[filename]);
101
console.log(`${filename}: ${fileSummary.statements.pct}% statements`);
102
});
103
```
104
105
### Summary Metrics Structure
106
107
Summary objects returned by utility functions contain comprehensive coverage metrics:
108
109
```javascript { .api }
110
interface SummaryMetrics {
111
/** Statement coverage metrics */
112
statements: CoverageMetric;
113
114
/** Branch coverage metrics */
115
branches: CoverageMetric;
116
117
/** Function coverage metrics */
118
functions: CoverageMetric;
119
120
/** Line coverage metrics */
121
lines: CoverageMetric;
122
}
123
124
interface CoverageMetric {
125
/** Total number of items */
126
total: number;
127
128
/** Number of covered items */
129
covered: number;
130
131
/** Number of skipped/ignored items */
132
skipped: number;
133
134
/** Coverage percentage (0-100) */
135
pct: number;
136
}
137
```
138
139
**Example Summary Output:**
140
141
```javascript
142
const summary = utils.summarizeCoverage(coverage);
143
console.log(JSON.stringify(summary, null, 2));
144
145
// Output:
146
{
147
"statements": {
148
"total": 45,
149
"covered": 38,
150
"skipped": 0,
151
"pct": 84.44
152
},
153
"branches": {
154
"total": 12,
155
"covered": 9,
156
"skipped": 0,
157
"pct": 75
158
},
159
"functions": {
160
"total": 8,
161
"covered": 7,
162
"skipped": 0,
163
"pct": 87.5
164
},
165
"lines": {
166
"total": 42,
167
"covered": 35,
168
"skipped": 0,
169
"pct": 83.33
170
}
171
}
172
```
173
174
### Coverage Merging
175
176
Merge coverage data from multiple sources or test runs:
177
178
```javascript
179
// Merge file coverage objects
180
const merged = utils.mergeFileCoverage(coverage1['app.js'], coverage2['app.js']);
181
182
// Example: coverage from two test runs
183
const testRun1 = {
184
s: { '1': 1, '2': 0, '3': 1 }, // statements
185
b: { '1': [1, 0] }, // branches
186
f: { '1': 1, '2': 0 } // functions
187
};
188
189
const testRun2 = {
190
s: { '1': 1, '2': 1, '3': 1 },
191
b: { '1': [1, 1] },
192
f: { '1': 1, '2': 1 }
193
};
194
195
const mergedFile = utils.mergeFileCoverage(testRun1, testRun2);
196
// Result: { s: { '1': 2, '2': 1, '3': 2 }, b: { '1': [2, 1] }, f: { '1': 2, '2': 1 } }
197
198
// Merge summary objects
199
const summary1 = utils.summarizeFileCoverage(coverage1['file1.js']);
200
const summary2 = utils.summarizeFileCoverage(coverage2['file2.js']);
201
const combinedSummary = utils.mergeSummaryObjects(summary1, summary2);
202
```
203
204
### Line Coverage Derivation
205
206
Add line coverage information derived from statement coverage:
207
208
```javascript
209
// Coverage object before adding derived info
210
const fileCoverage = {
211
path: 'app.js',
212
s: { '1': 1, '2': 0, '3': 1 },
213
statementMap: {
214
'1': { start: { line: 5, column: 0 }, end: { line: 5, column: 20 } },
215
'2': { start: { line: 8, column: 4 }, end: { line: 8, column: 15 } },
216
'3': { start: { line: 12, column: 0 }, end: { line: 12, column: 25 } }
217
}
218
// ... other coverage data
219
};
220
221
// Add line coverage (modifies object in place)
222
utils.addDerivedInfoForFile(fileCoverage);
223
224
// Now includes line coverage derived from statements
225
console.log(fileCoverage.l);
226
// Output: { '5': 1, '8': 0, '12': 1 }
227
228
// Remove derived info if needed
229
utils.removeDerivedInfo({ 'app.js': fileCoverage });
230
```
231
232
### Format Conversion
233
234
Convert between Istanbul and other coverage formats:
235
236
```javascript
237
// Convert to YUI Test format
238
const yuiCoverage = utils.toYUICoverage(coverage);
239
240
// YUI format is different structure optimized for YUI Test
241
console.log('YUI coverage format:', yuiCoverage);
242
243
// Example conversion
244
const istanbulCoverage = {
245
'app.js': {
246
path: 'app.js',
247
s: { '1': 1, '2': 0 },
248
b: { '1': [1, 0] },
249
f: { '1': 1 }
250
// ... other data
251
}
252
};
253
254
const yui = utils.toYUICoverage(istanbulCoverage);
255
// Converts to YUI Test's expected coverage object structure
256
```
257
258
### Advanced Processing Examples
259
260
#### Coverage Aggregation Pipeline
261
262
```javascript
263
function processCoverageData(coverageFiles) {
264
const allCoverage = {};
265
266
// Merge all coverage files
267
coverageFiles.forEach(file => {
268
const coverage = require(file);
269
270
Object.keys(coverage).forEach(filename => {
271
if (allCoverage[filename]) {
272
// Merge with existing
273
allCoverage[filename] = utils.mergeFileCoverage(
274
allCoverage[filename],
275
coverage[filename]
276
);
277
} else {
278
// First occurrence
279
allCoverage[filename] = coverage[filename];
280
}
281
});
282
});
283
284
// Add derived information
285
utils.addDerivedInfo(allCoverage);
286
287
// Generate summary
288
const summary = utils.summarizeCoverage(allCoverage);
289
290
return {
291
coverage: allCoverage,
292
summary: summary
293
};
294
}
295
```
296
297
#### Coverage Filtering and Analysis
298
299
```javascript
300
function analyzeCoverageQuality(coverage) {
301
utils.addDerivedInfo(coverage);
302
303
const fileAnalysis = [];
304
305
Object.keys(coverage).forEach(filename => {
306
const fileCoverage = coverage[filename];
307
const summary = utils.summarizeFileCoverage(fileCoverage);
308
309
fileAnalysis.push({
310
file: filename,
311
statements: summary.statements.pct,
312
branches: summary.branches.pct,
313
functions: summary.functions.pct,
314
lines: summary.lines.pct,
315
issues: []
316
});
317
318
// Identify coverage issues
319
const analysis = fileAnalysis[fileAnalysis.length - 1];
320
321
if (summary.statements.pct < 80) {
322
analysis.issues.push('Low statement coverage');
323
}
324
325
if (summary.branches.pct < 70) {
326
analysis.issues.push('Low branch coverage');
327
}
328
329
if (summary.functions.pct < 90) {
330
analysis.issues.push('Low function coverage');
331
}
332
});
333
334
return fileAnalysis;
335
}
336
```
337
338
#### Custom Summary Generation
339
340
```javascript
341
function generateCustomSummary(coverage) {
342
const blank = utils.blankSummary();
343
const files = Object.keys(coverage);
344
345
// Generate summary for each directory
346
const directorySummaries = {};
347
348
files.forEach(filename => {
349
const directory = path.dirname(filename);
350
351
if (!directorySummaries[directory]) {
352
directorySummaries[directory] = utils.blankSummary();
353
}
354
355
const fileSummary = utils.summarizeFileCoverage(coverage[filename]);
356
directorySummaries[directory] = utils.mergeSummaryObjects(
357
directorySummaries[directory],
358
fileSummary
359
);
360
});
361
362
// Overall summary
363
const overallSummary = utils.summarizeCoverage(coverage);
364
365
return {
366
overall: overallSummary,
367
directories: directorySummaries,
368
files: files.map(f => ({
369
name: f,
370
summary: utils.summarizeFileCoverage(coverage[f])
371
}))
372
};
373
}
374
```
375
376
### Working with Coverage Thresholds
377
378
```javascript
379
function checkCoverageThresholds(coverage, thresholds) {
380
const summary = utils.summarizeCoverage(coverage);
381
const results = {};
382
383
['statements', 'branches', 'functions', 'lines'].forEach(metric => {
384
const threshold = thresholds[metric];
385
const actual = summary[metric].pct;
386
387
results[metric] = {
388
threshold: threshold,
389
actual: actual,
390
passed: actual >= threshold,
391
difference: actual - threshold
392
};
393
});
394
395
return results;
396
}
397
398
// Usage
399
const thresholds = {
400
statements: 80,
401
branches: 75,
402
functions: 85,
403
lines: 80
404
};
405
406
const results = checkCoverageThresholds(coverage, thresholds);
407
console.log('Threshold results:', results);
408
409
// Check if all thresholds passed
410
const allPassed = Object.values(results).every(r => r.passed);
411
if (!allPassed) {
412
console.error('Coverage thresholds not met');
413
process.exit(1);
414
}
415
```
416
417
### Performance Optimization
418
419
For large coverage objects, optimize processing:
420
421
```javascript
422
// Process coverage in chunks for large datasets
423
function processLargeCoverage(coverage, chunkSize = 100) {
424
const files = Object.keys(coverage);
425
const results = [];
426
427
for (let i = 0; i < files.length; i += chunkSize) {
428
const chunk = files.slice(i, i + chunkSize);
429
const chunkCoverage = {};
430
431
chunk.forEach(file => {
432
chunkCoverage[file] = coverage[file];
433
});
434
435
// Process chunk
436
utils.addDerivedInfo(chunkCoverage);
437
const chunkSummary = utils.summarizeCoverage(chunkCoverage);
438
439
results.push(chunkSummary);
440
441
// Allow event loop to process other tasks
442
if (i % (chunkSize * 10) === 0) {
443
await new Promise(resolve => setImmediate(resolve));
444
}
445
}
446
447
// Merge all chunk summaries
448
return utils.mergeSummaryObjects(...results);
449
}
450
```
451
452
### Error Handling
453
454
Handle malformed coverage data:
455
456
```javascript
457
function safeCoverageProcessing(coverage) {
458
try {
459
// Validate coverage structure
460
if (typeof coverage !== 'object' || coverage === null) {
461
throw new Error('Invalid coverage object');
462
}
463
464
// Process each file safely
465
Object.keys(coverage).forEach(filename => {
466
const fileCoverage = coverage[filename];
467
468
if (!fileCoverage.s || !fileCoverage.b || !fileCoverage.f) {
469
console.warn(`Incomplete coverage data for ${filename}`);
470
return;
471
}
472
473
try {
474
utils.addDerivedInfoForFile(fileCoverage);
475
} catch (error) {
476
console.warn(`Failed to process ${filename}:`, error.message);
477
}
478
});
479
480
return utils.summarizeCoverage(coverage);
481
482
} catch (error) {
483
console.error('Coverage processing failed:', error.message);
484
return utils.blankSummary();
485
}
486
}
487
```