223 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			223 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| import {
 | |
|   el
 | |
| } from 'angular2/test_lib';
 | |
| 
 | |
| import {isBlank, isPresent, BaseException} from 'angular2/src/facade/lang';
 | |
| import {MapWrapper, ListWrapper, List} from 'angular2/src/facade/collection';
 | |
| import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
 | |
| import {DOM} from 'angular2/src/dom/dom_adapter';
 | |
| 
 | |
| import {Parser, Lexer} from 'angular2/change_detection';
 | |
| import {DirectDomRenderer} from 'angular2/src/render/dom/direct_dom_renderer';
 | |
| import {Compiler} from 'angular2/src/render/dom/compiler/compiler';
 | |
| import {ProtoViewRef, ProtoView, Template, ViewContainerRef, EventDispatcher, DirectiveMetadata} from 'angular2/src/render/api';
 | |
| import {DefaultStepFactory} from 'angular2/src/render/dom/compiler/compile_step_factory';
 | |
| import {TemplateLoader} from 'angular2/src/render/dom/compiler/template_loader';
 | |
| import {UrlResolver} from 'angular2/src/services/url_resolver';
 | |
| import {EmulatedUnscopedShadowDomStrategy} from 'angular2/src/render/dom/shadow_dom/emulated_unscoped_shadow_dom_strategy';
 | |
| import {EventManager, EventManagerPlugin} from 'angular2/src/render/dom/events/event_manager';
 | |
| import {VmTurnZone} from 'angular2/src/core/zone/vm_turn_zone';
 | |
| import {StyleUrlResolver} from 'angular2/src/render/dom/shadow_dom/style_url_resolver';
 | |
| import {ViewFactory} from 'angular2/src/render/dom/view/view_factory';
 | |
| 
 | |
