Skip to content

Karma in CI/CD Pipelines

Running Karma in CI is where most of the configuration decisions from earlier lessons — headless browsers, singleRun, timeouts — actually matter most. This lesson covers the general principles before specific platform lessons (GitHub Actions, Jenkins, Docker) go deeper.

The Non-Negotiables for CI

Every CI-run Karma configuration needs the same three things: a headless browser (no display server exists on most CI runners), singleRun: true (so the process actually exits), and a correct process exit code (so the CI system can tell pass from fail).

// karma.ci.conf.js (or branching inside karma.conf.js)
config.set({
  browsers: ['ChromeHeadlessCI'],
  customLaunchers: {
    ChromeHeadlessCI: {
      base: 'ChromeHeadless',
      flags: ['--no-sandbox', '--disable-gpu']
    }
  },
  singleRun: true,
  reporters: ['progress', 'junit']
});

This is close to the minimum viable CI configuration for most Karma projects — headless, single-run, and a CI-friendly reporter.

A Typical CI Invocation

npx karma start --single-run --browsers ChromeHeadlessCI

# Exit code 0 -> CI marks the step as passed
# Exit code non-zero -> CI marks the step as failed
  • CLI flags are commonly used to force CI-safe values without needing a fully separate config file.
  • The process exit code is the entire contract between Karma and the CI system — nothing else matters to CI at that boundary.
  • Generous timeouts (covered earlier) matter more in CI than locally, since CI machines are often slower or shared.
  • Artifacts (coverage reports, JUnit XML) should be written to a directory the CI platform is configured to collect.

Karma CI Checklist

Confirm each of these before trusting a Karma CI pipeline.

Checklist Item Why It Matters
Headless browser configured No display server exists on most CI runners
--no-sandbox (if containerized) Chrome's sandbox often conflicts with container restrictions
singleRun: true Without it, the CI job hangs indefinitely
Generous timeouts CI machines are often slower/more resource-constrained
A CI-readable reporter (junit) Enables dashboards and historical trend tracking
Coverage thresholds (if used) Turns coverage into an active regression gate

Keeping Local and CI Runs in Sync

A common source of 'works locally, fails in CI' confusion is configuration drift between what developers run locally and what CI actually executes. Branching on process.env.CI inside one shared config, rather than maintaining a wildly different CI-only config, keeps the two close enough that most bugs reproduce in both places.

const isCI = !!process.env.CI;

config.set({
  browsers: [isCI ? 'ChromeHeadlessCI' : 'ChromeHeadless'],
  singleRun: isCI
});

Speeding Up CI Karma Runs

Since browser launch overhead and dependency installation dominate CI run time far more than actual test execution for most suites, caching node_modules (or your package manager's cache) between CI runs is usually the single biggest speed win available, before considering Karma-specific tuning.

  • Cache dependency installs between CI runs using your platform's built-in caching mechanism.
  • Avoid installing full desktop browser packages when a headless-only Chromium/Chrome install suffices.
  • Run only the browsers actually needed for CI confidence, not every browser available locally.
  • Split very large suites across parallel CI jobs if run time becomes a genuine bottleneck.

Common Mistakes

  • Maintaining a CI config so different from the local config that failures rarely reproduce in both places.
  • Forgetting singleRun: true in CI, causing jobs to hang until a platform timeout kills them.
  • Not caching dependencies between CI runs, paying the full install cost on every single job.
  • Running every available browser in CI when the actual project only needs one or two for real confidence.

Key Takeaways

  • CI Karma runs need a headless browser, singleRun: true, and a correct process exit code as non-negotiables.
  • Branching on process.env.CI inside one shared config reduces drift between local and CI behavior.
  • Dependency caching is usually the biggest available speed win for CI run time.
  • CI-readable reporters and coverage thresholds turn results into actionable dashboards and regression gates.

Pro Tip

Whenever a bug report says 'CI fails but it passes on my machine,' run the exact CI command locally first (karma start --single-run --browsers ChromeHeadlessCI) before assuming it's a CI-specific flake. Reproducing the CI invocation locally rules out configuration drift as the culprit in minutes.