Skip to content

Commit 2b60292

Browse files
committed
feat(@angular-devkit/build-angular): providing a karma config is now optional
Karma will now use a builtin config when the `karmaConfig` is not specified.
1 parent 8f8e02c commit 2b60292

File tree

3 files changed

+61
-14
lines changed

3 files changed

+61
-14
lines changed

goldens/public-api/angular_devkit/build_angular/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ export interface KarmaBuilderOptions {
177177
fileReplacements?: FileReplacement_2[];
178178
include?: string[];
179179
inlineStyleLanguage?: InlineStyleLanguage_2;
180-
karmaConfig: string;
180+
karmaConfig?: string;
181181
main?: string;
182182
poll?: number;
183183
polyfills?: Polyfills_2;

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

Lines changed: 59 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
*/
88

99
import { BuilderContext, BuilderOutput, createBuilder } from '@angular-devkit/architect';
10-
import { Config, ConfigOptions } from 'karma';
10+
import { strings } from '@angular-devkit/core';
11+
import { Config, ConfigOptions, config, constants } from 'karma';
12+
import { createRequire } from 'module';
1113
import * as path from 'path';
1214
import { Observable, from } from 'rxjs';
1315
import { defaultIfEmpty, switchMap } from 'rxjs/operators';
@@ -86,9 +88,17 @@ export function execute(
8688

8789
return from(initialize(options, context, transforms.webpackConfiguration)).pipe(
8890
switchMap(async ([karma, webpackConfig]) => {
89-
const karmaOptions: KarmaConfigOptions = {
90-
singleRun,
91-
};
91+
// Determine project name from builder context target
92+
const projectName = context.target?.project;
93+
if (!projectName) {
94+
throw new Error(`The 'karma' builder requires a target to be specified.`);
95+
}
96+
97+
const karmaOptions: KarmaConfigOptions = options.karmaConfig
98+
? {}
99+
: getBuiltInKarmaConfig(context.workspaceRoot, projectName);
100+
101+
karmaOptions.singleRun = singleRun;
92102

93103
// Convert browsers from a string to an array
94104
if (options.browsers) {
@@ -106,11 +116,6 @@ export function execute(
106116
}
107117
}
108118

109-
const projectName = context.target?.project;
110-
if (!projectName) {
111-
throw new Error('The builder requires a target.');
112-
}
113-
114119
if (!options.main) {
115120
webpackConfig.entry ??= {};
116121
if (typeof webpackConfig.entry === 'object' && !Array.isArray(webpackConfig.entry)) {
@@ -140,13 +145,13 @@ export function execute(
140145
logger: context.logger,
141146
};
142147

143-
const config = await karma.config.parseConfig(
144-
path.resolve(context.workspaceRoot, options.karmaConfig),
148+
const parsedKarmaConfig = await config.parseConfig(
149+
options.karmaConfig && path.resolve(context.workspaceRoot, options.karmaConfig),
145150
transforms.karmaOptions ? transforms.karmaOptions(karmaOptions) : karmaOptions,
146151
{ promiseConfig: true, throwErrors: true },
147152
);
148153

149-
return [karma, config] as [typeof karma, KarmaConfigOptions];
154+
return [karma, parsedKarmaConfig] as [typeof karma, KarmaConfigOptions];
150155
}),
151156
switchMap(
152157
([karma, karmaConfig]) =>
@@ -178,6 +183,48 @@ export function execute(
178183
);
179184
}
180185

186+
function getBuiltInKarmaConfig(
187+
workspaceRoot: string,
188+
projectName: string,
189+
): ConfigOptions & Record<string, unknown> {
190+
let coverageFolderName = projectName.charAt(0) === '@' ? projectName.slice(1) : projectName;
191+
if (/[A-Z]/.test(coverageFolderName)) {
192+
coverageFolderName = strings.dasherize(coverageFolderName);
193+
}
194+
195+
const workspaceRootRequire = createRequire(workspaceRoot + '/');
196+
197+
return {
198+
basePath: '',
199+
frameworks: ['jasmine', '@angular-devkit/build-angular'],
200+
plugins: [
201+
'karma-jasmine',
202+
'karma-chrome-launcher',
203+
'karma-jasmine-html-reporter',
204+
'karma-coverage',
205+
'@angular-devkit/build-angular/plugins/karma',
206+
].map((p) => workspaceRootRequire(p)),
207+
client: {
208+
clearContext: false, // leave Jasmine Spec Runner output visible in browser
209+
},
210+
jasmineHtmlReporter: {
211+
suppressAll: true, // removes the duplicated traces
212+
},
213+
coverageReporter: {
214+
dir: path.join(workspaceRoot, 'coverage', coverageFolderName),
215+
subdir: '.',
216+
reporters: [{ type: 'html' }, { type: 'text-summary' }],
217+
},
218+
reporters: ['progress', 'kjhtml'],
219+
port: 9876,
220+
colors: true,
221+
logLevel: constants.LOG_INFO,
222+
autoWatch: true,
223+
browsers: ['Chrome'],
224+
restartOnFileChange: true,
225+
};
226+
}
227+
181228
export { KarmaBuilderOptions };
182229
export default createBuilder<Record<string, string> & KarmaBuilderOptions>(execute);
183230

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@
255255
}
256256
},
257257
"additionalProperties": false,
258-
"required": ["tsConfig", "karmaConfig"],
258+
"required": ["tsConfig"],
259259
"definitions": {
260260
"assetPattern": {
261261
"oneOf": [

0 commit comments

Comments
 (0)