Source profileQuality 93/100Review permissions

PramodDutta/qaskills/seed-skills/karma-testing/SKILL.md

Karma Testing

Comprehensive Karma test runner skill for browser-based JavaScript unit testing with Jasmine, Mocha, or QUnit frameworks, real browser execution, coverage reporting, and CI/CD pipeline integration.

Source repository stars
211
Declared platforms
3
Static risk flags
2
Last source update
2026-08-28
Source checked
2026-08-28

Decision brief

What it does: where it fits

You are an expert QA engineer specializing in Karma test runner configuration and browser-based JavaScript testing. When the user asks you to write, review, debug, or set up Karma-related tests or configurations, follow these detailed instructions.

Best for

  • When configuring Karma for an Angular, React, or vanilla JavaScript project
  • When setting up browser-based unit testing with Jasmine or Mocha
  • When adding code coverage reporting to a Karma test suite

Not for

  • Running headed browsers in CI -- Using non-headless Chrome/Firefox in CI requires a display server (Xvfb) and is slower. Always use headless variants.
  • Not cleaning up DOM elements -- Elements added to document.body in tests persist across tests, causing side effects and false positives.

Compatibility matrix

Platform support, with evidence labels

PlatformStatusEvidenceWhat to check
CodexDeclaredSource recordInstall path and trigger
Claude CodeDeclaredSource recordInstall path and trigger
CursorDeclaredSource recordInstall path and trigger
Gemini CLINot declaredNo explicit evidencePortability before use
Open the compatibility checker

Installation

Inspect first. Install second.

The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.

Source-detected install commandSource
npx skills add https://github.com/PramodDutta/qaskills --skill "seed-skills/karma-testing"
Safe inspection promptEditorial

Inspect the Agent Skill "Karma Testing" from https://github.com/PramodDutta/qaskills/blob/fb3fbec70591bad971dd97c5d9add6eaa99bae18/seed-skills/karma-testing/SKILL.md at commit fb3fbec70591bad971dd97c5d9add6eaa99bae18. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.

Workflow

What the source asks the agent to do

  1. 01

    Setup

    Review the “Setup” section in the pinned source before continuing.

    Review and apply the “Setup” source section.
  2. 02

    Core Principles

    1. Real Browser Execution -- Karma runs tests in actual browsers (Chrome, Firefox, Safari, Edge), catching browser-specific issues that Node.js-based runners miss. This is its primary advantage over purely Node-based test runners. 2. Framework Agnostic -- Karma works with Jasmin…

    Real Browser Execution -- Karma runs tests in actual browsers (Chrome, Firefox, Safari, Edge), catching browser-specific issues that Node.js-based runners miss. This is its primary advantage over purely Node-based test…Framework Agnostic -- Karma works with Jasmine, Mocha, QUnit, and other testing frameworks. Configuration determines which framework is used. Jasmine is the most common pairing.File Pattern Management -- Karma's files array in configuration determines which source and test files are loaded. Use glob patterns to include files systematically.
  3. 03

    When to Use This Skill

    When configuring Karma for an Angular, React, or vanilla JavaScript project

    When configuring Karma for an Angular, React, or vanilla JavaScript projectWhen setting up browser-based unit testing with Jasmine or MochaWhen adding code coverage reporting to a Karma test suite
  4. 04

    Project Structure

    Review the “Project Structure” section in the pinned source before continuing.

    Review and apply the “Project Structure” source section.
  5. 05

    Configuration

    Review the “Configuration” section in the pinned source before continuing.

    Review and apply the “Configuration” source section.

Permission review

Static risk signals and limitations

Network access

medium · line 329

The documentation includes network, browsing, or remote request actions.

apiClient = new ApiClient('http://localhost:3000/api');

Network access

medium · line 344

The documentation includes network, browsing, or remote request actions.

expect(window.fetch).toHaveBeenCalledWith('http://localhost:3000/api/users', jasmine.any(Object));

Runs scripts

medium · line 487

The documentation asks the agent to run terminal commands or scripts.

npx karma start

Runs scripts

medium · line 490

The documentation asks the agent to run terminal commands or scripts.

npx karma start --single-run

Evidence record

Why each signal appears

EvidenceSourceComputedTestedEditorial
SignalValueEvidence typeMeaning
Quality score93/100ComputedDocumentation, specificity, maintenance, and trust rules
Repository stars211SourceRepository attention, not individual Skill quality
Compatibility3 platformsSourceDeclared in the catalog source record
Usage guideautomated source guideEditorialGenerated or reviewed according to the visible evidence level

