diff --git a/packages/schematics/angular/migrations/migrate-karma-to-vitest/migration.ts b/packages/schematics/angular/migrations/migrate-karma-to-vitest/migration.ts index fc5b11fb5eb0..5a10f4a6f8a3 100644 --- a/packages/schematics/angular/migrations/migrate-karma-to-vitest/migration.ts +++ b/packages/schematics/angular/migrations/migrate-karma-to-vitest/migration.ts @@ -34,12 +34,16 @@ async function processTestTargetOptions( const configKey = configName || ''; if (!customBuildOptions[configKey]) { - // Match Karma behavior where AOT was disabled by default - customBuildOptions[configKey] = { - aot: false, - optimization: false, - extractLicenses: false, - }; + // Match Karma behavior where AOT was disabled by default. Configurations inherit these + // from the base options when present, so seeding them would override explicit base values. + customBuildOptions[configKey] = + configKey && testTarget.options + ? {} + : { + aot: false, + optimization: false, + extractLicenses: false, + }; } // Collect custom build options @@ -306,9 +310,16 @@ function updateProjects(tree: Tree, context: SchematicContext): Rule { for (const [configKey, configOptions] of Object.entries(customBuildOptions)) { const finalConfig: Record = {}; + // A test configuration inherits the base test options, so its build configuration must + // include the build options moved from them, with the configuration's own values on top. + const mergedOptions = + configKey && customBuildOptions[''] + ? { ...customBuildOptions[''], ...configOptions } + : configOptions; + // Omit options that already have the same value in the base build options. // Using isDeepStrictEqual for a deep comparison of arrays and objects. - for (const [key, value] of Object.entries(configOptions)) { + for (const [key, value] of Object.entries(mergedOptions)) { if (!isDeepStrictEqual(value, baseOptions[key])) { finalConfig[key] = value; } diff --git a/packages/schematics/angular/migrations/migrate-karma-to-vitest/migration_spec.ts b/packages/schematics/angular/migrations/migrate-karma-to-vitest/migration_spec.ts index fb6737b77d26..1989ec8777c7 100644 --- a/packages/schematics/angular/migrations/migrate-karma-to-vitest/migration_spec.ts +++ b/packages/schematics/angular/migrations/migrate-karma-to-vitest/migration_spec.ts @@ -128,6 +128,61 @@ describe('Migration from Karma to Vitest unit-test builder', () => { expect(newProjects.app.targets.test.configurations.ci.assets).toBeUndefined(); }); + it('should keep base custom build options in per configuration testing configurations', async () => { + const { projects } = tree.readJson('/angular.json') as any; + projects.app.targets.test.options.styles = ['src/test-styles.scss']; + projects.app.targets.test.configurations = { + ci: { + watch: false, + }, + }; + tree.overwrite('/angular.json', JSON.stringify({ version: 1, projects })); + + const newTree = await schematicRunner.runSchematic('migrate-karma-to-vitest', {}, tree); + const { projects: newProjects } = newTree.readJson('/angular.json') as any; + + expect(newProjects.app.targets.build.configurations.testing.styles).toEqual([ + 'src/test-styles.scss', + ]); + expect(newProjects.app.targets.test.configurations.ci.buildTarget).toBe(':build:testing-ci'); + expect(newProjects.app.targets.build.configurations['testing-ci'].styles).toEqual([ + 'src/test-styles.scss', + ]); + }); + + it('should keep explicit base Karma build defaults in per configuration testing configurations', async () => { + const { projects } = tree.readJson('/angular.json') as any; + projects.app.targets.test.options.styles = ['src/test-styles.scss']; + projects.app.targets.test.options.aot = true; + projects.app.targets.test.configurations = { + ci: { + watch: false, + }, + }; + tree.overwrite('/angular.json', JSON.stringify({ version: 1, projects })); + + const newTree = await schematicRunner.runSchematic('migrate-karma-to-vitest', {}, tree); + const { projects: newProjects } = newTree.readJson('/angular.json') as any; + + expect(newProjects.app.targets.build.configurations.testing.aot).toBeTrue(); + expect(newProjects.app.targets.build.configurations['testing-ci'].aot).toBeTrue(); + }); + + it('should apply Karma build defaults to testing configurations without base test options', async () => { + const { projects } = tree.readJson('/angular.json') as any; + projects.app.targets.test.configurations = { + ci: projects.app.targets.test.options, + }; + delete projects.app.targets.test.options; + tree.overwrite('/angular.json', JSON.stringify({ version: 1, projects })); + + const newTree = await schematicRunner.runSchematic('migrate-karma-to-vitest', {}, tree); + const { projects: newProjects } = newTree.readJson('/angular.json') as any; + + expect(newProjects.app.targets.test.configurations.ci.buildTarget).toBe(':build:testing-ci'); + expect(newProjects.app.targets.build.configurations['testing-ci'].aot).toBeFalse(); + }); + it('should omit testing configuration if options match base build options', async () => { const { projects } = tree.readJson('/angular.json') as any; projects.app.targets.build.options.aot = false;