344 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			344 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import {
 | |
|   AsyncTestCompleter,
 | |
|   inject,
 | |
|   describe,
 | |
|   it,
 | |
|   expect,
 | |
|   beforeEach,
 | |
|   createTestInjector,
 | |
|   beforeEachBindings
 | |
| } from "angular2/test_lib";
 | |
| import {DOM} from 'angular2/src/dom/dom_adapter';
 | |
| import {DomTestbed, TestRootView, elRef} from '../../render/dom/dom_testbed';
 | |
| import {bind} from 'angular2/di';
 | |
| import {WorkerCompiler, WorkerRenderer} from "angular2/src/web-workers/worker/renderer";
 | |
| import {MessageBroker, UiArguments, FnArg} from "angular2/src/web-workers/worker/broker";
 | |
| import {Serializer} from "angular2/src/web-workers/shared/serializer";
 | |
| import {isPresent, isBlank, BaseException, Type} from "angular2/src/facade/lang";
 | |
| import {MapWrapper, ListWrapper} from "angular2/src/facade/collection";
 | |
| import {
 | |
|   DirectiveMetadata,
 | |
|   ProtoViewDto,
 | |
|   RenderProtoViewRef,
 | |
|   RenderViewWithFragments,
 | |
|   ViewDefinition,
 | |
|   RenderProtoViewMergeMapping,
 | |
|   RenderViewRef,
 | |
|   RenderFragmentRef
 | |
| } from "angular2/src/render/api";
 | |
| import {
 | |
|   MessageBus,
 | |
|   MessageBusSource,
 | |
|   MessageBusSink,
 | |
|   SourceListener
 | |
| } from "angular2/src/web-workers/shared/message_bus";
 | |
| import {
 | |
|   RenderProtoViewRefStore,
 | |
|   WebworkerRenderProtoViewRef
 | |
| } from "angular2/src/web-workers/shared/render_proto_view_ref_store";
 | |
| import {
 | |
|   RenderViewWithFragmentsStore,
 | |
|   WorkerRenderViewRef
 | |
| } from 'angular2/src/web-workers/shared/render_view_with_fragments_store';
 | |
| import {resolveInternalDomProtoView} from 'angular2/src/render/dom/view/proto_view';
 | |
| import {someComponent} from '../../render/dom/dom_renderer_integration_spec';
 | |
| import {WebWorkerMain} from 'angular2/src/web-workers/ui/impl';
 | |
| import {AnchorBasedAppRootUrl} from 'angular2/src/services/anchor_based_app_root_url';
 | |
| 
 | |
