diff --git a/modules/testing/builder/src/jasmine-helpers.ts b/modules/testing/builder/src/jasmine-helpers.ts index dcd11fe59002..269cbc2396ed 100644 --- a/modules/testing/builder/src/jasmine-helpers.ts +++ b/modules/testing/builder/src/jasmine-helpers.ts @@ -9,9 +9,16 @@ import { BuilderHandlerFn } from '@angular-devkit/architect'; import { json } from '@angular-devkit/core'; import { readFileSync } from 'fs'; -import { BuilderHarness } from './builder-harness'; +import { concatMap, count, firstValueFrom, take, timeout } from 'rxjs'; +import { BuilderHarness, BuilderHarnessExecutionResult } from './builder-harness'; import { host } from './test-utils'; +/** + * Maximum time for single build/rebuild + * This accounts for CI variability. + */ +export const BUILD_TIMEOUT = 25_000; + const optionSchemaCache = new Map(); export function describeBuilder( @@ -45,6 +52,24 @@ export class JasmineBuilderHarness extends BuilderHarness { expectDirectory(path: string): HarnessDirectoryMatchers { return expectDirectory(path, this); } + + async executeWithCases( + cases: (( + executionResult: BuilderHarnessExecutionResult, + index: number, + ) => void | Promise)[], + ): Promise { + const executionCount = await firstValueFrom( + this.execute().pipe( + timeout(BUILD_TIMEOUT), + concatMap(async (result, index) => await cases[index](result, index)), + take(cases.length), + count(), + ), + ); + + expect(executionCount).toBe(cases.length); + } } export interface HarnessFileMatchers { diff --git a/packages/angular/build/src/builders/dev-server/tests/options/watch_spec.ts b/packages/angular/build/src/builders/dev-server/tests/options/watch_spec.ts index e09ea21a58c5..9840ea2adc36 100644 --- a/packages/angular/build/src/builders/dev-server/tests/options/watch_spec.ts +++ b/packages/angular/build/src/builders/dev-server/tests/options/watch_spec.ts @@ -6,10 +6,10 @@ * found in the LICENSE file at https://angular.dev/license */ -import { TimeoutError, concatMap, count, take, timeout } from 'rxjs'; +import { TimeoutError } from 'rxjs'; import { executeDevServer } from '../../index'; import { describeServeBuilder } from '../jasmine-helpers'; -import { BASE_OPTIONS, BUILD_TIMEOUT, DEV_SERVER_BUILDER_INFO } from '../setup'; +import { BASE_OPTIONS, DEV_SERVER_BUILDER_INFO } from '../setup'; describeServeBuilder(executeDevServer, DEV_SERVER_BUILDER_INFO, (harness, setupTarget) => { describe('Option: "watch"', () => { @@ -24,27 +24,21 @@ describeServeBuilder(executeDevServer, DEV_SERVER_BUILDER_INFO, (harness, setupT }); await harness - .execute() - .pipe( - timeout(BUILD_TIMEOUT), - concatMap(async ({ result }, index) => { + .executeWithCases([ + async ({ result }) => { + // Initial build should succeed expect(result?.success).toBe(true); - switch (index) { - case 0: - await harness.modifyFile( - 'src/main.ts', - (content) => content + 'console.log("abcd1234");', - ); - break; - case 1: - fail('Expected files to not be watched.'); - break; - } - }), - take(2), - ) - .toPromise() + // Modify a file to attempt to trigger file watcher + await harness.modifyFile( + 'src/main.ts', + (content) => content + 'console.log("abcd1234");', + ); + }, + () => { + fail('Expected files to not be watched.'); + }, + ]) .catch((error) => { // Timeout is expected if watching is disabled if (error instanceof TimeoutError) { @@ -60,30 +54,22 @@ describeServeBuilder(executeDevServer, DEV_SERVER_BUILDER_INFO, (harness, setupT watch: undefined, }); - const buildCount = await harness - .execute() - .pipe( - timeout(BUILD_TIMEOUT), - concatMap(async ({ result }, index) => { - expect(result?.success).toBe(true); - - switch (index) { - case 0: - await harness.modifyFile( - 'src/main.ts', - (content) => content + 'console.log("abcd1234");', - ); - break; - case 1: - break; - } - }), - take(2), - count(), - ) - .toPromise(); + await harness.executeWithCases([ + async ({ result }) => { + // Initial build should succeed + expect(result?.success).toBe(true); - expect(buildCount).toBe(2); + // Modify a file to trigger file watcher + await harness.modifyFile( + 'src/main.ts', + (content) => content + 'console.log("abcd1234");', + ); + }, + async ({ result }) => { + // Modifying a file should trigger a successful rebuild + expect(result?.success).toBe(true); + }, + ]); }); it('watches for file changes when true', async () => { @@ -92,30 +78,22 @@ describeServeBuilder(executeDevServer, DEV_SERVER_BUILDER_INFO, (harness, setupT watch: true, }); - const buildCount = await harness - .execute() - .pipe( - timeout(BUILD_TIMEOUT), - concatMap(async ({ result }, index) => { - expect(result?.success).toBe(true); - - switch (index) { - case 0: - await harness.modifyFile( - 'src/main.ts', - (content) => content + 'console.log("abcd1234");', - ); - break; - case 1: - break; - } - }), - take(2), - count(), - ) - .toPromise(); + await harness.executeWithCases([ + async ({ result }) => { + // Initial build should succeed + expect(result?.success).toBe(true); - expect(buildCount).toBe(2); + // Modify a file to trigger file watcher + await harness.modifyFile( + 'src/main.ts', + (content) => content + 'console.log("abcd1234");', + ); + }, + async ({ result }) => { + // Modifying a file should trigger a successful rebuild + expect(result?.success).toBe(true); + }, + ]); }); }); });