Pinned source

Provenance and original SKILL.md

Repository
PramodDutta/qaskills
Skill path
seed-skills/karma-testing/SKILL.md
Commit
fb3fbec70591bad971dd97c5d9add6eaa99bae18
License
MIT
Collected
2026-08-28
Default branch
main
View the original SKILL.md

Karma Testing

You are an expert QA engineer specializing in Karma test runner configuration and browser-based JavaScript testing. When the user asks you to write, review, debug, or set up Karma-related tests or configurations, follow these detailed instructions.

Karma is a test runner that executes JavaScript tests in real browsers. It works with testing frameworks like Jasmine, Mocha, and QUnit, providing real browser environments for accurate DOM testing, live-reload during development, and CI/CD-compatible reporting.

Core Principles

  1. Real Browser Execution -- Karma runs tests in actual browsers (Chrome, Firefox, Safari, Edge), catching browser-specific issues that Node.js-based runners miss. This is its primary advantage over purely Node-based test runners.
  2. Framework Agnostic -- Karma works with Jasmine, Mocha, QUnit, and other testing frameworks. Configuration determines which framework is used. Jasmine is the most common pairing.
  3. File Pattern Management -- Karma's files array in configuration determines which source and test files are loaded. Use glob patterns to include files systematically.
  4. Coverage Thresholds -- Configure Istanbul/Karma-coverage to enforce minimum coverage thresholds. Fail the build when coverage drops below acceptable levels.
  5. Preprocessor Pipeline -- Use preprocessors for TypeScript compilation, module bundling (webpack/browserify), and coverage instrumentation. Order matters in the pipeline.
  6. Watch Mode for Development -- Karma's watch mode (autoWatch: true) re-runs tests on file changes, providing instant feedback during development.
  7. Headless for CI -- Use headless Chrome/Firefox in CI pipelines to avoid display server dependencies while still testing in real browser engines.

When to Use This Skill

  • When configuring Karma for an Angular, React, or vanilla JavaScript project
  • When setting up browser-based unit testing with Jasmine or Mocha
  • When adding code coverage reporting to a Karma test suite
  • When configuring Karma for CI/CD pipelines
  • When debugging Karma configuration or test execution issues
  • When working with karma.conf.js, karma start, or Karma plugins

Project Structure

project-root/
├── karma.conf.js                   # Karma configuration
├── karma.ci.conf.js                # CI-specific overrides
├── src/
│   ├── components/
│   │   ├── calculator.js
│   │   ├── string-utils.js
│   │   └── form-validator.js
│   ├── services/
│   │   ├── api-client.js
│   │   └── storage.js
│   └── app.js
├── test/
│   ├── unit/
│   │   ├── components/
│   │   │   ├── calculator.spec.js
│   │   │   ├── string-utils.spec.js
│   │   │   └── form-validator.spec.js
│   │   └── services/
│   │       ├── api-client.spec.js
│   │       └── storage.spec.js
│   ├── helpers/
│   │   ├── test-setup.js
│   │   └── dom-helpers.js
│   └── fixtures/
│       └── mock-data.js
├── coverage/                       # Generated coverage reports
└── package.json

Configuration

karma.conf.js (Jasmine + Chrome)

module.exports = function (config) {
  config.set({
    basePath: '',
    frameworks: ['jasmine'],
    files: [
      'src/**/*.js',
      'test/helpers/**/*.js',
      'test/unit/**/*.spec.js',
    ],
    exclude: [],
    preprocessors: {
      'src/**/*.js': ['coverage'],
    },
    reporters: ['progress', 'coverage'],
    coverageReporter: {
      type: 'html',
      dir: 'coverage/',
      subdir: '.',
      check: {
        global: {
          statements: 80,
          branches: 75,
          functions: 80,
          lines: 80,
        },
      },
    },
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['Chrome'],
    singleRun: false,
    concurrency: Infinity,
    browserNoActivityTimeout: 30000,
  });
};

CI Configuration (karma.ci.conf.js)

const baseConfig = require('./karma.conf.js');

module.exports = function (config) {
  baseConfig(config);

  config.set({
    browsers: ['ChromeHeadless'],
    singleRun: true,
    autoWatch: false,
    reporters: ['progress', 'coverage', 'junit'],
    junitReporter: {
      outputDir: 'reports',
      outputFile: 'test-results.xml',
      useBrowserName: false,
    },
    coverageReporter: {
      type: 'lcov',
      dir: 'coverage/',
      subdir: '.',
      check: {
        global: {
          statements: 80,
          branches: 75,
          functions: 80,
          lines: 80,
        },
      },
    },
  });
};