| export function main() {
 | |
|   function createBroker(workerSerializer: Serializer, uiSerializer: Serializer, tb: DomTestbed,
 | |
|                         uiRenderViewStore: RenderViewWithFragmentsStore,
 | |
|                         workerRenderViewStore: RenderViewWithFragmentsStore): MessageBroker {
 | |
|     // set up the two message buses to pass messages to each other
 | |
|     var uiMessageBus = new MockMessageBus(new MockMessageBusSink(), new MockMessageBusSource());
 | |
|     var workerMessageBus = new MockMessageBus(new MockMessageBusSink(), new MockMessageBusSource());
 | |
|     uiMessageBus.attachToBus(workerMessageBus);
 | |
|     workerMessageBus.attachToBus(uiMessageBus);
 | |
| 
 | |
|     // set up the worker side
 | |
|     var broker = new MessageBroker(workerMessageBus, workerSerializer);
 | |
| 
 | |
|     // set up the ui side
 | |
|     var webWorkerMain = new WebWorkerMain(tb.compiler, tb.renderer, uiRenderViewStore, uiSerializer,
 | |
|                                           new AnchorBasedAppRootUrl());
 | |
|     webWorkerMain.attachToWorker(uiMessageBus);
 | |
|     return broker;
 | |
|   }
 | |
| 
 | |
|   function createWorkerRenderer(workerSerializer: Serializer, uiSerializer: Serializer,
 | |
|                                 tb: DomTestbed, uiRenderViewStore: RenderViewWithFragmentsStore,
 | |
|                                 workerRenderViewStore: RenderViewWithFragmentsStore):
 | |
|       WorkerRenderer {
 | |
|     var broker =
 | |
|         createBroker(workerSerializer, uiSerializer, tb, uiRenderViewStore, workerRenderViewStore);
 | |
|     return new WorkerRenderer(broker, workerRenderViewStore);
 | |
|   }
 | |
| 
 | |
|   function createWorkerCompiler(workerSerializer: Serializer, uiSerializer: Serializer,
 | |
|                                 tb: DomTestbed): WorkerCompiler {
 | |
|     var broker = createBroker(workerSerializer, uiSerializer, tb, null, null);
 | |
|     return new WorkerCompiler(broker);
 | |
|   }
 | |
| 
 | |
|   describe("Web Worker Compiler", function() {
 | |
|     var workerSerializer: Serializer;
 | |
|     var uiSerializer: Serializer;
 | |
|     var workerRenderProtoViewRefStore: RenderProtoViewRefStore;
 | |
|     var uiRenderProtoViewRefStore: RenderProtoViewRefStore;
 | |
|     var tb: DomTestbed;
 | |
| 
 | |
|     beforeEach(() => {
 | |
|       workerRenderProtoViewRefStore = new RenderProtoViewRefStore(true);
 | |
|       uiRenderProtoViewRefStore = new RenderProtoViewRefStore(false);
 | |
|       workerSerializer = createSerializer(workerRenderProtoViewRefStore, null);
 | |
|       uiSerializer = createSerializer(uiRenderProtoViewRefStore, null);
 | |
|       tb = createTestInjector([DomTestbed]).get(DomTestbed);
 | |
|     });
 | |
| 
 | |
|     function resolveWebWorkerRef(ref: RenderProtoViewRef) {
 | |
|       var refNumber = (<WebworkerRenderProtoViewRef>ref).refNumber;
 | |
|       return resolveInternalDomProtoView(uiRenderProtoViewRefStore.deserialize(refNumber));
 | |
|     }
 | |
| 
 | |
|     it('should build the proto view', inject([AsyncTestCompleter], (async) => {
 | |
|          var compiler: WorkerCompiler = createWorkerCompiler(workerSerializer, uiSerializer, tb);
 | |
| 
 | |
|          var dirMetadata = DirectiveMetadata.create(
 | |
|              {id: 'id', selector: 'CUSTOM', type: DirectiveMetadata.COMPONENT_TYPE});
 | |
|          compiler.compileHost(dirMetadata)
 | |
|              .then((protoView) => {
 | |
|                expect(DOM.tagName(DOM.firstChild(
 | |
|                           DOM.content(resolveWebWorkerRef(protoView.render).rootElement))))
 | |
|                    .toEqual('CUSTOM');
 | |
|                expect(protoView).not.toBeNull();
 | |
|                async.done();
 | |
|              });
 | |
|        }));
 | |
|   });
 | |
| 
 | |
|   describe("Web Worker Renderer", () => {
 | |
|     beforeEachBindings(() => [DomTestbed]);
 | |
|     var renderer: WorkerRenderer;
 | |
|     var workerSerializer: Serializer;
 | |
|     var workerRenderViewStore: RenderViewWithFragmentsStore;
 | |
|     var uiRenderViewStore: RenderViewWithFragmentsStore;
 | |
|     var uiSerializer: Serializer;
 | |
|     var tb: DomTestbed;
 | |
| 
 | |
|     /**
 | |
|      * Seriliazes the given obj with the uiSerializer and then returns the version that
 | |
|      * the worker would deserialize
 | |
|      */
 | |
|     function serialize(obj: any, type: Type): any {
 | |
|       var serialized = uiSerializer.serialize(obj, type);
 | |
|       return workerSerializer.deserialize(serialized, type);
 | |
|     }
 | |
| 
 | |
|     beforeEach(() => {
 | |
|       workerRenderViewStore = new RenderViewWithFragmentsStore(true);
 | |
|       tb = createTestInjector([DomTestbed]).get(DomTestbed);
 | |
|       uiRenderViewStore = new RenderViewWithFragmentsStore(false);
 | |
|       workerSerializer = createSerializer(new RenderProtoViewRefStore(true), workerRenderViewStore);
 | |
|       uiSerializer = createSerializer(new RenderProtoViewRefStore(false), uiRenderViewStore);
 | |
|       renderer = createWorkerRenderer(workerSerializer, uiSerializer, tb, uiRenderViewStore,
 | |
|                                       workerRenderViewStore);
 | |
|     });
 | |
| 
 | |
| 
 | |
|     it('should create and destroy root host views while using the given elements in place',
 | |
|        inject([AsyncTestCompleter], (async) => {
 | |
|          tb.compiler.compileHost(someComponent)
 | |
|              .then((hostProtoViewDto: any) => {
 | |
|                hostProtoViewDto = serialize(hostProtoViewDto, ProtoViewDto);
 | |
|                var viewWithFragments =
 | |
|                    renderer.createRootHostView(hostProtoViewDto.render, 1, '#root');
 | |
|                var view = new WorkerTestRootView(viewWithFragments, uiRenderViewStore);
 | |
| 
 | |
|                expect(tb.rootEl.parentNode).toBeTruthy();
 | |
|                expect(view.hostElement).toBe(tb.rootEl);
 | |
| 
 | |
|                renderer.detachFragment(viewWithFragments.fragmentRefs[0]);
 | |
|                renderer.destroyView(viewWithFragments.viewRef);
 | |
|                expect(tb.rootEl.parentNode).toBeFalsy();
 | |
| 
 | |
|                async.done();
 | |
|              });
 | |
|        }));
 | |
| 
 | |
|     it('should update text nodes', inject([AsyncTestCompleter], (async) => {
 | |
|          tb.compileAndMerge(
 | |
|                someComponent,
 | |
|                [
 | |
|                  new ViewDefinition(
 | |
|                      {componentId: 'someComponent', template: '{{a}}', directives: []})
 | |
|                ])
 | |
|              .then((protoViewMergeMappings) => {
 | |
|                protoViewMergeMappings =
 | |
|                    serialize(protoViewMergeMappings, RenderProtoViewMergeMapping);
 | |
|                var rootView = renderer.createView(protoViewMergeMappings.mergedProtoViewRef, 1);
 | |
|                renderer.hydrateView(rootView.viewRef);
 | |
| 
 | |
|                renderer.setText(rootView.viewRef, 0, 'hello');
 | |
|                var view = new WorkerTestRootView(rootView, uiRenderViewStore);
 | |
|                expect(view.hostElement).toHaveText('hello');
 | |
|                async.done();
 | |
|              });
 | |
|        }));
 | |
| 
 | |
| 
 | |
|     it('should update any element property/attributes/class/style independent of the compilation on the root element and other elements',
 | |
|        inject([AsyncTestCompleter], (async) => {
 | |
|          tb.compileAndMerge(someComponent,
 | |
|                             [
 | |
|                               new ViewDefinition({
 | |
|                                 componentId: 'someComponent',
 | |
|                                 template: '<input [title]="y" style="position:absolute">',
 | |
|                                 directives: []
 | |
|                               })
 | |
|                             ])
 | |
|              .then((protoViewMergeMappings) => {
 | |
|                protoViewMergeMappings =
 | |
|                    serialize(protoViewMergeMappings, RenderProtoViewMergeMapping);
 | |
| 
 | |
|                var checkSetters = (elr, el) => {
 | |
|                  renderer.setElementProperty(elr, 'tabIndex', 1);
 | |
|                  expect((<HTMLInputElement>el).tabIndex).toEqual(1);
 | |
| 
 | |
|                  renderer.setElementClass(elr, 'a', true);
 | |
|                  expect(DOM.hasClass(el, 'a')).toBe(true);
 | |
|                  renderer.setElementClass(elr, 'a', false);
 | |
|                  expect(DOM.hasClass(el, 'a')).toBe(false);
 | |
| 
 | |
|                  renderer.setElementStyle(elr, 'width', '10px');
 | |
|                  expect(DOM.getStyle(el, 'width')).toEqual('10px');
 | |
|                  renderer.setElementStyle(elr, 'width', null);
 | |
|                  expect(DOM.getStyle(el, 'width')).toEqual('');
 | |
| 
 | |
|                  renderer.setElementAttribute(elr, 'someAttr', 'someValue');
 | |
|                  expect(DOM.getAttribute(el, 'some-attr')).toEqual('someValue');
 | |
|                };
 | |
| 
 | |
|                var rootViewWithFragments =
 | |
|                    renderer.createView(protoViewMergeMappings.mergedProtoViewRef, 1);
 | |
|                renderer.hydrateView(rootViewWithFragments.viewRef);
 | |
| 
 | |
|                var rootView = new WorkerTestRootView(rootViewWithFragments, uiRenderViewStore);
 | |
|                // root element
 | |
|                checkSetters(elRef(rootViewWithFragments.viewRef, 0), rootView.hostElement);
 | |
|                // nested elements
 | |
|                checkSetters(elRef(rootViewWithFragments.viewRef, 1),
 | |
|                             DOM.firstChild(rootView.hostElement));
 | |
| 
 | |
|                async.done();
 | |
|              });
 | |
|        }));
 | |
| 
 | |
|     it('should add and remove empty fragments', inject([AsyncTestCompleter], (async) => {
 | |
|          tb.compileAndMerge(someComponent,
 | |
|                             [
 | |
|                               new ViewDefinition({
 | |
|                                 componentId: 'someComponent',
 | |
|                                 template: '<template></template><template></template>',
 | |
|                                 directives: []
 | |
|                               })
 | |
|                             ])
 | |
|              .then((protoViewMergeMappings) => {
 | |
|                protoViewMergeMappings =
 | |
|                    serialize(protoViewMergeMappings, RenderProtoViewMergeMapping);
 | |
|                var rootViewWithFragments =
 | |
|                    renderer.createView(protoViewMergeMappings.mergedProtoViewRef, 3);
 | |
| 
 | |
|                var elr = elRef(rootViewWithFragments.viewRef, 1);
 | |
|                var rootView = new WorkerTestRootView(rootViewWithFragments, uiRenderViewStore);
 | |
|                expect(rootView.hostElement).toHaveText('');
 | |
|                var fragment = rootViewWithFragments.fragmentRefs[1];
 | |
|                var fragment2 = rootViewWithFragments.fragmentRefs[2];
 | |
|                renderer.attachFragmentAfterElement(elr, fragment);
 | |
|                renderer.attachFragmentAfterFragment(fragment, fragment2);
 | |
|                renderer.detachFragment(fragment);
 | |
|                renderer.detachFragment(fragment2);
 | |
|                expect(rootView.hostElement).toHaveText('');
 | |
| 
 | |
|                async.done();
 | |
|              });
 | |
|        }));
 | |
| 
 | |
|     if (DOM.supportsDOMEvents()) {
 | |
|       it('should call actions on the element independent of the compilation',
 | |
|          inject([AsyncTestCompleter], (async) => {
 | |
|            tb.compileAndMerge(someComponent,
 | |
|                               [
 | |
|                                 new ViewDefinition({
 | |
|                                   componentId: 'someComponent',
 | |
|                                   template: '<input [title]="y"></input>',
 | |
|                                   directives: []
 | |
|                                 })
 | |
|                               ])
 | |
|                .then((protoViewMergeMappings) => {
 | |
|                  protoViewMergeMappings =
 | |
|                      serialize(protoViewMergeMappings, RenderProtoViewMergeMapping);
 | |
|                  var rootViewWithFragments =
 | |
|                      renderer.createView(protoViewMergeMappings.mergedProtoViewRef, 1);
 | |
|                  var rootView = new WorkerTestRootView(rootViewWithFragments, uiRenderViewStore);
 | |
| 
 | |
|                  renderer.invokeElementMethod(elRef(rootViewWithFragments.viewRef, 1),
 | |
|                                               'setAttribute', ['a', 'b']);
 | |
| 
 | |
|                  expect(DOM.getAttribute(DOM.childNodes(rootView.hostElement)[0], 'a'))
 | |
|                      .toEqual('b');
 | |
|                  async.done();
 | |
|                });
 | |
|          }));
 | |
|     }
 | |
|   });
 | |
| }
 | |
