refactor(ivy): check metadata presence before compiling Type in R3TestBed (#34204)

Prior to this commit, there was no check in R3TestBed to verify that metadata is resolved using a given Type. That leads to some cryptic error messages (when TestBed tries to compile a Type without having metadata) in case TestBed override functions receive unexpected Types (for example a Directive is used in `TestBed.overrideComponent` call). This commit adds the necessary checks to verify metadata presence before TestBed tries to (re)compile a Type.

PR Close #34204
This commit is contained in:
Andrew Kushnir
2019-12-02 21:51:19 -08:00
committed by Miško Hevery
parent 41ea3c214a
commit 668692598b
2 changed files with 49 additions and 10 deletions

View File

@ -129,10 +129,10 @@ export class R3TestBedCompiler {
this.resolvers.module.addOverride(ngModule, override);
const metadata = this.resolvers.module.resolve(ngModule);
if (metadata === null) {
throw new Error(`${ngModule.name} is not an @NgModule or is missing metadata`);
throw invalidTypeError(ngModule.name, 'NgModule');
}
this.recompileNgModule(ngModule);
this.recompileNgModule(ngModule, metadata);
// At this point, the module has a valid module def (ɵmod), but the override may have introduced
// new declarations or imported modules. Ingest any possible new types and add them to the
@ -306,7 +306,10 @@ export class R3TestBedCompiler {
let needsAsyncResources = false;
this.pendingComponents.forEach(declaration => {
needsAsyncResources = needsAsyncResources || isComponentDefPendingResolution(declaration);
const metadata = this.resolvers.component.resolve(declaration) !;
const metadata = this.resolvers.component.resolve(declaration);
if (metadata === null) {
throw invalidTypeError(declaration.name, 'Component');
}
this.maybeStoreNgDef(NG_COMP_DEF, declaration);
compileComponent(declaration, metadata);
});
@ -314,13 +317,19 @@ export class R3TestBedCompiler {
this.pendingDirectives.forEach(declaration => {
const metadata = this.resolvers.directive.resolve(declaration);
if (metadata === null) {
throw invalidTypeError(declaration.name, 'Directive');
}
this.maybeStoreNgDef(NG_DIR_DEF, declaration);
compileDirective(declaration, metadata);
});
this.pendingDirectives.clear();
this.pendingPipes.forEach(declaration => {
const metadata = this.resolvers.pipe.resolve(declaration) !;
const metadata = this.resolvers.pipe.resolve(declaration);
if (metadata === null) {
throw invalidTypeError(declaration.name, 'Pipe');
}
this.maybeStoreNgDef(NG_PIPE_DEF, declaration);
compilePipe(declaration, metadata);
});
@ -434,11 +443,7 @@ export class R3TestBedCompiler {
}
}
private recompileNgModule(ngModule: Type<any>): void {
const metadata = this.resolvers.module.resolve(ngModule);
if (metadata === null) {
throw new Error(`Unable to resolve metadata for NgModule: ${ngModule.name}`);
}
private recompileNgModule(ngModule: Type<any>, metadata: NgModule): void {
// Cache the initial ngModuleDef as it will be overwritten.
this.maybeStoreNgDef(NG_MOD_DEF, ngModule);
this.maybeStoreNgDef(NG_INJ_DEF, ngModule);
@ -751,6 +756,10 @@ function forEachRight<T>(values: T[], fn: (value: T, idx: number) => void): void
}
}
function invalidTypeError(name: string, expectedType: string): Error {
return new Error(`${name} class doesn't have @${expectedType} decorator or is missing metadata.`);
}
class R3TestCompiler implements Compiler {
constructor(private testBed: R3TestBedCompiler) {}