TypeScript + Webpack Configuration

const webpackConfig = require('./webpack.test.config');

module.exports = function (config) {
  config.set({
    basePath: '',
    frameworks: ['jasmine'],
    files: [
      { pattern: 'test/**/*.spec.ts', watched: false },
    ],
    preprocessors: {
      'test/**/*.spec.ts': ['webpack', 'sourcemap'],
    },
    webpack: webpackConfig,
    webpackMiddleware: {
      stats: 'errors-only',
    },
    reporters: ['progress', 'coverage-istanbul'],
    coverageIstanbulReporter: {
      reports: ['html', 'lcovonly', 'text-summary'],
      dir: 'coverage/',
      fixWebpackSourcePaths: true,
      thresholds: {
        emitWarning: false,
        global: {
          statements: 80,
          lines: 80,
          branches: 75,
          functions: 80,
        },
      },
    },
    browsers: ['ChromeHeadless'],
    singleRun: true,
  });
};

Writing Tests (Jasmine)

Basic Unit Tests

describe('Calculator', () => {
  let calculator;

  beforeEach(() => {
    calculator = new Calculator();
  });

  describe('add', () => {
    it('should add two positive numbers', () => {
      expect(calculator.add(2, 3)).toBe(5);
    });

    it('should handle negative numbers', () => {
      expect(calculator.add(-1, -2)).toBe(-3);
    });

    it('should handle zero', () => {
      expect(calculator.add(0, 5)).toBe(5);
      expect(calculator.add(5, 0)).toBe(5);
    });

    it('should handle floating point numbers', () => {
      expect(calculator.add(0.1, 0.2)).toBeCloseTo(0.3, 10);
    });
  });

  describe('divide', () => {
    it('should divide two numbers', () => {
      expect(calculator.divide(10, 2)).toBe(5);
    });

    it('should throw on division by zero', () => {
      expect(() => calculator.divide(10, 0)).toThrowError('Division by zero');
    });
  });
});

String Utility Tests

describe('StringUtils', () => {
  describe('capitalize', () => {
    it('should capitalize the first letter', () => {
      expect(StringUtils.capitalize('hello')).toBe('Hello');
    });

    it('should handle empty strings', () => {
      expect(StringUtils.capitalize('')).toBe('');
    });

    it('should handle already capitalized strings', () => {
      expect(StringUtils.capitalize('Hello')).toBe('Hello');
    });

    it('should handle single characters', () => {
      expect(StringUtils.capitalize('a')).toBe('A');
    });
  });

  describe('truncate', () => {
    it('should truncate long strings with ellipsis', () => {
      const result = StringUtils.truncate('This is a very long string', 10);
      expect(result).toBe('This is a ...');
      expect(result.length).toBeLessThanOrEqual(13);
    });

    it('should not truncate short strings', () => {
      expect(StringUtils.truncate('Short', 10)).toBe('Short');
    });
  });
});

DOM Testing

describe('Form Validator', () => {
  let container;
  let validator;

  beforeEach(() => {
    container = document.createElement('div');
    container.innerHTML = `
      <form id="test-form">
        <input type="text" id="name" data-testid="name-input" />
        <input type="email" id="email" data-testid="email-input" />
        <input type="password" id="password" data-testid="password-input" />
        <span id="name-error" class="error" data-testid="name-error"></span>
        <span id="email-error" class="error" data-testid="email-error"></span>
        <button type="submit" data-testid="submit-btn">Submit</button>
      </form>
    `;
    document.body.appendChild(container);
    validator = new FormValidator('#test-form');
  });

  afterEach(() => {
    document.body.removeChild(container);
  });

  it('should validate required name field', () => {
    const nameInput = document.querySelector('[data-testid="name-input"]');
    nameInput.value = '';
    const result = validator.validateField('name');
    expect(result.valid).toBe(false);
    expect(result.message).toBe('Name is required');
  });

  it('should validate email format', () => {
    const emailInput = document.querySelector('[data-testid="email-input"]');
    emailInput.value = 'not-an-email';
    const result = validator.validateField('email');
    expect(result.valid).toBe(false);
    expect(result.message).toContain('valid email');
  });

  it('should show error messages in DOM', () => {
    const nameInput = document.querySelector('[data-testid="name-input"]');
    nameInput.value = '';
    validator.validate();
    const errorEl = document.querySelector('[data-testid="name-error"]');
    expect(errorEl.textContent).toBe('Name is required');
    expect(errorEl.classList.contains('visible')).toBe(true);
  });

  it('should enable submit on valid form', () => {
    document.querySelector('[data-testid="name-input"]').value = 'John';
    document.querySelector('[data-testid="email-input"]').value = '[email protected]';
    document.querySelector('[data-testid="password-input"]').value = 'SecurePass123!';

    validator.validate();
    const submitBtn = document.querySelector('[data-testid="submit-btn"]');
    expect(submitBtn.disabled).toBe(false);
  });
});

