Skip to content

Commit 3bac7e2

Browse files
committed
refactor(@angular/build): extract headless configuration logic into helper function
This extracts the verbose headless property mutation logic from the monolithic setupBrowserConfiguration function into a standalone applyHeadlessConfiguration helper function to improve maintainability and readability.
1 parent 8dd341e commit 3bac7e2

File tree

2 files changed

+107
-32
lines changed

2 files changed

+107
-32
lines changed

packages/angular/build/src/builders/unit-test/runners/vitest/browser-provider.ts

Lines changed: 68 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@
77
*/
88

99
import { createRequire } from 'node:module';
10-
import type { BrowserBuiltinProvider, BrowserConfigOptions } from 'vitest/node';
10+
import type {
11+
BrowserBuiltinProvider,
12+
BrowserConfigOptions,
13+
BrowserProviderOption,
14+
} from 'vitest/node';
1115
import { assertIsError } from '../../../../utils/error';
1216

1317
export interface BrowserConfiguration {
@@ -40,7 +44,7 @@ function findBrowserProvider(
4044
export interface BrowserInstanceConfiguration {
4145
browser: string;
4246
headless: boolean;
43-
provider?: import('vitest/node').BrowserProviderOption;
47+
provider?: BrowserProviderOption;
4448
}
4549

4650
function normalizeBrowserName(browserName: string): BrowserInstanceConfiguration {
@@ -56,6 +60,67 @@ function normalizeBrowserName(browserName: string): BrowserInstanceConfiguration
5660
};
5761
}
5862

63+
/**
64+
* Mutates the provided browser instances to apply standard headless execution
65+
* constraints based on the chosen provider, user options, and CI environment presence.
66+
*
67+
* @param instances The normalized browser instances to mutate.
68+
* @param providerName The identifier for the chosen Vitest browser provider.
69+
* @param headless The user-provided headless configuration option.
70+
* @param isCI Whether the current environment is running in CI.
71+
* @returns An array of informational messages generated during evaluation.
72+
*/
73+
export function applyHeadlessConfiguration(
74+
instances: BrowserInstanceConfiguration[],
75+
providerName: BrowserBuiltinProvider | undefined,
76+
headless: boolean | undefined,
77+
isCI: boolean,
78+
): string[] {
79+
const messages: string[] = [];
80+
81+
if (providerName === 'preview') {
82+
instances.forEach((instance) => {
83+
// Preview mode only supports headed execution
84+
instance.headless = false;
85+
});
86+
87+
if (headless) {
88+
messages.push('The "headless" option is ignored when using the "preview" provider.');
89+
}
90+
} else if (headless !== undefined) {
91+
if (headless) {
92+
const allHeadlessByDefault = isCI || instances.every((i) => i.headless);
93+
if (allHeadlessByDefault) {
94+
messages.push(
95+
'The "headless" option is unnecessary as all browsers are already configured to run in headless mode.',
96+
);
97+
}
98+
}
99+
100+
instances.forEach((instance) => {
101+
instance.headless = headless;
102+
});
103+
} else if (isCI) {
104+
instances.forEach((instance) => {
105+
instance.headless = true;
106+
});
107+
}
108+
109+
return messages;
110+
}
111+
112+
/**
113+
* Resolves and configures the Vitest browser provider for the unit test builder.
114+
* Dynamically discovers and imports the necessary provider (Playwright, WebdriverIO, or Preview),
115+
* maps the requested browser instances, and applies environment-specific execution logic.
116+
*
117+
* @param browsers An array of requested browser names (e.g., 'chrome', 'firefox').
118+
* @param headless User-provided configuration for headless execution.
119+
* @param debug Whether the builder is running in watch or debug mode.
120+
* @param projectSourceRoot The root directory of the project being tested for resolving installed packages.
121+
* @param viewport Optional viewport dimensions to apply to the launched browser instances.
122+
* @returns A fully resolved Vitest browser configuration object alongside any generated warning or error messages.
123+
*/
59124
export async function setupBrowserConfiguration(
60125
browsers: string[] | undefined,
61126
headless: boolean | undefined,
@@ -149,35 +214,7 @@ export async function setupBrowserConfiguration(
149214
}
150215

151216
const isCI = !!process.env['CI'];
152-
const messages: string[] = [];
153-
154-
if (providerName === 'preview') {
155-
instances.forEach((instance) => {
156-
// Preview mode only supports headed execution
157-
instance.headless = false;
158-
});
159-
160-
if (headless) {
161-
messages.push('The "headless" option is ignored when using the "preview" provider.');
162-
}
163-
} else if (headless !== undefined) {
164-
if (headless) {
165-
const allHeadlessByDefault = isCI || instances.every((i) => i.headless);
166-
if (allHeadlessByDefault) {
167-
messages.push(
168-
'The "headless" option is unnecessary as all browsers are already configured to run in headless mode.',
169-
);
170-
}
171-
}
172-
173-
instances.forEach((instance) => {
174-
instance.headless = headless;
175-
});
176-
} else if (isCI) {
177-
instances.forEach((instance) => {
178-
instance.headless = true;
179-
});
180-
}
217+
const messages = applyHeadlessConfiguration(instances, providerName, headless, isCI);
181218

182219
const browser = {
183220
enabled: true,

packages/angular/build/src/builders/unit-test/runners/vitest/browser-provider_spec.ts

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises';
1010
import { tmpdir } from 'node:os';
1111
import { join } from 'node:path';
12-
import { setupBrowserConfiguration } from './browser-provider';
12+
import { applyHeadlessConfiguration, setupBrowserConfiguration } from './browser-provider';
1313

1414
describe('setupBrowserConfiguration', () => {
1515
let workspaceRoot: string;
@@ -279,4 +279,42 @@ describe('setupBrowserConfiguration', () => {
279279
expect(browser?.instances?.[1]?.provider).toBeUndefined();
280280
});
281281
});
282+
283+
describe('applyHeadlessConfiguration', () => {
284+
it('should set headless false and issue warning when using preview provider with headless true', () => {
285+
const instances = [{ browser: 'chrome', headless: true }];
286+
const messages = applyHeadlessConfiguration(instances, 'preview', true, false);
287+
288+
expect(instances[0].headless).toBeFalse();
289+
expect(messages).toEqual([
290+
'The "headless" option is ignored when using the "preview" provider.',
291+
]);
292+
});
293+
294+
it('should force headless mode when headless option is true', () => {
295+
const instances = [{ browser: 'chrome', headless: false }];
296+
const messages = applyHeadlessConfiguration(instances, 'playwright', true, false);
297+
298+
expect(instances[0].headless).toBeTrue();
299+
expect(messages).toEqual([]);
300+
});
301+
302+
it('should return information message when headless option is redundant', () => {
303+
const instances = [{ browser: 'chrome', headless: true }];
304+
const messages = applyHeadlessConfiguration(instances, 'playwright', true, false);
305+
306+
expect(instances[0].headless).toBeTrue();
307+
expect(messages).toEqual([
308+
'The "headless" option is unnecessary as all browsers are already configured to run in headless mode.',
309+
]);
310+
});
311+
312+
it('should force headless mode in CI environment when headless is undefined', () => {
313+
const instances = [{ browser: 'chrome', headless: false }];
314+
const messages = applyHeadlessConfiguration(instances, 'playwright', undefined, true);
315+
316+
expect(instances[0].headless).toBeTrue();
317+
expect(messages).toEqual([]);
318+
});
319+
});
282320
});

0 commit comments

Comments
 (0)