diff --git a/packages/@angular/cli/lib/config/schema.json b/packages/@angular/cli/lib/config/schema.json index 529b4e0fb65d..2b7515cb56a7 100644 --- a/packages/@angular/cli/lib/config/schema.json +++ b/packages/@angular/cli/lib/config/schema.json @@ -32,7 +32,7 @@ "name": { "type": "string", "description": "Name of the app." - }, + }, "root": { "type": "string", "description": "The root directory of the app." @@ -270,6 +270,20 @@ } }, "additionalProperties": false + }, + "codeCoverage": { + "type": "object", + "properties": { + "exclude": { + "description": "Globs to exclude from code coverage.", + "type": "array", + "items": { + "type": "string" + }, + "default": [] + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -435,19 +449,19 @@ "description": "The host the application will be served on", "type": "string", "default": "localhost" - + }, "ssl": { "description": "Enables ssl for the application", "type": "boolean", "default": false - + }, "sslKey": { "description": "The ssl key used by the server", "type": "string", "default": "ssl/server.key" - + }, "sslCert": { "description": "The ssl certificate used by the server", diff --git a/packages/@angular/cli/models/webpack-configs/test.ts b/packages/@angular/cli/models/webpack-configs/test.ts index 54061f231a91..842393e0d0e5 100644 --- a/packages/@angular/cli/models/webpack-configs/test.ts +++ b/packages/@angular/cli/models/webpack-configs/test.ts @@ -1,4 +1,5 @@ import * as path from 'path'; +import * as glob from 'glob'; import * as webpack from 'webpack'; import { CliConfig } from '../config'; @@ -20,14 +21,27 @@ export function getTestConfig(testConfig: WebpackTestOptions) { const appConfig = CliConfig.fromProject().config.apps[0]; const extraRules: any[] = []; - if (testConfig.codeCoverage) { + if (testConfig.codeCoverage && CliConfig.fromProject()) { + const codeCoverageExclude = CliConfig.fromProject().get('test.codeCoverage.exclude'); + let exclude: (string | RegExp)[] = [ + /\.(e2e|spec)\.ts$/, + /node_modules/ + ]; + + if (codeCoverageExclude) { + codeCoverageExclude.forEach((excludeGlob: string) => { + const excludeFiles = glob + .sync(path.join(projectRoot, excludeGlob), { nodir: true }) + .map(file => path.normalize(file)); + exclude.push(...excludeFiles); + }); + } + + extraRules.push({ test: /\.(js|ts)$/, loader: 'istanbul-instrumenter-loader', enforce: 'post', - exclude: [ - /\.(e2e|spec)\.ts$/, - /node_modules/ - ] + exclude }); } diff --git a/tests/e2e/tests/misc/coverage.ts b/tests/e2e/tests/misc/coverage.ts index 077d51fe36a4..f7e008b55072 100644 --- a/tests/e2e/tests/misc/coverage.ts +++ b/tests/e2e/tests/misc/coverage.ts @@ -1,9 +1,26 @@ -import {expectFileToExist} from '../../utils/fs'; +import {expectFileToExist, expectFileToMatch} from '../../utils/fs'; +import {updateJsonFile} from '../../utils/project'; +import {expectToFail} from '../../utils/utils'; import {ng} from '../../utils/process'; -export default function() { +export default function () { return ng('test', '--single-run', '--code-coverage') .then(() => expectFileToExist('coverage/src/app')) - .then(() => expectFileToExist('coverage/lcov.info')); + .then(() => expectFileToExist('coverage/lcov.info')) + // Verify code coverage exclude work + .then(() => expectFileToMatch('coverage/lcov.info', 'polyfills.ts')) + .then(() => expectFileToMatch('coverage/lcov.info', 'test.ts')) + .then(() => updateJsonFile('.angular-cli.json', configJson => { + const test = configJson['test']; + test['codeCoverage'] = { + exclude: [ + 'src/polyfills.ts', + '**/test.ts' + ] + }; + })) + .then(() => ng('test', '--single-run', '--code-coverage')) + .then(() => expectToFail(() => expectFileToMatch('coverage/lcov.info', 'polyfills.ts'))) + .then(() => expectToFail(() => expectFileToMatch('coverage/lcov.info', 'test.ts'))); }