diff --git a/packages/core/schematics/ng-generate/standalone-migration/to-standalone.ts b/packages/core/schematics/ng-generate/standalone-migration/to-standalone.ts index 2319d212bf3f6..2dcb7b9f1a4fa 100644 --- a/packages/core/schematics/ng-generate/standalone-migration/to-standalone.ts +++ b/packages/core/schematics/ng-generate/standalone-migration/to-standalone.ts @@ -442,15 +442,24 @@ export function findTestObjectsToMigrate(sourceFile: ts.SourceFile, typeChecker: if (testBedImport || catalystImport) { sourceFile.forEachChild(function walk(node) { - if (ts.isCallExpression(node) && node.arguments.length > 0 && + const isObjectLiteralCall = ts.isCallExpression(node) && node.arguments.length > 0 && // `arguments[0]` is the testing module config. - ts.isObjectLiteralExpression(node.arguments[0])) { - if ((testBedImport && ts.isPropertyAccessExpression(node.expression) && - node.expression.name.text === 'configureTestingModule' && - isReferenceToImport(typeChecker, node.expression.expression, testBedImport)) || - (catalystImport && ts.isIdentifier(node.expression) && - isReferenceToImport(typeChecker, node.expression, catalystImport))) { - testObjects.push(node.arguments[0]); + ts.isObjectLiteralExpression(node.arguments[0]); + const config = isObjectLiteralCall ? node.arguments[0] as ts.ObjectLiteralExpression : null; + const isTestBedCall = isObjectLiteralCall && + (testBedImport && ts.isPropertyAccessExpression(node.expression) && + node.expression.name.text === 'configureTestingModule' && + isReferenceToImport(typeChecker, node.expression.expression, testBedImport)); + const isCatalystCall = isObjectLiteralCall && + (catalystImport && ts.isIdentifier(node.expression) && + isReferenceToImport(typeChecker, node.expression, catalystImport)); + + if ((isTestBedCall || isCatalystCall) && config) { + const declarations = findLiteralProperty(config, 'declarations'); + if (declarations && ts.isPropertyAssignment(declarations) && + ts.isArrayLiteralExpression(declarations.initializer) && + declarations.initializer.elements.length > 0) { + testObjects.push(config); } } diff --git a/packages/core/schematics/test/standalone_migration_spec.ts b/packages/core/schematics/test/standalone_migration_spec.ts index 78fcd1e868a85..a4b37c398623d 100644 --- a/packages/core/schematics/test/standalone_migration_spec.ts +++ b/packages/core/schematics/test/standalone_migration_spec.ts @@ -1428,6 +1428,48 @@ describe('standalone migration', () => { `)); }); + it('should not migrate `configureTestingModule` with a non-array expression in the `declarations` field', + async () => { + const initialContent = ` + import {NgModule, Component} from '@angular/core'; + import {TestBed} from '@angular/core/testing'; + import {ButtonModule} from './button.module'; + import {MatCardModule} from '@angular/material/card'; + + function setup(declarations: any[], imports: any[]) { + TestBed.configureTestingModule({ + declarations: declarations, + imports, + }); + return TestBed.createComponent(App); + } + + describe('bootstrapping an app', () => { + it('should work', () => { + const fixture = setup([App, Hello], [ButtonModule, MatCardModule]); + expect(fixture.nativeElement.innerHTML).toBe('Hello'); + }); + + it('should work in a different way', () => { + const fixture = setup([App, Hello], [MatCardModule]); + expect(fixture.nativeElement.innerHTML).toBe('Hello'); + }); + }); + + @Component({selector: 'hello', template: 'Hello'}) + class Hello {} + + @Component({template: ''}) + class App {} + `; + + writeFile('app.spec.ts', initialContent); + + await runMigration('convert-to-standalone'); + + expect(tree.readContent('app.spec.ts')).toBe(initialContent); + }); + it('should not migrate modules with a `bootstrap` array', async () => { const initialModule = ` import {NgModule, Component} from '@angular/core';