A karma plugin which uploads coverage reports to coveralls.io
npx @tessl/cli install tessl/npm-karma-coveralls@2.1.0karma-coveralls is a Karma plugin that automatically uploads code coverage reports generated by karma-coverage to the coveralls.io service. It acts as a bridge between the Karma test runner ecosystem and the Coveralls coverage tracking platform, enabling continuous monitoring of test coverage in JavaScript projects.
npm install karma-coveralls --save-devSince this is a Karma plugin, it's registered via Karma's plugin system rather than imported directly:
// karma.conf.js
module.exports = function(config) {
config.set({
plugins: [
'karma-coverage',
'karma-coveralls'
],
reporters: ['coverage', 'coveralls']
});
};// karma.conf.js
module.exports = function(config) {
config.set({
// Basic reporter configuration
reporters: ['coverage', 'coveralls'],
// Coverage reporter setup (required)
coverageReporter: {
type: 'lcov',
dir: 'coverage/'
},
// Optional coveralls-specific configuration
coverallsReporter: {
repoToken: process.env.COVERALLS_REPO_TOKEN
}
});
};Environment variable configuration:
# Set repository token
export COVERALLS_REPO_TOKEN=your_repo_token_here
# Run Karma tests
npm testThe main functionality provided by karma-coveralls is the coveralls reporter that processes LCOV coverage data and uploads it to coveralls.io.
/**
* Coveralls reporter constructor for Karma
* Registered as 'reporter:coveralls' in Karma's plugin system
* @param rootConfig - Karma configuration object
* @param helper - Karma helper utilities
* @param logger - Karma logger instance
*/
function CoverallsReporter(rootConfig, helper, logger): void;
// Plugin registration
module.exports = {
'reporter:coveralls': ['type', CoverallsReporter]
};The reporter includes dependency injection configuration:
CoverallsReporter.$inject = ['config', 'helper', 'logger'];The plugin integrates with standard Karma configuration:
interface KarmaConfig {
/** Array of reporters including 'coveralls' */
reporters: string[];
/** Coverage reporter configuration (required) */
coverageReporter?: CoverageReporterConfig;
/** Coverage Istanbul reporter configuration (alternative) */
coverageIstanbulReporter?: CoverageIstanbulReporterConfig;
/** Coveralls-specific configuration */
coverallsReporter?: CoverallsReporterConfig;
/** Base path for resolving coverage files */
basePath?: string;
/** Auto-watch mode (disables coveralls reporter) */
autoWatch?: boolean;
}interface CoverageReporterConfig {
/** Coverage report type - must include 'lcov', 'lcovonly', or 'html' */
type?: 'lcov' | 'lcovonly' | 'html' | string;
/** Directory for coverage files */
dir?: string;
/** Array of reporter configurations */
reporters?: Array<{
/** Reporter type - must include 'lcov', 'lcovonly', or 'html' for coveralls */
type: 'lcov' | 'lcovonly' | 'html' | string;
/** Directory path for this reporter's output */
dir?: string;
}>;
}interface CoverageIstanbulReporterConfig {
/** Directory for coverage files */
dir?: string;
/** Array of reporter types - must include 'lcov' for coveralls */
reporters?: Array<'lcov' | 'lcovonly' | 'html' | 'text' | string>;
/** Report directory structure */
reports?: Array<{
type: 'lcov' | 'lcovonly' | 'html' | 'text' | string;
dir?: string;
}>;
}interface CoverallsReporterConfig {
/** Repository token for Coveralls authentication */
repoToken?: string;
}The CoverallsReporter class provides the following methods:
/**
* Called when Karma exits, handles coverage upload
* @param done - Callback function to signal completion
*/
onExit(done: () => void): void;The plugin includes several internal utility functions:
/**
* Resolves which coverage reporter configuration to use
* @returns CoverageReporterConfig or CoverageIstanbulReporterConfig
* @throws Error if coverage reporter doesn't precede coveralls reporter
*/
function resolve_coverage_reporter(): CoverageReporterConfig | CoverageIstanbulReporterConfig;
/**
* Attempts to read LCOV files from the specified directory
* @param basepath - Directory path to search for lcov.info files
* @param retry - Callback function for retry attempts
* @param done - Completion callback with LCOV content
*/
function try_read_lcov(
basepath: string,
retry: () => void,
done: (content?: string) => void
): void;
/**
* Reads LCOV files with retry logic (up to 5 attempts)
* @param basepath - Directory path to search for lcov.info files
* @param finish - Completion callback with LCOV content
*/
function read_lcov(basepath: string, finish: (content?: string) => void): void;
/**
* Handles response from Coveralls API and logs results
* @param done - Karma completion callback
* @param err - Error from Coveralls API
* @param response - HTTP response object
* @param body - Response body content
*/
function send_to_coveralls(
done: () => void,
err: Error | null,
response: { statusCode: number } | undefined,
body: string
): void;
/**
* Formats success response from Coveralls API
* @param body - Response body (string or parsed JSON)
* @returns Formatted success message
*/
function success(body: string | { message?: string; url?: string }): string;The coverage reporter must precede the coveralls reporter in the reporters array:
// Correct
reporters: ['coverage', 'coveralls']
reporters: ['coverage-istanbul', 'coveralls']
// Incorrect - will throw error
reporters: ['coveralls', 'coverage']The coverage reporter must generate LCOV format output:
coverageReporter: {
type: 'lcov', // or 'lcovonly'
dir: 'coverage/'
}Repository token can be provided via:
export COVERALLS_REPO_TOKEN=your_tokencoverallsReporter: {
repoToken: 'your_token'
}.coveralls.yml file:repo_token: your_tokenThe plugin automatically discovers LCOV files using the following logic:
lcov.info files in the coverage directorydir propertycoverageReporter.dir or coverageIstanbulReporter.dir./coverage if no directory specified/**
* LCOV file processing workflow:
* 1. Discovers lcov.info files using vinyl-fs glob pattern: '**\/lcov.info'
* 2. Merges multiple LCOV files using lcov-result-merger through stream processing
* 3. Converts merged LCOV to Coveralls format via coveralls.convertLcovToCoveralls()
* 4. Uploads to Coveralls API via coveralls.sendToCoveralls()
*
* Stream processing chain:
* vfs.src() -> through2.obj() -> lcovResultMerger() -> through2.obj() -> done()
*/
/**
* LCOV file discovery logic priority order:
* 1. First reporter in coverageReporter.reporters[] with type 'html', 'lcov', or 'lcovonly' and dir property
* 2. coverageReporter.dir (if no specific reporter dir found)
* 3. './coverage' (default fallback)
*/
interface LcovDiscoveryConfig {
/** Resolved path to directory containing lcov.info files */
filepath: string;
/** Source configuration that provided the path */
source: 'reporter' | 'coverage' | 'default';
}The plugin includes retry logic when LCOV files are not immediately available:
/**
* Configuration validation errors thrown by the plugin:
*/
interface ConfigurationError extends Error {
message: "coverage or coverage-istanbul reporter should precede coveralls";
}
/**
* Thrown when coveralls reporter appears before coverage reporter in config
* @throws ConfigurationError
*/
function validateReporterOrder(): void;/**
* HTTP response status codes and handling:
* - 200-299 with valid JSON: Success, logs formatted message
* - Other status codes: Error, logs status code and raw response body
* - Missing response object: Creates default response with statusCode: 0
* - Invalid JSON in response: Treated as error response
*/
interface CoverallsResponse {
statusCode: number;
body?: string;
}
/**
* Success response format from Coveralls API
*/
interface CoverallsSuccessBody {
message?: string;
url?: string;
}The plugin provides detailed logging through Karma's logger:
/**
* Log levels and messages used by the plugin:
* - log.info('disabled due to --auto-watch') - When auto-watch is enabled
* - log.debug('use lcov.info in %s', filepath) - Coverage directory path
* - log.info('uploading...') - Before sending to Coveralls
* - log.info('%d --- %s', statusCode, message) - Upload result
*/
interface LoggerMethods {
info(message: string, ...args: any[]): void;
debug(message: string, ...args: any[]): void;
}The plugin relies on the following dependencies:
/**
* Required dependencies and their usage:
* - coveralls: ~3.0.0 - Coveralls.io API client for authentication and upload
* - lcov-result-merger: ^3.0.0 - Merges multiple LCOV files into single stream
* - through2: ^2.0.0 - Stream transformation utilities for file processing
* - vinyl-fs: ^3.0.2 - File system operations with vinyl objects for glob patterns
*
* Core Node.js modules used:
* - fs: File system operations for checking file existence
* - path: Path manipulation for resolving coverage directories
*/
/**
* Dependency injection pattern used by Karma
*/
interface KarmaDependencyInjection {
/** Configuration object containing all Karma settings */
config: KarmaConfig;
/** Karma helper utilities for type checking and validation */
helper: KarmaHelper;
/** Karma logger factory for creating named loggers */
logger: KarmaLogger;
}
interface KarmaHelper {
/** Checks if a value is defined (not null or undefined) */
isDefined(value: any): boolean;
}
interface KarmaLogger {
/** Creates a named logger instance */
create(name: string): LoggerMethods;
}The plugin registers with Karma as a reporter:
// Plugin exports
module.exports = {
'reporter:coveralls': ['type', CoverallsReporter]
};Works with both karma-coverage and karma-coverage-istanbul:
coverageReporter configurationcoverageIstanbulReporter configurationIntegrates with the Coveralls service for:
/**
* Coveralls API integration workflow:
* 1. coveralls.getBaseOptions() - Gets base configuration and authentication
* 2. coveralls.convertLcovToCoveralls() - Converts LCOV to Coveralls format
* 3. coveralls.sendToCoveralls() - Uploads coverage data to Coveralls API
*/
/**
* Coveralls API options configuration
*/
interface CoverallsOptions {
/** Always set to "." by the plugin */
filepath: string;
/** Repository token from config or environment */
repo_token?: string;
/** Additional options provided by coveralls.getBaseOptions() */
[key: string]: any;
}
/**
* Coveralls API methods used by the plugin
*/
interface CoverallsAPI {
/** Gets base options including authentication from environment/config */
getBaseOptions(callback: (err: Error | null, options: CoverallsOptions) => void): void;
/** Converts LCOV format to Coveralls API format */
convertLcovToCoveralls(
lcovData: string,
options: CoverallsOptions,
callback: (err: Error | null, postData: any) => void
): void;
/** Sends formatted data to Coveralls API */
sendToCoveralls(
postData: any,
callback: (err: Error | null, response: CoverallsResponse, body: string) => void
): void;
}