Skip to content

Commit d60d49e

Browse files
committed
feat(@angular/build): add quiet option to suppress build noise in unit tests
The unit-test builder currently triggers a rebuild of the application on each test run in watch mode. This causes the application builder to print a full list of built files on every rebuild, which can be quite noisy and not useful during test development. This change introduces a `quiet` option to the `@angular/build:unit-test` builder. When enabled, it suppresses the build summary and stats table from the application builder. The option defaults to `true` when running locally to provide a cleaner development experience, and defaults to `false` when running in a CI environment to ensure detailed logs are available for troubleshooting.
1 parent a4f11c1 commit d60d49e

File tree

7 files changed

+101
-1
lines changed

7 files changed

+101
-1
lines changed

goldens/public-api/angular/build/index.api.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,7 @@ export type UnitTestBuilderOptions = {
237237
outputFile?: string;
238238
progress?: boolean;
239239
providersFile?: string;
240+
quiet?: boolean;
240241
reporters?: SchemaReporter[];
241242
runner?: Runner;
242243
runnerConfig?: RunnerConfig;

packages/angular/build/src/builders/application/execute-build.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ export async function executeBuild(
331331
);
332332
}
333333

334-
if (!jsonLogs) {
334+
if (!jsonLogs && !options.quiet) {
335335
const changedFiles =
336336
rebuildState && executionResult.findChangedFiles(rebuildState.previousOutputInfo);
337337
executionResult.addLog(

packages/angular/build/src/builders/application/options.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,11 @@ interface InternalOptions {
120120
* Used exclusively for tests and shouldn't be used for other kinds of builds.
121121
*/
122122
instrumentForCoverage?: (filename: string) => boolean;
123+
124+
/**
125+
* Suppress build summary and stats table.
126+
*/
127+
quiet?: boolean;
123128
}
124129

125130
/** Full set of options for `application` builder. */
@@ -502,6 +507,7 @@ export async function normalizeOptions(
502507
plugins: extensions?.codePlugins?.length ? extensions?.codePlugins : undefined,
503508
loaderExtensions,
504509
jsonLogs: useJSONBuildLogs,
510+
quiet: options.quiet,
505511
colors: supportColor(),
506512
clearScreen,
507513
define,

packages/angular/build/src/builders/unit-test/builder.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,7 @@ export async function* execute(
325325
...runnerBuildOptions,
326326
watch: normalizedOptions.watch,
327327
progress: normalizedOptions.buildProgress ?? buildTargetOptions.progress,
328+
quiet: normalizedOptions.quiet,
328329
...(normalizedOptions.tsConfig ? { tsConfig: normalizedOptions.tsConfig } : {}),
329330
} satisfies ApplicationBuilderInternalOptions;
330331

packages/angular/build/src/builders/unit-test/options.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ export async function normalizeOptions(
121121
watch,
122122
debug: options.debug ?? false,
123123
ui: process.env['CI'] ? false : ui,
124+
quiet: options.quiet ?? (process.env['CI'] ? false : true),
124125
providersFile: options.providersFile && path.join(workspaceRoot, options.providersFile),
125126
setupFiles: options.setupFiles
126127
? options.setupFiles.map((setupFile) => path.join(workspaceRoot, setupFile))

packages/angular/build/src/builders/unit-test/schema.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@
7373
"type": "boolean",
7474
"description": "Enables the Vitest UI for interactive test execution. This option is only available for the Vitest runner."
7575
},
76+
"quiet": {
77+
"type": "boolean",
78+
"description": "Suppresses the verbose build summary and stats table on each rebuild. Defaults to `true` locally and `false` in CI environments."
79+
},
7680
"coverage": {
7781
"type": "boolean",
7882
"description": "Enables coverage reporting for tests. If not specified, the coverage configuration from a runner configuration file will be used if present. Otherwise, coverage is disabled by default."
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
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+
9+
import { execute } from '../../index';
10+
import {
11+
BASE_OPTIONS,
12+
describeBuilder,
13+
UNIT_TEST_BUILDER_INFO,
14+
setupApplicationTarget,
15+
expectLog,
16+
expectNoLog,
17+
} from '../setup';
18+
19+
describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => {
20+
describe('Option: "quiet"', () => {
21+
let originalCI: string | undefined;
22+
23+
beforeEach(async () => {
24+
setupApplicationTarget(harness);
25+
originalCI = process.env['CI'];
26+
});
27+
28+
afterEach(() => {
29+
if (originalCI !== undefined) {
30+
process.env['CI'] = originalCI;
31+
} else {
32+
delete process.env['CI'];
33+
}
34+
});
35+
36+
it('should default to true (quiet) when CI is not set', async () => {
37+
delete process.env['CI'];
38+
39+
harness.useTarget('test', {
40+
...BASE_OPTIONS,
41+
});
42+
43+
const { result, logs } = await harness.executeOnce();
44+
expect(result?.success).toBeTrue();
45+
// Should not contain the stats table headers
46+
expectNoLog(logs, /Estimated transfer size/);
47+
});
48+
49+
it('should default to false (verbose) when CI is set', async () => {
50+
process.env['CI'] = 'true';
51+
52+
harness.useTarget('test', {
53+
...BASE_OPTIONS,
54+
});
55+
56+
const { result, logs } = await harness.executeOnce();
57+
expect(result?.success).toBeTrue();
58+
// Should contain the stats table headers or file listing
59+
expectLog(logs, /Application bundle generation complete/);
60+
});
61+
62+
it('should respect quiet: true explicitly', async () => {
63+
process.env['CI'] = 'false'; // Ensure CI doesn't interfere if it defaults to false
64+
65+
harness.useTarget('test', {
66+
...BASE_OPTIONS,
67+
quiet: true,
68+
});
69+
70+
const { result, logs } = await harness.executeOnce();
71+
expect(result?.success).toBeTrue();
72+
expectNoLog(logs, /Estimated transfer size/);
73+
});
74+
75+
it('should respect quiet: false explicitly', async () => {
76+
harness.useTarget('test', {
77+
...BASE_OPTIONS,
78+
quiet: false,
79+
});
80+
81+
const { result, logs } = await harness.executeOnce();
82+
expect(result?.success).toBeTrue();
83+
// On initial build, it should print the file list
84+
expectLog(logs, /Initial/);
85+
});
86+
});
87+
});

0 commit comments

Comments
 (0)