angular-cn/modules/angular2/test/web-workers/worker/renderer_spec.ts

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); }
}