| export class IntegrationTestbed {
 | |
|   renderer;
 | |
|   parser;
 | |
|   rootEl;
 | |
|   rootProtoViewRef;
 | |
|   eventPlugin;
 | |
|   _templates:Map<string, Template>;
 | |
|   _compileCache:Map<string, Promise<List>>;
 | |
| 
 | |
|   constructor({urlData, viewCacheCapacity, shadowDomStrategy, templates}) {
 | |
|     this._templates = MapWrapper.create();
 | |
|     if (isPresent(templates)) {
 | |
|       ListWrapper.forEach(templates, (template) => {
 | |
|         MapWrapper.set(this._templates, template.componentId, template);
 | |
|       });
 | |
|     }
 | |
|     this._compileCache = MapWrapper.create();
 | |
|     var parser = new Parser(new Lexer());
 | |
|     var urlResolver = new UrlResolver();
 | |
|     if (isBlank(shadowDomStrategy)) {
 | |
|       shadowDomStrategy = new EmulatedUnscopedShadowDomStrategy(new StyleUrlResolver(urlResolver), null);
 | |
|     }
 | |
|     var compiler = new Compiler(new DefaultStepFactory(parser, shadowDomStrategy), new FakeTemplateLoader(urlResolver, urlData));
 | |
| 
 | |
|     if (isBlank(viewCacheCapacity)) {
 | |
|       viewCacheCapacity = 1;
 | |
|     }
 | |
|     if (isBlank(urlData)) {
 | |
|       urlData = MapWrapper.create();
 | |
|     }
 | |
|     this.eventPlugin = new FakeEventManagerPlugin();
 | |
|     var eventManager = new EventManager([this.eventPlugin], new FakeVmTurnZone());
 | |
|     var viewFactory = new ViewFactory(viewCacheCapacity, eventManager, shadowDomStrategy);
 | |
|     this.renderer = new DirectDomRenderer(compiler, viewFactory, shadowDomStrategy);
 | |
| 
 | |
|     this.rootEl = el('<div></div>');
 | |
|     this.rootProtoViewRef = this.renderer.createRootProtoView(this.rootEl);
 | |
|   }
 | |
| 
 | |
|   compile(templateHtml, directives):Promise<List<ProtoViewRef>> {
 | |
|     return this._compileRecurse(new Template({
 | |
|       componentId: 'root',
 | |
|       inline: templateHtml,
 | |
|       directives: directives
 | |
|     })).then( (protoViewRefs) => {
 | |
|       return this._flattenList([
 | |
|         this.renderer.mergeChildComponentProtoViews(this.rootProtoViewRef, [protoViewRefs[0]]),
 | |
|         protoViewRefs
 | |
|       ]);
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   _compileRecurse(template):Promise<List<ProtoViewRef>> {
 | |
|     var result = MapWrapper.get(this._compileCache, template.componentId);
 | |
|     if (isPresent(result)) {
 | |
|       return result;
 | |
|     }
 | |
|     result = this.renderer.compile(template).then( (pv) => {
 | |
|       var childComponentPromises = ListWrapper.map(
 | |
|         this._findNestedComponentIds(template, pv),
 | |
|         (componentId) => {
 | |
|           var childTemplate = MapWrapper.get(this._templates, componentId);
 | |
|           if (isBlank(childTemplate)) {
 | |
|             throw new BaseException(`Could not find template for ${componentId}!`);
 | |
|           }
 | |
|           return this._compileRecurse(childTemplate);
 | |
|         }
 | |
|       );
 | |
|       return PromiseWrapper.all(childComponentPromises).then(
 | |
|         (protoViewRefsWithChildren) => {
 | |
|           var protoViewRefs =
 | |
|             ListWrapper.map(protoViewRefsWithChildren, (arr) => arr[0]);
 | |
|           return this._flattenList([
 | |
|             this.renderer.mergeChildComponentProtoViews(pv.render, protoViewRefs),
 | |
|             protoViewRefsWithChildren
 | |
|           ]);
 | |
|         }
 | |
|       );
 | |
|     });
 | |
|     MapWrapper.set(this._compileCache, template.componentId, result);
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   _findNestedComponentIds(template, pv, target = null):List<string> {
 | |
|     if (isBlank(target)) {
 | |
|       target = [];
 | |
|     }
 | |
|     for (var binderIdx=0; binderIdx<pv.elementBinders.length; binderIdx++) {
 | |
|       var eb = pv.elementBinders[binderIdx];
 | |
|       var componentDirective;
 | |
|       ListWrapper.forEach(eb.directives, (db) => {
 | |
|         var meta = template.directives[db.directiveIndex];
 | |
|         if (meta.type === DirectiveMetadata.COMPONENT_TYPE) {
 | |
|           componentDirective = meta;
 | |
|         }
 | |
|       });
 | |
|       if (isPresent(componentDirective)) {
 | |
|         ListWrapper.push(target, componentDirective.id);
 | |
|       } else if (isPresent(eb.nestedProtoView)) {
 | |
|         this._findNestedComponentIds(template, eb.nestedProtoView, target);
 | |
|       }
 | |
|     }
 | |
|     return target;
 | |
|   }
 | |
| 
 | |
|   _flattenList(tree:List, out:List = null):List {
 | |
|     if (isBlank(out)) {
 | |
|       out = [];
 | |
|     }
 | |
|     for (var i = 0; i < tree.length; i++) {
 | |
|       var item = tree[i];
 | |
|       if (ListWrapper.isList(item)) {
 | |
|         this._flattenList(item, out);
 | |
|       } else {
 | |
|         ListWrapper.push(out, item);
 | |
|       }
 | |
|     }
 | |
|     return out;
 | |
|   }
 | |
| 
 | |
| }
 | |
| 
 | |
| 
 | |
| class FakeTemplateLoader extends TemplateLoader {
 | |
|   _urlData: Map<string, string>;
 | |
| 
 | |
|   constructor(urlResolver, urlData) {
 | |
|     super(null, urlResolver);
 | |
|     this._urlData = urlData;
 | |
|   }
 | |
| 
 | |
|   load(template: Template) {
 | |
|     if (isPresent(template.inline)) {
 | |
|       return PromiseWrapper.resolve(DOM.createTemplate(template.inline));
 | |
|     }
 | |
| 
 | |
|     if (isPresent(template.absUrl)) {
 | |
|       var content = this._urlData[template.absUrl];
 | |
|       if (isPresent(content)) {
 | |
|         return PromiseWrapper.resolve(DOM.createTemplate(content));
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     return PromiseWrapper.reject('Load failed');
 | |
|   }
 | |
| }
 | |
| 
 | |
| export class FakeVmTurnZone extends VmTurnZone {
 | |
|   constructor() {
 | |
|     super({enableLongStackTrace: false});
 | |
|   }
 | |
| 
 | |
|   run(fn) {
 | |
|     fn();
 | |
|   }
 | |
| 
 | |
|   runOutsideAngular(fn) {
 | |
|     fn();
 | |
|   }
 | |
| }
 | |
| 
 | |
| export class FakeEventManagerPlugin extends EventManagerPlugin {
 | |
|   _eventHandlers: Map;
 | |
| 
 | |
|   constructor() {
 | |
|     super();
 | |
|     this._eventHandlers = MapWrapper.create();
 | |
|   }
 | |
| 
 | |
|   dispatchEvent(eventName, event) {
 | |
|     MapWrapper.get(this._eventHandlers, eventName)(event);
 | |
|   }
 | |
| 
 | |
|   supports(eventName: string): boolean {
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   addEventListener(element, eventName: string, handler: Function, shouldSupportBubble: boolean) {
 | |
|     MapWrapper.set(this._eventHandlers, eventName, handler);
 | |
|   }
 | |
| }
 | |
| 
 | |
| export class LoggingEventDispatcher extends EventDispatcher {
 | |
|   log:List;
 | |
|   constructor() {
 | |
|     super();
 | |
|     this.log = [];
 | |
|   }
 | |
|   dispatchEvent(
 | |
|     elementIndex:number, eventName:string, locals:List<any>
 | |
|   ) {
 | |
|     ListWrapper.push(this.log, [elementIndex, eventName, locals]);
 | |
|   }
 | |
| }
 | |
| 
 | |
| export class FakeEvent {
 | |
|   target;
 | |
|   constructor(target) {
 | |
|     this.target = target;
 | |
|   }
 | |
| } |