From 66c25be710f5d20e10d14be2949d793cb9938144 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Tue, 13 Dec 2016 21:05:42 +0100 Subject: [PATCH] build: avoid multiple refreshes when running unit tests locally (#2162) Avoids Karma running tests multiple times when developing locally, due to it triggering a test run whenever a new file is written to disk. This change switches to manually spawning a Karma server and then running the tests by depending on Gulp's API instead. --- test/karma.config.ts | 2 +- tools/gulp/constants.ts | 2 ++ tools/gulp/tasks/components.ts | 28 ++++++----------- tools/gulp/tasks/unit-test.ts | 57 ++++++++++++++++++++++------------ 4 files changed, 50 insertions(+), 39 deletions(-) diff --git a/test/karma.config.ts b/test/karma.config.ts index 78d1452808f..144746c27ca 100644 --- a/test/karma.config.ts +++ b/test/karma.config.ts @@ -58,7 +58,7 @@ export function config(config) { port: 9876, colors: true, logLevel: config.LOG_INFO, - autoWatch: true, + autoWatch: false, sauceLabs: { testName: 'material2', diff --git a/tools/gulp/constants.ts b/tools/gulp/constants.ts index 3da36e1a295..4a7f2fed561 100644 --- a/tools/gulp/constants.ts +++ b/tools/gulp/constants.ts @@ -18,3 +18,5 @@ export const SASS_AUTOPREFIXER_OPTIONS = { export const NPM_VENDOR_FILES = [ '@angular', 'core-js/client', 'hammerjs', 'rxjs', 'systemjs/dist', 'zone.js/dist' ]; + +export const COMPONENTS_DIR = join(SOURCE_ROOT, 'lib'); diff --git a/tools/gulp/tasks/components.ts b/tools/gulp/tasks/components.ts index fcf54b59359..41f9d5e33b8 100644 --- a/tools/gulp/tasks/components.ts +++ b/tools/gulp/tasks/components.ts @@ -1,7 +1,7 @@ import {task, watch} from 'gulp'; import * as path from 'path'; -import {SOURCE_ROOT, DIST_COMPONENTS_ROOT, PROJECT_ROOT} from '../constants'; +import {DIST_COMPONENTS_ROOT, PROJECT_ROOT, COMPONENTS_DIR} from '../constants'; import {sassBuildTask, tsBuildTask, execNodeTask, copyTask, sequenceTask} from '../task_helpers'; import {writeFileSync} from 'fs'; @@ -17,42 +17,32 @@ const rollup = require('rollup').rollup; // When `tsconfig-spec.json` is used, we are outputting CommonJS modules. This is used // for unit tests (karma). -/** Path to the root of the Angular Material component library. */ -const componentsDir = path.join(SOURCE_ROOT, 'lib'); - /** Path to the tsconfig used for ESM output. */ -const tsconfigPath = path.relative(PROJECT_ROOT, path.join(componentsDir, 'tsconfig.json')); +const tsconfigPath = path.relative(PROJECT_ROOT, path.join(COMPONENTS_DIR, 'tsconfig.json')); /** [Watch task] Rebuilds (ESM output) whenever ts, scss, or html sources change. */ task(':watch:components', () => { - watch(path.join(componentsDir, '**/*.ts'), [':build:components:rollup']); - watch(path.join(componentsDir, '**/*.scss'), [':build:components:rollup']); - watch(path.join(componentsDir, '**/*.html'), [':build:components:rollup']); -}); - -/** [Watch task] Rebuilds for tests (CJS output) whenever ts, scss, or html sources change. */ -task(':watch:components:spec', () => { - watch(path.join(componentsDir, '**/*.ts'), [':build:components:spec']); - watch(path.join(componentsDir, '**/*.scss'), [':build:components:scss']); - watch(path.join(componentsDir, '**/*.html'), [':build:components:assets']); + watch(path.join(COMPONENTS_DIR, '**/*.ts'), [':build:components:rollup']); + watch(path.join(COMPONENTS_DIR, '**/*.scss'), [':build:components:rollup']); + watch(path.join(COMPONENTS_DIR, '**/*.html'), [':build:components:rollup']); }); /** Builds component typescript only (ESM output). */ -task(':build:components:ts', tsBuildTask(componentsDir, 'tsconfig-srcs.json')); +task(':build:components:ts', tsBuildTask(COMPONENTS_DIR, 'tsconfig-srcs.json')); /** Builds components typescript for tests (CJS output). */ -task(':build:components:spec', tsBuildTask(componentsDir)); +task(':build:components:spec', tsBuildTask(COMPONENTS_DIR)); /** Copies assets (html, markdown) to build output. */ task(':build:components:assets', copyTask([ - path.join(componentsDir, '**/*.!(ts|spec.ts)'), + path.join(COMPONENTS_DIR, '**/*.!(ts|spec.ts)'), path.join(PROJECT_ROOT, 'README.md'), ], DIST_COMPONENTS_ROOT)); /** Builds scss into css. */ -task(':build:components:scss', sassBuildTask(DIST_COMPONENTS_ROOT, componentsDir)); +task(':build:components:scss', sassBuildTask(DIST_COMPONENTS_ROOT, COMPONENTS_DIR)); /** Builds the UMD bundle for all of Angular Material. */ task(':build:components:rollup', [':build:components:inline'], () => { diff --git a/tools/gulp/tasks/unit-test.ts b/tools/gulp/tasks/unit-test.ts index 76801d022e6..cdd4746787b 100644 --- a/tools/gulp/tasks/unit-test.ts +++ b/tools/gulp/tasks/unit-test.ts @@ -2,10 +2,11 @@ import gulp = require('gulp'); import path = require('path'); import gulpMerge = require('merge2'); -import {PROJECT_ROOT} from '../constants'; +import {PROJECT_ROOT, COMPONENTS_DIR} from '../constants'; import {sequenceTask} from '../task_helpers'; const karma = require('karma'); +const runSequence = require('run-sequence'); /** Copies deps for unit tests to the build output. */ gulp.task(':build:test:vendor', function() { @@ -32,28 +33,10 @@ gulp.task(':test:deps', sequenceTask( ] )); -/** - * [Watch task] Build unit test dependencies, and rebuild whenever sources are changed. - * This should only be used when running tests locally. - */ -gulp.task(':test:watch', sequenceTask(':test:deps', ':watch:components:spec')); /** Build unit test dependencies and then inlines resources (html, css) into the JS output. */ gulp.task(':test:deps:inline', sequenceTask(':test:deps', ':inline-resources')); - -/** - * [Watch task] Runs the unit tests, rebuilding and re-testing when sources change. - * Does not inline resources. - * - * This task should be used when running unit tests locally. - */ -gulp.task('test', [':test:watch'], (done: () => void) => { - new karma.Server({ - configFile: path.join(PROJECT_ROOT, 'test/karma.conf.js') - }, done).start(); -}); - /** * Runs the unit tests once with inlined resources (html, css). Does not watch for changes. * @@ -65,3 +48,39 @@ gulp.task('test:single-run', [':test:deps:inline'], (done: () => void) => { singleRun: true }, done).start(); }); + +/** + * [Watch task] Runs the unit tests, rebuilding and re-testing when sources change. + * Does not inline resources. Note that this doesn't use Karma's built-in file + * watching. Due to the way our build process is set up, Karma ends up firing + * it's change detection for every file that is written to disk, which causes + * it to run tests multiple time and makes it hard to follow the console output. + * This approach runs the Karma server and then depends on the Gulp API to tell + * Karma when to run the tests. + * + * This task should be used when running unit tests locally. + */ +gulp.task('test', [':test:deps'], () => { + let patternRoot = path.join(COMPONENTS_DIR, '**/*'); + + // Configure the Karma server and override the autoWatch and singleRun just in case. + let server = new karma.Server({ + configFile: path.join(PROJECT_ROOT, 'test/karma.conf.js'), + autoWatch: false, + singleRun: false + }); + + // Refreshes Karma's file list and schedules a test run. + let runTests = () => { + server.refreshFiles().then(() => server._injector.get('executor').schedule()); + }; + + // Boot up the test server and run the tests whenever a new browser connects. + server.start(); + server.on('browser_register', runTests); + + // Watch for file changes, rebuild and run the tests. + gulp.watch(patternRoot + '.ts', () => runSequence(':build:components:spec', runTests)); + gulp.watch(patternRoot + '.scss', () => runSequence(':build:components:scss', runTests)); + gulp.watch(patternRoot + '.html', () => runSequence(':build:components:assets', runTests)); +});