Skip to content

Commit 1cd0a08

Browse files
committed
feat(@angular/cli): improve ng test performance
This PR uses a new Karma plugin to enable vendor bundles in unit tests, increasing rebuild performance. On a medium size project rebuilds times were 15x smaller (16.5s to 0.9s). Fix #5423
1 parent 5905e9a commit 1cd0a08

File tree

10 files changed

+235
-76
lines changed

10 files changed

+235
-76
lines changed

package.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,6 @@
6363
"isbinaryfile": "^3.0.0",
6464
"istanbul-instrumenter-loader": "^2.0.0",
6565
"json-loader": "^0.5.4",
66-
"karma-sourcemap-loader": "^0.3.7",
67-
"karma-webpack": "^2.0.0",
6866
"less": "^2.7.2",
6967
"less-loader": "^4.0.2",
7068
"loader-utils": "^1.0.2",
@@ -96,6 +94,7 @@
9694
"url-loader": "^0.5.7",
9795
"walk-sync": "^0.3.1",
9896
"webpack": "~2.4.0",
97+
"webpack-dev-middleware": "^1.10.2",
9998
"webpack-dev-server": "~2.4.2",
10099
"webpack-merge": "^2.4.0",
101100
"zone.js": "^0.8.4"

packages/@angular/cli/blueprints/ng/files/karma.conf.js

