Skip to content

Commit 8264382

Browse files
crisbetothePunderWoman
authored andcommitted
fix(migrations): error in standalone migration when non-array value is used as declarations in TestBed (#54122)
Adds some logic to skip over `TestBed.configureTestingModule` calls where the `declarations` aren't initialized to an array. We can't migrate these cases, because test migrations don't have access to the Angular compiler. Previously the migration would throw a runtime error. PR Close #54122
1 parent f5a5215 commit 8264382

File tree

2 files changed

+59
-8
lines changed

2 files changed

+59
-8
lines changed

packages/core/schematics/ng-generate/standalone-migration/to-standalone.ts

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -442,15 +442,24 @@ export function findTestObjectsToMigrate(sourceFile: ts.SourceFile, typeChecker:
442442

443443
if (testBedImport || catalystImport) {
444444
sourceFile.forEachChild(function walk(node) {
445-
if (ts.isCallExpression(node) && node.arguments.length > 0 &&
445+
const isObjectLiteralCall = ts.isCallExpression(node) && node.arguments.length > 0 &&
446446
// `arguments[0]` is the testing module config.
447-
ts.isObjectLiteralExpression(node.arguments[0])) {
448-
if ((testBedImport && ts.isPropertyAccessExpression(node.expression) &&
449-
node.expression.name.text === 'configureTestingModule' &&
450-
isReferenceToImport(typeChecker, node.expression.expression, testBedImport)) ||
451-
(catalystImport && ts.isIdentifier(node.expression) &&
452-
isReferenceToImport(typeChecker, node.expression, catalystImport))) {
453-
testObjects.push(node.arguments[0]);
447+
ts.isObjectLiteralExpression(node.arguments[0]);
448+
const config = isObjectLiteralCall ? node.arguments[0] as ts.ObjectLiteralExpression : null;
449+
const isTestBedCall = isObjectLiteralCall &&
450+
(testBedImport && ts.isPropertyAccessExpression(node.expression) &&
451+
node.expression.name.text === 'configureTestingModule' &&
452+
isReferenceToImport(typeChecker, node.expression.expression, testBedImport));
453+
const isCatalystCall = isObjectLiteralCall &&
454+
(catalystImport && ts.isIdentifier(node.expression) &&
455+
isReferenceToImport(typeChecker, node.expression, catalystImport));
456+
457+
if ((isTestBedCall || isCatalystCall) && config) {
458+
const declarations = findLiteralProperty(config, 'declarations');
459+
if (declarations && ts.isPropertyAssignment(declarations) &&
460+
ts.isArrayLiteralExpression(declarations.initializer) &&
461+
declarations.initializer.elements.length > 0) {
462+
testObjects.push(config);
454463
}
455464
}
456465

packages/core/schematics/test/standalone_migration_spec.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1428,6 +1428,48 @@ describe('standalone migration', () => {
14281428
`));
14291429
});
14301430

1431+
it('should not migrate `configureTestingModule` with a non-array expression in the `declarations` field',
1432+
async () => {
1433+
const initialContent = `
1434+
import {NgModule, Component} from '@angular/core';
1435+
import {TestBed} from '@angular/core/testing';
1436+
import {ButtonModule} from './button.module';
1437+
import {MatCardModule} from '@angular/material/card';
1438+
1439+
function setup(declarations: any[], imports: any[]) {
1440+
TestBed.configureTestingModule({
1441+
declarations: declarations,
1442+
imports,
1443+
});
1444+
return TestBed.createComponent(App);
1445+
}
1446+
1447+
describe('bootstrapping an app', () => {
1448+
it('should work', () => {
1449+
const fixture = setup([App, Hello], [ButtonModule, MatCardModule]);
1450+
expect(fixture.nativeElement.innerHTML).toBe('<hello>Hello</hello>');
1451+
});
1452+
1453+
it('should work in a different way', () => {
1454+
const fixture = setup([App, Hello], [MatCardModule]);
1455+
expect(fixture.nativeElement.innerHTML).toBe('<hello>Hello</hello>');
1456+
});
1457+
});
1458+
1459+
@Component({selector: 'hello', template: 'Hello'})
1460+
class Hello {}
1461+
1462+
@Component({template: '<hello></hello>'})
1463+
class App {}
1464+
`;
1465+
1466+
writeFile('app.spec.ts', initialContent);
1467+
1468+
await runMigration('convert-to-standalone');
1469+
1470+
expect(tree.readContent('app.spec.ts')).toBe(initialContent);
1471+
});
1472+
14311473
it('should not migrate modules with a `bootstrap` array', async () => {
14321474
const initialModule = `
14331475
import {NgModule, Component} from '@angular/core';

0 commit comments

Comments
 (0)