feat(ivy): implement compileComponents
method for TestBedRender3
(#27778)
The implementation of the `compileComponents` method for `TestBedRender3` was missing. We now pass each component through `resolveComponentResources` when `TestBed.compileComponents` is called so that `templateUrl` and `styleUrls` can be resolved asynchronously and used once `TestBed.createComponent` is called. The component's metadata are overriden in `TestBed` instead of mutating the original metadata like this is the case outside of TestBed. The reason for that is that we need to ensure that we didn't mutate anything so that the following tests can run with the same original metadata, otherwise we it could trigger or hide some errors. FW-553 #resolve PR Close #27778
This commit is contained in:

committed by
Andrew Kushnir

parent
3a31a2795e
commit
29bff0f02e
@ -12,6 +12,7 @@ ng_module(
|
||||
module_name = "@angular/core/testing",
|
||||
deps = [
|
||||
"//packages:types",
|
||||
"//packages/compiler",
|
||||
"//packages/core",
|
||||
"@ngdeps//@types/jasmine",
|
||||
"@ngdeps//zone.js",
|
||||
|
@ -34,7 +34,6 @@ import {
|
||||
ɵNG_PIPE_DEF as NG_PIPE_DEF,
|
||||
ɵNgModuleDef as NgModuleDef,
|
||||
ɵNgModuleFactory as R3NgModuleFactory,
|
||||
ɵNgModuleTransitiveScopes as NgModuleTransitiveScopes,
|
||||
ɵNgModuleType as NgModuleType,
|
||||
ɵRender3ComponentFactory as ComponentFactory,
|
||||
ɵRender3NgModuleRef as NgModuleRef,
|
||||
@ -46,10 +45,16 @@ import {
|
||||
ɵflushModuleScopingQueueAsMuchAsPossible as flushModuleScopingQueueAsMuchAsPossible,
|
||||
ɵpatchComponentDefWithScope as patchComponentDefWithScope,
|
||||
ɵresetCompiledComponents as resetCompiledComponents,
|
||||
ɵstringify as stringify, ɵtransitiveScopesFor as transitiveScopesFor,
|
||||
ɵstringify as stringify,
|
||||
ɵtransitiveScopesFor as transitiveScopesFor,
|
||||
CompilerOptions,
|
||||
StaticProvider,
|
||||
COMPILER_OPTIONS,
|
||||
} from '@angular/core';
|
||||
// clang-format on
|
||||
import {ResourceLoader} from '@angular/compiler';
|
||||
|
||||
import {clearResolutionOfComponentResourcesQueue, resolveComponentResources} from '../../src/metadata/resource_loading';
|
||||
import {ComponentFixture} from './component_fixture';
|
||||
import {MetadataOverride} from './metadata_override';
|
||||
import {ComponentResolver, DirectiveResolver, NgModuleResolver, PipeResolver, Resolver} from './resolvers';
|
||||
@ -229,6 +234,7 @@ export class TestBedRender3 implements Injector, TestBed {
|
||||
private _directiveOverrides: [Type<any>, MetadataOverride<Directive>][] = [];
|
||||
private _pipeOverrides: [Type<any>, MetadataOverride<Pipe>][] = [];
|
||||
private _providerOverrides: Provider[] = [];
|
||||
private _compilerProviders: StaticProvider[] = [];
|
||||
private _rootProviderOverrides: Provider[] = [];
|
||||
private _providerOverridesByToken: Map<any, Provider[]> = new Map();
|
||||
private _templateOverrides: Map<Type<any>, string> = new Map();
|
||||
@ -236,12 +242,14 @@ export class TestBedRender3 implements Injector, TestBed {
|
||||
|
||||
// test module configuration
|
||||
private _providers: Provider[] = [];
|
||||
private _compilerOptions: CompilerOptions[] = [];
|
||||
private _declarations: Array<Type<any>|any[]|any> = [];
|
||||
private _imports: Array<Type<any>|any[]|any> = [];
|
||||
private _schemas: Array<SchemaMetadata|any[]> = [];
|
||||
|
||||
private _activeFixtures: ComponentFixture<any>[] = [];
|
||||
|
||||
private _compilerInjector: Injector = null !;
|
||||
private _moduleRef: NgModuleRef<any> = null !;
|
||||
private _testModuleType: NgModuleType<any> = null !;
|
||||
|
||||
@ -302,12 +310,14 @@ export class TestBedRender3 implements Injector, TestBed {
|
||||
|
||||
// reset test module config
|
||||
this._providers = [];
|
||||
this._compilerOptions = [];
|
||||
this._declarations = [];
|
||||
this._imports = [];
|
||||
this._schemas = [];
|
||||
this._moduleRef = null !;
|
||||
this._testModuleType = null !;
|
||||
|
||||
this._compilerInjector = null !;
|
||||
this._instantiated = false;
|
||||
this._activeFixtures.forEach((fixture) => {
|
||||
try {
|
||||
@ -326,6 +336,7 @@ export class TestBedRender3 implements Injector, TestBed {
|
||||
Object.defineProperty(type, value[0], value[1]);
|
||||
});
|
||||
this._initiaNgDefs.clear();
|
||||
clearResolutionOfComponentResourcesQueue();
|
||||
}
|
||||
|
||||
configureCompiler(config: {providers?: any[]; useJit?: boolean;}): void {
|
||||
@ -335,6 +346,7 @@ export class TestBedRender3 implements Injector, TestBed {
|
||||
|
||||
if (config.providers) {
|
||||
this._providerOverrides.push(...config.providers);
|
||||
this._compilerProviders.push(...config.providers);
|
||||
}
|
||||
}
|
||||
|
||||
@ -355,9 +367,37 @@ export class TestBedRender3 implements Injector, TestBed {
|
||||
}
|
||||
|
||||
compileComponents(): Promise<any> {
|
||||
// assume for now that components don't use templateUrl / stylesUrl to unblock further testing
|
||||
// TODO(pk): plug into the ivy's resource fetching pipeline
|
||||
return Promise.resolve();
|
||||
const resolvers = this._getResolvers();
|
||||
const declarations: Type<any>[] = flatten(this._declarations || EMPTY_ARRAY, resolveForwardRef);
|
||||
|
||||
const componentOverrides: [Type<any>, Component][] = [];
|
||||
// Compile the components declared by this module
|
||||
declarations.forEach(declaration => {
|
||||
const component = resolvers.component.resolve(declaration);
|
||||
if (component) {
|
||||
// We make a copy of the metadata to ensure that we don't mutate the original metadata
|
||||
const metadata = {...component};
|
||||
compileComponent(declaration, metadata);
|
||||
componentOverrides.push([declaration, metadata]);
|
||||
}
|
||||
});
|
||||
|
||||
let resourceLoader: ResourceLoader;
|
||||
|
||||
return resolveComponentResources(url => {
|
||||
if (!resourceLoader) {
|
||||
resourceLoader = this.compilerInjector.get(ResourceLoader);
|
||||
}
|
||||
return Promise.resolve(resourceLoader.get(url));
|
||||
})
|
||||
.then(() => {
|
||||
componentOverrides.forEach((override: [Type<any>, Component]) => {
|
||||
// Once resolved, we override the existing metadata, ensuring that the resolved
|
||||
// resources
|
||||
// are only available until the next TestBed reset (when `resetTestingModule` is called)
|
||||
this.overrideComponent(override[0], {set: override[1]});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
get(token: any, notFoundValue: any = Injector.THROW_IF_NOT_FOUND): any {
|
||||
@ -549,6 +589,30 @@ export class TestBedRender3 implements Injector, TestBed {
|
||||
return DynamicTestModule as NgModuleType;
|
||||
}
|
||||
|
||||
get compilerInjector(): Injector {
|
||||
if (this._compilerInjector !== undefined) {
|
||||
this._compilerInjector;
|
||||
}
|
||||
|
||||
const providers: StaticProvider[] = [];
|
||||
const compilerOptions = this.platform.injector.get(COMPILER_OPTIONS);
|
||||
compilerOptions.forEach(opts => {
|
||||
if (opts.providers) {
|
||||
providers.push(opts.providers);
|
||||
}
|
||||
});
|
||||
providers.push(...this._compilerProviders);
|
||||
|
||||
// TODO(ocombe): make this work with an Injector directly instead of creating a module for it
|
||||
@NgModule({providers})
|
||||
class CompilerModule {
|
||||
}
|
||||
|
||||
const CompilerModuleFactory = new R3NgModuleFactory(CompilerModule);
|
||||
this._compilerInjector = CompilerModuleFactory.create(this.platform.injector).injector;
|
||||
return this._compilerInjector;
|
||||
}
|
||||
|
||||
private _getMetaWithOverrides(meta: Component|Directive|NgModule, type?: Type<any>) {
|
||||
const overrides: {providers?: any[], template?: string} = {};
|
||||
if (meta.providers && meta.providers.length) {
|
||||
@ -659,19 +723,6 @@ export function _getTestBedRender3(): TestBedRender3 {
|
||||
return testBed = testBed || new TestBedRender3();
|
||||
}
|
||||
|
||||
const OWNER_MODULE = '__NG_MODULE__';
|
||||
/**
|
||||
* This function clears the OWNER_MODULE property from the Types. This is set in
|
||||
* r3/jit/modules.ts. It is common for the same Type to be compiled in different tests. If we don't
|
||||
* clear this we will get errors which will complain that the same Component/Directive is in more
|
||||
* than one NgModule.
|
||||
*/
|
||||
function clearNgModules(type: Type<any>) {
|
||||
if (type.hasOwnProperty(OWNER_MODULE)) {
|
||||
(type as any)[OWNER_MODULE] = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function flatten<T>(values: any[], mapFn?: (value: T) => any): T[] {
|
||||
const out: T[] = [];
|
||||
values.forEach(value => {
|
||||
|
Reference in New Issue
Block a user