| 
 | |
| class WorkerTestRootView extends TestRootView {
 | |
|   constructor(workerViewWithFragments: RenderViewWithFragments, uiRenderViewStore) {
 | |
|     super(new RenderViewWithFragments(
 | |
|         uiRenderViewStore.retreive(
 | |
|             (<WorkerRenderViewRef>workerViewWithFragments.viewRef).refNumber),
 | |
|         ListWrapper.map(workerViewWithFragments.fragmentRefs,
 | |
|                         (val) => { return uiRenderViewStore.retreive(val.refNumber); })));
 | |
|   }
 | |
| }
 | |
| 
 | |
| function createSerializer(protoViewRefStore: RenderProtoViewRefStore,
 | |
|                           renderViewStore: RenderViewWithFragmentsStore): Serializer {
 | |
|   var injector = createTestInjector([
 | |
|     bind(RenderProtoViewRefStore)
 | |
|         .toValue(protoViewRefStore),
 | |
|     bind(RenderViewWithFragmentsStore).toValue(renderViewStore)
 | |
|   ]);
 | |
|   return injector.get(Serializer);
 | |
| }
 | |
| 
 | |
| class MockMessageBusSource implements MessageBusSource {
 | |
|   private _listenerStore: Map<int, SourceListener> = new Map<int, SourceListener>();
 | |
|   private _numListeners: number = 0;
 | |
| 
 | |
|   addListener(fn: SourceListener): int {
 | |
|     this._listenerStore.set(++this._numListeners, fn);
 | |
|     return this._numListeners;
 | |
|   }
 | |
| 
 | |
|   removeListener(index: int): void { MapWrapper.delete(this._listenerStore, index); }
 | |
| 
 | |
|   receive(message: Object): void {
 | |
|     MapWrapper.forEach(this._listenerStore, (fn: SourceListener, key: int) => { fn(message); });
 | |
|   }
 | |
| }
 | |
| 
 | |
| class MockMessageBusSink implements MessageBusSink {
 | |
|   private _sendTo: MockMessageBusSource;
 | |
| 
 | |
|   send(message: Object): void { this._sendTo.receive({'data': message}); }
 | |
| 
 | |
|   attachToSource(source: MockMessageBusSource) { this._sendTo = source; }
 | |
| }
 | |
| 
 | |
| class MockMessageBus implements MessageBus {
 | |
|   constructor(public sink: MockMessageBusSink, public source: MockMessageBusSource) {}
 | |
|   attachToBus(bus: MockMessageBus) { this.sink.attachToSource(bus.source); }
 | |
| }
 |