Async Testing

describe('ApiClient', () => {
  let apiClient;

  beforeEach(() => {
    apiClient = new ApiClient('http://localhost:3000/api');
  });

  it('should fetch users successfully', async () => {
    spyOn(window, 'fetch').and.returnValue(
      Promise.resolve(
        new Response(JSON.stringify([{ id: 1, name: 'Alice' }]), {
          status: 200,
          headers: { 'Content-Type': 'application/json' },
        })
      )
    );

    const users = await apiClient.getUsers();
    expect(users).toEqual([{ id: 1, name: 'Alice' }]);
    expect(window.fetch).toHaveBeenCalledWith('http://localhost:3000/api/users', jasmine.any(Object));
  });

  it('should handle API errors', async () => {
    spyOn(window, 'fetch').and.returnValue(
      Promise.resolve(new Response('Not Found', { status: 404 }))
    );

    try {
      await apiClient.getUsers();
      fail('Expected an error to be thrown');
    } catch (error) {
      expect(error.message).toContain('404');
    }
  });

  it('should handle network failures', async () => {
    spyOn(window, 'fetch').and.returnValue(Promise.reject(new Error('Network error')));

    try {
      await apiClient.getUsers();
      fail('Expected an error to be thrown');
    } catch (error) {
      expect(error.message).toBe('Network error');
    }
  });
});

Jasmine Spies and Mocks

describe('EventTracker', () => {
  let tracker;
  let mockStorage;

  beforeEach(() => {
    mockStorage = jasmine.createSpyObj('Storage', ['getItem', 'setItem', 'removeItem']);
    tracker = new EventTracker(mockStorage);
  });

  it('should store events in storage', () => {
    tracker.track('page_view', { page: '/home' });

    expect(mockStorage.setItem).toHaveBeenCalledWith(
      jasmine.stringMatching(/^event_/),
      jasmine.stringContaining('"type":"page_view"')
    );
  });

  it('should retrieve event count', () => {
    mockStorage.getItem.and.returnValue(JSON.stringify({ count: 5 }));

    const count = tracker.getEventCount('page_view');
    expect(count).toBe(5);
  });

  it('should call flush callback after batch size', () => {
    const flushCallback = jasmine.createSpy('flushCallback');
    tracker.onFlush(flushCallback);

    for (let i = 0; i < 10; i++) {
      tracker.track('click', { element: `btn_${i}` });
    }

    expect(flushCallback).toHaveBeenCalledTimes(1);
    expect(flushCallback).toHaveBeenCalledWith(jasmine.arrayContaining([
      jasmine.objectContaining({ type: 'click' }),
    ]));
  });
});

LocalStorage and SessionStorage Testing

describe('StorageService', () => {
  let service;

  beforeEach(() => {
    localStorage.clear();
    sessionStorage.clear();
    service = new StorageService();
  });

  afterEach(() => {
    localStorage.clear();
    sessionStorage.clear();
  });

  it('should store and retrieve values', () => {
    service.set('user', { name: 'Alice', role: 'admin' });
    const result = service.get('user');
    expect(result).toEqual({ name: 'Alice', role: 'admin' });
  });

  it('should return null for missing keys', () => {
    expect(service.get('nonexistent')).toBeNull();
  });

  it('should handle storage quota exceeded', () => {
    spyOn(localStorage, 'setItem').and.throwError('QuotaExceededError');
    expect(() => service.set('key', 'value')).toThrowError('Storage quota exceeded');
  });

  it('should clear expired items', () => {
    service.set('temp', 'data', { ttl: -1 }); // Already expired
    service.cleanExpired();
    expect(service.get('temp')).toBeNull();
  });
});

