CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-karma

Spectacular Test Runner for JavaScript with multi-browser support and plugin architecture.

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

plugin-system.mddocs/

Plugin System

Karma's extensive plugin architecture enables customization of browsers, reporters, preprocessors, testing frameworks, and middleware. The plugin system uses dependency injection and provides standardized interfaces for extensibility.

Capabilities

Plugin Resolution

Load and resolve Karma plugins from various sources including npm packages, inline objects, and glob patterns.

/**
 * Resolve plugins from configuration
 * @param plugins - Plugin specifications array
 * @param emitter - Event emitter for plugin events
 * @returns Array of resolved plugin modules
 */
function resolve(plugins: string[], emitter: EventEmitter): any[];

/**
 * Create plugin instantiation function
 * @param injector - Dependency injector instance
 * @returns Function for instantiating plugins
 */
function createInstantiatePlugin(injector: any): Function;

Plugin Resolution Examples:

// In karma.conf.js
module.exports = function(config) {
  config.set({
    plugins: [
      // NPM package names
      'karma-jasmine',
      'karma-chrome-launcher',
      'karma-coverage',
      
      // Glob patterns
      'karma-*',
      
      // Scoped packages
      '@angular-devkit/build-angular/plugins/karma',
      
      // Inline plugin objects
      {
        'framework:custom': ['factory', function() {
          // Custom framework implementation
        }]
      },
      
      // Relative paths
      './custom-plugins/my-plugin.js'
    ]
  });
};

Plugin Types

Karma supports several plugin categories, each with specific interfaces and lifecycles.

/**
 * Plugin type definitions and interfaces
 */
interface PluginTypes {
  // Browser launchers - launch and manage browser instances
  'launcher': BrowserLauncher;
  
  // Test result reporters - format and output test results  
  'reporter': Reporter;
  
  // File preprocessors - transform files before serving
  'preprocessor': Preprocessor;
  
  // Testing frameworks - integrate test libraries
  'framework': Framework;
  
  // HTTP middleware - custom request handling
  'middleware': Middleware;
}

Browser Launchers

Create custom browser launchers for different environments and browser configurations.

/**
 * Browser launcher interface
 */
interface BrowserLauncher {
  /**
   * Start browser instance
   * @param url - Test runner URL
   * @param onExit - Exit callback
   */
  start(url: string, onExit: Function): void;
  
  /**
   * Kill browser process
   * @param done - Completion callback
   */
  kill(done: Function): void;
  
  /**
   * Force kill browser process
   * @returns Promise for completion
   */
  forceKill(): Promise<void>;
  
  /**
   * Mark browser as captured
   */
  markCaptured(): void;
  
  /**
   * Check if browser is captured
   * @returns True if captured
   */
  isCaptured(): boolean;
  
  // Browser state properties
  state: 'BEING_CAPTURED' | 'CAPTURED' | 'BEING_KILLED' | 'FINISHED' | 'RESTARTING';
  id: string;
  name: string;
  displayName: string;
}

/**
 * Base launcher decorator factory
 */
interface BaseLauncherDecorator {
  (launcher: any): void;
}

Custom Launcher Examples:

// Custom Chrome launcher with specific flags
module.exports = function(config) {
  config.set({
    customLaunchers: {
      ChromeHeadlessCustom: {
        base: 'ChromeHeadless',
        flags: [
          '--no-sandbox',
          '--disable-web-security',
          '--disable-features=VizDisplayCompositor',
          '--remote-debugging-port=9222'
        ]
      },
      
      ChromeDebug: {
        base: 'Chrome',
        flags: ['--remote-debugging-port=9333'],
        debug: true
      },
      
      FirefoxHeadlessCustom: {
        base: 'FirefoxHeadless',
        prefs: {
          'network.proxy.type': 1,
          'network.proxy.http': 'localhost',
          'network.proxy.http_port': 9090
        }
      }
    },
    
    browsers: ['ChromeHeadlessCustom', 'FirefoxHeadlessCustom']
  });
};

// Inline custom launcher plugin
const customLauncher = {
  'launcher:CustomBrowser': ['type', function CustomBrowserLauncher() {
    this.start = function(url) {
      // Launch custom browser
    };
    
    this.kill = function(done) {
      // Kill browser process
      done();
    };
  }]
};

module.exports = function(config) {
  config.set({
    plugins: [customLauncher],
    browsers: ['CustomBrowser']
  });
};

