CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-istanbul

Comprehensive JavaScript code coverage tool that computes statement, line, function and branch coverage with module loader hooks for transparent instrumentation

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

utilities.mddocs/

Coverage Utilities

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.

Capabilities

Coverage Processing

Process coverage objects to add derived information and calculate metrics.

const utils = {
    /**
     * Adds line coverage information to all file coverage objects
     * @param {Object} coverage - Coverage object containing file coverage data
     */
    addDerivedInfo(coverage: Object): void;
    
    /**
     * Adds line coverage information to a single file coverage object
     * @param {Object} fileCoverage - Single file coverage object
     */
    addDerivedInfoForFile(fileCoverage: Object): void;
    
    /**
     * Removes line coverage information from coverage objects
     * @param {Object} coverage - Coverage object to clean
     */
    removeDerivedInfo(coverage: Object): void;
    
    /**
     * Returns a blank summary metrics object
     * @returns {Object} Empty summary with zero counts
     */
    blankSummary(): Object;
    
    /**
     * Returns summary metrics for a single file coverage object
     * @param {Object} fileCoverage - File coverage object
     * @returns {Object} Summary metrics (totals, covered, skipped, percentages)
     */
    summarizeFileCoverage(fileCoverage: Object): Object;
    
    /**
     * Returns summary metrics for entire coverage object
     * @param {Object} coverage - Complete coverage object
     * @returns {Object} Aggregated summary metrics
     */
    summarizeCoverage(coverage: Object): Object;
    
    /**
     * Merges two file coverage objects
     * @param {Object} first - First file coverage object
     * @param {Object} second - Second file coverage object  
     * @returns {Object} Merged file coverage object
     */
    mergeFileCoverage(first: Object, second: Object): Object;
    
    /**
     * Merges multiple summary metrics objects
     * @param {...Object} summaries - Summary objects to merge
     * @returns {Object} Merged summary metrics
     */
    mergeSummaryObjects(...summaries: Object[]): Object;
    
    /**
     * Converts coverage object to YUI Test coverage format
     * @param {Object} coverage - Istanbul coverage object
     * @returns {Object} YUI Test coverage format
     */
    toYUICoverage(coverage: Object): Object;
    
    /**
     * Increments hit counts on items marked as ignored/skipped
     * @param {Object} cov - File coverage object to process
     * @returns {Object} New file coverage object with incremented ignored totals
     */
    incrementIgnoredTotals(cov: Object): Object;
};

Usage Examples:

const { utils } = require('istanbul');

// Process coverage data after collection
const collector = new Collector();
collector.add(global.__coverage__);
const coverage = collector.getFinalCoverage();

// Add line coverage information (derived from statements)
utils.addDerivedInfo(coverage);

// Get summary metrics
const summary = utils.summarizeCoverage(coverage);
console.log('Overall coverage:', summary);

// Get summary for individual files
Object.keys(coverage).forEach(filename => {
    const fileSummary = utils.summarizeFileCoverage(coverage[filename]);
    console.log(`${filename}: ${fileSummary.statements.pct}% statements`);
});

Summary Metrics Structure

Summary objects returned by utility functions contain comprehensive coverage metrics:

interface SummaryMetrics {
    /** Statement coverage metrics */
    statements: CoverageMetric;
    
    /** Branch coverage metrics */
    branches: CoverageMetric;
    
    /** Function coverage metrics */
    functions: CoverageMetric;
    
    /** Line coverage metrics */
    lines: CoverageMetric;
}

interface CoverageMetric {
    /** Total number of items */
    total: number;
    
    /** Number of covered items */
    covered: number;
    
    /** Number of skipped/ignored items */
    skipped: number;
    
    /** Coverage percentage (0-100) */
    pct: number;
}

Example Summary Output:

const summary = utils.summarizeCoverage(coverage);
console.log(JSON.stringify(summary, null, 2));

// Output:
{
  "statements": {
    "total": 45,
    "covered": 38,
    "skipped": 0,
    "pct": 84.44
  },
  "branches": {
    "total": 12,
    "covered": 9,
    "skipped": 0,
    "pct": 75
  },
  "functions": {
    "total": 8,
    "covered": 7,
    "skipped": 0,
    "pct": 87.5
  },
  "lines": {
    "total": 42,
    "covered": 35,
    "skipped": 0,
    "pct": 83.33
  }
}

