Skip to content

chore: fix CI flakiness caused by race condition #1371

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Sep 29, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 3 additions & 11 deletions src/e2e-app/e2e-app-module.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {NgModule, ApplicationRef} from '@angular/core';
import {NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {RouterModule} from '@angular/router';
import {E2EApp, Home} from './e2e-app/e2e-app';
Expand All @@ -24,14 +24,6 @@ import {E2E_APP_ROUTES} from './e2e-app/routes';
BasicTabs,
Home,
],
entryComponents: [
E2EApp,
],
bootstrap: [E2EApp],
})
export class E2eAppModule {
constructor(private _appRef: ApplicationRef) { }

ngDoBootstrap() {
this._appRef.bootstrap(E2EApp);
}
}
export class E2eAppModule { }
2 changes: 1 addition & 1 deletion tools/gulp/tasks/ci.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ task('ci:forbidden-identifiers', function() {
// Travis sometimes does not exit the process and times out. This is to prevent that.
task('ci:test', ['test:single-run'], () => process.exit(0));
// Travis sometimes does not exit the process and times out. This is to prevent that.
task('ci:e2e', ['e2e'], () => process.exit(0));
task('ci:e2e', ['e2e:single-run'], () => process.exit(0));
47 changes: 35 additions & 12 deletions tools/gulp/tasks/components.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {task, watch} from 'gulp';
import {readdirSync, statSync, readFileSync} from 'fs';
import * as path from 'path';

import {SOURCE_ROOT, DIST_COMPONENTS_ROOT, PROJECT_ROOT} from '../constants';
Expand All @@ -10,31 +9,54 @@ import {writeFileSync} from 'fs';
const inlineResources = require('../../../scripts/release/inline-resources');
const rollup = require('rollup').rollup;


// NOTE: there are two build "modes" in this file, based on which tsconfig is used.
// When `tsconfig.json` is used, we are outputting ES6 modules and a UMD bundle. This is used
// for serving and for release.
//
// 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'));


/** [Watch task] Rebuilds (ESM output) whenever ts, scss, or html sources change. */
task(':watch:components', () => {
watch(path.join(componentsDir, '**/*.ts'), [':build:components:ts']);
watch(path.join(componentsDir, '**/*.scss'), [':build:components:scss']);
watch(path.join(componentsDir, '**/*.html'), [':build:components:assets']);
});

/** [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']);
});


/** Builds component typescript only (ESM output). */
task(':build:components:ts', tsBuildTask(componentsDir));

/** Builds components typescript for tests (CJS output). */
task(':build:components:spec', tsBuildTask(path.join(componentsDir, 'tsconfig-spec.json')));

/** Copies assets (html, markdown) to build output. */
task(':build:components:assets', copyTask([
path.join(componentsDir, '**/*.!(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, [path.join(componentsDir, 'core/style')]
));

/** Builds the UMD bundle for all of Angular Material. */
task(':build:components:rollup', [':build:components:inline'], () => {
const globals: {[name: string]: string} = {
// Angular dependencies
Expand Down Expand Up @@ -85,18 +107,19 @@ task(':build:components:rollup', [':build:components:inline'], () => {
});
});

task(':build:components:inline', [
':build:components:ts',
':build:components:scss',
':build:components:assets'
], () => {
return inlineResources(DIST_COMPONENTS_ROOT);
});

task('build:components', sequenceTask(
':build:components:rollup',
/** Builds components with resources (html, css) inlined into the built JS (ESM output). */
task(':build:components:inline', sequenceTask(
[':build:components:ts', ':build:components:scss', ':build:components:assets'],
':inline-resources',
));

/** Inlines resources (html, css) into the JS output (for either ESM or CJS output). */
task(':inline-resources', () => inlineResources(DIST_COMPONENTS_ROOT));

/** Builds components to ESM output and UMD bundle. */
task('build:components', [':build:components:rollup']);

/** Generates metadata.json files for all of the components. */
task(':build:components:ngc', ['build:components'], execNodeTask(
'@angular/compiler-cli', 'ngc', ['-p', path.relative(PROJECT_ROOT, path.join(componentsDir, 'tsconfig.json'))]
'@angular/compiler-cli', 'ngc', ['-p', tsconfigPath]
));
59 changes: 49 additions & 10 deletions tools/gulp/tasks/e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,36 +17,75 @@ const PROTRACTOR_CONFIG_PATH = path.join(PROJECT_ROOT, 'test/protractor.conf.js'

task(':watch:e2eapp', () => {
watch(path.join(appDir, '**/*.ts'), [':build:e2eapp:ts']);
watch(path.join(appDir, '**/*.scss'), [':build:e2eapp:scss']);
watch(path.join(appDir, '**/*.html'), [':build:e2eapp:assets']);
});


/** Copies e2e app dependencies to build output. */
task(':build:e2eapp:vendor', vendorTask());

/** Builds e2e app ts to js. */
task(':build:e2eapp:ts', [':build:components:ts'], tsBuildTask(appDir));

/** No-op (needed by buildAppTask). */
task(':build:e2eapp:scss', [':build:components:scss'], sassBuildTask(outDir, appDir, []));

/** Copies e2e app assets (html, css) to build output. */
task(':build:e2eapp:assets', copyTask(appDir, outDir));

/** Builds the entire e2e app. */
task('build:e2eapp', buildAppTask('e2eapp'));


/** Ensures that protractor and webdriver are set up to run. */
task(':test:protractor:setup', execNodeTask('protractor', 'webdriver-manager', ['update']));

/** Runs protractor tests (assumes that server is already running. */
task(':test:protractor', execNodeTask('protractor', [PROTRACTOR_CONFIG_PATH]));
// This task is used because, in some cases, protractor will block and not exit the process,
// causing Travis to timeout. This task should always be used in a synchronous sequence as
// the last step.

/**
* Forces process termination.
*
* This task is used because, in some cases, protractor will block and not exit the process,
* causing Travis to timeout. This task should always be used in a synchronous sequence as
* the last step.
*/
task(':e2e:done', () => process.exit(0));

let stopE2eServer: () => void = null;

/** Starts up the e2e app server. */
task(':serve:e2eapp', serverTask(false, (stream) => { stopE2eServer = () => stream.emit('kill') }));

/** Terminates the e2e app server */
task(':serve:e2eapp:stop', () => stopE2eServer());
task('serve:e2eapp', ['build:e2eapp'], sequenceTask([
':serve:e2eapp',
':watch:components',
]));

/** Builds and serves the e2e app. */
task('serve:e2eapp', sequenceTask('build:components', 'build:e2eapp', ':serve:e2eapp'));

/**
* [Watch task] Builds and serves e2e app, rebuilding whenever the sources change.
* This should only be used when running e2e tests locally.
*/
task('serve:e2eapp:watch', ['serve:e2eapp', ':watch:components', ':watch:e2eapp']);

/**
* [Watch task] Serves the e2e app and runs the protractor tests. Rebuilds when sources change.
*
* This task should only be used when running the e2e tests locally.
*/
task('e2e', sequenceTask(
':test:protractor:setup',
'serve:e2eapp:watch',
':test:protractor',
':serve:e2eapp:stop',
':e2e:done',
));

/**
* Runs the e2e once. Does not watch for changes.
*
* This task should be used when running tests on the CI server.
*/
task('e2e:single-run', sequenceTask(
':test:protractor:setup',
'serve:e2eapp',
':test:protractor',
Expand Down
31 changes: 26 additions & 5 deletions tools/gulp/tasks/unit-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import {PROJECT_ROOT, DIST_COMPONENTS_ROOT} 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() {
const npmVendorFiles = [
'@angular', 'core-js/client', 'hammerjs', 'rxjs', 'systemjs/dist', 'zone.js/dist'
Expand All @@ -21,24 +21,45 @@ gulp.task(':build:test:vendor', function() {
}));
});

/** Builds dependencies for unit tests. */
gulp.task(':test:deps', sequenceTask(
'clean',
[
':build:test:vendor',
':build:components:assets',
':build:components:scss',
':build:components:spec',
':build:components:inline',
':watch:components:spec',
]
));

gulp.task('test', [':test:deps'], (done: () => void) => {
/**
* [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();
});

gulp.task('test:single-run', [':test:deps'], (done: () => void) => {
/**
* Runs the unit tests once with inlined resources (html, css). Does not watch for changes.
*
* This task should be used when running tests on the CI server.
*/
gulp.task('test:single-run', [':test:deps:inline'], (done: () => void) => {
new karma.Server({
configFile: path.join(PROJECT_ROOT, 'test/karma.conf.js'),
singleRun: true
Expand Down