Reporters

Create custom reporters to format and output test results in different ways.

/**
 * Reporter interface
 */
interface Reporter {
  /**
   * Test run started
   * @param browsers - Array of browser instances
   */
  onRunStart(browsers: Browser[]): void;
  
  /**
   * Browser started
   * @param browser - Browser instance
   */
  onBrowserStart(browser: Browser): void;
  
  /**
   * Browser completed
   * @param browser - Browser instance
   * @param result - Test results
   */
  onBrowserComplete(browser: Browser, result: BrowserResult): void;
  
  /**
   * Browser error
   * @param browser - Browser instance  
   * @param error - Error object
   */
  onBrowserError(browser: Browser, error: any): void;
  
  /**
   * Browser log message
   * @param browser - Browser instance
   * @param log - Log message
   * @param type - Log type
   */
  onBrowserLog(browser: Browser, log: string, type: string): void;
  
  /**
   * Individual test completed
   * @param browser - Browser instance
   * @param result - Test result
   */
  onSpecComplete(browser: Browser, result: TestResult): void;
  
  /**
   * Test run completed
   * @param browsers - Array of browser instances
   * @param results - Overall results
   */
  onRunComplete(browsers: Browser[], results: TestResults): void;
  
  // Optional methods
  write?(chunk: string): void;
  writeCommonMsg?(msg: string): void;
  onExit?(done: Function): void;
}

/**
 * Base reporter with utility methods
 */
class BaseReporter implements Reporter {
  write(chunk: string): void;
  renderBrowser(browser: Browser): string;
  specSuccess(browser: Browser, result: TestResult): void;
  specFailure(browser: Browser, result: TestResult): void;
  specSkipped(browser: Browser, result: TestResult): void;
}

Custom Reporter Examples:

// Custom JSON reporter
const jsonReporter = function(baseReporterDecorator, config, logger, helper) {
  baseReporterDecorator(this);
  
  const log = logger.create('reporter.json');
  const reporterConfig = config.jsonReporter || {};
  const outputFile = reporterConfig.outputFile || 'test-results.json';
  
  let results = {
    browsers: {},
    summary: {},
    tests: []
  };
  
  this.onRunStart = function(browsers) {
    results.timestamp = new Date().toISOString();
    results.browsers = browsers.map(b => ({
      id: b.id,
      name: b.name,
      fullName: b.fullName
    }));
  };
  
  this.onSpecComplete = function(browser, result) {
    results.tests.push({
      browser: browser.name,
      suite: result.suite,
      description: result.description,
      success: result.success,
      time: result.time,
      log: result.log
    });
  };
  
  this.onRunComplete = function(browsers, summary) {
    results.summary = summary;
    
    helper.mkdirIfNotExists(path.dirname(outputFile), () => {
      fs.writeFileSync(outputFile, JSON.stringify(results, null, 2));
      log.info('Test results written to %s', outputFile);
    });
  };
};

jsonReporter.$inject = ['baseReporterDecorator', 'config', 'logger', 'helper'];

module.exports = {
  'reporter:json': ['type', jsonReporter]
};

Preprocessors

Transform files before they are served to browsers, enabling transpilation, bundling, and instrumentation.

/**
 * Preprocessor interface
 */
interface Preprocessor {
  /**
   * Process file content
   * @param content - Original file content
   * @param file - File object with metadata
   * @param done - Completion callback with (error, content)
   */
  (content: string, file: File, done: (error?: any, content?: string) => void): void;
}

/**
 * File object passed to preprocessors
 */
interface File {
  originalPath: string;     // Original file path
  path: string;            // Processed file path  
  contentPath: string;     // Path to content file
  mtime: Date;            // Modification time
  isUrl: boolean;         // Is external URL
  isBinary: boolean;      // Is binary file
}

Custom Preprocessor Examples:

// Simple string replacement preprocessor
const stringReplace = function(args, config, logger) {
  const log = logger.create('preprocessor.string-replace');
  const options = args.options || {};
  
  return function(content, file, done) {
    log.debug('Processing "%s".', file.originalPath);
    
    let result = content;
    
    // Apply string replacements
    if (options.replacements) {
      options.replacements.forEach(replacement => {
        result = result.replace(replacement.from, replacement.to);
      });
    }
    
    done(null, result);
  };
};

stringReplace.$inject = ['args', 'config', 'logger'];

