feat(core): allow to pass in aot summaries also to `TestBed.configureTestingModule`

Also adds caching for summaries.

Closes #19817.
This commit is contained in:
Tobias Bosch 2017-10-27 15:45:59 -07:00 committed by Victor Berchet
parent b489259a34
commit 05d96dc507
3 changed files with 68 additions and 30 deletions

View File

@ -42,6 +42,7 @@ export class JitCompiler {
private _compiledDirectiveWrapperCache = new Map<Type, Type>(); private _compiledDirectiveWrapperCache = new Map<Type, Type>();
private _compiledNgModuleCache = new Map<Type, object>(); private _compiledNgModuleCache = new Map<Type, object>();
private _sharedStylesheetCount = 0; private _sharedStylesheetCount = 0;
private _addedAotSummaries = new Set<() => any[]>();
constructor( constructor(
private _metadataResolver: CompileMetadataResolver, private _templateParser: TemplateParser, private _metadataResolver: CompileMetadataResolver, private _templateParser: TemplateParser,
@ -74,10 +75,25 @@ export class JitCompiler {
loadAotSummaries(summaries: () => any[]) { loadAotSummaries(summaries: () => any[]) {
this.clearCache(); this.clearCache();
flattenSummaries(summaries).forEach((summary) => { this._addAotSummaries(summaries);
this._summaryResolver.addSummary( }
{symbol: summary.type.reference, metadata: null, type: summary});
}); private _addAotSummaries(fn: () => any[]) {
if (this._addedAotSummaries.has(fn)) {
return;
}
this._addedAotSummaries.add(fn);
const summaries = fn();
for (let i = 0; i < summaries.length; i++) {
const entry = summaries[i];
if (typeof entry === 'function') {
this._addAotSummaries(entry);
} else {
const summary = entry as CompileTypeSummary;
this._summaryResolver.addSummary(
{symbol: summary.type.reference, metadata: null, type: summary})
}
}
} }
hasAotSummary(ref: Type) { return !!this._summaryResolver.resolveSummary(ref); } hasAotSummary(ref: Type) { return !!this._summaryResolver.resolveSummary(ref); }
@ -200,6 +216,7 @@ export class JitCompiler {
} }
clearCache(): void { clearCache(): void {
// Note: don't clear the _addedAotSummaries, as they don't change!
this._metadataResolver.clearCache(); this._metadataResolver.clearCache();
this._compiledTemplateCache.clear(); this._compiledTemplateCache.clear();
this._compiledHostTemplateCache.clear(); this._compiledHostTemplateCache.clear();
@ -335,25 +352,6 @@ function assertComponent(meta: CompileDirectiveMetadata) {
} }
} }
function flattenSummaries(
fn: () => any[], out: CompileTypeSummary[] = [],
seen = new Set<() => any[]>()): CompileTypeSummary[] {
if (seen.has(fn)) {
return out;
}
seen.add(fn);
const summaries = fn();
for (let i = 0; i < summaries.length; i++) {
const entry = summaries[i];
if (typeof entry === 'function') {
flattenSummaries(entry, out, seen);
} else {
out.push(entry);
}
}
return out;
}
function createOutputContext(): OutputContext { function createOutputContext(): OutputContext {
const importExpr = (symbol: any) => const importExpr = (symbol: any) =>
ir.importExpr({name: identifierName(symbol), moduleName: null, runtime: symbol}); ir.importExpr({name: identifierName(symbol), moduleName: null, runtime: symbol});

View File

@ -15,6 +15,7 @@ import {TestBed, async, getTestBed} from '@angular/core/testing';
export function main() { export function main() {
describe('Jit Summaries', () => { describe('Jit Summaries', () => {
let instances: Map<any, Base>; let instances: Map<any, Base>;
let summaries: () => any[];
class SomeDep {} class SomeDep {}
@ -69,7 +70,7 @@ export function main() {
TestBed.configureCompiler({providers: [{provide: ResourceLoader, useValue: resourceLoader}]}); TestBed.configureCompiler({providers: [{provide: ResourceLoader, useValue: resourceLoader}]});
TestBed.configureTestingModule({imports: [SomeModule], providers: [SomeDep]}); TestBed.configureTestingModule({imports: [SomeModule], providers: [SomeDep]});
TestBed.compileComponents().then(() => { let summariesPromise = TestBed.compileComponents().then(() => {
const metadataResolver = TestBed.get(CompileMetadataResolver) as CompileMetadataResolver; const metadataResolver = TestBed.get(CompileMetadataResolver) as CompileMetadataResolver;
const summaries = [ const summaries = [
metadataResolver.getNgModuleSummary(SomeModule), metadataResolver.getNgModuleSummary(SomeModule),
@ -83,10 +84,12 @@ export function main() {
metadataResolver.getInjectableSummary(SomeService) metadataResolver.getInjectableSummary(SomeService)
]; ];
clearMetadata(); clearMetadata();
resetTestEnvironmentWithSummaries(() => summaries); TestBed.resetTestingModule();
return () => summaries;
}); });
resourceLoader.flush(); resourceLoader.flush();
return summariesPromise;
} }
function setMetadata(resourceLoader: MockResourceLoader) { function setMetadata(resourceLoader: MockResourceLoader) {
@ -123,12 +126,14 @@ export function main() {
beforeEach(async(() => { beforeEach(async(() => {
instances = new Map<any, any>(); instances = new Map<any, any>();
createSummaries(); createSummaries().then(s => summaries = s);
})); }));
afterEach(() => { resetTestEnvironmentWithSummaries(); }); afterEach(() => { resetTestEnvironmentWithSummaries(); });
it('should use directive metadata from summaries', () => { it('should use directive metadata from summaries', () => {
resetTestEnvironmentWithSummaries(summaries);
@Component({template: '<div someDir></div>'}) @Component({template: '<div someDir></div>'})
class TestComp { class TestComp {
} }
@ -140,6 +145,8 @@ export function main() {
}); });
it('should use pipe metadata from summaries', () => { it('should use pipe metadata from summaries', () => {
resetTestEnvironmentWithSummaries(summaries);
@Component({template: '{{1 | somePipe}}'}) @Component({template: '{{1 | somePipe}}'})
class TestComp { class TestComp {
} }
@ -150,6 +157,8 @@ export function main() {
}); });
it('should use Service metadata from summaries', () => { it('should use Service metadata from summaries', () => {
resetTestEnvironmentWithSummaries(summaries);
TestBed.configureTestingModule({ TestBed.configureTestingModule({
providers: [SomeService, SomeDep], providers: [SomeService, SomeDep],
}); });
@ -158,6 +167,8 @@ export function main() {
}); });
it('should use NgModule metadata from summaries', () => { it('should use NgModule metadata from summaries', () => {
resetTestEnvironmentWithSummaries(summaries);
TestBed TestBed
.configureTestingModule( .configureTestingModule(
{providers: [SomeDep], declarations: [TestComp3], imports: [SomeModule]}) {providers: [SomeDep], declarations: [TestComp3], imports: [SomeModule]})
@ -170,12 +181,16 @@ export function main() {
}); });
it('should allow to create private components from imported NgModule summaries', () => { it('should allow to create private components from imported NgModule summaries', () => {
resetTestEnvironmentWithSummaries(summaries);
TestBed.configureTestingModule({providers: [SomeDep], imports: [SomeModule]}) TestBed.configureTestingModule({providers: [SomeDep], imports: [SomeModule]})
.createComponent(SomePrivateComponent); .createComponent(SomePrivateComponent);
expectInstanceCreated(SomePrivateComponent); expectInstanceCreated(SomePrivateComponent);
}); });
it('should throw when trying to mock a type with a summary', () => { it('should throw when trying to mock a type with a summary', () => {
resetTestEnvironmentWithSummaries(summaries);
TestBed.resetTestingModule(); TestBed.resetTestingModule();
expect(() => TestBed.overrideComponent(SomePrivateComponent, {add: {}}).compileComponents()) expect(() => TestBed.overrideComponent(SomePrivateComponent, {add: {}}).compileComponents())
.toThrowError( .toThrowError(
@ -190,5 +205,22 @@ export function main() {
expect(() => TestBed.overrideModule(SomeModule, {add: {}}).compileComponents()) expect(() => TestBed.overrideModule(SomeModule, {add: {}}).compileComponents())
.toThrowError('SomeModule was AOT compiled, so its metadata cannot be changed.'); .toThrowError('SomeModule was AOT compiled, so its metadata cannot be changed.');
}); });
it('should allow to add summaries via configureTestingModule', () => {
resetTestEnvironmentWithSummaries();
@Component({template: '<div someDir></div>'})
class TestComp {
}
TestBed
.configureTestingModule({
providers: [SomeDep],
declarations: [TestComp, SomeDirective],
aotSummaries: summaries
})
.createComponent(TestComp);
expectInstanceCreated(SomeDirective);
});
}); });
} }

View File

@ -45,6 +45,7 @@ export type TestModuleMetadata = {
declarations?: any[], declarations?: any[],
imports?: any[], imports?: any[],
schemas?: Array<SchemaMetadata|any[]>, schemas?: Array<SchemaMetadata|any[]>,
aotSummaries?: () => any[],
}; };
/** /**
@ -205,7 +206,8 @@ export class TestBed implements Injector {
private _schemas: Array<SchemaMetadata|any[]> = []; private _schemas: Array<SchemaMetadata|any[]> = [];
private _activeFixtures: ComponentFixture<any>[] = []; private _activeFixtures: ComponentFixture<any>[] = [];
private _aotSummaries: () => any[] = () => []; private _testEnvAotSummaries: () => any[] = () => [];
private _aotSummaries: Array<() => any[]> = [];
platform: PlatformRef = null !; platform: PlatformRef = null !;
@ -232,7 +234,7 @@ export class TestBed implements Injector {
this.platform = platform; this.platform = platform;
this.ngModule = ngModule; this.ngModule = ngModule;
if (aotSummaries) { if (aotSummaries) {
this._aotSummaries = aotSummaries; this._testEnvAotSummaries = aotSummaries;
} }
} }
@ -245,11 +247,12 @@ export class TestBed implements Injector {
this.resetTestingModule(); this.resetTestingModule();
this.platform = null !; this.platform = null !;
this.ngModule = null !; this.ngModule = null !;
this._aotSummaries = () => []; this._testEnvAotSummaries = () => [];
} }
resetTestingModule() { resetTestingModule() {
clearProviderOverrides(); clearProviderOverrides();
this._aotSummaries = [];
this._compiler = null !; this._compiler = null !;
this._moduleOverrides = []; this._moduleOverrides = [];
this._componentOverrides = []; this._componentOverrides = [];
@ -293,6 +296,9 @@ export class TestBed implements Injector {
if (moduleDef.schemas) { if (moduleDef.schemas) {
this._schemas.push(...moduleDef.schemas); this._schemas.push(...moduleDef.schemas);
} }
if (moduleDef.aotSummaries) {
this._aotSummaries.push(moduleDef.aotSummaries);
}
} }
compileComponents(): Promise<any> { compileComponents(): Promise<any> {
@ -350,7 +356,9 @@ export class TestBed implements Injector {
const compilerFactory: TestingCompilerFactory = const compilerFactory: TestingCompilerFactory =
this.platform.injector.get(TestingCompilerFactory); this.platform.injector.get(TestingCompilerFactory);
this._compiler = compilerFactory.createTestingCompiler(this._compilerOptions); this._compiler = compilerFactory.createTestingCompiler(this._compilerOptions);
this._compiler.loadAotSummaries(this._aotSummaries); for (const summary of [this._testEnvAotSummaries, ...this._aotSummaries]) {
this._compiler.loadAotSummaries(summary);
}
this._moduleOverrides.forEach((entry) => this._compiler.overrideModule(entry[0], entry[1])); this._moduleOverrides.forEach((entry) => this._compiler.overrideModule(entry[0], entry[1]));
this._componentOverrides.forEach( this._componentOverrides.forEach(
(entry) => this._compiler.overrideComponent(entry[0], entry[1])); (entry) => this._compiler.overrideComponent(entry[0], entry[1]));