From fb41d182ee9e50386b0412e07a8bacaaa8c7ce13 Mon Sep 17 00:00:00 2001 From: Jan Krems Date: Thu, 5 Dec 2024 08:37:36 -0800 Subject: [PATCH] fix(@angular-devkit/build-angular): fix webpack config transform for karma --- .../testing/builder/src/builder-harness.ts | 13 +++- .../angular_devkit/build_angular/BUILD.bazel | 1 + .../src/builders/karma/browser_builder.ts | 2 +- .../karma/tests/options/custom-loader_spec.ts | 67 +++++++++++++++++++ 4 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 packages/angular_devkit/build_angular/src/builders/karma/tests/options/custom-loader_spec.ts diff --git a/modules/testing/builder/src/builder-harness.ts b/modules/testing/builder/src/builder-harness.ts index d38858565389..9fdca970a277 100644 --- a/modules/testing/builder/src/builder-harness.ts +++ b/modules/testing/builder/src/builder-harness.ts @@ -54,6 +54,11 @@ export interface BuilderHarnessExecutionOptions { outputLogsOnException: boolean; useNativeFileWatching: boolean; signal: AbortSignal; + additionalExecuteArguments: unknown[]; +} + +interface BuilderHandlerFnWithVarArgs extends BuilderHandlerFn { + (input: T, context: BuilderContext, ...args: unknown[]): BuilderOutputLike; } /** @@ -256,7 +261,13 @@ export class BuilderHarness { mergeMap((validator) => validator(targetOptions)), map((validationResult) => validationResult.data), mergeMap((data) => - convertBuilderOutputToObservable(this.builderHandler(data as T & json.JsonObject, context)), + convertBuilderOutputToObservable( + (this.builderHandler as BuilderHandlerFnWithVarArgs)( + data as T & json.JsonObject, + context, + ...(options.additionalExecuteArguments ?? []), + ), + ), ), map((buildResult) => ({ result: buildResult, error: undefined })), catchError((error) => { diff --git a/packages/angular_devkit/build_angular/BUILD.bazel b/packages/angular_devkit/build_angular/BUILD.bazel index 4a3b2ab94478..fd803c9ba7f2 100644 --- a/packages/angular_devkit/build_angular/BUILD.bazel +++ b/packages/angular_devkit/build_angular/BUILD.bazel @@ -321,6 +321,7 @@ LARGE_SPECS = { "@npm//karma-jasmine", "@npm//karma-jasmine-html-reporter", "@npm//puppeteer", + "@npm//webpack", ], }, "protractor": { diff --git a/packages/angular_devkit/build_angular/src/builders/karma/browser_builder.ts b/packages/angular_devkit/build_angular/src/builders/karma/browser_builder.ts index 6abf78e1f68a..6bc18e6e2b4e 100644 --- a/packages/angular_devkit/build_angular/src/builders/karma/browser_builder.ts +++ b/packages/angular_devkit/build_angular/src/builders/karma/browser_builder.ts @@ -34,7 +34,7 @@ export function execute( karmaOptions?: (options: KarmaConfigOptions) => KarmaConfigOptions; } = {}, ): Observable { - return from(initializeBrowser(options, context)).pipe( + return from(initializeBrowser(options, context, transforms.webpackConfiguration)).pipe( switchMap(async ([karma, webpackConfig]) => { const projectName = context.target?.project; if (!projectName) { diff --git a/packages/angular_devkit/build_angular/src/builders/karma/tests/options/custom-loader_spec.ts b/packages/angular_devkit/build_angular/src/builders/karma/tests/options/custom-loader_spec.ts new file mode 100644 index 000000000000..5b40e38451c4 --- /dev/null +++ b/packages/angular_devkit/build_angular/src/builders/karma/tests/options/custom-loader_spec.ts @@ -0,0 +1,67 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import { Configuration } from 'webpack'; + +import { execute } from '../../index'; +import { BASE_OPTIONS, KARMA_BUILDER_INFO, describeKarmaBuilder } from '../setup'; +import { ExecutionTransformer } from '../../../../transforms'; + +describeKarmaBuilder(execute, KARMA_BUILDER_INFO, (harness, setupTarget, isApplicationBuilder) => { + describe('Option: Custom file loader', () => { + beforeEach(async () => { + if (isApplicationBuilder) { + pending('not implemented yet for application builder'); + } + await setupTarget(harness); + }); + + beforeEach(async () => { + await harness.writeFiles({ + 'src/number-webpack-loader.js': ` + module.exports = (source) => { + return 'export const DOUBLED = ' + (Number(source) * 2) + ';\\n'; + };`, + 'src/app/app.number': `42`, + 'src/app/app.number.d.ts': `export const DOUBLED: number;`, + 'src/app/app.component.spec.ts': ` + import { DOUBLED } from './app.number'; + describe('Custom webpack transform', () => { + it('generates expected export', () => { + expect(DOUBLED).toBe(84); + }); + });`, + }); + }); + + it('applies the webpack configuration transform', async () => { + harness.useTarget('test', { + ...BASE_OPTIONS, + }); + + const webpackConfiguration: ExecutionTransformer = (config: Configuration) => { + config.module ??= {}; + config.module.rules ??= []; + config.module.rules.push({ + test: /\.number$/, + loader: './src/number-webpack-loader.js', + }); + return config; + }; + + const { result } = await harness.executeOnce({ + additionalExecuteArguments: [ + { + webpackConfiguration, + }, + ], + }); + expect(result?.success).toBeTrue(); + }); + }); +});