Skip to content

Commit b2f7a03

Browse files
committed
feat(@schematics/angular): conditionally install istanbul coverage provider for Vitest migration
Recognize non-Chromium browser usage during the Karma to Vitest migration. While Node-based and Chromium environments can leverage @vitest/coverage-v8, browser-based test runs using engines such as Firefox or WebKit natively necessitate istanbul instrumentation.
1 parent 8d0805d commit b2f7a03

2 files changed

Lines changed: 82 additions & 33 deletions

File tree

packages/schematics/angular/migrations/migrate-karma-to-vitest/migration.ts

Lines changed: 81 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@ async function processTestTargetOptions(
2727
customBuildOptions: Record<string, Record<string, json.JsonValue | undefined>>,
2828
needDevkitPlugin: boolean,
2929
manualMigrationFiles: string[],
30-
): Promise<boolean> {
30+
): Promise<{ needsCoverage: boolean; needsIstanbul: boolean }> {
3131
let needsCoverage = false;
32+
let needsIstanbul = false;
3233
for (const [configName, options] of allTargetOptions(testTarget, false)) {
3334
const configKey = configName || '';
3435
if (!customBuildOptions[configKey]) {
@@ -81,6 +82,20 @@ async function processTestTargetOptions(
8182

8283
const updatedBrowsers = options['browsers'];
8384
if (Array.isArray(updatedBrowsers) && updatedBrowsers.length > 0) {
85+
const hasNonChromium = updatedBrowsers.some((b) => {
86+
if (typeof b !== 'string') {
87+
return false;
88+
}
89+
90+
const normalized = b.toLowerCase();
91+
92+
return !['chrome', 'chromium', 'edge'].some((name) => normalized.includes(name));
93+
});
94+
95+
if (hasNonChromium) {
96+
needsIstanbul = true;
97+
}
98+
8499
context.logger.info(
85100
`Project "${projectName}" has browsers configured for tests. ` +
86101
`To run tests in a browser with Vitest, you will need to install either ` +
@@ -118,7 +133,7 @@ async function processTestTargetOptions(
118133
delete options['main'];
119134
}
120135

121-
return needsCoverage;
136+
return { needsCoverage, needsIstanbul };
122137
}
123138

124139
function updateTsConfigTypes(
@@ -154,9 +169,49 @@ function updateTsConfigTypes(
154169
}
155170
}
156171

172+
function logSummary(
173+
context: SchematicContext,
174+
migratedProjects: string[],
175+
skippedNonApplications: string[],
176+
skippedMissingAppBuilder: string[],
177+
manualMigrationFiles: string[],
178+
): void {
179+
context.logger.info('\n--- Karma to Vitest Migration Summary ---');
180+
context.logger.info(`Projects migrated: ${migratedProjects.length}`);
181+
if (migratedProjects.length > 0) {
182+
context.logger.info(` - ${migratedProjects.join(', ')}`);
183+
}
184+
context.logger.info(`Projects skipped (non-applications): ${skippedNonApplications.length}`);
185+
if (skippedNonApplications.length > 0) {
186+
context.logger.info(` - ${skippedNonApplications.join(', ')}`);
187+
}
188+
context.logger.info(
189+
`Projects skipped (missing application builder): ${skippedMissingAppBuilder.length}`,
190+
);
191+
if (skippedMissingAppBuilder.length > 0) {
192+
context.logger.info(` - ${skippedMissingAppBuilder.join(', ')}`);
193+
}
194+
195+
const uniqueManualFiles = [...new Set(manualMigrationFiles)];
196+
if (uniqueManualFiles.length > 0) {
197+
context.logger.warn(`\nThe following Karma configuration files require manual migration:`);
198+
for (const file of uniqueManualFiles) {
199+
context.logger.warn(` - ${file}`);
200+
}
201+
}
202+
if (migratedProjects.length > 0) {
203+
context.logger.info(
204+
`\nNote: To refactor your test files from Jasmine to Vitest, consider running the following command:` +
205+
`\n ng g @schematics/angular:refactor-jasmine-vitest <project_name>`,
206+
);
207+
}
208+
context.logger.info('-----------------------------------------\n');
209+
}
210+
157211
function updateProjects(tree: Tree, context: SchematicContext): Rule {
158212
return updateWorkspace(async (workspace) => {
159213
let needsCoverage = false;
214+
let needsIstanbul = false;
160215
const removableKarmaConfigs = new Map<string, KarmaConfigProcessingResult>();
161216
const migratedProjects: string[] = [];
162217
const tsConfigsToUpdate = new Set<string>();
@@ -223,7 +278,7 @@ function updateProjects(tree: Tree, context: SchematicContext): Rule {
223278
// Store custom build options to move to a new build configuration if needed
224279
const customBuildOptions: Record<string, Record<string, json.JsonValue | undefined>> = {};
225280

226-
const projectNeedsCoverage = await processTestTargetOptions(
281+
const projectCoverageInfo = await processTestTargetOptions(
227282
testTarget,
228283
projectName,
229284
context,
@@ -234,8 +289,11 @@ function updateProjects(tree: Tree, context: SchematicContext): Rule {
234289
manualMigrationFiles,
235290
);
236291

237-
if (projectNeedsCoverage) {
292+
if (projectCoverageInfo.needsCoverage) {
238293
needsCoverage = true;
294+
if (projectCoverageInfo.needsIstanbul) {
295+
needsIstanbul = true;
296+
}
239297
}
240298

241299
// If we have custom build options, create testing configurations
@@ -297,36 +355,13 @@ function updateProjects(tree: Tree, context: SchematicContext): Rule {
297355
updateTsConfigTypes(tree, tsConfigsToUpdate, context);
298356

299357
// Log summary
300-
context.logger.info('\n--- Karma to Vitest Migration Summary ---');
301-
context.logger.info(`Projects migrated: ${migratedProjects.length}`);
302-
if (migratedProjects.length > 0) {
303-
context.logger.info(` - ${migratedProjects.join(', ')}`);
304-
}
305-
context.logger.info(`Projects skipped (non-applications): ${skippedNonApplications.length}`);
306-
if (skippedNonApplications.length > 0) {
307-
context.logger.info(` - ${skippedNonApplications.join(', ')}`);
308-
}
309-
context.logger.info(
310-
`Projects skipped (missing application builder): ${skippedMissingAppBuilder.length}`,
358+
logSummary(
359+
context,
360+
migratedProjects,
361+
skippedNonApplications,
362+
skippedMissingAppBuilder,
363+
manualMigrationFiles,
311364
);
312-
if (skippedMissingAppBuilder.length > 0) {
313-
context.logger.info(` - ${skippedMissingAppBuilder.join(', ')}`);
314-
}
315-
316-
const uniqueManualFiles = [...new Set(manualMigrationFiles)];
317-
if (uniqueManualFiles.length > 0) {
318-
context.logger.warn(`\nThe following Karma configuration files require manual migration:`);
319-
for (const file of uniqueManualFiles) {
320-
context.logger.warn(` - ${file}`);
321-
}
322-
}
323-
if (migratedProjects.length > 0) {
324-
context.logger.info(
325-
`\nNote: To refactor your test files from Jasmine to Vitest, consider running the following command:` +
326-
`\n ng g @schematics/angular:refactor-jasmine-vitest <project_name>`,
327-
);
328-
}
329-
context.logger.info('-----------------------------------------\n');
330365

331366
if (migratedProjects.length > 0) {
332367
const rules = [
@@ -343,6 +378,19 @@ function updateProjects(tree: Tree, context: SchematicContext): Rule {
343378
existing: ExistingBehavior.Skip,
344379
}),
345380
);
381+
382+
if (needsIstanbul) {
383+
rules.push(
384+
addDependency(
385+
'@vitest/coverage-istanbul',
386+
latestVersions['@vitest/coverage-istanbul'],
387+
{
388+
type: DependencyType.Dev,
389+
existing: ExistingBehavior.Skip,
390+
},
391+
),
392+
);
393+
}
346394
}
347395

348396
return chain(rules);

packages/schematics/angular/utility/latest-versions/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"typescript": "~6.0.2",
2828
"vitest": "^4.0.8",
2929
"@vitest/coverage-v8": "^4.0.8",
30+
"@vitest/coverage-istanbul": "^4.0.8",
3031
"@vitest/browser-playwright": "^4.0.8",
3132
"@vitest/browser-webdriverio": "^4.0.8",
3233
"@vitest/browser-preview": "^4.0.8",

0 commit comments

Comments
 (0)