Skip to content

Commit d2bef98

Browse files
authored
feat(@angular/cli): ng e2e defaults to random port (#4753)
BREAKING CHANGE: `ng e2e` will use a random port for serving by default instead of using 4200.
1 parent b6d8511 commit d2bef98

File tree

6 files changed

+101
-66
lines changed

6 files changed

+101
-66
lines changed

packages/@angular/cli/commands/build.ts

+1-7
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export const baseBuildCommandOptions: any = [
2828
{ name: 'i18n-format', type: String },
2929
{ name: 'locale', type: String },
3030
{ name: 'extract-css', type: Boolean, aliases: ['ec'] },
31-
{ name: 'watch', type: Boolean, aliases: ['w'] },
31+
{ name: 'watch', type: Boolean, default: false, aliases: ['w'] },
3232
{
3333
name: 'output-hashing',
3434
type: String,
@@ -60,12 +60,6 @@ const BuildCommand = Command.extend({
6060
run: function (commandOptions: BuildTaskOptions) {
6161
const project = this.project;
6262

63-
const additionalDefaults: any = {
64-
watch: false
65-
};
66-
67-
commandOptions = Object.assign({}, additionalDefaults, commandOptions);
68-
6963
// Check angular version.
7064
Version.assertAngularVersionIs2_3_1OrHigher(project.root);
7165

packages/@angular/cli/commands/e2e.ts

+17-17
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
const SilentError = require('silent-error');
22

3+
import { overrideOptions } from '../utilities/override-options';
34
import { CliConfig } from '../models/config';
45
import { ServeTaskOptions, baseServeCommandOptions } from './serve';
6+
import { checkPort } from '../utilities/check-port';
57
const Command = require('../ember-cli/lib/models/command');
68

79

@@ -13,31 +15,27 @@ export interface E2eTaskOptions extends ServeTaskOptions {
1315
elementExplorer: boolean;
1416
}
1517

16-
export const e2eCommandOptions = baseServeCommandOptions.concat([
17-
{ name: 'config', type: String, aliases: ['c'] },
18-
{ name: 'specs', type: Array, default: [], aliases: ['sp'] },
19-
{ name: 'element-explorer', type: Boolean, default: false, aliases: ['ee'] },
20-
{ name: 'webdriver-update', type: Boolean, default: true, aliases: ['wu'] },
21-
{ name: 'serve', type: Boolean, default: true, aliases: ['s'] }
22-
]);
23-
24-
2518
const E2eCommand = Command.extend({
2619
name: 'e2e',
2720
aliases: ['e'],
2821
description: 'Run e2e tests in existing project',
2922
works: 'insideProject',
30-
availableOptions: e2eCommandOptions,
23+
availableOptions: overrideOptions(
24+
baseServeCommandOptions.concat([
25+
{ name: 'config', type: String, aliases: ['c'] },
26+
{ name: 'specs', type: Array, default: [], aliases: ['sp'] },
27+
{ name: 'element-explorer', type: Boolean, default: false, aliases: ['ee'] },
28+
{ name: 'webdriver-update', type: Boolean, default: true, aliases: ['wu'] },
29+
{ name: 'serve', type: Boolean, default: true, aliases: ['s'] }
30+
]), [
31+
{ name: 'port', default: 0 },
32+
{ name: 'watch', default: false },
33+
]
34+
),
3135
run: function (commandOptions: E2eTaskOptions) {
3236
const E2eTask = require('../tasks/e2e').E2eTask;
3337
this.project.ngConfig = this.project.ngConfig || CliConfig.fromProject();
3438

35-
const additionalDefaults: any = {
36-
watch: false
37-
};
38-
39-
commandOptions = Object.assign({}, additionalDefaults, commandOptions);
40-
4139
const e2eTask = new E2eTask({
4240
ui: this.ui,
4341
project: this.project
@@ -72,7 +70,9 @@ const E2eCommand = Command.extend({
7270
}
7371
}
7472

75-
serve.run(commandOptions, rebuildCb)
73+
checkPort(commandOptions.port, commandOptions.host)
74+
.then((port: number) => commandOptions.port = port)
75+
.then(() => serve.run(commandOptions, rebuildCb))
7676
.catch(reject);
7777
});
7878
} else {

packages/@angular/cli/commands/serve.ts

+41-40
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { baseBuildCommandOptions } from './build';
44
import { CliConfig } from '../models/config';
55
import { Version } from '../upgrade/version';
66
import { ServeTaskOptions } from './serve';
7+
import { checkPort } from '../utilities/check-port';
8+
import { overrideOptions } from '../utilities/override-options';
79

810
const SilentError = require('silent-error');
911
const PortFinder = require('portfinder');
@@ -60,53 +62,52 @@ const ServeCommand = Command.extend({
6062
description: 'Builds and serves your app, rebuilding on file changes.',
6163
aliases: ['server', 's'],
6264

63-
availableOptions: baseServeCommandOptions.concat([
64-
{ name: 'live-reload', type: Boolean, default: true, aliases: ['lr'] },
65-
{
66-
name: 'live-reload-host',
67-
type: String,
68-
aliases: ['lrh'],
69-
description: 'Defaults to host'
70-
},
71-
{
72-
name: 'live-reload-base-url',
73-
type: String,
74-
aliases: ['lrbu'],
75-
description: 'Defaults to baseURL'
76-
},
77-
{
78-
name: 'live-reload-port',
79-
type: Number,
80-
aliases: ['lrp'],
81-
description: '(Defaults to port number within [49152...65535])'
82-
},
83-
{
84-
name: 'live-reload-live-css',
85-
type: Boolean,
86-
default: true,
87-
description: 'Whether to live reload CSS (default true)'
88-
},
89-
{
90-
name: 'hmr',
91-
type: Boolean,
92-
default: false,
93-
description: 'Enable hot module replacement',
94-
}
95-
]),
65+
availableOptions: overrideOptions(
66+
baseServeCommandOptions.concat([
67+
{ name: 'live-reload', type: Boolean, default: true, aliases: ['lr'] },
68+
{
69+
name: 'live-reload-host',
70+
type: String,
71+
aliases: ['lrh'],
72+
description: 'Defaults to host'
73+
},
74+
{
75+
name: 'live-reload-base-url',
76+
type: String,
77+
aliases: ['lrbu'],
78+
description: 'Defaults to baseURL'
79+
},
80+
{
81+
name: 'live-reload-port',
82+
type: Number,
83+
aliases: ['lrp'],
84+
description: '(Defaults to port number within [49152...65535])'
85+
},
86+
{
87+
name: 'live-reload-live-css',
88+
type: Boolean,
89+
default: true,
90+
description: 'Whether to live reload CSS (default true)'
91+
},
92+
{
93+
name: 'hmr',
94+
type: Boolean,
95+
default: false,
96+
description: 'Enable hot module replacement',
97+
}
98+
]), [
99+
{ name: 'watch', default: true },
100+
]
101+
),
96102

97103
run: function (commandOptions: ServeTaskOptions) {
98104
const ServeTask = require('../tasks/serve').default;
99105

100-
const additionalDefaults: any = {
101-
watch: true
102-
};
103-
104-
commandOptions = Object.assign({}, additionalDefaults, commandOptions);
105-
106106
Version.assertAngularVersionIs2_3_1OrHigher(this.project.root);
107107
commandOptions.liveReloadHost = commandOptions.liveReloadHost || commandOptions.host;
108108

109-
return checkExpressPort(commandOptions)
109+
return checkPort(commandOptions.port, commandOptions.host)
110+
.then((port: number) => commandOptions.port = port)
110111
.then(() => autoFindLiveReloadPort(commandOptions))
111112
.then((opts: ServeTaskOptions) => {
112113
const serve = new ServeTask({
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import * as denodeify from 'denodeify';
2+
3+
const SilentError = require('silent-error');
4+
const PortFinder = require('portfinder');
5+
const getPort = <any>denodeify(PortFinder.getPort);
6+
7+
PortFinder.basePort = 49152;
8+
9+
10+
export function checkPort(port: number, host: string) {
11+
return getPort({ port, host })
12+
.then((foundPort: number) => {
13+
14+
// If the port isn't available and we weren't looking for any port, throw error.
15+
if (port !== foundPort && port !== 0) {
16+
throw new SilentError(
17+
`Port ${port} is already in use. Use '--port' to specify a different port.`
18+
);
19+
}
20+
21+
// Otherwise, our found port is good.
22+
return foundPort;
23+
});
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
const cloneDeep = require('lodash/cloneDeep');
2+
3+
export function overrideOptions(original: any[], overrides: any[]) {
4+
let copy = cloneDeep(original);
5+
overrides.forEach(override => {
6+
const option = copy.find((opt: any) => opt.name == override.name);
7+
if (option) {
8+
Object.assign(option, override);
9+
}
10+
});
11+
return copy;
12+
}

tests/e2e/tests/test/e2e.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {
22
ng,
33
npm,
44
execAndWaitForOutputToMatch,
5+
silentExecAndWaitForOutputToMatch,
56
killAllProcesses
67
} from '../../utils/process';
78
import { updateJsonFile } from '../../utils/project';
@@ -40,6 +41,9 @@ export default function () {
4041
.then(() => killAllProcesses(), (err: any) => {
4142
killAllProcesses();
4243
throw err;
43-
});
44-
44+
})
45+
// Should run side-by-side with `ng serve`
46+
.then(() => silentExecAndWaitForOutputToMatch('ng', ['serve'],
47+
/webpack: Compiled successfully./))
48+
.then(() => ng('e2e'));
4549
}

0 commit comments

Comments
 (0)