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 = (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: '', directives: [] }) ]) .then((protoViewMergeMappings) => { protoViewMergeMappings = serialize(protoViewMergeMappings, RenderProtoViewMergeMapping); var checkSetters = (elr, el) => { renderer.setElementProperty(elr, 'tabIndex', 1); expect((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: '', 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: '', 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( (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 = new Map(); 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); } }