Skip to content

Commit fa275bf

Browse files
committed
ci: fix npm5 on CircleCI
1 parent 834b090 commit fa275bf

File tree

6 files changed

+153
-107
lines changed

6 files changed

+153
-107
lines changed

.circleci/config.yml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,17 @@ jobs:
44
working_directory: ~/angular-cli
55
docker:
66
- image: angular/ngcontainer
7+
- image: node:8.4
78
steps:
89
- checkout
910
- restore_cache:
10-
key: angular-cli-{{ .Branch }}-{{ checksum "yarn.lock" }}
11+
key: angular-cli-{{ .Branch }}-{{ checksum "package-lock.json" }}
1112
- run: |
12-
npm install -g npm@~5.3.0
13-
npm install
13+
node --version
14+
npm --version
15+
npm install --quiet
1416
- save_cache:
15-
key: angular-cli-{{ .Branch }}-{{ checksum "yarn.lock" }}
17+
key: angular-cli-{{ .Branch }}-{{ checksum "package-lock.json" }}
1618
paths:
1719
- "node_modules"
1820
- run: xvfb-run -a node tests/run_e2e.js --glob=tests/build/**

tests/e2e/tests/build/rebuild-css-change.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import {
22
killAllProcesses,
3-
exec,
43
waitForAnyProcessOutputToMatch,
54
execAndWaitForOutputToMatch
65
} from '../../utils/process';
@@ -21,8 +20,6 @@ export default function() {
2120

2221
return execAndWaitForOutputToMatch('ng', ['serve'], webpackGoodRegEx)
2322
// Should trigger a rebuild.
24-
.then(() => exec('touch', 'src/main.ts'))
25-
.then(() => waitForAnyProcessOutputToMatch(webpackGoodRegEx, 10000))
2623
.then(() => appendToFile('src/app/app.component.css', ':host { color: blue; }'))
2724
.then(() => waitForAnyProcessOutputToMatch(webpackGoodRegEx, 10000))
2825
.then(() => killAllProcesses(), (err: any) => {

tests/e2e/tests/build/rebuild-deps-type-check.ts

Lines changed: 32 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import {
44
execAndWaitForOutputToMatch,
55
} from '../../utils/process';
66
import {writeFile, prependToFile, appendToFile} from '../../utils/fs';
7-
import {wait} from '../../utils/utils';
87
import {getGlobalVariable} from '../../utils/env';
98

109

@@ -37,45 +36,50 @@ export default function() {
3736
.then(() => appendToFile('src/main.ts', `
3837
console.log(funky2('town'));
3938
`))
40-
.then(() => wait(2000))
4139
// Should trigger a rebuild, no error expected.
4240
.then(() => execAndWaitForOutputToMatch('ng', ['serve'], doneRe))
4341
// Make an invalid version of the file.
44-
.then(() => wait(2000))
45-
.then(() => writeFile('src/funky2.ts', `
46-
export function funky2(value: number): number {
47-
return value + 1;
48-
}
49-
`))
5042
// Should trigger a rebuild, this time an error is expected.
51-
.then(() => waitForAnyProcessOutputToMatch(doneRe, 10000))
52-
.then(({ stderr }) => {
53-
if (!/ERROR in .*\/src\/main\.ts \(/.test(stderr)) {
43+
.then(() => Promise.all([
44+
waitForAnyProcessOutputToMatch(doneRe, 20000),
45+
writeFile('src/funky2.ts', `
46+
export function funky2(value: number): number {
47+
return value + 1;
48+
}
49+
`)
50+
]))
51+
.then((results) => {
52+
const stderr = results[0].stderr;
53+
if (!/ERROR in (.*\/src\/)?main\.ts/.test(stderr)) {
5454
throw new Error('Expected an error but none happened.');
5555
}
5656
})
5757
// Change an UNRELATED file and the error should still happen.
58-
.then(() => wait(2000))
59-
.then(() => appendToFile('src/app/app.module.ts', `
60-
function anything(): number {}
61-
`))
62-
// Should trigger a rebuild, this time an error is expected.
63-
.then(() => waitForAnyProcessOutputToMatch(doneRe, 10000))
64-
.then(({ stderr }) => {
65-
if (!/ERROR in .*\/src\/main\.ts \(/.test(stderr)) {
58+
// Should trigger a rebuild, this time an error is also expected.
59+
.then(() => Promise.all([
60+
waitForAnyProcessOutputToMatch(doneRe, 20000),
61+
appendToFile('src/app/app.module.ts', `
62+
function anything(): number {}
63+
`)
64+
]))
65+
.then((results) => {
66+
const stderr = results[0].stderr;
67+
if (!/ERROR in (.*\/src\/)?main\.ts/.test(stderr)) {
6668
throw new Error('Expected an error but none happened.');
6769
}
6870
})
6971
// Fix the error!
70-
.then(() => wait(2000))
71-
.then(() => writeFile('src/funky2.ts', `
72-
export function funky2(value: string): string {
73-
return value + 'hello';
74-
}
75-
`))
76-
.then(() => waitForAnyProcessOutputToMatch(doneRe, 10000))
77-
.then(({ stderr }) => {
78-
if (/ERROR in .*\/src\/main\.ts \(/.test(stderr)) {
72+
.then(() => Promise.all([
73+
waitForAnyProcessOutputToMatch(doneRe, 20000),
74+
writeFile('src/funky2.ts', `
75+
export function funky2(value: string): string {
76+
return value + 'hello';
77+
}
78+
`)
79+
]))
80+
.then((results) => {
81+
const stderr = results[0].stderr;
82+
if (/ERROR in (.*\/src\/)?main\.ts/.test(stderr)) {
7983
throw new Error('Expected no error but an error was shown.');
8084
}
8185
})

tests/e2e/tests/build/rebuild.ts

Lines changed: 75 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import {
22
killAllProcesses,
3-
exec,
43
waitForAnyProcessOutputToMatch,
54
execAndWaitForOutputToMatch,
65
ng,
@@ -11,7 +10,6 @@ import {request} from '../../utils/http';
1110
import {getGlobalVariable} from '../../utils/env';
1211

1312
const validBundleRegEx = /webpack: bundle is now VALID|webpack: Compiled successfully./;
14-
const invalidBundleRegEx = /webpack: bundle is now INVALID|webpack: Compiling.../;
1513

1614
export default function() {
1715
if (process.platform.startsWith('win')) {
@@ -26,66 +24,96 @@ export default function() {
2624
const chunkRegExp = /chunk\s+\{/g;
2725

2826
return execAndWaitForOutputToMatch('ng', ['serve'], validBundleRegEx)
29-
// Should trigger a rebuild.
30-
.then(() => exec('touch', 'src/main.ts'))
31-
.then(() => waitForAnyProcessOutputToMatch(invalidBundleRegEx, 10000))
32-
.then(() => waitForAnyProcessOutputToMatch(validBundleRegEx, 10000))
3327
// Count the bundles.
3428
.then(({ stdout }) => {
3529
oldNumberOfChunks = stdout.split(chunkRegExp).length;
3630
})
3731
// Add a lazy module.
3832
.then(() => ng('generate', 'module', 'lazy', '--routing'))
39-
// Just wait for the rebuild, otherwise we might be validating the last build.
40-
.then(() => waitForAnyProcessOutputToMatch(validBundleRegEx, 10000))
41-
.then(() => writeFile('src/app/app.module.ts', `
42-
import { BrowserModule } from '@angular/platform-browser';
43-
import { NgModule } from '@angular/core';
44-
import { FormsModule } from '@angular/forms';
45-
import { HttpModule } from '@angular/http';
33+
// Should trigger a rebuild with a new bundle.
34+
// We need to use Promise.all to ensure we are waiting for the rebuild just before we write
35+
// the file, otherwise rebuilds can be too fast and fail CI.
36+
.then(() => Promise.all([
37+
waitForAnyProcessOutputToMatch(validBundleRegEx, 10000),
38+
writeFile('src/app/app.module.ts', `
39+
import { BrowserModule } from '@angular/platform-browser';
40+
import { NgModule } from '@angular/core';
41+
import { FormsModule } from '@angular/forms';
42+
import { HttpModule } from '@angular/http';
4643
47-
import { AppComponent } from './app.component';
48-
import { RouterModule } from '@angular/router';
44+
import { AppComponent } from './app.component';
45+
import { RouterModule } from '@angular/router';
4946
50-
@NgModule({
51-
declarations: [
52-
AppComponent
53-
],
54-
imports: [
55-
BrowserModule,
56-
FormsModule,
57-
HttpModule,
58-
RouterModule.forRoot([
59-
{ path: 'lazy', loadChildren: './lazy/lazy.module#LazyModule' }
60-
])
61-
],
62-
providers: [],
63-
bootstrap: [AppComponent]
64-
})
65-
export class AppModule { }
66-
`))
67-
// Should trigger a rebuild with a new bundle.
68-
.then(() => waitForAnyProcessOutputToMatch(validBundleRegEx, 10000))
47+
@NgModule({
48+
declarations: [
49+
AppComponent
50+
],
51+
imports: [
52+
BrowserModule,
53+
FormsModule,
54+
HttpModule,
55+
RouterModule.forRoot([
56+
{ path: 'lazy', loadChildren: './lazy/lazy.module#LazyModule' }
57+
])
58+
],
59+
providers: [],
60+
bootstrap: [AppComponent]
61+
})
62+
export class AppModule { }
63+
`)
64+
]))
6965
// Count the bundles.
70-
.then(({ stdout }) => {
66+
.then((results) => {
67+
const stdout = results[0].stdout;
7168
let newNumberOfChunks = stdout.split(chunkRegExp).length;
7269
if (oldNumberOfChunks >= newNumberOfChunks) {
7370
throw new Error('Expected webpack to create a new chunk, but did not.');
7471
}
7572
})
7673
// Change multiple files and check that all of them are invalidated and recompiled.
77-
.then(() => writeMultipleFiles({
78-
'src/app/app.module.ts': `
79-
console.log('$$_E2E_GOLDEN_VALUE_1');
80-
export let X = '$$_E2E_GOLDEN_VALUE_2';
81-
`,
82-
'src/main.ts': `
83-
import * as m from './app/app.module';
84-
console.log(m.X);
85-
console.log('$$_E2E_GOLDEN_VALUE_3');
86-
`
87-
}))
88-
.then(() => waitForAnyProcessOutputToMatch(validBundleRegEx, 10000))
74+
.then(() => Promise.all([
75+
waitForAnyProcessOutputToMatch(validBundleRegEx, 10000),
76+
writeMultipleFiles({
77+
'src/app/app.module.ts': `
78+
import { BrowserModule } from '@angular/platform-browser';
79+
import { NgModule } from '@angular/core';
80+
81+
import { AppComponent } from './app.component';
82+
83+
@NgModule({
84+
declarations: [
85+
AppComponent
86+
],
87+
imports: [
88+
BrowserModule
89+
],
90+
providers: [],
91+
bootstrap: [AppComponent]
92+
})
93+
export class AppModule { }
94+
95+
console.log('$$_E2E_GOLDEN_VALUE_1');
96+
export let X = '$$_E2E_GOLDEN_VALUE_2';
97+
`,
98+
'src/main.ts': `
99+
import { enableProdMode } from '@angular/core';
100+
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
101+
102+
import { AppModule } from './app/app.module';
103+
import { environment } from './environments/environment';
104+
105+
if (environment.production) {
106+
enableProdMode();
107+
}
108+
109+
platformBrowserDynamic().bootstrapModule(AppModule);
110+
111+
import * as m from './app/app.module';
112+
console.log(m.X);
113+
console.log('$$_E2E_GOLDEN_VALUE_3');
114+
`
115+
})
116+
]))
89117
.then(() => wait(2000))
90118
.then(() => request('http://localhost:4200/main.bundle.js'))
91119
.then((body) => {

tests/e2e/utils/process.ts

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import * as child_process from 'child_process';
22
import {blue, yellow} from 'chalk';
33
import {getGlobalVariable} from './env';
4-
import {rimraf, writeFile} from './fs';
4+
import {rimraf} from './fs';
5+
import {wait} from './utils';
56
const treeKill = require('tree-kill');
67

78

@@ -13,7 +14,7 @@ interface ExecOptions {
1314

1415
let _processes: child_process.ChildProcess[] = [];
1516

16-
type ProcessOutput = {
17+
export type ProcessOutput = {
1718
stdout: string;
1819
stderr: string;
1920
};
@@ -97,26 +98,29 @@ function _exec(options: ExecOptions, cmd: string, args: string[]): Promise<Proce
9798
export function waitForAnyProcessOutputToMatch(match: RegExp,
9899
timeout = 30000): Promise<ProcessOutput> {
99100
// Race between _all_ processes, and the timeout. First one to resolve/reject wins.
100-
return Promise.race(_processes.map(childProcess => new Promise(resolve => {
101-
let stdout = '';
102-
let stderr = '';
103-
childProcess.stdout.on('data', (data: Buffer) => {
104-
stdout += data.toString();
105-
if (data.toString().match(match)) {
106-
resolve({ stdout, stderr });
107-
}
108-
});
109-
childProcess.stderr.on('data', (data: Buffer) => {
110-
stderr += data.toString();
111-
});
112-
})).concat([
113-
new Promise((resolve, reject) => {
114-
// Wait for 30 seconds and timeout.
115-
setTimeout(() => {
116-
reject(new Error(`Waiting for ${match} timed out (timeout: ${timeout}msec)...`));
117-
}, timeout);
118-
})
119-
]));
101+
const timeoutPromise: Promise<ProcessOutput> = new Promise((_resolve, reject) => {
102+
// Wait for 30 seconds and timeout.
103+
setTimeout(() => {
104+
reject(new Error(`Waiting for ${match} timed out (timeout: ${timeout}msec)...`));
105+
}, timeout);
106+
});
107+
108+
const matchPromises: Promise<ProcessOutput>[] = _processes.map(
109+
childProcess => new Promise(resolve => {
110+
let stdout = '';
111+
let stderr = '';
112+
childProcess.stdout.on('data', (data: Buffer) => {
113+
stdout += data.toString();
114+
if (data.toString().match(match)) {
115+
resolve({ stdout, stderr });
116+
}
117+
});
118+
childProcess.stderr.on('data', (data: Buffer) => {
119+
stderr += data.toString();
120+
});
121+
}));
122+
123+
return Promise.race(matchPromises.concat([timeoutPromise]));
120124
}
121125

122126
export function killAllProcesses(signal = 'SIGTERM') {
@@ -133,11 +137,22 @@ export function silentExec(cmd: string, ...args: string[]) {
133137
}
134138

135139
export function execAndWaitForOutputToMatch(cmd: string, args: string[], match: RegExp) {
136-
return _exec({ waitForMatch: match }, cmd, args);
140+
let maybeWait = Promise.resolve();
141+
if (cmd === 'ng' && args[0] === 'serve') {
142+
// Webpack watcher can rebuild a few times due to files changes that happened just before the
143+
// build (e.g. `git clean`), so we wait here.
144+
maybeWait = wait(5000);
145+
}
146+
return maybeWait.then(() => _exec({ waitForMatch: match }, cmd, args));
137147
}
138148

139149
export function silentExecAndWaitForOutputToMatch(cmd: string, args: string[], match: RegExp) {
140-
return _exec({ silent: true, waitForMatch: match }, cmd, args);
150+
let maybeWait = Promise.resolve();
151+
if (cmd === 'ng' && args[0] === 'serve') {
152+
// See execAndWaitForOutputToMatch for why the wait.
153+
maybeWait = wait(5000);
154+
}
155+
return maybeWait.then(() => _exec({ silent: true, waitForMatch: match }, cmd, args));
141156
}
142157

143158

tests/e2e/utils/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export function expectToFail(fn: () => Promise<any>, errorMessage?: string): Pro
99
}, () => { });
1010
}
1111

12-
export function wait(msecs: number) {
12+
export function wait(msecs: number): Promise<void> {
1313
return new Promise((resolve) => {
1414
setTimeout(resolve, msecs);
1515
});

0 commit comments

Comments
 (0)