Best Practices

  1. Use headless browsers in CI -- Configure ChromeHeadless or FirefoxHeadless for CI pipelines to avoid display server requirements while maintaining real browser engine testing.
  2. Enforce coverage thresholds -- Set minimum coverage percentages in coverageReporter.check.global and fail the build when coverage drops below acceptable levels.
  3. Use singleRun: true for CI -- Ensure Karma exits after tests complete in CI environments. Watch mode (autoWatch: true) is for development only.
  4. Clean up DOM after each test -- Remove any elements added to document.body in afterEach hooks to prevent test pollution.
  5. Use Jasmine's createSpyObj for creating mock objects with multiple methods. This is cleaner than manually stubbing each method.
  6. Configure appropriate timeouts -- Set browserNoActivityTimeout and browserDisconnectTimeout high enough for slow CI environments but low enough to catch hanging tests.
  7. Use preprocessors for modern JavaScript -- Configure webpack or browserify preprocessors for TypeScript, ES modules, and JSX compilation before test execution.
  8. Separate CI and dev configurations -- Create karma.ci.conf.js that extends the base config with CI-specific settings (headless, single run, junit reporter).
  9. Use source maps -- Enable source map preprocessor for accurate error stack traces and coverage mapping back to original source files.
  10. Group related specs in describe blocks -- Organize tests hierarchically by module, class, and method for clear reporting output.

Anti-Patterns

  1. Running headed browsers in CI -- Using non-headless Chrome/Firefox in CI requires a display server (Xvfb) and is slower. Always use headless variants.
  2. Not cleaning up DOM elements -- Elements added to document.body in tests persist across tests, causing side effects and false positives.
  3. Missing singleRun in CI -- Without singleRun: true, Karma watches for changes and never exits, hanging the CI pipeline.
  4. Hardcoding file paths in files array -- Use glob patterns (src/**/*.js) instead of listing individual files. New files are automatically included.
  5. Not using coverage thresholds -- Without thresholds, coverage can silently drop over time. Enforce minimums to maintain quality.
  6. Ignoring browser-specific failures -- Tests that pass in Chrome but fail in Firefox indicate real compatibility issues. Investigate rather than skip.
  7. Using fit and fdescribe in committed code -- Focused tests (fit, fdescribe) skip other tests silently. Use --grep for selective execution instead.
  8. Not configuring preprocessors -- Serving raw TypeScript or ES modules to browsers causes syntax errors. Always configure appropriate compilation preprocessors.
  9. Setting browserNoActivityTimeout too low -- Tests that involve async operations may need more than the default timeout. Set it to at least 30 seconds for CI.
  10. Not using reporter plugins -- The default progress reporter is insufficient for CI. Add junit for CI integration and coverage for quality metrics.

CLI Reference

# Run tests (uses karma.conf.js by default)
npx karma start

# Run in single-run mode
npx karma start --single-run

# Run with specific config
npx karma start karma.ci.conf.js

# Run with specific browsers
npx karma start --browsers ChromeHeadless,FirefoxHeadless

# Run specific test files
npx karma start --files "test/unit/calculator.spec.js"

# Run with verbose logging
npx karma start --log-level debug

# Initialize karma config
npx karma init

# Watch and re-run on changes
npx karma start --auto-watch --no-single-run

Setup

# Install Karma and Jasmine
npm install --save-dev karma karma-jasmine karma-chrome-launcher jasmine-core

# Coverage reporting
npm install --save-dev karma-coverage

# CI reporting
npm install --save-dev karma-junit-reporter

# TypeScript support
npm install --save-dev karma-webpack karma-sourcemap-loader typescript ts-loader

# Istanbul coverage for webpack/TypeScript
npm install --save-dev karma-coverage-istanbul-reporter

# Initialize configuration
npx karma init karma.conf.js

Frequently asked questions

What to verify before installation and use

What does the Karma Testing source document cover?

You are an expert QA engineer specializing in Karma test runner configuration and browser-based JavaScript testing. When the user asks you to write, review, debug, or set up Karma-related tests or configurations, follow these detailed instructions.

How do I install Karma Testing?

The source record exposes this install command: npx skills add https://github.com/PramodDutta/qaskills --skill "seed-skills/karma-testing". Inspect the command and pinned source before running it.

Which Agent platforms does the source record declare?

The pinned source record declares support for: codex, claude code, cursor.

Which permission-related actions were detected?

Static rules flagged network, exec-script in the source; the page lists the matching lines and excerpts.

Alternatives

Compare before choosing