Skip to content

Commit

Permalink
test(@angular-devkit/build-angular): test karma on watch mode
Browse files Browse the repository at this point in the history
  • Loading branch information
filipesilva committed Nov 1, 2018
1 parent 1774624 commit 1453b9d
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@
// TODO: cleanup this file, it's copied as is from Angular CLI.

import * as path from 'path';
import * as fs from 'fs';
import * as glob from 'glob';
import * as webpack from 'webpack';
const webpackDevMiddleware = require('webpack-dev-middleware');

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

/**
* Enumerate needed (but not require/imported) dependencies from this file
Expand Down Expand Up @@ -62,7 +64,7 @@ const init: any = (config: any, emitter: any, customFileHandlers: any) => {
)
}
const options = config.buildWebpack.options;
const projectRoot = config.buildWebpack.projectRoot as string;
const logger: logging.Logger = config.buildWebpack.logger || createConsoleLogger();
successCb = config.buildWebpack.successCb;
failureCb = config.buildWebpack.failureCb;

Expand Down Expand Up @@ -93,7 +95,9 @@ const init: any = (config: any, emitter: any, customFileHandlers: any) => {
// Add webpack config.
const webpackConfig = config.buildWebpack.webpackConfig;
const webpackMiddlewareConfig = {
logLevel: 'error', // Hide webpack output because its noisy.
// Hide webpack output because its noisy.
logLevel: 'error',
stats: false,
watchOptions: { poll: options.poll },
publicPath: '/_karma_webpack_/',
};
Expand Down Expand Up @@ -147,9 +151,9 @@ const init: any = (config: any, emitter: any, customFileHandlers: any) => {
try {
compiler = webpack(webpackConfig);
} catch (e) {
console.error(e.stack || e);
logger.error(e.stack || e)
if (e.details) {
console.error(e.details);
logger.error(e.details)
}
throw e;
}
Expand All @@ -175,9 +179,17 @@ const init: any = (config: any, emitter: any, customFileHandlers: any) => {
}

let lastCompilationHash: string | undefined;
const statsConfig = getWebpackStatsConfig();
compiler.hooks.done.tap('karma', (stats: any) => {
// Refresh karma only when there are no webpack errors, and if the compilation changed.
if (stats.compilation.errors.length === 0 && stats.hash != lastCompilationHash) {
if (stats.compilation.errors.length > 0) {
const json = stats.toJson(config.stats);
// Print compilation errors.
logger.error(statsErrorsToString(json, statsConfig));
lastCompilationHash = undefined;
// Emit a failure build event if there are compilation errors.
failureCb && failureCb();
} else if (stats.hash != lastCompilationHash) {
// Refresh karma only when there are no webpack errors, and if the compilation changed.
lastCompilationHash = stats.hash;
emitter.refreshFiles();
}
Expand Down
1 change: 1 addition & 0 deletions packages/angular_devkit/build_angular/src/karma/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ export class KarmaBuilder implements Builder<KarmaBuilderSchema> {
// When this workaround is removed, user projects need to be updated to use a Karma
// version that has a fix for this issue.
toJSON: () => { },
logger: this.context.logger,
};

// TODO: inside the configs, always use the project root and not the workspace root.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
* found in the LICENSE file at https://angular.io/license
*/

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


describe('Karma Builder watch mode', () => {
beforeEach(done => host.initialize().toPromise().then(done, done.fail));
afterEach(done => host.restore().toPromise().then(done, done.fail));
Expand All @@ -23,5 +25,83 @@ describe('Karma Builder watch mode', () => {
).toPromise();

expect(res).toEqual({ success: true });
}, 30000);
});

it('recovers from compilation failures in watch mode', (done) => {
const overrides = { watch: true };
let buildCount = 0;
let phase = 1;

runTargetSpec(host, karmaTargetSpec, overrides, DefaultTimeout * 3).pipe(
debounceTime(500),
tap((buildEvent) => {
buildCount += 1;
switch (phase) {
case 1:
// Karma run should succeed.
// Add a compilation error.
expect(buildEvent.success).toBe(true);
// Add an syntax error to a non-main file.
host.appendToFile('src/app/app.component.spec.ts', `]]]`);
phase = 2;
break;

case 2:
// Karma run should fail due to compilation error. Fix it.
expect(buildEvent.success).toBe(false);
host.replaceInFile('src/app/app.component.spec.ts', `]]]`, '');
phase = 3;
break;

case 3:
// Karma run should succeed again.
expect(buildEvent.success).toBe(true);
phase = 4;
break;
}
}),
takeWhile(() => phase < 4),
).toPromise().then(
() => done(),
() => done.fail(`stuck at phase ${phase} [builds: ${buildCount}]`),
);
});

it('does not rebuild when nothing changed', (done) => {
const overrides = { watch: true };
let buildCount = 0;
let phase = 1;

const stopSubject = new Subject();
const stop$ = stopSubject.asObservable().pipe(delay(5000));

runTargetSpec(host, karmaTargetSpec, overrides, DefaultTimeout * 3).pipe(
debounceTime(500),
tap((buildEvent) => {
buildCount += 1;
switch (phase) {
case 1:
// Karma run should succeed.
// Add a compilation error.
expect(buildEvent.success).toBe(true);
// Touch the file.
host.appendToFile('src/app/app.component.spec.ts', ``);
// Signal the stopper, which delays emission by 5s.
// If there's no rebuild within that time then the test is successful.
stopSubject.next();
phase = 2;
break;

case 2:
// Should never trigger this second build.
expect(true).toBeFalsy('Should not trigger second build.');
break;
}
}),
takeUntil(stop$),
).toPromise().then(
() => done(),
() => done.fail(`stuck at phase ${phase} [builds: ${buildCount}]`),
);
});
});

0 comments on commit 1453b9d

Please sign in to comment.