Skip to content

Commit 0909943

Browse files
filipesilvaalexeagle
authored andcommitted
test(@angular-devkit/build-angular): test karma on watch mode
1 parent e4c0151 commit 0909943

File tree

3 files changed

+104
-11
lines changed

3 files changed

+104
-11
lines changed

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

+20-8
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,15 @@
99
// TODO: cleanup this file, it's copied as is from Angular CLI.
1010

1111
import * as path from 'path';
12-
import * as fs from 'fs';
1312
import * as glob from 'glob';
1413
import * as webpack from 'webpack';
1514
const webpackDevMiddleware = require('webpack-dev-middleware');
1615

17-
import { AssetPattern } from '../../browser/schema';
1816
import { KarmaWebpackFailureCb } from './karma-webpack-failure-cb';
17+
import { statsErrorsToString } from '../utilities/stats';
18+
import { getWebpackStatsConfig } from '../models/webpack-configs/stats';
19+
import { createConsoleLogger } from '@angular-devkit/core/node';
20+
import { logging } from '@angular-devkit/core';
1921

2022
/**
2123
* Enumerate needed (but not require/imported) dependencies from this file
@@ -62,7 +64,7 @@ const init: any = (config: any, emitter: any, customFileHandlers: any) => {
6264
)
6365
}
6466
const options = config.buildWebpack.options;
65-
const projectRoot = config.buildWebpack.projectRoot as string;
67+
const logger: logging.Logger = config.buildWebpack.logger || createConsoleLogger();
6668
successCb = config.buildWebpack.successCb;
6769
failureCb = config.buildWebpack.failureCb;
6870

@@ -93,7 +95,9 @@ const init: any = (config: any, emitter: any, customFileHandlers: any) => {
9395
// Add webpack config.
9496
const webpackConfig = config.buildWebpack.webpackConfig;
9597
const webpackMiddlewareConfig = {
96-
logLevel: 'error', // Hide webpack output because its noisy.
98+
// Hide webpack output because its noisy.
99+
logLevel: 'error',
100+
stats: false,
97101
watchOptions: { poll: options.poll },
98102
publicPath: '/_karma_webpack_/',
99103
};
@@ -147,9 +151,9 @@ const init: any = (config: any, emitter: any, customFileHandlers: any) => {
147151
try {
148152
compiler = webpack(webpackConfig);
149153
} catch (e) {
150-
console.error(e.stack || e);
154+
logger.error(e.stack || e)
151155
if (e.details) {
152-
console.error(e.details);
156+
logger.error(e.details)
153157
}
154158
throw e;
155159
}
@@ -175,9 +179,17 @@ const init: any = (config: any, emitter: any, customFileHandlers: any) => {
175179
}
176180

177181
let lastCompilationHash: string | undefined;
182+
const statsConfig = getWebpackStatsConfig();
178183
compiler.hooks.done.tap('karma', (stats: any) => {
179-
// Refresh karma only when there are no webpack errors, and if the compilation changed.
180-
if (stats.compilation.errors.length === 0 && stats.hash != lastCompilationHash) {
184+
if (stats.compilation.errors.length > 0) {
185+
const json = stats.toJson(config.stats);
186+
// Print compilation errors.
187+
logger.error(statsErrorsToString(json, statsConfig));
188+
lastCompilationHash = undefined;
189+
// Emit a failure build event if there are compilation errors.
190+
failureCb && failureCb();
191+
} else if (stats.hash != lastCompilationHash) {
192+
// Refresh karma only when there are no webpack errors, and if the compilation changed.
181193
lastCompilationHash = stats.hash;
182194
emitter.refreshFiles();
183195
}

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

+1
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ export class KarmaBuilder implements Builder<KarmaBuilderSchema> {
9797
// When this workaround is removed, user projects need to be updated to use a Karma
9898
// version that has a fix for this issue.
9999
toJSON: () => { },
100+
logger: this.context.logger,
100101
};
101102

102103
// TODO: inside the configs, always use the project root and not the workspace root.

packages/angular_devkit/build_angular/test/karma/rebuilds_spec_large.ts

+83-3
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import { runTargetSpec } from '@angular-devkit/architect/testing';
10-
import { debounceTime, take, tap } from 'rxjs/operators';
9+
import { DefaultTimeout, runTargetSpec } from '@angular-devkit/architect/testing';
10+
import { Subject } from 'rxjs';
11+
import { debounceTime, delay, take, takeUntil, takeWhile, tap } from 'rxjs/operators';
1112
import { host, karmaTargetSpec } from '../utils';
1213

14+
1315
describe('Karma Builder watch mode', () => {
1416
beforeEach(done => host.initialize().toPromise().then(done, done.fail));
1517
afterEach(done => host.restore().toPromise().then(done, done.fail));
@@ -23,5 +25,83 @@ describe('Karma Builder watch mode', () => {
2325
).toPromise();
2426

2527
expect(res).toEqual({ success: true });
26-
}, 30000);
28+
});
29+
30+
it('recovers from compilation failures in watch mode', (done) => {
31+
const overrides = { watch: true };
32+
let buildCount = 0;
33+
let phase = 1;
34+
35+
runTargetSpec(host, karmaTargetSpec, overrides, DefaultTimeout * 3).pipe(
36+
debounceTime(500),
37+
tap((buildEvent) => {
38+
buildCount += 1;
39+
switch (phase) {
40+
case 1:
41+
// Karma run should succeed.
42+
// Add a compilation error.
43+
expect(buildEvent.success).toBe(true);
44+
// Add an syntax error to a non-main file.
45+
host.appendToFile('src/app/app.component.spec.ts', `]]]`);
46+
phase = 2;
47+
break;
48+
49+
case 2:
50+
// Karma run should fail due to compilation error. Fix it.
51+
expect(buildEvent.success).toBe(false);
52+
host.replaceInFile('src/app/app.component.spec.ts', `]]]`, '');
53+
phase = 3;
54+
break;
55+
56+
case 3:
57+
// Karma run should succeed again.
58+
expect(buildEvent.success).toBe(true);
59+
phase = 4;
60+
break;
61+
}
62+
}),
63+
takeWhile(() => phase < 4),
64+
).toPromise().then(
65+
() => done(),
66+
() => done.fail(`stuck at phase ${phase} [builds: ${buildCount}]`),
67+
);
68+
});
69+
70+
it('does not rebuild when nothing changed', (done) => {
71+
const overrides = { watch: true };
72+
let buildCount = 0;
73+
let phase = 1;
74+
75+
const stopSubject = new Subject();
76+
const stop$ = stopSubject.asObservable().pipe(delay(5000));
77+
78+
runTargetSpec(host, karmaTargetSpec, overrides, DefaultTimeout * 3).pipe(
79+
debounceTime(500),
80+
tap((buildEvent) => {
81+
buildCount += 1;
82+
switch (phase) {
83+
case 1:
84+
// Karma run should succeed.
85+
// Add a compilation error.
86+
expect(buildEvent.success).toBe(true);
87+
// Touch the file.
88+
host.appendToFile('src/app/app.component.spec.ts', ``);
89+
// Signal the stopper, which delays emission by 5s.
90+
// If there's no rebuild within that time then the test is successful.
91+
stopSubject.next();
92+
phase = 2;
93+
break;
94+
95+
case 2:
96+
// Should never trigger this second build.
97+
expect(true).toBeFalsy('Should not trigger second build.');
98+
break;
99+
}
100+
}),
101+
takeUntil(stop$),
102+
).toPromise().then(
103+
() => done(),
104+
() => done.fail(`stuck at phase ${phase} [builds: ${buildCount}]`),
105+
);
106+
});
27107
});

0 commit comments

Comments
 (0)