Coverage Merging

Merge coverage data from multiple sources or test runs:

// Merge file coverage objects
const merged = utils.mergeFileCoverage(coverage1['app.js'], coverage2['app.js']);

// Example: coverage from two test runs
const testRun1 = {
    s: { '1': 1, '2': 0, '3': 1 },  // statements
    b: { '1': [1, 0] },              // branches
    f: { '1': 1, '2': 0 }            // functions
};

const testRun2 = {
    s: { '1': 1, '2': 1, '3': 1 },
    b: { '1': [1, 1] },
    f: { '1': 1, '2': 1 }
};

const mergedFile = utils.mergeFileCoverage(testRun1, testRun2);
// Result: { s: { '1': 2, '2': 1, '3': 2 }, b: { '1': [2, 1] }, f: { '1': 2, '2': 1 } }

// Merge summary objects
const summary1 = utils.summarizeFileCoverage(coverage1['file1.js']);
const summary2 = utils.summarizeFileCoverage(coverage2['file2.js']);
const combinedSummary = utils.mergeSummaryObjects(summary1, summary2);

Line Coverage Derivation

Add line coverage information derived from statement coverage:

// Coverage object before adding derived info
const fileCoverage = {
    path: 'app.js',
    s: { '1': 1, '2': 0, '3': 1 },
    statementMap: {
        '1': { start: { line: 5, column: 0 }, end: { line: 5, column: 20 } },
        '2': { start: { line: 8, column: 4 }, end: { line: 8, column: 15 } },
        '3': { start: { line: 12, column: 0 }, end: { line: 12, column: 25 } }
    }
    // ... other coverage data
};

// Add line coverage (modifies object in place)
utils.addDerivedInfoForFile(fileCoverage);

// Now includes line coverage derived from statements
console.log(fileCoverage.l);
// Output: { '5': 1, '8': 0, '12': 1 }

// Remove derived info if needed
utils.removeDerivedInfo({ 'app.js': fileCoverage });

Format Conversion

Convert between Istanbul and other coverage formats:

// Convert to YUI Test format
const yuiCoverage = utils.toYUICoverage(coverage);

// YUI format is different structure optimized for YUI Test
console.log('YUI coverage format:', yuiCoverage);

// Example conversion
const istanbulCoverage = {
    'app.js': {
        path: 'app.js',
        s: { '1': 1, '2': 0 },
        b: { '1': [1, 0] },
        f: { '1': 1 }
        // ... other data
    }
};

const yui = utils.toYUICoverage(istanbulCoverage);
// Converts to YUI Test's expected coverage object structure

Advanced Processing Examples

Coverage Aggregation Pipeline

function processCoverageData(coverageFiles) {
    const allCoverage = {};
    
    // Merge all coverage files
    coverageFiles.forEach(file => {
        const coverage = require(file);
        
        Object.keys(coverage).forEach(filename => {
            if (allCoverage[filename]) {
                // Merge with existing
                allCoverage[filename] = utils.mergeFileCoverage(
                    allCoverage[filename],
                    coverage[filename]
                );
            } else {
                // First occurrence
                allCoverage[filename] = coverage[filename];
            }
        });
    });
    
    // Add derived information
    utils.addDerivedInfo(allCoverage);
    
    // Generate summary
    const summary = utils.summarizeCoverage(allCoverage);
    
    return {
        coverage: allCoverage,
        summary: summary
    };
}

Coverage Filtering and Analysis

function analyzeCoverageQuality(coverage) {
    utils.addDerivedInfo(coverage);
    
    const fileAnalysis = [];
    
    Object.keys(coverage).forEach(filename => {
        const fileCoverage = coverage[filename];
        const summary = utils.summarizeFileCoverage(fileCoverage);
        
        fileAnalysis.push({
            file: filename,
            statements: summary.statements.pct,
            branches: summary.branches.pct,
            functions: summary.functions.pct,
            lines: summary.lines.pct,
            issues: []
        });
        
        // Identify coverage issues
        const analysis = fileAnalysis[fileAnalysis.length - 1];
        
        if (summary.statements.pct < 80) {
            analysis.issues.push('Low statement coverage');
        }
        
        if (summary.branches.pct < 70) {
            analysis.issues.push('Low branch coverage');
        }
        
        if (summary.functions.pct < 90) {
            analysis.issues.push('Low function coverage');
        }
    });
    
    return fileAnalysis;
}

