Skip to content

Commit 8995e49

Browse files
kyliaualan-agius4
authored andcommitted
feat(@angular-devkit/build-angular): Switch to karma-coverage
This commit switches coverage tooling from karma-coverage-istanbul-reporter to karma-coverage since it's better supported. Closes #17757
1 parent f52e5a7 commit 8995e49

File tree

13 files changed

+142
-74
lines changed

13 files changed

+142
-74
lines changed

integration/angular_cli/karma.conf.js

+7-4
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,19 @@ module.exports = function (config) {
1212
require('karma-jasmine'),
1313
require('karma-chrome-launcher'),
1414
require('karma-jasmine-html-reporter'),
15-
require('karma-coverage-istanbul-reporter'),
15+
require('karma-coverage'),
1616
require('@angular-devkit/build-angular/plugins/karma'),
1717
],
1818
client: {
1919
clearContext: false, // leave Jasmine Spec Runner output visible in browser
2020
},
21-
coverageIstanbulReporter: {
21+
coverageReporter: {
2222
dir: path.join(__dirname, 'coverage'),
23-
reports: ['html', 'lcovonly'],
24-
fixWebpackSourcePaths: true,
23+
subdir: '.',
24+
reporters: [
25+
{type: 'html'},
26+
{type: 'lcov'},
27+
],
2528
},
2629
reporters: ['progress', 'kjhtml'],
2730
port: 9876,

integration/angular_cli/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
"jasmine-spec-reporter": "~5.0.0",
3737
"karma": "~4.4.1",
3838
"karma-chrome-launcher": "~3.1.0",
39-
"karma-coverage-istanbul-reporter": "~2.1.0",
39+
"karma-coverage": "~2.0.3",
4040
"karma-jasmine": "~3.3.0",
4141
"karma-jasmine-html-reporter": "^1.5.0",
4242
"protractor": "~5.4.3",

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@
161161
"jsonc-parser": "2.3.0",
162162
"karma": "~5.2.0",
163163
"karma-chrome-launcher": "~3.1.0",
164-
"karma-coverage-istanbul-reporter": "~3.0.0",
164+
"karma-coverage": "~2.0.3",
165165
"karma-jasmine": "~4.0.0",
166166
"karma-jasmine-html-reporter": "^1.5.0",
167167
"karma-source-map-support": "1.4.0",

packages/angular_devkit/build_angular/BUILD.bazel

+1-1
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ LARGE_SPECS = {
282282
"extra_deps": [
283283
"@npm//karma",
284284
"@npm//karma-chrome-launcher",
285-
"@npm//karma-coverage-istanbul-reporter",
285+
"@npm//karma-coverage",
286286
"@npm//karma-jasmine",
287287
"@npm//karma-jasmine-html-reporter",
288288
"@npm//puppeteer",

packages/angular_devkit/build_angular/src/angular-cli-files/plugins/karma.ts

+28-8
Original file line numberDiff line numberDiff line change
@@ -88,12 +88,32 @@ const init: any = (config: any, emitter: any, customFileHandlers: any) => {
8888

8989
config.reporters.unshift('@angular-devkit/build-angular--event-reporter');
9090

91-
// When using code-coverage, auto-add coverage-istanbul.
92-
config.reporters = config.reporters || [];
93-
if (options.codeCoverage && config.reporters.indexOf('coverage-istanbul') === -1) {
94-
config.reporters.unshift('coverage-istanbul');
91+
// When using code-coverage, auto-add karma-coverage.
92+
if (options.codeCoverage) {
93+
config.plugins = config.plugins || [];
94+
config.reporters = config.reporters || [];
95+
const {plugins, reporters} = config;
96+
const hasCoveragePlugin = plugins.some((p: {}) => 'reporter:coverage' in p);
97+
const hasIstanbulPlugin = plugins.some((p: {}) => 'reporter:coverage-istanbul' in p);
98+
const hasCoverageReporter = reporters.includes('coverage');
99+
const hasIstanbulReporter = reporters.includes('coverage-istanbul');
100+
if (hasCoveragePlugin && !hasCoverageReporter) {
101+
reporters.push('coverage');
102+
}
103+
else if (hasIstanbulPlugin && !hasIstanbulReporter) {
104+
// coverage-istanbul is deprecated in favor of karma-coverage
105+
reporters.push('coverage-istanbul');
106+
}
107+
else {
108+
throw new Error('karma-coverage must be installed in order to run code coverage');
109+
}
110+
if (hasIstanbulPlugin) {
111+
logger.warn(`'karma-coverage-istanbul-reporter' usage has been deprecated since version 11.\n` +
112+
`Please install 'karma-coverage' and update 'karma.conf.js.' ` +
113+
'For more info, see https://github.com/karma-runner/karma-coverage/blob/master/README.md');
114+
}
95115
}
96-
116+
97117
// Add webpack config.
98118
const webpackConfig = config.buildWebpack.webpackConfig;
99119
const webpackMiddlewareConfig = {
@@ -188,7 +208,7 @@ const init: any = (config: any, emitter: any, customFileHandlers: any) => {
188208
logger.error(statsErrorsToString(json, statsConfig));
189209
lastCompilationHash = undefined;
190210
// Emit a failure build event if there are compilation errors.
191-
failureCb && failureCb();
211+
failureCb();
192212
} else if (stats.hash != lastCompilationHash) {
193213
// Refresh karma only when there are no webpack errors, and if the compilation changed.
194214
lastCompilationHash = stats.hash;
@@ -269,9 +289,9 @@ const eventReporter: any = function (this: any, baseReporterDecorator: any, conf
269289

270290
this.onRunComplete = function (_browsers: any, results: any) {
271291
if (results.exitCode === 0) {
272-
successCb && successCb();
292+
successCb();
273293
} else {
274-
failureCb && failureCb();
294+
failureCb();
275295
}
276296
}
277297

packages/angular_devkit/build_angular/src/karma/code-coverage_spec.ts

+54-21
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,24 @@
55
* Use of this source code is governed by an MIT-style license that can be
66
* found in the LICENSE file at https://angular.io/license
77
*/
8+
9+
// tslint:disable:no-implicit-dependencies
10+
811
import { Architect } from '@angular-devkit/architect';
912
import { normalize, virtualFs } from '@angular-devkit/core';
13+
import { last, tap } from 'rxjs/operators';
14+
import { promisify } from 'util';
1015
import { createArchitect, host, karmaTargetSpec } from '../test-utils';
1116

17+
// In each of the test below we'll have to call setTimeout to wait for the coverage
18+
// analysis to be done. This is because karma-coverage performs the analysis
19+
// asynchronously but the promise that it returns is not awaited by Karma.
20+
// Coverage analysis begins when onRunComplete() is invoked, and output files
21+
// are subsequently written to disk. For more information, see
22+
// https://github.com/karma-runner/karma-coverage/blob/32acafa90ed621abd1df730edb44ae55a4009c2c/lib/reporter.js#L221
23+
24+
const setTimeoutPromise = promisify(setTimeout);
25+
1226
describe('Karma Builder code coverage', () => {
1327
const coverageFilePath = normalize('coverage/lcov.info');
1428
let architect: Architect;
@@ -23,16 +37,19 @@ describe('Karma Builder code coverage', () => {
2337
it('supports code coverage option', async () => {
2438
const run = await architect.scheduleTarget(karmaTargetSpec, { codeCoverage: true });
2539

26-
await expectAsync(run.result).toBeResolvedTo(jasmine.objectContaining({ success: true }));
40+
const {success} = await run.result;
41+
expect(success).toBe(true);
2742

2843
await run.stop();
2944

45+
await setTimeoutPromise(1000);
46+
3047
const exists = host.scopedSync().exists(coverageFilePath);
31-
expect(exists).toBe(true);
48+
expect(exists).toBe(true, `${coverageFilePath} does not exist`);
3249

3350
if (exists) {
3451
const content = virtualFs.fileBufferToString(host.scopedSync().read(coverageFilePath));
35-
expect(content).toContain('polyfills.ts');
52+
expect(content).toContain('app.component.ts');
3653
expect(content).toContain('test.ts');
3754
}
3855
}, 120000);
@@ -41,23 +58,24 @@ describe('Karma Builder code coverage', () => {
4158
const overrides = {
4259
codeCoverage: true,
4360
codeCoverageExclude: [
44-
'src/polyfills.ts',
4561
'**/test.ts',
4662
],
4763
};
4864

4965
const run = await architect.scheduleTarget(karmaTargetSpec, overrides);
5066

51-
await expectAsync(run.result).toBeResolvedTo(jasmine.objectContaining({ success: true }));
67+
const {success} = await run.result;
68+
expect(success).toBe(true);
5269

5370
await run.stop();
5471

72+
await setTimeoutPromise(1000);
73+
5574
const exists = host.scopedSync().exists(coverageFilePath);
5675
expect(exists).toBe(true);
5776

5877
if (exists) {
5978
const content = virtualFs.fileBufferToString(host.scopedSync().read(coverageFilePath));
60-
expect(content).not.toContain('polyfills.ts');
6179
expect(content).not.toContain('test.ts');
6280
}
6381
}, 120000);
@@ -98,10 +116,13 @@ describe('Karma Builder code coverage', () => {
98116

99117
const run = await architect.scheduleTarget(karmaTargetSpec, { codeCoverage: true });
100118

101-
await expectAsync(run.result).toBeResolvedTo(jasmine.objectContaining({ success: true }));
119+
const {success} = await run.result;
120+
expect(success).toBe(true);
102121

103122
await run.stop();
104123

124+
await setTimeoutPromise(1000);
125+
105126
const exists = host.scopedSync().exists(coverageFilePath);
106127
expect(exists).toBe(true);
107128

@@ -111,20 +132,18 @@ describe('Karma Builder code coverage', () => {
111132
}
112133
}, 120000);
113134

114-
it(`should fail when coverage is below threhold and 'emitWarning' is false`, async () => {
115-
host.replaceInFile('karma.conf.js', 'fixWebpackSourcePaths: true',
116-
`
117-
fixWebpackSourcePaths: true,
118-
thresholds: {
119-
emitWarning: false,
120-
global: {
121-
statements: 100,
122-
lines: 100,
123-
branches: 100,
124-
functions: 100
135+
it('should exit with non-zero code when coverage is below threshold', async () => {
136+
host.replaceInFile('karma.conf.js', 'coverageReporter: {', `
137+
coverageReporter: {
138+
check: {
139+
global: {
140+
statements: 100,
141+
lines: 100,
142+
branches: 100,
143+
functions: 100
144+
}
125145
},
126-
}`,
127-
);
146+
`);
128147

129148
host.appendToFile('src/app/app.component.ts', `
130149
export function nonCovered(): boolean {
@@ -133,7 +152,21 @@ describe('Karma Builder code coverage', () => {
133152
`);
134153

135154
const run = await architect.scheduleTarget(karmaTargetSpec, { codeCoverage: true });
136-
await expectAsync(run.result).toBeResolvedTo(jasmine.objectContaining({ success: false }));
155+
156+
// In incremental mode, karma-coverage does not have the ability to mark a
157+
// run as failed if code coverage does not pass. This is because it does
158+
// the coverage asynchoronously and Karma does not await the promise
159+
// returned by the plugin.
160+
expect((await run.result).success).toBeTrue();
161+
162+
// However the program must exit with non-zero exit code.
163+
// This is a more common use case of coverage testing and must be supported.
164+
await run.output.pipe(
165+
last(),
166+
tap(buildEvent => expect(buildEvent.success).toBeFalse()),
167+
).toPromise();
168+
137169
await run.stop();
170+
138171
}, 120000);
139172
});

packages/angular_devkit/build_angular/src/karma/index.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,10 @@ export function execute(
155155
// Complete the observable once the Karma server returns.
156156
const karmaServer = new karma.Server(
157157
transforms.karmaOptions ? transforms.karmaOptions(karmaOptions) : karmaOptions,
158-
() => subscriber.complete(),
158+
(exitCode: number) => {
159+
subscriber.next({ success: exitCode === 0 });
160+
subscriber.complete();
161+
},
159162
);
160163
// karma typings incorrectly define start's return value as void
161164
// tslint:disable-next-line:no-use-of-empty-return-value

packages/angular_devkit/build_angular/test/hello-world-app/karma.conf.js

+8-5
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,19 @@ module.exports = function(config) {
1919
require('karma-jasmine'),
2020
require('karma-chrome-launcher'),
2121
require('karma-jasmine-html-reporter'),
22-
require('karma-coverage-istanbul-reporter'),
22+
require('karma-coverage'),
2323
require('@angular-devkit/build-angular/plugins/karma'),
2424
],
2525
client: {
2626
clearContext: false, // leave Jasmine Spec Runner output visible in browser
2727
},
28-
coverageIstanbulReporter: {
29-
dir: path.join(__dirname, 'coverage'),
30-
reports: ['html', 'lcovonly'],
31-
fixWebpackSourcePaths: true,
28+
coverageReporter: {
29+
dir: path.join(__dirname, './coverage'),
30+
subdir: '.',
31+
reporters: [
32+
{type: 'html'},
33+
{type: 'lcov'},
34+
],
3235
},
3336
reporters: ['progress', 'kjhtml'],
3437
port: 9876,

packages/angular_devkit/build_ng_packagr/test/ng-packaged/projects/lib/karma.conf.js

+8-4
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,20 @@ module.exports = function (config) {
1616
require('karma-jasmine'),
1717
require('karma-chrome-launcher'),
1818
require('karma-jasmine-html-reporter'),
19-
require('karma-coverage-istanbul-reporter'),
19+
require('karma-coverage'),
2020
require('@angular-devkit/build-angular/plugins/karma')
2121
],
2222
client: {
2323
clearContext: false // leave Jasmine Spec Runner output visible in browser
2424
},
25-
coverageIstanbulReporter: {
25+
coverageReporter: {
2626
dir: require('path').join(__dirname, 'coverage'),
27-
reports: ['html', 'lcovonly'],
28-
fixWebpackSourcePaths: true
27+
subdir: '.',
28+
reporters: [
29+
{type: 'html'},
30+
{type: 'lcov'},
31+
{type: 'text-summary'},
32+
],
2933
},
3034
reporters: ['progress', 'kjhtml'],
3135
port: 9876,

packages/schematics/angular/application/files/karma.conf.js.template

+8-4
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,20 @@ module.exports = function (config) {
99
require('karma-jasmine'),
1010
require('karma-chrome-launcher'),
1111
require('karma-jasmine-html-reporter'),
12-
require('karma-coverage-istanbul-reporter'),
12+
require('karma-coverage'),
1313
require('@angular-devkit/build-angular/plugins/karma')
1414
],
1515
client: {
1616
clearContext: false // leave Jasmine Spec Runner output visible in browser
1717
},
18-
coverageIstanbulReporter: {
18+
coverageReporter: {
1919
dir: require('path').join(__dirname, '<%= relativePathToWorkspaceRoot %>/coverage/<%= appName%>'),
20-
reports: ['html', 'lcovonly', 'text-summary'],
21-
fixWebpackSourcePaths: true
20+
subdir: '.',
21+
reporters: [
22+
{type: 'html'},
23+
{type: 'lcov'},
24+
{type: 'text-summary'},
25+
],
2226
},
2327
reporters: ['progress', 'kjhtml'],
2428
port: 9876,

packages/schematics/angular/library/files/karma.conf.js.template

+8-4
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,20 @@ module.exports = function (config) {
99
require('karma-jasmine'),
1010
require('karma-chrome-launcher'),
1111
require('karma-jasmine-html-reporter'),
12-
require('karma-coverage-istanbul-reporter'),
12+
require('karma-coverage'),
1313
require('@angular-devkit/build-angular/plugins/karma')
1414
],
1515
client: {
1616
clearContext: false // leave Jasmine Spec Runner output visible in browser
1717
},
18-
coverageIstanbulReporter: {
18+
coverageReporter: {
1919
dir: require('path').join(__dirname, '<%= relativePathToWorkspaceRoot %>/coverage/<%= folderName %>'),
20-
reports: ['html', 'lcovonly', 'text-summary'],
21-
fixWebpackSourcePaths: true
20+
subdir: '.',
21+
reporters: [
22+
{type: 'html'},
23+
{type: 'lcov'},
24+
{type: 'text-summary'},
25+
],
2226
},
2327
reporters: ['progress', 'kjhtml'],
2428
port: 9876,

packages/schematics/angular/workspace/files/package.json.template

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
"jasmine-spec-reporter": "~5.0.0",
3434
"karma": "~5.2.0",
3535
"karma-chrome-launcher": "~3.1.0",
36-
"karma-coverage-istanbul-reporter": "~3.0.2",
36+
"karma-coverage": "~2.0.3",
3737
"karma-jasmine": "~4.0.0",
3838
"karma-jasmine-html-reporter": "^1.5.0",
3939
"protractor": "~7.0.0",<% } %>

0 commit comments

Comments
 (0)