// ES6 to ES5 transpiler preprocessor
const babelPreprocessor = function(args, config, logger) {
  const babel = require('@babel/core');
  const log = logger.create('preprocessor.babel');
  
  return function(content, file, done) {
    log.debug('Processing "%s".', file.originalPath);
    
    try {
      const result = babel.transform(content, {
        filename: file.originalPath,
        presets: ['@babel/preset-env'],
        sourceMaps: 'inline'
      });
      
      done(null, result.code);
    } catch (error) {
      log.error('Error processing "%s": %s', file.originalPath, error.message);
      done(error);
    }
  };
};

babelPreprocessor.$inject = ['args', 'config', 'logger'];

module.exports = {
  'preprocessor:string-replace': ['factory', stringReplace],
  'preprocessor:babel': ['factory', babelPreprocessor]
};

Testing Frameworks

Integrate different testing libraries and frameworks with Karma.

/**
 * Framework interface
 */
interface Framework {
  /**
   * Initialize framework on client side
   * @param files - File list to modify
   */
  (files: any[]): void;
}

Framework Plugin Examples:

// Custom testing framework
const customFramework = function(files) {
  // Add framework files to the file list
  files.unshift({
    pattern: require.resolve('./custom-framework.js'),
    included: true,
    served: true,
    watched: false
  });
  
  // Add adapter file
  files.unshift({
    pattern: require.resolve('./custom-adapter.js'), 
    included: true,
    served: true,
    watched: false
  });
};

customFramework.$inject = ['config.files'];

module.exports = {
  'framework:custom': ['factory', customFramework]
};

Middleware

Add custom HTTP middleware to the Karma server for handling specific requests.

/**
 * Middleware interface  
 */
interface Middleware {
  /**
   * Express-style middleware function
   * @param req - HTTP request
   * @param res - HTTP response  
   * @param next - Next middleware function
   */
  (req: any, res: any, next: Function): void;
}

Middleware Examples:

// API endpoint middleware
const apiMiddleware = function(config) {
  return function(req, res, next) {
    if (req.url.startsWith('/api/')) {
      // Handle API requests
      res.setHeader('Content-Type', 'application/json');
      res.end(JSON.stringify({ message: 'API response' }));
    } else {
      next();
    }
  };
};

apiMiddleware.$inject = ['config'];

// CORS middleware
const corsMiddleware = function() {
  return function(req, res, next) {
    res.setHeader('Access-Control-Allow-Origin', '*');
    res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
    res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
    
    if (req.method === 'OPTIONS') {
      res.end();
    } else {
      next();
    }
  };
};

module.exports = {
  'middleware:api': ['factory', apiMiddleware],
  'middleware:cors': ['factory', corsMiddleware]
};

Built-in Plugins

Karma includes several built-in plugins and supports many community plugins.

Popular Browser Launchers

// Built-in and popular browser launchers
'karma-chrome-launcher'        // Google Chrome
'karma-firefox-launcher'       // Mozilla Firefox  
'karma-safari-launcher'        // Safari
'karma-ie-launcher'           // Internet Explorer
'karma-edge-launcher'         // Microsoft Edge
'karma-phantomjs-launcher'    // PhantomJS (deprecated)
'karma-browserstack-launcher' // BrowserStack
'karma-sauce-launcher'        // Sauce Labs

Popular Reporters

// Built-in and popular reporters
'karma-progress-reporter'     // Progress dots (built-in)
'karma-dots-reporter'        // Simple dots (built-in)
'karma-junit-reporter'       // JUnit XML
'karma-coverage-reporter'    // Code coverage
'karma-spec-reporter'        // Detailed spec output
'karma-json-reporter'        // JSON output
'karma-teamcity-reporter'    // TeamCity integration

Popular Preprocessors

// Popular preprocessors
'karma-babel-preprocessor'    // Babel transpilation
'karma-webpack'              // Webpack bundling
'karma-rollup-preprocessor'  // Rollup bundling
'karma-browserify'           // Browserify bundling
'karma-typescript'           // TypeScript compilation
'karma-coverage'             // Code coverage instrumentation

Popular Frameworks

// Popular testing frameworks
'karma-jasmine'              // Jasmine
'karma-mocha'               // Mocha
'karma-qunit'               // QUnit
'karma-tap'                 // TAP
'karma-jest'                // Jest compatibility

docs

cli-interface.md

configuration.md

index.md

plugin-system.md

programmatic-api.md

tile.json