Skip to content

Commit c6d1570

Browse files
committed
feat(@angular-devkit/build-angular): support karma with esbuild
Adds a new "builderMode" setting for Karma that can be used to switch between webpack ("browser") and esbuild ("application"). It supports a third value "detect" that will use the same bundler that's also used for development builds. The detect mode is modelled after the logic used for the dev-server builder. This initial implementation doesn't properly support `--watch` mode or code coverage.
1 parent 6aab17a commit c6d1570

File tree

10 files changed

+466
-60
lines changed

10 files changed

+466
-60
lines changed

packages/angular_devkit/build_angular/src/builders/karma/index.ts

Lines changed: 332 additions & 55 deletions
Large diffs are not rendered by default.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.dev/license
7+
*/
8+
9+
import { getTestBed } from '@angular/core/testing';
10+
import {
11+
BrowserDynamicTestingModule,
12+
platformBrowserDynamicTesting,
13+
} from '@angular/platform-browser-dynamic/testing';
14+
15+
// Initialize the Angular testing environment.
16+
getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), {
17+
errorOnUnknownElements: true,
18+
errorOnUnknownProperties: true,
19+
});

packages/angular_devkit/build_angular/src/builders/karma/schema.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,12 @@
267267
"type": "string"
268268
}
269269
},
270+
"builderMode": {
271+
"type": "string",
272+
"description": "Determines how to build the code under test. If set to 'detect', attempts to follow the development builder.",
273+
"enum": ["detect", "browser", "application"],
274+
"default": "browser"
275+
},
270276
"webWorkerTsConfig": {
271277
"type": "string",
272278
"description": "TypeScript configuration for Web Worker modules."

packages/angular_devkit/build_angular/src/builders/karma/tests/behavior/code-coverage_spec.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ const coveragePath = 'coverage/lcov.info';
2323

2424
describeKarmaBuilder(execute, KARMA_BUILDER_INFO, (harness, setupTarget, isApplicationBuilder) => {
2525
describe('Behavior: "codeCoverage"', () => {
26+
if (isApplicationBuilder) {
27+
beforeEach(() => {
28+
pending('Code coverage not implemented yet for application builder');
29+
});
30+
}
31+
2632
beforeEach(() => {
2733
setupTarget(harness);
2834
});

packages/angular_devkit/build_angular/src/builders/karma/tests/behavior/rebuilds_spec.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,14 @@ import { concatMap, count, debounceTime, take, timeout } from 'rxjs';
1010
import { execute } from '../../index';
1111
import { BASE_OPTIONS, KARMA_BUILDER_INFO, describeKarmaBuilder } from '../setup';
1212

13-
describeKarmaBuilder(execute, KARMA_BUILDER_INFO, (harness, setupTarget) => {
13+
describeKarmaBuilder(execute, KARMA_BUILDER_INFO, (harness, setupTarget, isApplicationBuilder) => {
1414
describe('Behavior: "Rebuilds"', () => {
15+
if (isApplicationBuilder) {
16+
beforeEach(() => {
17+
pending('--watch not implemented yet for application builder');
18+
});
19+
}
20+
1521
beforeEach(() => {
1622
setupTarget(harness);
1723
});
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.dev/license
7+
*/
8+
9+
import { execute } from '../../index';
10+
import { BASE_OPTIONS, KARMA_BUILDER_INFO, describeKarmaBuilder } from '../setup';
11+
import { BuilderMode } from '../../schema';
12+
13+
const ESBUILD_LOG_TEXT = 'Application bundle generation complete.';
14+
15+
describeKarmaBuilder(execute, KARMA_BUILDER_INFO, (harness, setupTarget, isApplicationTarget) => {
16+
describe('option: "builderMode"', () => {
17+
beforeEach(() => {
18+
setupTarget(harness);
19+
});
20+
21+
it('"application" always uses esbuild', async () => {
22+
harness.useTarget('test', {
23+
...BASE_OPTIONS,
24+
builderMode: BuilderMode.Application,
25+
});
26+
27+
const { result, logs } = await harness.executeOnce();
28+
expect(result?.success).toBeTrue();
29+
expect(logs).toContain(
30+
jasmine.objectContaining({
31+
message: jasmine.stringMatching(ESBUILD_LOG_TEXT),
32+
}),
33+
);
34+
});
35+
36+
it('"browser" always uses webpack', async () => {
37+
harness.useTarget('test', {
38+
...BASE_OPTIONS,
39+
builderMode: BuilderMode.Browser,
40+
});
41+
42+
const { result, logs } = await harness.executeOnce();
43+
expect(result?.success).toBeTrue();
44+
expect(logs).not.toContain(
45+
jasmine.objectContaining({
46+
message: jasmine.stringMatching(ESBUILD_LOG_TEXT),
47+
}),
48+
);
49+
});
50+
51+
it('"detect" follows configuration of the development builder', async () => {
52+
harness.useTarget('test', {
53+
...BASE_OPTIONS,
54+
builderMode: BuilderMode.Detect,
55+
});
56+
57+
const { result, logs } = await harness.executeOnce();
58+
expect(result?.success).toBeTrue();
59+
if (isApplicationTarget) {
60+
expect(logs).toContain(
61+
jasmine.objectContaining({
62+
message: jasmine.stringMatching(ESBUILD_LOG_TEXT),
63+
}),
64+
);
65+
} else {
66+
expect(logs).not.toContain(
67+
jasmine.objectContaining({
68+
message: jasmine.stringMatching(ESBUILD_LOG_TEXT),
69+
}),
70+
);
71+
}
72+
});
73+
});
74+
});

packages/angular_devkit/build_angular/src/builders/karma/tests/options/code-coverage-exclude_spec.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,14 @@ import { BASE_OPTIONS, KARMA_BUILDER_INFO, describeKarmaBuilder } from '../setup
1818

1919
const coveragePath = 'coverage/lcov.info';
2020

21-
describeKarmaBuilder(execute, KARMA_BUILDER_INFO, (harness, setupTarget) => {
21+
describeKarmaBuilder(execute, KARMA_BUILDER_INFO, (harness, setupTarget, isApplicationBuilder) => {
2222
describe('Option: "codeCoverageExclude"', () => {
23+
if (isApplicationBuilder) {
24+
beforeEach(() => {
25+
pending('Code coverage not implemented yet for application builder');
26+
});
27+
}
28+
2329
beforeEach(() => {
2430
setupTarget(harness);
2531
});

packages/angular_devkit/build_angular/src/builders/karma/tests/options/code-coverage_spec.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,14 @@ import { BASE_OPTIONS, KARMA_BUILDER_INFO, describeKarmaBuilder } from '../setup
1919

2020
const coveragePath = 'coverage/lcov.info';
2121

22-
describeKarmaBuilder(execute, KARMA_BUILDER_INFO, (harness, setupTarget) => {
22+
describeKarmaBuilder(execute, KARMA_BUILDER_INFO, (harness, setupTarget, isApplicationBuilder) => {
2323
describe('Option: "codeCoverage"', () => {
24+
if (isApplicationBuilder) {
25+
beforeEach(() => {
26+
pending('Code coverage not implemented yet for application builder');
27+
});
28+
}
29+
2430
beforeEach(() => {
2531
setupTarget(harness);
2632
});

packages/angular_devkit/build_angular/src/builders/karma/tests/options/include_spec.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,17 @@
99
import { execute } from '../../index';
1010
import { BASE_OPTIONS, KARMA_BUILDER_INFO, describeKarmaBuilder } from '../setup';
1111

12-
describeKarmaBuilder(execute, KARMA_BUILDER_INFO, (harness, setupTarget) => {
12+
describeKarmaBuilder(execute, KARMA_BUILDER_INFO, (harness, setupTarget, isApplicationBuilder) => {
13+
const originalIt = it;
14+
const originalFit = fit;
15+
1316
describe('Option: "include"', () => {
1417
beforeEach(() => {
1518
setupTarget(harness);
1619
});
1720

21+
const it = isApplicationBuilder ? originalFit : originalIt;
22+
1823
it(`should fail when includes doesn't match any files`, async () => {
1924
harness.useTarget('test', {
2025
...BASE_OPTIONS,

packages/angular_devkit/build_angular/src/builders/karma/tests/setup.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* found in the LICENSE file at https://angular.dev/license
77
*/
88

9-
import { Schema } from '../schema';
9+
import { BuilderMode, Schema } from '../schema';
1010
import { BuilderHandlerFn } from '@angular-devkit/architect';
1111
import { json } from '@angular-devkit/core';
1212
import { ApplicationBuilderOptions as ApplicationSchema, buildApplication } from '@angular/build';
@@ -41,6 +41,7 @@ export const BASE_OPTIONS = Object.freeze<Schema>({
4141
browsers: 'ChromeHeadlessCI',
4242
progress: false,
4343
watch: false,
44+
builderMode: BuilderMode.Detect,
4445
});
4546

4647
const optionSchemaCache = new Map<string, json.schema.JsonSchema>();

0 commit comments

Comments
 (0)