diff --git a/goldens/public-api/angular_devkit/build_angular/index.md b/goldens/public-api/angular_devkit/build_angular/index.md index 2a53da0543bd..032ae5741772 100644 --- a/goldens/public-api/angular_devkit/build_angular/index.md +++ b/goldens/public-api/angular_devkit/build_angular/index.md @@ -176,6 +176,7 @@ export interface KarmaBuilderOptions { browsers?: string; codeCoverage?: boolean; codeCoverageExclude?: string[]; + exclude?: string[]; fileReplacements?: FileReplacement_2[]; include?: string[]; inlineStyleLanguage?: InlineStyleLanguage_2; diff --git a/packages/angular_devkit/build_angular/src/builders/karma/find-tests-plugin.ts b/packages/angular_devkit/build_angular/src/builders/karma/find-tests-plugin.ts index 6fc935393ed8..2710d92a507f 100644 --- a/packages/angular_devkit/build_angular/src/builders/karma/find-tests-plugin.ts +++ b/packages/angular_devkit/build_angular/src/builders/karma/find-tests-plugin.ts @@ -23,6 +23,7 @@ const PLUGIN_NAME = 'angular-find-tests-plugin'; export interface FindTestsPluginOptions { include?: string[]; + exclude?: string[]; workspaceRoot: string; projectSourceRoot: string; } @@ -33,7 +34,12 @@ export class FindTestsPlugin { constructor(private options: FindTestsPluginOptions) {} apply(compiler: Compiler): void { - const { include = ['**/*.spec.ts'], projectSourceRoot, workspaceRoot } = this.options; + const { + include = ['**/*.spec.ts'], + exclude = [], + projectSourceRoot, + workspaceRoot, + } = this.options; const webpackOptions = compiler.options; const entry = typeof webpackOptions.entry === 'function' ? webpackOptions.entry() : webpackOptions.entry; @@ -42,7 +48,7 @@ export class FindTestsPlugin { // Add tests files are part of the entry-point. webpackOptions.entry = async () => { - const specFiles = await findTests(include, workspaceRoot, projectSourceRoot); + const specFiles = await findTests(include, exclude, workspaceRoot, projectSourceRoot); if (!specFiles.length) { assert(this.compilation, 'Compilation cannot be undefined.'); @@ -73,12 +79,13 @@ export class FindTestsPlugin { // go through all patterns and find unique list of files async function findTests( - patterns: string[], + include: string[], + exclude: string[], workspaceRoot: string, projectSourceRoot: string, ): Promise { - const matchingTestsPromises = patterns.map((pattern) => - findMatchingTests(pattern, workspaceRoot, projectSourceRoot), + const matchingTestsPromises = include.map((pattern) => + findMatchingTests(pattern, exclude, workspaceRoot, projectSourceRoot), ); const files = await Promise.all(matchingTestsPromises); @@ -90,6 +97,7 @@ const normalizePath = (path: string): string => path.replace(/\\/g, '/'); async function findMatchingTests( pattern: string, + ignore: string[], workspaceRoot: string, projectSourceRoot: string, ): Promise { @@ -132,7 +140,7 @@ async function findMatchingTests( root: projectSourceRoot, nomount: true, absolute: true, - ignore: ['**/node_modules/**'], + ignore: ['**/node_modules/**', ...ignore], }); } diff --git a/packages/angular_devkit/build_angular/src/builders/karma/index.ts b/packages/angular_devkit/build_angular/src/builders/karma/index.ts index 6f232944290b..cae73d5e1158 100644 --- a/packages/angular_devkit/build_angular/src/builders/karma/index.ts +++ b/packages/angular_devkit/build_angular/src/builders/karma/index.ts @@ -134,6 +134,7 @@ export function execute( webpackConfig.plugins.push( new FindTestsPlugin({ include: options.include, + exclude: options.exclude, workspaceRoot: context.workspaceRoot, projectSourceRoot: path.join(context.workspaceRoot, sourceRoot), }), diff --git a/packages/angular_devkit/build_angular/src/builders/karma/schema.json b/packages/angular_devkit/build_angular/src/builders/karma/schema.json index f9a081b5bdd5..7f9a5e699b8d 100644 --- a/packages/angular_devkit/build_angular/src/builders/karma/schema.json +++ b/packages/angular_devkit/build_angular/src/builders/karma/schema.json @@ -141,7 +141,15 @@ "type": "string" }, "default": ["**/*.spec.ts"], - "description": "Globs of files to include, relative to workspace or project root. \nThere are 2 special cases:\n - when a path to directory is provided, all spec files ending \".spec.@(ts|tsx)\" will be included\n - when a path to a file is provided, and a matching spec file exists it will be included instead." + "description": "Globs of files to include, relative to project root. \nThere are 2 special cases:\n - when a path to directory is provided, all spec files ending \".spec.@(ts|tsx)\" will be included\n - when a path to a file is provided, and a matching spec file exists it will be included instead." + }, + "exclude": { + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "description": "Globs of files to exclude, relative to the project root." }, "sourceMap": { "description": "Output source maps for scripts and styles. For more information, see https://angular.io/guide/workspace-config#source-map-configuration.", diff --git a/packages/angular_devkit/build_angular/src/builders/karma/tests/options/exclude_spec.ts b/packages/angular_devkit/build_angular/src/builders/karma/tests/options/exclude_spec.ts new file mode 100644 index 000000000000..8befe0ae8865 --- /dev/null +++ b/packages/angular_devkit/build_angular/src/builders/karma/tests/options/exclude_spec.ts @@ -0,0 +1,44 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import { execute } from '../../index'; +import { BASE_OPTIONS, KARMA_BUILDER_INFO, describeBuilder } from '../setup'; + +describeBuilder(execute, KARMA_BUILDER_INFO, (harness) => { + describe('Option: "exclude"', () => { + beforeEach(async () => { + await harness.writeFiles({ + 'src/app/error.spec.ts': ` + describe('Error spec', () => { + it('should error', () => { + expect(false).toBe(true); + }); + });`, + }); + }); + + it(`should not exclude any spec when exclude is not supplied`, async () => { + harness.useTarget('test', { + ...BASE_OPTIONS, + }); + + const { result } = await harness.executeOnce(); + expect(result?.success).toBeFalse(); + }); + + it(`should exclude spec that matches the 'exclude' pattern`, async () => { + harness.useTarget('test', { + ...BASE_OPTIONS, + exclude: ['**/error.spec.ts'], + }); + + const { result } = await harness.executeOnce(); + expect(result?.success).toBeTrue(); + }); + }); +});