@angular/build 21.0.0-next.3 → 21.0.0-next.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/package.json +11 -11
  2. package/src/builders/application/index.js +2 -1
  3. package/src/builders/karma/application_builder.d.ts +0 -2
  4. package/src/builders/karma/application_builder.js +44 -352
  5. package/src/builders/karma/assets-middleware.d.ts +26 -0
  6. package/src/builders/karma/assets-middleware.js +65 -0
  7. package/src/builders/karma/coverage.d.ts +9 -0
  8. package/src/builders/karma/coverage.js +31 -0
  9. package/src/builders/karma/find-tests.d.ts +1 -9
  10. package/src/builders/karma/find-tests.js +6 -106
  11. package/src/builders/karma/karma-config.d.ts +11 -0
  12. package/src/builders/karma/karma-config.js +79 -0
  13. package/src/builders/karma/polyfills-plugin.d.ts +13 -0
  14. package/src/builders/karma/polyfills-plugin.js +74 -0
  15. package/src/builders/karma/progress-reporter.d.ts +17 -0
  16. package/src/builders/karma/progress-reporter.js +73 -0
  17. package/src/builders/karma/utils.d.ts +17 -0
  18. package/src/builders/karma/utils.js +66 -0
  19. package/src/builders/unit-test/builder.js +150 -44
  20. package/src/builders/unit-test/options.d.ts +5 -1
  21. package/src/builders/unit-test/options.js +12 -5
  22. package/src/builders/unit-test/runners/api.d.ts +19 -1
  23. package/src/builders/unit-test/runners/dependency-checker.d.ts +43 -0
  24. package/src/builders/unit-test/runners/dependency-checker.js +82 -0
  25. package/src/builders/unit-test/runners/karma/executor.js +26 -2
  26. package/src/builders/unit-test/runners/karma/index.js +17 -0
  27. package/src/builders/unit-test/runners/vitest/browser-provider.d.ts +3 -2
  28. package/src/builders/unit-test/runners/vitest/build-options.js +6 -4
  29. package/src/builders/unit-test/runners/vitest/executor.d.ts +4 -5
  30. package/src/builders/unit-test/runners/vitest/executor.js +23 -135
  31. package/src/builders/unit-test/runners/vitest/index.js +19 -2
  32. package/src/builders/unit-test/runners/vitest/plugins.d.ts +23 -0
  33. package/src/builders/unit-test/runners/vitest/plugins.js +131 -0
  34. package/src/builders/unit-test/schema.d.ts +59 -30
  35. package/src/builders/unit-test/schema.js +1 -1
  36. package/src/builders/unit-test/schema.json +70 -16
  37. package/src/builders/unit-test/test-discovery.d.ts +25 -1
  38. package/src/builders/unit-test/test-discovery.js +194 -5
  39. package/src/tools/angular/transformers/jit-bootstrap-transformer.js +1 -1
  40. package/src/tools/angular/transformers/jit-resource-transformer.js +1 -1
  41. package/src/tools/esbuild/application-code-bundle.js +4 -7
  42. package/src/tools/esbuild/stylesheets/less-language.js +2 -26
  43. package/src/tools/esbuild/stylesheets/stylesheet-plugin-factory.js +2 -1
  44. package/src/tools/vite/middlewares/assets-middleware.d.ts +2 -0
  45. package/src/tools/vite/middlewares/assets-middleware.js +31 -0
  46. package/src/tools/vite/utils.js +0 -1
  47. package/src/utils/normalize-cache.js +1 -1
  48. package/src/utils/supported-browsers.js +7 -3
  49. package/src/utils/test-files.d.ts +17 -0
  50. package/src/utils/test-files.js +82 -0
  51. package/.browserslistrc +0 -7
@@ -12,14 +12,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
12
12
  Object.defineProperty(exports, "__esModule", { value: true });
13
13
  exports.VitestExecutor = void 0;
14
14
  const node_assert_1 = __importDefault(require("node:assert"));
15
- const promises_1 = require("node:fs/promises");
16
15
  const node_path_1 = __importDefault(require("node:path"));
17
16
  const error_1 = require("../../../../utils/error");
18
17
  const load_esm_1 = require("../../../../utils/load-esm");
19
18
  const path_1 = require("../../../../utils/path");
20
19
  const results_1 = require("../../../application/results");
