Skip to content

Commit

Permalink
fix(migrations): error in standalone migration when non-array value i…
Browse files Browse the repository at this point in the history
…s 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
  • Loading branch information
crisbeto authored and thePunderWoman committed Jan 29, 2024
1 parent f5a5215 commit 8264382
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 8 deletions.
Expand Up @@ -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);
}
}

Expand Down
42 changes: 42 additions & 0 deletions packages/core/schematics/test/standalone_migration_spec.ts
Expand Up @@ -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>Hello</hello>');
});
it('should work in a different way', () => {
const fixture = setup([App, Hello], [MatCardModule]);
expect(fixture.nativeElement.innerHTML).toBe('<hello>Hello</hello>');
});
});
@Component({selector: 'hello', template: 'Hello'})
class Hello {}
@Component({template: '<hello></hello>'})
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';
Expand Down

0 comments on commit 8264382

Please sign in to comment.