Skip to content

Commit f145312

Browse files
alan-agius4clydin
authored andcommitted
fix(@angular-devkit/build-angular): update karma builder to use non-deprecated API
With this change we update the Karma builder to use the new Server API. Closes: #20479
1 parent b6abef1 commit f145312

File tree

3 files changed

+105
-101
lines changed

3 files changed

+105
-101
lines changed

packages/angular_devkit/build_angular/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@
8181
"@angular/compiler-cli": "^12.0.0-next",
8282
"@angular/localize": "^12.0.0-next",
8383
"@angular/service-worker": "^12.0.0-next",
84-
"karma": "^6.0.0",
84+
"karma": "^6.3.0",
8585
"ng-packagr": "^12.0.0-next",
8686
"protractor": "^7.0.0",
8787
"tailwindcss": "^2.0.0",

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

+102-98
Original file line numberDiff line numberDiff line change
@@ -72,105 +72,109 @@ export function execute(
7272
assertCompatibleAngularVersion(context.workspaceRoot, context.logger);
7373

7474
return from(initialize(options, context, transforms.webpackConfiguration)).pipe(
75-
switchMap(
76-
([karma, webpackConfig]) =>
77-
new Observable<BuilderOutput>(subscriber => {
78-
const karmaOptions: KarmaConfigOptions = {};
79-
80-
if (options.watch !== undefined) {
81-
karmaOptions.singleRun = !options.watch;
82-
}
83-
84-
// Convert browsers from a string to an array
85-
if (options.browsers) {
86-
karmaOptions.browsers = options.browsers.split(',');
87-
}
88-
89-
if (options.reporters) {
90-
// Split along commas to make it more natural, and remove empty strings.
91-
const reporters = options.reporters
92-
.reduce<string[]>((acc, curr) => acc.concat(curr.split(',')), [])
93-
.filter(x => !!x);
94-
95-
if (reporters.length > 0) {
96-
karmaOptions.reporters = reporters;
97-
}
98-
}
99-
100-
// prepend special webpack loader that will transform test.ts
101-
if (options.include && options.include.length > 0) {
102-
const mainFilePath = getSystemPath(
103-
join(normalize(context.workspaceRoot), options.main),
104-
);
105-
const files = findTests(options.include, dirname(mainFilePath), context.workspaceRoot);
106-
// early exit, no reason to start karma
107-
if (!files.length) {
108-
subscriber.error(
109-
`Specified patterns: "${options.include.join(', ')}" did not match any spec files`,
110-
);
111-
112-
return;
113-
}
114-
115-
// Get the rules and ensure the Webpack configuration is setup properly
116-
const rules = webpackConfig.module?.rules || [];
117-
if (!webpackConfig.module) {
118-
webpackConfig.module = { rules };
119-
} else if (!webpackConfig.module.rules) {
120-
webpackConfig.module.rules = rules;
121-
}
122-
123-
rules.unshift({
124-
test: mainFilePath,
125-
use: {
126-
// cannot be a simple path as it differs between environments
127-
loader: SingleTestTransformLoader,
128-
options: {
129-
files,
130-
logger: context.logger,
131-
},
132-
},
133-
});
134-
}
135-
136-
// Assign additional karmaConfig options to the local ngapp config
137-
karmaOptions.configFile = resolve(context.workspaceRoot, options.karmaConfig);
138-
139-
karmaOptions.buildWebpack = {
140-
options,
141-
webpackConfig,
142-
// Pass onto Karma to emit BuildEvents.
143-
successCb: () => subscriber.next({ success: true }),
144-
failureCb: () => subscriber.next({ success: false }),
145-
// Workaround for https://github.com/karma-runner/karma/issues/3154
146-
// When this workaround is removed, user projects need to be updated to use a Karma
147-
// version that has a fix for this issue.
148-
toJSON: () => {},
149-
logger: context.logger,
150-
};
151-
152-
// Complete the observable once the Karma server returns.
153-
const karmaServer = new karma.Server(
154-
transforms.karmaOptions ? transforms.karmaOptions(karmaOptions) : karmaOptions,
155-
(exitCode: number) => {
156-
subscriber.next({ success: exitCode === 0 });
157-
subscriber.complete();
158-
},
75+
switchMap(async ([karma, webpackConfig]) => {
76+
const karmaOptions: KarmaConfigOptions = {};
77+
78+
if (options.watch !== undefined) {
79+
karmaOptions.singleRun = !options.watch;
80+
}
81+
82+
// Convert browsers from a string to an array
83+
if (options.browsers) {
84+
karmaOptions.browsers = options.browsers.split(',');
85+
}
86+
87+
if (options.reporters) {
88+
// Split along commas to make it more natural, and remove empty strings.
89+
const reporters = options.reporters
90+
.reduce<string[]>((acc, curr) => acc.concat(curr.split(',')), [])
91+
.filter(x => !!x);
92+
93+
if (reporters.length > 0) {
94+
karmaOptions.reporters = reporters;
95+
}
96+
}
97+
98+
// prepend special webpack loader that will transform test.ts
99+
if (options.include && options.include.length > 0) {
100+
const mainFilePath = getSystemPath(
101+
join(normalize(context.workspaceRoot), options.main),
102+
);
103+
const files = findTests(options.include, dirname(mainFilePath), context.workspaceRoot);
104+
// early exit, no reason to start karma
105+
if (!files.length) {
106+
throw new Error(
107+
`Specified patterns: "${options.include.join(', ')}" did not match any spec files.`,
159108
);
160-
// karma typings incorrectly define start's return value as void
161-
// tslint:disable-next-line:no-use-of-empty-return-value
162-
const karmaStart = (karmaServer.start() as unknown) as Promise<void>;
163-
164-
// Cleanup, signal Karma to exit.
165-
return () => {
166-
// Karma only has the `stop` method start with 3.1.1, so we must defensively check.
167-
const karmaServerWithStop = (karmaServer as unknown) as { stop: () => Promise<void> };
168-
if (typeof karmaServerWithStop.stop === 'function') {
169-
return karmaStart.then(() => karmaServerWithStop.stop());
170-
}
171-
};
172-
}),
173-
),
109+
}
110+
111+
// Get the rules and ensure the Webpack configuration is setup properly
112+
const rules = webpackConfig.module?.rules || [];
113+
if (!webpackConfig.module) {
114+
webpackConfig.module = { rules };
115+
} else if (!webpackConfig.module.rules) {
116+
webpackConfig.module.rules = rules;
117+
}
118+
119+
rules.unshift({
120+
test: mainFilePath,
121+
use: {
122+
// cannot be a simple path as it differs between environments
123+
loader: SingleTestTransformLoader,
124+
options: {
125+
files,
126+
logger: context.logger,
127+
},
128+
},
129+
});
130+
}
131+
132+
karmaOptions.buildWebpack = {
133+
options,
134+
webpackConfig,
135+
logger: context.logger,
136+
};
137+
138+
// @types/karma doesn't include the last parameter.
139+
// https://github.com/DefinitelyTyped/DefinitelyTyped/pull/52286
140+
// tslint:disable-next-line: no-any
141+
const config = await (karma.config.parseConfig as any)(
142+
resolve(context.workspaceRoot, options.karmaConfig),
143+
transforms.karmaOptions ? transforms.karmaOptions(karmaOptions) : karmaOptions,
144+
{ promiseConfig: true, throwErrors: true },
145+
) as Promise<KarmaConfigOptions>;
146+
147+
return [karma, config] as [typeof karma, KarmaConfigOptions];
148+
}),
149+
switchMap(([karma, karmaConfig]) => new Observable<BuilderOutput>(subscriber => {
150+
// Pass onto Karma to emit BuildEvents.
151+
karmaConfig.buildWebpack ??= {};
152+
if (typeof karmaConfig.buildWebpack === 'object') {
153+
// tslint:disable-next-line: no-any
154+
(karmaConfig.buildWebpack as any).failureCb ??= () => subscriber.next({ success: false });
155+
// tslint:disable-next-line: no-any
156+
(karmaConfig.buildWebpack as any).successCb ??= () => subscriber.next({ success: true });
157+
}
158+
159+
// Complete the observable once the Karma server returns.
160+
const karmaServer = new karma.Server(
161+
karmaConfig,
162+
(exitCode: number) => {
163+
subscriber.next({ success: exitCode === 0 });
164+
subscriber.complete();
165+
},
166+
);
167+
// karma typings incorrectly define start's return value as void
168+
// tslint:disable-next-line:no-use-of-empty-return-value
169+
const karmaStart = (karmaServer.start() as unknown) as Promise<void>;
170+
171+
// Cleanup, signal Karma to exit.
172+
return () => {
173+
const karmaServerWithStop = (karmaServer as unknown) as { stop: () => Promise<void> };
174+
175+
return karmaStart.then(() => karmaServerWithStop.stop());
176+
};
177+
})),
174178
defaultIfEmpty({ success: false }),
175179
);
176180
}

packages/angular_devkit/build_angular/src/karma/selected_spec.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ describe('Karma Builder', () => {
2626
};
2727
const run = await architect.scheduleTarget(karmaTargetSpec, overrides);
2828

29-
await expectAsync(run.result).toBeRejectedWith(
30-
`Specified patterns: "abc.spec.ts, def.spec.ts" did not match any spec files`,
29+
await expectAsync(run.result).toBeRejectedWithError(
30+
`Specified patterns: "abc.spec.ts, def.spec.ts" did not match any spec files.`,
3131
);
3232

3333
await run.stop();

0 commit comments

Comments
 (0)