Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions goldens/public-api/angular/build/index.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ export type UnitTestBuilderOptions = {
dumpVirtualFiles?: boolean;
exclude?: string[];
filter?: string;
headless?: boolean;
include?: string[];
listTests?: boolean;
outputFile?: string;
Expand Down
1 change: 1 addition & 0 deletions packages/angular/build/src/builders/unit-test/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ export async function normalizeOptions(
exclude: options.exclude,
filter,
runnerName: runner ?? Runner.Vitest,
headless: options.headless,
coverage: {
enabled: options.coverage,
exclude: options.coverageExclude,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ export class KarmaExecutor implements TestExecutor {
);
}

if (unitTestOptions.headless !== undefined) {
context.logger.warn(
'The "karma" test runner does not support the "headless" option. The option will be ignored.',
);
}

const buildTargetOptions = (await context.validateOptions(
await context.getTargetOptions(unitTestOptions.buildTarget),
await context.getBuilderNameForTarget(unitTestOptions.buildTarget),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { assertIsError } from '../../../../utils/error';
export interface BrowserConfiguration {
browser?: BrowserConfigOptions;
errors?: string[];
messages?: string[];
}

function findBrowserProvider(
Expand Down Expand Up @@ -51,11 +52,18 @@ function normalizeBrowserName(browserName: string): { browser: string; headless:

export async function setupBrowserConfiguration(
browsers: string[] | undefined,
headless: boolean | undefined,
debug: boolean,
projectSourceRoot: string,
viewport: { width: number; height: number } | undefined,
): Promise<BrowserConfiguration> {
if (browsers === undefined) {
if (headless !== undefined) {
return {
messages: ['The "headless" option is ignored when no browsers are configured.'],
};
}

return {};
}

Expand Down Expand Up @@ -125,10 +133,30 @@ export async function setupBrowserConfiguration(

const isCI = !!process.env['CI'];
const instances = browsers.map(normalizeBrowserName);
const messages: string[] = [];

if (providerName === 'preview') {
instances.forEach((instance) => {
// Preview mode only supports headed execution
instance.headless = false;
});

if (headless) {
messages.push('The "headless" option is ignored when using the "preview" provider.');
}
} else if (headless !== undefined) {
if (headless) {
const allHeadlessByDefault = isCI || instances.every((i) => i.headless);
if (allHeadlessByDefault) {
messages.push(
'The "headless" option is unnecessary as all browsers are already configured to run in headless mode.',
);
}
}

instances.forEach((instance) => {
instance.headless = headless;
});
} else if (isCI) {
instances.forEach((instance) => {
instance.headless = true;
Expand All @@ -143,5 +171,5 @@ export async function setupBrowserConfiguration(
instances,
} satisfies BrowserConfigOptions;

return { browser };
return { browser, messages };
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ describe('setupBrowserConfiguration', () => {
it('should configure headless mode for specific browsers based on name', async () => {
const { browser } = await setupBrowserConfiguration(
['ChromeHeadless', 'Firefox'],
undefined,
false,
workspaceRoot,
undefined,
Expand All @@ -58,6 +59,7 @@ describe('setupBrowserConfiguration', () => {
try {
const { browser } = await setupBrowserConfiguration(
['Chrome', 'FirefoxHeadless'],
undefined,
false,
workspaceRoot,
undefined,
Expand Down Expand Up @@ -85,6 +87,7 @@ describe('setupBrowserConfiguration', () => {
// Case 1: All headless -> UI false
let result = await setupBrowserConfiguration(
['ChromeHeadless'],
undefined,
false,
workspaceRoot,
undefined,
Expand All @@ -94,6 +97,7 @@ describe('setupBrowserConfiguration', () => {
// Case 2: Mixed -> UI true
result = await setupBrowserConfiguration(
['ChromeHeadless', 'Firefox'],
undefined,
false,
workspaceRoot,
undefined,
Expand All @@ -113,6 +117,7 @@ describe('setupBrowserConfiguration', () => {
try {
const { browser } = await setupBrowserConfiguration(
['Chrome'],
undefined,
false,
workspaceRoot,
undefined,
Expand Down Expand Up @@ -151,6 +156,7 @@ describe('setupBrowserConfiguration', () => {

const { browser } = await setupBrowserConfiguration(
['ChromeHeadless'],
undefined,
false,
workspaceRoot,
undefined,
Expand All @@ -160,4 +166,34 @@ describe('setupBrowserConfiguration', () => {
// Preview forces headless false
expect(browser?.instances?.[0].headless).toBeFalse();
});

it('should force headless mode when headless option is true', async () => {
const { browser, messages } = await setupBrowserConfiguration(
['Chrome', 'Firefox'],
true,
false,
workspaceRoot,
undefined,
);

expect(browser?.instances).toEqual([
{ browser: 'chrome', headless: true },
{ browser: 'firefox', headless: true },
]);
expect(messages).toEqual([]);
});

it('should return information message when headless option is redundant', async () => {
const { messages } = await setupBrowserConfiguration(
['ChromeHeadless'],
true,
false,
workspaceRoot,
undefined,
);

expect(messages).toEqual([
'The "headless" option is unnecessary as all browsers are already configured to run in headless mode.',
]);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ export class VitestExecutor implements TestExecutor {
// Setup vitest browser options if configured
const browserOptions = await setupBrowserConfiguration(
browsers,
this.options.headless,
debug,
projectSourceRoot,
browserViewport,
Expand All @@ -200,6 +201,12 @@ export class VitestExecutor implements TestExecutor {
throw new Error(browserOptions.errors.join('\n'));
}

if (browserOptions.messages?.length) {
for (const message of browserOptions.messages) {
this.logger.info(message);
}
}

assert(
this.buildResultFiles.size > 0,
'buildResult must be available before initializing vitest',
Expand Down
4 changes: 4 additions & 0 deletions packages/angular/build/src/builders/unit-test/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@
"type": "boolean",
"description": "Enables watch mode, which re-runs tests when source files change. Defaults to `true` in TTY environments and `false` otherwise."
},
"headless": {
"type": "boolean",
"description": "Forces all configured browsers to run in headless mode. When using the Vitest runner, this option is ignored if no browsers are configured. The Karma runner does not support this option."
},
"debug": {
"type": "boolean",
"description": "Enables debugging mode for tests, allowing the use of the Node Inspector.",
Expand Down