refactor(view): introduce AppViewManager to consolidate logic
AppViewManager is the single entry point to changing the view hierarchy. It is split between the manager itself which does coordination and helper methods, so both are easily testable in isolation. Also, ViewContainer is now only a pure reference to a bound element with the previous functionality but does not contain the list of views any more. Part of #1477
This commit is contained in:
@ -414,6 +414,13 @@ export function main() {
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should throw for non component types', () => {
|
||||
var compiler = createCompiler([], []);
|
||||
expect(
|
||||
() => compiler.compile(SomeDecoratorDirective)
|
||||
).toThrowError(`Could not load '${stringify(SomeDecoratorDirective)}' because it is not a component.`);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -69,7 +69,7 @@ export function main() {
|
||||
tb.createView(MyComp).then((view) => {
|
||||
view.context.ctxBoolProp = true;
|
||||
view.detectChanges();
|
||||
var dynamicComponent = view.rawView.viewContainers[0].get(0).locals.get("dynamic");
|
||||
var dynamicComponent = view.rawView.viewContainers[0].views[0].locals.get("dynamic");
|
||||
dynamicComponent.done.then((_) => {
|
||||
view.detectChanges();
|
||||
expect(view.rootNodes).toHaveText('hello');
|
||||
@ -77,13 +77,13 @@ export function main() {
|
||||
view.context.ctxBoolProp = false;
|
||||
view.detectChanges();
|
||||
|
||||
expect(view.rawView.viewContainers[0].length).toBe(0);
|
||||
expect(view.rawView.viewContainers[0].views.length).toBe(0);
|
||||
expect(view.rootNodes).toHaveText('');
|
||||
|
||||
view.context.ctxBoolProp = true;
|
||||
view.detectChanges();
|
||||
|
||||
var dynamicComponent = view.rawView.viewContainers[0].get(0).locals.get("dynamic");
|
||||
var dynamicComponent = view.rawView.viewContainers[0].views[0].locals.get("dynamic");
|
||||
return dynamicComponent.done;
|
||||
}).then((_) => {
|
||||
view.detectChanges();
|
||||
|
@ -21,7 +21,16 @@ class DummyDirective extends Directive {
|
||||
|
||||
@proxy
|
||||
@IMPLEMENTS(AppView)
|
||||
class DummyView extends SpyObject {noSuchMethod(m){return super.noSuchMethod(m)}}
|
||||
class DummyView extends SpyObject {
|
||||
componentChildViews;
|
||||
changeDetector;
|
||||
constructor() {
|
||||
super();
|
||||
this.componentChildViews = [];
|
||||
this.changeDetector = null;
|
||||
}
|
||||
noSuchMethod(m){return super.noSuchMethod(m);}
|
||||
}
|
||||
|
||||
|
||||
class SimpleDirective {
|
||||
@ -119,6 +128,20 @@ class NeedsElementRef {
|
||||
}
|
||||
}
|
||||
|
||||
class NeedsViewContainer {
|
||||
viewContainer;
|
||||
constructor(vc:ViewContainer) {
|
||||
this.viewContainer = vc;
|
||||
}
|
||||
}
|
||||
|
||||
class NeedsChangeDetectorRef {
|
||||
changeDetectorRef;
|
||||
constructor(cdr:ChangeDetectorRef) {
|
||||
this.changeDetectorRef = cdr;
|
||||
}
|
||||
}
|
||||
|
||||
class A_Needs_B {
|
||||
constructor(dep){}
|
||||
}
|
||||
@ -158,7 +181,7 @@ class TestNode extends TreeNode {
|
||||
}
|
||||
|
||||
export function main() {
|
||||
var defaultPreBuiltObjects = new PreBuiltObjects(null, null, null);
|
||||
var defaultPreBuiltObjects = new PreBuiltObjects(null, null, null, null);
|
||||
var appInjector = Injector.resolveAndCreate([]);
|
||||
|
||||
function humanize(tree, names:List) {
|
||||
@ -431,7 +454,7 @@ export function main() {
|
||||
|
||||
it("should instantiate directives that depend on pre built objects", function () {
|
||||
var view = new DummyView();
|
||||
var inj = injector([NeedsView], null, null, new PreBuiltObjects(view, null, null));
|
||||
var inj = injector([NeedsView], null, null, new PreBuiltObjects(null, view, null, null));
|
||||
|
||||
expect(inj.get(NeedsView).view).toBe(view);
|
||||
});
|
||||
@ -557,34 +580,23 @@ export function main() {
|
||||
describe("pre built objects", function () {
|
||||
it("should return view", function () {
|
||||
var view = new DummyView();
|
||||
var inj = injector([], null, null, new PreBuiltObjects(view, null, null));
|
||||
var inj = injector([], null, null, new PreBuiltObjects(null, view, null, null));
|
||||
|
||||
expect(inj.get(AppView)).toEqual(view);
|
||||
});
|
||||
|
||||
it("should return element", function () {
|
||||
var element = new NgElement(null, null);
|
||||
var inj = injector([], null, null, new PreBuiltObjects(null, element, null));
|
||||
var inj = injector([], null, null, new PreBuiltObjects(null, null, element, null));
|
||||
|
||||
expect(inj.get(NgElement)).toEqual(element);
|
||||
});
|
||||
|
||||
it('should return viewContainer', function () {
|
||||
var viewContainer = new ViewContainer(null, null, null);
|
||||
var view = new DummyView();
|
||||
view.spy('getOrCreateViewContainer').andCallFake( (index) => {
|
||||
return viewContainer;
|
||||
});
|
||||
var inj = injector([], null, null, new PreBuiltObjects(view, null, null));
|
||||
it("should return default ProtoView", function () {
|
||||
var protoView = new AppProtoView(null, null);
|
||||
var inj = injector([], null, null, new PreBuiltObjects(null, null, null, protoView));
|
||||
|
||||
expect(inj.get(ViewContainer)).toEqual(viewContainer);
|
||||
});
|
||||
|
||||
it('should return changeDetectorRef', function () {
|
||||
var cd = new DynamicChangeDetector(null, null, null, [], []);
|
||||
var inj = injector([], null, null, new PreBuiltObjects(null, null, cd));
|
||||
|
||||
expect(inj.get(ChangeDetectorRef)).toBe(cd.ref);
|
||||
expect(inj.get(AppProtoView)).toEqual(protoView);
|
||||
});
|
||||
});
|
||||
|
||||
@ -694,14 +706,20 @@ export function main() {
|
||||
expect(inj.get(NeedsElementRef).elementRef).toBeAnInstanceOf(ElementRef);
|
||||
});
|
||||
|
||||
it('should return the viewContainer from the view', () => {
|
||||
var viewContainer = new ViewContainer(null, null, null);
|
||||
it('should inject ChangeDetectorRef', function () {
|
||||
var cd = new DynamicChangeDetector(null, null, null, [], []);
|
||||
var view = new DummyView();
|
||||
view.spy('getOrCreateViewContainer').andCallFake( (index) => {
|
||||
return viewContainer;
|
||||
});
|
||||
var inj = injector([NeedsElementRef], null, null, new PreBuiltObjects(view, null, null));
|
||||
expect(inj.get(NeedsElementRef).elementRef.viewContainer).toBe(viewContainer);
|
||||
var childView = new DummyView();
|
||||
childView.changeDetector = cd;
|
||||
view.componentChildViews = [childView];
|
||||
var inj = injector([NeedsChangeDetectorRef], null, null, new PreBuiltObjects(null, view, null, null));
|
||||
|
||||
expect(inj.get(NeedsChangeDetectorRef).changeDetectorRef).toBe(cd.ref);
|
||||
});
|
||||
|
||||
it('should inject ViewContainer', () => {
|
||||
var inj = injector([NeedsViewContainer]);
|
||||
expect(inj.get(NeedsViewContainer).viewContainer).toBeAnInstanceOf(ViewContainer);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -537,7 +537,7 @@ export function main() {
|
||||
tb.createView(MyComp, {context: ctx}).then((view) => {
|
||||
view.detectChanges();
|
||||
|
||||
var subview = view.rawView.viewContainers[1].get(0);
|
||||
var subview = view.rawView.viewContainers[1].views[0];
|
||||
var childComponent = subview.locals.get('child');
|
||||
expect(childComponent.myAncestor).toBeAnInstanceOf(SomeDirective);
|
||||
|
||||
@ -665,7 +665,7 @@ export function main() {
|
||||
ctx.ctxBoolProp = true;
|
||||
view.detectChanges();
|
||||
|
||||
var subview = view.rawView.viewContainers[0].get(0);
|
||||
var subview = view.rawView.viewContainers[0].views[0];
|
||||
var injector = subview.elementInjectors[0];
|
||||
var listener = injector.get(DecoratorListeningDomEvent);
|
||||
var listenerother = injector.get(DecoratorListeningDomEventOther);
|
||||
|
75
modules/angular2/test/core/compiler/view_container_spec.js
vendored
Normal file
75
modules/angular2/test/core/compiler/view_container_spec.js
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
xdescribe,
|
||||
describe,
|
||||
el,
|
||||
dispatchEvent,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
beforeEachBindings,
|
||||
it,
|
||||
xit,
|
||||
SpyObject, proxy
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import {MapWrapper} from 'angular2/src/facade/collection';
|
||||
import {IMPLEMENTS, isBlank, isPresent} from 'angular2/src/facade/lang';
|
||||
|
||||
import {ElementRef} from 'angular2/src/core/compiler/element_injector';
|
||||
import {AppView, AppProtoView, InternalAppViewContainer} from 'angular2/src/core/compiler/view';
|
||||
import {ViewContainer} from 'angular2/src/core/compiler/view_container';
|
||||
import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
|
||||
|
||||
export function main() {
|
||||
// TODO(tbosch): add missing tests
|
||||
|
||||
describe('ViewContainer', () => {
|
||||
var location;
|
||||
var view;
|
||||
var viewManager;
|
||||
|
||||
function createProtoView() {
|
||||
return new AppProtoView(null, null);
|
||||
}
|
||||
|
||||
function createView() {
|
||||
return new AppView(null, createProtoView(), MapWrapper.create());
|
||||
}
|
||||
|
||||
function createViewContainer(defaultProtoView = null) {
|
||||
return new ViewContainer(viewManager, location, defaultProtoView);
|
||||
}
|
||||
|
||||
beforeEach( () => {
|
||||
viewManager = new AppViewManagerSpy();
|
||||
view = createView();
|
||||
view.viewContainers = [null];
|
||||
location = new ElementRef(null, view, 0, null, null, null);
|
||||
});
|
||||
|
||||
it('should return a 0 length if there is no underlying ViewContainer', () => {
|
||||
var vc = createViewContainer();
|
||||
expect(vc.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should return the size of the underlying ViewContainer', () => {
|
||||
var vc = createViewContainer();
|
||||
view.viewContainers = [new InternalAppViewContainer()];
|
||||
view.viewContainers[0].views = [createView()];
|
||||
expect(vc.length).toBe(1);
|
||||
});
|
||||
|
||||
// TODO: add missing tests here!
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@proxy
|
||||
@IMPLEMENTS(AppViewManager)
|
||||
class AppViewManagerSpy extends SpyObject {
|
||||
constructor(){super(AppViewManager);}
|
||||
noSuchMethod(m){return super.noSuchMethod(m)}
|
||||
}
|
@ -1,179 +0,0 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
xdescribe,
|
||||
describe,
|
||||
el,
|
||||
dispatchEvent,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
beforeEachBindings,
|
||||
it,
|
||||
xit,
|
||||
SpyObject, proxy
|
||||
} from 'angular2/test_lib';
|
||||
import {IMPLEMENTS, isBlank} from 'angular2/src/facade/lang';
|
||||
import {ViewFactory} from 'angular2/src/core/compiler/view_factory';
|
||||
import {Renderer, ViewRef} from 'angular2/src/render/api';
|
||||
import {AppProtoView, AppView} from 'angular2/src/core/compiler/view';
|
||||
import {DirectiveBinding, ElementInjector} from 'angular2/src/core/compiler/element_injector';
|
||||
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
|
||||
import {Component} from 'angular2/src/core/annotations/annotations';
|
||||
import {ElementBinder} from 'angular2/src/core/compiler/element_binder';
|
||||
import {ChangeDetector, ProtoChangeDetector} from 'angular2/change_detection';
|
||||
|
||||
export function main() {
|
||||
describe('AppViewFactory', () => {
|
||||
var reader;
|
||||
var renderer;
|
||||
|
||||
beforeEach( () => {
|
||||
renderer = new SpyRenderer();
|
||||
reader = new DirectiveMetadataReader();
|
||||
});
|
||||
|
||||
function createViewFactory({capacity}):ViewFactory {
|
||||
return new ViewFactory(capacity, renderer);
|
||||
}
|
||||
|
||||
function createProtoChangeDetector() {
|
||||
var pcd = new SpyProtoChangeDetector();
|
||||
pcd.spy('instantiate').andCallFake( (dispatcher, bindingRecords, variableBindings, directiveRecords) => {
|
||||
return new SpyChangeDetector();
|
||||
});
|
||||
return pcd;
|
||||
}
|
||||
|
||||
function createProtoView(binders=null) {
|
||||
if (isBlank(binders)) {
|
||||
binders = [];
|
||||
}
|
||||
var pv = new AppProtoView(null, createProtoChangeDetector());
|
||||
pv.elementBinders = binders;
|
||||
return pv;
|
||||
}
|
||||
|
||||
function createDirectiveBinding(type) {
|
||||
var meta = reader.read(type);
|
||||
return DirectiveBinding.createFromType(meta.type, meta.annotation);
|
||||
}
|
||||
|
||||
function createComponentElBinder(binding, nestedProtoView = null) {
|
||||
var binder = new ElementBinder(0, null, 0, null, binding, null);
|
||||
binder.nestedProtoView = nestedProtoView;
|
||||
return binder;
|
||||
}
|
||||
|
||||
it('should create views without cache', () => {
|
||||
var pv = createProtoView();
|
||||
var vf = createViewFactory({
|
||||
capacity: 0
|
||||
});
|
||||
expect(vf.getView(pv) instanceof AppView).toBe(true);
|
||||
});
|
||||
|
||||
describe('caching', () => {
|
||||
|
||||
it('should support multiple AppProtoViews', () => {
|
||||
var pv1 = createProtoView();
|
||||
var pv2 = createProtoView();
|
||||
var vf = createViewFactory({ capacity: 2 });
|
||||
var view1 = vf.getView(pv1);
|
||||
var view2 = vf.getView(pv2);
|
||||
vf.returnView(view1);
|
||||
vf.returnView(view2);
|
||||
|
||||
expect(vf.getView(pv1)).toBe(view1);
|
||||
expect(vf.getView(pv2)).toBe(view2);
|
||||
});
|
||||
|
||||
it('should reuse the newest view that has been returned', () => {
|
||||
var pv = createProtoView();
|
||||
var vf = createViewFactory({ capacity: 2 });
|
||||
var view1 = vf.getView(pv);
|
||||
var view2 = vf.getView(pv);
|
||||
vf.returnView(view1);
|
||||
vf.returnView(view2);
|
||||
|
||||
expect(vf.getView(pv)).toBe(view2);
|
||||
});
|
||||
|
||||
it('should not add views when the capacity has been reached', () => {
|
||||
var pv = createProtoView();
|
||||
var vf = createViewFactory({ capacity: 2 });
|
||||
var view1 = vf.getView(pv);
|
||||
var view2 = vf.getView(pv);
|
||||
var view3 = vf.getView(pv);
|
||||
vf.returnView(view1);
|
||||
vf.returnView(view2);
|
||||
vf.returnView(view3);
|
||||
|
||||
expect(vf.getView(pv)).toBe(view2);
|
||||
expect(vf.getView(pv)).toBe(view1);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('child components', () => {
|
||||
|
||||
var vf;
|
||||
|
||||
beforeEach(() => {
|
||||
vf = createViewFactory({capacity: 1});
|
||||
});
|
||||
|
||||
it('should create static child component views', () => {
|
||||
var hostPv = createProtoView([
|
||||
createComponentElBinder(
|
||||
createDirectiveBinding(SomeComponent),
|
||||
createProtoView()
|
||||
)
|
||||
]);
|
||||
var hostView = vf.getView(hostPv);
|
||||
var shadowView = hostView.componentChildViews[0];
|
||||
expect(shadowView).toBeTruthy();
|
||||
expect(hostView.changeDetector.spy('addShadowDomChild')).toHaveBeenCalledWith(shadowView.changeDetector);
|
||||
});
|
||||
|
||||
it('should not create dynamic child component views', () => {
|
||||
var hostPv = createProtoView([
|
||||
createComponentElBinder(
|
||||
createDirectiveBinding(SomeComponent),
|
||||
null
|
||||
)
|
||||
]);
|
||||
var hostView = vf.getView(hostPv);
|
||||
var shadowView = hostView.componentChildViews[0];
|
||||
expect(shadowView).toBeFalsy();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Component({ selector: 'someComponent' })
|
||||
class SomeComponent {}
|
||||
|
||||
@proxy
|
||||
@IMPLEMENTS(Renderer)
|
||||
class SpyRenderer extends SpyObject {
|
||||
constructor(){super(Renderer);}
|
||||
noSuchMethod(m){return super.noSuchMethod(m)}
|
||||
}
|
||||
|
||||
@proxy
|
||||
@IMPLEMENTS(ChangeDetector)
|
||||
class SpyChangeDetector extends SpyObject {
|
||||
constructor(){super(ChangeDetector);}
|
||||
noSuchMethod(m){return super.noSuchMethod(m)}
|
||||
}
|
||||
|
||||
@proxy
|
||||
@IMPLEMENTS(ProtoChangeDetector)
|
||||
class SpyProtoChangeDetector extends SpyObject {
|
||||
constructor(){super(ProtoChangeDetector);}
|
||||
noSuchMethod(m){return super.noSuchMethod(m)}
|
||||
}
|
@ -1,282 +0,0 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
xdescribe,
|
||||
describe,
|
||||
el,
|
||||
dispatchEvent,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
beforeEachBindings,
|
||||
it,
|
||||
xit,
|
||||
SpyObject, proxy
|
||||
} from 'angular2/test_lib';
|
||||
import {IMPLEMENTS, isBlank, isPresent} from 'angular2/src/facade/lang';
|
||||
import {MapWrapper, ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
import {AppProtoView, AppView} from 'angular2/src/core/compiler/view';
|
||||
import {Renderer, ViewRef} from 'angular2/src/render/api';
|
||||
import {ChangeDetector} from 'angular2/change_detection';
|
||||
import {ElementBinder} from 'angular2/src/core/compiler/element_binder';
|
||||
import {DirectiveBinding, ElementInjector, ElementRef} from 'angular2/src/core/compiler/element_injector';
|
||||
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
|
||||
import {Component} from 'angular2/src/core/annotations/annotations';
|
||||
import {AppViewHydrator} from 'angular2/src/core/compiler/view_hydrator';
|
||||
import {ViewFactory} from 'angular2/src/core/compiler/view_factory';
|
||||
|
||||
export function main() {
|
||||
describe('AppViewHydrator', () => {
|
||||
var renderer;
|
||||
var reader;
|
||||
var hydrator;
|
||||
var viewFactory;
|
||||
|
||||
beforeEach( () => {
|
||||
renderer = new SpyRenderer();
|
||||
reader = new DirectiveMetadataReader();
|
||||
viewFactory = new SpyViewFactory();
|
||||
hydrator = new AppViewHydrator(renderer, viewFactory);
|
||||
});
|
||||
|
||||
function createDirectiveBinding(type) {
|
||||
var meta = reader.read(type);
|
||||
return DirectiveBinding.createFromType(meta.type, meta.annotation);
|
||||
}
|
||||
|
||||
function createElementInjector(overrides) {
|
||||
return SpyObject.stub(new SpyElementInjector(), {
|
||||
'isExportingComponent' : false,
|
||||
'isExportingElement' : false,
|
||||
'getEventEmitterAccessors' : [],
|
||||
'getComponent' : null
|
||||
}, overrides);
|
||||
}
|
||||
|
||||
function createEmptyElBinder() {
|
||||
return new ElementBinder(0, null, 0, null, null, null);
|
||||
}
|
||||
|
||||
function createComponentElBinder(binding, nestedProtoView = null) {
|
||||
var binder = new ElementBinder(0, null, 0, null, binding, null);
|
||||
binder.nestedProtoView = nestedProtoView;
|
||||
return binder;
|
||||
}
|
||||
|
||||
function createProtoView(binders = null) {
|
||||
if (isBlank(binders)) {
|
||||
binders = [];
|
||||
}
|
||||
var res = new AppProtoView(null, null);
|
||||
res.elementBinders = binders;
|
||||
return res;
|
||||
}
|
||||
|
||||
function createHostProtoView(nestedProtoView) {
|
||||
return createProtoView([
|
||||
createComponentElBinder(
|
||||
createDirectiveBinding(SomeComponent),
|
||||
nestedProtoView
|
||||
)
|
||||
]);
|
||||
}
|
||||
|
||||
function createEmptyView() {
|
||||
var view = new AppView(renderer, null, createProtoView(), MapWrapper.create());
|
||||
var changeDetector = new SpyChangeDetector();
|
||||
view.init(changeDetector, [], [], [], []);
|
||||
return view;
|
||||
}
|
||||
|
||||
function createHostView(pv, shadowView, componentInstance, elementInjectors = null) {
|
||||
var view = new AppView(renderer, null, pv, MapWrapper.create());
|
||||
var changeDetector = new SpyChangeDetector();
|
||||
|
||||
var eis;
|
||||
if (isPresent(elementInjectors)) {
|
||||
eis = elementInjectors;
|
||||
} else {
|
||||
eis = [createElementInjector({'getComponent': componentInstance})];
|
||||
}
|
||||
|
||||
view.init(changeDetector, eis, eis, ListWrapper.createFixedSize(eis.length), [shadowView]);
|
||||
return view;
|
||||
}
|
||||
|
||||
function hydrate(view) {
|
||||
hydrator.hydrateInPlaceHostView(null, null, view, null);
|
||||
}
|
||||
|
||||
function dehydrate(view) {
|
||||
hydrator.dehydrateInPlaceHostView(null, view);
|
||||
}
|
||||
|
||||
describe('hydrateDynamicComponentView', () => {
|
||||
|
||||
it('should not allow to use non component indices', () => {
|
||||
var pv = createProtoView([createEmptyElBinder()]);
|
||||
var view = createHostView(pv, null, null);
|
||||
var shadowView = createEmptyView();
|
||||
expect(
|
||||
() => hydrator.hydrateDynamicComponentView(new ElementRef(null, view, 0, null), shadowView, null, null)
|
||||
).toThrowError('There is no dynamic component directive at element 0');
|
||||
});
|
||||
|
||||
it('should not allow to use static component indices', () => {
|
||||
var pv = createHostProtoView(createProtoView());
|
||||
var view = createHostView(pv, null, null);
|
||||
var shadowView = createEmptyView();
|
||||
expect(
|
||||
() => hydrator.hydrateDynamicComponentView(new ElementRef(null, view, 0, null), shadowView, null, null)
|
||||
).toThrowError('There is no dynamic component directive at element 0');
|
||||
});
|
||||
|
||||
it('should not allow to overwrite an existing component', () => {
|
||||
var pv = createHostProtoView(null);
|
||||
var shadowView = createEmptyView();
|
||||
var view = createHostView(pv, null, null);
|
||||
renderer.spy('createDynamicComponentView').andReturn([new ViewRef(), new ViewRef()]);
|
||||
var elRef = new ElementRef(null, view, 0, null);
|
||||
hydrator.hydrateDynamicComponentView(elRef, shadowView, createDirectiveBinding(SomeComponent), null);
|
||||
expect(
|
||||
() => hydrator.hydrateDynamicComponentView(elRef, shadowView, null, null)
|
||||
).toThrowError('There already is a bound component at element 0');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('hydrate... shared functionality', () => {
|
||||
|
||||
it('should hydrate existing child components', () => {
|
||||
var hostPv = createHostProtoView(createProtoView());
|
||||
var componentInstance = new Object();
|
||||
var shadowView = createEmptyView();
|
||||
var hostView = createHostView(hostPv, shadowView, componentInstance);
|
||||
renderer.spy('createInPlaceHostView').andCallFake( (a,b,c) => {
|
||||
return [new ViewRef(), new ViewRef()];
|
||||
});
|
||||
|
||||
hydrate(hostView);
|
||||
|
||||
expect(shadowView.hydrated()).toBe(true);
|
||||
});
|
||||
|
||||
it("should set up event listeners", () => {
|
||||
var dir = new Object();
|
||||
|
||||
var hostPv = createProtoView([
|
||||
createComponentElBinder(createDirectiveBinding(SomeComponent)),
|
||||
createEmptyElBinder()
|
||||
]);
|
||||
|
||||
var spyEventAccessor1 = SpyObject.stub({"subscribe" : null});
|
||||
var ei1 = createElementInjector({
|
||||
'getEventEmitterAccessors': [[spyEventAccessor1]],
|
||||
'getDirectiveAtIndex': dir
|
||||
});
|
||||
|
||||
var spyEventAccessor2 = SpyObject.stub({"subscribe" : null});
|
||||
var ei2 = createElementInjector({
|
||||
'getEventEmitterAccessors': [[spyEventAccessor2]],
|
||||
'getDirectiveAtIndex': dir
|
||||
});
|
||||
|
||||
var shadowView = createEmptyView();
|
||||
var hostView = createHostView(hostPv, shadowView, null, [ei1, ei2]);
|
||||
renderer.spy('createInPlaceHostView').andReturn([new ViewRef(), new ViewRef()]);
|
||||
|
||||
hydrate(hostView);
|
||||
|
||||
expect(spyEventAccessor1.spy('subscribe')).toHaveBeenCalledWith(hostView, 0, dir);
|
||||
expect(spyEventAccessor2.spy('subscribe')).toHaveBeenCalledWith(hostView, 1, dir);
|
||||
});
|
||||
});
|
||||
|
||||
describe('dehydrate... shared functionality', () => {
|
||||
var hostView;
|
||||
var shadowView;
|
||||
|
||||
function createAndHydrate(nestedProtoView) {
|
||||
var componentInstance = new Object();
|
||||
shadowView = createEmptyView();
|
||||
var hostPv = createHostProtoView(nestedProtoView);
|
||||
hostView = createHostView(hostPv, shadowView, componentInstance);
|
||||
renderer.spy('createInPlaceHostView').andReturn([new ViewRef(), new ViewRef()]);
|
||||
|
||||
hydrate(hostView);
|
||||
}
|
||||
|
||||
it('should dehydrate child components', () => {
|
||||
createAndHydrate(createProtoView());
|
||||
dehydrate(hostView);
|
||||
|
||||
expect(shadowView.hydrated()).toBe(false);
|
||||
});
|
||||
|
||||
it('should not clear static child components', () => {
|
||||
createAndHydrate(createProtoView());
|
||||
dehydrate(hostView);
|
||||
|
||||
expect(hostView.componentChildViews[0]).toBe(shadowView);
|
||||
expect(hostView.changeDetector.spy('removeShadowDomChild')).not.toHaveBeenCalled();
|
||||
expect(viewFactory.spy('returnView')).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should clear dynamic child components', () => {
|
||||
createAndHydrate(null);
|
||||
dehydrate(hostView);
|
||||
|
||||
expect(hostView.componentChildViews[0]).toBe(null);
|
||||
expect(hostView.changeDetector.spy('removeShadowDomChild')).toHaveBeenCalledWith(shadowView.changeDetector);
|
||||
expect(viewFactory.spy('returnView')).toHaveBeenCalledWith(shadowView);
|
||||
});
|
||||
|
||||
it('should clear imperatively added child components', () => {
|
||||
createAndHydrate(createProtoView());
|
||||
var impHostView = createHostView(createHostProtoView(createProtoView()), createEmptyView(), null);
|
||||
shadowView.imperativeHostViews = [impHostView];
|
||||
|
||||
dehydrate(hostView);
|
||||
|
||||
expect(shadowView.imperativeHostViews).toEqual([]);
|
||||
expect(viewFactory.spy('returnView')).toHaveBeenCalledWith(impHostView);
|
||||
expect(shadowView.changeDetector.spy('removeChild')).toHaveBeenCalledWith(impHostView.changeDetector);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Component({ selector: 'someComponent' })
|
||||
class SomeComponent {}
|
||||
|
||||
@proxy
|
||||
@IMPLEMENTS(Renderer)
|
||||
class SpyRenderer extends SpyObject {
|
||||
constructor(){super(Renderer);}
|
||||
noSuchMethod(m){return super.noSuchMethod(m)}
|
||||
}
|
||||
|
||||
@proxy
|
||||
@IMPLEMENTS(ChangeDetector)
|
||||
class SpyChangeDetector extends SpyObject {
|
||||
constructor(){super(ChangeDetector);}
|
||||
noSuchMethod(m){return super.noSuchMethod(m)}
|
||||
}
|
||||
|
||||
@proxy
|
||||
@IMPLEMENTS(ElementInjector)
|
||||
class SpyElementInjector extends SpyObject {
|
||||
constructor(){super(ElementInjector);}
|
||||
noSuchMethod(m){return super.noSuchMethod(m)}
|
||||
}
|
||||
|
||||
@proxy
|
||||
@IMPLEMENTS(ViewFactory)
|
||||
class SpyViewFactory extends SpyObject {
|
||||
constructor(){super(ViewFactory);}
|
||||
noSuchMethod(m){return super.noSuchMethod(m)}
|
||||
}
|
518
modules/angular2/test/core/compiler/view_manager_spec.js
vendored
Normal file
518
modules/angular2/test/core/compiler/view_manager_spec.js
vendored
Normal file
@ -0,0 +1,518 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
xdescribe,
|
||||
describe,
|
||||
el,
|
||||
dispatchEvent,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
beforeEachBindings,
|
||||
it,
|
||||
xit,
|
||||
SpyObject, proxy
|
||||
} from 'angular2/test_lib';
|
||||
import {Injector, bind} from 'angular2/di';
|
||||
import {IMPLEMENTS, isBlank, isPresent} from 'angular2/src/facade/lang';
|
||||
import {MapWrapper, ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
import {AppProtoView, AppView, InternalAppViewContainer} from 'angular2/src/core/compiler/view';
|
||||
import {Renderer, ViewRef, ProtoViewRef, ViewContainerRef} from 'angular2/src/render/api';
|
||||
import {ElementBinder} from 'angular2/src/core/compiler/element_binder';
|
||||
import {DirectiveBinding, ElementInjector, ElementRef} from 'angular2/src/core/compiler/element_injector';
|
||||
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
|
||||
import {Component} from 'angular2/src/core/annotations/annotations';
|
||||
import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
|
||||
import {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils';
|
||||
import {AppViewPool} from 'angular2/src/core/compiler/view_pool';
|
||||
|
||||
export function main() {
|
||||
// TODO(tbosch): add missing tests
|
||||
|
||||
describe('AppViewManager', () => {
|
||||
var renderer;
|
||||
var utils;
|
||||
var viewPool;
|
||||
var manager;
|
||||
var reader;
|
||||
var createdViews;
|
||||
var createdRenderViews;
|
||||
|
||||
function elementRef(parentView, boundElementIndex) {
|
||||
return new ElementRef(null, parentView, boundElementIndex, null, null, null);
|
||||
}
|
||||
|
||||
function createDirectiveBinding(type) {
|
||||
var meta = reader.read(type);
|
||||
return DirectiveBinding.createFromType(meta.type, meta.annotation);
|
||||
}
|
||||
|
||||
function createEmptyElBinder() {
|
||||
return new ElementBinder(0, null, 0, null, null, null);
|
||||
}
|
||||
|
||||
function createComponentElBinder(nestedProtoView = null) {
|
||||
var binding = createDirectiveBinding(SomeComponent);
|
||||
var binder = new ElementBinder(0, null, 0, null, binding, null);
|
||||
binder.nestedProtoView = nestedProtoView;
|
||||
return binder;
|
||||
}
|
||||
|
||||
function createProtoView(binders = null) {
|
||||
if (isBlank(binders)) {
|
||||
binders = [];
|
||||
}
|
||||
var staticChildComponentCount = 0;
|
||||
for (var i = 0; i < binders.length; i++) {
|
||||
if (binders[i].hasStaticComponent()) {
|
||||
staticChildComponentCount++;
|
||||
}
|
||||
}
|
||||
var res = new AppProtoView(new MockProtoViewRef(staticChildComponentCount), null);
|
||||
res.elementBinders = binders;
|
||||
return res;
|
||||
}
|
||||
|
||||
function createElementInjector() {
|
||||
return SpyObject.stub(new SpyElementInjector(), {
|
||||
'isExportingComponent' : false,
|
||||
'isExportingElement' : false,
|
||||
'getEventEmitterAccessors' : [],
|
||||
'getComponent' : null
|
||||
}, {});
|
||||
}
|
||||
|
||||
function createView(pv=null) {
|
||||
if (isBlank(pv)) {
|
||||
pv = createProtoView();
|
||||
}
|
||||
var view = new AppView(renderer, pv, MapWrapper.create());
|
||||
var elementInjectors = ListWrapper.createFixedSize(pv.elementBinders.length);
|
||||
for (var i=0; i<pv.elementBinders.length; i++) {
|
||||
elementInjectors[i] = createElementInjector();
|
||||
}
|
||||
view.init(null,
|
||||
elementInjectors,
|
||||
[],
|
||||
ListWrapper.createFixedSize(pv.elementBinders.length),
|
||||
ListWrapper.createFixedSize(pv.elementBinders.length)
|
||||
);
|
||||
return view;
|
||||
}
|
||||
|
||||
beforeEach( () => {
|
||||
reader = new DirectiveMetadataReader();
|
||||
renderer = new SpyRenderer();
|
||||
utils = new SpyAppViewManagerUtils();
|
||||
viewPool = new SpyAppViewPool();
|
||||
manager = new AppViewManager(viewPool, utils, renderer);
|
||||
createdViews = [];
|
||||
createdRenderViews = [];
|
||||
|
||||
utils.spy('createView').andCallFake( (proto, _a, _b) => {
|
||||
var view = createView(proto);
|
||||
ListWrapper.push(createdViews, view);
|
||||
return view;
|
||||
});
|
||||
utils.spy('attachComponentView').andCallFake( (hostView, elementIndex, childView) => {
|
||||
hostView.componentChildViews[elementIndex] = childView;
|
||||
});
|
||||
utils.spy('attachViewInContainer').andCallFake( (parentView, elementIndex, atIndex, childView) => {
|
||||
var viewContainer = parentView.viewContainers[elementIndex];
|
||||
if (isBlank(viewContainer)) {
|
||||
viewContainer = new InternalAppViewContainer();
|
||||
parentView.viewContainers[elementIndex] = viewContainer;
|
||||
}
|
||||
ListWrapper.insert(viewContainer.views, atIndex, childView);
|
||||
});
|
||||
var createRenderViewRefs = function(renderPvRef) {
|
||||
var res = [];
|
||||
for (var i=0; i<renderPvRef.nestedComponentCount+1; i++) {
|
||||
var renderViewRef = new ViewRef();
|
||||
ListWrapper.push(res, renderViewRef);
|
||||
ListWrapper.push(createdRenderViews, renderViewRef);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
renderer.spy('createDynamicComponentView').andCallFake( (_a, _b, childPvRef) => {
|
||||
return createRenderViewRefs(childPvRef);
|
||||
});
|
||||
renderer.spy('createInPlaceHostView').andCallFake( (_a, _b, childPvRef) => {
|
||||
return createRenderViewRefs(childPvRef);
|
||||
});
|
||||
renderer.spy('createViewInContainer').andCallFake( (_a, _b, childPvRef) => {
|
||||
return createRenderViewRefs(childPvRef);
|
||||
});
|
||||
});
|
||||
|
||||
describe('createDynamicComponentView', () => {
|
||||
|
||||
describe('basic functionality', () => {
|
||||
var hostView, componentProtoView;
|
||||
beforeEach( () => {
|
||||
hostView = createView(createProtoView(
|
||||
[createComponentElBinder(null)]
|
||||
));
|
||||
hostView.render = new ViewRef();
|
||||
componentProtoView = createProtoView();
|
||||
});
|
||||
|
||||
it('should create the view', () => {
|
||||
expect(
|
||||
manager.createDynamicComponentView(elementRef(hostView, 0), componentProtoView, null, null)
|
||||
).toBe(createdViews[0]);
|
||||
expect(createdViews[0].proto).toBe(componentProtoView);
|
||||
});
|
||||
|
||||
it('should get the view from the pool', () => {
|
||||
var createdView;
|
||||
viewPool.spy('getView').andCallFake( (protoView) => {
|
||||
createdView = createView(protoView);
|
||||
return createdView;
|
||||
});
|
||||
expect(
|
||||
manager.createDynamicComponentView(elementRef(hostView, 0), componentProtoView, null, null)
|
||||
).toBe(createdView);
|
||||
expect(utils.spy('createView')).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should attach the view', () => {
|
||||
manager.createDynamicComponentView(elementRef(hostView, 0), componentProtoView, null, null)
|
||||
expect(utils.spy('attachComponentView')).toHaveBeenCalledWith(hostView, 0, createdViews[0]);
|
||||
});
|
||||
|
||||
it('should hydrate the dynamic component', () => {
|
||||
var injector = new Injector([], null, false);
|
||||
var componentBinding = bind(SomeComponent).toClass(SomeComponent);
|
||||
manager.createDynamicComponentView(elementRef(hostView, 0), componentProtoView, componentBinding, injector);
|
||||
expect(utils.spy('hydrateDynamicComponentInElementInjector')).toHaveBeenCalledWith(hostView, 0, componentBinding, injector);
|
||||
});
|
||||
|
||||
it('should hydrate the view', () => {
|
||||
manager.createDynamicComponentView(elementRef(hostView, 0), componentProtoView, null, null);
|
||||
expect(utils.spy('hydrateComponentView')).toHaveBeenCalledWith(hostView, 0);
|
||||
});
|
||||
|
||||
it('should create and set the render view', () => {
|
||||
manager.createDynamicComponentView(elementRef(hostView, 0), componentProtoView, null, null);
|
||||
expect(renderer.spy('createDynamicComponentView')).toHaveBeenCalledWith(hostView.render, 0, componentProtoView.render);
|
||||
expect(createdViews[0].render).toBe(createdRenderViews[0]);
|
||||
});
|
||||
|
||||
it('should set the event dispatcher', () => {
|
||||
manager.createDynamicComponentView(elementRef(hostView, 0), componentProtoView, null, null);
|
||||
var cmpView = createdViews[0];
|
||||
expect(renderer.spy('setEventDispatcher')).toHaveBeenCalledWith(cmpView.render, cmpView);
|
||||
});
|
||||
});
|
||||
|
||||
describe('error cases', () => {
|
||||
|
||||
it('should not allow to use non component indices', () => {
|
||||
var hostView = createView(createProtoView(
|
||||
[createEmptyElBinder()]
|
||||
));
|
||||
var componentProtoView = createProtoView();
|
||||
expect(
|
||||
() => manager.createDynamicComponentView(elementRef(hostView, 0), componentProtoView, null, null)
|
||||
).toThrowError('There is no dynamic component directive at element 0');
|
||||
});
|
||||
|
||||
it('should not allow to use static component indices', () => {
|
||||
var hostView = createView(createProtoView(
|
||||
[createComponentElBinder(createProtoView())]
|
||||
));
|
||||
var componentProtoView = createProtoView();
|
||||
expect(
|
||||
() => manager.createDynamicComponentView(elementRef(hostView, 0), componentProtoView, null, null)
|
||||
).toThrowError('There is no dynamic component directive at element 0');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('recurse into static child component views', () => {
|
||||
var hostView, componentProtoView, nestedProtoView;
|
||||
beforeEach( () => {
|
||||
hostView = createView(createProtoView(
|
||||
[createComponentElBinder(null)]
|
||||
));
|
||||
hostView.render = new ViewRef();
|
||||
nestedProtoView = createProtoView();
|
||||
componentProtoView = createProtoView([
|
||||
createComponentElBinder(nestedProtoView)
|
||||
]);
|
||||
});
|
||||
|
||||
it('should create the view', () => {
|
||||
manager.createDynamicComponentView(elementRef(hostView, 0), componentProtoView, null, null);
|
||||
expect(createdViews[0].proto).toBe(componentProtoView);
|
||||
expect(createdViews[1].proto).toBe(nestedProtoView);
|
||||
});
|
||||
|
||||
it('should hydrate the view', () => {
|
||||
manager.createDynamicComponentView(elementRef(hostView, 0), componentProtoView, null, null);
|
||||
expect(utils.spy('hydrateComponentView')).toHaveBeenCalledWith(createdViews[0], 0);
|
||||
});
|
||||
|
||||
it('should set the render view', () => {
|
||||
manager.createDynamicComponentView(elementRef(hostView, 0), componentProtoView, null, null);
|
||||
expect(createdViews[1].render).toBe(createdRenderViews[1])
|
||||
});
|
||||
|
||||
it('should set the event dispatcher', () => {
|
||||
manager.createDynamicComponentView(elementRef(hostView, 0), componentProtoView, null, null);
|
||||
var cmpView = createdViews[1];
|
||||
expect(renderer.spy('setEventDispatcher')).toHaveBeenCalledWith(cmpView.render, cmpView);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('createDynamicComponentView', () => {
|
||||
// TODO: implement this!
|
||||
describe('recurse into static child component views', () => {
|
||||
// TODO
|
||||
});
|
||||
|
||||
describe('recurse into dynamic child component views', () => {
|
||||
// TODO
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('createInPlaceHostView', () => {
|
||||
|
||||
// Note: We don't add tests for recursion or viewpool here as we assume that
|
||||
// this is using the same mechanism as the other methods...
|
||||
|
||||
describe('basic functionality', () => {
|
||||
var parentHostView, parentView, hostProtoView;
|
||||
beforeEach( () => {
|
||||
parentHostView = createView(createProtoView(
|
||||
[createComponentElBinder(null)]
|
||||
));
|
||||
parentView = createView();
|
||||
utils.attachComponentView(parentHostView, 0, parentView);
|
||||
parentView.render = new ViewRef();
|
||||
hostProtoView = createProtoView(
|
||||
[createComponentElBinder(null)]
|
||||
);
|
||||
});
|
||||
|
||||
it('should create the view', () => {
|
||||
expect(
|
||||
manager.createInPlaceHostView(elementRef(parentHostView, 0), null, hostProtoView, null)
|
||||
).toBe(createdViews[0]);
|
||||
expect(createdViews[0].proto).toBe(hostProtoView);
|
||||
});
|
||||
|
||||
it('should attachAndHydrate the view', () => {
|
||||
var injector = new Injector([], null, false);
|
||||
manager.createInPlaceHostView(elementRef(parentHostView, 0), null, hostProtoView, injector);
|
||||
expect(utils.spy('attachAndHydrateInPlaceHostView')).toHaveBeenCalledWith(parentHostView, 0, createdViews[0], injector);
|
||||
});
|
||||
|
||||
it('should create and set the render view', () => {
|
||||
var elementOrSelector = 'someSelector';
|
||||
manager.createInPlaceHostView(elementRef(parentHostView, 0), elementOrSelector, hostProtoView, null)
|
||||
expect(renderer.spy('createInPlaceHostView')).toHaveBeenCalledWith(parentView.render, elementOrSelector, hostProtoView.render);
|
||||
expect(createdViews[0].render).toBe(createdRenderViews[0]);
|
||||
});
|
||||
|
||||
it('should set the event dispatcher', () => {
|
||||
manager.createInPlaceHostView(elementRef(parentHostView, 0), null, hostProtoView, null);
|
||||
var cmpView = createdViews[0];
|
||||
expect(renderer.spy('setEventDispatcher')).toHaveBeenCalledWith(cmpView.render, cmpView);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('destroyInPlaceHostView', () => {
|
||||
describe('basic functionality', () => {
|
||||
var parentHostView, parentView, hostProtoView, hostView, hostRenderViewRef;
|
||||
beforeEach( () => {
|
||||
parentHostView = createView(createProtoView(
|
||||
[createComponentElBinder(null)]
|
||||
));
|
||||
parentView = createView();
|
||||
utils.attachComponentView(parentHostView, 0, parentView);
|
||||
parentView.render = new ViewRef();
|
||||
hostProtoView = createProtoView(
|
||||
[createComponentElBinder(null)]
|
||||
);
|
||||
hostView = manager.createInPlaceHostView(elementRef(parentHostView, 0), null, hostProtoView, null);
|
||||
hostRenderViewRef = hostView.render;
|
||||
});
|
||||
|
||||
it('should dehydrateAndDetach', () => {
|
||||
manager.destroyInPlaceHostView(elementRef(parentHostView, 0), hostView);
|
||||
expect(utils.spy('dehydrateAndDetachInPlaceHostView')).toHaveBeenCalledWith(parentView, hostView);
|
||||
});
|
||||
|
||||
it('should destroy and clear the render view', () => {
|
||||
manager.destroyInPlaceHostView(elementRef(parentHostView, 0), hostView);
|
||||
expect(renderer.spy('destroyInPlaceHostView')).toHaveBeenCalledWith(parentView.render, hostRenderViewRef);
|
||||
expect(hostView.render).toBe(null);
|
||||
});
|
||||
|
||||
it('should return the view to the pool', () => {
|
||||
manager.destroyInPlaceHostView(elementRef(parentHostView, 0), hostView);
|
||||
expect(viewPool.spy('returnView')).toHaveBeenCalledWith(hostView);
|
||||
});
|
||||
});
|
||||
|
||||
describe('recurse into imperativeHostViews', () => {
|
||||
// TODO
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('createViewInContainer', () => {
|
||||
|
||||
// Note: We don't add tests for recursion or viewpool here as we assume that
|
||||
// this is using the same mechanism as the other methods...
|
||||
|
||||
describe('basic functionality', () => {
|
||||
var parentView, childProtoView;
|
||||
beforeEach( () => {
|
||||
parentView = createView(createProtoView(
|
||||
[createEmptyElBinder()]
|
||||
));
|
||||
parentView.render = new ViewRef();
|
||||
childProtoView = createProtoView();
|
||||
});
|
||||
|
||||
it('should create a ViewContainer if not yet existing', () => {
|
||||
manager.createViewInContainer(elementRef(parentView, 0), 0, childProtoView, null);
|
||||
expect(parentView.viewContainers[0]).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should create the view', () => {
|
||||
expect(
|
||||
manager.createViewInContainer(elementRef(parentView, 0), 0, childProtoView, null)
|
||||
).toBe(createdViews[0]);
|
||||
expect(createdViews[0].proto).toBe(childProtoView);
|
||||
});
|
||||
|
||||
it('should attach the view', () => {
|
||||
manager.createViewInContainer(elementRef(parentView, 0), 0, childProtoView, null)
|
||||
expect(utils.spy('attachViewInContainer')).toHaveBeenCalledWith(parentView, 0, 0, createdViews[0]);
|
||||
});
|
||||
|
||||
it('should hydrate the view', () => {
|
||||
var injector = new Injector([], null, false);
|
||||
manager.createViewInContainer(elementRef(parentView, 0), 0, childProtoView, injector);
|
||||
expect(utils.spy('hydrateViewInContainer')).toHaveBeenCalledWith(parentView, 0, 0, injector);
|
||||
});
|
||||
|
||||
it('should create and set the render view', () => {
|
||||
manager.createViewInContainer(elementRef(parentView, 0), 0, childProtoView, null);
|
||||
expect(renderer.spy('createViewInContainer')).toHaveBeenCalledWith(
|
||||
new ViewContainerRef(parentView.render, 0), 0, childProtoView.render);
|
||||
expect(createdViews[0].render).toBe(createdRenderViews[0]);
|
||||
});
|
||||
|
||||
it('should set the event dispatcher', () => {
|
||||
manager.createViewInContainer(elementRef(parentView, 0), 0, childProtoView, null);
|
||||
var childView = createdViews[0];
|
||||
expect(renderer.spy('setEventDispatcher')).toHaveBeenCalledWith(childView.render, childView);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('destroyViewInContainer', () => {
|
||||
// Note: We don't add tests for recursion here as we assume that
|
||||
// this is using the same mechanism as the other methods...
|
||||
|
||||
describe('basic functionality', () => {
|
||||
var parentView, childProtoView, childView;
|
||||
beforeEach( () => {
|
||||
parentView = createView(createProtoView(
|
||||
[createEmptyElBinder()]
|
||||
));
|
||||
parentView.render = new ViewRef();
|
||||
childProtoView = createProtoView();
|
||||
childView = manager.createViewInContainer(elementRef(parentView, 0), 0, childProtoView, null);
|
||||
});
|
||||
|
||||
it('should dehydrate', () => {
|
||||
manager.destroyViewInContainer(elementRef(parentView, 0), 0);
|
||||
expect(utils.spy('dehydrateView')).toHaveBeenCalledWith(parentView.viewContainers[0].views[0]);
|
||||
});
|
||||
|
||||
it('should detach', () => {
|
||||
manager.destroyViewInContainer(elementRef(parentView, 0), 0);
|
||||
expect(utils.spy('detachViewInContainer')).toHaveBeenCalledWith(parentView, 0, 0);
|
||||
});
|
||||
|
||||
it('should destroy and clear the render view', () => {
|
||||
manager.destroyViewInContainer(elementRef(parentView, 0), 0);
|
||||
expect(renderer.spy('destroyViewInContainer')).toHaveBeenCalledWith(new ViewContainerRef(parentView.render, 0), 0);
|
||||
expect(childView.render).toBe(null);
|
||||
});
|
||||
|
||||
it('should return the view to the pool', () => {
|
||||
manager.destroyViewInContainer(elementRef(parentView, 0), 0);
|
||||
expect(viewPool.spy('returnView')).toHaveBeenCalledWith(childView);
|
||||
});
|
||||
});
|
||||
|
||||
describe('recurse into ViewContainers', () => {
|
||||
// TODO
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('attachViewInContainer', () => {
|
||||
|
||||
});
|
||||
|
||||
describe('detachViewInContainer', () => {
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
class MockProtoViewRef extends ProtoViewRef {
|
||||
nestedComponentCount:number;
|
||||
constructor(nestedComponentCount:number) {
|
||||
super();
|
||||
this.nestedComponentCount = nestedComponentCount;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({ selector: 'someComponent' })
|
||||
class SomeComponent {}
|
||||
|
||||
@proxy
|
||||
@IMPLEMENTS(Renderer)
|
||||
class SpyRenderer extends SpyObject {
|
||||
constructor(){super(Renderer);}
|
||||
noSuchMethod(m){return super.noSuchMethod(m)}
|
||||
}
|
||||
|
||||
@proxy
|
||||
@IMPLEMENTS(AppViewPool)
|
||||
class SpyAppViewPool extends SpyObject {
|
||||
constructor(){super(AppViewPool);}
|
||||
noSuchMethod(m){return super.noSuchMethod(m)}
|
||||
}
|
||||
|
||||
@proxy
|
||||
@IMPLEMENTS(AppViewManagerUtils)
|
||||
class SpyAppViewManagerUtils extends SpyObject {
|
||||
constructor(){super(AppViewManagerUtils);}
|
||||
noSuchMethod(m){return super.noSuchMethod(m)}
|
||||
}
|
||||
|
||||
@proxy
|
||||
@IMPLEMENTS(ElementInjector)
|
||||
class SpyElementInjector extends SpyObject {
|
||||
constructor(){super(ElementInjector);}
|
||||
noSuchMethod(m){return super.noSuchMethod(m)}
|
||||
}
|
168
modules/angular2/test/core/compiler/view_manager_utils_spec.js
vendored
Normal file
168
modules/angular2/test/core/compiler/view_manager_utils_spec.js
vendored
Normal file
@ -0,0 +1,168 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
xdescribe,
|
||||
describe,
|
||||
el,
|
||||
dispatchEvent,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
beforeEachBindings,
|
||||
it,
|
||||
xit,
|
||||
SpyObject, proxy
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import {Injector, bind} from 'angular2/di';
|
||||
import {IMPLEMENTS, isBlank, isPresent} from 'angular2/src/facade/lang';
|
||||
import {MapWrapper, ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
import {AppProtoView, AppView} from 'angular2/src/core/compiler/view';
|
||||
import {ChangeDetector} from 'angular2/change_detection';
|
||||
import {ElementBinder} from 'angular2/src/core/compiler/element_binder';
|
||||
import {DirectiveBinding, ElementInjector, ElementRef} from 'angular2/src/core/compiler/element_injector';
|
||||
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
|
||||
import {Component} from 'angular2/src/core/annotations/annotations';
|
||||
import {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils';
|
||||
|
||||
export function main() {
|
||||
// TODO(tbosch): add more tests here!
|
||||
|
||||
describe('AppViewManagerUtils', () => {
|
||||
|
||||
var metadataReader;
|
||||
var utils;
|
||||
|
||||
function createInjector() {
|
||||
return new Injector([], null, false);
|
||||
}
|
||||
|
||||
function createDirectiveBinding(type) {
|
||||
var meta = metadataReader.read(type);
|
||||
return DirectiveBinding.createFromType(meta.type, meta.annotation);
|
||||
}
|
||||
|
||||
function createEmptyElBinder() {
|
||||
return new ElementBinder(0, null, 0, null, null, null);
|
||||
}
|
||||
|
||||
function createComponentElBinder(nestedProtoView = null) {
|
||||
var binding = createDirectiveBinding(SomeComponent);
|
||||
var binder = new ElementBinder(0, null, 0, null, binding, null);
|
||||
binder.nestedProtoView = nestedProtoView;
|
||||
return binder;
|
||||
}
|
||||
|
||||
function createProtoView(binders = null) {
|
||||
if (isBlank(binders)) {
|
||||
binders = [];
|
||||
}
|
||||
var res = new AppProtoView(null, null);
|
||||
res.elementBinders = binders;
|
||||
return res;
|
||||
}
|
||||
|
||||
function createElementInjector() {
|
||||
return SpyObject.stub(new SpyElementInjector(), {
|
||||
'isExportingComponent' : false,
|
||||
'isExportingElement' : false,
|
||||
'getEventEmitterAccessors' : [],
|
||||
'getComponent' : null,
|
||||
'getDynamicallyLoadedComponent': null
|
||||
}, {});
|
||||
}
|
||||
|
||||
function createView(pv=null) {
|
||||
if (isBlank(pv)) {
|
||||
pv = createProtoView();
|
||||
}
|
||||
var view = new AppView(null, pv, MapWrapper.create());
|
||||
var elementInjectors = ListWrapper.createFixedSize(pv.elementBinders.length);
|
||||
for (var i=0; i<pv.elementBinders.length; i++) {
|
||||
elementInjectors[i] = createElementInjector();
|
||||
}
|
||||
view.init(new SpyChangeDetector(),
|
||||
elementInjectors,
|
||||
[],
|
||||
ListWrapper.createFixedSize(pv.elementBinders.length),
|
||||
ListWrapper.createFixedSize(pv.elementBinders.length)
|
||||
);
|
||||
return view;
|
||||
}
|
||||
|
||||
beforeEach( () => {
|
||||
metadataReader = new DirectiveMetadataReader();
|
||||
utils = new AppViewManagerUtils(metadataReader);
|
||||
});
|
||||
|
||||
describe('hydrateDynamicComponentInElementInjector', () => {
|
||||
|
||||
it('should not allow to overwrite an existing component', () => {
|
||||
var hostView = createView(createProtoView(
|
||||
[createComponentElBinder(createProtoView())]
|
||||
));
|
||||
var componentBinding = bind(SomeComponent).toClass(SomeComponent);
|
||||
SpyObject.stub(hostView.elementInjectors[0], {
|
||||
'getDynamicallyLoadedComponent': new SomeComponent()
|
||||
});
|
||||
expect(
|
||||
() => utils.hydrateDynamicComponentInElementInjector(hostView, 0, componentBinding, null)
|
||||
).toThrowError('There already is a dynamic component loaded at element 0');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('shared hydrate functionality', () => {
|
||||
|
||||
it("should set up event listeners", () => {
|
||||
var dir = new Object();
|
||||
|
||||
var hostPv = createProtoView([
|
||||
createComponentElBinder(null),
|
||||
createEmptyElBinder()
|
||||
]);
|
||||
var hostView = createView(hostPv);
|
||||
var spyEventAccessor1 = SpyObject.stub({"subscribe" : null});
|
||||
SpyObject.stub(hostView.elementInjectors[0], {
|
||||
'getEventEmitterAccessors': [[spyEventAccessor1]],
|
||||
'getDirectiveAtIndex': dir
|
||||
});
|
||||
var spyEventAccessor2 = SpyObject.stub({"subscribe" : null});
|
||||
SpyObject.stub(hostView.elementInjectors[1], {
|
||||
'getEventEmitterAccessors': [[spyEventAccessor2]],
|
||||
'getDirectiveAtIndex': dir
|
||||
});
|
||||
|
||||
var shadowView = createView();
|
||||
utils.attachComponentView(hostView, 0, shadowView);
|
||||
|
||||
utils.attachAndHydrateInPlaceHostView(null, null, hostView, createInjector());
|
||||
|
||||
expect(spyEventAccessor1.spy('subscribe')).toHaveBeenCalledWith(hostView, 0, dir);
|
||||
expect(spyEventAccessor2.spy('subscribe')).toHaveBeenCalledWith(hostView, 1, dir);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Component({ selector: 'someComponent' })
|
||||
class SomeComponent {}
|
||||
|
||||
@proxy
|
||||
@IMPLEMENTS(ElementInjector)
|
||||
class SpyElementInjector extends SpyObject {
|
||||
constructor(){super(ElementInjector);}
|
||||
noSuchMethod(m){return super.noSuchMethod(m)}
|
||||
}
|
||||
|
||||
@proxy
|
||||
@IMPLEMENTS(ChangeDetector)
|
||||
class SpyChangeDetector extends SpyObject {
|
||||
constructor(){super(ChangeDetector);}
|
||||
noSuchMethod(m){return super.noSuchMethod(m)}
|
||||
}
|
75
modules/angular2/test/core/compiler/view_pool_spec.js
vendored
Normal file
75
modules/angular2/test/core/compiler/view_pool_spec.js
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
xdescribe,
|
||||
describe,
|
||||
el,
|
||||
dispatchEvent,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
beforeEachBindings,
|
||||
it,
|
||||
xit,
|
||||
SpyObject, proxy
|
||||
} from 'angular2/test_lib';
|
||||
import {AppViewPool} from 'angular2/src/core/compiler/view_pool';
|
||||
import {AppProtoView, AppView} from 'angular2/src/core/compiler/view';
|
||||
import {MapWrapper, Map} from 'angular2/src/facade/collection';
|
||||
|
||||
export function main() {
|
||||
describe('AppViewPool', () => {
|
||||
|
||||
function createViewPool({capacity}):AppViewPool {
|
||||
return new AppViewPool(capacity);
|
||||
}
|
||||
|
||||
function createProtoView() {
|
||||
return new AppProtoView(null, null);
|
||||
}
|
||||
|
||||
function createView(pv) {
|
||||
return new AppView(null, pv, MapWrapper.create());
|
||||
}
|
||||
|
||||
it('should support multiple AppProtoViews', () => {
|
||||
var vf = createViewPool({ capacity: 2 });
|
||||
var pv1 = createProtoView();
|
||||
var pv2 = createProtoView();
|
||||
var view1 = createView(pv1);
|
||||
var view2 = createView(pv2);
|
||||
vf.returnView(view1);
|
||||
vf.returnView(view2);
|
||||
|
||||
expect(vf.getView(pv1)).toBe(view1);
|
||||
expect(vf.getView(pv2)).toBe(view2);
|
||||
});
|
||||
|
||||
it('should reuse the newest view that has been returned', () => {
|
||||
var pv = createProtoView();
|
||||
var vf = createViewPool({ capacity: 2 });
|
||||
var view1 = createView(pv);
|
||||
var view2 = createView(pv);
|
||||
vf.returnView(view1);
|
||||
vf.returnView(view2);
|
||||
|
||||
expect(vf.getView(pv)).toBe(view2);
|
||||
});
|
||||
|
||||
it('should not add views when the capacity has been reached', () => {
|
||||
var pv = createProtoView();
|
||||
var vf = createViewPool({ capacity: 2 });
|
||||
var view1 = createView(pv);
|
||||
var view2 = createView(pv);
|
||||
var view3 = createView(pv);
|
||||
vf.returnView(view1);
|
||||
vf.returnView(view2);
|
||||
vf.returnView(view3);
|
||||
|
||||
expect(vf.getView(pv)).toBe(view2);
|
||||
expect(vf.getView(pv)).toBe(view1);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
115
modules/angular2/test/core/compiler/view_spec.js
vendored
115
modules/angular2/test/core/compiler/view_spec.js
vendored
@ -1,115 +0,0 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
xdescribe,
|
||||
describe,
|
||||
el,
|
||||
dispatchEvent,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
beforeEachBindings,
|
||||
it,
|
||||
xit,
|
||||
SpyObject, proxy
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import {IMPLEMENTS, isBlank} from 'angular2/src/facade/lang';
|
||||
import {MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
import {AppProtoView, AppView} from 'angular2/src/core/compiler/view';
|
||||
import {ViewContainer} from 'angular2/src/core/compiler/view_container';
|
||||
import {Renderer} from 'angular2/src/render/api';
|
||||
import {ChangeDetector} from 'angular2/change_detection';
|
||||
import {ElementBinder} from 'angular2/src/core/compiler/element_binder';
|
||||
import {ElementInjector} from 'angular2/src/core/compiler/element_injector';
|
||||
|
||||
export function main() {
|
||||
describe('AppView', () => {
|
||||
var renderer;
|
||||
|
||||
beforeEach( () => {
|
||||
renderer = new SpyRenderer();
|
||||
});
|
||||
|
||||
function createElementInjector() {
|
||||
return new SpyElementInjector();
|
||||
}
|
||||
|
||||
function createEmptyElBinder() {
|
||||
return new ElementBinder(0, null, 0, null, null, null);
|
||||
}
|
||||
|
||||
function createEmbeddedProtoViewElBinder(nestedProtoView) {
|
||||
var binder = new ElementBinder(0, null, 0, null, null, null);
|
||||
binder.nestedProtoView = nestedProtoView;
|
||||
return binder;
|
||||
}
|
||||
|
||||
function createProtoView(binders = null) {
|
||||
if (isBlank(binders)) {
|
||||
binders = [];
|
||||
}
|
||||
var res = new AppProtoView(null, null);
|
||||
res.elementBinders = binders;
|
||||
return res;
|
||||
}
|
||||
|
||||
function createViewWithOneBoundElement(pv) {
|
||||
var view = new AppView(renderer, null, pv, MapWrapper.create());
|
||||
var changeDetector = new SpyChangeDetector();
|
||||
var eij = createElementInjector();
|
||||
view.init(changeDetector, [eij], [eij],
|
||||
[null], [null]);
|
||||
return view;
|
||||
}
|
||||
|
||||
describe('getOrCreateViewContainer()', () => {
|
||||
|
||||
it('should create a new container', () => {
|
||||
var pv = createProtoView([createEmptyElBinder()]);
|
||||
var view = createViewWithOneBoundElement(pv);
|
||||
expect(view.getOrCreateViewContainer(0) instanceof ViewContainer).toBe(true);
|
||||
});
|
||||
|
||||
it('should return an existing container', () => {
|
||||
var pv = createProtoView([createEmptyElBinder()]);
|
||||
var view = createViewWithOneBoundElement(pv);
|
||||
var vc = view.getOrCreateViewContainer(0);
|
||||
expect(view.getOrCreateViewContainer(0)).toBe(vc);
|
||||
});
|
||||
|
||||
it('should store an existing nestedProtoView in the container', () => {
|
||||
var defaultProtoView = createProtoView();
|
||||
var pv = createProtoView([createEmbeddedProtoViewElBinder(defaultProtoView)]);
|
||||
var view = createViewWithOneBoundElement(pv);
|
||||
var vc = view.getOrCreateViewContainer(0);
|
||||
expect(vc.defaultProtoView).toBe(defaultProtoView);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@proxy
|
||||
@IMPLEMENTS(Renderer)
|
||||
class SpyRenderer extends SpyObject {
|
||||
constructor(){super(Renderer);}
|
||||
noSuchMethod(m){return super.noSuchMethod(m)}
|
||||
}
|
||||
|
||||
@proxy
|
||||
@IMPLEMENTS(ChangeDetector)
|
||||
class SpyChangeDetector extends SpyObject {
|
||||
constructor(){super(ChangeDetector);}
|
||||
noSuchMethod(m){return super.noSuchMethod(m)}
|
||||
}
|
||||
|
||||
@proxy
|
||||
@IMPLEMENTS(ElementInjector)
|
||||
class SpyElementInjector extends SpyObject {
|
||||
constructor(){super(ElementInjector);}
|
||||
noSuchMethod(m){return super.noSuchMethod(m)}
|
||||
}
|
Reference in New Issue
Block a user