+1-12
Original file line numberDiff line numberDiff line change
@@ -15,25 +15,14 @@ module.exports = function (config) {
1515
client:{
1616
clearContext: false // leave Jasmine Spec Runner output visible in browser
1717
},
18-
files: [
19-
{ pattern: './<%= sourceDir %>/test.ts', watched: false }
20-
],
21-
preprocessors: {
22-
'./<%= sourceDir %>/test.ts': ['@angular/cli']
23-
},
24-
mime: {
25-
'text/x-typescript': ['ts','tsx']
26-
},
2718
coverageIstanbulReporter: {
2819
reports: [ 'html', 'lcovonly' ],
2920
fixWebpackSourcePaths: true
3021
},
3122
angularCli: {
3223
environment: 'dev'
3324
},
34-
reporters: config.angularCli && config.angularCli.codeCoverage
35-
? ['progress', 'coverage-istanbul']
36-
: ['progress', 'kjhtml'],
25+
reporters: ['progress', 'kjhtml'],
3726
port: 9876,
3827
colors: true,
3928
logLevel: config.LOG_INFO,

packages/@angular/cli/models/webpack-configs/test.ts

+13-8
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import * as webpack from 'webpack';
44

55
import { CliConfig } from '../config';
66
import { WebpackTestOptions } from '../webpack-test-config';
7-
import { KarmaWebpackEmitlessError } from '../../plugins/karma-webpack-emitless-error';
7+
88

99
/**
1010
* Enumerate loaders and their dependencies from this file to let the dependency validator
@@ -20,7 +20,9 @@ export function getTestConfig(testConfig: WebpackTestOptions) {
2020
const configPath = CliConfig.configFilePath();
2121
const projectRoot = path.dirname(configPath);
2222
const appConfig = CliConfig.fromProject().config.apps[0];
23+
const nodeModules = path.resolve(projectRoot, 'node_modules');
2324
const extraRules: any[] = [];
25+
const extraPlugins: any[] = [];
2426

2527
if (testConfig.codeCoverage && CliConfig.fromProject()) {
2628
const codeCoverageExclude = CliConfig.fromProject().get('test.codeCoverage.exclude');
@@ -38,7 +40,6 @@ export function getTestConfig(testConfig: WebpackTestOptions) {
3840
});
3941
}
4042

41-
4243
extraRules.push({
4344
test: /\.(js|ts)$/, loader: 'istanbul-instrumenter-loader',
4445
enforce: 'post',
@@ -49,17 +50,21 @@ export function getTestConfig(testConfig: WebpackTestOptions) {
4950
return {
5051
devtool: testConfig.sourcemaps ? 'inline-source-map' : 'eval',
5152
entry: {
52-
test: path.resolve(projectRoot, appConfig.root, appConfig.test)
53+
main: path.resolve(projectRoot, appConfig.root, appConfig.test)
5354
},
5455
module: {
5556
rules: [].concat(extraRules)
5657
},
5758
plugins: [
58-
new webpack.SourceMapDevToolPlugin({
59-
filename: null, // if no value is provided the sourcemap is inlined
60-
test: /\.(ts|js)($|\?)/i // process .js and .ts files only
59+
new webpack.optimize.CommonsChunkPlugin({
60+
minChunks: Infinity,
61+
name: 'inline'
6162
}),
62-
new KarmaWebpackEmitlessError()
63-
]
63+
new webpack.optimize.CommonsChunkPlugin({
64+
name: 'vendor',
65+
chunks: ['main'],
66+
minChunks: (module: any) => module.resource && module.resource.startsWith(nodeModules)
67+
})
68+
].concat(extraPlugins)
6469
};
6570
}

packages/@angular/cli/models/webpack-test-config.ts

-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import * as webpack from 'webpack';
21
const webpackMerge = require('webpack-merge');
32

43
import { BuildOptions } from './build-options';
@@ -28,11 +27,6 @@ export class WebpackTestConfig extends NgCliWebpackConfig {
2827
];
2928

3029
this.config = webpackMerge(webpackConfigs);
31-
delete this.config.entry;
32-
33-
// Remove any instance of CommonsChunkPlugin, not needed with karma-webpack.
34-
this.config.plugins = this.config.plugins.filter((plugin: any) =>
35-
!(plugin instanceof webpack.optimize.CommonsChunkPlugin));
3630

3731
return this.config;
3832
}

packages/@angular/cli/package.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,6 @@
5050
"inquirer": "^3.0.0",
5151
"isbinaryfile": "^3.0.0",
5252
"json-loader": "^0.5.4",
53-
"karma-sourcemap-loader": "^0.3.7",
54-
"karma-webpack": "^2.0.0",
5553
"less": "^2.7.2",
5654
"less-loader": "^4.0.2",
5755
"lodash": "^4.11.1",
@@ -81,6 +79,7 @@
8179
"url-loader": "^0.5.7",
8280
"walk-sync": "^0.3.1",
8381
"webpack": "~2.4.0",
82+
"webpack-dev-middleware": "^1.10.2",
8483
"webpack-dev-server": "~2.4.2",
8584
"webpack-merge": "^2.4.0",
8685
"zone.js": "^0.8.4"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<!DOCTYPE html>
2+
<!--
3+
This is the execution context.
4+
Loaded within the iframe.
5+
Reloaded before every execution run.
6+
-->
7+
<html>
8+
9+
<head>
10+
<title></title>
11+
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
12+
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
13+
</head>
14+
15+
<body>
16+
<!-- The scripts need to be in the body DOM element, as some test running frameworks need the body
17+
to have already been created so they can insert their magic into it. For example, if loaded
18+
before body, Angular Scenario test framework fails to find the body and crashes and burns in
19+
an epic manner. -->
20+
<script src="context.js"></script>
21+
<script type="text/javascript">
22+
// Configure our Karma and set up bindings
23+
%CLIENT_CONFIG%
24+
window.__karma__.setupContext(window);
25+
26+
// All served files with the latest timestamps
27+
%MAPPINGS%
28+
</script>
29+
<script type="text/javascript" src="_karma_webpack_/inline.bundle.js" crossorigin="anonymous"></script>
30+
<script type="text/javascript" src="_karma_webpack_/polyfills.bundle.js" crossorigin="anonymous"></script>
31+
<!-- Dynamically replaced with <script> tags -->
32+
%SCRIPTS%
33+
<script type="text/javascript" src="_karma_webpack_/scripts.bundle.js" crossorigin="anonymous"></script>
34+
<script type="text/javascript" src="_karma_webpack_/vendor.bundle.js" crossorigin="anonymous"></script>
35+
<script type="text/javascript" src="_karma_webpack_/main.bundle.js" crossorigin="anonymous"></script>
36+
<script type="text/javascript">
37+
window.__karma__.loaded();
38+
</script>
39+
</body>
40+
41+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<!doctype html>
2+
<!--
3+
This file is almost the same as context.html - loads all source files,
4+
but its purpose is to be loaded in the main frame (not within an iframe),
5+
just for immediate execution, without reporting to Karma server.
6+
-->
7+
<html>
8+
9+
<head>
10+
%X_UA_COMPATIBLE%
11+
<title>Karma DEBUG RUNNER</title>
12+
<link href="favicon.ico" rel="icon" type="image/x-icon" />
13+
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
14+
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
15+
</head>
16+
17+
<body>
18+
<!-- The scripts need to be at the end of body, so that some test running frameworks
19+
(Angular Scenario, for example) need the body to be loaded so that it can insert its magic
20+
into it. If it is before body, then it fails to find the body and crashes and burns in an epic
21+
manner. -->
22+
<script src="context.js"></script>
23+
<script src="debug.js"></script>
24+
<script type="text/javascript">
25+
// Configure our Karma
26+
%CLIENT_CONFIG%
27+
28+
// All served files with the latest timestamps
29+
%MAPPINGS%
30+
</script>
31+
<script type="text/javascript" src="_karma_webpack_/inline.bundle.js" crossorigin="anonymous"></script>
32+
<script type="text/javascript" src="_karma_webpack_/polyfills.bundle.js" crossorigin="anonymous"></script>
33+
<!-- Dynamically replaced with <script> tags -->
34+
%SCRIPTS%
35+
<script type="text/javascript" src="_karma_webpack_/scripts.bundle.js" crossorigin="anonymous"></script>
36+
<script type="text/javascript" src="_karma_webpack_/vendor.bundle.js" crossorigin="anonymous"></script>
37+
<script type="text/javascript" src="_karma_webpack_/main.bundle.js" crossorigin="anonymous"></script>
38+
<script type="text/javascript">
39+
window.__karma__.loaded();
40+
</script>
41+
</body>
42+
43+
</html>

packages/@angular/cli/plugins/karma-webpack-emitless-error.ts

-20
This file was deleted.

0 commit comments

Comments
 (0)