Comprehensive testing utilities for unit testing Angular builders, including test harness, file system mocking, and builder execution helpers.
The testing utilities provide a complete testing framework specifically designed for Angular builders.
import {
BuilderHarness,
JasmineBuilderHarness,
describeBuilder,
type BuilderHarnessExecutionOptions,
type BuilderHarnessExecutionResult,
type HarnessFileMatchers
} from "@angular-devkit/build-angular/testing";Core testing infrastructure for Angular builders providing isolated execution environment.
/**
* Test harness for Angular builders
* Provides isolated file system and workspace environment for testing builder execution
*/
class BuilderHarness {
/** Execute a builder with the given options */
executeTarget<T extends BuilderOutput = BuilderOutput>(
target: Target,
overrides?: any,
options?: BuilderHarnessExecutionOptions
): Promise<BuilderHarnessExecutionResult<T>>;
/** Write a file to the harness file system */
writeFile(path: string, content: string | Buffer): Promise<void>;
/** Write multiple files to the harness file system */
writeFiles(files: Record<string, string | Buffer>): Promise<void>;
/** Remove a file from the harness file system */
removeFile(path: string): Promise<void>;
/** Modify a file in the harness file system */
modifyFile(path: string, modifier: (content: string) => string): Promise<void>;
/** Check if a file exists in the harness file system */
hasFile(path: string): Promise<boolean>;
/** Read a file from the harness file system */
readFile(path: string): Promise<string>;
/** Append content to a file in the harness file system */
appendToFile(path: string, content: string): Promise<void>;
}
/**
* Jasmine-specific builder harness with additional test helpers
*/
class JasmineBuilderHarness extends BuilderHarness {
/** Expect file to exist */
expectFile(path: string): HarnessFileMatchers;
}
interface BuilderHarnessExecutionOptions {
/** Override configuration options */
outputLogsOnException?: boolean;
/** Override configuration options */
outputLogsOnFailure?: boolean;
/** Logger to use during execution */
logger?: logging.Logger;
}
interface BuilderHarnessExecutionResult<T extends BuilderOutput = BuilderOutput> {
/** Execution result if successful */
result?: T;
/** Error if execution failed */
error?: Error;
/** All log entries from execution */
logs: readonly logging.LogEntry[];
}
interface HarnessFileMatchers {
/** Expect file content to match */
toEqual(content: string): void;
/** Expect file content to contain */
toContain(content: string): void;
/** Expect file to match pattern */
toMatch(pattern: RegExp): void;
}Jasmine-specific testing utilities for Angular builders.
/**
* Describe a builder test suite with automatic harness setup
* @param builderHandler - The builder function to test
* @param options - Test configuration options
* @param specDefinitions - Test specifications
*/
function describeBuilder<T>(
builderHandler: BuilderHandlerFn<T>,
options: {
name?: string;
schemaPath: string;
workspaceRoot?: string;
},
specDefinitions: (harness: JasmineBuilderHarness) => void
): void;Usage Examples:
import { describeBuilder } from '@angular-devkit/build-angular/testing';
import { buildWebpackBrowser } from '@angular-devkit/build-angular';
describeBuilder(buildWebpackBrowser, {
name: 'Browser Builder',
schemaPath: require.resolve('../schema.json'),
}, (harness) => {
beforeEach(async () => {
// Setup test files
await harness.writeFiles({
'src/main.ts': `console.log('Hello World');`,
'src/index.html': '<html><body><app-root></app-root></body></html>',
'tsconfig.json': JSON.stringify({
compilerOptions: { target: 'es2015' }
})
});
});
it('should build successfully', async () => {
const { result } = await harness.executeTarget({
project: 'test',
target: 'build'
}, {
outputPath: 'dist',
index: 'src/index.html',
main: 'src/main.ts',
tsConfig: 'tsconfig.json'
});
expect(result?.success).toBe(true);
harness.expectFile('dist/main.js').toExist();
});
});Virtual file system for testing builders without real file I/O.
/**
* Virtual file system host for testing
* Provides in-memory file system operations
*/
interface VirtualFileSystemHost {
/** Write file to virtual file system */
writeFile(path: string, content: string): void;
/** Read file from virtual file system */
readFile(path: string): string;
/** Check if file exists */
exists(path: string): boolean;
/** List directory contents */
list(path: string): string[];
/** Create directory */
mkdir(path: string): void;
/** Remove file or directory */
delete(path: string): void;
/** Get file stats */
stat(path: string): { isFile(): boolean; isDirectory(): boolean };
}
/**
* Create virtual file system host
* @param files - Initial file structure
* @returns Virtual host instance
*/
function createVirtualHost(files: Record<string, string>): VirtualFileSystemHost;/**
* Workspace configuration for testing
*/
interface TestWorkspace {
/** Workspace root directory */
root: string;
/** Project configurations */
projects: Record<string, TestProject>;
/** Angular CLI configuration */
cli?: {
version: string;
packageManager?: string;
};
}
/**
* Project configuration for testing
*/
interface TestProject {
/** Project root relative to workspace */
root: string;
/** Project type */
projectType: 'application' | 'library';
/** Build targets */
targets: Record<string, TestTarget>;
/** Source root */
sourceRoot?: string;
}
/**
* Target configuration for testing
*/
interface TestTarget {
/** Builder to use */
builder: string;
/** Default options */
options: any;
/** Configuration-specific options */
configurations?: Record<string, any>;
}
/**
* Create test workspace configuration
* @param projects - Project configurations
* @param workspaceOptions - Additional workspace options
* @returns Test workspace
*/
function createTestWorkspace(
projects: Record<string, Partial<TestProject>>,
workspaceOptions?: Partial<TestWorkspace>
): TestWorkspace;/**
* Mock logger for testing
*/
interface MockLogger {
/** Debug messages */
debug: jest.Mock;
/** Info messages */
info: jest.Mock;
/** Warning messages */
warn: jest.Mock;
/** Error messages */
error: jest.Mock;
/** Fatal messages */
fatal: jest.Mock;
}
/**
* Create mock logger instance
* @returns Mock logger with jest spy functions
*/
function createMockLogger(): MockLogger;
/**
* Mock analytics for testing
*/
interface MockAnalytics {
/** Track events */
track: jest.Mock;
/** Page views */
page: jest.Mock;
/** User identification */
identify: jest.Mock;
/** Flush analytics */
flush: jest.Mock;
}
/**
* Create mock analytics instance
* @returns Mock analytics with jest spy functions
*/
function createMockAnalytics(): MockAnalytics;/**
* Builder test runner with common setup
*/
class BuilderTestRunner {
/** Setup test environment */
setup(workspaceRoot: string, projectRoot: string): Promise<void>;
/** Run builder with options */
runBuilder<T>(
builderName: string,
options: T,
timeout?: number
): Promise<BuilderOutput>;
/** Add file to virtual file system */
addFile(path: string, content: string): void;
/** Cleanup test environment */
cleanup(): Promise<void>;
}
/**
* Webpack test utilities
*/
interface WebpackTestUtils {
/** Create test webpack configuration */
createTestConfig(options: any): webpack.Configuration;
/** Mock webpack stats */
createMockStats(options?: Partial<webpack.Stats>): webpack.Stats;
/** Validate webpack configuration */
validateConfig(config: webpack.Configuration): ValidationResult;
}
/**
* Karma test utilities
*/
interface KarmaTestUtils {
/** Create test karma configuration */
createTestKarmaConfig(options: any): any;
/** Mock karma server */
createMockKarmaServer(): any;
/** Generate test coverage report */
generateCoverageReport(coverage: any): CoverageReport;
}import { createArchitectHost, createArchitect, testBuilder } from "@angular-devkit/build-angular/testing";
import { executeBrowserBuilder } from "@angular-devkit/build-angular";
describe('Browser Builder', () => {
let architect: Architect;
let architectHost: TestingArchitectHost;
beforeEach(async () => {
const workspaceRoot = '/test-workspace';
architectHost = createArchitectHost(workspaceRoot);
architect = createArchitect(architectHost);
// Add builder to test environment
architectHost.addBuilder('@angular-devkit/build-angular:browser', executeBrowserBuilder);
// Add target configuration
architectHost.addTarget({
project: 'test-app',
target: 'build'
}, '@angular-devkit/build-angular:browser', {
outputPath: 'dist/test-app',
index: 'src/index.html',
main: 'src/main.ts',
tsConfig: 'tsconfig.app.json'
});
});
it('should build application successfully', async () => {
const run = await architect.scheduleTarget({
project: 'test-app',
target: 'build'
});
const output = await run.result;
expect(output.success).toBe(true);
await run.stop();
});
it('should test builder directly', async () => {
const options = {
outputPath: 'dist/test-app',
index: 'src/index.html',
main: 'src/main.ts',
tsConfig: 'tsconfig.app.json'
};
const context = createMockContext(
{ name: '@angular-devkit/build-angular:browser', version: '1.0.0' },
'/test-workspace',
'src'
);
const output = await testBuilder(executeBrowserBuilder, options, context);
expect(output.success).toBe(true);
});
});