21
- const test_discovery_1 = require("../../test-discovery");
22
20
  const browser_provider_1 = require("./browser-provider");
21
+ const plugins_1 = require("./plugins");
23
22
  class VitestExecutor {
24
23
  vitest;
25
24
  projectName;
@@ -31,9 +30,15 @@ class VitestExecutor {
31
30
  // Example: `Map<'/path/to/src/app.spec.ts', 'spec-src-app-spec'>`
32
31
  testFileToEntryPoint = new Map();
33
32
  entryPointToTestFile = new Map();
34
- constructor(projectName, options) {
33
+ constructor(projectName, options, testEntryPointMappings) {
35
34
  this.projectName = projectName;
36
35
  this.options = options;
36
+ if (testEntryPointMappings) {
37
+ for (const [entryPoint, testFile] of testEntryPointMappings) {
38
+ this.testFileToEntryPoint.set(testFile, entryPoint);
39
+ this.entryPointToTestFile.set(entryPoint + '.js', testFile);
40
+ }
41
+ }
37
42
  }
38
43
  async *execute(buildResult) {
39
44
  if (buildResult.kind === results_1.ResultKind.Full) {
@@ -50,24 +55,6 @@ class VitestExecutor {
50
55
  this.buildResultFiles.set(path, file);
51
56
  }
52
57
  }
53
- // The `getTestEntrypoints` function is used here to create the same mapping
54
- // that was used in `build-options.ts` to generate the build entry points.
55
- // This is a deliberate duplication to avoid a larger refactoring of the
56
- // builder's core interfaces to pass the entry points from the build setup
57
- // phase to the execution phase.
58
- if (this.testFileToEntryPoint.size === 0) {
59
- const { include, exclude = [], workspaceRoot, projectSourceRoot } = this.options;
60
- const testFiles = await (0, test_discovery_1.findTests)(include, exclude, workspaceRoot, projectSourceRoot);
61
- const entryPoints = (0, test_discovery_1.getTestEntrypoints)(testFiles, {
62
- projectSourceRoot,
63
- workspaceRoot,
64
- removeTestExtension: true,
65
- });
66
- for (const [entryPoint, testFile] of entryPoints) {
67
- this.testFileToEntryPoint.set(testFile, entryPoint);
68
- this.entryPointToTestFile.set(entryPoint + '.js', testFile);
69
- }
70
- }
71
58
  // Initialize Vitest if not already present.
72
59
  this.vitest ??= await this.initializeVitest();
73
60
  const vitest = this.vitest;
@@ -113,118 +100,8 @@ class VitestExecutor {
113
100
  }
114
101
  return testSetupFiles;
115
102
  }
116
- createVitestPlugins(testSetupFiles, browserOptions) {
117
- const { workspaceRoot } = this.options;
118
- return [
119
- {
120
- name: 'angular:project-init',
121
- // Type is incorrect. This allows a Promise<void>.
122
- // eslint-disable-next-line @typescript-eslint/no-misused-promises
123
- configureVitest: async (context) => {
124
- // Create a subproject that can be configured with plugins for browser mode.
125
- // Plugins defined directly in the vite overrides will not be present in the
126
- // browser specific Vite instance.
127
- await context.injectTestProjects({
128
- test: {
129
- name: this.projectName,
130
- root: workspaceRoot,
131
- globals: true,
132
- setupFiles: testSetupFiles,
133
- // Use `jsdom` if no browsers are explicitly configured.
134
- // `node` is effectively no "environment" and the default.
135
- environment: browserOptions.browser ? 'node' : 'jsdom',
136
- browser: browserOptions.browser,
137
- include: this.options.include,
138
- ...(this.options.exclude ? { exclude: this.options.exclude } : {}),
139
- },
140
- plugins: [
141
- {
142
- name: 'angular:test-in-memory-provider',
143
- enforce: 'pre',
144
- resolveId: (id, importer) => {
145
- if (importer && (id[0] === '.' || id[0] === '/')) {
146
- let fullPath;
147
- if (this.testFileToEntryPoint.has(importer)) {
148
- fullPath = (0, path_1.toPosixPath)(node_path_1.default.join(this.options.workspaceRoot, id));
149
- }
150
- else {
151
- fullPath = (0, path_1.toPosixPath)(node_path_1.default.join(node_path_1.default.dirname(importer), id));
152
- }
153
- const relativePath = node_path_1.default.relative(this.options.workspaceRoot, fullPath);
154
- if (this.buildResultFiles.has((0, path_1.toPosixPath)(relativePath))) {
155
- return fullPath;
156
- }
157
- }
158
- if (this.testFileToEntryPoint.has(id)) {
159
- return id;
160
- }
161
- (0, node_assert_1.default)(this.buildResultFiles.size > 0, 'buildResult must be available for resolving.');
162
- const relativePath = node_path_1.default.relative(this.options.workspaceRoot, id);
163
- if (this.buildResultFiles.has((0, path_1.toPosixPath)(relativePath))) {
164
- return id;
165
- }
166
- },
167
- load: async (id) => {
168
- (0, node_assert_1.default)(this.buildResultFiles.size > 0, 'buildResult must be available for in-memory loading.');
169
- // Attempt to load as a source test file.
170
- const entryPoint = this.testFileToEntryPoint.get(id);
171
- let outputPath;
172
- if (entryPoint) {
173
- outputPath = entryPoint + '.js';
174
- // To support coverage exclusion of the actual test file, the virtual
175
- // test entry point only references the built and bundled intermediate file.
176
- return {
177
- code: `import "./${outputPath}";`,
178
- };
179
- }
180
- else {
181
- // Attempt to load as a built artifact.
182
- const relativePath = node_path_1.default.relative(this.options.workspaceRoot, id);
183
- outputPath = (0, path_1.toPosixPath)(relativePath);
184
- }
185
- const outputFile = this.buildResultFiles.get(outputPath);
186
- if (outputFile) {
187
- const sourceMapPath = outputPath + '.map';
188
- const sourceMapFile = this.buildResultFiles.get(sourceMapPath);
189
- const code = outputFile.origin === 'memory'
190
- ? Buffer.from(outputFile.contents).toString('utf-8')
191
- : await (0, promises_1.readFile)(outputFile.inputPath, 'utf-8');
192
- const map = sourceMapFile
193
- ? sourceMapFile.origin === 'memory'
194
- ? Buffer.from(sourceMapFile.contents).toString('utf-8')
195
- : await (0, promises_1.readFile)(sourceMapFile.inputPath, 'utf-8')
196
- : undefined;
197
- return {
198
- code,
199
- map: map ? JSON.parse(map) : undefined,
200
- };
201
- }
202
- },
203
- },
204
- {
205
- name: 'angular:html-index',
206
- transformIndexHtml: () => {
207
- // Add all global stylesheets
208
- if (this.buildResultFiles.has('styles.css')) {
209
- return [
210
- {
211
- tag: 'link',
212
- attrs: { href: 'styles.css', rel: 'stylesheet' },
213
- injectTo: 'head',
214
- },
215
- ];
216
- }
217
- return [];
218
- },
219
- },
220
- ],
221
- });
222
- },
223
- },
224
- ];
225
- }
226
103
  async initializeVitest() {
227
- const { codeCoverage, reporters, workspaceRoot, browsers, debug, watch } = this.options;
104
+ const { codeCoverage, reporters, outputFile, workspaceRoot, browsers, debug, watch } = this.options;
228
105
  let vitestNodeModule;
229
106
  try {
230
107
  vitestNodeModule = await (0, load_esm_1.loadEsmModule)('vitest/node');
@@ -244,7 +121,15 @@ class VitestExecutor {
244
121
  }
245
122
  (0, node_assert_1.default)(this.buildResultFiles.size > 0, 'buildResult must be available before initializing vitest');
246
123
  const testSetupFiles = this.prepareSetupFiles();
247
- const plugins = this.createVitestPlugins(testSetupFiles, browserOptions);
124
+ const plugins = (0, plugins_1.createVitestPlugins)(this.options, testSetupFiles, browserOptions, {
125
+ workspaceRoot,
126
+ projectSourceRoot: this.options.projectSourceRoot,
127
+ projectName: this.projectName,
128
+ include: this.options.include,
129
+ exclude: this.options.exclude,
130
+ buildResultFiles: this.buildResultFiles,
131
+ testFileToEntryPoint: this.testFileToEntryPoint,
132
+ });
248
133
  const debugOptions = debug
249
134
  ? {
250
135
  inspectBrk: true,
@@ -259,9 +144,11 @@ class VitestExecutor {
259
144
  project: ['base', this.projectName],
260
145
  name: 'base',
261
146
  include: [],
147
+ testNamePattern: this.options.filter,
262
148
  reporters: reporters ?? ['default'],
149
+ outputFile,
263
150
  watch,
264
- coverage: generateCoverageOption(codeCoverage),
151
+ coverage: generateCoverageOption(codeCoverage, this.projectName),
265
152
  ...debugOptions,
266
153
  }, {
267
154
  server: {
@@ -274,7 +161,7 @@ class VitestExecutor {
274
161
  }
275
162
  }
276
163
  exports.VitestExecutor = VitestExecutor;
277
- function generateCoverageOption(codeCoverage) {
164
+ function generateCoverageOption(codeCoverage, projectName) {
278
165
  if (!codeCoverage) {
279
166
  return {
280
167
  enabled: false,
@@ -283,6 +170,7 @@ function generateCoverageOption(codeCoverage) {
283
170
  return {
284
171
  enabled: true,
285
172
  excludeAfterRemap: true,
173
+ reportsDirectory: (0, path_1.toPosixPath)(node_path_1.default.join('coverage', projectName)),
286
174
  // Special handling for `exclude`/`reporters` due to an undefined value causing upstream failures
287
175
  ...(codeCoverage.exclude ? { exclude: codeCoverage.exclude } : {}),
288
176
  ...(codeCoverage.reporters
@@ -11,6 +11,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
11
11
  };
12
12
  Object.defineProperty(exports, "__esModule", { value: true });
13
13
  const node_assert_1 = __importDefault(require("node:assert"));
14
+ const dependency_checker_1 = require("../dependency-checker");
14
15
  const build_options_1 = require("./build-options");
15
16
  const executor_1 = require("./executor");
16
17
  /**
@@ -18,13 +19,29 @@ const executor_1 = require("./executor");
18
19
  */
19
20
  const VitestTestRunner = {
20
21
  name: 'vitest',
22
+ validateDependencies(options) {
23
+ const checker = new dependency_checker_1.DependencyChecker(options.projectSourceRoot);
24
+ checker.check('vitest');
25
+ if (options.browsers?.length) {
26
+ checker.check('@vitest/browser');
27
+ checker.checkAny(['playwright', 'webdriverio'], 'The "browsers" option requires either "playwright" or "webdriverio" to be installed.');
28
+ }
29
+ else {
30
+ // JSDOM is used when no browsers are specified
31
+ checker.check('jsdom');
32
+ }
33
+ if (options.codeCoverage) {
34
+ checker.check('@vitest/coverage-v8');
35
+ }
36
+ checker.report();
37
+ },
21
38
  getBuildOptions(options, baseBuildOptions) {
22
39
  return (0, build_options_1.getVitestBuildOptions)(options, baseBuildOptions);
23
40
  },
24
- async createExecutor(context, options) {
41
+ async createExecutor(context, options, testEntryPointMappings) {
25
42
  const projectName = context.target?.project;
26
43
  (0, node_assert_1.default)(projectName, 'The builder requires a target.');
27
- return new executor_1.VitestExecutor(projectName, options);
44
+ return new executor_1.VitestExecutor(projectName, options, testEntryPointMappings);
28
45
  },
29
46
  };
30
47
  exports.default = VitestTestRunner;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * @license
3
+ * Copyright Google LLC All Rights Reserved.
4
+ *
5
+ * Use of this source code is governed by an MIT-style license that can be
6
+ * found in the LICENSE file at https://angular.dev/license
7
+ */
8
+ import type { VitestPlugin } from 'vitest/node';
9
+ import type { ResultFile } from '../../../application/results';
10
+ import type { NormalizedUnitTestBuilderOptions } from '../../options';
11
+ import type { BrowserConfiguration } from './browser-provider';
12
+ type VitestPlugins = Awaited<ReturnType<typeof VitestPlugin>>;
13
+ interface PluginOptions {
14
+ workspaceRoot: string;
15
+ projectSourceRoot: string;
16
+ projectName: string;
17
+ include?: string[];
18
+ exclude?: string[];
19
+ buildResultFiles: ReadonlyMap<string, ResultFile>;
20
+ testFileToEntryPoint: ReadonlyMap<string, string>;
21
+ }
22
+ export declare function createVitestPlugins(options: NormalizedUnitTestBuilderOptions, testSetupFiles: string[], browserOptions: BrowserConfiguration, pluginOptions: PluginOptions): VitestPlugins;
23
+ export {};
@@ -0,0 +1,131 @@
1
+ "use strict";
2
+ /**
3
+ * @license
4
+ * Copyright Google LLC All Rights Reserved.
5
+ *
6
+ * Use of this source code is governed by an MIT-style license that can be
7
+ * found in the LICENSE file at https://angular.dev/license
8
+ */
9
+ var __importDefault = (this && this.__importDefault) || function (mod) {
10
+ return (mod && mod.__esModule) ? mod : { "default": mod };
11
+ };
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.createVitestPlugins = createVitestPlugins;
14
+ const node_assert_1 = __importDefault(require("node:assert"));
15
+ const promises_1 = require("node:fs/promises");
16
+ const node_path_1 = __importDefault(require("node:path"));
17
+ const assets_middleware_1 = require("../../../../tools/vite/middlewares/assets-middleware");
18
+ const path_1 = require("../../../../utils/path");
19
+ function createVitestPlugins(options, testSetupFiles, browserOptions, pluginOptions) {
20
+ const { workspaceRoot, projectName, buildResultFiles, testFileToEntryPoint } = pluginOptions;
21
+ return [
22
+ {
23
+ name: 'angular:project-init',
24
+ // Type is incorrect. This allows a Promise<void>.
25
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
26
+ configureVitest: async (context) => {
27
+ // Create a subproject that can be configured with plugins for browser mode.
28
+ // Plugins defined directly in the vite overrides will not be present in the
29
+ // browser specific Vite instance.
30
+ await context.injectTestProjects({
31
+ test: {
32
+ name: projectName,
33
+ root: workspaceRoot,
34
+ globals: true,
35
+ setupFiles: testSetupFiles,
36
+ // Use `jsdom` if no browsers are explicitly configured.
37
+ // `node` is effectively no "environment" and the default.
38
+ environment: browserOptions.browser ? 'node' : 'jsdom',
39
+ browser: browserOptions.browser,
40
+ include: options.include,
41
+ ...(options.exclude ? { exclude: options.exclude } : {}),
42
+ },
43
+ plugins: [
44
+ {
45
+ name: 'angular:test-in-memory-provider',
46
+ enforce: 'pre',
47
+ resolveId: (id, importer) => {
48
+ if (importer && (id[0] === '.' || id[0] === '/')) {
49
+ let fullPath;
50
+ if (testFileToEntryPoint.has(importer)) {
51
+ fullPath = (0, path_1.toPosixPath)(node_path_1.default.join(workspaceRoot, id));
52
+ }
53
+ else {
54
+ fullPath = (0, path_1.toPosixPath)(node_path_1.default.join(node_path_1.default.dirname(importer), id));
55
+ }
56
+ const relativePath = node_path_1.default.relative(workspaceRoot, fullPath);
57
+ if (buildResultFiles.has((0, path_1.toPosixPath)(relativePath))) {
58
+ return fullPath;
59
+ }
60
+ }
61
+ if (testFileToEntryPoint.has(id)) {
62
+ return id;
63
+ }
64
+ (0, node_assert_1.default)(buildResultFiles.size > 0, 'buildResult must be available for resolving.');
65
+ const relativePath = node_path_1.default.relative(workspaceRoot, id);
66
+ if (buildResultFiles.has((0, path_1.toPosixPath)(relativePath))) {
67
+ return id;
68
+ }
69
+ },
70
+ load: async (id) => {
71
+ (0, node_assert_1.default)(buildResultFiles.size > 0, 'buildResult must be available for in-memory loading.');
72
+ // Attempt to load as a source test file.
73
+ const entryPoint = testFileToEntryPoint.get(id);
74
+ let outputPath;
75
+ if (entryPoint) {
76
+ outputPath = entryPoint + '.js';
77
+ // To support coverage exclusion of the actual test file, the virtual
78
+ // test entry point only references the built and bundled intermediate file.
79
+ return {
80
+ code: `import "./${outputPath}";`,
81
+ };
82
+ }
83
+ else {
84
+ // Attempt to load as a built artifact.
85
+ const relativePath = node_path_1.default.relative(workspaceRoot, id);
86
+ outputPath = (0, path_1.toPosixPath)(relativePath);
87
+ }
88
+ const outputFile = buildResultFiles.get(outputPath);
89
+ if (outputFile) {
90
+ const sourceMapPath = outputPath + '.map';
91
+ const sourceMapFile = buildResultFiles.get(sourceMapPath);
92
+ const code = outputFile.origin === 'memory'
93
+ ? Buffer.from(outputFile.contents).toString('utf-8')
94
+ : await (0, promises_1.readFile)(outputFile.inputPath, 'utf-8');
95
+ const map = sourceMapFile
96
+ ? sourceMapFile.origin === 'memory'
97
+ ? Buffer.from(sourceMapFile.contents).toString('utf-8')
98
+ : await (0, promises_1.readFile)(sourceMapFile.inputPath, 'utf-8')
99
+ : undefined;
100
+ return {
101
+ code,
102
+ map: map ? JSON.parse(map) : undefined,
103
+ };
104
+ }
105
+ },
106
+ configureServer: (server) => {
107
+ server.middlewares.use((0, assets_middleware_1.createBuildAssetsMiddleware)(server.config.base, buildResultFiles));
108
+ },
109
+ },
110
+ {
111
+ name: 'angular:html-index',
112
+ transformIndexHtml: () => {
113
+ // Add all global stylesheets
114
+ if (buildResultFiles.has('styles.css')) {
115
+ return [
116
+ {
117
+ tag: 'link',
118
+ attrs: { href: 'styles.css', rel: 'stylesheet' },
119
+ injectTo: 'head',
120
+ },
121
+ ];
122
+ }
123
+ return [];
124
+ },
125
+ },
126
+ ],
127
+ });
128
+ },
129
+ },
130
+ ];
131
+ }
@@ -3,76 +3,101 @@
3
3
  */
4
4
  export type Schema = {
5
5
  /**
6
- * A list of browsers to use for test execution. If undefined, jsdom on Node.js will be used
7
- * instead of a browser. For Vitest and Karma, browser names ending with 'Headless' (e.g.,
8
- * 'ChromeHeadless') will enable headless mode for that browser.
6
+ * Specifies the browsers to use for test execution. When not specified, tests are run in a
7
+ * Node.js environment using jsdom. For both Vitest and Karma, browser names ending with
8
+ * 'Headless' (e.g., 'ChromeHeadless') will enable headless mode.
9
9
  */
10
10
  browsers?: string[];
11
11
  /**
12
- * A build builder target to serve in the format of `project:target[:configuration]`. You
13
- * can also pass in more than one configuration name as a comma-separated list. Example:
14
- * `project:target:production,staging`.
12
+ * Specifies the build target to use for the unit test build in the format
13
+ * `project:target[:configuration]`. You can also pass a comma-separated list of
14
+ * configurations. Example: `project:target:production,staging`.
15
15
  */
16
16
  buildTarget: string;
17
17
  /**
18
- * Output a code coverage report.
18
+ * Enables code coverage reporting for tests.
19
19
  */
20
20
  codeCoverage?: boolean;
21
21
  /**
22
- * Globs to exclude from code coverage.
22
+ * Specifies glob patterns of files to exclude from the code coverage report.
23
23
  */
24
24
  codeCoverageExclude?: string[];
25
25
  /**
26
- * Reporters to use for code coverage results.
26
+ * Specifies the reporters to use for code coverage results. Each reporter can be a string
27
+ * representing its name, or a tuple containing the name and an options object. Built-in
28
+ * reporters include 'html', 'lcov', 'lcovonly', 'text', 'text-summary', 'cobertura',
29
+ * 'json', and 'json-summary'.
27
30
  */
28
31
  codeCoverageReporters?: SchemaCodeCoverageReporter[];
29
32
  /**
30
- * Initialize the test runner to support using the Node Inspector for test debugging.
33
+ * Enables debugging mode for tests, allowing the use of the Node Inspector.
31
34
  */
32
35
  debug?: boolean;
33
36
  /**
34
- * Globs of files to exclude, relative to the project root.
37
+ * Dumps build output files to the `.angular/cache` directory for debugging purposes.
38
+ */
39
+ dumpVirtualFiles?: boolean;
40
+ /**
41
+ * Specifies glob patterns of files to exclude from testing, relative to the project root.
35
42
  */
36
43
  exclude?: string[];
37
44
  /**
38
- * Globs of files to include, relative to project root.
39
- * There are 2 special cases:
40
- * - when a path to directory is provided, all spec files ending ".spec.@(ts|tsx)" will be
41
- * included
42
- * - when a path to a file is provided, and a matching spec file exists it will be included
43
- * instead.
45
+ * Specifies a regular expression pattern to match against test suite and test names. Only
46
+ * tests with a name matching the pattern will be executed. For example, `^App` will run
47
+ * only tests in suites beginning with 'App'.
48
+ */
49
+ filter?: string;
50
+ /**
51
+ * Specifies glob patterns of files to include for testing, relative to the project root.
52
+ * This option also has special handling for directory paths (includes all `.spec.ts` files
53
+ * within) and file paths (includes the corresponding `.spec` file if one exists).
44
54
  */
45
55
  include?: string[];
46
56
  /**
47
- * Log progress to the console while building. Defaults to the build target's progress value.
57
+ * Lists all discovered test files and exits the process without building or executing the
58
+ * tests.
59
+ */
60
+ listTests?: boolean;
61
+ /**
62
+ * Specifies a file path for the test report, applying only to the first reporter. To
63
+ * configure output files for multiple reporters, use the tuple format `['reporter-name', {
64
+ * outputFile: '...' }]` within the `reporters` option. When not provided, output is written
65
+ * to the console.
66
+ */
67
+ outputFile?: string;
68
+ /**
69
+ * Shows build progress information in the console. Defaults to the `progress` setting of
70
+ * the specified `buildTarget`.
48
71
  */
49
72
  progress?: boolean;
50
73
  /**
51
- * TypeScript file that exports an array of Angular providers to use during test execution.
52
- * The array must be a default export.
74
+ * Specifies the path to a TypeScript file that provides an array of Angular providers for
75
+ * the test environment. The file must contain a default export of the provider array.
53
76
  */
54
77
  providersFile?: string;
55
78
  /**
56
- * Test runner reporters to use. Directly passed to the test runner.
79
+ * Specifies the reporters to use during test execution. Each reporter can be a string
80
+ * representing its name, or a tuple containing the name and an options object. Built-in
81
+ * reporters include 'default', 'verbose', 'dots', 'json', 'junit', 'tap', 'tap-flat', and
82
+ * 'html'. You can also provide a path to a custom reporter.
57
83
  */
58
- reporters?: string[];
84
+ reporters?: SchemaReporter[];
59
85
  /**
60
- * The name of the test runner to use for test execution.
86
+ * Specifies the test runner to use for test execution.
61
87
  */
62
88
  runner: Runner;
63
89
  /**
64
- * A list of global setup and configuration files that are included before the test files.
65
- * The application's polyfills are always included before these files. The Angular Testbed
66
- * is also initialized prior to the execution of these files.
90
+ * A list of paths to global setup files that are executed before the test files. The
91
+ * application's polyfills and the Angular TestBed are always initialized before these files.
67
92
  */
68
93
  setupFiles?: string[];
69
94
  /**
70
- * The name of the TypeScript configuration file.
95
+ * The path to the TypeScript configuration file, relative to the workspace root.
71
96
  */
72
97
  tsConfig: string;
73
98
  /**
74
- * Re-run tests when source files change. Defaults to `true` in TTY environments and `false`
75
- * otherwise.
99
+ * Enables watch mode, which re-runs tests when source files change. Defaults to `true` in
100
+ * TTY environments and `false` otherwise.
76
101
  */
77
102
  watch?: boolean;
78
103
  };
@@ -90,8 +115,12 @@ export declare enum CoverageReporters {
90
115
  Text = "text",
91
116
  TextSummary = "text-summary"
92
117
  }
118
+ export type SchemaReporter = ReporterReporter[] | string;
119
+ export type ReporterReporter = {
120
+ [key: string]: any;
121
+ } | string;
93
122
  /**
94
- * The name of the test runner to use for test execution.
123
+ * Specifies the test runner to use for test execution.
95
124
  */
96
125
  export declare enum Runner {
97
126
  Karma = "karma",
@@ -15,7 +15,7 @@ var CoverageReporters;
15
15
  CoverageReporters["TextSummary"] = "text-summary";
16
16
  })(CoverageReporters || (exports.CoverageReporters = CoverageReporters = {}));
17
17
  /**
18
- * The name of the test runner to use for test execution.
18
+ * Specifies the test runner to use for test execution.
19
19
  */
20
20
  var Runner;
21
21
  (function (Runner) {