Skip to content

fix(@angular/build): only require karma-coverage when coverage is enabled - #34500

Open
MeAkib wants to merge 3 commits into
angular:mainfrom
MeAkib:fix/unit-test-karma-coverage-dependency
Open

MeAkib wants to merge 3 commits into
angular:mainfrom
MeAkib:fix/unit-test-karma-coverage-dependency

Conversation

@MeAkib

@MeAkib MeAkib commented Oct 23, 2026

Copy link
Copy Markdown
Contributor

The upfront dependency validation of the unit-test builder's karma runner checks the truthiness of the normalized coverage option, which is always an object since the introduction of the advanced coverage options. As a result, ng test with the karma runner fails with a MissingDependenciesError requesting the karma-coverage package even when coverage is disabled. Check the enabled property instead, matching the vitest runner and the karma executor. A new unit spec for the karma runner's dependency validation fails without this change.

…bled

The upfront dependency validation of the unit-test builder's karma runner checks
the truthiness of the normalized `coverage` option, which is always an object
since the introduction of the advanced coverage options. As a result, `ng test`
with the karma runner fails with a `MissingDependenciesError` requesting the
`karma-coverage` package even when coverage is disabled. Check the `enabled`
property instead, matching the vitest runner and the karma executor. A new unit
spec for the karma runner's dependency validation fails without this change.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the Karma test runner to check options.coverage.enabled instead of options.coverage when verifying dependencies, and introduces a new unit test suite to validate this behavior. The review feedback suggests simplifying the newly added tests by replacing the asynchronous assertions with synchronous Jasmine matchers, as the validateDependencies method is synchronous.

Comment thread packages/angular/build/src/builders/unit-test/runners/karma/index_spec.ts Outdated
Comment on lines +9 to +53
import assert from 'node:assert';
import { mkdir, mkdtemp, writeFile } from 'node:fs/promises';
import { join } from 'node:path';
import type { NormalizedUnitTestBuilderOptions } from '../../options';
import KarmaTestRunner from './index';

describe('KarmaTestRunner', () => {
describe('validateDependencies', () => {
let projectSourceRoot: string;

function createOptions(coverageEnabled: boolean): NormalizedUnitTestBuilderOptions {
return {
projectSourceRoot,
coverage: { enabled: coverageEnabled },
} as NormalizedUnitTestBuilderOptions;
}

beforeAll(async () => {
const baseTmpDir = process.env['TEST_TMPDIR'];
assert(baseTmpDir, 'TEST_TMPDIR is not set');
projectSourceRoot = await mkdtemp(join(baseTmpDir, 'angular-cli-karma-runner-'));

// A project with karma and karma-jasmine installed but not karma-coverage
await writeFile(join(projectSourceRoot, 'package.json'), '{"name": "test-project"}');
for (const packageName of ['karma', 'karma-jasmine']) {
const packageRoot = join(projectSourceRoot, 'node_modules', packageName);
await mkdir(packageRoot, { recursive: true });
await writeFile(
join(packageRoot, 'package.json'),
JSON.stringify({ name: packageName, version: '0.0.0', main: 'index.js' }),
);
await writeFile(join(packageRoot, 'index.js'), '');
}
});

it('does not require karma-coverage when coverage is disabled', () => {
expect(() => KarmaTestRunner.validateDependencies?.(createOptions(false))).not.toThrow();
});

it('requires karma-coverage when coverage is enabled', () => {
expect(() => KarmaTestRunner.validateDependencies?.(createOptions(true))).toThrowError(
/karma-coverage/,
);
});
});

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can simplify this test and avoid creating a temporary directory and mock node_modules on disk by passing a non-existent projectSourceRoot and asserting whether karma-coverage is included in the thrown MissingDependenciesError:

Suggested change
import assert from 'node:assert';
import { mkdir, mkdtemp, writeFile } from 'node:fs/promises';
import { join } from 'node:path';
import type { NormalizedUnitTestBuilderOptions } from '../../options';
import KarmaTestRunner from './index';
describe('KarmaTestRunner', () => {
describe('validateDependencies', () => {
let projectSourceRoot: string;
function createOptions(coverageEnabled: boolean): NormalizedUnitTestBuilderOptions {
return {
projectSourceRoot,
coverage: { enabled: coverageEnabled },
} as NormalizedUnitTestBuilderOptions;
}
beforeAll(async () => {
const baseTmpDir = process.env['TEST_TMPDIR'];
assert(baseTmpDir, 'TEST_TMPDIR is not set');
projectSourceRoot = await mkdtemp(join(baseTmpDir, 'angular-cli-karma-runner-'));
// A project with karma and karma-jasmine installed but not karma-coverage
await writeFile(join(projectSourceRoot, 'package.json'), '{"name": "test-project"}');
for (const packageName of ['karma', 'karma-jasmine']) {
const packageRoot = join(projectSourceRoot, 'node_modules', packageName);
await mkdir(packageRoot, { recursive: true });
await writeFile(
join(packageRoot, 'package.json'),
JSON.stringify({ name: packageName, version: '0.0.0', main: 'index.js' }),
);
await writeFile(join(packageRoot, 'index.js'), '');
}
});
it('does not require karma-coverage when coverage is disabled', () => {
expect(() => KarmaTestRunner.validateDependencies?.(createOptions(false))).not.toThrow();
});
it('requires karma-coverage when coverage is enabled', () => {
expect(() => KarmaTestRunner.validateDependencies?.(createOptions(true))).toThrowError(
/karma-coverage/,
);
});
});
import type { NormalizedUnitTestBuilderOptions } from '../../options';
import KarmaTestRunner from './index';
describe('KarmaTestRunner', () => {
describe('validateDependencies', () => {
function createOptions(coverageEnabled: boolean): NormalizedUnitTestBuilderOptions {
return {
projectSourceRoot: '/non-existent',
coverage: { enabled: coverageEnabled },
} as NormalizedUnitTestBuilderOptions;
}
it('does not require karma-coverage when coverage is disabled', () => {
expect(() => KarmaTestRunner.validateDependencies?.(createOptions(false))).toThrowError(
/^(?!.*karma-coverage)/s,
);
});
it('requires karma-coverage when coverage is enabled', () => {
expect(() => KarmaTestRunner.validateDependencies?.(createOptions(true))).toThrowError(
/karma-coverage/,
);
});
});
});

@alan-agius4 alan-agius4 added the target: patch This PR is targeted for the next patch release label Oct 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: @angular/build target: patch This PR is targeted for the next patch release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants