510 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			510 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import {
 | |
|   ddescribe,
 | |
|   describe,
 | |
|   xdescribe,
 | |
|   it,
 | |
|   iit,
 | |
|   xit,
 | |
|   expect,
 | |
|   beforeEach,
 | |
|   afterEach,
 | |
|   AsyncTestCompleter,
 | |
|   inject,
 | |
|   beforeEachProviders
 | |
| } from 'angular2/testing_internal';
 | |
| 
 | |
| import {PromiseWrapper} from 'angular2/src/facade/async';
 | |
| import {Type, isPresent, isBlank, stringify, isString, IS_DART} from 'angular2/src/facade/lang';
 | |
| import {
 | |
|   MapWrapper,
 | |
|   SetWrapper,
 | |
|   ListWrapper,
 | |
|   StringMapWrapper
 | |
| } from 'angular2/src/facade/collection';
 | |
| import {RuntimeMetadataResolver} from 'angular2/src/compiler/runtime_metadata';
 | |
| import {
 | |
|   TemplateCompiler,
 | |
|   NormalizedComponentWithViewDirectives
 | |
| } from 'angular2/src/compiler/template_compiler';
 | |
| import {CompileDirectiveMetadata} from 'angular2/src/compiler/directive_metadata';
 | |
| import {evalModule} from './eval_module';
 | |
| import {SourceModule, moduleRef} from 'angular2/src/compiler/source_module';
 | |
| import {XHR} from 'angular2/src/compiler/xhr';
 | |
| import {MockXHR} from 'angular2/src/compiler/xhr_mock';
 | |
| import {SpyRootRenderer, SpyRenderer, SpyAppViewManager} from '../core/spies';
 | |
| import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
 | |
| import {AppView, AppProtoView} from 'angular2/src/core/linker/view';
 | |
| import {AppElement} from 'angular2/src/core/linker/element';
 | |
| import {Locals, ChangeDetectorGenConfig} from 'angular2/src/core/change_detection/change_detection';
 | |
| 
 | |
| import {Component, Directive, provide, RenderComponentType} from 'angular2/core';
 | |
| 
 | |
| import {TEST_PROVIDERS} from './test_bindings';
 | |
| import {
 | |
|   codeGenValueFn,
 | |
|   codeGenFnHeader,
 | |
|   codeGenExportVariable,
 | |
|   MODULE_SUFFIX
 | |
| } from 'angular2/src/compiler/util';
 | |
| import {PipeTransform, WrappedValue, Injectable, Pipe} from 'angular2/core';
 | |
| 
 | |
| 
 | |
| // Attention: This path has to point to this test file!
 | |
| const THIS_MODULE_ID = 'angular2/test/compiler/template_compiler_spec';
 | |
| var THIS_MODULE_REF = moduleRef(`package:${THIS_MODULE_ID}${MODULE_SUFFIX}`);
 | |
| var REFLECTOR_MODULE_REF =
 | |
|     moduleRef(`package:angular2/src/core/reflection/reflection${MODULE_SUFFIX}`);
 | |
| var REFLECTION_CAPS_MODULE_REF =
 | |
|     moduleRef(`package:angular2/src/core/reflection/reflection_capabilities${MODULE_SUFFIX}`);
 | |
| 
 | |
| export function main() {
 | |
|   // Dart's isolate support is broken, and these tests will be obsolote soon with
 | |
|   // https://github.com/angular/angular/issues/6270
 | |
|   if (IS_DART) {
 | |
|     return;
 | |
|   }
 | |
|   describe('TemplateCompiler', () => {
 | |
|     var compiler: TemplateCompiler;
 | |
|     var runtimeMetadataResolver: RuntimeMetadataResolver;
 | |
| 
 | |
|     beforeEachProviders(() => TEST_PROVIDERS);
 | |
|     beforeEach(inject([TemplateCompiler, RuntimeMetadataResolver],
 | |
|                       (_compiler, _runtimeMetadataResolver) => {
 | |
|                         compiler = _compiler;
 | |
|                         runtimeMetadataResolver = _runtimeMetadataResolver;
 | |
|                       }));
 | |
| 
 | |
|     describe('compile templates', () => {
 | |
| 
 | |
|       function runTests(compile: (components: Type[]) => Promise<any[]>) {
 | |
|         it('should throw for non components', inject([AsyncTestCompleter], (async) => {
 | |
|              PromiseWrapper.catchError(
 | |
|                  PromiseWrapper.wrap(() => compile([NonComponent])), (error): any => {
 | |
|                    expect(error.message)
 | |
|                        .toEqual(
 | |
|                            `Could not compile '${stringify(NonComponent)}' because it is not a component.`);
 | |
|                    async.done();
 | |
|                  });
 | |
|            }));
 | |
| 
 | |
|         it('should compile host components', inject([AsyncTestCompleter], (async) => {
 | |
|              compile([CompWithBindingsAndStylesAndPipes])
 | |
|                  .then((humanizedView) => {
 | |
|                    expect(humanizedView['styles']).toEqual([]);
 | |
|                    expect(humanizedView['elements']).toEqual(['<comp-a>']);
 | |
|                    expect(humanizedView['pipes']).toEqual({});
 | |
|                    expect(humanizedView['cd']).toEqual(['prop(title)=someHostValue']);
 | |
|                    async.done();
 | |
|                  });
 | |
|            }));
 | |
| 
 | |
|         it('should compile nested components', inject([AsyncTestCompleter], (async) => {
 | |
|              compile([CompWithBindingsAndStylesAndPipes])
 | |
|                  .then((humanizedView) => {
 | |
|                    var componentView = humanizedView['componentViews'][0];
 | |
|                    expect(componentView['styles']).toEqual(['div {color: red}']);
 | |
|                    expect(componentView['elements']).toEqual(['<a>']);
 | |
|                    expect(componentView['pipes']).toEqual({'uppercase': stringify(UpperCasePipe)});
 | |
|                    expect(componentView['cd']).toEqual(['prop(href)=SOMECTXVALUE']);
 | |
| 
 | |
|                    async.done();
 | |
|                  });
 | |
|            }));
 | |
| 
 | |
|         it('should compile components at various nesting levels',
 | |
|            inject([AsyncTestCompleter], (async) => {
 | |
|              compile([CompWith2NestedComps, Comp1, Comp2])
 | |
|                  .then((humanizedView) => {
 | |
|                    expect(humanizedView['elements']).toEqual(['<comp-with-2nested>']);
 | |
|                    expect(humanizedView['componentViews'][0]['elements'])
 | |
|                        .toEqual(['<comp1>', '<comp2>']);
 | |
|                    expect(humanizedView['componentViews'][0]['componentViews'][0]['elements'])
 | |
|                        .toEqual(['<a>', '<comp2>']);
 | |
|                    expect(humanizedView['componentViews'][0]['componentViews'][1]['elements'])
 | |
|                        .toEqual(['<b>']);
 | |
| 
 | |
|                    async.done();
 | |
|                  });
 | |
|            }));
 | |
| 
 | |
|         it('should compile recursive components', inject([AsyncTestCompleter], (async) => {
 | |
|              compile([TreeComp])
 | |
|                  .then((humanizedView) => {
 | |
|                    expect(humanizedView['elements']).toEqual(['<tree>']);
 | |
|                    expect(humanizedView['componentViews'][0]['embeddedViews'][0]['elements'])
 | |
|                        .toEqual(['<tree>']);
 | |
|                    expect(humanizedView['componentViews'][0]['embeddedViews'][0]['componentViews']
 | |
|                                        [0]['embeddedViews'][0]['elements'])
 | |
|                        .toEqual(['<tree>']);
 | |
| 
 | |
|                    async.done();
 | |
|                  });
 | |
|            }));
 | |
| 
 | |
|         it('should compile embedded templates', inject([AsyncTestCompleter], (async) => {
 | |
|              compile([CompWithEmbeddedTemplate])
 | |
|                  .then((humanizedView) => {
 | |
|                    var embeddedView = humanizedView['componentViews'][0]['embeddedViews'][0];
 | |
|                    expect(embeddedView['elements']).toEqual(['<a>']);
 | |
|                    expect(embeddedView['cd']).toEqual(['prop(href)=someEmbeddedValue']);
 | |
| 
 | |
|                    async.done();
 | |
|                  });
 | |
|            }));
 | |
| 
 | |
|         it('should dedup directives', inject([AsyncTestCompleter], (async) => {
 | |
|              compile([CompWithDupDirectives, TreeComp])
 | |
|                  .then((humanizedView) => {
 | |
|                    expect(humanizedView['componentViews'][0]['componentViews'].length).toBe(1);
 | |
|                    async.done();
 | |
| 
 | |
|                  });
 | |
|            }));
 | |
|       }
 | |
| 
 | |
|       describe('compileHostComponentRuntime', () => {
 | |
|         function compile(components: Type[]): Promise<any[]> {
 | |
|           return compiler.compileHostComponentRuntime(components[0])
 | |
|               .then((compiledHostTemplate) =>
 | |
|                         humanizeViewFactory(compiledHostTemplate.viewFactory));
 | |
|         }
 | |
| 
 | |
|         describe('no jit', () => {
 | |
|           beforeEachProviders(() => [
 | |
|             provide(ChangeDetectorGenConfig,
 | |
|                     {useValue: new ChangeDetectorGenConfig(true, false, false)})
 | |
|           ]);
 | |
|           runTests(compile);
 | |
|         });
 | |
| 
 | |
|         describe('jit', () => {
 | |
|           beforeEachProviders(() => [
 | |
|             provide(ChangeDetectorGenConfig,
 | |
|                     {useValue: new ChangeDetectorGenConfig(true, false, true)})
 | |
|           ]);
 | |
|           runTests(compile);
 | |
|         });
 | |
| 
 | |
|         it('should cache components for parallel requests',
 | |
|            inject([AsyncTestCompleter, XHR], (async, xhr: MockXHR) => {
 | |
|              // Expecting only one xhr...
 | |
|              xhr.expect('package:angular2/test/compiler/compUrl.html', '<a></a>');
 | |
|              PromiseWrapper.all([compile([CompWithTemplateUrl]), compile([CompWithTemplateUrl])])
 | |
|                  .then((humanizedViews) => {
 | |
|                    expect(humanizedViews[0]['componentViews'][0]['elements']).toEqual(['<a>']);
 | |
|                    expect(humanizedViews[1]['componentViews'][0]['elements']).toEqual(['<a>']);
 | |
| 
 | |
|                    async.done();
 | |
|                  });
 | |
|              xhr.flush();
 | |
|            }));
 | |
| 
 | |
|         it('should cache components for sequential requests',
 | |
|            inject([AsyncTestCompleter, XHR], (async, xhr: MockXHR) => {
 | |
|              // Expecting only one xhr...
 | |
|              xhr.expect('package:angular2/test/compiler/compUrl.html', '<a>');
 | |
|              compile([CompWithTemplateUrl])
 | |
|                  .then((humanizedView0) => {
 | |
|                    return compile([CompWithTemplateUrl])
 | |
|                        .then((humanizedView1) => {
 | |
|                          expect(humanizedView0['componentViews'][0]['elements']).toEqual(['<a>']);
 | |
|                          expect(humanizedView1['componentViews'][0]['elements']).toEqual(['<a>']);
 | |
|                          async.done();
 | |
|                        });
 | |
|                  });
 | |
|              xhr.flush();
 | |
|            }));
 | |
| 
 | |
|         it('should allow to clear the cache',
 | |
|            inject([AsyncTestCompleter, XHR], (async, xhr: MockXHR) => {
 | |
|              xhr.expect('package:angular2/test/compiler/compUrl.html', '<a>');
 | |
|              compile([CompWithTemplateUrl])
 | |
|                  .then((humanizedView) => {
 | |
|                    compiler.clearCache();
 | |
|                    xhr.expect('package:angular2/test/compiler/compUrl.html', '<b>');
 | |
|                    var result = compile([CompWithTemplateUrl]);
 | |
|                    xhr.flush();
 | |
|                    return result;
 | |
|                  })
 | |
|                  .then((humanizedView) => {
 | |
|                    expect(humanizedView['componentViews'][0]['elements']).toEqual(['<b>']);
 | |
|                    async.done();
 | |
|                  });
 | |
|              xhr.flush();
 | |
|            }));
 | |
|       });
 | |
| 
 | |
|       describe('compileTemplatesCodeGen', () => {
 | |
|         function normalizeComponent(
 | |
|             component: Type): Promise<NormalizedComponentWithViewDirectives> {
 | |
|           var compAndViewDirMetas =
 | |
|               [runtimeMetadataResolver.getDirectiveMetadata(component)].concat(
 | |
|                   runtimeMetadataResolver.getViewDirectivesMetadata(component));
 | |
|           var upperCasePipeMeta = runtimeMetadataResolver.getPipeMetadata(UpperCasePipe);
 | |
|           upperCasePipeMeta.type.moduleUrl = `package:${THIS_MODULE_ID}${MODULE_SUFFIX}`;
 | |
|           return PromiseWrapper.all(compAndViewDirMetas.map(
 | |
|                                         meta => compiler.normalizeDirectiveMetadata(meta)))
 | |
|               .then((normalizedCompAndViewDirMetas: CompileDirectiveMetadata[]) =>
 | |
|                         new NormalizedComponentWithViewDirectives(
 | |
|                             normalizedCompAndViewDirMetas[0],
 | |
|                             normalizedCompAndViewDirMetas.slice(1), [upperCasePipeMeta]));
 | |
|         }
 | |
| 
 | |
|         function compile(components: Type[]): Promise<any[]> {
 | |
|           return PromiseWrapper.all(components.map(normalizeComponent))
 | |
|               .then((normalizedCompWithViewDirMetas: NormalizedComponentWithViewDirectives[]) => {
 | |
|                 var sourceModule = compiler.compileTemplatesCodeGen(normalizedCompWithViewDirMetas);
 | |
|                 var sourceWithImports =
 | |
|                     testableTemplateModule(sourceModule,
 | |
|                                            normalizedCompWithViewDirMetas[0].component)
 | |
|                         .getSourceWithImports();
 | |
|                 return evalModule(sourceWithImports.source, sourceWithImports.imports, null);
 | |
|               });
 | |
|         }
 | |
| 
 | |
|         runTests(compile);
 | |
|       });
 | |
| 
 | |
|     });
 | |
| 
 | |
|     describe('normalizeDirectiveMetadata', () => {
 | |
|       it('should return the given DirectiveMetadata for non components',
 | |
|          inject([AsyncTestCompleter], (async) => {
 | |
|            var meta = runtimeMetadataResolver.getDirectiveMetadata(NonComponent);
 | |
|            compiler.normalizeDirectiveMetadata(meta).then(normMeta => {
 | |
|              expect(normMeta).toBe(meta);
 | |
|              async.done();
 | |
|            });
 | |
|          }));
 | |
| 
 | |
|       it('should normalize the template',
 | |
|          inject([AsyncTestCompleter, XHR], (async, xhr: MockXHR) => {
 | |
|            xhr.expect('package:angular2/test/compiler/compUrl.html', 'loadedTemplate');
 | |
|            compiler.normalizeDirectiveMetadata(
 | |
|                        runtimeMetadataResolver.getDirectiveMetadata(CompWithTemplateUrl))
 | |
|                .then((meta: CompileDirectiveMetadata) => {
 | |
|                  expect(meta.template.template).toEqual('loadedTemplate');
 | |
|                  async.done();
 | |
|                });
 | |
|            xhr.flush();
 | |
|          }));
 | |
| 
 | |
|       it('should copy all the other fields', inject([AsyncTestCompleter], (async) => {
 | |
|            var meta =
 | |
|                runtimeMetadataResolver.getDirectiveMetadata(CompWithBindingsAndStylesAndPipes);
 | |
|            compiler.normalizeDirectiveMetadata(meta).then((normMeta: CompileDirectiveMetadata) => {
 | |
|              expect(normMeta.type).toEqual(meta.type);
 | |
|              expect(normMeta.isComponent).toEqual(meta.isComponent);
 | |
|              expect(normMeta.dynamicLoadable).toEqual(meta.dynamicLoadable);
 | |
|              expect(normMeta.selector).toEqual(meta.selector);
 | |
|              expect(normMeta.exportAs).toEqual(meta.exportAs);
 | |
|              expect(normMeta.changeDetection).toEqual(meta.changeDetection);
 | |
|              expect(normMeta.inputs).toEqual(meta.inputs);
 | |
|              expect(normMeta.outputs).toEqual(meta.outputs);
 | |
|              expect(normMeta.hostListeners).toEqual(meta.hostListeners);
 | |
|              expect(normMeta.hostProperties).toEqual(meta.hostProperties);
 | |
|              expect(normMeta.hostAttributes).toEqual(meta.hostAttributes);
 | |
|              expect(normMeta.lifecycleHooks).toEqual(meta.lifecycleHooks);
 | |
|              async.done();
 | |
|            });
 | |
|          }));
 | |
|     });
 | |
| 
 | |
|     describe('compileStylesheetCodeGen', () => {
 | |
|       it('should compile stylesheets into code', inject([AsyncTestCompleter], (async) => {
 | |
|            var cssText = 'div {color: red}';
 | |
|            var sourceModule =
 | |
|                compiler.compileStylesheetCodeGen('package:someModuleUrl', cssText)[0];
 | |
|            var sourceWithImports = testableStylesModule(sourceModule).getSourceWithImports();
 | |
|            evalModule(sourceWithImports.source, sourceWithImports.imports, null)
 | |
|                .then(loadedCssText => {
 | |
|                  expect(loadedCssText).toEqual([cssText]);
 | |
|                  async.done();
 | |
|                });
 | |
| 
 | |
|          }));
 | |
|     });
 | |
|   });
 | |
| }
 | |
| 
 | |
| 
 | |
| @Pipe({name: 'uppercase'})
 | |
| @Injectable()
 | |
| export class UpperCasePipe implements PipeTransform {
 | |
|   transform(value: string, args: any[] = null): string { return value.toUpperCase(); }
 | |
| }
 | |
| 
 | |
| @Component({
 | |
|   selector: 'comp-a',
 | |
|   host: {'[title]': '\'someHostValue\''},
 | |
|   moduleId: THIS_MODULE_ID,
 | |
|   exportAs: 'someExportAs',
 | |
|   template: '<a [href]="\'someCtxValue\' | uppercase"></a>',
 | |
|   styles: ['div {color: red}'],
 | |
|   encapsulation: ViewEncapsulation.None,
 | |
|   pipes: [UpperCasePipe]
 | |
| })
 | |
| export class CompWithBindingsAndStylesAndPipes {
 | |
| }
 | |
| 
 | |
| @Component({
 | |
|   selector: 'tree',
 | |
|   moduleId: THIS_MODULE_ID,
 | |
|   template: '<template><tree></tree></template>',
 | |
|   directives: [TreeComp],
 | |
|   encapsulation: ViewEncapsulation.None
 | |
| })
 | |
| export class TreeComp {
 | |
| }
 | |
| 
 | |
| @Component({
 | |
|   selector: 'comp-wit-dup-tpl',
 | |
|   moduleId: THIS_MODULE_ID,
 | |
|   template: '<tree></tree>',
 | |
|   directives: [TreeComp, TreeComp],
 | |
|   encapsulation: ViewEncapsulation.None
 | |
| })
 | |
| export class CompWithDupDirectives {
 | |
| }
 | |
| 
 | |
| @Component({
 | |
|   selector: 'comp-url',
 | |
|   moduleId: THIS_MODULE_ID,
 | |
|   templateUrl: 'compUrl.html',
 | |
|   encapsulation: ViewEncapsulation.None
 | |
| })
 | |
| export class CompWithTemplateUrl {
 | |
| }
 | |
| 
 | |
| @Component({
 | |
|   selector: 'comp-tpl',
 | |
|   moduleId: THIS_MODULE_ID,
 | |
|   template: '<template><a [href]="\'someEmbeddedValue\'"></a></template>',
 | |
|   encapsulation: ViewEncapsulation.None
 | |
| })
 | |
| export class CompWithEmbeddedTemplate {
 | |
| }
 | |
| 
 | |
| 
 | |
| @Directive({selector: 'plain'})
 | |
| export class NonComponent {
 | |
| }
 | |
| 
 | |
| 
 | |
| @Component({
 | |
|   selector: 'comp2',
 | |
|   moduleId: THIS_MODULE_ID,
 | |
|   template: '<b></b>',
 | |
|   encapsulation: ViewEncapsulation.None
 | |
| })
 | |
| export class Comp2 {
 | |
| }
 | |
| 
 | |
| @Component({
 | |
|   selector: 'comp1',
 | |
|   moduleId: THIS_MODULE_ID,
 | |
|   template: '<a></a>, <comp2></comp2>',
 | |
|   encapsulation: ViewEncapsulation.None,
 | |
|   directives: [Comp2]
 | |
| })
 | |
| export class Comp1 {
 | |
| }
 | |
| 
 | |
| @Component({
 | |
|   selector: 'comp-with-2nested',
 | |
|   moduleId: THIS_MODULE_ID,
 | |
|   template: '<comp1></comp1>, <comp2></comp2>',
 | |
|   encapsulation: ViewEncapsulation.None,
 | |
|   directives: [Comp1, Comp2]
 | |
| })
 | |
| export class CompWith2NestedComps {
 | |
| }
 | |
| 
 | |
| function testableTemplateModule(sourceModule: SourceModule,
 | |
|                                 normComp: CompileDirectiveMetadata): SourceModule {
 | |
|   var testableSource = `
 | |
|   ${sourceModule.sourceWithModuleRefs}
 | |
|   ${codeGenFnHeader(['_'], '_run')}{
 | |
|     ${REFLECTOR_MODULE_REF}reflector.reflectionCapabilities = new ${REFLECTION_CAPS_MODULE_REF}ReflectionCapabilities();
 | |
|     return ${THIS_MODULE_REF}humanizeViewFactory(hostViewFactory_${normComp.type.name}.viewFactory);
 | |
|   }
 | |
|   ${codeGenExportVariable('run')}_run;`;
 | |
|   return new SourceModule(sourceModule.moduleUrl, testableSource);
 | |
| }
 | |
| 
 | |
| function testableStylesModule(sourceModule: SourceModule): SourceModule {
 | |
|   var testableSource = `${sourceModule.sourceWithModuleRefs}
 | |
|   ${codeGenValueFn(['_'], 'STYLES', '_run')};
 | |
|   ${codeGenExportVariable('run')}_run;`;
 | |
|   return new SourceModule(sourceModule.moduleUrl, testableSource);
 | |
| }
 | |
| 
 | |
| function humanizeView(view: AppView, cachedResults: Map<AppProtoView, any>): {[key: string]: any} {
 | |
|   var result = cachedResults.get(view.proto);
 | |
|   if (isPresent(result)) {
 | |
|     return result;
 | |
|   }
 | |
|   result = {};
 | |
|   // fill the cache early to break cycles.
 | |
|   cachedResults.set(view.proto, result);
 | |
| 
 | |
|   view.changeDetector.detectChanges();
 | |
| 
 | |
|   var pipes = {};
 | |
|   if (isPresent(view.proto.protoPipes)) {
 | |
|     StringMapWrapper.forEach(view.proto.protoPipes.config, (pipeProvider, pipeName) => {
 | |
|       pipes[pipeName] = stringify(pipeProvider.key.token);
 | |
|     });
 | |
|   }
 | |
|   var componentViews = [];
 | |
|   var embeddedViews = [];
 | |
| 
 | |
|   view.appElements.forEach((appElement) => {
 | |
|     if (isPresent(appElement.componentView)) {
 | |
|       componentViews.push(humanizeView(appElement.componentView, cachedResults));
 | |
|     } else if (isPresent(appElement.embeddedViewFactory)) {
 | |
|       embeddedViews.push(
 | |
|           humanizeViewFactory(appElement.embeddedViewFactory, appElement, cachedResults));
 | |
|     }
 | |
|   });
 | |
|   result['styles'] = (<any>view.renderer).styles;
 | |
|   result['elements'] = (<any>view.renderer).elements;
 | |
|   result['pipes'] = pipes;
 | |
|   result['cd'] = (<any>view.renderer).props;
 | |
|   result['componentViews'] = componentViews;
 | |
|   result['embeddedViews'] = embeddedViews;
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| // Attention: read by eval!
 | |
| export function humanizeViewFactory(
 | |
|     viewFactory: Function, containerAppElement: AppElement = null,
 | |
|     cachedResults: Map<AppProtoView, any> = null): {[key: string]: any} {
 | |
|   if (isBlank(cachedResults)) {
 | |
|     cachedResults = new Map<AppProtoView, any>();
 | |
|   }
 | |
|   var viewManager = new SpyAppViewManager();
 | |
|   viewManager.spy('createRenderComponentType')
 | |
|       .andCallFake((encapsulation: ViewEncapsulation, styles: Array<string | any[]>) => {
 | |
|         return new RenderComponentType('someId', encapsulation, styles);
 | |
|       });
 | |
|   var view: AppView = viewFactory(new RecordingRenderer([]), viewManager, containerAppElement, [],
 | |
|                                   null, null, null);
 | |
|   return humanizeView(view, cachedResults);
 | |
| }
 | |
| 
 | |
| class RecordingRenderer extends SpyRenderer {
 | |
|   props: string[] = [];
 | |
|   elements: string[] = [];
 | |
| 
 | |
|   constructor(public styles: string[]) {
 | |
|     super();
 | |
|     this.spy('renderComponent')
 | |
|         .andCallFake((componentProto) => new RecordingRenderer(componentProto.styles));
 | |
|     this.spy('setElementProperty')
 | |
|         .andCallFake((el, prop, value) => { this.props.push(`prop(${prop})=${value}`); });
 | |
|     this.spy('createElement')
 | |
|         .andCallFake((parent, elName) => { this.elements.push(`<${elName}>`); });
 | |
|   }
 | |
| }
 |