Skip to content

Commit 3ef98a4

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 3ef98a4

File tree

2 files changed

+100
-31
lines changed

2 files changed

+100
-31
lines changed

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

Lines changed: 62 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,67 @@ function normalizeBrowserName(browserName: string): BrowserInstanceConfiguration
5656
};
5757
}
5858

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

151212
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-
}
213+
const messages = applyHeadlessConfiguration(instances, providerName, headless, isCI);
181214

182215
const browser = {
183216
enabled: true,

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

Lines changed: 38 additions & 2 deletions
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,40 @@ describe('setupBrowserConfiguration', () => {
279279
expect(browser?.instances?.[1]?.provider).toBeUndefined();
280280
});
281281
});
282-
});
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(['The "headless" option is ignored when using the "preview" provider.']);
290+
});
291+
292+
it('should force headless mode when headless option is true', () => {
293+
const instances = [{ browser: 'chrome', headless: false }];
294+
const messages = applyHeadlessConfiguration(instances, 'playwright', true, false);
295+
296+
expect(instances[0].headless).toBeTrue();
297+
expect(messages).toEqual([]);
298+
});
299+
300+
it('should return information message when headless option is redundant', () => {
301+
const instances = [{ browser: 'chrome', headless: true }];
302+
const messages = applyHeadlessConfiguration(instances, 'playwright', true, false);
303+
304+
expect(instances[0].headless).toBeTrue();
305+
expect(messages).toEqual([
306+
'The "headless" option is unnecessary as all browsers are already configured to run in headless mode.',
307+
]);
308+
});
309+
310+
it('should force headless mode in CI environment when headless is undefined', () => {
311+
const instances = [{ browser: 'chrome', headless: false }];
312+
const messages = applyHeadlessConfiguration(instances, 'playwright', undefined, true);
313+
314+
expect(instances[0].headless).toBeTrue();
315+
expect(messages).toEqual([]);
316+
});
317+
});
318+
});

0 commit comments

Comments
 (0)