Running JavaScript Tests in CI: Configuring Karma with ChromeHeadless
Stop CI failures caused by missing display servers. Learn how to configure Karma with ChromeHeadless and Puppeteer for reliable, headless JavaScript unit testing.
31 Dec 2025, 07:43 UTC

The CI Bottleneck: GUI-Dependent Tests
When running JavaScript unit tests locally, seeing a browser window pop up to execute tests is helpful. However, this becomes a blocker in Continuous Integration pipelines. CI runners—like GitHub Actions, GitLab CI, or Jenkins—typically operate in headless environments without a display server. If your test runner attempts to launch a visible browser, the process will crash with a "No usable browser found" or "Could not find a display" error.
The solution is to decouple test execution from the graphical user interface using ChromeHeadless. By configuring Karma to use a headless browser, you can execute Jasmine or Mocha suites in a real Chromium environment without needing a physical monitor or a virtual frame buffer.
How Karma Orchestrates Headless Execution
Karma is not a test framework or a browser; it is a test runner. It acts as a proxy server that launches the browser, injects test files, and captures results to report them back to the terminal.
To move to a headless setup, Karma relies on a launcher. While karma-chrome-launcher is the standard, integrating it with Puppeteer ensures the CI environment has a compatible version of Chromium installed and configured to run without a window.
Implementation: Configuring karma.conf.js
Install karma, karma-chrome-launcher, and puppeteer via npm. The critical logic resides in karma.conf.js.
module.exports = function(config) {
config.set({
frameworks: ['jasmine'],
files: [
'src/**/*.js',
'test/**/*.spec.js'
],
browsers: ['ChromeHeadless'],
singleRun: true,
customLaunchers: {
ChromeHeadlessCI: {
base: 'ChromeHeadless',
flags: ['--no-sandbox', '--disable-setuid-sandbox']
}
},
reporters: ['progress']
});
};Execution and Permissions
Run the command from your project root in the terminal:
npm testThe user executing the command must have permissions to execute binaries in node_modules. If running as root in a Docker container, the --no-sandbox flag is mandatory, as Chrome refuses to run as root with the sandbox enabled for security reasons.
Trade-offs and Resource Constraints
- Rendering Discrepancies: Headless Chrome is highly accurate, but it may not catch CSS layout bugs or specific interaction glitches that only appear in a headed browser. It is a tool for logic and functional testing, not visual regression.
- Memory Spikes: Each browser instance launched by Karma consumes significant RAM. Running multiple browsers in parallel on a small CI runner can lead to Out of Memory kills.
- Version Drift: If the version of Chromium installed by Puppeteer diverges significantly from karma-chrome-launcher expectations, you may see intermittent connection timeouts during the "Connecting to browser" phase.
Verifying the Setup
Check for these markers in CI logs:
- No window pop-up: the process starts and runs without attempting to open a Chrome window.
- Browser connection: the log states ChromeHeadless connected.
- Process exit: because singleRun is true, the terminal returns immediately after the final test result rather than hanging in watch mode.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.