refactor(render): remove recursion from renderer
The goal is to make implementing a renderer straight forward.
BREAKING_CHANGE:
- Renderer interface was redone / simplified.
- `DirectDomRenderer` was replaced by `DomRenderer`.
- `DirectDomRenderer.setImperativeComponentRootNodes` is replaced
by the following 2 steps:
1. `ViewManager.getComponentView(elementRef) -> ViewRef`
2. `DomRenderer.setComponentViewRootNodes(viewRef, rootNodes)`
- all `@View` annotations need to have a template, but the template
may be empty. Previously views that had a `renderer` property did
not have to have a `template`.
- `dynamicComponentLoader.loadIntoNewLocation` does no more allow
to pass an element, but requires a css selector.
Special syntax: `:document` can be used as prefix to search globally
on the document instead of in the provided parent view.
Part of #1675
This commit is contained in:
+9
-8
@@ -11,7 +11,7 @@ import {
|
||||
xit,
|
||||
} from 'angular2/test_lib';
|
||||
import {bootstrap} from 'angular2/src/core/application';
|
||||
import {appDocumentToken, appElementToken} from 'angular2/src/core/application_tokens';
|
||||
import {appComponentAnnotatedTypeToken} from 'angular2/src/core/application_tokens';
|
||||
import {Component, Directive} from 'angular2/src/core/annotations_impl/annotations';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||
@@ -21,6 +21,7 @@ import {Inject} from 'angular2/src/di/annotations_impl';
|
||||
import {View} from 'angular2/src/core/annotations_impl/view';
|
||||
import {LifeCycle} from 'angular2/src/core/life_cycle/life_cycle';
|
||||
import {Testability, TestabilityRegistry} from 'angular2/src/core/testability/testability';
|
||||
import {DOCUMENT_TOKEN} from 'angular2/src/render/dom/dom_renderer';
|
||||
|
||||
@Component({selector: 'hello-app'})
|
||||
@View({template: '{{greeting}} world!'})
|
||||
@@ -84,7 +85,7 @@ export function main() {
|
||||
DOM.appendChild(fakeDoc.body, el2);
|
||||
DOM.appendChild(el, lightDom);
|
||||
DOM.setText(lightDom, 'loading');
|
||||
testBindings = [bind(appDocumentToken).toValue(fakeDoc)];
|
||||
testBindings = [bind(DOCUMENT_TOKEN).toValue(fakeDoc)];
|
||||
});
|
||||
|
||||
describe('bootstrap factory method', () => {
|
||||
@@ -100,7 +101,7 @@ export function main() {
|
||||
var refPromise = bootstrap(HelloRootCmp, [], (e,t) => {throw e;});
|
||||
PromiseWrapper.then(refPromise, null, (reason) => {
|
||||
expect(reason.message).toContain(
|
||||
'The app selector "hello-app" did not match any elements');
|
||||
'The selector "hello-app" did not match any elements');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
@@ -113,7 +114,7 @@ export function main() {
|
||||
it('should resolve an injector promise and contain bindings', inject([AsyncTestCompleter], (async) => {
|
||||
var refPromise = bootstrap(HelloRootCmp, testBindings);
|
||||
refPromise.then((ref) => {
|
||||
expect(ref.injector.get(appElementToken)).toBe(el);
|
||||
expect(ref.injector.get(appComponentAnnotatedTypeToken).type).toBe(HelloRootCmp);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
@@ -129,7 +130,7 @@ export function main() {
|
||||
it('should display hello world', inject([AsyncTestCompleter], (async) => {
|
||||
var refPromise = bootstrap(HelloRootCmp, testBindings);
|
||||
refPromise.then((ref) => {
|
||||
expect(ref.injector.get(appElementToken)).toHaveText('hello world!');
|
||||
expect(el).toHaveText('hello world!');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
@@ -138,8 +139,8 @@ export function main() {
|
||||
var refPromise1 = bootstrap(HelloRootCmp, testBindings);
|
||||
var refPromise2 = bootstrap(HelloRootCmp2, testBindings);
|
||||
PromiseWrapper.all([refPromise1, refPromise2]).then((refs) => {
|
||||
expect(refs[0].injector.get(appElementToken)).toHaveText('hello world!');
|
||||
expect(refs[1].injector.get(appElementToken)).toHaveText('hello world, again!');
|
||||
expect(el).toHaveText('hello world!');
|
||||
expect(el2).toHaveText('hello world, again!');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
@@ -168,7 +169,7 @@ export function main() {
|
||||
it("should support shadow dom content tag", inject([AsyncTestCompleter], (async) => {
|
||||
var refPromise = bootstrap(HelloRootCmpContent, testBindings);
|
||||
refPromise.then((ref) => {
|
||||
expect(ref.injector.get(appElementToken)).toHaveText('before: loading after: done');
|
||||
expect(el).toHaveText('before: loading after: done');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
@@ -391,25 +391,6 @@ export function main() {
|
||||
});
|
||||
}));
|
||||
|
||||
it('should create imperative proto views', inject([AsyncTestCompleter], (async) => {
|
||||
renderCompiler.spy('createImperativeComponentProtoView').andCallFake( (rendererId) => {
|
||||
return PromiseWrapper.resolve(
|
||||
createRenderProtoView([])
|
||||
);
|
||||
});
|
||||
tplResolver.setView(MainComponent, new View({renderer: 'some-renderer'}));
|
||||
var mainProtoView = createProtoView();
|
||||
var compiler = createCompiler(
|
||||
[],
|
||||
[mainProtoView]
|
||||
);
|
||||
compiler.compile(MainComponent).then( (protoViewRef) => {
|
||||
expect(internalProtoView(protoViewRef)).toBe(mainProtoView);
|
||||
expect(renderCompiler.spy('createImperativeComponentProtoView')).toHaveBeenCalledWith('some-renderer');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should throw for non component types', () => {
|
||||
var compiler = createCompiler([], []);
|
||||
expect(
|
||||
|
||||
@@ -21,9 +21,9 @@ import {View} from 'angular2/src/core/annotations_impl/view';
|
||||
import {DynamicComponentLoader} from 'angular2/src/core/compiler/dynamic_component_loader';
|
||||
import {ElementRef} from 'angular2/src/core/compiler/element_ref';
|
||||
import {If} from 'angular2/src/directives/if';
|
||||
import {DirectDomRenderer} from 'angular2/src/render/dom/direct_dom_renderer';
|
||||
import {DomRenderer} from 'angular2/src/render/dom/dom_renderer';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
|
||||
|
||||
export function main() {
|
||||
describe('DynamicComponentLoader', function () {
|
||||
@@ -200,15 +200,17 @@ export function main() {
|
||||
selector: 'imp-ng-cmp'
|
||||
})
|
||||
@View({
|
||||
renderer: 'imp-ng-cmp-renderer'
|
||||
renderer: 'imp-ng-cmp-renderer',
|
||||
template: ''
|
||||
})
|
||||
class ImperativeViewComponentUsingNgComponent {
|
||||
done;
|
||||
|
||||
constructor(self:ElementRef, dynamicComponentLoader:DynamicComponentLoader, renderer:DirectDomRenderer) {
|
||||
var div = el('<div></div>');
|
||||
renderer.setImperativeComponentRootNodes(self.parentView.render, self.boundElementIndex, [div]);
|
||||
this.done = dynamicComponentLoader.loadIntoNewLocation(ChildComp, self, div, null);
|
||||
constructor(self:ElementRef, dynamicComponentLoader:DynamicComponentLoader, viewManager:AppViewManager, renderer:DomRenderer) {
|
||||
var div = el('<div id="impHost"></div>');
|
||||
var shadowViewRef = viewManager.getComponentView(self);
|
||||
renderer.setComponentViewRootNodes(shadowViewRef.render, [div]);
|
||||
this.done = dynamicComponentLoader.loadIntoNewLocation(ChildComp, self, '#impHost', null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+7
-4
@@ -37,7 +37,8 @@ import {ProtoViewRef} from 'angular2/src/core/compiler/view_ref';
|
||||
import {Compiler} from 'angular2/src/core/compiler/compiler';
|
||||
import {ElementRef} from 'angular2/src/core/compiler/element_ref';
|
||||
|
||||
import {DirectDomRenderer} from 'angular2/src/render/dom/direct_dom_renderer';
|
||||
import {DomRenderer} from 'angular2/src/render/dom/dom_renderer';
|
||||
import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
|
||||
|
||||
export function main() {
|
||||
describe('integration tests', function() {
|
||||
@@ -863,13 +864,15 @@ export function main() {
|
||||
selector: 'simple-imp-cmp'
|
||||
})
|
||||
@View({
|
||||
renderer: 'simple-imp-cmp-renderer'
|
||||
renderer: 'simple-imp-cmp-renderer',
|
||||
template: ''
|
||||
})
|
||||
class SimpleImperativeViewComponent {
|
||||
done;
|
||||
|
||||
constructor(self:ElementRef, renderer:DirectDomRenderer) {
|
||||
renderer.setImperativeComponentRootNodes(self.parentView.render, self.boundElementIndex, [el('hello imp view')]);
|
||||
constructor(self:ElementRef, viewManager:AppViewManager, renderer:DomRenderer) {
|
||||
var shadowViewRef = viewManager.getComponentView(self);
|
||||
renderer.setComponentViewRootNodes(shadowViewRef.render, [el('hello imp view')]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+33
-46
@@ -94,11 +94,15 @@ export function main() {
|
||||
}, {});
|
||||
}
|
||||
|
||||
function createView(pv=null) {
|
||||
function createView(pv=null, renderViewRef=null) {
|
||||
if (isBlank(pv)) {
|
||||
pv = createProtoView();
|
||||
}
|
||||
if (isBlank(renderViewRef)) {
|
||||
renderViewRef = new RenderViewRef();
|
||||
}
|
||||
var view = new AppView(renderer, pv, MapWrapper.create());
|
||||
view.render = renderViewRef;
|
||||
var elementInjectors = ListWrapper.createFixedSize(pv.elementBinders.length);
|
||||
for (var i=0; i<pv.elementBinders.length; i++) {
|
||||
elementInjectors[i] = createElementInjector();
|
||||
@@ -121,8 +125,8 @@ export function main() {
|
||||
createdViews = [];
|
||||
createdRenderViews = [];
|
||||
|
||||
utils.spy('createView').andCallFake( (proto, _a, _b) => {
|
||||
var view = createView(proto);
|
||||
utils.spy('createView').andCallFake( (proto, renderViewRef, _a, _b) => {
|
||||
var view = createView(proto, renderViewRef);
|
||||
ListWrapper.push(createdViews, view);
|
||||
return view;
|
||||
});
|
||||
@@ -137,23 +141,15 @@ export function main() {
|
||||
}
|
||||
ListWrapper.insert(viewContainer.views, atIndex, childView);
|
||||
});
|
||||
var createRenderViewRefs = function(renderPvRef) {
|
||||
var res = [];
|
||||
for (var i=0; i<renderPvRef.nestedComponentCount+1; i++) {
|
||||
var renderViewRef = new RenderViewRef();
|
||||
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, _c) => {
|
||||
var rv = new RenderViewRef();
|
||||
ListWrapper.push(createdRenderViews, rv);
|
||||
return rv;
|
||||
});
|
||||
renderer.spy('createInPlaceHostView').andCallFake( (_a, _b, childPvRef) => {
|
||||
return createRenderViewRefs(childPvRef);
|
||||
});
|
||||
renderer.spy('createViewInContainer').andCallFake( (_a, _b, childPvRef) => {
|
||||
return createRenderViewRefs(childPvRef);
|
||||
renderer.spy('createView').andCallFake( (_a) => {
|
||||
var rv = new RenderViewRef();
|
||||
ListWrapper.push(createdRenderViews, rv);
|
||||
return rv;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -165,7 +161,6 @@ export function main() {
|
||||
hostView = createView(createProtoView(
|
||||
[createComponentElBinder(null)]
|
||||
));
|
||||
hostView.render = new RenderViewRef();
|
||||
componentProtoView = createProtoView();
|
||||
});
|
||||
|
||||
@@ -186,11 +181,13 @@ export function main() {
|
||||
internalView(manager.createDynamicComponentView(elementRef(wrapView(hostView), 0), wrapPv(componentProtoView), null, null))
|
||||
).toBe(createdView);
|
||||
expect(utils.spy('createView')).not.toHaveBeenCalled();
|
||||
expect(renderer.spy('createView')).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should attach the view', () => {
|
||||
manager.createDynamicComponentView(elementRef(wrapView(hostView), 0), wrapPv(componentProtoView), null, null)
|
||||
expect(utils.spy('attachComponentView')).toHaveBeenCalledWith(hostView, 0, createdViews[0]);
|
||||
expect(renderer.spy('attachComponentView')).toHaveBeenCalledWith(hostView.render, 0, createdViews[0].render);
|
||||
});
|
||||
|
||||
it('should hydrate the dynamic component', () => {
|
||||
@@ -203,11 +200,12 @@ export function main() {
|
||||
it('should hydrate the view', () => {
|
||||
manager.createDynamicComponentView(elementRef(wrapView(hostView), 0), wrapPv(componentProtoView), null, null);
|
||||
expect(utils.spy('hydrateComponentView')).toHaveBeenCalledWith(hostView, 0);
|
||||
expect(renderer.spy('hydrateView')).toHaveBeenCalledWith(createdViews[0].render);
|
||||
});
|
||||
|
||||
it('should create and set the render view', () => {
|
||||
manager.createDynamicComponentView(elementRef(wrapView(hostView), 0), wrapPv(componentProtoView), null, null);
|
||||
expect(renderer.spy('createDynamicComponentView')).toHaveBeenCalledWith(hostView.render, 0, componentProtoView.render);
|
||||
expect(renderer.spy('createView')).toHaveBeenCalledWith(componentProtoView.render);
|
||||
expect(createdViews[0].render).toBe(createdRenderViews[0]);
|
||||
});
|
||||
|
||||
@@ -256,7 +254,6 @@ export function main() {
|
||||
hostView = createView(createProtoView(
|
||||
[createComponentElBinder(null)]
|
||||
));
|
||||
hostView.render = new RenderViewRef();
|
||||
nestedProtoView = createProtoView();
|
||||
componentProtoView = createProtoView([
|
||||
createComponentElBinder(nestedProtoView)
|
||||
@@ -272,6 +269,7 @@ export function main() {
|
||||
it('should hydrate the view', () => {
|
||||
manager.createDynamicComponentView(elementRef(wrapView(hostView), 0), wrapPv(componentProtoView), null, null);
|
||||
expect(utils.spy('hydrateComponentView')).toHaveBeenCalledWith(createdViews[0], 0);
|
||||
expect(renderer.spy('hydrateView')).toHaveBeenCalledWith(createdViews[0].render);
|
||||
});
|
||||
|
||||
it('should set the render view', () => {
|
||||
@@ -309,7 +307,6 @@ export function main() {
|
||||
));
|
||||
parentView = createView();
|
||||
utils.attachComponentView(parentHostView, 0, parentView);
|
||||
parentView.render = new RenderViewRef();
|
||||
hostProtoView = createProtoView(
|
||||
[createComponentElBinder(null)]
|
||||
);
|
||||
@@ -326,6 +323,7 @@ export function main() {
|
||||
var injector = new Injector([], null, false);
|
||||
manager.createInPlaceHostView(elementRef(wrapView(parentHostView), 0), null, wrapPv(hostProtoView), injector);
|
||||
expect(utils.spy('attachAndHydrateInPlaceHostView')).toHaveBeenCalledWith(parentHostView, 0, createdViews[0], injector);
|
||||
expect(renderer.spy('hydrateView')).toHaveBeenCalledWith(createdViews[0].render);
|
||||
});
|
||||
|
||||
it('should create and set the render view', () => {
|
||||
@@ -354,7 +352,6 @@ export function main() {
|
||||
));
|
||||
parentView = createView();
|
||||
utils.attachComponentView(parentHostView, 0, parentView);
|
||||
parentView.render = new RenderViewRef();
|
||||
hostProtoView = createProtoView(
|
||||
[createComponentElBinder(null)]
|
||||
);
|
||||
@@ -362,25 +359,25 @@ export function main() {
|
||||
hostRenderViewRef = hostView.render;
|
||||
});
|
||||
|
||||
it('should dehydrate', () => {
|
||||
it('should detach', () => {
|
||||
manager.destroyInPlaceHostView(elementRef(wrapView(parentHostView), 0), wrapView(hostView));
|
||||
expect(utils.spy('detachInPlaceHostView')).toHaveBeenCalledWith(parentView, hostView);
|
||||
});
|
||||
|
||||
it('should detach', () => {
|
||||
it('should dehydrate', () => {
|
||||
manager.destroyInPlaceHostView(elementRef(wrapView(parentHostView), 0), wrapView(hostView));
|
||||
expect(utils.spy('dehydrateView')).toHaveBeenCalledWith(hostView);
|
||||
expect(renderer.spy('dehydrateView')).toHaveBeenCalledWith(hostView.render);
|
||||
});
|
||||
|
||||
it('should destroy and clear the render view', () => {
|
||||
manager.destroyInPlaceHostView(elementRef(wrapView(parentHostView), 0), wrapView(hostView));
|
||||
expect(renderer.spy('destroyInPlaceHostView')).toHaveBeenCalledWith(parentView.render, hostRenderViewRef);
|
||||
expect(hostView.render).toBe(null);
|
||||
});
|
||||
|
||||
it('should return the view to the pool', () => {
|
||||
it('should not return the view to the pool', () => {
|
||||
manager.destroyInPlaceHostView(elementRef(wrapView(parentHostView), 0), wrapView(hostView));
|
||||
expect(viewPool.spy('returnView')).toHaveBeenCalledWith(hostView);
|
||||
expect(viewPool.spy('returnView')).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -398,7 +395,6 @@ export function main() {
|
||||
parentView = createView(createProtoView(
|
||||
[createEmptyElBinder()]
|
||||
));
|
||||
parentView.render = new RenderViewRef();
|
||||
childProtoView = createProtoView();
|
||||
});
|
||||
|
||||
@@ -417,18 +413,19 @@ export function main() {
|
||||
it('should attach the view', () => {
|
||||
manager.createViewInContainer(elementRef(wrapView(parentView), 0), 0, wrapPv(childProtoView), null)
|
||||
expect(utils.spy('attachViewInContainer')).toHaveBeenCalledWith(parentView, 0, 0, createdViews[0]);
|
||||
expect(renderer.spy('attachViewInContainer')).toHaveBeenCalledWith(parentView.render, 0, 0, createdViews[0].render);
|
||||
});
|
||||
|
||||
it('should hydrate the view', () => {
|
||||
var injector = new Injector([], null, false);
|
||||
manager.createViewInContainer(elementRef(wrapView(parentView), 0), 0, wrapPv(childProtoView), injector);
|
||||
expect(utils.spy('hydrateViewInContainer')).toHaveBeenCalledWith(parentView, 0, 0, injector);
|
||||
expect(renderer.spy('hydrateView')).toHaveBeenCalledWith(createdViews[0].render);
|
||||
});
|
||||
|
||||
it('should create and set the render view', () => {
|
||||
manager.createViewInContainer(elementRef(wrapView(parentView), 0), 0, wrapPv(childProtoView), null);
|
||||
expect(renderer.spy('createViewInContainer')).toHaveBeenCalledWith(
|
||||
new RenderViewContainerRef(parentView.render, 0), 0, childProtoView.render);
|
||||
expect(renderer.spy('createView')).toHaveBeenCalledWith(childProtoView.render);
|
||||
expect(createdViews[0].render).toBe(createdRenderViews[0]);
|
||||
});
|
||||
|
||||
@@ -449,7 +446,6 @@ export function main() {
|
||||
parentView = createView(createProtoView(
|
||||
[createEmptyElBinder()]
|
||||
));
|
||||
parentView.render = new RenderViewRef();
|
||||
childProtoView = createProtoView();
|
||||
childView = internalView(manager.createViewInContainer(elementRef(wrapView(parentView), 0), 0, wrapPv(childProtoView), null));
|
||||
});
|
||||
@@ -457,17 +453,13 @@ export function main() {
|
||||
it('should dehydrate', () => {
|
||||
manager.destroyViewInContainer(elementRef(wrapView(parentView), 0), 0);
|
||||
expect(utils.spy('dehydrateView')).toHaveBeenCalledWith(parentView.viewContainers[0].views[0]);
|
||||
expect(renderer.spy('dehydrateView')).toHaveBeenCalledWith(childView.render);
|
||||
});
|
||||
|
||||
it('should detach', () => {
|
||||
manager.destroyViewInContainer(elementRef(wrapView(parentView), 0), 0);
|
||||
expect(utils.spy('detachViewInContainer')).toHaveBeenCalledWith(parentView, 0, 0);
|
||||
});
|
||||
|
||||
it('should destroy and clear the render view', () => {
|
||||
manager.destroyViewInContainer(elementRef(wrapView(parentView), 0), 0);
|
||||
expect(renderer.spy('destroyViewInContainer')).toHaveBeenCalledWith(new RenderViewContainerRef(parentView.render, 0), 0);
|
||||
expect(childView.render).toBe(null);
|
||||
expect(renderer.spy('detachViewInContainer')).toHaveBeenCalledWith(parentView.render, 0, 0, childView.render);
|
||||
});
|
||||
|
||||
it('should return the view to the pool', () => {
|
||||
@@ -482,7 +474,6 @@ export function main() {
|
||||
parentView = createView(createProtoView(
|
||||
[createEmptyElBinder()]
|
||||
));
|
||||
parentView.render = new RenderViewRef();
|
||||
childProtoView = createProtoView();
|
||||
childView = internalView(manager.createViewInContainer(elementRef(wrapView(parentView), 0), 0, wrapPv(childProtoView), null));
|
||||
});
|
||||
@@ -490,17 +481,13 @@ export function main() {
|
||||
it('should dehydrate', () => {
|
||||
manager.destroyInPlaceHostView(null, wrapView(parentView));
|
||||
expect(utils.spy('dehydrateView')).toHaveBeenCalledWith(parentView.viewContainers[0].views[0]);
|
||||
expect(renderer.spy('dehydrateView')).toHaveBeenCalledWith(childView.render);
|
||||
});
|
||||
|
||||
it('should detach', () => {
|
||||
manager.destroyInPlaceHostView(null, wrapView(parentView));
|
||||
expect(utils.spy('detachViewInContainer')).toHaveBeenCalledWith(parentView, 0, 0);
|
||||
});
|
||||
|
||||
it('should not destroy but clear the render view', () => {
|
||||
manager.destroyInPlaceHostView(null, wrapView(parentView));
|
||||
expect(renderer.spy('destroyViewInContainer')).not.toHaveBeenCalled();
|
||||
expect(childView.render).toBe(null);
|
||||
expect(renderer.spy('detachViewInContainer')).toHaveBeenCalledWith(parentView.render, 0, 0, childView.render);
|
||||
});
|
||||
|
||||
it('should return the view to the pool', () => {
|
||||
|
||||
@@ -26,6 +26,8 @@ import {TemplateLoader} from 'angular2/src/render/dom/compiler/template_loader';
|
||||
|
||||
import {UrlResolver} from 'angular2/src/services/url_resolver';
|
||||
|
||||
import {resolveInternalDomProtoView} from 'angular2/src/render/dom/view/proto_view';
|
||||
|
||||
export function runCompilerCommonTests() {
|
||||
describe('DomCompiler', function() {
|
||||
var mockStepFactory;
|
||||
@@ -61,7 +63,7 @@ export function runCompilerCommonTests() {
|
||||
|
||||
var dirMetadata = new DirectiveMetadata({id: 'id', selector: 'CUSTOM', type: DirectiveMetadata.COMPONENT_TYPE});
|
||||
compiler.compileHost(dirMetadata).then( (protoView) => {
|
||||
expect(DOM.tagName(protoView.render.delegate.element)).toEqual('CUSTOM')
|
||||
expect(DOM.tagName(resolveInternalDomProtoView(protoView.render).element)).toEqual('CUSTOM')
|
||||
expect(mockStepFactory.viewDef.directives).toEqual([dirMetadata]);
|
||||
expect(protoView.variableBindings).toEqual(MapWrapper.createFromStringMap({
|
||||
'a': 'b'
|
||||
@@ -76,7 +78,7 @@ export function runCompilerCommonTests() {
|
||||
componentId: 'someId',
|
||||
template: 'inline component'
|
||||
})).then( (protoView) => {
|
||||
expect(DOM.getInnerHTML(protoView.render.delegate.element)).toEqual('inline component');
|
||||
expect(DOM.getInnerHTML(resolveInternalDomProtoView(protoView.render).element)).toEqual('inline component');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
@@ -90,7 +92,7 @@ export function runCompilerCommonTests() {
|
||||
componentId: 'someId',
|
||||
absUrl: 'someUrl'
|
||||
})).then( (protoView) => {
|
||||
expect(DOM.getInnerHTML(protoView.render.delegate.element)).toEqual('url component');
|
||||
expect(DOM.getInnerHTML(resolveInternalDomProtoView(protoView.render).element)).toEqual('url component');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
@@ -1,213 +0,0 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
el,
|
||||
elementText,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
SpyObject,
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
import {ProtoViewDto, ViewDefinition, RenderViewContainerRef, EventDispatcher, DirectiveMetadata} from 'angular2/src/render/api';
|
||||
|
||||
import {IntegrationTestbed, LoggingEventDispatcher, FakeEvent} from './integration_testbed';
|
||||
|
||||
export function main() {
|
||||
describe('DirectDomRenderer integration', () => {
|
||||
var testbed, renderer, renderCompiler, eventPlugin, compileRoot, rootEl;
|
||||
|
||||
beforeEach(() => {
|
||||
rootEl = el('<div></div>');
|
||||
});
|
||||
|
||||
function createRenderer({urlData, viewCacheCapacity, shadowDomStrategy, templates}={}) {
|
||||
testbed = new IntegrationTestbed({
|
||||
urlData: urlData,
|
||||
viewCacheCapacity: viewCacheCapacity,
|
||||
shadowDomStrategy: shadowDomStrategy,
|
||||
templates: templates
|
||||
});
|
||||
renderer = testbed.renderer;
|
||||
renderCompiler = testbed.renderCompiler;
|
||||
eventPlugin = testbed.eventPlugin;
|
||||
compileRoot = (componentId) => testbed.compileRoot(componentId);
|
||||
}
|
||||
|
||||
it('should create host views while using the given elements in place', inject([AsyncTestCompleter], (async) => {
|
||||
createRenderer();
|
||||
renderCompiler.compileHost(someComponent).then( (rootProtoView) => {
|
||||
expect(rootProtoView.elementBinders[0].directives[0].directiveIndex).toBe(0);
|
||||
var viewRefs = renderer.createInPlaceHostView(null, rootEl, rootProtoView.render);
|
||||
expect(viewRefs.length).toBe(1);
|
||||
expect(viewRefs[0].delegate.rootNodes[0]).toEqual(rootEl);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should create imperative proto views', inject([AsyncTestCompleter], (async) => {
|
||||
createRenderer();
|
||||
renderCompiler.createImperativeComponentProtoView('someRenderId').then( (rootProtoView) => {
|
||||
expect(rootProtoView.elementBinders).toEqual([]);
|
||||
|
||||
expect(rootProtoView.render.delegate.imperativeRendererId).toBe('someRenderId');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should add a static component', inject([AsyncTestCompleter], (async) => {
|
||||
createRenderer();
|
||||
renderCompiler.compileHost(someComponent).then( (rootProtoView) => {
|
||||
var template = new ViewDefinition({
|
||||
componentId: 'someComponent',
|
||||
template: 'hello',
|
||||
directives: []
|
||||
});
|
||||
renderCompiler.compile(template).then( (pv) => {
|
||||
renderCompiler.mergeChildComponentProtoViews(rootProtoView.render, [pv.render]);
|
||||
renderer.createInPlaceHostView(null, rootEl, rootProtoView.render);
|
||||
expect(rootEl).toHaveText('hello');
|
||||
async.done();
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
it('should add a a dynamic component', inject([AsyncTestCompleter], (async) => {
|
||||
createRenderer();
|
||||
renderCompiler.compileHost(someComponent).then( (rootProtoView) => {
|
||||
var template = new ViewDefinition({
|
||||
componentId: 'someComponent',
|
||||
template: 'hello',
|
||||
directives: []
|
||||
});
|
||||
renderCompiler.compile(template).then( (pv) => {
|
||||
var rootViewRef = renderer.createInPlaceHostView(null, rootEl, rootProtoView.render)[0];
|
||||
renderer.createDynamicComponentView(rootViewRef, 0, pv.render)[0];
|
||||
expect(rootEl).toHaveText('hello');
|
||||
async.done();
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
it('should update text nodes', inject([AsyncTestCompleter], (async) => {
|
||||
createRenderer({
|
||||
templates: [new ViewDefinition({
|
||||
componentId: 'someComponent',
|
||||
template: '{{a}}',
|
||||
directives: []
|
||||
})]
|
||||
});
|
||||
compileRoot(someComponent).then( (rootProtoView) => {
|
||||
var viewRefs = renderer.createInPlaceHostView(null, rootEl, rootProtoView.render);
|
||||
renderer.setText(viewRefs[1], 0, 'hello');
|
||||
expect(rootEl).toHaveText('hello');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should update element properties', inject([AsyncTestCompleter], (async) => {
|
||||
createRenderer({
|
||||
templates: [new ViewDefinition({
|
||||
componentId: 'someComponent',
|
||||
template: '<input [value]="someProp">',
|
||||
directives: []
|
||||
})]
|
||||
});
|
||||
compileRoot(someComponent).then( (rootProtoView) => {
|
||||
var viewRefs = renderer.createInPlaceHostView(null, rootEl, rootProtoView.render);
|
||||
renderer.setElementProperty(viewRefs[1], 0, 'value', 'hello');
|
||||
expect(DOM.childNodes(rootEl)[0].value).toEqual('hello');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should add and remove views to and from containers', inject([AsyncTestCompleter], (async) => {
|
||||
createRenderer({
|
||||
templates: [new ViewDefinition({
|
||||
componentId: 'someComponent',
|
||||
template: '<template>hello</template>',
|
||||
directives: []
|
||||
})]
|
||||
});
|
||||
compileRoot(someComponent).then( (rootProtoView) => {
|
||||
var viewRef = renderer.createInPlaceHostView(null, rootEl, rootProtoView.render)[1];
|
||||
var vcProtoViewRef = rootProtoView.elementBinders[0]
|
||||
.nestedProtoView.elementBinders[0].nestedProtoView.render;
|
||||
var vcRef = new RenderViewContainerRef(viewRef, 0);
|
||||
expect(rootEl).toHaveText('');
|
||||
var childViewRef = renderer.createViewInContainer(vcRef, 0, vcProtoViewRef)[0];
|
||||
expect(rootEl).toHaveText('hello');
|
||||
renderer.detachViewFromContainer(vcRef, 0);
|
||||
expect(rootEl).toHaveText('');
|
||||
renderer.insertViewIntoContainer(vcRef, 0, childViewRef);
|
||||
expect(rootEl).toHaveText('hello');
|
||||
renderer.destroyViewInContainer(vcRef, 0);
|
||||
expect(rootEl).toHaveText('');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should cache views', inject([AsyncTestCompleter], (async) => {
|
||||
createRenderer({
|
||||
templates: [new ViewDefinition({
|
||||
componentId: 'someComponent',
|
||||
template: '<template>hello</template>',
|
||||
directives: []
|
||||
})],
|
||||
viewCacheCapacity: 2
|
||||
});
|
||||
compileRoot(someComponent).then( (rootProtoView) => {
|
||||
var viewRef = renderer.createInPlaceHostView(null, rootEl, rootProtoView.render)[1];
|
||||
var vcProtoViewRef = rootProtoView.elementBinders[0]
|
||||
.nestedProtoView.elementBinders[0].nestedProtoView.render;
|
||||
var vcRef = new RenderViewContainerRef(viewRef, 0);
|
||||
|
||||
var viewRef1 = renderer.createViewInContainer(vcRef, 0, vcProtoViewRef)[0];
|
||||
renderer.destroyViewInContainer(vcRef, 0);
|
||||
var viewRef2 = renderer.createViewInContainer(vcRef, 0, vcProtoViewRef)[0];
|
||||
var viewRef3 = renderer.createViewInContainer(vcRef, 0, vcProtoViewRef)[0];
|
||||
expect(viewRef2.delegate).toBe(viewRef1.delegate);
|
||||
expect(viewRef3.delegate).not.toBe(viewRef1.delegate);
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
// TODO(tbosch): This is not working yet as we commented out
|
||||
// the event expression processing...
|
||||
xit('should handle events', inject([AsyncTestCompleter], (async) => {
|
||||
createRenderer({
|
||||
templates: [new ViewDefinition({
|
||||
componentId: 'someComponent',
|
||||
template: '<input (change)="$event.target.value">',
|
||||
directives: []
|
||||
})]
|
||||
});
|
||||
compileRoot(someComponent).then( (rootProtoView) => {
|
||||
var viewRef = renderer.createInPlaceHostView(null, rootEl, rootProtoView.render)[1];
|
||||
var dispatcher = new LoggingEventDispatcher();
|
||||
renderer.setEventDispatcher(viewRef, dispatcher);
|
||||
var inputEl = DOM.childNodes(rootEl)[0];
|
||||
inputEl.value = 'hello';
|
||||
eventPlugin.dispatchEvent('change', new FakeEvent(inputEl));
|
||||
expect(dispatcher.log).toEqual([[0, 'change', ['hello']]]);
|
||||
async.done();
|
||||
});
|
||||
|
||||
}));
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
var someComponent = new DirectiveMetadata({
|
||||
id: 'someComponent',
|
||||
type: DirectiveMetadata.COMPONENT_TYPE,
|
||||
selector: 'some-comp'
|
||||
});
|
||||
@@ -0,0 +1,154 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
el,
|
||||
elementText,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
beforeEachBindings,
|
||||
SpyObject,
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import {MapWrapper} from 'angular2/src/facade/collection';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
import {DomTestbed} from './dom_testbed';
|
||||
|
||||
import {ViewDefinition, DirectiveMetadata, RenderViewRef} from 'angular2/src/render/api';
|
||||
|
||||
export function main() {
|
||||
describe('DomRenderer integration', () => {
|
||||
beforeEachBindings(() => [
|
||||
DomTestbed
|
||||
]);
|
||||
|
||||
it('should create and destroy host views while using the given elements in place',
|
||||
inject([AsyncTestCompleter, DomTestbed], (async, tb) => {
|
||||
tb.compileAll([someComponent]).then( (protoViewDtos) => {
|
||||
var view = tb.createRootView(protoViewDtos[0]);
|
||||
expect(tb.rootEl.parentNode).toBeTruthy();
|
||||
expect(view.rawView.rootNodes[0]).toEqual(tb.rootEl);
|
||||
|
||||
tb.renderer.destroyInPlaceHostView(null, view.viewRef);
|
||||
expect(tb.rootEl.parentNode).toBeFalsy();
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should attach and detach component views',
|
||||
inject([AsyncTestCompleter, DomTestbed], (async, tb) => {
|
||||
tb.compileAll([
|
||||
someComponent,
|
||||
new ViewDefinition({
|
||||
componentId: 'someComponent',
|
||||
template: 'hello',
|
||||
directives: []
|
||||
})
|
||||
]).then( (protoViewDtos) => {
|
||||
var rootView = tb.createRootView(protoViewDtos[0]);
|
||||
var cmpView = tb.createComponentView(rootView.viewRef, 0, protoViewDtos[1]);
|
||||
expect(tb.rootEl).toHaveText('hello');
|
||||
tb.destroyComponentView(rootView.viewRef, 0, cmpView.viewRef);
|
||||
expect(tb.rootEl).toHaveText('');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should update text nodes',
|
||||
inject([AsyncTestCompleter, DomTestbed], (async, tb) => {
|
||||
tb.compileAll([someComponent,
|
||||
new ViewDefinition({
|
||||
componentId: 'someComponent',
|
||||
template: '{{a}}',
|
||||
directives: []
|
||||
})
|
||||
]).then( (protoViewDtos) => {
|
||||
var rootView = tb.createRootView(protoViewDtos[0]);
|
||||
var cmpView = tb.createComponentView(rootView.viewRef, 0, protoViewDtos[1]);
|
||||
|
||||
tb.renderer.setText(cmpView.viewRef, 0, 'hello');
|
||||
expect(tb.rootEl).toHaveText('hello');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should update element properties',
|
||||
inject([AsyncTestCompleter, DomTestbed], (async, tb) => {
|
||||
tb.compileAll([someComponent,
|
||||
new ViewDefinition({
|
||||
componentId: 'someComponent',
|
||||
template: '<input [value]="someProp">asdf',
|
||||
directives: []
|
||||
})
|
||||
]).then( (protoViewDtos) => {
|
||||
var rootView = tb.createRootView(protoViewDtos[0]);
|
||||
var cmpView = tb.createComponentView(rootView.viewRef, 0, protoViewDtos[1]);
|
||||
|
||||
tb.renderer.setElementProperty(cmpView.viewRef, 0, 'value', 'hello');
|
||||
expect(DOM.childNodes(tb.rootEl)[0].value).toEqual('hello');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should add and remove views to and from containers',
|
||||
inject([AsyncTestCompleter, DomTestbed], (async, tb) => {
|
||||
tb.compileAll([someComponent,
|
||||
new ViewDefinition({
|
||||
componentId: 'someComponent',
|
||||
template: '<template>hello</template>',
|
||||
directives: []
|
||||
})
|
||||
]).then( (protoViewDtos) => {
|
||||
var rootView = tb.createRootView(protoViewDtos[0]);
|
||||
var cmpView = tb.createComponentView(rootView.viewRef, 0, protoViewDtos[1]);
|
||||
|
||||
var childProto = protoViewDtos[1].elementBinders[0].nestedProtoView;
|
||||
expect(tb.rootEl).toHaveText('');
|
||||
var childView = tb.createViewInContainer(cmpView.viewRef, 0, 0, childProto);
|
||||
expect(tb.rootEl).toHaveText('hello');
|
||||
tb.destroyViewInContainer(cmpView.viewRef, 0, 0, childView.viewRef);
|
||||
expect(tb.rootEl).toHaveText('');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should handle events',
|
||||
inject([AsyncTestCompleter, DomTestbed], (async, tb) => {
|
||||
tb.compileAll([someComponent,
|
||||
new ViewDefinition({
|
||||
componentId: 'someComponent',
|
||||
template: '<input (change)="doSomething()">',
|
||||
directives: []
|
||||
})
|
||||
]).then( (protoViewDtos) => {
|
||||
var rootView = tb.createRootView(protoViewDtos[0]);
|
||||
var cmpView = tb.createComponentView(rootView.viewRef, 0, protoViewDtos[1]);
|
||||
|
||||
tb.triggerEvent(cmpView.viewRef, 0, 'change');
|
||||
var eventEntry = cmpView.events[0];
|
||||
// bound element index
|
||||
expect(eventEntry[0]).toEqual(0);
|
||||
// event type
|
||||
expect(eventEntry[1]).toEqual('change');
|
||||
// actual event
|
||||
expect(MapWrapper.get(eventEntry[2], '$event').type).toEqual('change');
|
||||
async.done();
|
||||
});
|
||||
|
||||
}));
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
var someComponent = new DirectiveMetadata({
|
||||
id: 'someComponent',
|
||||
type: DirectiveMetadata.COMPONENT_TYPE,
|
||||
selector: 'some-comp'
|
||||
});
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
import {Inject, Injectable} from 'angular2/src/di/annotations_impl';
|
||||
import {MapWrapper, ListWrapper, List, Map} from 'angular2/src/facade/collection';
|
||||
import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
import {DomRenderer, DOCUMENT_TOKEN} from 'angular2/src/render/dom/dom_renderer';
|
||||
import {DefaultDomCompiler} from 'angular2/src/render/dom/compiler/compiler';
|
||||
import {DomView} from 'angular2/src/render/dom/view/view';
|
||||
import {RenderViewRef, ProtoViewDto, ViewDefinition, EventDispatcher, DirectiveMetadata} from 'angular2/src/render/api';
|
||||
import {resolveInternalDomView} from 'angular2/src/render/dom/view/view';
|
||||
import {el, dispatchEvent} from 'angular2/test_lib';
|
||||
|
||||
export class TestView extends EventDispatcher {
|
||||
rawView:DomView;
|
||||
viewRef:RenderViewRef;
|
||||
events:List;
|
||||
|
||||
constructor(viewRef:RenderViewRef) {
|
||||
super();
|
||||
this.viewRef = viewRef;
|
||||
this.rawView = resolveInternalDomView(viewRef);
|
||||
this.events = [];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class LoggingEventDispatcher extends EventDispatcher {
|
||||
log:List;
|
||||
|
||||
constructor(log:List) {
|
||||
super();
|
||||
this.log = log;
|
||||
}
|
||||
|
||||
dispatchEvent(
|
||||
elementIndex:number, eventName:string, locals:Map<string, any>
|
||||
) {
|
||||
ListWrapper.push(this.log, [elementIndex, eventName, locals]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Injectable()
|
||||
export class DomTestbed {
|
||||
renderer:DomRenderer;
|
||||
compiler:DefaultDomCompiler;
|
||||
rootEl;
|
||||
|
||||
constructor(renderer:DomRenderer, compiler:DefaultDomCompiler, @Inject(DOCUMENT_TOKEN) document) {
|
||||
this.renderer = renderer;
|
||||
this.compiler = compiler;
|
||||
this.rootEl = el('<div id="root"></div>');
|
||||
var oldRoots = DOM.querySelectorAll(document, '#root');
|
||||
for (var i=0; i<oldRoots.length; i++) {
|
||||
DOM.remove(oldRoots[i]);
|
||||
}
|
||||
DOM.appendChild(DOM.querySelector(document, 'body'), this.rootEl);
|
||||
}
|
||||
|
||||
compileAll(directivesOrViewDefinitions:List):Promise<List<ProtoViewDto>> {
|
||||
return PromiseWrapper.all(
|
||||
ListWrapper.map(directivesOrViewDefinitions, (entry) => {
|
||||
if (entry instanceof DirectiveMetadata) {
|
||||
return this.compiler.compileHost(entry);
|
||||
} else {
|
||||
return this.compiler.compile(entry);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
_createTestView(viewRef:RenderViewRef) {
|
||||
var testView = new TestView(viewRef);
|
||||
this.renderer.setEventDispatcher(viewRef, new LoggingEventDispatcher(testView.events));
|
||||
return testView;
|
||||
}
|
||||
|
||||
createRootView(rootProtoView:ProtoViewDto):TestView {
|
||||
var viewRef = this.renderer.createInPlaceHostView(null, '#root', rootProtoView.render);
|
||||
this.renderer.hydrateView(viewRef);
|
||||
return this._createTestView(viewRef);
|
||||
}
|
||||
|
||||
createComponentView(parentViewRef:RenderViewRef, boundElementIndex:number, componentProtoView:ProtoViewDto):TestView {
|
||||
var componentViewRef = this.renderer.createView(componentProtoView.render);
|
||||
this.renderer.attachComponentView(parentViewRef, boundElementIndex, componentViewRef);
|
||||
this.renderer.hydrateView(componentViewRef);
|
||||
return this._createTestView(componentViewRef);
|
||||
}
|
||||
|
||||
createRootViews(protoViews:List<ProtoViewDto>):List<TestView> {
|
||||
var views = [];
|
||||
var lastView = this.createRootView(protoViews[0]);
|
||||
ListWrapper.push(views, lastView);
|
||||
for (var i=1; i<protoViews.length; i++) {
|
||||
lastView = this.createComponentView(lastView.viewRef, 0, protoViews[i]);
|
||||
ListWrapper.push(views, lastView);
|
||||
}
|
||||
return views;
|
||||
}
|
||||
|
||||
destroyComponentView(parentViewRef:RenderViewRef, boundElementIndex:number, componentView:RenderViewRef) {
|
||||
this.renderer.dehydrateView(componentView);
|
||||
this.renderer.detachComponentView(parentViewRef, boundElementIndex, componentView);
|
||||
}
|
||||
|
||||
createViewInContainer(parentViewRef:RenderViewRef, boundElementIndex:number, atIndex:number, protoView:ProtoViewDto):TestView {
|
||||
var viewRef = this.renderer.createView(protoView.render);
|
||||
this.renderer.attachViewInContainer(parentViewRef, boundElementIndex, atIndex, viewRef);
|
||||
this.renderer.hydrateView(viewRef);
|
||||
return this._createTestView(viewRef);
|
||||
}
|
||||
|
||||
destroyViewInContainer(parentViewRef:RenderViewRef, boundElementIndex:number, atIndex:number, viewRef:RenderViewRef) {
|
||||
this.renderer.dehydrateView(viewRef);
|
||||
this.renderer.detachViewInContainer(parentViewRef, boundElementIndex, atIndex, viewRef);
|
||||
this.renderer.destroyView(viewRef);
|
||||
}
|
||||
|
||||
triggerEvent(viewRef:RenderViewRef, boundElementIndex:number, eventName:string) {
|
||||
var element = resolveInternalDomView(viewRef).boundElements[boundElementIndex];
|
||||
dispatchEvent(element, eventName);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,195 +0,0 @@
|
||||
import {isBlank, isPresent, BaseException} from 'angular2/src/facade/lang';
|
||||
import {MapWrapper, ListWrapper, List, Map} from 'angular2/src/facade/collection';
|
||||
import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
import {Parser, Lexer} from 'angular2/change_detection';
|
||||
import {DirectDomRenderer} from 'angular2/src/render/dom/direct_dom_renderer';
|
||||
import {DefaultDomCompiler} from 'angular2/src/render/dom/compiler/compiler';
|
||||
import {RenderProtoViewRef, ProtoViewDto, ViewDefinition, RenderViewContainerRef, EventDispatcher, DirectiveMetadata} from 'angular2/src/render/api';
|
||||
import {TemplateLoader} from 'angular2/src/render/dom/compiler/template_loader';
|
||||
import {UrlResolver} from 'angular2/src/services/url_resolver';
|
||||
import {EmulatedUnscopedShadowDomStrategy} from 'angular2/src/render/dom/shadow_dom/emulated_unscoped_shadow_dom_strategy';
|
||||
import {EventManager, EventManagerPlugin} from 'angular2/src/render/dom/events/event_manager';
|
||||
import {VmTurnZone} from 'angular2/src/core/zone/vm_turn_zone';
|
||||
import {StyleUrlResolver} from 'angular2/src/render/dom/shadow_dom/style_url_resolver';
|
||||
import {ViewFactory} from 'angular2/src/render/dom/view/view_factory';
|
||||
import {RenderViewHydrator} from 'angular2/src/render/dom/view/view_hydrator';
|
||||
|
||||
export class IntegrationTestbed {
|
||||
renderer;
|
||||
renderCompiler;
|
||||
parser;
|
||||
eventPlugin;
|
||||
_templates:Map<string, ViewDefinition>;
|
||||
|
||||
constructor({urlData, viewCacheCapacity, shadowDomStrategy, templates}) {
|
||||
this._templates = MapWrapper.create();
|
||||
if (isPresent(templates)) {
|
||||
ListWrapper.forEach(templates, (template) => {
|
||||
MapWrapper.set(this._templates, template.componentId, template);
|
||||
});
|
||||
}
|
||||
var parser = new Parser(new Lexer());
|
||||
var urlResolver = new UrlResolver();
|
||||
if (isBlank(shadowDomStrategy)) {
|
||||
shadowDomStrategy = new EmulatedUnscopedShadowDomStrategy(new StyleUrlResolver(urlResolver), null);
|
||||
}
|
||||
this.renderCompiler = new DefaultDomCompiler(parser, shadowDomStrategy, new FakeTemplateLoader(urlResolver, urlData));
|
||||
|
||||
if (isBlank(viewCacheCapacity)) {
|
||||
viewCacheCapacity = 0;
|
||||
}
|
||||
if (isBlank(urlData)) {
|
||||
urlData = MapWrapper.create();
|
||||
}
|
||||
this.eventPlugin = new FakeEventManagerPlugin();
|
||||
var eventManager = new EventManager([this.eventPlugin], new FakeVmTurnZone());
|
||||
var viewFactory = new ViewFactory(viewCacheCapacity, eventManager, shadowDomStrategy);
|
||||
var viewHydrator = new RenderViewHydrator(eventManager, viewFactory, shadowDomStrategy);
|
||||
this.renderer = new DirectDomRenderer(viewFactory, viewHydrator, shadowDomStrategy);
|
||||
}
|
||||
|
||||
compileRoot(componentMetadata):Promise<ProtoViewDto> {
|
||||
return this.renderCompiler.compileHost(componentMetadata).then( (rootProtoView) => {
|
||||
return this._compileNestedProtoViews(rootProtoView, [componentMetadata]);
|
||||
});
|
||||
}
|
||||
|
||||
compile(componentId):Promise<ProtoViewDto> {
|
||||
var childTemplate = MapWrapper.get(this._templates, componentId);
|
||||
if (isBlank(childTemplate)) {
|
||||
throw new BaseException(`No template for component ${componentId}`);
|
||||
}
|
||||
return this.renderCompiler.compile(childTemplate).then( (protoView) => {
|
||||
return this._compileNestedProtoViews(protoView, childTemplate.directives);
|
||||
});
|
||||
}
|
||||
|
||||
_compileNestedProtoViews(protoView, directives):Promise<ProtoViewDto> {
|
||||
var childComponentRenderPvRefs = [];
|
||||
var nestedPVPromises = [];
|
||||
ListWrapper.forEach(protoView.elementBinders, (elementBinder) => {
|
||||
var nestedComponentId = null;
|
||||
ListWrapper.forEach(elementBinder.directives, (db) => {
|
||||
var directiveMeta = directives[db.directiveIndex];
|
||||
if (directiveMeta.type === DirectiveMetadata.COMPONENT_TYPE) {
|
||||
nestedComponentId = directiveMeta.id;
|
||||
}
|
||||
});
|
||||
var nestedCall;
|
||||
if (isPresent(nestedComponentId)) {
|
||||
var childTemplate = MapWrapper.get(this._templates, nestedComponentId);
|
||||
if (isBlank(childTemplate)) {
|
||||
// dynamic component
|
||||
ListWrapper.push(childComponentRenderPvRefs, null);
|
||||
} else {
|
||||
nestedCall = this.compile(nestedComponentId);
|
||||
}
|
||||
} else if (isPresent(elementBinder.nestedProtoView)) {
|
||||
nestedCall = this._compileNestedProtoViews(elementBinder.nestedProtoView, directives);
|
||||
}
|
||||
if (isPresent(nestedCall)) {
|
||||
ListWrapper.push(
|
||||
nestedPVPromises,
|
||||
nestedCall.then( (nestedPv) => {
|
||||
elementBinder.nestedProtoView = nestedPv;
|
||||
if (isPresent(nestedComponentId)) {
|
||||
ListWrapper.push(childComponentRenderPvRefs, nestedPv.render);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
if (nestedPVPromises.length > 0) {
|
||||
return PromiseWrapper.all(nestedPVPromises).then((_) => {
|
||||
this.renderCompiler.mergeChildComponentProtoViews(protoView.render, childComponentRenderPvRefs);
|
||||
return protoView;
|
||||
});
|
||||
} else {
|
||||
return PromiseWrapper.resolve(protoView);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
class FakeTemplateLoader extends TemplateLoader {
|
||||
_urlData: Map<string, string>;
|
||||
|
||||
constructor(urlResolver, urlData) {
|
||||
super(null, urlResolver);
|
||||
this._urlData = urlData;
|
||||
}
|
||||
|
||||
load(template: ViewDefinition) {
|
||||
if (isPresent(template.template)) {
|
||||
return PromiseWrapper.resolve(DOM.createTemplate(template.template));
|
||||
}
|
||||
|
||||
if (isPresent(template.absUrl)) {
|
||||
var content = this._urlData[template.absUrl];
|
||||
if (isPresent(content)) {
|
||||
return PromiseWrapper.resolve(DOM.createTemplate(content));
|
||||
}
|
||||
}
|
||||
|
||||
return PromiseWrapper.reject('Load failed');
|
||||
}
|
||||
}
|
||||
|
||||
export class FakeVmTurnZone extends VmTurnZone {
|
||||
constructor() {
|
||||
super({enableLongStackTrace: false});
|
||||
}
|
||||
|
||||
run(fn) {
|
||||
fn();
|
||||
}
|
||||
|
||||
runOutsideAngular(fn) {
|
||||
fn();
|
||||
}
|
||||
}
|
||||
|
||||
export class FakeEventManagerPlugin extends EventManagerPlugin {
|
||||
_eventHandlers: Map;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._eventHandlers = MapWrapper.create();
|
||||
}
|
||||
|
||||
dispatchEvent(eventName, event) {
|
||||
MapWrapper.get(this._eventHandlers, eventName)(event);
|
||||
}
|
||||
|
||||
supports(eventName: string): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
addEventListener(element, eventName: string, handler: Function, shouldSupportBubble: boolean) {
|
||||
MapWrapper.set(this._eventHandlers, eventName, handler);
|
||||
return () => {MapWrapper.delete(this._eventHandlers, eventName);}
|
||||
}
|
||||
}
|
||||
|
||||
export class LoggingEventDispatcher extends EventDispatcher {
|
||||
log:List;
|
||||
constructor() {
|
||||
super();
|
||||
this.log = [];
|
||||
}
|
||||
dispatchEvent(
|
||||
elementIndex:number, eventName:string, locals:Map<string, any>
|
||||
) {
|
||||
ListWrapper.push(this.log, [elementIndex, eventName, locals]);
|
||||
}
|
||||
}
|
||||
|
||||
export class FakeEvent {
|
||||
target;
|
||||
constructor(target) {
|
||||
this.target = target;
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ export function main() {
|
||||
|
||||
it("should insert the nodes", () => {
|
||||
var c = new Content(content, '');
|
||||
c.hydrate(null);
|
||||
c.init(null);
|
||||
c.insert([el("<a></a>"), el("<b></b>")])
|
||||
|
||||
expect(DOM.getInnerHTML(parent)).toEqual(`${_scriptStart}<a></a><b></b>${_scriptEnd}`);
|
||||
@@ -31,7 +31,7 @@ export function main() {
|
||||
|
||||
it("should remove the nodes from the previous insertion", () => {
|
||||
var c = new Content(content, '');
|
||||
c.hydrate(null);
|
||||
c.init(null);
|
||||
c.insert([el("<a></a>")]);
|
||||
c.insert([el("<b></b>")]);
|
||||
|
||||
@@ -40,7 +40,7 @@ export function main() {
|
||||
|
||||
it("should insert empty list", () => {
|
||||
var c = new Content(content, '');
|
||||
c.hydrate(null);
|
||||
c.init(null);
|
||||
c.insert([el("<a></a>")]);
|
||||
c.insert([]);
|
||||
|
||||
|
||||
+2
-9
@@ -28,7 +28,6 @@ import {
|
||||
import {UrlResolver} from 'angular2/src/services/url_resolver';
|
||||
import {StyleUrlResolver} from 'angular2/src/render/dom/shadow_dom/style_url_resolver';
|
||||
import {StyleInliner} from 'angular2/src/render/dom/shadow_dom/style_inliner';
|
||||
import {DomView} from 'angular2/src/render/dom/view/view';
|
||||
|
||||
export function main() {
|
||||
describe('EmulatedScopedShadowDomStrategy', () => {
|
||||
@@ -44,15 +43,9 @@ export function main() {
|
||||
resetShadowDomCache();
|
||||
});
|
||||
|
||||
it('should attach the view nodes as child of the host element', () => {
|
||||
it('should use the host element as shadow root', () => {
|
||||
var host = el('<div><span>original content</span></div>');
|
||||
var originalChild = DOM.childNodes(host)[0];
|
||||
var nodes = el('<div>view</div>');
|
||||
var view = new DomView(null, [nodes], [], [], []);
|
||||
|
||||
strategy.attachTemplate(host, view);
|
||||
expect(DOM.childNodes(host)[0]).toBe(originalChild);
|
||||
expect(DOM.childNodes(host)[1]).toBe(nodes);
|
||||
expect(strategy.prepareShadowRoot(host)).toBe(host);
|
||||
});
|
||||
|
||||
it('should rewrite style urls', () => {
|
||||
|
||||
+2
-9
@@ -23,7 +23,6 @@ import {
|
||||
} from 'angular2/src/render/dom/shadow_dom/util';
|
||||
import {UrlResolver} from 'angular2/src/services/url_resolver';
|
||||
import {StyleUrlResolver} from 'angular2/src/render/dom/shadow_dom/style_url_resolver';
|
||||
import {DomView} from 'angular2/src/render/dom/view/view';
|
||||
|
||||
export function main() {
|
||||
var strategy;
|
||||
@@ -39,15 +38,9 @@ export function main() {
|
||||
resetShadowDomCache();
|
||||
});
|
||||
|
||||
it('should attach the view nodes as child of the host element', () => {
|
||||
it('should use the host element as shadow root', () => {
|
||||
var host = el('<div><span>original content</span></div>');
|
||||
var originalChild = DOM.childNodes(host)[0];
|
||||
var nodes = el('<div>view</div>');
|
||||
var view = new DomView(null, [nodes], [], [], []);
|
||||
|
||||
strategy.attachTemplate(host, view);
|
||||
expect(DOM.childNodes(host)[0]).toBe(originalChild);
|
||||
expect(DOM.childNodes(host)[1]).toBe(nodes);
|
||||
expect(strategy.prepareShadowRoot(host)).toBe(host);
|
||||
});
|
||||
|
||||
it('should rewrite style urls', () => {
|
||||
|
||||
+17
-12
@@ -5,7 +5,7 @@ import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
import {Content} from 'angular2/src/render/dom/shadow_dom/content_tag';
|
||||
import {LightDom} from 'angular2/src/render/dom/shadow_dom/light_dom';
|
||||
import {DomView} from 'angular2/src/render/dom/view/view';
|
||||
import {ViewContainer} from 'angular2/src/render/dom/view/view_container';
|
||||
import {DomViewContainer} from 'angular2/src/render/dom/view/view_container';
|
||||
|
||||
@proxy
|
||||
@IMPLEMENTS(DomView)
|
||||
@@ -44,7 +44,7 @@ class FakeView {
|
||||
}
|
||||
|
||||
@proxy
|
||||
@IMPLEMENTS(ViewContainer)
|
||||
@IMPLEMENTS(DomViewContainer)
|
||||
class FakeViewContainer {
|
||||
_nodes;
|
||||
_contentTagContainers;
|
||||
@@ -96,6 +96,11 @@ class FakeContentTag {
|
||||
}
|
||||
}
|
||||
|
||||
function createLightDom(hostView, shadowView, el) {
|
||||
var lightDom = new LightDom(hostView, el);
|
||||
lightDom.attachShadowDomView(shadowView);
|
||||
return lightDom;
|
||||
}
|
||||
|
||||
export function main() {
|
||||
describe('LightDom', function() {
|
||||
@@ -110,7 +115,7 @@ export function main() {
|
||||
var tag = new FakeContentTag(el('<script></script>'));
|
||||
var shadowDomView = new FakeView([tag]);
|
||||
|
||||
var lightDom = new LightDom(lightDomView, shadowDomView,
|
||||
var lightDom = createLightDom(lightDomView, shadowDomView,
|
||||
el("<div></div>"));
|
||||
|
||||
expect(lightDom.contentTags()).toEqual([tag]);
|
||||
@@ -122,7 +127,7 @@ export function main() {
|
||||
new FakeView([tag])
|
||||
]);
|
||||
var shadowDomView = new FakeView([vc]);
|
||||
var lightDom = new LightDom(lightDomView, shadowDomView,
|
||||
var lightDom = createLightDom(lightDomView, shadowDomView,
|
||||
el("<div></div>"));
|
||||
|
||||
expect(lightDom.contentTags()).toEqual([tag]);
|
||||
@@ -132,13 +137,13 @@ export function main() {
|
||||
describe("expandedDomNodes", () => {
|
||||
it("should contain root nodes", () => {
|
||||
var lightDomEl = el("<div><a></a></div>")
|
||||
var lightDom = new LightDom(lightDomView, new FakeView(), lightDomEl);
|
||||
var lightDom = createLightDom(lightDomView, new FakeView(), lightDomEl);
|
||||
expect(toHtml(lightDom.expandedDomNodes())).toEqual(["<a></a>"]);
|
||||
});
|
||||
|
||||
it("should include view container nodes", () => {
|
||||
var lightDomEl = el("<div><template></template></div>");
|
||||
var lightDom = new LightDom(
|
||||
var lightDom = createLightDom(
|
||||
new FakeView([
|
||||
new FakeViewContainer(
|
||||
DOM.firstChild(lightDomEl), // template element
|
||||
@@ -153,7 +158,7 @@ export function main() {
|
||||
|
||||
it("should include content nodes", () => {
|
||||
var lightDomEl = el("<div><content></content></div>");
|
||||
var lightDom = new LightDom(
|
||||
var lightDom = createLightDom(
|
||||
new FakeView([
|
||||
new FakeContentTag(
|
||||
DOM.firstChild(lightDomEl), // content element
|
||||
@@ -172,7 +177,7 @@ export function main() {
|
||||
|
||||
var lightDomView = new FakeView();
|
||||
|
||||
var lightDom = new LightDom(
|
||||
var lightDom = createLightDom(
|
||||
lightDomView,
|
||||
new FakeView(),
|
||||
lightDomEl);
|
||||
@@ -188,7 +193,7 @@ export function main() {
|
||||
|
||||
var lightDomEl = el("<div><a>1</a><b>2</b><a>3</a></div>")
|
||||
|
||||
var lightDom = new LightDom(lightDomView, new FakeView([
|
||||
var lightDom = createLightDom(lightDomView, new FakeView([
|
||||
contentA,
|
||||
contentB
|
||||
]), lightDomEl);
|
||||
@@ -205,7 +210,7 @@ export function main() {
|
||||
|
||||
var lightDomEl = el("<div><a>1</a><b>2</b><a>3</a></div>")
|
||||
|
||||
var lightDom = new LightDom(lightDomView, new FakeView([
|
||||
var lightDom = createLightDom(lightDomView, new FakeView([
|
||||
wildcard,
|
||||
contentB
|
||||
]), lightDomEl);
|
||||
@@ -219,7 +224,7 @@ export function main() {
|
||||
it("should remove all nodes if there are no content tags", () => {
|
||||
var lightDomEl = el("<div><a>1</a><b>2</b><a>3</a></div>")
|
||||
|
||||
var lightDom = new LightDom(lightDomView, new FakeView([]), lightDomEl);
|
||||
var lightDom = createLightDom(lightDomView, new FakeView([]), lightDomEl);
|
||||
|
||||
lightDom.redistribute();
|
||||
|
||||
@@ -230,7 +235,7 @@ export function main() {
|
||||
var lightDomEl = el("<div><a>1</a><b>2</b><a>3</a></div>");
|
||||
var bNode = DOM.childNodes(lightDomEl)[1];
|
||||
|
||||
var lightDom = new LightDom(lightDomView, new FakeView([
|
||||
var lightDom = createLightDom(lightDomView, new FakeView([
|
||||
new FakeContentTag(null, "a")
|
||||
]), lightDomEl);
|
||||
|
||||
|
||||
+2
-10
@@ -17,9 +17,7 @@ import {
|
||||
} from 'angular2/src/render/dom/shadow_dom/native_shadow_dom_strategy';
|
||||
import {UrlResolver} from 'angular2/src/services/url_resolver';
|
||||
import {StyleUrlResolver} from 'angular2/src/render/dom/shadow_dom/style_url_resolver';
|
||||
import {DomView} from 'angular2/src/render/dom/view/view';
|
||||
|
||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
export function main() {
|
||||
@@ -32,15 +30,9 @@ export function main() {
|
||||
strategy = new NativeShadowDomStrategy(styleUrlResolver);
|
||||
});
|
||||
|
||||
it('should attach the view nodes to the shadow root', () => {
|
||||
it('should use the native shadow root', () => {
|
||||
var host = el('<div><span>original content</span></div>');
|
||||
var nodes = el('<div>view</div>');
|
||||
var view = new DomView(null, [nodes], [], [], []);
|
||||
|
||||
strategy.attachTemplate(host, view);
|
||||
var shadowRoot = DOM.getShadowRoot(host);
|
||||
expect(isPresent(shadowRoot)).toBeTruthy();
|
||||
expect(shadowRoot).toHaveText('view');
|
||||
expect(strategy.prepareShadowRoot(host)).toBe(DOM.getShadowRoot(host));
|
||||
});
|
||||
|
||||
it('should rewrite style urls', () => {
|
||||
|
||||
+220
-223
@@ -10,128 +10,118 @@ import {
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
beforeEachBindings,
|
||||
SpyObject,
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import {bind} from 'angular2/di';
|
||||
import {MapWrapper, ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
import {
|
||||
ProtoViewDto, ViewDefinition, RenderViewContainerRef, DirectiveMetadata
|
||||
ViewDefinition, DirectiveMetadata
|
||||
} from 'angular2/src/render/api';
|
||||
|
||||
import {ShadowDomStrategy} from 'angular2/src/render/dom/shadow_dom/shadow_dom_strategy';
|
||||
import {EmulatedScopedShadowDomStrategy} from 'angular2/src/render/dom/shadow_dom/emulated_scoped_shadow_dom_strategy';
|
||||
import {EmulatedUnscopedShadowDomStrategy} from 'angular2/src/render/dom/shadow_dom/emulated_unscoped_shadow_dom_strategy';
|
||||
import {NativeShadowDomStrategy} from 'angular2/src/render/dom/shadow_dom/native_shadow_dom_strategy';
|
||||
import {UrlResolver} from 'angular2/src/services/url_resolver';
|
||||
import {StyleUrlResolver} from 'angular2/src/render/dom/shadow_dom/style_url_resolver';
|
||||
import {StyleInliner} from 'angular2/src/render/dom/shadow_dom/style_inliner';
|
||||
|
||||
import {IntegrationTestbed} from './integration_testbed';
|
||||
import {DomTestbed} from './dom_testbed';
|
||||
|
||||
export function main() {
|
||||
describe('ShadowDom integration tests', function() {
|
||||
var urlResolver, styleUrlResolver, styleInliner;
|
||||
var strategies = {
|
||||
"scoped" : () => new EmulatedScopedShadowDomStrategy(styleInliner, styleUrlResolver, null),
|
||||
"unscoped" : () => new EmulatedUnscopedShadowDomStrategy(styleUrlResolver, null)
|
||||
"scoped" : bind(ShadowDomStrategy).toFactory(
|
||||
(styleInliner, styleUrlResolver) => new EmulatedScopedShadowDomStrategy(styleInliner, styleUrlResolver, null),
|
||||
[StyleInliner, StyleUrlResolver]),
|
||||
"unscoped" : bind(ShadowDomStrategy).toFactory(
|
||||
(styleUrlResolver) => new EmulatedUnscopedShadowDomStrategy(styleUrlResolver, null),
|
||||
[StyleUrlResolver])
|
||||
}
|
||||
if (DOM.supportsNativeShadowDOM()) {
|
||||
StringMapWrapper.set(strategies, "native", () => new NativeShadowDomStrategy(styleUrlResolver));
|
||||
StringMapWrapper.set(strategies, "native", bind(ShadowDomStrategy).toFactory(
|
||||
(styleUrlResolver) => new NativeShadowDomStrategy(styleUrlResolver),
|
||||
[StyleUrlResolver])
|
||||
);
|
||||
}
|
||||
|
||||
beforeEach( () => {
|
||||
urlResolver = new UrlResolver();
|
||||
styleUrlResolver = new StyleUrlResolver(urlResolver);
|
||||
styleInliner = new StyleInliner(null, styleUrlResolver, urlResolver);
|
||||
});
|
||||
|
||||
|
||||
StringMapWrapper.forEach(strategies,
|
||||
(strategyFactory, name) => {
|
||||
(strategyBinding, name) => {
|
||||
|
||||
beforeEachBindings( () => {
|
||||
return [strategyBinding, DomTestbed];
|
||||
});
|
||||
|
||||
describe(`${name} shadow dom strategy`, () => {
|
||||
|
||||
var testbed, renderer, rootEl, compile, compileRoot;
|
||||
|
||||
function createRenderer({templates, viewCacheCapacity}) {
|
||||
testbed = new IntegrationTestbed({
|
||||
shadowDomStrategy: strategyFactory(),
|
||||
templates: ListWrapper.concat(templates, componentTemplates),
|
||||
viewCacheCapacity: viewCacheCapacity
|
||||
});
|
||||
renderer = testbed.renderer;
|
||||
compileRoot = (rootEl) => testbed.compileRoot(rootEl);
|
||||
compile = (componentId) => testbed.compile(componentId);
|
||||
}
|
||||
|
||||
beforeEach( () => {
|
||||
rootEl = el('<div></div>');
|
||||
});
|
||||
|
||||
it('should support simple components', inject([AsyncTestCompleter], (async) => {
|
||||
createRenderer({
|
||||
templates: [new ViewDefinition({
|
||||
it('should support simple components', inject([AsyncTestCompleter, DomTestbed], (async, tb) => {
|
||||
tb.compileAll([
|
||||
mainDir,
|
||||
new ViewDefinition({
|
||||
componentId: 'main',
|
||||
template: '<simple>' +
|
||||
'<div>A</div>' +
|
||||
'</simple>',
|
||||
directives: [simple]
|
||||
})]
|
||||
});
|
||||
compileRoot(mainDir).then( (pv) => {
|
||||
renderer.createInPlaceHostView(null, rootEl, pv.render);
|
||||
}),
|
||||
simpleTemplate
|
||||
]).then( (protoViews) => {
|
||||
tb.createRootViews(protoViews);
|
||||
|
||||
expect(rootEl).toHaveText('SIMPLE(A)');
|
||||
expect(tb.rootEl).toHaveText('SIMPLE(A)');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should not show the light dom event if there is not content tag', inject([AsyncTestCompleter], (async) => {
|
||||
createRenderer({
|
||||
templates: [new ViewDefinition({
|
||||
it('should not show the light dom even if there is not content tag', inject([AsyncTestCompleter, DomTestbed], (async, tb) => {
|
||||
tb.compileAll([
|
||||
mainDir,
|
||||
new ViewDefinition({
|
||||
componentId: 'main',
|
||||
template: '<empty>' +
|
||||
'<div>A</div>' +
|
||||
'</empty>',
|
||||
directives: [empty]
|
||||
})]
|
||||
});
|
||||
compileRoot(mainDir).then( (pv) => {
|
||||
renderer.createInPlaceHostView(null, rootEl, pv.render);
|
||||
}),
|
||||
emptyTemplate
|
||||
]).then( (protoViews) => {
|
||||
tb.createRootViews(protoViews);
|
||||
|
||||
expect(rootEl).toHaveText('');
|
||||
expect(tb.rootEl).toHaveText('');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should support dynamic components', inject([AsyncTestCompleter], (async) => {
|
||||
createRenderer({
|
||||
templates: [new ViewDefinition({
|
||||
it('should support dynamic components', inject([AsyncTestCompleter, DomTestbed], (async, tb) => {
|
||||
tb.compileAll([
|
||||
mainDir,
|
||||
new ViewDefinition({
|
||||
componentId: 'main',
|
||||
template: '<dynamic>' +
|
||||
'<div>A</div>' +
|
||||
'</dynamic>',
|
||||
directives: [dynamicComponent]
|
||||
})]
|
||||
});
|
||||
compileRoot(mainDir).then( (rootPv) => {
|
||||
compile('simple').then( (simplePv) => {
|
||||
var views = renderer.createInPlaceHostView(null, rootEl, rootPv.render);
|
||||
renderer.createDynamicComponentView(views[1], 0, simplePv.render);
|
||||
}),
|
||||
simpleTemplate
|
||||
]).then( (protoViews) => {
|
||||
var views = tb.createRootViews(ListWrapper.slice(protoViews, 0, 2));
|
||||
tb.createComponentView(views[1].viewRef, 0, protoViews[2]);
|
||||
|
||||
expect(rootEl).toHaveText('SIMPLE(A)');
|
||||
expect(tb.rootEl).toHaveText('SIMPLE(A)');
|
||||
|
||||
async.done();
|
||||
});
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should support multiple content tags', inject([AsyncTestCompleter], (async) => {
|
||||
createRenderer({
|
||||
templates: [new ViewDefinition({
|
||||
it('should support multiple content tags', inject([AsyncTestCompleter, DomTestbed], (async, tb) => {
|
||||
tb.compileAll([
|
||||
mainDir,
|
||||
new ViewDefinition({
|
||||
componentId: 'main',
|
||||
template: '<multiple-content-tags>' +
|
||||
'<div>B</div>' +
|
||||
@@ -139,120 +129,126 @@ export function main() {
|
||||
'<div class="left">A</div>' +
|
||||
'</multiple-content-tags>',
|
||||
directives: [multipleContentTagsComponent]
|
||||
})]
|
||||
});
|
||||
compileRoot(mainDir).then( (pv) => {
|
||||
renderer.createInPlaceHostView(null, rootEl, pv.render);
|
||||
}),
|
||||
multipleContentTagsTemplate
|
||||
]).then( (protoViews) => {
|
||||
tb.createRootViews(protoViews);
|
||||
|
||||
expect(rootEl).toHaveText('(A, BC)');
|
||||
expect(tb.rootEl).toHaveText('(A, BC)');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should redistribute only direct children', inject([AsyncTestCompleter], (async) => {
|
||||
createRenderer({
|
||||
templates: [new ViewDefinition({
|
||||
it('should redistribute only direct children', inject([AsyncTestCompleter, DomTestbed], (async, tb) => {
|
||||
tb.compileAll([
|
||||
mainDir,
|
||||
new ViewDefinition({
|
||||
componentId: 'main',
|
||||
template: '<multiple-content-tags>' +
|
||||
'<div>B<div class="left">A</div></div>' +
|
||||
'<div>C</div>' +
|
||||
'</multiple-content-tags>',
|
||||
directives: [multipleContentTagsComponent]
|
||||
})]
|
||||
});
|
||||
compileRoot(mainDir).then( (pv) => {
|
||||
renderer.createInPlaceHostView(null, rootEl, pv.render);
|
||||
}),
|
||||
multipleContentTagsTemplate
|
||||
]).then( (protoViews) => {
|
||||
tb.createRootViews(protoViews);
|
||||
|
||||
expect(rootEl).toHaveText('(, BAC)');
|
||||
expect(tb.rootEl).toHaveText('(, BAC)');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it("should redistribute direct child viewcontainers when the light dom changes", inject([AsyncTestCompleter], (async) => {
|
||||
createRenderer({
|
||||
templates: [new ViewDefinition({
|
||||
it("should redistribute direct child viewcontainers when the light dom changes",
|
||||
inject([AsyncTestCompleter, DomTestbed], (async, tb) => {
|
||||
tb.compileAll([
|
||||
mainDir,
|
||||
new ViewDefinition({
|
||||
componentId: 'main',
|
||||
template: '<multiple-content-tags>' +
|
||||
'<div><div template="manual" class="left">A</div></div>' +
|
||||
'<div>B</div>' +
|
||||
'</multiple-content-tags>',
|
||||
directives: [multipleContentTagsComponent, manualViewportDirective]
|
||||
})]
|
||||
});
|
||||
compileRoot(mainDir).then( (pv) => {
|
||||
var viewRefs = renderer.createInPlaceHostView(null, rootEl, pv.render);
|
||||
var vcRef = new RenderViewContainerRef(viewRefs[1], 1);
|
||||
var vcProtoViewRef = pv.elementBinders[0].nestedProtoView
|
||||
.elementBinders[1].nestedProtoView.render;
|
||||
expect(rootEl).toHaveText('(, B)');
|
||||
}),
|
||||
multipleContentTagsTemplate
|
||||
]).then( (protoViews) => {
|
||||
var views = tb.createRootViews(protoViews);
|
||||
var childProtoView = protoViews[1].elementBinders[1].nestedProtoView;
|
||||
expect(tb.rootEl).toHaveText('(, B)');
|
||||
|
||||
renderer.createViewInContainer(vcRef, 0, vcProtoViewRef)[0];
|
||||
var childView = tb.createViewInContainer(views[1].viewRef, 1, 0, childProtoView);
|
||||
|
||||
expect(rootEl).toHaveText('(, AB)');
|
||||
expect(tb.rootEl).toHaveText('(, AB)');
|
||||
|
||||
renderer.destroyViewInContainer(vcRef, 0);
|
||||
tb.destroyViewInContainer(views[1].viewRef, 1, 0, childView.viewRef);
|
||||
|
||||
expect(rootEl).toHaveText('(, B)');
|
||||
expect(tb.rootEl).toHaveText('(, B)');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it("should redistribute when the light dom changes", inject([AsyncTestCompleter], (async) => {
|
||||
createRenderer({
|
||||
templates: [new ViewDefinition({
|
||||
it("should redistribute when the light dom changes",
|
||||
inject([AsyncTestCompleter, DomTestbed], (async, tb) => {
|
||||
tb.compileAll([
|
||||
mainDir,
|
||||
new ViewDefinition({
|
||||
componentId: 'main',
|
||||
template: '<multiple-content-tags>' +
|
||||
'<div template="manual" class="left">A</div>' +
|
||||
'<div>B</div>' +
|
||||
'</multiple-content-tags>',
|
||||
directives: [multipleContentTagsComponent, manualViewportDirective]
|
||||
})]
|
||||
});
|
||||
compileRoot(mainDir).then( (pv) => {
|
||||
var viewRefs = renderer.createInPlaceHostView(null, rootEl, pv.render);
|
||||
var vcRef = new RenderViewContainerRef(viewRefs[1], 1);
|
||||
var vcProtoViewRef = pv.elementBinders[0].nestedProtoView
|
||||
.elementBinders[1].nestedProtoView.render;
|
||||
expect(rootEl).toHaveText('(, B)');
|
||||
}),
|
||||
multipleContentTagsTemplate
|
||||
]).then( (protoViews) => {
|
||||
var views = tb.createRootViews(protoViews);
|
||||
var childProtoView = protoViews[1].elementBinders[1].nestedProtoView;
|
||||
|
||||
renderer.createViewInContainer(vcRef, 0, vcProtoViewRef)[0];
|
||||
expect(tb.rootEl).toHaveText('(, B)');
|
||||
|
||||
expect(rootEl).toHaveText('(A, B)');
|
||||
var childView = tb.createViewInContainer(views[1].viewRef, 1, 0, childProtoView);
|
||||
|
||||
renderer.destroyViewInContainer(vcRef, 0);
|
||||
expect(tb.rootEl).toHaveText('(A, B)');
|
||||
|
||||
expect(rootEl).toHaveText('(, B)');
|
||||
tb.destroyViewInContainer(views[1].viewRef, 1, 0, childView.viewRef);
|
||||
|
||||
expect(tb.rootEl).toHaveText('(, B)');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it("should support nested components", inject([AsyncTestCompleter], (async) => {
|
||||
createRenderer({
|
||||
templates: [new ViewDefinition({
|
||||
it("should support nested components", inject([AsyncTestCompleter, DomTestbed], (async, tb) => {
|
||||
tb.compileAll([
|
||||
mainDir,
|
||||
new ViewDefinition({
|
||||
componentId: 'main',
|
||||
template: '<outer-with-indirect-nested>' +
|
||||
'<div>A</div>' +
|
||||
'<div>B</div>' +
|
||||
'</outer-with-indirect-nested>',
|
||||
directives: [outerWithIndirectNestedComponent]
|
||||
})]
|
||||
});
|
||||
compileRoot(mainDir).then( (pv) => {
|
||||
renderer.createInPlaceHostView(null, rootEl, pv.render);
|
||||
}),
|
||||
outerWithIndirectNestedTemplate,
|
||||
simpleTemplate
|
||||
]).then( (protoViews) => {
|
||||
tb.createRootViews(protoViews);
|
||||
|
||||
expect(rootEl).toHaveText('OUTER(SIMPLE(AB))');
|
||||
expect(tb.rootEl).toHaveText('OUTER(SIMPLE(AB))');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it("should support nesting with content being direct child of a nested component", inject([AsyncTestCompleter], (async) => {
|
||||
createRenderer({
|
||||
templates: [new ViewDefinition({
|
||||
it("should support nesting with content being direct child of a nested component",
|
||||
inject([AsyncTestCompleter, DomTestbed], (async, tb) => {
|
||||
tb.compileAll([
|
||||
mainDir,
|
||||
new ViewDefinition({
|
||||
componentId: 'main',
|
||||
template: '<outer>' +
|
||||
'<div template="manual" class="left">A</div>' +
|
||||
@@ -260,25 +256,27 @@ export function main() {
|
||||
'<div>C</div>' +
|
||||
'</outer>',
|
||||
directives: [outerComponent, manualViewportDirective]
|
||||
})]
|
||||
});
|
||||
compileRoot(mainDir).then( (pv) => {
|
||||
var viewRefs = renderer.createInPlaceHostView(null, rootEl, pv.render);
|
||||
var vcRef = new RenderViewContainerRef(viewRefs[1], 1);
|
||||
var vcProtoViewRef = pv.elementBinders[0].nestedProtoView
|
||||
.elementBinders[1].nestedProtoView.render;
|
||||
expect(rootEl).toHaveText('OUTER(INNER(INNERINNER(,BC)))');
|
||||
}),
|
||||
outerTemplate,
|
||||
innerTemplate,
|
||||
innerInnerTemplate
|
||||
]).then( (protoViews) => {
|
||||
var views = tb.createRootViews(protoViews);
|
||||
var childProtoView = protoViews[1].elementBinders[1].nestedProtoView;
|
||||
|
||||
renderer.createViewInContainer(vcRef, 0, vcProtoViewRef)[0];
|
||||
expect(tb.rootEl).toHaveText('OUTER(INNER(INNERINNER(,BC)))');
|
||||
|
||||
expect(rootEl).toHaveText('OUTER(INNER(INNERINNER(A,BC)))');
|
||||
tb.createViewInContainer(views[1].viewRef, 1, 0, childProtoView);
|
||||
|
||||
expect(tb.rootEl).toHaveText('OUTER(INNER(INNERINNER(A,BC)))');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should redistribute when the shadow dom changes', inject([AsyncTestCompleter], (async) => {
|
||||
createRenderer({
|
||||
templates: [new ViewDefinition({
|
||||
it('should redistribute when the shadow dom changes', inject([AsyncTestCompleter, DomTestbed], (async, tb) => {
|
||||
tb.compileAll([
|
||||
mainDir,
|
||||
new ViewDefinition({
|
||||
componentId: 'main',
|
||||
template: '<conditional-content>' +
|
||||
'<div class="left">A</div>' +
|
||||
@@ -286,64 +284,68 @@ export function main() {
|
||||
'<div>C</div>' +
|
||||
'</conditional-content>',
|
||||
directives: [conditionalContentComponent]
|
||||
})]
|
||||
});
|
||||
compileRoot(mainDir).then( (pv) => {
|
||||
var viewRefs = renderer.createInPlaceHostView(null, rootEl, pv.render);
|
||||
var vcRef = new RenderViewContainerRef(viewRefs[2], 0);
|
||||
var vcProtoViewRef = pv.elementBinders[0].nestedProtoView
|
||||
.elementBinders[0].nestedProtoView
|
||||
.elementBinders[0].nestedProtoView.render;
|
||||
}),
|
||||
conditionalContentTemplate
|
||||
]).then( (protoViews) => {
|
||||
var views = tb.createRootViews(protoViews);
|
||||
var childProtoView = protoViews[2].elementBinders[0].nestedProtoView;
|
||||
|
||||
expect(rootEl).toHaveText('(, ABC)');
|
||||
expect(tb.rootEl).toHaveText('(, ABC)');
|
||||
|
||||
renderer.createViewInContainer(vcRef, 0, vcProtoViewRef)[0];
|
||||
var childView = tb.createViewInContainer(views[2].viewRef, 0, 0, childProtoView);
|
||||
|
||||
expect(rootEl).toHaveText('(A, BC)');
|
||||
expect(tb.rootEl).toHaveText('(A, BC)');
|
||||
|
||||
renderer.destroyViewInContainer(vcRef, 0);
|
||||
tb.destroyViewInContainer(views[2].viewRef, 0, 0, childView.viewRef);
|
||||
|
||||
expect(rootEl).toHaveText('(, ABC)');
|
||||
expect(tb.rootEl).toHaveText('(, ABC)');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it("should support tabs with view caching", inject([AsyncTestCompleter], (async) => {
|
||||
createRenderer({
|
||||
templates: [new ViewDefinition({
|
||||
it("should support tabs with view caching", inject([AsyncTestCompleter, DomTestbed], (async, tb) => {
|
||||
tb.compileAll([
|
||||
mainDir,
|
||||
new ViewDefinition({
|
||||
componentId: 'main',
|
||||
template:
|
||||
'(<tab><span>0</span></tab>'+
|
||||
'<tab><span>1</span></tab>'+
|
||||
'<tab><span>2</span></tab>)',
|
||||
directives: [tabComponent]
|
||||
})],
|
||||
viewCacheCapacity: 5
|
||||
});
|
||||
compileRoot(mainDir).then( (pv) => {
|
||||
var viewRefs = renderer.createInPlaceHostView(null, rootEl, pv.render);
|
||||
var vcRef0 = new RenderViewContainerRef(viewRefs[2], 0);
|
||||
var vcRef1 = new RenderViewContainerRef(viewRefs[3], 0);
|
||||
var vcRef2 = new RenderViewContainerRef(viewRefs[4], 0);
|
||||
var mainPv = pv.elementBinders[0].nestedProtoView;
|
||||
var pvRef = mainPv.elementBinders[0].nestedProtoView.elementBinders[0].nestedProtoView.render;
|
||||
}),
|
||||
tabTemplate
|
||||
]).then( (protoViews) => {
|
||||
var views = tb.createRootViews(ListWrapper.slice(protoViews, 0, 2));
|
||||
var tabProtoView = protoViews[2];
|
||||
var tabChildProtoView = tabProtoView.elementBinders[0].nestedProtoView;
|
||||
|
||||
expect(rootEl).toHaveText('()');
|
||||
var tab1View = tb.createComponentView(views[1].viewRef, 0, tabProtoView);
|
||||
var tab2View = tb.createComponentView(views[1].viewRef, 1, tabProtoView);
|
||||
var tab3View = tb.createComponentView(views[1].viewRef, 2, tabProtoView);
|
||||
|
||||
renderer.createViewInContainer(vcRef0, 0, pvRef);
|
||||
expect(tb.rootEl).toHaveText('()');
|
||||
|
||||
expect(rootEl).toHaveText('(TAB(0))');
|
||||
var tabChildView = tb.createViewInContainer(tab1View.viewRef, 0, 0, tabChildProtoView);
|
||||
|
||||
renderer.destroyViewInContainer(vcRef0, 0);
|
||||
renderer.createViewInContainer(vcRef1, 0, pvRef);
|
||||
expect(tb.rootEl).toHaveText('(TAB(0))');
|
||||
|
||||
expect(rootEl).toHaveText('(TAB(1))');
|
||||
tb.renderer.dehydrateView(tabChildView.viewRef);
|
||||
tb.renderer.detachViewInContainer(tab1View.viewRef, 0, 0, tabChildView.viewRef);
|
||||
|
||||
renderer.destroyViewInContainer(vcRef1, 0);
|
||||
renderer.createViewInContainer(vcRef2, 0, pvRef);
|
||||
tb.renderer.attachViewInContainer(tab2View.viewRef, 0, 0, tabChildView.viewRef);
|
||||
tb.renderer.hydrateView(tabChildView.viewRef);
|
||||
|
||||
expect(rootEl).toHaveText('(TAB(2))');
|
||||
expect(tb.rootEl).toHaveText('(TAB(1))');
|
||||
|
||||
tb.renderer.dehydrateView(tabChildView.viewRef);
|
||||
tb.renderer.detachViewInContainer(tab2View.viewRef, 0, 0, tabChildView.viewRef);
|
||||
|
||||
tb.renderer.attachViewInContainer(tab3View.viewRef, 0, 0, tabChildView.viewRef);
|
||||
tb.renderer.hydrateView(tabChildView.viewRef);
|
||||
|
||||
expect(tb.rootEl).toHaveText('(TAB(2))');
|
||||
|
||||
async.done();
|
||||
});
|
||||
@@ -444,67 +446,62 @@ var autoViewportDirective = new DirectiveMetadata({
|
||||
type: DirectiveMetadata.DIRECTIVE_TYPE
|
||||
});
|
||||
|
||||
var tabGroupComponent = new DirectiveMetadata({
|
||||
selector: 'tab-group',
|
||||
id: 'tab-group',
|
||||
type: DirectiveMetadata.COMPONENT_TYPE
|
||||
});
|
||||
|
||||
var tabComponent = new DirectiveMetadata({
|
||||
selector: 'tab',
|
||||
id: 'tab',
|
||||
type: DirectiveMetadata.COMPONENT_TYPE
|
||||
});
|
||||
|
||||
var componentTemplates = [
|
||||
new ViewDefinition({
|
||||
componentId: 'simple',
|
||||
template: 'SIMPLE(<content></content>)',
|
||||
directives: []
|
||||
}),
|
||||
new ViewDefinition({
|
||||
componentId: 'empty',
|
||||
template: '',
|
||||
directives: []
|
||||
}),
|
||||
new ViewDefinition({
|
||||
componentId: 'multiple-content-tags',
|
||||
template: '(<content select=".left"></content>, <content></content>)',
|
||||
directives: []
|
||||
}),
|
||||
new ViewDefinition({
|
||||
componentId: 'outer-with-indirect-nested',
|
||||
template: 'OUTER(<simple><div><content></content></div></simple>)',
|
||||
directives: [simple]
|
||||
}),
|
||||
new ViewDefinition({
|
||||
componentId: 'outer',
|
||||
template: 'OUTER(<inner><content></content></inner>)',
|
||||
directives: [innerComponent]
|
||||
}),
|
||||
new ViewDefinition({
|
||||
componentId: 'inner',
|
||||
template: 'INNER(<innerinner><content></content></innerinner>)',
|
||||
directives: [innerInnerComponent]
|
||||
}),
|
||||
new ViewDefinition({
|
||||
componentId: 'innerinner',
|
||||
template: 'INNERINNER(<content select=".left"></content>,<content></content>)',
|
||||
directives: []
|
||||
}),
|
||||
new ViewDefinition({
|
||||
componentId: 'conditional-content',
|
||||
template: '<div>(<div *auto="cond"><content select=".left"></content></div>, <content></content>)</div>',
|
||||
directives: [autoViewportDirective]
|
||||
}),
|
||||
new ViewDefinition({
|
||||
componentId: 'tab-group',
|
||||
template: 'GROUP(<content></content>)',
|
||||
directives: []
|
||||
}),
|
||||
new ViewDefinition({
|
||||
componentId: 'tab',
|
||||
template: '<div><div *auto="cond">TAB(<content></content>)</div></div>',
|
||||
directives: [autoViewportDirective]
|
||||
})
|
||||
];
|
||||
var simpleTemplate = new ViewDefinition({
|
||||
componentId: 'simple',
|
||||
template: 'SIMPLE(<content></content>)',
|
||||
directives: []
|
||||
});
|
||||
|
||||
var emptyTemplate = new ViewDefinition({
|
||||
componentId: 'empty',
|
||||
template: '',
|
||||
directives: []
|
||||
});
|
||||
|
||||
var multipleContentTagsTemplate = new ViewDefinition({
|
||||
componentId: 'multiple-content-tags',
|
||||
template: '(<content select=".left"></content>, <content></content>)',
|
||||
directives: []
|
||||
});
|
||||
|
||||
var outerWithIndirectNestedTemplate = new ViewDefinition({
|
||||
componentId: 'outer-with-indirect-nested',
|
||||
template: 'OUTER(<simple><div><content></content></div></simple>)',
|
||||
directives: [simple]
|
||||
});
|
||||
|
||||
var outerTemplate = new ViewDefinition({
|
||||
componentId: 'outer',
|
||||
template: 'OUTER(<inner><content></content></inner>)',
|
||||
directives: [innerComponent]
|
||||
});
|
||||
|
||||
var innerTemplate = new ViewDefinition({
|
||||
componentId: 'inner',
|
||||
template: 'INNER(<innerinner><content></content></innerinner>)',
|
||||
directives: [innerInnerComponent]
|
||||
});
|
||||
|
||||
var innerInnerTemplate = new ViewDefinition({
|
||||
componentId: 'innerinner',
|
||||
template: 'INNERINNER(<content select=".left"></content>,<content></content>)',
|
||||
directives: []
|
||||
});
|
||||
|
||||
var conditionalContentTemplate = new ViewDefinition({
|
||||
componentId: 'conditional-content',
|
||||
template: '<div>(<div *auto="cond"><content select=".left"></content></div>, <content></content>)</div>',
|
||||
directives: [autoViewportDirective]
|
||||
});
|
||||
|
||||
var tabTemplate = new ViewDefinition({
|
||||
componentId: 'tab',
|
||||
template: '<div><div *auto="cond">TAB(<content></content>)</div></div>',
|
||||
directives: [autoViewportDirective]
|
||||
});
|
||||
|
||||
@@ -1,183 +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 {ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {ViewFactory} from 'angular2/src/render/dom/view/view_factory';
|
||||
import {DomProtoView} from 'angular2/src/render/dom/view/proto_view';
|
||||
import {DomView} from 'angular2/src/render/dom/view/view';
|
||||
import {ElementBinder} from 'angular2/src/render/dom/view/element_binder';
|
||||
import {ShadowDomStrategy} from 'angular2/src/render/dom/shadow_dom/shadow_dom_strategy';
|
||||
import {LightDom} from 'angular2/src/render/dom/shadow_dom/light_dom'
|
||||
import {EventManager} from 'angular2/src/render/dom/events/event_manager';
|
||||
|
||||
export function main() {
|
||||
describe('RenderViewFactory', () => {
|
||||
var eventManager;
|
||||
var shadowDomStrategy;
|
||||
|
||||
function createViewFactory({capacity}):ViewFactory {
|
||||
return new ViewFactory(capacity, eventManager, shadowDomStrategy);
|
||||
}
|
||||
|
||||
function createProtoView(rootEl=null, binders=null) {
|
||||
if (isBlank(rootEl)) {
|
||||
rootEl = el('<div></div>');
|
||||
}
|
||||
if (isBlank(binders)) {
|
||||
binders = [];
|
||||
}
|
||||
return new DomProtoView({
|
||||
element: rootEl,
|
||||
elementBinders: binders
|
||||
});
|
||||
}
|
||||
|
||||
function createComponentElBinder(componentId, nestedProtoView = null) {
|
||||
var binder = new ElementBinder({
|
||||
componentId: componentId,
|
||||
textNodeIndices: []
|
||||
});
|
||||
binder.nestedProtoView = nestedProtoView;
|
||||
return binder;
|
||||
}
|
||||
|
||||
|
||||
beforeEach( () => {
|
||||
eventManager = new SpyEventManager();
|
||||
shadowDomStrategy = new SpyShadowDomStrategy();
|
||||
});
|
||||
|
||||
it('should create views without cache', () => {
|
||||
var pv = createProtoView();
|
||||
var vf = createViewFactory({
|
||||
capacity: 0
|
||||
});
|
||||
expect(vf.getView(pv) instanceof DomView).toBe(true);
|
||||
});
|
||||
|
||||
describe('caching', () => {
|
||||
|
||||
it('should support multiple RenderProtoViews', () => {
|
||||
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, log;
|
||||
|
||||
beforeEach(() => {
|
||||
vf = createViewFactory({capacity: 1});
|
||||
log = [];
|
||||
shadowDomStrategy.spy('attachTemplate').andCallFake( (el, view) => {
|
||||
ListWrapper.push(log, ['attachTemplate', el, view]);
|
||||
});
|
||||
shadowDomStrategy.spy('constructLightDom').andCallFake( (lightDomView, shadowDomView, el) => {
|
||||
ListWrapper.push(log, ['constructLightDom', lightDomView, shadowDomView, el]);
|
||||
return new SpyLightDom();
|
||||
});
|
||||
});
|
||||
|
||||
it('should create static child component views', () => {
|
||||
var hostPv = createProtoView(el('<div><div class="ng-binding"></div></div>'), [
|
||||
createComponentElBinder(
|
||||
'someComponent',
|
||||
createProtoView()
|
||||
)
|
||||
]);
|
||||
var hostView = vf.getView(hostPv);
|
||||
var shadowView = hostView.componentChildViews[0];
|
||||
expect(shadowView).toBeTruthy();
|
||||
expect(hostView.lightDoms[0]).toBeTruthy();
|
||||
expect(log[0]).toEqual(['constructLightDom', hostView, shadowView, hostView.boundElements[0]]);
|
||||
expect(log[1]).toEqual(['attachTemplate', hostView.boundElements[0], shadowView]);
|
||||
});
|
||||
|
||||
it('should not create dynamic child component views', () => {
|
||||
var hostPv = createProtoView(el('<div><div class="ng-binding"></div></div>'), [
|
||||
createComponentElBinder(
|
||||
'someComponent',
|
||||
null
|
||||
)
|
||||
]);
|
||||
var hostView = vf.getView(hostPv);
|
||||
var shadowView = hostView.componentChildViews[0];
|
||||
expect(shadowView).toBeFalsy();
|
||||
expect(hostView.lightDoms[0]).toBeFalsy();
|
||||
expect(log).toEqual([]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@proxy
|
||||
@IMPLEMENTS(EventManager)
|
||||
class SpyEventManager extends SpyObject {
|
||||
constructor(){super(EventManager);}
|
||||
noSuchMethod(m){return super.noSuchMethod(m)}
|
||||
}
|
||||
|
||||
@proxy
|
||||
@IMPLEMENTS(ShadowDomStrategy)
|
||||
class SpyShadowDomStrategy extends SpyObject {
|
||||
constructor(){super(ShadowDomStrategy);}
|
||||
noSuchMethod(m){return super.noSuchMethod(m)}
|
||||
}
|
||||
|
||||
@proxy
|
||||
@IMPLEMENTS(LightDom)
|
||||
class SpyLightDom extends SpyObject {
|
||||
constructor(){super(LightDom);}
|
||||
noSuchMethod(m){return super.noSuchMethod(m)}
|
||||
}
|
||||
@@ -1,285 +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 {DomProtoView} from 'angular2/src/render/dom/view/proto_view';
|
||||
import {ElementBinder} from 'angular2/src/render/dom/view/element_binder';
|
||||
import {DomView} from 'angular2/src/render/dom/view/view';
|
||||
import {ShadowDomStrategy} from 'angular2/src/render/dom/shadow_dom/shadow_dom_strategy';
|
||||
import {LightDom} from 'angular2/src/render/dom/shadow_dom/light_dom';
|
||||
import {EventManager} from 'angular2/src/render/dom/events/event_manager';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
import {ViewFactory} from 'angular2/src/render/dom/view/view_factory';
|
||||
import {RenderViewHydrator} from 'angular2/src/render/dom/view/view_hydrator';
|
||||
|
||||
export function main() {
|
||||
|
||||
describe('RenderViewHydrator', () => {
|
||||
var shadowDomStrategy;
|
||||
var eventManager;
|
||||
var viewFactory;
|
||||
var viewHydrator;
|
||||
|
||||
function createProtoView({rootEl, binders}={}) {
|
||||
if (isBlank(rootEl)) {
|
||||
rootEl = el('<div></div>');
|
||||
}
|
||||
if (isBlank(binders)) {
|
||||
binders = [];
|
||||
}
|
||||
return new DomProtoView({
|
||||
element: rootEl,
|
||||
elementBinders: binders
|
||||
});
|
||||
}
|
||||
|
||||
function createComponentElBinder(componentId, nestedProtoView = null) {
|
||||
var binder = new ElementBinder({
|
||||
componentId: componentId,
|
||||
textNodeIndices: []
|
||||
});
|
||||
binder.nestedProtoView = nestedProtoView;
|
||||
return binder;
|
||||
}
|
||||
|
||||
function createHostProtoView(nestedProtoView) {
|
||||
return createProtoView({
|
||||
binders: [
|
||||
createComponentElBinder(
|
||||
'someComponent',
|
||||
nestedProtoView
|
||||
)
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
function createEmptyView() {
|
||||
var root = el('<div><div></div></div>');
|
||||
return new DomView(createProtoView(), [DOM.childNodes(root)[0]],
|
||||
[], [], []);
|
||||
}
|
||||
|
||||
function createHostView(pv, shadowDomView) {
|
||||
var view = new DomView(pv, [el('<div></div>')],
|
||||
[], [el('<div></div>')], [null]);
|
||||
ViewFactory.setComponentView(shadowDomStrategy, view, 0, shadowDomView);
|
||||
return view;
|
||||
}
|
||||
|
||||
function hydrate(view) {
|
||||
viewHydrator.hydrateInPlaceHostView(null, view);
|
||||
}
|
||||
|
||||
function dehydrate(view) {
|
||||
viewHydrator.dehydrateInPlaceHostView(null, view);
|
||||
}
|
||||
|
||||
beforeEach( () => {
|
||||
eventManager = new SpyEventManager();
|
||||
shadowDomStrategy = new SpyShadowDomStrategy();
|
||||
shadowDomStrategy.spy('constructLightDom').andCallFake( (lightDomView, shadowDomView, el) => {
|
||||
return new SpyLightDom();
|
||||
});
|
||||
viewFactory = new SpyViewFactory();
|
||||
viewHydrator = new RenderViewHydrator(eventManager, viewFactory, shadowDomStrategy);
|
||||
});
|
||||
|
||||
describe('hydrateDynamicComponentView', () => {
|
||||
|
||||
it('should redistribute', () => {
|
||||
var shadowView = createEmptyView();
|
||||
var hostPv = createHostProtoView(createProtoView());
|
||||
var hostView = createHostView(hostPv, shadowView);
|
||||
viewHydrator.hydrateDynamicComponentView(hostView, 0, shadowView);
|
||||
var lightDomSpy:SpyLightDom = hostView.lightDoms[0];
|
||||
expect(lightDomSpy.spy('redistribute')).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('hydrateInPlaceHostView', () => {
|
||||
|
||||
function createInPlaceHostView() {
|
||||
var hostPv = createHostProtoView(createProtoView());
|
||||
var shadowView = createEmptyView();
|
||||
return createHostView(hostPv, shadowView);
|
||||
}
|
||||
|
||||
it('should hydrate the view', () => {
|
||||
var hostView = createInPlaceHostView();
|
||||
viewHydrator.hydrateInPlaceHostView(null, hostView);
|
||||
|
||||
expect(hostView.hydrated).toBe(true);
|
||||
});
|
||||
|
||||
it('should store the view in the parent view', () => {
|
||||
var parentView = createEmptyView();
|
||||
var hostView = createInPlaceHostView();
|
||||
|
||||
viewHydrator.hydrateInPlaceHostView(parentView, hostView);
|
||||
|
||||
expect(parentView.imperativeHostViews).toEqual([hostView]);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('dehydrateInPlaceHostView', () => {
|
||||
|
||||
function createAndHydrateInPlaceHostView(parentView) {
|
||||
var hostPv = createHostProtoView(createProtoView());
|
||||
var shadowView = createEmptyView();
|
||||
var hostView = createHostView(hostPv, shadowView);
|
||||
viewHydrator.hydrateInPlaceHostView(parentView, hostView);
|
||||
return hostView;
|
||||
}
|
||||
|
||||
it('should clear the host view', () => {
|
||||
var parentView = createEmptyView();
|
||||
var hostView = createAndHydrateInPlaceHostView(parentView);
|
||||
|
||||
var rootNodes = hostView.rootNodes;
|
||||
expect(rootNodes[0].parentNode).toBeTruthy();
|
||||
|
||||
viewHydrator.dehydrateInPlaceHostView(parentView, hostView);
|
||||
|
||||
expect(parentView.imperativeHostViews).toEqual([]);
|
||||
expect(rootNodes[0].parentNode).toBeFalsy();
|
||||
expect(hostView.rootNodes).toEqual([]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('hydrate... shared functionality', () => {
|
||||
|
||||
it('should hydrate existing child components', () => {
|
||||
var hostPv = createHostProtoView(createProtoView());
|
||||
var shadowView = createEmptyView();
|
||||
createHostView(hostPv, shadowView);
|
||||
|
||||
hydrate(shadowView);
|
||||
|
||||
expect(shadowView.hydrated).toBe(true);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('dehydrate... shared functionality', () => {
|
||||
var hostView;
|
||||
|
||||
function createAndHydrate(nestedProtoView, shadowView, imperativeHostView = null) {
|
||||
var hostPv = createHostProtoView(nestedProtoView);
|
||||
hostView = createHostView(hostPv, shadowView);
|
||||
if (isPresent(imperativeHostView)) {
|
||||
viewHydrator.hydrateInPlaceHostView(hostView, imperativeHostView);
|
||||
}
|
||||
|
||||
hydrate(hostView);
|
||||
}
|
||||
|
||||
it('should dehydrate child components', () => {
|
||||
var shadowView = createEmptyView();
|
||||
createAndHydrate(createProtoView(), shadowView);
|
||||
|
||||
expect(shadowView.hydrated).toBe(true);
|
||||
dehydrate(hostView);
|
||||
|
||||
expect(shadowView.hydrated).toBe(false);
|
||||
});
|
||||
|
||||
it('should not clear static child components', () => {
|
||||
var shadowView = createEmptyView();
|
||||
createAndHydrate(createProtoView(), shadowView);
|
||||
dehydrate(hostView);
|
||||
|
||||
expect(hostView.componentChildViews[0]).toBe(shadowView);
|
||||
expect(shadowView.rootNodes[0].parentNode).toBeTruthy();
|
||||
expect(viewFactory.spy('returnView')).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should clear dynamic child components', () => {
|
||||
var shadowView = createEmptyView();
|
||||
createAndHydrate(null, shadowView);
|
||||
expect(shadowView.rootNodes[0].parentNode).toBeTruthy();
|
||||
|
||||
dehydrate(hostView);
|
||||
|
||||
expect(hostView.componentChildViews[0]).toBe(null);
|
||||
expect(shadowView.rootNodes[0].parentNode).toBe(null);
|
||||
expect(viewFactory.spy('returnView')).toHaveBeenCalledWith(shadowView);
|
||||
});
|
||||
|
||||
it('should clear views in ViewContainers', () => {
|
||||
createAndHydrate(null, null);
|
||||
var vc = hostView.getOrCreateViewContainer(0);
|
||||
var childView = createEmptyView();
|
||||
vc.insert(childView);
|
||||
|
||||
dehydrate(hostView);
|
||||
|
||||
expect(viewFactory.spy('returnView')).toHaveBeenCalledWith(childView);
|
||||
});
|
||||
|
||||
it('should clear imperatively added child components', () => {
|
||||
var shadowView = createEmptyView();
|
||||
createAndHydrate(createProtoView(), shadowView);
|
||||
var impHostView = createHostView(createHostProtoView(createProtoView()), createEmptyView());
|
||||
shadowView.imperativeHostViews = [impHostView];
|
||||
|
||||
var rootNodes = impHostView.rootNodes;
|
||||
expect(rootNodes[0].parentNode).toBeTruthy();
|
||||
|
||||
dehydrate(hostView);
|
||||
|
||||
expect(shadowView.imperativeHostViews).toEqual([]);
|
||||
expect(impHostView.rootNodes).toEqual([]);
|
||||
expect(rootNodes[0].parentNode).toBeFalsy();
|
||||
expect(viewFactory.spy('returnView')).toHaveBeenCalledWith(impHostView);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@proxy
|
||||
@IMPLEMENTS(EventManager)
|
||||
class SpyEventManager extends SpyObject {
|
||||
constructor(){super(EventManager);}
|
||||
noSuchMethod(m){return super.noSuchMethod(m)}
|
||||
}
|
||||
|
||||
@proxy
|
||||
@IMPLEMENTS(ShadowDomStrategy)
|
||||
class SpyShadowDomStrategy extends SpyObject {
|
||||
constructor(){super(ShadowDomStrategy);}
|
||||
noSuchMethod(m){return super.noSuchMethod(m)}
|
||||
}
|
||||
|
||||
@proxy
|
||||
@IMPLEMENTS(LightDom)
|
||||
class SpyLightDom extends SpyObject {
|
||||
constructor(){super(LightDom);}
|
||||
noSuchMethod(m){return super.noSuchMethod(m)}
|
||||
}
|
||||
|
||||
@proxy
|
||||
@IMPLEMENTS(ViewFactory)
|
||||
class SpyViewFactory extends SpyObject {
|
||||
constructor(){super(ViewFactory);}
|
||||
noSuchMethod(m){return super.noSuchMethod(m)}
|
||||
}
|
||||
@@ -20,7 +20,6 @@ import {ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {DomProtoView} from 'angular2/src/render/dom/view/proto_view';
|
||||
import {ElementBinder} from 'angular2/src/render/dom/view/element_binder';
|
||||
import {DomView} from 'angular2/src/render/dom/view/view';
|
||||
import {ViewContainer} from 'angular2/src/render/dom/view/view_container';
|
||||
import {LightDom} from 'angular2/src/render/dom/shadow_dom/light_dom';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
@@ -82,21 +81,6 @@ export function main() {
|
||||
|
||||
});
|
||||
|
||||
describe('getOrCreateViewContainer', () => {
|
||||
it('should create a new container', () => {
|
||||
var pv = createProtoView([new ElementBinder()]);
|
||||
var view = createView(pv, 1);
|
||||
expect(view.getOrCreateViewContainer(0) instanceof ViewContainer).toBe(true);
|
||||
});
|
||||
|
||||
it('should return an existing container', () => {
|
||||
var pv = createProtoView([new ElementBinder()]);
|
||||
var view = createView(pv, 1);
|
||||
var vc = view.getOrCreateViewContainer(0);
|
||||
expect(view.getOrCreateViewContainer(0)).toBe(vc);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user