Custom Summary Generation

function generateCustomSummary(coverage) {
    const blank = utils.blankSummary();
    const files = Object.keys(coverage);
    
    // Generate summary for each directory
    const directorySummaries = {};
    
    files.forEach(filename => {
        const directory = path.dirname(filename);
        
        if (!directorySummaries[directory]) {
            directorySummaries[directory] = utils.blankSummary();
        }
        
        const fileSummary = utils.summarizeFileCoverage(coverage[filename]);
        directorySummaries[directory] = utils.mergeSummaryObjects(
            directorySummaries[directory],
            fileSummary
        );
    });
    
    // Overall summary
    const overallSummary = utils.summarizeCoverage(coverage);
    
    return {
        overall: overallSummary,
        directories: directorySummaries,
        files: files.map(f => ({
            name: f,
            summary: utils.summarizeFileCoverage(coverage[f])
        }))
    };
}

Working with Coverage Thresholds

function checkCoverageThresholds(coverage, thresholds) {
    const summary = utils.summarizeCoverage(coverage);
    const results = {};
    
    ['statements', 'branches', 'functions', 'lines'].forEach(metric => {
        const threshold = thresholds[metric];
        const actual = summary[metric].pct;
        
        results[metric] = {
            threshold: threshold,
            actual: actual,
            passed: actual >= threshold,
            difference: actual - threshold
        };
    });
    
    return results;
}

// Usage
const thresholds = {
    statements: 80,
    branches: 75, 
    functions: 85,
    lines: 80
};

const results = checkCoverageThresholds(coverage, thresholds);
console.log('Threshold results:', results);

// Check if all thresholds passed
const allPassed = Object.values(results).every(r => r.passed);
if (!allPassed) {
    console.error('Coverage thresholds not met');
    process.exit(1);
}

Performance Optimization

For large coverage objects, optimize processing:

// Process coverage in chunks for large datasets
function processLargeCoverage(coverage, chunkSize = 100) {
    const files = Object.keys(coverage);
    const results = [];
    
    for (let i = 0; i < files.length; i += chunkSize) {
        const chunk = files.slice(i, i + chunkSize);
        const chunkCoverage = {};
        
        chunk.forEach(file => {
            chunkCoverage[file] = coverage[file];
        });
        
        // Process chunk
        utils.addDerivedInfo(chunkCoverage);
        const chunkSummary = utils.summarizeCoverage(chunkCoverage);
        
        results.push(chunkSummary);
        
        // Allow event loop to process other tasks
        if (i % (chunkSize * 10) === 0) {
            await new Promise(resolve => setImmediate(resolve));
        }
    }
    
    // Merge all chunk summaries
    return utils.mergeSummaryObjects(...results);
}

Error Handling

Handle malformed coverage data:

function safeCoverageProcessing(coverage) {
    try {
        // Validate coverage structure
        if (typeof coverage !== 'object' || coverage === null) {
            throw new Error('Invalid coverage object');
        }
        
        // Process each file safely
        Object.keys(coverage).forEach(filename => {
            const fileCoverage = coverage[filename];
            
            if (!fileCoverage.s || !fileCoverage.b || !fileCoverage.f) {
                console.warn(`Incomplete coverage data for ${filename}`);
                return;
            }
            
            try {
                utils.addDerivedInfoForFile(fileCoverage);
            } catch (error) {
                console.warn(`Failed to process ${filename}:`, error.message);
            }
        });
        
        return utils.summarizeCoverage(coverage);
        
    } catch (error) {
        console.error('Coverage processing failed:', error.message);
        return utils.blankSummary();
    }
}

docs

cli.md

collection.md

configuration.md

hooks.md

index.md

instrumentation.md

reporting.md

storage.md

tree-summarizer.md

utilities.md

tile.json