feat(compiler): attach components and project light dom during compilation.
Closes #2529 BREAKING CHANGES: - shadow dom emulation no longer supports the `<content>` tag. Use the new `<ng-content>` instead (works with all shadow dom strategies). - removed `DomRenderer.setViewRootNodes` and `AppViewManager.getComponentView` -> use `DomRenderer.getNativeElementSync(elementRef)` and change shadow dom directly - the `Renderer` interface has changed: * `createView` now also has to support sub views * the notion of a container has been removed. Instead, the renderer has to implement methods to attach views next to elements or other views. * a RenderView now contains multiple RenderFragments. Fragments are used to move DOM nodes around. Internal changes / design changes: - Introduce notion of view fragments on render side - DomProtoViews and DomViews on render side are merged, AppProtoViews are not merged, AppViews are partially merged (they share arrays with the other merged AppViews but we keep individual AppView instances for now). - DomProtoViews always have a `<template>` element as root * needed for storing subviews * we have less chunks of DOM to clone now - remove fake ElementBinder / Bound element for root text bindings and model them explicitly. This removes a lot of special cases we had! - AppView shares data with nested component views - some methods in AppViewManager (create, hydrate, dehydrate) are iterative now * now possible as we have all child AppViews / ElementRefs already in an array!
This commit is contained in:
@@ -48,7 +48,7 @@ import "package:angular2/di.dart" show Injectable;
|
||||
@Template(
|
||||
inline: '<div class="greeting">{{greeting}} <span red>world</span>!</div>'
|
||||
'<button class="changeButton" (click)="changeGreeting()">'
|
||||
'change greeting</button><content></content>',
|
||||
'change greeting</button><ng-content></ng-content>',
|
||||
directives: const [RedDec])
|
||||
class HelloCmp {
|
||||
String greeting;
|
||||
|
||||
@@ -29,7 +29,7 @@ class HelloRootCmp {
|
||||
}
|
||||
|
||||
@Component({selector: 'hello-app'})
|
||||
@View({template: 'before: <content></content> after: done'})
|
||||
@View({template: 'before: <ng-content></ng-content> after: done'})
|
||||
class HelloRootCmpContent {
|
||||
constructor() {}
|
||||
}
|
||||
@@ -68,19 +68,19 @@ class HelloRootDirectiveIsNotCmp {
|
||||
export function main() {
|
||||
var fakeDoc, el, el2, testBindings, lightDom;
|
||||
|
||||
beforeEach(() => {
|
||||
fakeDoc = DOM.createHtmlDocument();
|
||||
el = DOM.createElement('hello-app', fakeDoc);
|
||||
el2 = DOM.createElement('hello-app-2', fakeDoc);
|
||||
lightDom = DOM.createElement('light-dom-el', fakeDoc);
|
||||
DOM.appendChild(fakeDoc.body, el);
|
||||
DOM.appendChild(fakeDoc.body, el2);
|
||||
DOM.appendChild(el, lightDom);
|
||||
DOM.setText(lightDom, 'loading');
|
||||
testBindings = [bind(DOCUMENT_TOKEN).toValue(fakeDoc)];
|
||||
});
|
||||
|
||||
describe('bootstrap factory method', () => {
|
||||
beforeEach(() => {
|
||||
fakeDoc = DOM.createHtmlDocument();
|
||||
el = DOM.createElement('hello-app', fakeDoc);
|
||||
el2 = DOM.createElement('hello-app-2', fakeDoc);
|
||||
lightDom = DOM.createElement('light-dom-el', fakeDoc);
|
||||
DOM.appendChild(fakeDoc.body, el);
|
||||
DOM.appendChild(fakeDoc.body, el2);
|
||||
DOM.appendChild(el, lightDom);
|
||||
DOM.setText(lightDom, 'loading');
|
||||
testBindings = [bind(DOCUMENT_TOKEN).toValue(fakeDoc)];
|
||||
});
|
||||
|
||||
it('should throw if bootstrapped Directive is not a Component',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
var refPromise =
|
||||
@@ -149,14 +149,6 @@ export function main() {
|
||||
});
|
||||
}));
|
||||
|
||||
it("should support shadow dom content tag", inject([AsyncTestCompleter], (async) => {
|
||||
var refPromise = bootstrap(HelloRootCmpContent, testBindings);
|
||||
refPromise.then((ref) => {
|
||||
expect(el).toHaveText('before: loading after: done');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should register each application with the testability registry',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
var refPromise1 = bootstrap(HelloRootCmp, testBindings);
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import {List, ListWrapper, Map, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {IMPLEMENTS, Type, isBlank, stringify, isPresent} from 'angular2/src/facade/lang';
|
||||
import {IMPLEMENTS, Type, isBlank, stringify, isPresent, isArray} from 'angular2/src/facade/lang';
|
||||
import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
|
||||
|
||||
import {Compiler, CompilerCache} from 'angular2/src/core/compiler/compiler';
|
||||
@@ -45,24 +45,41 @@ export function main() {
|
||||
rootProtoView;
|
||||
var renderCompileRequests: any[];
|
||||
|
||||
beforeEach(() => {
|
||||
directiveResolver = new DirectiveResolver();
|
||||
tplResolver = new FakeViewResolver();
|
||||
cmpUrlMapper = new RuntimeComponentUrlMapper();
|
||||
renderCompiler = new SpyRenderCompiler();
|
||||
renderCompiler.spy('compileHost')
|
||||
.andCallFake((componentId) => {
|
||||
return PromiseWrapper.resolve(createRenderProtoView(
|
||||
[createRenderComponentElementBinder(0)], renderApi.ViewType.HOST));
|
||||
});
|
||||
rootProtoView = createRootProtoView(directiveResolver, MainComponent);
|
||||
});
|
||||
function mergeProtoViewsRecursively(
|
||||
protoViewRefs: List<renderApi.RenderProtoViewRef | List<any>>,
|
||||
target: renderApi.RenderProtoViewMergeMapping[]): renderApi.RenderProtoViewRef {
|
||||
var targetIndex = target.length;
|
||||
target.push(null);
|
||||
|
||||
var flattended = protoViewRefs.map(protoViewRefOrArray => {
|
||||
var resolvedProtoViewRef;
|
||||
if (isArray(protoViewRefOrArray)) {
|
||||
resolvedProtoViewRef = mergeProtoViewsRecursively(
|
||||
<List<renderApi.RenderProtoViewRef>>protoViewRefOrArray, target);
|
||||
} else {
|
||||
resolvedProtoViewRef = protoViewRefOrArray;
|
||||
}
|
||||
return resolvedProtoViewRef;
|
||||
});
|
||||
var merged = [];
|
||||
flattended.forEach((entry) => {
|
||||
if (entry instanceof MergedRenderProtoViewRef) {
|
||||
entry.originals.forEach(ref => merged.push(ref));
|
||||
} else {
|
||||
merged.push(entry);
|
||||
}
|
||||
});
|
||||
var result = new MergedRenderProtoViewRef(merged);
|
||||
target[targetIndex] = new renderApi.RenderProtoViewMergeMapping(result, 1, [], [], []);
|
||||
return result;
|
||||
}
|
||||
|
||||
function createCompiler(renderCompileResults:
|
||||
List<renderApi.ProtoViewDto | Promise<renderApi.ProtoViewDto>>,
|
||||
protoViewFactoryResults: List<List<AppProtoView>>) {
|
||||
protoViewFactoryResults: List<AppProtoView>) {
|
||||
var urlResolver = new UrlResolver();
|
||||
renderCompileRequests = [];
|
||||
renderCompileResults = ListWrapper.clone(renderCompileResults);
|
||||
renderCompiler.spy('compile').andCallFake((view) => {
|
||||
renderCompileRequests.push(view);
|
||||
return PromiseWrapper.resolve(ListWrapper.removeAt(renderCompileResults, 0));
|
||||
@@ -73,12 +90,31 @@ export function main() {
|
||||
urlResolver, renderCompiler, protoViewFactory, new FakeAppRootUrl());
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
directiveResolver = new DirectiveResolver();
|
||||
tplResolver = new FakeViewResolver();
|
||||
cmpUrlMapper = new RuntimeComponentUrlMapper();
|
||||
renderCompiler = new SpyRenderCompiler();
|
||||
renderCompiler.spy('compileHost')
|
||||
.andCallFake((componentId) => {
|
||||
return PromiseWrapper.resolve(createRenderProtoView(
|
||||
[createRenderComponentElementBinder(0)], renderApi.ViewType.HOST));
|
||||
});
|
||||
renderCompiler.spy('mergeProtoViewsRecursively')
|
||||
.andCallFake((protoViewRefs: List<renderApi.RenderProtoViewRef | List<any>>) => {
|
||||
var result: renderApi.RenderProtoViewMergeMapping[] = [];
|
||||
mergeProtoViewsRecursively(protoViewRefs, result);
|
||||
return PromiseWrapper.resolve(result);
|
||||
});
|
||||
rootProtoView = createRootProtoView(directiveResolver, MainComponent);
|
||||
});
|
||||
|
||||
describe('serialize template', () => {
|
||||
|
||||
function captureTemplate(template: viewAnn.View): Promise<renderApi.ViewDefinition> {
|
||||
tplResolver.setView(MainComponent, template);
|
||||
var compiler =
|
||||
createCompiler([createRenderProtoView()], [[rootProtoView], [createProtoView()]]);
|
||||
createCompiler([createRenderProtoView()], [rootProtoView, createProtoView()]);
|
||||
return compiler.compileInHost(MainComponent)
|
||||
.then((_) => {
|
||||
expect(renderCompileRequests.length).toBe(1);
|
||||
@@ -260,7 +296,7 @@ export function main() {
|
||||
tplResolver.setView(MainComponent, new viewAnn.View({template: '<div></div>'}));
|
||||
var renderProtoView = createRenderProtoView();
|
||||
var expectedProtoView = createProtoView();
|
||||
var compiler = createCompiler([renderProtoView], [[rootProtoView], [expectedProtoView]]);
|
||||
var compiler = createCompiler([renderProtoView], [rootProtoView, expectedProtoView]);
|
||||
compiler.compileInHost(MainComponent)
|
||||
.then((_) => {
|
||||
var request = protoViewFactory.requests[1];
|
||||
@@ -272,7 +308,7 @@ export function main() {
|
||||
it('should pass the component binding', inject([AsyncTestCompleter], (async) => {
|
||||
tplResolver.setView(MainComponent, new viewAnn.View({template: '<div></div>'}));
|
||||
var compiler =
|
||||
createCompiler([createRenderProtoView()], [[rootProtoView], [createProtoView()]]);
|
||||
createCompiler([createRenderProtoView()], [rootProtoView, createProtoView()]);
|
||||
compiler.compileInHost(MainComponent)
|
||||
.then((_) => {
|
||||
var request = protoViewFactory.requests[1];
|
||||
@@ -286,7 +322,7 @@ export function main() {
|
||||
MainComponent,
|
||||
new viewAnn.View({template: '<div></div>', directives: [SomeDirective]}));
|
||||
var compiler =
|
||||
createCompiler([createRenderProtoView()], [[rootProtoView], [createProtoView()]]);
|
||||
createCompiler([createRenderProtoView()], [rootProtoView, createProtoView()]);
|
||||
compiler.compileInHost(MainComponent)
|
||||
.then((_) => {
|
||||
var request = protoViewFactory.requests[1];
|
||||
@@ -300,7 +336,7 @@ export function main() {
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
tplResolver.setView(MainComponent, new viewAnn.View({template: '<div></div>'}));
|
||||
var compiler =
|
||||
createCompiler([createRenderProtoView()], [[rootProtoView], [createProtoView()]]);
|
||||
createCompiler([createRenderProtoView()], [rootProtoView, createProtoView()]);
|
||||
compiler.compileInHost(MainComponent)
|
||||
.then((protoViewRef) => {
|
||||
expect(internalProtoView(protoViewRef)).toBe(rootProtoView);
|
||||
@@ -316,17 +352,21 @@ export function main() {
|
||||
var mainProtoView =
|
||||
createProtoView([createComponentElementBinder(directiveResolver, NestedComponent)]);
|
||||
var nestedProtoView = createProtoView();
|
||||
var compiler = createCompiler(
|
||||
[
|
||||
createRenderProtoView([createRenderComponentElementBinder(0)]),
|
||||
createRenderProtoView()
|
||||
],
|
||||
[[rootProtoView], [mainProtoView], [nestedProtoView]]);
|
||||
var renderPvDtos = [
|
||||
createRenderProtoView([createRenderComponentElementBinder(0)]),
|
||||
createRenderProtoView()
|
||||
];
|
||||
var compiler =
|
||||
createCompiler(renderPvDtos, [rootProtoView, mainProtoView, nestedProtoView]);
|
||||
compiler.compileInHost(MainComponent)
|
||||
.then((protoViewRef) => {
|
||||
expect(internalProtoView(protoViewRef).elementBinders[0].nestedProtoView)
|
||||
.toBe(mainProtoView);
|
||||
expect(originalRenderProtoViewRefs(mainProtoView))
|
||||
.toEqual([renderPvDtos[0].render, renderPvDtos[1].render]);
|
||||
expect(mainProtoView.elementBinders[0].nestedProtoView).toBe(nestedProtoView);
|
||||
expect(originalRenderProtoViewRefs(nestedProtoView))
|
||||
.toEqual([renderPvDtos[1].render]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
@@ -334,25 +374,40 @@ export function main() {
|
||||
it('should load nested components in viewcontainers', inject([AsyncTestCompleter], (async) => {
|
||||
tplResolver.setView(MainComponent, new viewAnn.View({template: '<div></div>'}));
|
||||
tplResolver.setView(NestedComponent, new viewAnn.View({template: '<div></div>'}));
|
||||
var mainProtoView = createProtoView([createViewportElementBinder(null)]);
|
||||
var viewportProtoView =
|
||||
createProtoView([createComponentElementBinder(directiveResolver, NestedComponent)]);
|
||||
var mainProtoView = createProtoView([createViewportElementBinder(viewportProtoView)]);
|
||||
var nestedProtoView = createProtoView();
|
||||
var compiler = createCompiler(
|
||||
[
|
||||
createRenderProtoView([
|
||||
createRenderViewportElementBinder(createRenderProtoView(
|
||||
[createRenderComponentElementBinder(0)], renderApi.ViewType.EMBEDDED))
|
||||
]),
|
||||
createRenderProtoView()
|
||||
],
|
||||
[[rootProtoView], [mainProtoView, viewportProtoView], [nestedProtoView]]);
|
||||
var renderPvDtos = [
|
||||
createRenderProtoView([
|
||||
createRenderViewportElementBinder(createRenderProtoView(
|
||||
[createRenderComponentElementBinder(0)], renderApi.ViewType.EMBEDDED))
|
||||
]),
|
||||
createRenderProtoView()
|
||||
];
|
||||
var compiler =
|
||||
createCompiler(renderPvDtos, [rootProtoView, mainProtoView, nestedProtoView]);
|
||||
compiler.compileInHost(MainComponent)
|
||||
.then((protoViewRef) => {
|
||||
expect(internalProtoView(protoViewRef).elementBinders[0].nestedProtoView)
|
||||
.toBe(mainProtoView);
|
||||
expect(originalRenderProtoViewRefs(mainProtoView))
|
||||
.toEqual([
|
||||
renderPvDtos[0]
|
||||
.render,
|
||||
renderPvDtos[0].elementBinders[0].nestedProtoView.render,
|
||||
renderPvDtos[1].render
|
||||
]);
|
||||
expect(viewportProtoView.elementBinders[0].nestedProtoView).toBe(nestedProtoView);
|
||||
|
||||
expect(originalRenderProtoViewRefs(viewportProtoView))
|
||||
.toEqual([
|
||||
renderPvDtos[0]
|
||||
.elementBinders[0]
|
||||
.nestedProtoView.render,
|
||||
renderPvDtos[1].render
|
||||
]);
|
||||
expect(originalRenderProtoViewRefs(nestedProtoView))
|
||||
.toEqual([renderPvDtos[1].render]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
@@ -360,7 +415,7 @@ export function main() {
|
||||
it('should cache compiled host components', inject([AsyncTestCompleter], (async) => {
|
||||
tplResolver.setView(MainComponent, new viewAnn.View({template: '<div></div>'}));
|
||||
var mainPv = createProtoView();
|
||||
var compiler = createCompiler([createRenderProtoView()], [[rootProtoView], [mainPv]]);
|
||||
var compiler = createCompiler([createRenderProtoView()], [rootProtoView, mainPv]);
|
||||
compiler.compileInHost(MainComponent)
|
||||
.then((protoViewRef) => {
|
||||
expect(internalProtoView(protoViewRef).elementBinders[0].nestedProtoView)
|
||||
@@ -404,7 +459,7 @@ export function main() {
|
||||
var nestedPv = createProtoView([]);
|
||||
var compiler = createCompiler(
|
||||
[createRenderProtoView(), createRenderProtoView(), createRenderProtoView()],
|
||||
[[rootProtoView], [mainPv], [nestedPv], [rootProtoView2], [mainPv]]);
|
||||
[rootProtoView, mainPv, nestedPv, rootProtoView2, mainPv]);
|
||||
compiler.compileInHost(MainComponent)
|
||||
.then((protoViewRef) => {
|
||||
expect(internalProtoView(protoViewRef)
|
||||
@@ -429,7 +484,7 @@ export function main() {
|
||||
var renderProtoViewCompleter = PromiseWrapper.completer();
|
||||
var expectedProtoView = createProtoView();
|
||||
var compiler = createCompiler([renderProtoViewCompleter.promise],
|
||||
[[rootProtoView], [rootProtoView], [expectedProtoView]]);
|
||||
[rootProtoView, rootProtoView, expectedProtoView]);
|
||||
var result = PromiseWrapper.all([
|
||||
compiler.compileInHost(MainComponent),
|
||||
compiler.compileInHost(MainComponent),
|
||||
@@ -445,18 +500,55 @@ export function main() {
|
||||
});
|
||||
}));
|
||||
|
||||
it('should allow recursive components', inject([AsyncTestCompleter], (async) => {
|
||||
it('should throw on unconditional recursive components',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
tplResolver.setView(MainComponent, new viewAnn.View({template: '<div></div>'}));
|
||||
var mainProtoView =
|
||||
createProtoView([createComponentElementBinder(directiveResolver, MainComponent)]);
|
||||
var compiler =
|
||||
createCompiler([createRenderProtoView([createRenderComponentElementBinder(0)])],
|
||||
[[rootProtoView], [mainProtoView]]);
|
||||
[rootProtoView, mainProtoView]);
|
||||
PromiseWrapper.catchError(compiler.compileInHost(MainComponent), (e) => {
|
||||
expect(() => { throw e; })
|
||||
.toThrowError(`Unconditional component cycle in ${stringify(MainComponent)}`);
|
||||
async.done();
|
||||
return null;
|
||||
});
|
||||
}));
|
||||
|
||||
it('should allow recursive components that are connected via an embedded ProtoView',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
tplResolver.setView(MainComponent, new viewAnn.View({template: '<div></div>'}));
|
||||
var viewportProtoView =
|
||||
createProtoView([createComponentElementBinder(directiveResolver, MainComponent)]);
|
||||
var mainProtoView = createProtoView([createViewportElementBinder(viewportProtoView)]);
|
||||
var renderPvDtos = [
|
||||
createRenderProtoView([
|
||||
createRenderViewportElementBinder(createRenderProtoView(
|
||||
[createRenderComponentElementBinder(0)], renderApi.ViewType.EMBEDDED))
|
||||
]),
|
||||
createRenderProtoView()
|
||||
];
|
||||
var compiler = createCompiler(renderPvDtos, [rootProtoView, mainProtoView]);
|
||||
compiler.compileInHost(MainComponent)
|
||||
.then((protoViewRef) => {
|
||||
expect(internalProtoView(protoViewRef).elementBinders[0].nestedProtoView)
|
||||
.toBe(mainProtoView);
|
||||
expect(mainProtoView.elementBinders[0].nestedProtoView).toBe(mainProtoView);
|
||||
expect(mainProtoView.elementBinders[0]
|
||||
.nestedProtoView.elementBinders[0]
|
||||
.nestedProtoView)
|
||||
.toBe(mainProtoView);
|
||||
// In case of a cycle, don't merge the embedded proto views into the component!
|
||||
expect(originalRenderProtoViewRefs(mainProtoView))
|
||||
.toEqual([renderPvDtos[0].render, null]);
|
||||
expect(originalRenderProtoViewRefs(viewportProtoView))
|
||||
.toEqual([
|
||||
renderPvDtos[0]
|
||||
.elementBinders[0]
|
||||
.nestedProtoView.render,
|
||||
renderPvDtos[1].render,
|
||||
null
|
||||
]);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
@@ -466,8 +558,7 @@ export function main() {
|
||||
var rootProtoView =
|
||||
createProtoView([createComponentElementBinder(directiveResolver, MainComponent)]);
|
||||
var mainProtoView = createProtoView();
|
||||
var compiler =
|
||||
createCompiler([createRenderProtoView()], [[rootProtoView], [mainProtoView]]);
|
||||
var compiler = createCompiler([createRenderProtoView()], [rootProtoView, mainProtoView]);
|
||||
compiler.compileInHost(MainComponent)
|
||||
.then((protoViewRef) => {
|
||||
expect(internalProtoView(protoViewRef)).toBe(rootProtoView);
|
||||
@@ -491,7 +582,7 @@ function createDirectiveBinding(directiveResolver, type): DirectiveBinding {
|
||||
}
|
||||
|
||||
function createProtoView(elementBinders = null): AppProtoView {
|
||||
var pv = new AppProtoView(null, null, new Map(), null);
|
||||
var pv = new AppProtoView(null, null, null, new Map(), null);
|
||||
if (isBlank(elementBinders)) {
|
||||
elementBinders = [];
|
||||
}
|
||||
@@ -518,7 +609,8 @@ function createRenderProtoView(elementBinders = null, type: renderApi.ViewType =
|
||||
if (isBlank(elementBinders)) {
|
||||
elementBinders = [];
|
||||
}
|
||||
return new renderApi.ProtoViewDto({elementBinders: elementBinders, type: type});
|
||||
return new renderApi.ProtoViewDto(
|
||||
{elementBinders: elementBinders, type: type, render: new renderApi.RenderProtoViewRef()});
|
||||
}
|
||||
|
||||
function createRenderComponentElementBinder(directiveIndex): renderApi.ElementBinder {
|
||||
@@ -611,14 +703,22 @@ class FakeViewResolver extends ViewResolver {
|
||||
class FakeProtoViewFactory extends ProtoViewFactory {
|
||||
requests: List<List<any>>;
|
||||
|
||||
constructor(public results: List<List<AppProtoView>>) {
|
||||
constructor(public results: List<AppProtoView>) {
|
||||
super(null);
|
||||
this.requests = [];
|
||||
}
|
||||
|
||||
createAppProtoViews(componentBinding: DirectiveBinding, renderProtoView: renderApi.ProtoViewDto,
|
||||
directives: List<DirectiveBinding>): List<AppProtoView> {
|
||||
directives: List<DirectiveBinding>): AppProtoView {
|
||||
this.requests.push([componentBinding, renderProtoView, directives]);
|
||||
return ListWrapper.removeAt(this.results, 0);
|
||||
}
|
||||
}
|
||||
|
||||
class MergedRenderProtoViewRef extends renderApi.RenderProtoViewRef {
|
||||
constructor(public originals: renderApi.RenderProtoViewRef[]) { super(); }
|
||||
}
|
||||
|
||||
function originalRenderProtoViewRefs(appProtoView: AppProtoView) {
|
||||
return (<MergedRenderProtoViewRef>appProtoView.mergeMapping.renderProtoViewRef).originals;
|
||||
}
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
beforeEachBindings,
|
||||
it,
|
||||
xit,
|
||||
viewRootNodes,
|
||||
TestComponentBuilder,
|
||||
RootTestComponent,
|
||||
inspectElement,
|
||||
@@ -240,8 +239,7 @@ export function main() {
|
||||
|
||||
componentRef.dispose();
|
||||
|
||||
expect(rootEl).toHaveText('');
|
||||
expect(rootEl.parentNode).toBe(doc.body);
|
||||
expect(rootEl.parentNode).toBeFalsy();
|
||||
|
||||
async.done();
|
||||
});
|
||||
|
||||
@@ -51,17 +51,10 @@ import {QueryList} from 'angular2/src/core/compiler/query_list';
|
||||
@proxy
|
||||
@IMPLEMENTS(AppView)
|
||||
class DummyView extends SpyObject {
|
||||
componentChildViews;
|
||||
changeDetector;
|
||||
elementRefs;
|
||||
constructor(elementCount = 0) {
|
||||
constructor() {
|
||||
super(AppView);
|
||||
this.componentChildViews = [];
|
||||
this.changeDetector = null;
|
||||
this.elementRefs = ListWrapper.createFixedSize(elementCount);
|
||||
for (var i=0; i<elementCount; i++) {
|
||||
this.elementRefs[i] = new DummyElementRef();
|
||||
}
|
||||
}
|
||||
noSuchMethod(m) { return super.noSuchMethod(m); }
|
||||
}
|
||||
@@ -69,6 +62,7 @@ class DummyView extends SpyObject {
|
||||
@proxy
|
||||
@IMPLEMENTS(ElementRef)
|
||||
class DummyElementRef extends SpyObject {
|
||||
boundElementIndex: number = 0;
|
||||
constructor() { super(ElementRef); }
|
||||
noSuchMethod(m) { return super.noSuchMethod(m); }
|
||||
}
|
||||
@@ -246,14 +240,14 @@ class TestNode extends TreeNode<TestNode> {
|
||||
}
|
||||
|
||||
export function main() {
|
||||
var defaultPreBuiltObjects = new PreBuiltObjects(null, <any>new DummyView(1), null);
|
||||
var defaultPreBuiltObjects = new PreBuiltObjects(null, <any>new DummyView(), <any>new DummyElementRef(), null);
|
||||
|
||||
// An injector with more than 10 bindings will switch to the dynamic strategy
|
||||
var dynamicBindings = [];
|
||||
|
||||
for (var i = 0; i < 20; i++) {
|
||||
dynamicBindings.push(bind(i).toValue(i));
|
||||
}
|
||||
}
|
||||
|
||||
function createPei(parent, index, bindings, distance = 1, hasShadowRoot = false, dirVariableBindings = null) {
|
||||
var directiveBinding = ListWrapper.map(bindings, b => {
|
||||
@@ -754,9 +748,9 @@ export function main() {
|
||||
});
|
||||
|
||||
it("should instantiate directives that depend on pre built objects", () => {
|
||||
var protoView = new AppProtoView(null, null, null, null);
|
||||
var protoView = new AppProtoView(null, null, null, null, null);
|
||||
var bindings = ListWrapper.concat([NeedsProtoViewRef], extraBindings);
|
||||
var inj = injector(bindings, null, false, new PreBuiltObjects(null, null, protoView));
|
||||
var inj = injector(bindings, null, false, new PreBuiltObjects(null, null, null, protoView));
|
||||
|
||||
expect(inj.get(NeedsProtoViewRef).protoViewRef).toEqual(new ProtoViewRef(protoView));
|
||||
});
|
||||
@@ -947,7 +941,7 @@ export function main() {
|
||||
describe("refs", () => {
|
||||
it("should inject ElementRef", () => {
|
||||
var inj = injector(ListWrapper.concat([NeedsElementRef], extraBindings));
|
||||
expect(inj.get(NeedsElementRef).elementRef).toBe(defaultPreBuiltObjects.view.elementRefs[0]);
|
||||
expect(inj.get(NeedsElementRef).elementRef).toBe(defaultPreBuiltObjects.elementRef);
|
||||
});
|
||||
|
||||
it("should inject ChangeDetectorRef of the component's view into the component", () => {
|
||||
@@ -955,10 +949,10 @@ export function main() {
|
||||
var view = <any>new DummyView();
|
||||
var childView = new DummyView();
|
||||
childView.changeDetector = cd;
|
||||
view.componentChildViews = [childView];
|
||||
view.spy('getNestedView').andReturn(childView);
|
||||
var binding = DirectiveBinding.createFromType(ComponentNeedsChangeDetectorRef, new dirAnn.Component());
|
||||
var inj = injector(ListWrapper.concat([binding], extraBindings), null, true,
|
||||
new PreBuiltObjects(null, view, null));
|
||||
new PreBuiltObjects(null, view, <any>new DummyElementRef(), null));
|
||||
|
||||
expect(inj.get(ComponentNeedsChangeDetectorRef).changeDetectorRef).toBe(cd.ref);
|
||||
});
|
||||
@@ -969,7 +963,7 @@ export function main() {
|
||||
view.changeDetector =cd;
|
||||
var binding = DirectiveBinding.createFromType(DirectiveNeedsChangeDetectorRef, new dirAnn.Directive());
|
||||
var inj = injector(ListWrapper.concat([binding], extraBindings), null, false,
|
||||
new PreBuiltObjects(null, view, null));
|
||||
new PreBuiltObjects(null, view, <any>new DummyElementRef(), null));
|
||||
|
||||
expect(inj.get(DirectiveNeedsChangeDetectorRef).changeDetectorRef).toBe(cd.ref);
|
||||
});
|
||||
@@ -980,9 +974,9 @@ export function main() {
|
||||
});
|
||||
|
||||
it("should inject ProtoViewRef", () => {
|
||||
var protoView = new AppProtoView(null, null, null, null);
|
||||
var protoView = new AppProtoView(null, null, null, null, null);
|
||||
var inj = injector(ListWrapper.concat([NeedsProtoViewRef], extraBindings), null, false,
|
||||
new PreBuiltObjects(null, null, protoView));
|
||||
new PreBuiltObjects(null, null, null, protoView));
|
||||
|
||||
expect(inj.get(NeedsProtoViewRef).protoViewRef).toEqual(new ProtoViewRef(protoView));
|
||||
});
|
||||
@@ -1071,7 +1065,7 @@ export function main() {
|
||||
var inj = injector(ListWrapper.concat(dirs, extraBindings), null,
|
||||
false, preBuildObjects, null, dirVariableBindings);
|
||||
|
||||
expect(inj.get(NeedsQueryByVarBindings).query.first).toBe(defaultPreBuiltObjects.view.elementRefs[0]);
|
||||
expect(inj.get(NeedsQueryByVarBindings).query.first).toBe(defaultPreBuiltObjects.elementRef);
|
||||
});
|
||||
|
||||
it('should contain directives on the same injector when querying by variable bindings' +
|
||||
@@ -1198,7 +1192,7 @@ export function main() {
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class ContextWithHandler {
|
||||
|
||||
@@ -15,7 +15,8 @@ import {
|
||||
xit,
|
||||
containsRegexp,
|
||||
stringifyElement,
|
||||
TestComponentBuilder
|
||||
TestComponentBuilder,
|
||||
fakeAsync
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
|
||||
@@ -1369,8 +1370,8 @@ class SimpleImperativeViewComponent {
|
||||
done;
|
||||
|
||||
constructor(self: ElementRef, viewManager: AppViewManager, renderer: DomRenderer) {
|
||||
var shadowViewRef = viewManager.getComponentView(self);
|
||||
renderer.setComponentViewRootNodes(shadowViewRef.render, [el('hello imp view')]);
|
||||
var hostElement = renderer.getNativeElementSync(self);
|
||||
DOM.appendChild(hostElement, el('hello imp view'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1870,7 +1871,7 @@ class SomeImperativeViewport {
|
||||
}
|
||||
if (value) {
|
||||
this.view = this.vc.create(this.protoView);
|
||||
var nodes = this.renderer.getRootNodes(this.view.render);
|
||||
var nodes = this.renderer.getRootNodes(this.view.renderFragment);
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
DOM.appendChild(this.anchor, nodes[i]);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,492 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
xdescribe,
|
||||
describe,
|
||||
el,
|
||||
dispatchEvent,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
IS_DARTIUM,
|
||||
beforeEachBindings,
|
||||
it,
|
||||
xit,
|
||||
containsRegexp,
|
||||
stringifyElement,
|
||||
TestComponentBuilder,
|
||||
RootTestComponent,
|
||||
fakeAsync,
|
||||
tick,
|
||||
By
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
import * as viewAnn from 'angular2/src/core/annotations_impl/view';
|
||||
|
||||
import {
|
||||
Component,
|
||||
Directive,
|
||||
View,
|
||||
forwardRef,
|
||||
ViewContainerRef,
|
||||
ProtoViewRef,
|
||||
ElementRef,
|
||||
bind
|
||||
} from 'angular2/angular2';
|
||||
import {ShadowDomStrategy, NativeShadowDomStrategy} from 'angular2/render';
|
||||
|
||||
export function main() {
|
||||
describe('projection', () => {
|
||||
it('should support simple components',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MainComp, new viewAnn.View({
|
||||
template: '<simple>' +
|
||||
'<div>A</div>' +
|
||||
'</simple>',
|
||||
directives: [Simple]
|
||||
}))
|
||||
.createAsync(MainComp)
|
||||
.then((main) => {
|
||||
expect(main.nativeElement).toHaveText('SIMPLE(A)');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should support simple components with text interpolation as direct children',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MainComp, new viewAnn.View({
|
||||
template: '{{\'START(\'}}<simple>' +
|
||||
'{{text}}' +
|
||||
'</simple>{{\')END\'}}',
|
||||
directives: [Simple]
|
||||
}))
|
||||
.createAsync(MainComp)
|
||||
.then((main) => {
|
||||
|
||||
main.componentInstance.text = 'A';
|
||||
main.detectChanges();
|
||||
expect(main.nativeElement).toHaveText('START(SIMPLE(A))END');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should not show the light dom even if there is no content tag',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MainComp,
|
||||
new viewAnn.View({template: '<empty>A</empty>', directives: [Empty]}))
|
||||
.createAsync(MainComp)
|
||||
.then((main) => {
|
||||
|
||||
expect(main.nativeElement).toHaveText('');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should support multiple content tags',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MainComp, new viewAnn.View({
|
||||
template: '<multiple-content-tags>' +
|
||||
'<div>B</div>' +
|
||||
'<div>C</div>' +
|
||||
'<div class="left">A</div>' +
|
||||
'</multiple-content-tags>',
|
||||
directives: [MultipleContentTagsComponent]
|
||||
}))
|
||||
.createAsync(MainComp)
|
||||
.then((main) => {
|
||||
|
||||
expect(main.nativeElement).toHaveText('(A, BC)');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should redistribute only direct children',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MainComp, new viewAnn.View({
|
||||
template: '<multiple-content-tags>' +
|
||||
'<div>B<div class="left">A</div></div>' +
|
||||
'<div>C</div>' +
|
||||
'</multiple-content-tags>',
|
||||
directives: [MultipleContentTagsComponent]
|
||||
}))
|
||||
.createAsync(MainComp)
|
||||
.then((main) => {
|
||||
|
||||
expect(main.nativeElement).toHaveText('(, BAC)');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it("should redistribute direct child viewcontainers when the light dom changes",
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MainComp, new viewAnn.View({
|
||||
template: '<multiple-content-tags>' +
|
||||
'<template manual class="left"><div>A1</div></template>' +
|
||||
'<div>B</div>' +
|
||||
'</multiple-content-tags>',
|
||||
directives: [MultipleContentTagsComponent, ManualViewportDirective]
|
||||
}))
|
||||
.createAsync(MainComp)
|
||||
.then((main) => {
|
||||
|
||||
var viewportDirectives = main.queryAll(By.directive(ManualViewportDirective))
|
||||
.map(de => de.inject(ManualViewportDirective));
|
||||
|
||||
expect(main.nativeElement).toHaveText('(, B)');
|
||||
viewportDirectives.forEach(d => d.show());
|
||||
expect(main.nativeElement).toHaveText('(A1, B)');
|
||||
|
||||
viewportDirectives.forEach(d => d.hide());
|
||||
|
||||
expect(main.nativeElement).toHaveText('(, B)');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it("should support nested components",
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MainComp, new viewAnn.View({
|
||||
template: '<outer-with-indirect-nested>' +
|
||||
'<div>A</div>' +
|
||||
'<div>B</div>' +
|
||||
'</outer-with-indirect-nested>',
|
||||
directives: [OuterWithIndirectNestedComponent]
|
||||
}))
|
||||
.createAsync(MainComp)
|
||||
.then((main) => {
|
||||
|
||||
expect(main.nativeElement).toHaveText('OUTER(SIMPLE(AB))');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it("should support nesting with content being direct child of a nested component",
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MainComp, new viewAnn.View({
|
||||
template: '<outer>' +
|
||||
'<template manual class="left"><div>A</div></template>' +
|
||||
'<div>B</div>' +
|
||||
'<div>C</div>' +
|
||||
'</outer>',
|
||||
directives: [OuterComponent, ManualViewportDirective],
|
||||
}))
|
||||
.createAsync(MainComp)
|
||||
.then((main) => {
|
||||
|
||||
var viewportDirective = main.query(By.directive(ManualViewportDirective))
|
||||
.inject(ManualViewportDirective);
|
||||
|
||||
expect(main.nativeElement).toHaveText('OUTER(INNER(INNERINNER(,BC)))');
|
||||
viewportDirective.show();
|
||||
|
||||
expect(main.nativeElement).toHaveText('OUTER(INNER(INNERINNER(A,BC)))');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should redistribute when the shadow dom changes',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MainComp, new viewAnn.View({
|
||||
template: '<conditional-content>' +
|
||||
'<div class="left">A</div>' +
|
||||
'<div>B</div>' +
|
||||
'<div>C</div>' +
|
||||
'</conditional-content>',
|
||||
directives: [ConditionalContentComponent]
|
||||
}))
|
||||
.createAsync(MainComp)
|
||||
.then((main) => {
|
||||
|
||||
var viewportDirective = main.query(By.directive(ManualViewportDirective))
|
||||
.inject(ManualViewportDirective);
|
||||
|
||||
expect(main.nativeElement).toHaveText('(, BC)');
|
||||
|
||||
viewportDirective.show();
|
||||
expect(main.nativeElement).toHaveText('(A, BC)');
|
||||
|
||||
viewportDirective.hide();
|
||||
|
||||
expect(main.nativeElement).toHaveText('(, BC)');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
// GH-2095 - https://github.com/angular/angular/issues/2095
|
||||
// important as we are removing the ng-content element during compilation,
|
||||
// which could skrew up text node indices.
|
||||
it('should support text nodes after content tags',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb, async) => {
|
||||
|
||||
tcb.overrideView(
|
||||
MainComp,
|
||||
new viewAnn.View(
|
||||
{template: '<simple string-prop="text"></simple>', directives: [Simple]}))
|
||||
.overrideTemplate(Simple, '<ng-content></ng-content><p>P,</p>{{stringProp}}')
|
||||
.createAsync(MainComp)
|
||||
.then((main) => {
|
||||
|
||||
main.detectChanges();
|
||||
|
||||
expect(main.nativeElement).toHaveText('P,text');
|
||||
async.done();
|
||||
});
|
||||
|
||||
}));
|
||||
|
||||
// important as we are moving style tags around during compilation,
|
||||
// which could skrew up text node indices.
|
||||
it('should support text nodes after style tags',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb, async) => {
|
||||
|
||||
tcb.overrideView(
|
||||
MainComp,
|
||||
new viewAnn.View(
|
||||
{template: '<simple string-prop="text"></simple>', directives: [Simple]}))
|
||||
.overrideTemplate(Simple, '<style></style><p>P,</p>{{stringProp}}')
|
||||
.createAsync(MainComp)
|
||||
.then((main) => {
|
||||
|
||||
main.detectChanges();
|
||||
expect(main.nativeElement).toHaveText('P,text');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should support moving non projected light dom around',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MainComp, new viewAnn.View({
|
||||
template: '<empty>' +
|
||||
' <template manual><div>A</div></template>' +
|
||||
'</empty>' +
|
||||
'START(<div project></div>)END',
|
||||
directives: [Empty, ProjectDirective, ManualViewportDirective],
|
||||
}))
|
||||
.createAsync(MainComp)
|
||||
.then((main) => {
|
||||
|
||||
var sourceDirective: ManualViewportDirective =
|
||||
main.query(By.directive(ManualViewportDirective))
|
||||
.inject(ManualViewportDirective);
|
||||
var projectDirective: ProjectDirective =
|
||||
main.query(By.directive(ProjectDirective)).inject(ProjectDirective);
|
||||
expect(main.nativeElement).toHaveText('START()END');
|
||||
|
||||
projectDirective.show(sourceDirective.protoViewRef, sourceDirective.elementRef);
|
||||
expect(main.nativeElement).toHaveText('START(A)END');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should support moving projected light dom around',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MainComp, new viewAnn.View({
|
||||
template: '<simple><template manual><div>A</div></template></simple>' +
|
||||
'START(<div project></div>)END',
|
||||
directives: [Simple, ProjectDirective, ManualViewportDirective],
|
||||
}))
|
||||
.createAsync(MainComp)
|
||||
.then((main) => {
|
||||
|
||||
var sourceDirective: ManualViewportDirective =
|
||||
main.query(By.directive(ManualViewportDirective))
|
||||
.inject(ManualViewportDirective);
|
||||
var projectDirective: ProjectDirective =
|
||||
main.query(By.directive(ProjectDirective)).inject(ProjectDirective);
|
||||
expect(main.nativeElement).toHaveText('SIMPLE()START()END');
|
||||
|
||||
projectDirective.show(sourceDirective.protoViewRef, sourceDirective.elementRef);
|
||||
expect(main.nativeElement).toHaveText('SIMPLE()START(A)END');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should support moving ng-content around',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MainComp, new viewAnn.View({
|
||||
template: '<conditional-content>' +
|
||||
'<div class="left">A</div>' +
|
||||
'<div>B</div>' +
|
||||
'</conditional-content>' +
|
||||
'START(<div project></div>)END',
|
||||
directives:
|
||||
[ConditionalContentComponent, ProjectDirective, ManualViewportDirective]
|
||||
}))
|
||||
.createAsync(MainComp)
|
||||
.then((main) => {
|
||||
|
||||
var sourceDirective: ManualViewportDirective =
|
||||
main.query(By.directive(ManualViewportDirective))
|
||||
.inject(ManualViewportDirective);
|
||||
var projectDirective: ProjectDirective =
|
||||
main.query(By.directive(ProjectDirective)).inject(ProjectDirective);
|
||||
expect(main.nativeElement).toHaveText('(, B)START()END');
|
||||
|
||||
projectDirective.show(sourceDirective.protoViewRef, sourceDirective.elementRef);
|
||||
expect(main.nativeElement).toHaveText('(, B)START(A)END');
|
||||
|
||||
// Stamping ng-content multiple times should not produce the content multiple
|
||||
// times...
|
||||
projectDirective.show(sourceDirective.protoViewRef, sourceDirective.elementRef);
|
||||
expect(main.nativeElement).toHaveText('(, B)START(A)END');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
// Note: This does not use a ng-content element, but
|
||||
// is still important as we are merging proto views independent of
|
||||
// the presence of ng-content elements!
|
||||
it('should still allow to implement a recursive trees',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MainComp,
|
||||
new viewAnn.View({template: '<tree></tree>', directives: [Tree]}))
|
||||
.createAsync(MainComp)
|
||||
.then((main) => {
|
||||
|
||||
main.detectChanges();
|
||||
var manualDirective: ManualViewportDirective =
|
||||
main.query(By.directive(ManualViewportDirective))
|
||||
.inject(ManualViewportDirective);
|
||||
expect(main.nativeElement).toHaveText('TREE(0:)');
|
||||
manualDirective.show();
|
||||
main.detectChanges();
|
||||
expect(main.nativeElement).toHaveText('TREE(0:TREE(1:))');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
if (DOM.supportsNativeShadowDOM()) {
|
||||
describe('native shadow dom support', () => {
|
||||
beforeEachBindings(
|
||||
() => { return [bind(ShadowDomStrategy).toValue(new NativeShadowDomStrategy())]; });
|
||||
|
||||
it('should support native content projection',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MainComp, new viewAnn.View({
|
||||
template: '<simple-native>' +
|
||||
'<div>A</div>' +
|
||||
'</simple-native>',
|
||||
directives: [SimpleNative]
|
||||
}))
|
||||
.createAsync(MainComp)
|
||||
.then((main) => {
|
||||
|
||||
expect(main.nativeElement).toHaveText('SIMPLE(A)');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Component({selector: 'main'})
|
||||
@View({template: '', directives: []})
|
||||
class MainComp {
|
||||
text: string = '';
|
||||
}
|
||||
|
||||
@Component({selector: 'simple', properties: ['stringProp']})
|
||||
@View({template: 'SIMPLE(<ng-content></ng-content>)', directives: []})
|
||||
class Simple {
|
||||
stringProp: string = '';
|
||||
}
|
||||
|
||||
@Component({selector: 'simple-native'})
|
||||
@View({template: 'SIMPLE(<content></content>)', directives: []})
|
||||
class SimpleNative {
|
||||
}
|
||||
|
||||
@Component({selector: 'empty'})
|
||||
@View({template: '', directives: []})
|
||||
class Empty {
|
||||
}
|
||||
|
||||
@Component({selector: 'multiple-content-tags'})
|
||||
@View({
|
||||
template: '(<ng-content select=".left"></ng-content>, <ng-content></ng-content>)',
|
||||
directives: []
|
||||
})
|
||||
class MultipleContentTagsComponent {
|
||||
}
|
||||
|
||||
@Directive({selector: '[manual]'})
|
||||
class ManualViewportDirective {
|
||||
constructor(public vc: ViewContainerRef, public protoViewRef: ProtoViewRef,
|
||||
public elementRef: ElementRef) {}
|
||||
show() { this.vc.create(this.protoViewRef, 0); }
|
||||
hide() { this.vc.clear(); }
|
||||
}
|
||||
|
||||
@Directive({selector: '[project]'})
|
||||
class ProjectDirective {
|
||||
constructor(public vc: ViewContainerRef) {}
|
||||
show(protoViewRef: ProtoViewRef, context: ElementRef) {
|
||||
this.vc.create(protoViewRef, 0, context);
|
||||
}
|
||||
hide() { this.vc.clear(); }
|
||||
}
|
||||
|
||||
@Component({selector: 'outer-with-indirect-nested'})
|
||||
@View({
|
||||
template: 'OUTER(<simple><div><ng-content></ng-content></div></simple>)',
|
||||
directives: [Simple]
|
||||
})
|
||||
class OuterWithIndirectNestedComponent {
|
||||
}
|
||||
|
||||
@Component({selector: 'outer'})
|
||||
@View({
|
||||
template: 'OUTER(<inner><ng-content></ng-content></inner>)',
|
||||
directives: [forwardRef(() => InnerComponent)]
|
||||
})
|
||||
class OuterComponent {
|
||||
}
|
||||
|
||||
@Component({selector: 'inner'})
|
||||
@View({
|
||||
template: 'INNER(<innerinner><ng-content></ng-content></innerinner>)',
|
||||
directives: [forwardRef(() => InnerInnerComponent)]
|
||||
})
|
||||
class InnerComponent {
|
||||
}
|
||||
|
||||
@Component({selector: 'innerinner'})
|
||||
@View({
|
||||
template: 'INNERINNER(<ng-content select=".left"></ng-content>,<ng-content></ng-content>)',
|
||||
directives: []
|
||||
})
|
||||
class InnerInnerComponent {
|
||||
}
|
||||
|
||||
@Component({selector: 'conditional-content'})
|
||||
@View({
|
||||
template:
|
||||
'<div>(<div *manual><ng-content select=".left"></ng-content></div>, <ng-content></ng-content>)</div>',
|
||||
directives: [ManualViewportDirective]
|
||||
})
|
||||
class ConditionalContentComponent {
|
||||
}
|
||||
|
||||
@Component({selector: 'tab'})
|
||||
@View({
|
||||
template: '<div><div *manual>TAB(<ng-content></ng-content>)</div></div>',
|
||||
directives: [ManualViewportDirective]
|
||||
})
|
||||
class Tab {
|
||||
}
|
||||
|
||||
@Component({selector: 'tree', properties: ['depth']})
|
||||
@View({
|
||||
template: 'TREE({{depth}}:<tree *manual [depth]="depth+1"></tree>)',
|
||||
directives: [ManualViewportDirective, Tree]
|
||||
})
|
||||
class Tree {
|
||||
depth = 0;
|
||||
}
|
||||
@@ -35,7 +35,7 @@ export function main() {
|
||||
|
||||
describe('ProtoViewFactory', () => {
|
||||
var changeDetection;
|
||||
var protoViewFactory;
|
||||
var protoViewFactory: ProtoViewFactory;
|
||||
var directiveResolver;
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -63,10 +63,13 @@ export function main() {
|
||||
describe('createAppProtoViews', () => {
|
||||
|
||||
it('should create an AppProtoView for the root render proto view', () => {
|
||||
var renderPv = createRenderProtoView();
|
||||
var pvs = protoViewFactory.createAppProtoViews(bindDirective(MainComponent), renderPv, []);
|
||||
expect(pvs.length).toBe(1);
|
||||
expect(pvs[0].render).toBe(renderPv.render);
|
||||
var varBindings = new Map();
|
||||
varBindings.set('a', 'b');
|
||||
var renderPv = createRenderProtoView([], null, varBindings);
|
||||
var appPv =
|
||||
protoViewFactory.createAppProtoViews(bindDirective(MainComponent), renderPv, []);
|
||||
expect(appPv.variableBindings.get('a')).toEqual('b');
|
||||
expect(appPv).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -159,15 +162,23 @@ function directiveBinding({metadata}: {metadata?: any} = {}) {
|
||||
return new DirectiveBinding(Key.get("dummy"), null, [], [], [], metadata);
|
||||
}
|
||||
|
||||
function createRenderProtoView(elementBinders = null, type: renderApi.ViewType = null) {
|
||||
function createRenderProtoView(elementBinders = null, type: renderApi.ViewType = null,
|
||||
variableBindings = null) {
|
||||
if (isBlank(type)) {
|
||||
type = renderApi.ViewType.COMPONENT;
|
||||
}
|
||||
if (isBlank(elementBinders)) {
|
||||
elementBinders = [];
|
||||
}
|
||||
return new renderApi.ProtoViewDto(
|
||||
{elementBinders: elementBinders, type: type, variableBindings: new Map()});
|
||||
if (isBlank(variableBindings)) {
|
||||
variableBindings = new Map();
|
||||
}
|
||||
return new renderApi.ProtoViewDto({
|
||||
elementBinders: elementBinders,
|
||||
type: type,
|
||||
variableBindings: variableBindings,
|
||||
textBindings: []
|
||||
});
|
||||
}
|
||||
|
||||
function createRenderComponentElementBinder(directiveIndex) {
|
||||
|
||||
@@ -398,7 +398,7 @@ class NeedsQueryDesc {
|
||||
}
|
||||
|
||||
@Component({selector: 'needs-query-by-var-binding'})
|
||||
@View({directives: [], template: '<content>'})
|
||||
@View({directives: [], template: '<ng-content>'})
|
||||
@Injectable()
|
||||
class NeedsQueryByLabel {
|
||||
query: QueryList<any>;
|
||||
@@ -408,7 +408,7 @@ class NeedsQueryByLabel {
|
||||
}
|
||||
|
||||
@Component({selector: 'needs-query-by-var-bindings'})
|
||||
@View({directives: [], template: '<content>'})
|
||||
@View({directives: [], template: '<ng-content>'})
|
||||
@Injectable()
|
||||
class NeedsQueryByTwoLabels {
|
||||
query: QueryList<any>;
|
||||
|
||||
@@ -18,10 +18,11 @@ import {
|
||||
|
||||
import {IMPLEMENTS} from 'angular2/src/facade/lang';
|
||||
|
||||
import {AppView, AppProtoView, AppViewContainer} from 'angular2/src/core/compiler/view';
|
||||
import {AppView, AppViewContainer} from 'angular2/src/core/compiler/view';
|
||||
import {ViewContainerRef} from 'angular2/src/core/compiler/view_container_ref';
|
||||
import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
|
||||
import {ElementBinder} from 'angular2/src/core/compiler/element_binder';
|
||||
import {ElementRef} from 'angular2/src/core/compiler/element_ref';
|
||||
import {ViewRef} from 'angular2/src/core/compiler/view_ref';
|
||||
|
||||
export function main() {
|
||||
// TODO(tbosch): add missing tests
|
||||
@@ -31,34 +32,26 @@ export function main() {
|
||||
var view;
|
||||
var viewManager;
|
||||
|
||||
function createProtoView() {
|
||||
var pv = new AppProtoView(null, null, null, null);
|
||||
pv.elementBinders = [new ElementBinder(0, null, 0, null, null)];
|
||||
return pv;
|
||||
}
|
||||
|
||||
function createView() { return new AppView(null, createProtoView(), new Map()); }
|
||||
|
||||
function createViewContainer() { return new ViewContainerRef(viewManager, location); }
|
||||
|
||||
beforeEach(() => {
|
||||
viewManager = new AppViewManagerSpy();
|
||||
view = createView();
|
||||
view.viewContainers = [null];
|
||||
location = view.elementRefs[0];
|
||||
view = new AppViewSpy();
|
||||
location = new ElementRef(new ViewRef(view), 0, 0, null);
|
||||
});
|
||||
|
||||
describe('length', () => {
|
||||
|
||||
it('should return a 0 length if there is no underlying ViewContainerRef', () => {
|
||||
it('should return a 0 length if there is no underlying AppViewContainer', () => {
|
||||
var vc = createViewContainer();
|
||||
expect(vc.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should return the size of the underlying ViewContainerRef', () => {
|
||||
it('should return the size of the underlying AppViewContainer', () => {
|
||||
var vc = createViewContainer();
|
||||
view.viewContainers = [new AppViewContainer()];
|
||||
view.viewContainers[0].views = [createView()];
|
||||
var appVc = new AppViewContainer();
|
||||
view.viewContainers = [appVc];
|
||||
appVc.views = [<any>new AppViewSpy()];
|
||||
expect(vc.length).toBe(1);
|
||||
});
|
||||
|
||||
@@ -69,6 +62,14 @@ export function main() {
|
||||
});
|
||||
}
|
||||
|
||||
@proxy
|
||||
@IMPLEMENTS(AppView)
|
||||
class AppViewSpy extends SpyObject {
|
||||
viewContainers: AppViewContainer[] = [null];
|
||||
constructor() { super(AppView); }
|
||||
noSuchMethod(m) { return super.noSuchMethod(m) }
|
||||
}
|
||||
|
||||
@proxy
|
||||
@IMPLEMENTS(AppViewManager)
|
||||
class AppViewManagerSpy extends SpyObject {
|
||||
|
||||
@@ -16,240 +16,155 @@ import {
|
||||
proxy
|
||||
} from 'angular2/test_lib';
|
||||
import {Injector, bind} from 'angular2/di';
|
||||
import {IMPLEMENTS, isBlank, isPresent} from 'angular2/src/facade/lang';
|
||||
import {MapWrapper, ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {IMPLEMENTS} from 'angular2/src/facade/lang';
|
||||
|
||||
import {AppProtoView, AppView, AppViewContainer} from 'angular2/src/core/compiler/view';
|
||||
import {
|
||||
AppProtoView,
|
||||
AppView,
|
||||
AppViewContainer,
|
||||
AppProtoViewMergeMapping
|
||||
} from 'angular2/src/core/compiler/view';
|
||||
import {ProtoViewRef, ViewRef, internalView} from 'angular2/src/core/compiler/view_ref';
|
||||
import {Renderer, RenderViewRef, RenderProtoViewRef} from 'angular2/src/render/api';
|
||||
import {ElementBinder} from 'angular2/src/core/compiler/element_binder';
|
||||
import {DirectiveBinding, ElementInjector} from 'angular2/src/core/compiler/element_injector';
|
||||
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
||||
import {Component} from 'angular2/annotations';
|
||||
import {ElementRef} from 'angular2/src/core/compiler/element_ref';
|
||||
import {
|
||||
Renderer,
|
||||
RenderViewRef,
|
||||
RenderProtoViewRef,
|
||||
RenderFragmentRef,
|
||||
ViewType,
|
||||
RenderProtoViewMergeMapping,
|
||||
RenderViewWithFragments
|
||||
} from 'angular2/src/render/api';
|
||||
import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
|
||||
import {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils';
|
||||
import {AppViewListener} from 'angular2/src/core/compiler/view_listener';
|
||||
import {AppViewPool} from 'angular2/src/core/compiler/view_pool';
|
||||
|
||||
import {
|
||||
createHostPv,
|
||||
createComponentPv,
|
||||
createEmbeddedPv,
|
||||
createEmptyElBinder,
|
||||
createNestedElBinder,
|
||||
createProtoElInjector
|
||||
} from './view_manager_utils_spec';
|
||||
|
||||
export function main() {
|
||||
// TODO(tbosch): add missing tests
|
||||
|
||||
describe('AppViewManager', () => {
|
||||
var renderer;
|
||||
var utils;
|
||||
var utils: AppViewManagerUtils;
|
||||
var viewListener;
|
||||
var viewPool;
|
||||
var manager;
|
||||
var directiveResolver;
|
||||
var createdViews: any[];
|
||||
var createdRenderViews: any[];
|
||||
var manager: AppViewManager;
|
||||
var createdRenderViews: RenderViewWithFragments[];
|
||||
|
||||
function wrapPv(protoView: AppProtoView): ProtoViewRef { return new ProtoViewRef(protoView); }
|
||||
|
||||
function wrapView(view: AppView): ViewRef { return new ViewRef(view); }
|
||||
|
||||
function elementRef(parentView, boundElementIndex) {
|
||||
return parentView.elementRefs[boundElementIndex];
|
||||
}
|
||||
|
||||
function createDirectiveBinding(type) {
|
||||
var annotation = directiveResolver.resolve(type);
|
||||
return DirectiveBinding.createFromType(type, annotation);
|
||||
}
|
||||
|
||||
function createEmptyElBinder() { return new ElementBinder(0, null, 0, null, null); }
|
||||
|
||||
function createComponentElBinder(nestedProtoView = null) {
|
||||
var binding = createDirectiveBinding(SomeComponent);
|
||||
var binder = new ElementBinder(0, null, 0, null, binding);
|
||||
binder.nestedProtoView = nestedProtoView;
|
||||
return binder;
|
||||
}
|
||||
|
||||
function createProtoView(binders = null) {
|
||||
if (isBlank(binders)) {
|
||||
binders = [];
|
||||
}
|
||||
var staticChildComponentCount = 0;
|
||||
for (var i = 0; i < binders.length; i++) {
|
||||
if (binders[i].hasStaticComponent()) {
|
||||
staticChildComponentCount++;
|
||||
}
|
||||
}
|
||||
var res = new AppProtoView(new MockProtoViewRef(staticChildComponentCount), null, null, null);
|
||||
res.elementBinders = binders;
|
||||
return res;
|
||||
}
|
||||
|
||||
function createElementInjector() {
|
||||
return SpyObject.stub(new SpyElementInjector(),
|
||||
{
|
||||
'isExportingComponent': false,
|
||||
'isExportingElement': false,
|
||||
'getEventEmitterAccessors': [],
|
||||
'getComponent': null
|
||||
},
|
||||
{});
|
||||
}
|
||||
|
||||
function createView(pv = null, renderViewRef = null) {
|
||||
if (isBlank(pv)) {
|
||||
pv = createProtoView();
|
||||
}
|
||||
if (isBlank(renderViewRef)) {
|
||||
renderViewRef = new RenderViewRef();
|
||||
}
|
||||
var view = new AppView(renderer, pv, new Map());
|
||||
view.render = renderViewRef;
|
||||
var elementInjectors = ListWrapper.createFixedSize(pv.elementBinders.length);
|
||||
for (var i = 0; i < pv.elementBinders.length; i++) {
|
||||
elementInjectors[i] = createElementInjector();
|
||||
}
|
||||
view.init(null, elementInjectors, [], ListWrapper.createFixedSize(pv.elementBinders.length),
|
||||
ListWrapper.createFixedSize(pv.elementBinders.length));
|
||||
return view;
|
||||
function resetSpies() {
|
||||
viewListener.spy('viewCreated').reset();
|
||||
viewListener.spy('viewDestroyed').reset();
|
||||
renderer.spy('createView').reset();
|
||||
renderer.spy('destroyView').reset();
|
||||
renderer.spy('createRootHostView').reset();
|
||||
renderer.spy('setEventDispatcher').reset();
|
||||
renderer.spy('hydrateView').reset();
|
||||
renderer.spy('dehydrateView').reset();
|
||||
viewPool.spy('returnView').reset();
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
directiveResolver = new DirectiveResolver();
|
||||
renderer = new SpyRenderer();
|
||||
utils = new SpyAppViewManagerUtils();
|
||||
utils = new AppViewManagerUtils();
|
||||
viewListener = new SpyAppViewListener();
|
||||
viewPool = new SpyAppViewPool();
|
||||
manager = new AppViewManager(viewPool, viewListener, utils, renderer);
|
||||
createdViews = [];
|
||||
createdRenderViews = [];
|
||||
|
||||
utils.spy('createView')
|
||||
.andCallFake((proto, renderViewRef, _a, _b) => {
|
||||
var view = createView(proto, renderViewRef);
|
||||
createdViews.push(view);
|
||||
return view;
|
||||
});
|
||||
utils.spy('attachComponentView')
|
||||
.andCallFake((hostView, elementIndex, childView) => {
|
||||
hostView.componentChildViews[elementIndex] = childView;
|
||||
});
|
||||
utils.spy('attachViewInContainer')
|
||||
.andCallFake((parentView, elementIndex, _a, _b, atIndex, childView) => {
|
||||
var viewContainer = parentView.viewContainers[elementIndex];
|
||||
if (isBlank(viewContainer)) {
|
||||
viewContainer = new AppViewContainer();
|
||||
parentView.viewContainers[elementIndex] = viewContainer;
|
||||
}
|
||||
ListWrapper.insert(viewContainer.views, atIndex, childView);
|
||||
});
|
||||
renderer.spy('createRootHostView')
|
||||
.andCallFake((_b, _c) => {
|
||||
var rv = new RenderViewRef();
|
||||
.andCallFake((_a, renderFragmentCount, _b) => {
|
||||
var fragments = [];
|
||||
for (var i = 0; i < renderFragmentCount; i++) {
|
||||
fragments.push(new RenderFragmentRef());
|
||||
}
|
||||
var rv = new RenderViewWithFragments(new RenderViewRef(), fragments);
|
||||
createdRenderViews.push(rv);
|
||||
return rv;
|
||||
});
|
||||
renderer.spy('createView')
|
||||
.andCallFake((_a) => {
|
||||
var rv = new RenderViewRef();
|
||||
.andCallFake((_a, renderFragmentCount) => {
|
||||
var fragments = [];
|
||||
for (var i = 0; i < renderFragmentCount; i++) {
|
||||
fragments.push(new RenderFragmentRef());
|
||||
}
|
||||
var rv = new RenderViewWithFragments(new RenderViewRef(), fragments);
|
||||
createdRenderViews.push(rv);
|
||||
return rv;
|
||||
});
|
||||
viewPool.spy('returnView').andReturn(true);
|
||||
});
|
||||
|
||||
describe('static child components', () => {
|
||||
|
||||
describe('recursively create when not cached', () => {
|
||||
var rootProtoView, hostProtoView, componentProtoView, hostView, componentView;
|
||||
beforeEach(() => {
|
||||
componentProtoView = createProtoView();
|
||||
hostProtoView = createProtoView([createComponentElBinder(componentProtoView)]);
|
||||
rootProtoView = createProtoView([createComponentElBinder(hostProtoView)]);
|
||||
manager.createRootHostView(wrapPv(rootProtoView), null, null);
|
||||
hostView = createdViews[1];
|
||||
componentView = createdViews[2];
|
||||
});
|
||||
|
||||
it('should create the view', () => {
|
||||
expect(hostView.proto).toBe(hostProtoView);
|
||||
expect(componentView.proto).toBe(componentProtoView);
|
||||
});
|
||||
|
||||
it('should hydrate the view', () => {
|
||||
expect(utils.spy('hydrateComponentView')).toHaveBeenCalledWith(hostView, 0);
|
||||
expect(renderer.spy('hydrateView')).toHaveBeenCalledWith(componentView.render);
|
||||
});
|
||||
|
||||
it('should set the render view',
|
||||
() => {expect(componentView.render).toBe(createdRenderViews[2])});
|
||||
|
||||
it('should set the event dispatcher', () => {
|
||||
expect(renderer.spy('setEventDispatcher'))
|
||||
.toHaveBeenCalledWith(componentView.render, componentView);
|
||||
});
|
||||
});
|
||||
|
||||
describe('recursively hydrate when getting from from the cache',
|
||||
() => {
|
||||
// TODO(tbosch): implement this
|
||||
});
|
||||
|
||||
describe('recursively dehydrate', () => {
|
||||
// TODO(tbosch): implement this
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('createRootHostView', () => {
|
||||
|
||||
var hostProtoView;
|
||||
beforeEach(() => { hostProtoView = createProtoView([createComponentElBinder(null)]); });
|
||||
var hostProtoView: AppProtoView;
|
||||
beforeEach(
|
||||
() => { hostProtoView = createHostPv([createNestedElBinder(createComponentPv())]); });
|
||||
|
||||
it('should create the view', () => {
|
||||
expect(internalView(manager.createRootHostView(wrapPv(hostProtoView), null, null)))
|
||||
.toBe(createdViews[0]);
|
||||
expect(createdViews[0].proto).toBe(hostProtoView);
|
||||
expect(viewListener.spy('viewCreated')).toHaveBeenCalledWith(createdViews[0]);
|
||||
var rootView = internalView(manager.createRootHostView(wrapPv(hostProtoView), null, null));
|
||||
expect(rootView.proto).toBe(hostProtoView);
|
||||
expect(viewListener.spy('viewCreated')).toHaveBeenCalledWith(rootView);
|
||||
});
|
||||
|
||||
it('should hydrate the view', () => {
|
||||
var injector = Injector.resolveAndCreate([]);
|
||||
manager.createRootHostView(wrapPv(hostProtoView), null, injector);
|
||||
expect(utils.spy('hydrateRootHostView')).toHaveBeenCalledWith(createdViews[0], injector);
|
||||
expect(renderer.spy('hydrateView')).toHaveBeenCalledWith(createdViews[0].render);
|
||||
var rootView =
|
||||
internalView(manager.createRootHostView(wrapPv(hostProtoView), null, injector));
|
||||
expect(rootView.hydrated()).toBe(true);
|
||||
expect(renderer.spy('hydrateView')).toHaveBeenCalledWith(rootView.render);
|
||||
});
|
||||
|
||||
it('should create and set the render view using the component selector', () => {
|
||||
manager.createRootHostView(wrapPv(hostProtoView), null, null)
|
||||
expect(renderer.spy('createRootHostView'))
|
||||
.toHaveBeenCalledWith(hostProtoView.render, 'someComponent');
|
||||
expect(createdViews[0].render).toBe(createdRenderViews[0]);
|
||||
var rootView = internalView(manager.createRootHostView(wrapPv(hostProtoView), null, null));
|
||||
expect(renderer.spy('createRootHostView'))
|
||||
.toHaveBeenCalledWith(hostProtoView.mergeMapping.renderProtoViewRef,
|
||||
hostProtoView.mergeMapping.renderFragmentCount, 'someComponent');
|
||||
expect(rootView.render).toBe(createdRenderViews[0].viewRef);
|
||||
expect(rootView.renderFragment).toBe(createdRenderViews[0].fragmentRefs[0]);
|
||||
});
|
||||
|
||||
it('should allow to override the selector', () => {
|
||||
var selector = 'someOtherSelector';
|
||||
manager.createRootHostView(wrapPv(hostProtoView), selector, null)
|
||||
expect(renderer.spy('createRootHostView'))
|
||||
.toHaveBeenCalledWith(hostProtoView.render, selector);
|
||||
internalView(manager.createRootHostView(wrapPv(hostProtoView), selector, null));
|
||||
expect(renderer.spy('createRootHostView'))
|
||||
.toHaveBeenCalledWith(hostProtoView.mergeMapping.renderProtoViewRef,
|
||||
hostProtoView.mergeMapping.renderFragmentCount, selector);
|
||||
});
|
||||
|
||||
it('should set the event dispatcher', () => {
|
||||
manager.createRootHostView(wrapPv(hostProtoView), null, null);
|
||||
var cmpView = createdViews[0];
|
||||
expect(renderer.spy('setEventDispatcher')).toHaveBeenCalledWith(cmpView.render, cmpView);
|
||||
var rootView = internalView(manager.createRootHostView(wrapPv(hostProtoView), null, null));
|
||||
expect(renderer.spy('setEventDispatcher')).toHaveBeenCalledWith(rootView.render, rootView);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('destroyRootHostView', () => {
|
||||
var hostProtoView, hostView, hostRenderViewRef;
|
||||
var hostProtoView: AppProtoView;
|
||||
var hostView: AppView;
|
||||
var hostRenderViewRef: RenderViewRef;
|
||||
beforeEach(() => {
|
||||
hostProtoView = createProtoView([createComponentElBinder(null)]);
|
||||
hostProtoView = createHostPv([createNestedElBinder(createComponentPv())]);
|
||||
hostView = internalView(manager.createRootHostView(wrapPv(hostProtoView), null, null));
|
||||
hostRenderViewRef = hostView.render;
|
||||
});
|
||||
|
||||
it('should dehydrate', () => {
|
||||
manager.destroyRootHostView(wrapView(hostView));
|
||||
expect(utils.spy('dehydrateView')).toHaveBeenCalledWith(hostView);
|
||||
expect(hostView.hydrated()).toBe(false);
|
||||
expect(renderer.spy('dehydrateView')).toHaveBeenCalledWith(hostView.render);
|
||||
});
|
||||
|
||||
@@ -269,60 +184,120 @@ export function main() {
|
||||
describe('createViewInContainer', () => {
|
||||
|
||||
describe('basic functionality', () => {
|
||||
var parentView, childProtoView;
|
||||
var hostView: AppView;
|
||||
var childProtoView: AppProtoView;
|
||||
var vcRef: ElementRef;
|
||||
beforeEach(() => {
|
||||
parentView = createView(createProtoView([createEmptyElBinder()]));
|
||||
childProtoView = createProtoView();
|
||||
childProtoView = createEmbeddedPv();
|
||||
var hostProtoView = createHostPv(
|
||||
[createNestedElBinder(createComponentPv([createNestedElBinder(childProtoView)]))]);
|
||||
hostView = internalView(manager.createRootHostView(wrapPv(hostProtoView), null, null));
|
||||
vcRef = hostView.elementRefs[1];
|
||||
resetSpies();
|
||||
});
|
||||
|
||||
it('should create a ViewContainerRef if not yet existing', () => {
|
||||
manager.createViewInContainer(elementRef(parentView, 0), 0, wrapPv(childProtoView), null);
|
||||
expect(parentView.viewContainers[0]).toBeTruthy();
|
||||
describe('create the first view', () => {
|
||||
|
||||
it('should create an AppViewContainer if not yet existing', () => {
|
||||
manager.createViewInContainer(vcRef, 0, wrapPv(childProtoView), null);
|
||||
expect(hostView.viewContainers[1]).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should use an existing nested view', () => {
|
||||
var childView =
|
||||
internalView(manager.createViewInContainer(vcRef, 0, wrapPv(childProtoView), null));
|
||||
expect(childView.proto).toBe(childProtoView);
|
||||
expect(childView).toBe(hostView.views[2]);
|
||||
expect(viewListener.spy('viewCreated')).not.toHaveBeenCalled();
|
||||
expect(renderer.spy('createView')).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should attach the fragment', () => {
|
||||
var childView =
|
||||
internalView(manager.createViewInContainer(vcRef, 0, wrapPv(childProtoView), null));
|
||||
expect(childView.proto).toBe(childProtoView);
|
||||
expect(hostView.viewContainers[1].views.length).toBe(1);
|
||||
expect(hostView.viewContainers[1].views[0]).toBe(childView);
|
||||
expect(renderer.spy('attachFragmentAfterElement'))
|
||||
.toHaveBeenCalledWith(vcRef, childView.renderFragment);
|
||||
});
|
||||
|
||||
it('should hydrate the view but not the render view', () => {
|
||||
var childView =
|
||||
internalView(manager.createViewInContainer(vcRef, 0, wrapPv(childProtoView), null));
|
||||
expect(childView.hydrated()).toBe(true);
|
||||
expect(renderer.spy('hydrateView')).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not set the EventDispatcher', () => {
|
||||
internalView(manager.createViewInContainer(vcRef, 0, wrapPv(childProtoView), null));
|
||||
expect(renderer.spy('setEventDispatcher')).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('should create the view', () => {
|
||||
expect(internalView(manager.createViewInContainer(elementRef(parentView, 0), 0,
|
||||
wrapPv(childProtoView), null)))
|
||||
.toBe(createdViews[0]);
|
||||
expect(createdViews[0].proto).toBe(childProtoView);
|
||||
expect(viewListener.spy('viewCreated')).toHaveBeenCalledWith(createdViews[0]);
|
||||
describe('create the second view', () => {
|
||||
var firstChildView;
|
||||
beforeEach(() => {
|
||||
firstChildView =
|
||||
internalView(manager.createViewInContainer(vcRef, 0, wrapPv(childProtoView), null));
|
||||
resetSpies();
|
||||
});
|
||||
|
||||
it('should create a new view', () => {
|
||||
var childView =
|
||||
internalView(manager.createViewInContainer(vcRef, 1, wrapPv(childProtoView), null));
|
||||
expect(childView.proto).toBe(childProtoView);
|
||||
expect(childView).not.toBe(firstChildView);
|
||||
expect(viewListener.spy('viewCreated')).toHaveBeenCalledWith(childView);
|
||||
expect(renderer.spy('createView'))
|
||||
.toHaveBeenCalledWith(childProtoView.mergeMapping.renderProtoViewRef,
|
||||
childProtoView.mergeMapping.renderFragmentCount);
|
||||
expect(childView.render).toBe(createdRenderViews[1].viewRef);
|
||||
expect(childView.renderFragment).toBe(createdRenderViews[1].fragmentRefs[0]);
|
||||
});
|
||||
|
||||
it('should attach the fragment', () => {
|
||||
var childView =
|
||||
internalView(manager.createViewInContainer(vcRef, 1, wrapPv(childProtoView), null));
|
||||
expect(childView.proto).toBe(childProtoView);
|
||||
expect(hostView.viewContainers[1].views[1]).toBe(childView);
|
||||
expect(renderer.spy('attachFragmentAfterFragment'))
|
||||
.toHaveBeenCalledWith(firstChildView.renderFragment, childView.renderFragment);
|
||||
});
|
||||
|
||||
it('should hydrate the view', () => {
|
||||
var childView =
|
||||
internalView(manager.createViewInContainer(vcRef, 1, wrapPv(childProtoView), null));
|
||||
expect(childView.hydrated()).toBe(true);
|
||||
expect(renderer.spy('hydrateView')).toHaveBeenCalledWith(childView.render);
|
||||
});
|
||||
|
||||
it('should set the EventDispatcher', () => {
|
||||
var childView =
|
||||
internalView(manager.createViewInContainer(vcRef, 1, wrapPv(childProtoView), null));
|
||||
expect(renderer.spy('setEventDispatcher'))
|
||||
.toHaveBeenCalledWith(childView.render, childView);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('should attach the view', () => {
|
||||
var contextView =
|
||||
createView(createProtoView([createEmptyElBinder(), createEmptyElBinder()]));
|
||||
var elRef = elementRef(parentView, 0);
|
||||
manager.createViewInContainer(elRef, 0, wrapPv(childProtoView),
|
||||
elementRef(contextView, 1), null);
|
||||
expect(utils.spy('attachViewInContainer'))
|
||||
.toHaveBeenCalledWith(parentView, 0, contextView, 1, 0, createdViews[0]);
|
||||
expect(renderer.spy('attachViewInContainer'))
|
||||
.toHaveBeenCalledWith(elRef, 0, createdViews[0].render);
|
||||
});
|
||||
describe('create another view when the first view has been returned', () => {
|
||||
beforeEach(() => {
|
||||
internalView(manager.createViewInContainer(vcRef, 0, wrapPv(childProtoView), null));
|
||||
manager.destroyViewInContainer(vcRef, 0);
|
||||
resetSpies();
|
||||
});
|
||||
|
||||
it('should hydrate the view', () => {
|
||||
var contextView =
|
||||
createView(createProtoView([createEmptyElBinder(), createEmptyElBinder()]));
|
||||
manager.createViewInContainer(elementRef(parentView, 0), 0, wrapPv(childProtoView),
|
||||
elementRef(contextView, 1), []);
|
||||
expect(utils.spy('hydrateViewInContainer'))
|
||||
.toHaveBeenCalledWith(parentView, 0, contextView, 1, 0, []);
|
||||
expect(renderer.spy('hydrateView')).toHaveBeenCalledWith(createdViews[0].render);
|
||||
});
|
||||
it('should use an existing nested view', () => {
|
||||
var childView =
|
||||
internalView(manager.createViewInContainer(vcRef, 0, wrapPv(childProtoView), null));
|
||||
expect(childView.proto).toBe(childProtoView);
|
||||
expect(childView).toBe(hostView.views[2]);
|
||||
expect(viewListener.spy('viewCreated')).not.toHaveBeenCalled();
|
||||
expect(renderer.spy('createView')).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should create and set the render view', () => {
|
||||
manager.createViewInContainer(elementRef(parentView, 0), 0, wrapPv(childProtoView), null,
|
||||
null);
|
||||
expect(renderer.spy('createView')).toHaveBeenCalledWith(childProtoView.render);
|
||||
expect(createdViews[0].render).toBe(createdRenderViews[0]);
|
||||
});
|
||||
|
||||
it('should set the event dispatcher', () => {
|
||||
manager.createViewInContainer(elementRef(parentView, 0), 0, wrapPv(childProtoView), null,
|
||||
null);
|
||||
var childView = createdViews[0];
|
||||
expect(renderer.spy('setEventDispatcher'))
|
||||
.toHaveBeenCalledWith(childView.render, childView);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -331,61 +306,159 @@ export function main() {
|
||||
describe('destroyViewInContainer', () => {
|
||||
|
||||
describe('basic functionality', () => {
|
||||
var parentView, childProtoView, childView;
|
||||
var hostView: AppView;
|
||||
var childProtoView: AppProtoView;
|
||||
var vcRef: ElementRef;
|
||||
var firstChildView: AppView;
|
||||
beforeEach(() => {
|
||||
parentView = createView(createProtoView([createEmptyElBinder()]));
|
||||
childProtoView = createProtoView();
|
||||
childView = internalView(manager.createViewInContainer(elementRef(parentView, 0), 0,
|
||||
wrapPv(childProtoView), null));
|
||||
childProtoView = createEmbeddedPv();
|
||||
var hostProtoView = createHostPv(
|
||||
[createNestedElBinder(createComponentPv([createNestedElBinder(childProtoView)]))]);
|
||||
hostView = internalView(manager.createRootHostView(wrapPv(hostProtoView), null, null));
|
||||
vcRef = hostView.elementRefs[1];
|
||||
firstChildView =
|
||||
internalView(manager.createViewInContainer(vcRef, 0, wrapPv(childProtoView), null));
|
||||
resetSpies();
|
||||
});
|
||||
|
||||
it('should dehydrate', () => {
|
||||
manager.destroyViewInContainer(elementRef(parentView, 0), 0);
|
||||
expect(utils.spy('dehydrateView'))
|
||||
.toHaveBeenCalledWith(parentView.viewContainers[0].views[0]);
|
||||
expect(renderer.spy('dehydrateView')).toHaveBeenCalledWith(childView.render);
|
||||
describe('destroy the first view', () => {
|
||||
it('should dehydrate the app view but not the render view', () => {
|
||||
manager.destroyViewInContainer(vcRef, 0);
|
||||
expect(firstChildView.hydrated()).toBe(false);
|
||||
expect(renderer.spy('dehydrateView')).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should detach', () => {
|
||||
manager.destroyViewInContainer(vcRef, 0);
|
||||
expect(hostView.viewContainers[1].views).toEqual([]);
|
||||
expect(renderer.spy('detachFragment'))
|
||||
.toHaveBeenCalledWith(firstChildView.renderFragment);
|
||||
});
|
||||
|
||||
it('should not return the view to the pool', () => {
|
||||
manager.destroyViewInContainer(vcRef, 0);
|
||||
expect(viewPool.spy('returnView')).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('should detach', () => {
|
||||
var elRef = elementRef(parentView, 0);
|
||||
manager.destroyViewInContainer(elRef, 0);
|
||||
expect(utils.spy('detachViewInContainer')).toHaveBeenCalledWith(parentView, 0, 0);
|
||||
expect(renderer.spy('detachViewInContainer'))
|
||||
.toHaveBeenCalledWith(elRef, 0, childView.render);
|
||||
});
|
||||
describe('destroy another view', () => {
|
||||
var secondChildView;
|
||||
beforeEach(() => {
|
||||
secondChildView =
|
||||
internalView(manager.createViewInContainer(vcRef, 1, wrapPv(childProtoView), null));
|
||||
resetSpies();
|
||||
});
|
||||
|
||||
it('should dehydrate', () => {
|
||||
manager.destroyViewInContainer(vcRef, 1);
|
||||
expect(secondChildView.hydrated()).toBe(false);
|
||||
expect(renderer.spy('dehydrateView')).toHaveBeenCalledWith(secondChildView.render);
|
||||
});
|
||||
|
||||
it('should detach', () => {
|
||||
manager.destroyViewInContainer(vcRef, 1);
|
||||
expect(hostView.viewContainers[1].views[0]).toBe(firstChildView);
|
||||
expect(renderer.spy('detachFragment'))
|
||||
.toHaveBeenCalledWith(secondChildView.renderFragment);
|
||||
});
|
||||
|
||||
it('should return the view to the pool', () => {
|
||||
manager.destroyViewInContainer(vcRef, 1);
|
||||
expect(viewPool.spy('returnView')).toHaveBeenCalledWith(secondChildView);
|
||||
});
|
||||
|
||||
it('should return the view to the pool', () => {
|
||||
manager.destroyViewInContainer(elementRef(parentView, 0), 0);
|
||||
expect(viewPool.spy('returnView')).toHaveBeenCalledWith(childView);
|
||||
});
|
||||
});
|
||||
|
||||
describe('recursively destroy views in ViewContainers', () => {
|
||||
var parentView, childProtoView, childView;
|
||||
beforeEach(() => {
|
||||
parentView = createView(createProtoView([createEmptyElBinder()]));
|
||||
childProtoView = createProtoView();
|
||||
childView = internalView(manager.createViewInContainer(elementRef(parentView, 0), 0,
|
||||
wrapPv(childProtoView), null));
|
||||
|
||||
describe('destroy child views when a component is destroyed', () => {
|
||||
var hostView: AppView;
|
||||
var childProtoView: AppProtoView;
|
||||
var vcRef: ElementRef;
|
||||
var firstChildView: AppView;
|
||||
var secondChildView: AppView;
|
||||
beforeEach(() => {
|
||||
childProtoView = createEmbeddedPv();
|
||||
var hostProtoView = createHostPv(
|
||||
[createNestedElBinder(createComponentPv([createNestedElBinder(childProtoView)]))]);
|
||||
hostView = internalView(manager.createRootHostView(wrapPv(hostProtoView), null, null));
|
||||
vcRef = hostView.elementRefs[1];
|
||||
firstChildView =
|
||||
internalView(manager.createViewInContainer(vcRef, 0, wrapPv(childProtoView), null));
|
||||
secondChildView =
|
||||
internalView(manager.createViewInContainer(vcRef, 1, wrapPv(childProtoView), null));
|
||||
resetSpies();
|
||||
});
|
||||
|
||||
it('should dehydrate', () => {
|
||||
manager.destroyRootHostView(wrapView(hostView));
|
||||
expect(firstChildView.hydrated()).toBe(false);
|
||||
expect(secondChildView.hydrated()).toBe(false);
|
||||
expect(renderer.spy('dehydrateView')).toHaveBeenCalledWith(hostView.render);
|
||||
expect(renderer.spy('dehydrateView')).toHaveBeenCalledWith(secondChildView.render);
|
||||
});
|
||||
|
||||
it('should detach', () => {
|
||||
manager.destroyRootHostView(wrapView(hostView));
|
||||
expect(hostView.viewContainers[1].views).toEqual([]);
|
||||
expect(renderer.spy('detachFragment'))
|
||||
.toHaveBeenCalledWith(firstChildView.renderFragment);
|
||||
expect(renderer.spy('detachFragment'))
|
||||
.toHaveBeenCalledWith(secondChildView.renderFragment);
|
||||
});
|
||||
|
||||
it('should return the view to the pool', () => {
|
||||
manager.destroyRootHostView(wrapView(hostView));
|
||||
expect(viewPool.spy('returnView')).not.toHaveBeenCalledWith(firstChildView);
|
||||
expect(viewPool.spy('returnView')).toHaveBeenCalledWith(secondChildView);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('should dehydrate', () => {
|
||||
manager.destroyRootHostView(wrapView(parentView));
|
||||
expect(utils.spy('dehydrateView'))
|
||||
.toHaveBeenCalledWith(parentView.viewContainers[0].views[0]);
|
||||
expect(renderer.spy('dehydrateView')).toHaveBeenCalledWith(childView.render);
|
||||
});
|
||||
describe('destroy child views over multiple levels', () => {
|
||||
var hostView: AppView;
|
||||
var childProtoView: AppProtoView;
|
||||
var nestedChildProtoView: AppProtoView;
|
||||
var vcRef: ElementRef;
|
||||
var nestedVcRefs: ElementRef[];
|
||||
var childViews: AppView[];
|
||||
var nestedChildViews: AppView[];
|
||||
beforeEach(() => {
|
||||
nestedChildProtoView = createEmbeddedPv();
|
||||
childProtoView = createEmbeddedPv([
|
||||
createNestedElBinder(
|
||||
createComponentPv([createNestedElBinder(nestedChildProtoView)]))
|
||||
]);
|
||||
var hostProtoView = createHostPv(
|
||||
[createNestedElBinder(createComponentPv([createNestedElBinder(childProtoView)]))]);
|
||||
hostView = internalView(manager.createRootHostView(wrapPv(hostProtoView), null, null));
|
||||
vcRef = hostView.elementRefs[1];
|
||||
nestedChildViews = [];
|
||||
childViews = [];
|
||||
nestedVcRefs = [];
|
||||
for (var i = 0; i < 2; i++) {
|
||||
var view = internalView(
|
||||
manager.createViewInContainer(vcRef, i, wrapPv(childProtoView), null));
|
||||
childViews.push(view);
|
||||
var nestedVcRef = view.elementRefs[view.elementOffset];
|
||||
nestedVcRefs.push(nestedVcRef);
|
||||
for (var j = 0; j < 2; j++) {
|
||||
var nestedView = internalView(
|
||||
manager.createViewInContainer(nestedVcRef, j, wrapPv(childProtoView), null));
|
||||
nestedChildViews.push(nestedView);
|
||||
}
|
||||
}
|
||||
resetSpies();
|
||||
});
|
||||
|
||||
it('should detach', () => {
|
||||
manager.destroyRootHostView(wrapView(parentView));
|
||||
expect(utils.spy('detachViewInContainer')).toHaveBeenCalledWith(parentView, 0, 0);
|
||||
expect(renderer.spy('detachViewInContainer'))
|
||||
.toHaveBeenCalledWith(parentView.elementRefs[0], 0, childView.render);
|
||||
});
|
||||
it('should dehydrate all child views', () => {
|
||||
manager.destroyRootHostView(wrapView(hostView));
|
||||
childViews.forEach((childView) => expect(childView.hydrated()).toBe(false));
|
||||
nestedChildViews.forEach((childView) => expect(childView.hydrated()).toBe(false));
|
||||
});
|
||||
|
||||
it('should return the view to the pool', () => {
|
||||
manager.destroyRootHostView(wrapView(parentView));
|
||||
expect(viewPool.spy('returnView')).toHaveBeenCalledWith(childView);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -402,18 +475,6 @@ export function main() {
|
||||
});
|
||||
}
|
||||
|
||||
class MockProtoViewRef extends RenderProtoViewRef {
|
||||
nestedComponentCount: number;
|
||||
constructor(nestedComponentCount: number) {
|
||||
super();
|
||||
this.nestedComponentCount = nestedComponentCount;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'someComponent'})
|
||||
class SomeComponent {
|
||||
}
|
||||
|
||||
@proxy
|
||||
@IMPLEMENTS(Renderer)
|
||||
class SpyRenderer extends SpyObject {
|
||||
@@ -428,23 +489,9 @@ class SpyAppViewPool extends SpyObject {
|
||||
noSuchMethod(m) { return super.noSuchMethod(m) }
|
||||
}
|
||||
|
||||
@proxy
|
||||
@IMPLEMENTS(AppViewManagerUtils)
|
||||
class SpyAppViewManagerUtils extends SpyObject {
|
||||
constructor() { super(AppViewManagerUtils); }
|
||||
noSuchMethod(m) { return super.noSuchMethod(m) }
|
||||
}
|
||||
|
||||
@proxy
|
||||
@IMPLEMENTS(AppViewListener)
|
||||
class SpyAppViewListener extends SpyObject {
|
||||
constructor() { super(AppViewListener); }
|
||||
noSuchMethod(m) { return super.noSuchMethod(m) }
|
||||
}
|
||||
|
||||
@proxy
|
||||
@IMPLEMENTS(ElementInjector)
|
||||
class SpyElementInjector extends SpyObject {
|
||||
constructor() { super(ElementInjector); }
|
||||
noSuchMethod(m) { return super.noSuchMethod(m) }
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
xit,
|
||||
SpyObject,
|
||||
SpyChangeDetector,
|
||||
SpyProtoChangeDetector,
|
||||
proxy,
|
||||
Log
|
||||
} from 'angular2/test_lib';
|
||||
@@ -22,7 +23,7 @@ import {Injector, bind} from 'angular2/di';
|
||||
import {IMPLEMENTS, isBlank, isPresent} from 'angular2/src/facade/lang';
|
||||
import {MapWrapper, ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
import {AppProtoView, AppView} from 'angular2/src/core/compiler/view';
|
||||
import {AppProtoView, AppView, AppProtoViewMergeMapping} from 'angular2/src/core/compiler/view';
|
||||
import {ElementBinder} from 'angular2/src/core/compiler/element_binder';
|
||||
import {
|
||||
DirectiveBinding,
|
||||
@@ -33,89 +34,31 @@ import {
|
||||
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
||||
import {Component} from 'angular2/annotations';
|
||||
import {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils';
|
||||
import {RenderProtoViewMergeMapping, ViewType, RenderViewWithFragments} from 'angular2/render';
|
||||
|
||||
export function main() {
|
||||
// TODO(tbosch): add more tests here!
|
||||
|
||||
describe('AppViewManagerUtils', () => {
|
||||
|
||||
var directiveResolver;
|
||||
var utils;
|
||||
var utils: AppViewManagerUtils;
|
||||
|
||||
function createInjector() { return Injector.resolveAndCreate([]); }
|
||||
beforeEach(() => { utils = new AppViewManagerUtils(); });
|
||||
|
||||
function createDirectiveBinding(type) {
|
||||
var annotation = directiveResolver.resolve(type);
|
||||
return DirectiveBinding.createFromType(type, annotation);
|
||||
function createViewWithChildren(pv: AppProtoView): AppView {
|
||||
var renderViewWithFragments = new RenderViewWithFragments(null, [null, null]);
|
||||
return utils.createView(pv, renderViewWithFragments, null, null);
|
||||
}
|
||||
|
||||
function createEmptyElBinder() { return new ElementBinder(0, null, 0, null, null); }
|
||||
|
||||
function createComponentElBinder(nestedProtoView = null) {
|
||||
var binding = createDirectiveBinding(SomeComponent);
|
||||
var binder = new ElementBinder(0, null, 0, null, binding);
|
||||
binder.nestedProtoView = nestedProtoView;
|
||||
return binder;
|
||||
}
|
||||
|
||||
function createProtoView(binders = null) {
|
||||
if (isBlank(binders)) {
|
||||
binders = [];
|
||||
}
|
||||
var res = new AppProtoView(null, null, null, null);
|
||||
res.elementBinders = binders;
|
||||
return res;
|
||||
}
|
||||
|
||||
function createElementInjector(parent = null) {
|
||||
var host = new SpyElementInjector();
|
||||
var elementInjector =
|
||||
isPresent(parent) ? new SpyElementInjectorWithParent(parent) : new SpyElementInjector();
|
||||
return SpyObject.stub(elementInjector,
|
||||
{
|
||||
'isExportingComponent': false,
|
||||
'isExportingElement': false,
|
||||
'getEventEmitterAccessors': [],
|
||||
'getHostActionAccessors': [],
|
||||
'getComponent': null,
|
||||
'getHost': host
|
||||
},
|
||||
{});
|
||||
}
|
||||
|
||||
function createView(pv = null, nestedInjectors = false) {
|
||||
if (isBlank(pv)) {
|
||||
pv = createProtoView();
|
||||
}
|
||||
var view = new AppView(null, pv, new Map());
|
||||
var elementInjectors = ListWrapper.createGrowableSize(pv.elementBinders.length);
|
||||
var preBuiltObjects = ListWrapper.createFixedSize(pv.elementBinders.length);
|
||||
for (var i = 0; i < pv.elementBinders.length; i++) {
|
||||
if (nestedInjectors && i > 0) {
|
||||
elementInjectors[i] = createElementInjector(elementInjectors[i - 1]);
|
||||
} else {
|
||||
elementInjectors[i] = createElementInjector();
|
||||
}
|
||||
preBuiltObjects[i] = new SpyPreBuiltObjects();
|
||||
}
|
||||
view.init(<any>new SpyChangeDetector(), elementInjectors, elementInjectors, preBuiltObjects,
|
||||
ListWrapper.createFixedSize(pv.elementBinders.length));
|
||||
return view;
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
directiveResolver = new DirectiveResolver();
|
||||
utils = new AppViewManagerUtils();
|
||||
});
|
||||
|
||||
describe("hydrateComponentView", () => {
|
||||
describe('shared hydrate functionality', () => {
|
||||
|
||||
it("should hydrate the change detector after hydrating element injectors", () => {
|
||||
var log = new Log();
|
||||
|
||||
var componentView = createView(createProtoView([createEmptyElBinder()]));
|
||||
var hostView = createView(createProtoView([createComponentElBinder(createProtoView())]));
|
||||
hostView.componentChildViews = [componentView];
|
||||
var componentProtoView = createComponentPv([createEmptyElBinder()]);
|
||||
var hostView =
|
||||
createViewWithChildren(createHostPv([createNestedElBinder(componentProtoView)]));
|
||||
var componentView = hostView.views[1];
|
||||
|
||||
var spyEi = <any>componentView.elementInjectors[0];
|
||||
spyEi.spy('hydrate').andCallFake(log.fn('hydrate'));
|
||||
@@ -123,20 +66,17 @@ export function main() {
|
||||
var spyCd = <any>componentView.changeDetector;
|
||||
spyCd.spy('hydrate').andCallFake(log.fn('hydrateCD'));
|
||||
|
||||
utils.hydrateComponentView(hostView, 0);
|
||||
utils.hydrateRootHostView(hostView, createInjector());
|
||||
|
||||
expect(log.result()).toEqual('hydrate; hydrateCD');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('shared hydrate functionality', () => {
|
||||
|
||||
it("should set up event listeners", () => {
|
||||
var dir = new Object();
|
||||
|
||||
var hostPv = createProtoView([createComponentElBinder(null), createEmptyElBinder()]);
|
||||
var hostView = createView(hostPv);
|
||||
var hostPv =
|
||||
createHostPv([createNestedElBinder(createComponentPv()), createEmptyElBinder()]);
|
||||
var hostView = createViewWithChildren(hostPv);
|
||||
var spyEventAccessor1 = SpyObject.stub({"subscribe": null});
|
||||
SpyObject.stub(hostView.elementInjectors[0], {
|
||||
'getHostActionAccessors': [],
|
||||
@@ -150,9 +90,6 @@ export function main() {
|
||||
'getDirectiveAtIndex': dir
|
||||
});
|
||||
|
||||
var shadowView = createView();
|
||||
utils.attachComponentView(hostView, 0, shadowView);
|
||||
|
||||
utils.hydrateRootHostView(hostView, createInjector());
|
||||
|
||||
expect(spyEventAccessor1.spy('subscribe')).toHaveBeenCalledWith(hostView, 0, dir);
|
||||
@@ -162,8 +99,9 @@ export function main() {
|
||||
it("should set up host action listeners", () => {
|
||||
var dir = new Object();
|
||||
|
||||
var hostPv = createProtoView([createComponentElBinder(null), createEmptyElBinder()]);
|
||||
var hostView = createView(hostPv);
|
||||
var hostPv =
|
||||
createHostPv([createNestedElBinder(createComponentPv()), createEmptyElBinder()]);
|
||||
var hostView = createViewWithChildren(hostPv);
|
||||
var spyActionAccessor1 = SpyObject.stub({"subscribe": null});
|
||||
SpyObject.stub(hostView.elementInjectors[0], {
|
||||
'getHostActionAccessors': [[spyActionAccessor1]],
|
||||
@@ -177,37 +115,51 @@ export function main() {
|
||||
'getDirectiveAtIndex': dir
|
||||
});
|
||||
|
||||
var shadowView = createView();
|
||||
utils.attachComponentView(hostView, 0, shadowView);
|
||||
|
||||
utils.hydrateRootHostView(hostView, createInjector());
|
||||
|
||||
expect(spyActionAccessor1.spy('subscribe')).toHaveBeenCalledWith(hostView, 0, dir);
|
||||
expect(spyActionAccessor2.spy('subscribe')).toHaveBeenCalledWith(hostView, 1, dir);
|
||||
});
|
||||
|
||||
it("should not hydrate element injectors of component views inside of embedded fragments",
|
||||
() => {
|
||||
var hostView = createViewWithChildren(createHostPv([
|
||||
createNestedElBinder(createComponentPv([
|
||||
createNestedElBinder(createEmbeddedPv(
|
||||
[createNestedElBinder(createComponentPv([createEmptyElBinder()]))]))
|
||||
]))
|
||||
]));
|
||||
|
||||
utils.hydrateRootHostView(hostView, createInjector());
|
||||
expect(hostView.elementInjectors.length).toBe(4);
|
||||
expect((<any>hostView.elementInjectors[3]).spy('hydrate')).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
describe('attachViewInContainer', () => {
|
||||
var parentView, contextView, childView;
|
||||
|
||||
function createViews(numInj = 1) {
|
||||
var parentPv = createProtoView([createEmptyElBinder()]);
|
||||
parentView = createView(parentPv);
|
||||
var childPv = createEmbeddedPv([createEmptyElBinder()]);
|
||||
childView = createViewWithChildren(childPv);
|
||||
|
||||
var parentPv = createHostPv([createEmptyElBinder()]);
|
||||
parentView = createViewWithChildren(parentPv);
|
||||
|
||||
var binders = [];
|
||||
for (var i = 0; i < numInj; i++) binders.push(createEmptyElBinder());
|
||||
var contextPv = createProtoView(binders);
|
||||
contextView = createView(contextPv, true);
|
||||
|
||||
var childPv = createProtoView([createEmptyElBinder()]);
|
||||
childView = createView(childPv);
|
||||
for (var i = 0; i < numInj; i++) {
|
||||
binders.push(createEmptyElBinder(i > 0 ? binders[i - 1] : null))
|
||||
};
|
||||
var contextPv = createHostPv(binders);
|
||||
contextView = createViewWithChildren(contextPv);
|
||||
}
|
||||
|
||||
it('should link the views rootElementInjectors at the given context', () => {
|
||||
createViews();
|
||||
utils.attachViewInContainer(parentView, 0, contextView, 0, 0, childView);
|
||||
expect(contextView.elementInjectors.length).toEqual(2);
|
||||
expect(contextView.rootElementInjectors.length).toEqual(2);
|
||||
});
|
||||
|
||||
it('should link the views rootElementInjectors after the elementInjector at the given context',
|
||||
@@ -223,14 +175,14 @@ export function main() {
|
||||
var parentView, contextView, childView;
|
||||
|
||||
function createViews() {
|
||||
var parentPv = createProtoView([createEmptyElBinder()]);
|
||||
parentView = createView(parentPv);
|
||||
var parentPv = createHostPv([createEmptyElBinder()]);
|
||||
parentView = createViewWithChildren(parentPv);
|
||||
|
||||
var contextPv = createProtoView([createEmptyElBinder()]);
|
||||
contextView = createView(contextPv);
|
||||
var contextPv = createHostPv([createEmptyElBinder()]);
|
||||
contextView = createViewWithChildren(contextPv);
|
||||
|
||||
var childPv = createProtoView([createEmptyElBinder()]);
|
||||
childView = createView(childPv);
|
||||
var childPv = createEmbeddedPv([createEmptyElBinder()]);
|
||||
childView = createViewWithChildren(childPv);
|
||||
utils.attachViewInContainer(parentView, 0, contextView, 0, 0, childView);
|
||||
}
|
||||
|
||||
@@ -249,8 +201,8 @@ export function main() {
|
||||
var hostView;
|
||||
|
||||
function createViews() {
|
||||
var hostPv = createProtoView([createComponentElBinder()]);
|
||||
hostView = createView(hostPv);
|
||||
var hostPv = createHostPv([createNestedElBinder(createComponentPv())]);
|
||||
hostView = createViewWithChildren(hostPv);
|
||||
}
|
||||
|
||||
it("should instantiate the elementInjectors with the given injector and an empty host element injector",
|
||||
@@ -268,25 +220,125 @@ export function main() {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
export function createInjector() {
|
||||
return Injector.resolveAndCreate([]);
|
||||
}
|
||||
|
||||
function createElementInjector(parent = null) {
|
||||
var host = new SpyElementInjector(null);
|
||||
var elementInjector = new SpyElementInjector(parent);
|
||||
return SpyObject.stub(elementInjector,
|
||||
{
|
||||
'isExportingComponent': false,
|
||||
'isExportingElement': false,
|
||||
'getEventEmitterAccessors': [],
|
||||
'getHostActionAccessors': [],
|
||||
'getComponent': new Object(),
|
||||
'getHost': host
|
||||
},
|
||||
{});
|
||||
}
|
||||
|
||||
export function createProtoElInjector(parent: ProtoElementInjector = null): ProtoElementInjector {
|
||||
var pei = new SpyProtoElementInjector(parent);
|
||||
pei.spy('instantiate').andCallFake((parentEli) => createElementInjector(parentEli));
|
||||
return <any>pei;
|
||||
}
|
||||
|
||||
export function createEmptyElBinder(parent: ElementBinder = null) {
|
||||
var parentPeli = isPresent(parent) ? parent.protoElementInjector : null;
|
||||
return new ElementBinder(0, null, 0, createProtoElInjector(parentPeli), null);
|
||||
}
|
||||
|
||||
export function createNestedElBinder(nestedProtoView: AppProtoView) {
|
||||
var componentBinding = null;
|
||||
if (nestedProtoView.type === ViewType.COMPONENT) {
|
||||
var annotation = new DirectiveResolver().resolve(SomeComponent);
|
||||
componentBinding = DirectiveBinding.createFromType(SomeComponent, annotation);
|
||||
}
|
||||
var binder = new ElementBinder(0, null, 0, createProtoElInjector(), componentBinding);
|
||||
binder.nestedProtoView = nestedProtoView;
|
||||
return binder;
|
||||
}
|
||||
|
||||
function countNestedElementBinders(pv: AppProtoView): number {
|
||||
var result = pv.elementBinders.length;
|
||||
pv.elementBinders.forEach(binder => {
|
||||
if (isPresent(binder.nestedProtoView)) {
|
||||
result += countNestedElementBinders(binder.nestedProtoView);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
function calcHostElementIndicesByViewIndex(pv: AppProtoView, elementOffset = 0,
|
||||
target: number[] = null): number[] {
|
||||
if (isBlank(target)) {
|
||||
target = [null];
|
||||
}
|
||||
for (var binderIdx = 0; binderIdx < pv.elementBinders.length; binderIdx++) {
|
||||
var binder = pv.elementBinders[binderIdx];
|
||||
if (isPresent(binder.nestedProtoView)) {
|
||||
target.push(elementOffset + binderIdx);
|
||||
calcHostElementIndicesByViewIndex(binder.nestedProtoView,
|
||||
elementOffset + pv.elementBinders.length, target);
|
||||
elementOffset += countNestedElementBinders(binder.nestedProtoView);
|
||||
}
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
function _createProtoView(type: ViewType, binders: ElementBinder[] = null) {
|
||||
if (isBlank(binders)) {
|
||||
binders = [];
|
||||
}
|
||||
var protoChangeDetector = <any>new SpyProtoChangeDetector();
|
||||
protoChangeDetector.spy('instantiate').andReturn(new SpyChangeDetector());
|
||||
var res = new AppProtoView(type, protoChangeDetector, null, null, 0);
|
||||
res.elementBinders = binders;
|
||||
var mappedElementIndices = ListWrapper.createFixedSize(countNestedElementBinders(res));
|
||||
for (var i = 0; i < binders.length; i++) {
|
||||
var binder = binders[i];
|
||||
mappedElementIndices[i] = i;
|
||||
binder.protoElementInjector.index = i;
|
||||
}
|
||||
var hostElementIndicesByViewIndex = calcHostElementIndicesByViewIndex(res);
|
||||
res.mergeMapping = new AppProtoViewMergeMapping(
|
||||
new RenderProtoViewMergeMapping(null, hostElementIndicesByViewIndex.length,
|
||||
mappedElementIndices, [], hostElementIndicesByViewIndex));
|
||||
return res;
|
||||
}
|
||||
|
||||
export function createHostPv(binders: ElementBinder[] = null) {
|
||||
return _createProtoView(ViewType.HOST, binders);
|
||||
}
|
||||
|
||||
export function createComponentPv(binders: ElementBinder[] = null) {
|
||||
return _createProtoView(ViewType.COMPONENT, binders);
|
||||
}
|
||||
|
||||
export function createEmbeddedPv(binders: ElementBinder[] = null) {
|
||||
return _createProtoView(ViewType.EMBEDDED, binders);
|
||||
}
|
||||
|
||||
|
||||
@Component({selector: 'someComponent'})
|
||||
class SomeComponent {
|
||||
}
|
||||
|
||||
@proxy
|
||||
@IMPLEMENTS(ElementInjector)
|
||||
class SpyElementInjector extends SpyObject {
|
||||
constructor() { super(ElementInjector); }
|
||||
@IMPLEMENTS(ProtoElementInjector)
|
||||
class SpyProtoElementInjector extends SpyObject {
|
||||
index: number;
|
||||
constructor(public parent: ProtoElementInjector) { super(ProtoElementInjector); }
|
||||
noSuchMethod(m) { return super.noSuchMethod(m) }
|
||||
}
|
||||
|
||||
@proxy
|
||||
@IMPLEMENTS(ElementInjector)
|
||||
class SpyElementInjectorWithParent extends SpyObject {
|
||||
parent: ElementInjector;
|
||||
constructor(parent) {
|
||||
super(ElementInjector);
|
||||
this.parent = parent;
|
||||
}
|
||||
class SpyElementInjector extends SpyObject {
|
||||
constructor(public parent: ElementInjector) { super(ElementInjector); }
|
||||
noSuchMethod(m) { return super.noSuchMethod(m) }
|
||||
}
|
||||
|
||||
|
||||
@@ -24,9 +24,11 @@ export function main() {
|
||||
|
||||
function createViewPool({capacity}): AppViewPool { return new AppViewPool(capacity); }
|
||||
|
||||
function createProtoView() { return new AppProtoView(null, null, null, null); }
|
||||
function createProtoView() { return new AppProtoView(null, null, null, null, null); }
|
||||
|
||||
function createView(pv) { return new AppView(null, pv, new Map()); }
|
||||
function createView(pv) {
|
||||
return new AppView(null, pv, null, null, null, null, new Map(), null, null);
|
||||
}
|
||||
|
||||
it('should support multiple AppProtoViews', () => {
|
||||
var vf = createViewPool({capacity: 2});
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
el,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
beforeEachBindings,
|
||||
SpyObject,
|
||||
stringifyElement
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
export function main() {
|
||||
describe('dom adapter', () => {
|
||||
it('should not coalesque text nodes', () => {
|
||||
var el1 = el('<div>a</div>');
|
||||
var el2 = el('<div>b</div>');
|
||||
DOM.appendChild(el2, DOM.firstChild(el1));
|
||||
expect(DOM.childNodes(el2).length).toBe(2);
|
||||
|
||||
var el2Clone = DOM.clone(el2);
|
||||
expect(DOM.childNodes(el2Clone).length).toBe(2);
|
||||
});
|
||||
|
||||
it('should clone correctly', () => {
|
||||
var el1 = el('<div x="y">a<span>b</span></div>');
|
||||
var clone = DOM.clone(el1);
|
||||
|
||||
expect(clone).not.toBe(el1);
|
||||
DOM.setAttribute(clone, 'test', '1');
|
||||
expect(DOM.getOuterHTML(clone)).toEqual('<div x="y" test="1">a<span>b</span></div>');
|
||||
expect(DOM.getAttribute(el1, 'test')).toBeFalsy();
|
||||
|
||||
var cNodes = DOM.childNodes(clone);
|
||||
var firstChild = cNodes[0];
|
||||
var secondChild = cNodes[1];
|
||||
expect(DOM.parentElement(firstChild)).toBe(clone);
|
||||
expect(DOM.nextSibling(firstChild)).toBe(secondChild);
|
||||
expect(DOM.isTextNode(firstChild)).toBe(true);
|
||||
|
||||
expect(DOM.parentElement(secondChild)).toBe(clone);
|
||||
expect(DOM.nextSibling(secondChild)).toBeFalsy();
|
||||
expect(DOM.isElementNode(secondChild)).toBe(true);
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -36,7 +36,7 @@ export function runCompilerCommonTests() {
|
||||
}
|
||||
var tplLoader = new FakeViewLoader(urlData);
|
||||
mockStepFactory = new MockStepFactory([new MockStep(processClosure)]);
|
||||
return new DomCompiler(mockStepFactory, tplLoader);
|
||||
return new DomCompiler(mockStepFactory, tplLoader, false);
|
||||
}
|
||||
|
||||
describe('compile', () => {
|
||||
@@ -64,7 +64,8 @@ export function runCompilerCommonTests() {
|
||||
{id: 'id', selector: 'CUSTOM', type: DirectiveMetadata.COMPONENT_TYPE});
|
||||
compiler.compileHost(dirMetadata)
|
||||
.then((protoView) => {
|
||||
expect(DOM.tagName(resolveInternalDomProtoView(protoView.render).element))
|
||||
expect(DOM.tagName(DOM.firstChild(DOM.content(
|
||||
resolveInternalDomProtoView(protoView.render).rootElement))))
|
||||
.toEqual('CUSTOM');
|
||||
expect(mockStepFactory.viewDef.directives).toEqual([dirMetadata]);
|
||||
expect(protoView.variableBindings)
|
||||
@@ -79,7 +80,7 @@ export function runCompilerCommonTests() {
|
||||
compiler.compile(
|
||||
new ViewDefinition({componentId: 'someId', template: 'inline component'}))
|
||||
.then((protoView) => {
|
||||
expect(DOM.getInnerHTML(resolveInternalDomProtoView(protoView.render).element))
|
||||
expect(DOM.getInnerHTML(resolveInternalDomProtoView(protoView.render).rootElement))
|
||||
.toEqual('inline component');
|
||||
async.done();
|
||||
});
|
||||
@@ -90,7 +91,7 @@ export function runCompilerCommonTests() {
|
||||
var compiler = createCompiler(EMPTY_STEP, urlData);
|
||||
compiler.compile(new ViewDefinition({componentId: 'someId', templateAbsUrl: 'someUrl'}))
|
||||
.then((protoView) => {
|
||||
expect(DOM.getInnerHTML(resolveInternalDomProtoView(protoView.render).element))
|
||||
expect(DOM.getInnerHTML(resolveInternalDomProtoView(protoView.render).rootElement))
|
||||
.toEqual('url component');
|
||||
async.done();
|
||||
});
|
||||
|
||||
@@ -2,9 +2,12 @@ import {describe, beforeEach, expect, it, iit, ddescribe, el} from 'angular2/tes
|
||||
import {TextInterpolationParser} from 'angular2/src/render/dom/compiler/text_interpolation_parser';
|
||||
import {CompilePipeline} from 'angular2/src/render/dom/compiler/compile_pipeline';
|
||||
import {MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {Lexer, Parser} from 'angular2/change_detection';
|
||||
import {Lexer, Parser, ASTWithSource} from 'angular2/change_detection';
|
||||
import {IgnoreChildrenStep} from './pipeline_spec';
|
||||
import {ElementBinderBuilder} from 'angular2/src/render/dom/view/proto_view_builder';
|
||||
import {
|
||||
ProtoViewBuilder,
|
||||
ElementBinderBuilder
|
||||
} from 'angular2/src/render/dom/view/proto_view_builder';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
export function main() {
|
||||
@@ -14,48 +17,55 @@ export function main() {
|
||||
[new IgnoreChildrenStep(), new TextInterpolationParser(new Parser(new Lexer()))]);
|
||||
}
|
||||
|
||||
function process(element): List<ElementBinderBuilder> {
|
||||
return ListWrapper.map(createPipeline().process(element),
|
||||
(compileElement) => compileElement.inheritedElementBinder);
|
||||
function process(templateString: string): ProtoViewBuilder {
|
||||
var compileElements = createPipeline().process(DOM.createTemplate(templateString));
|
||||
return compileElements[0].inheritedProtoView;
|
||||
}
|
||||
|
||||
function assertTextBinding(elementBinder, bindingIndex, nodeIndex, expression) {
|
||||
expect(elementBinder.textBindings[bindingIndex].source).toEqual(expression);
|
||||
expect(elementBinder.textBindingNodes[bindingIndex])
|
||||
.toEqual(DOM.childNodes(DOM.templateAwareRoot(elementBinder.element))[nodeIndex]);
|
||||
function assertRootTextBinding(protoViewBuilder: ProtoViewBuilder, nodeIndex: number,
|
||||
expression: string) {
|
||||
var node = DOM.childNodes(DOM.templateAwareRoot(protoViewBuilder.rootElement))[nodeIndex];
|
||||
expect(protoViewBuilder.rootTextBindings.get(node).source).toEqual(expression);
|
||||
}
|
||||
|
||||
it('should find text interpolation in normal elements', () => {
|
||||
var result = process(el('<div>{{expr1}}<span></span>{{expr2}}</div>'))[0];
|
||||
assertTextBinding(result, 0, 0, "{{expr1}}");
|
||||
assertTextBinding(result, 1, 2, "{{expr2}}");
|
||||
function assertElementTextBinding(elementBinderBuilder: ElementBinderBuilder, nodeIndex: number,
|
||||
expression: string) {
|
||||
var node = DOM.childNodes(DOM.templateAwareRoot(elementBinderBuilder.element))[nodeIndex];
|
||||
expect(elementBinderBuilder.textBindings.get(node).source).toEqual(expression);
|
||||
}
|
||||
|
||||
it('should find root text interpolations', () => {
|
||||
var result = process('{{expr1}}{{expr2}}<div></div>{{expr3}}');
|
||||
assertRootTextBinding(result, 0, "{{expr1}}{{expr2}}");
|
||||
assertRootTextBinding(result, 2, "{{expr3}}");
|
||||
});
|
||||
|
||||
it('should find text interpolation in template elements', () => {
|
||||
var result = process(el('<template>{{expr1}}<span></span>{{expr2}}</template>'))[0];
|
||||
assertTextBinding(result, 0, 0, "{{expr1}}");
|
||||
assertTextBinding(result, 1, 2, "{{expr2}}");
|
||||
it('should find text interpolation in normal elements', () => {
|
||||
var result = process('<div>{{expr1}}<span></span>{{expr2}}</div>');
|
||||
assertElementTextBinding(result.elements[0], 0, "{{expr1}}");
|
||||
assertElementTextBinding(result.elements[0], 2, "{{expr2}}");
|
||||
});
|
||||
|
||||
it('should allow multiple expressions', () => {
|
||||
var result = process(el('<div>{{expr1}}{{expr2}}</div>'))[0];
|
||||
assertTextBinding(result, 0, 0, "{{expr1}}{{expr2}}");
|
||||
var result = process('<div>{{expr1}}{{expr2}}</div>');
|
||||
assertElementTextBinding(result.elements[0], 0, "{{expr1}}{{expr2}}");
|
||||
});
|
||||
|
||||
it('should not interpolate when compileChildren is false', () => {
|
||||
var results = process(el('<div>{{included}}<span ignore-children>{{excluded}}</span></div>'));
|
||||
assertTextBinding(results[0], 0, 0, "{{included}}");
|
||||
expect(results[1]).toBe(results[0]);
|
||||
var results = process('<div>{{included}}<span ignore-children>{{excluded}}</span></div>');
|
||||
assertElementTextBinding(results.elements[0], 0, "{{included}}");
|
||||
expect(results.elements.length).toBe(1);
|
||||
expect(results.elements[0].textBindings.size).toBe(1);
|
||||
});
|
||||
|
||||
it('should allow fixed text before, in between and after expressions', () => {
|
||||
var result = process(el('<div>a{{expr1}}b{{expr2}}c</div>'))[0];
|
||||
assertTextBinding(result, 0, 0, "a{{expr1}}b{{expr2}}c");
|
||||
var result = process('<div>a{{expr1}}b{{expr2}}c</div>');
|
||||
assertElementTextBinding(result.elements[0], 0, "a{{expr1}}b{{expr2}}c");
|
||||
});
|
||||
|
||||
it('should escape quotes in fixed parts', () => {
|
||||
var result = process(el("<div>'\"a{{expr1}}</div>"))[0];
|
||||
assertTextBinding(result, 0, 0, "'\"a{{expr1}}");
|
||||
var result = process("<div>'\"a{{expr1}}</div>");
|
||||
assertElementTextBinding(result.elements[0], 0, "'\"a{{expr1}}");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ export function main() {
|
||||
describe('<template> elements', () => {
|
||||
|
||||
it('should move the content into a new <template> element and mark that as viewRoot', () => {
|
||||
var rootElement = el('<div><template if="true">a</template></div>');
|
||||
var rootElement = DOM.createTemplate('<template if="true">a</template>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
|
||||
expect(stringifyElement(results[1].element))
|
||||
@@ -38,32 +38,32 @@ export function main() {
|
||||
});
|
||||
|
||||
it('should mark the new <template> element as viewRoot', () => {
|
||||
var rootElement = el('<div><template if="true">a</template></div>');
|
||||
var rootElement = DOM.createTemplate('<template if="true">a</template>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
expect(results[2].isViewRoot).toBe(true);
|
||||
});
|
||||
|
||||
it('should not wrap the root element', () => {
|
||||
var rootElement = el('<div></div>');
|
||||
var rootElement = DOM.createTemplate('');
|
||||
var results = createPipeline().process(rootElement);
|
||||
expect(results.length).toBe(1);
|
||||
expect(stringifyElement(rootElement)).toEqual('<div></div>');
|
||||
expect(stringifyElement(rootElement)).toEqual('<template></template>');
|
||||
});
|
||||
|
||||
it('should copy over the elementDescription', () => {
|
||||
var rootElement = el('<div><template if="true">a</template></div>');
|
||||
var rootElement = DOM.createTemplate('<template if="true">a</template>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
expect(results[2].elementDescription).toBe(results[1].elementDescription);
|
||||
});
|
||||
|
||||
it('should clean out the inheritedElementBinder', () => {
|
||||
var rootElement = el('<div><template if="true">a</template></div>');
|
||||
var rootElement = DOM.createTemplate('<template if="true">a</template>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
expect(results[2].inheritedElementBinder).toBe(null);
|
||||
});
|
||||
|
||||
it('should create a nestedProtoView', () => {
|
||||
var rootElement = el('<div><template if="true">a</template></div>');
|
||||
var rootElement = DOM.createTemplate('<template if="true">a</template>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
expect(results[2].inheritedProtoView).not.toBe(null);
|
||||
expect(results[2].inheritedProtoView)
|
||||
@@ -78,18 +78,19 @@ export function main() {
|
||||
describe('elements with template attribute', () => {
|
||||
|
||||
it('should replace the element with an empty <template> element', () => {
|
||||
var rootElement = el('<div><span template=""></span></div>');
|
||||
var originalChild = rootElement.childNodes[0];
|
||||
var rootElement = DOM.createTemplate('<span template=""></span>');
|
||||
var originalChild = DOM.firstChild(DOM.content(rootElement));
|
||||
var results = createPipeline().process(rootElement);
|
||||
expect(results[0].element).toBe(rootElement);
|
||||
expect(stringifyElement(results[0].element))
|
||||
.toEqual('<div><template class="ng-binding"></template></div>');
|
||||
expect(stringifyElement(results[2].element)).toEqual('<span template=""></span>');
|
||||
expect(results[2].element).toBe(originalChild);
|
||||
.toEqual('<template><template class="ng-binding"></template></template>');
|
||||
expect(stringifyElement(results[2].element))
|
||||
.toEqual('<template><span template=""></span></template>');
|
||||
expect(DOM.firstChild(DOM.content(results[2].element))).toBe(originalChild);
|
||||
});
|
||||
|
||||
it('should work with top-level template node', () => {
|
||||
var rootElement = el('<template><div template>x</div></template>');
|
||||
var rootElement = DOM.createTemplate('<div template>x</div>');
|
||||
var originalChild = DOM.content(rootElement).childNodes[0];
|
||||
var results = createPipeline().process(rootElement);
|
||||
|
||||
@@ -98,17 +99,17 @@ export function main() {
|
||||
expect(results[2].isViewRoot).toBe(true);
|
||||
expect(stringifyElement(results[0].element))
|
||||
.toEqual('<template><template class="ng-binding"></template></template>');
|
||||
expect(results[2].element).toBe(originalChild);
|
||||
expect(DOM.firstChild(DOM.content(results[2].element))).toBe(originalChild);
|
||||
});
|
||||
|
||||
it('should mark the element as viewRoot', () => {
|
||||
var rootElement = el('<div><div template></div></div>');
|
||||
var rootElement = DOM.createTemplate('<div template></div>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
expect(results[2].isViewRoot).toBe(true);
|
||||
});
|
||||
|
||||
it('should add property bindings from the template attribute', () => {
|
||||
var rootElement = el('<div><div template="some-prop:expr"></div></div>');
|
||||
var rootElement = DOM.createTemplate('<div template="some-prop:expr"></div>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
expect(results[1].inheritedElementBinder.propertyBindings.get('someProp').source)
|
||||
.toEqual('expr');
|
||||
@@ -116,14 +117,14 @@ export function main() {
|
||||
});
|
||||
|
||||
it('should add variable mappings from the template attribute to the nestedProtoView', () => {
|
||||
var rootElement = el('<div><div template="var var-name=mapName"></div></div>');
|
||||
var rootElement = DOM.createTemplate('<div template="var var-name=mapName"></div>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
expect(results[2].inheritedProtoView.variableBindings)
|
||||
.toEqual(MapWrapper.createFromStringMap({'mapName': 'varName'}));
|
||||
});
|
||||
|
||||
it('should add entries without value as attributes to the element', () => {
|
||||
var rootElement = el('<div><div template="varname"></div></div>');
|
||||
var rootElement = DOM.createTemplate('<div template="varname"></div>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
expect(results[1].attrs().get('varname')).toEqual('');
|
||||
expect(results[1].inheritedElementBinder.propertyBindings).toEqual(new Map());
|
||||
@@ -131,32 +132,32 @@ export function main() {
|
||||
});
|
||||
|
||||
it('should iterate properly after a template dom modification', () => {
|
||||
var rootElement = el('<div><div template></div><after></after></div>');
|
||||
var rootElement = DOM.createTemplate('<div template></div><after></after>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
// 1 root + 2 initial + 1 generated template elements
|
||||
expect(results.length).toEqual(4);
|
||||
// 1 root + 2 initial + 2 generated template elements
|
||||
expect(results.length).toEqual(5);
|
||||
});
|
||||
|
||||
it('should copy over the elementDescription', () => {
|
||||
var rootElement = el('<div><span template=""></span></div>');
|
||||
var rootElement = DOM.createTemplate('<span template=""></span>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
expect(results[2].elementDescription).toBe(results[1].elementDescription);
|
||||
});
|
||||
|
||||
it('should clean out the inheritedElementBinder', () => {
|
||||
var rootElement = el('<div><span template=""></span></div>');
|
||||
var rootElement = DOM.createTemplate('<span template=""></span>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
expect(results[2].inheritedElementBinder).toBe(null);
|
||||
});
|
||||
|
||||
it('should create a nestedProtoView', () => {
|
||||
var rootElement = el('<div><span template=""></span></div>');
|
||||
var rootElement = DOM.createTemplate('<span template=""></span>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
expect(results[2].inheritedProtoView).not.toBe(null);
|
||||
expect(results[2].inheritedProtoView)
|
||||
.toBe(results[1].inheritedElementBinder.nestedProtoView);
|
||||
expect(stringifyElement(results[2].inheritedProtoView.rootElement))
|
||||
.toEqual('<span template=""></span>');
|
||||
.toEqual('<template><span template=""></span></template>');
|
||||
});
|
||||
|
||||
});
|
||||
@@ -164,24 +165,25 @@ export function main() {
|
||||
describe('elements with *directive_name attribute', () => {
|
||||
|
||||
it('should replace the element with an empty <template> element', () => {
|
||||
var rootElement = el('<div><span *ng-if></span></div>');
|
||||
var originalChild = rootElement.childNodes[0];
|
||||
var rootElement = DOM.createTemplate('<span *ng-if></span>');
|
||||
var originalChild = DOM.firstChild(DOM.content(rootElement));
|
||||
var results = createPipeline().process(rootElement);
|
||||
expect(results[0].element).toBe(rootElement);
|
||||
expect(stringifyElement(results[0].element))
|
||||
.toEqual('<div><template class="ng-binding" ng-if=""></template></div>');
|
||||
expect(stringifyElement(results[2].element)).toEqual('<span *ng-if=""></span>');
|
||||
expect(results[2].element).toBe(originalChild);
|
||||
.toEqual('<template><template class="ng-binding" ng-if=""></template></template>');
|
||||
expect(stringifyElement(results[2].element))
|
||||
.toEqual('<template><span *ng-if=""></span></template>');
|
||||
expect(DOM.firstChild(DOM.content(results[2].element))).toBe(originalChild);
|
||||
});
|
||||
|
||||
it('should mark the element as viewRoot', () => {
|
||||
var rootElement = el('<div><div *foo="bar"></div></div>');
|
||||
var rootElement = DOM.createTemplate('<div *foo="bar"></div>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
expect(results[2].isViewRoot).toBe(true);
|
||||
});
|
||||
|
||||
it('should work with top-level template node', () => {
|
||||
var rootElement = el('<template><div *foo>x</div></template>');
|
||||
var rootElement = DOM.createTemplate('<div *foo>x</div>');
|
||||
var originalChild = DOM.content(rootElement).childNodes[0];
|
||||
var results = createPipeline().process(rootElement);
|
||||
|
||||
@@ -190,11 +192,11 @@ export function main() {
|
||||
expect(results[2].isViewRoot).toBe(true);
|
||||
expect(stringifyElement(results[0].element))
|
||||
.toEqual('<template><template class="ng-binding" foo=""></template></template>');
|
||||
expect(results[2].element).toBe(originalChild);
|
||||
expect(DOM.firstChild(DOM.content(results[2].element))).toBe(originalChild);
|
||||
});
|
||||
|
||||
it('should add property bindings from the template attribute', () => {
|
||||
var rootElement = el('<div><div *prop="expr"></div></div>');
|
||||
var rootElement = DOM.createTemplate('<div *prop="expr"></div>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
expect(results[1].inheritedElementBinder.propertyBindings.get('prop').source)
|
||||
.toEqual('expr');
|
||||
@@ -202,14 +204,14 @@ export function main() {
|
||||
});
|
||||
|
||||
it('should add variable mappings from the template attribute to the nestedProtoView', () => {
|
||||
var rootElement = el('<div><div *foreach="var varName=mapName"></div></div>');
|
||||
var rootElement = DOM.createTemplate('<div *foreach="var varName=mapName"></div>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
expect(results[2].inheritedProtoView.variableBindings)
|
||||
.toEqual(MapWrapper.createFromStringMap({'mapName': 'varName'}));
|
||||
});
|
||||
|
||||
it('should add entries without value as attribute to the element', () => {
|
||||
var rootElement = el('<div><div *varname></div></div>');
|
||||
var rootElement = DOM.createTemplate('<div *varname></div>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
expect(results[1].attrs().get('varname')).toEqual('');
|
||||
expect(results[1].inheritedElementBinder.propertyBindings).toEqual(new Map());
|
||||
@@ -217,32 +219,32 @@ export function main() {
|
||||
});
|
||||
|
||||
it('should iterate properly after a template dom modification', () => {
|
||||
var rootElement = el('<div><div *foo></div><after></after></div>');
|
||||
var rootElement = DOM.createTemplate('<div *foo></div><after></after>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
// 1 root + 2 initial + 1 generated template elements
|
||||
expect(results.length).toEqual(4);
|
||||
// 1 root + 2 initial + 2 generated template elements
|
||||
expect(results.length).toEqual(5);
|
||||
});
|
||||
|
||||
it('should copy over the elementDescription', () => {
|
||||
var rootElement = el('<div><span *foo></span></div>');
|
||||
var rootElement = DOM.createTemplate('<span *foo></span>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
expect(results[2].elementDescription).toBe(results[1].elementDescription);
|
||||
});
|
||||
|
||||
it('should clean out the inheritedElementBinder', () => {
|
||||
var rootElement = el('<div><span *foo></span></div>');
|
||||
var rootElement = DOM.createTemplate('<span *foo></span>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
expect(results[2].inheritedElementBinder).toBe(null);
|
||||
});
|
||||
|
||||
it('should create a nestedProtoView', () => {
|
||||
var rootElement = el('<div><span *foo></span></div>');
|
||||
var rootElement = DOM.createTemplate('<span *foo></span>');
|
||||
var results = createPipeline().process(rootElement);
|
||||
expect(results[2].inheritedProtoView).not.toBe(null);
|
||||
expect(results[2].inheritedProtoView)
|
||||
.toBe(results[1].inheritedElementBinder.nestedProtoView);
|
||||
expect(stringifyElement(results[2].inheritedProtoView.rootElement))
|
||||
.toEqual('<span *foo=""></span>');
|
||||
.toEqual('<template><span *foo=""></span></template>');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -16,10 +16,11 @@ import {
|
||||
import {MapWrapper} from 'angular2/src/facade/collection';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
import {DomTestbed, TestView, elRef} from './dom_testbed';
|
||||
import {DomTestbed, TestRootView, elRef} from './dom_testbed';
|
||||
|
||||
import {ViewDefinition, DirectiveMetadata, RenderViewRef} from 'angular2/src/render/api';
|
||||
import {DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES} from 'angular2/src/render/dom/dom_renderer';
|
||||
import {ShadowDomStrategy, NativeShadowDomStrategy} from 'angular2/render';
|
||||
import {bind} from 'angular2/di';
|
||||
|
||||
export function main() {
|
||||
@@ -27,101 +28,69 @@ export function main() {
|
||||
beforeEachBindings(() => [DomTestbed]);
|
||||
|
||||
it('should create and destroy root host views while using the given elements in place',
|
||||
inject([AsyncTestCompleter, DomTestbed], (async, tb) => {
|
||||
inject([AsyncTestCompleter, DomTestbed], (async, tb: DomTestbed) => {
|
||||
tb.compiler.compileHost(someComponent)
|
||||
.then((hostProtoViewDto) => {
|
||||
var view =
|
||||
new TestView(tb.renderer.createRootHostView(hostProtoViewDto.render, '#root'));
|
||||
expect(view.rawView.rootNodes[0]).toEqual(tb.rootEl);
|
||||
|
||||
tb.renderer.destroyView(view.viewRef);
|
||||
// destroying a root view should not disconnect it!
|
||||
var view = new TestRootView(
|
||||
tb.renderer.createRootHostView(hostProtoViewDto.render, 0, '#root'));
|
||||
expect(tb.rootEl.parentNode).toBeTruthy();
|
||||
expect(view.hostElement).toEqual(tb.rootEl);
|
||||
|
||||
tb.renderer.detachFragment(view.fragments[0]);
|
||||
tb.renderer.destroyView(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: DomTestbed) => {
|
||||
tb.compileAndMerge(
|
||||
someComponent,
|
||||
[
|
||||
new ViewDefinition(
|
||||
{componentId: 'someComponent', template: '{{a}}', directives: []})
|
||||
])
|
||||
.then((protoViewMergeMappings) => {
|
||||
var rootView = tb.createView(protoViewMergeMappings[0]);
|
||||
|
||||
it('should not create LightDom instances if the host element is empty',
|
||||
inject([AsyncTestCompleter, DomTestbed], (async, tb) => {
|
||||
tb.compileAll([
|
||||
someComponent,
|
||||
new ViewDefinition({
|
||||
componentId: 'someComponent',
|
||||
template: '<some-comp> <!-- comment -->\n </some-comp>',
|
||||
directives: [someComponent]
|
||||
})
|
||||
])
|
||||
.then((protoViewDtos) => {
|
||||
var rootView = tb.createRootView(protoViewDtos[0]);
|
||||
var cmpView = tb.createComponentView(rootView.viewRef, 0, protoViewDtos[1]);
|
||||
expect(cmpView.rawView.proto.elementBinders[0].componentId).toBe('someComponent');
|
||||
expect(cmpView.rawView.boundElements[0].lightDom).toBe(null);
|
||||
|
||||
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');
|
||||
tb.renderer.setText(rootView.viewRef, 0, 'hello');
|
||||
expect(rootView.hostElement).toHaveText('hello');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should update any element property/attributes/class/style independent of the compilation',
|
||||
inject([AsyncTestCompleter, DomTestbed], (async, tb) => {
|
||||
tb.compileAll([
|
||||
someComponent,
|
||||
new ViewDefinition({
|
||||
componentId: 'someComponent',
|
||||
template: '<input [title]="y" style="position:absolute">',
|
||||
directives: []
|
||||
})
|
||||
])
|
||||
.then((protoViewDtos) => {
|
||||
var rootView = tb.createRootView(protoViewDtos[0]);
|
||||
var cmpView = tb.createComponentView(rootView.viewRef, 0, protoViewDtos[1]);
|
||||
inject([AsyncTestCompleter, DomTestbed], (async, tb: DomTestbed) => {
|
||||
tb.compileAndMerge(someComponent,
|
||||
[
|
||||
new ViewDefinition({
|
||||
componentId: 'someComponent',
|
||||
template: '<input [title]="y" style="position:absolute">',
|
||||
directives: []
|
||||
})
|
||||
])
|
||||
.then((protoViewMergeMappings) => {
|
||||
var rootView = tb.createView(protoViewMergeMappings[0]);
|
||||
|
||||
var el = DOM.childNodes(tb.rootEl)[0];
|
||||
tb.renderer.setElementProperty(elRef(cmpView.viewRef, 0), 'value', 'hello');
|
||||
var elr = elRef(rootView.viewRef, 1);
|
||||
var el = DOM.childNodes(rootView.hostElement)[0];
|
||||
tb.renderer.setElementProperty(elr, 'value', 'hello');
|
||||
expect((<HTMLInputElement>el).value).toEqual('hello');
|
||||
|
||||
tb.renderer.setElementClass(elRef(cmpView.viewRef, 0), 'a', true);
|
||||
expect((<HTMLInputElement>DOM.childNodes(tb.rootEl)[0]).value).toEqual('hello');
|
||||
tb.renderer.setElementClass(elRef(cmpView.viewRef, 0), 'a', false);
|
||||
tb.renderer.setElementClass(elr, 'a', true);
|
||||
expect((<HTMLInputElement>DOM.childNodes(rootView.hostElement)[0]).value)
|
||||
.toEqual('hello');
|
||||
tb.renderer.setElementClass(elr, 'a', false);
|
||||
expect(DOM.hasClass(el, 'a')).toBe(false);
|
||||
|
||||
tb.renderer.setElementStyle(elRef(cmpView.viewRef, 0), 'width', '10px');
|
||||
tb.renderer.setElementStyle(elr, 'width', '10px');
|
||||
expect(DOM.getStyle(el, 'width')).toEqual('10px');
|
||||
tb.renderer.setElementStyle(elRef(cmpView.viewRef, 0), 'width', null);
|
||||
tb.renderer.setElementStyle(elr, 'width', null);
|
||||
expect(DOM.getStyle(el, 'width')).toEqual('');
|
||||
|
||||
tb.renderer.setElementAttribute(elRef(cmpView.viewRef, 0), 'someAttr', 'someValue');
|
||||
tb.renderer.setElementAttribute(elr, 'someAttr', 'someValue');
|
||||
expect(DOM.getAttribute(el, 'some-attr')).toEqual('someValue');
|
||||
|
||||
async.done();
|
||||
@@ -131,16 +100,18 @@ export function main() {
|
||||
|
||||
it('should NOT reflect property values as attributes if flag is NOT set',
|
||||
inject([AsyncTestCompleter, DomTestbed], (async, tb) => {
|
||||
tb.compileAll([
|
||||
someComponent,
|
||||
new ViewDefinition(
|
||||
{componentId: 'someComponent', template: '<input [title]="y">', directives: []})
|
||||
])
|
||||
.then((protoViewDtos) => {
|
||||
var rootView = tb.createRootView(protoViewDtos[0]);
|
||||
var cmpView = tb.createComponentView(rootView.viewRef, 0, protoViewDtos[1]);
|
||||
var el = DOM.childNodes(tb.rootEl)[0];
|
||||
tb.renderer.setElementProperty(elRef(cmpView.viewRef, 0), 'maxLength', '20');
|
||||
tb.compileAndMerge(someComponent,
|
||||
[
|
||||
new ViewDefinition({
|
||||
componentId: 'someComponent',
|
||||
template: '<input [title]="y">',
|
||||
directives: []
|
||||
})
|
||||
])
|
||||
.then((protoViewMergeMappings) => {
|
||||
var rootView = tb.createView(protoViewMergeMappings[0]);
|
||||
var el = DOM.childNodes(rootView.hostElement)[0];
|
||||
tb.renderer.setElementProperty(elRef(rootView.viewRef, 1), 'maxLength', '20');
|
||||
expect(DOM.getAttribute(<HTMLInputElement>el, 'ng-reflect-max-length'))
|
||||
.toEqual(null);
|
||||
|
||||
@@ -150,21 +121,21 @@ export function main() {
|
||||
|
||||
describe('reflection', () => {
|
||||
beforeEachBindings(() => [bind(DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES).toValue(true)]);
|
||||
|
||||
it('should reflect property values as attributes if flag is set',
|
||||
inject([AsyncTestCompleter, DomTestbed], (async, tb) => {
|
||||
tb.compileAll([
|
||||
someComponent,
|
||||
new ViewDefinition({
|
||||
componentId: 'someComponent',
|
||||
template: '<input [title]="y">',
|
||||
directives: []
|
||||
})
|
||||
])
|
||||
.then((protoViewDtos) => {
|
||||
var rootView = tb.createRootView(protoViewDtos[0]);
|
||||
var cmpView = tb.createComponentView(rootView.viewRef, 0, protoViewDtos[1]);
|
||||
var el = DOM.childNodes(tb.rootEl)[0];
|
||||
tb.renderer.setElementProperty(elRef(cmpView.viewRef, 0), 'maxLength', '20');
|
||||
tb.compileAndMerge(someComponent,
|
||||
[
|
||||
new ViewDefinition({
|
||||
componentId: 'someComponent',
|
||||
template: '<input [title]="y">',
|
||||
directives: []
|
||||
})
|
||||
])
|
||||
.then((protoViewMergeMappings) => {
|
||||
var rootView = tb.createView(protoViewMergeMappings[0]);
|
||||
var el = DOM.childNodes(rootView.hostElement)[0];
|
||||
tb.renderer.setElementProperty(elRef(rootView.viewRef, 1), 'maxLength', '20');
|
||||
expect(DOM.getAttribute(<HTMLInputElement>el, 'ng-reflect-max-length'))
|
||||
.toEqual('20');
|
||||
async.done();
|
||||
@@ -174,70 +145,69 @@ export function main() {
|
||||
|
||||
if (DOM.supportsDOMEvents()) {
|
||||
it('should call actions on the element independent of the compilation',
|
||||
inject([AsyncTestCompleter, DomTestbed], (async, tb) => {
|
||||
tb.compileAll([
|
||||
someComponent,
|
||||
new ViewDefinition({
|
||||
componentId: 'someComponent',
|
||||
template: '<input [title]="y"></input>',
|
||||
directives: []
|
||||
})
|
||||
])
|
||||
.then((protoViewDtos) => {
|
||||
var views = tb.createRootViews(protoViewDtos);
|
||||
var componentView = views[1];
|
||||
inject([AsyncTestCompleter, DomTestbed], (async, tb: DomTestbed) => {
|
||||
tb.compileAndMerge(someComponent,
|
||||
[
|
||||
new ViewDefinition({
|
||||
componentId: 'someComponent',
|
||||
template: '<input [title]="y"></input>',
|
||||
directives: []
|
||||
})
|
||||
])
|
||||
.then((protoViewMergeMappings) => {
|
||||
var rootView = tb.createView(protoViewMergeMappings[0]);
|
||||
|
||||
tb.renderer.invokeElementMethod(elRef(componentView.viewRef, 0), 'setAttribute',
|
||||
tb.renderer.invokeElementMethod(elRef(rootView.viewRef, 1), 'setAttribute',
|
||||
['a', 'b']);
|
||||
|
||||
expect(DOM.getAttribute(DOM.childNodes(tb.rootEl)[0], 'a')).toEqual('b');
|
||||
expect(DOM.getAttribute(DOM.childNodes(rootView.hostElement)[0], 'a'))
|
||||
.toEqual('b');
|
||||
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]);
|
||||
it('should add and remove fragments',
|
||||
inject([AsyncTestCompleter, DomTestbed], (async, tb: DomTestbed) => {
|
||||
tb.compileAndMerge(someComponent,
|
||||
[
|
||||
new ViewDefinition({
|
||||
componentId: 'someComponent',
|
||||
template: '<template>hello</template>',
|
||||
directives: []
|
||||
})
|
||||
])
|
||||
.then((protoViewMergeMappings) => {
|
||||
var rootView = tb.createView(protoViewMergeMappings[0]);
|
||||
|
||||
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('');
|
||||
var elr = elRef(rootView.viewRef, 1);
|
||||
expect(rootView.hostElement).toHaveText('');
|
||||
var fragment = rootView.fragments[1];
|
||||
tb.renderer.attachFragmentAfterElement(elr, fragment);
|
||||
expect(rootView.hostElement).toHaveText('hello');
|
||||
tb.renderer.detachFragment(fragment);
|
||||
expect(rootView.hostElement).toHaveText('');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should handle events', inject([AsyncTestCompleter, DomTestbed], (async, tb: DomTestbed) => {
|
||||
tb.compileAll([
|
||||
someComponent,
|
||||
new ViewDefinition({
|
||||
componentId: 'someComponent',
|
||||
template: '<input (change)="doSomething()">',
|
||||
directives: []
|
||||
})
|
||||
])
|
||||
tb.compileAndMerge(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]);
|
||||
var rootView = tb.createView(protoViewDtos[0]);
|
||||
|
||||
tb.triggerEvent(cmpView.viewRef, 0, 'change');
|
||||
var eventEntry = cmpView.events[0];
|
||||
tb.triggerEvent(elRef(rootView.viewRef, 1), 'change');
|
||||
var eventEntry = rootView.events[0];
|
||||
// bound element index
|
||||
expect(eventEntry[0]).toEqual(0);
|
||||
expect(eventEntry[0]).toEqual(1);
|
||||
// event type
|
||||
expect(eventEntry[1]).toEqual('change');
|
||||
// actual event
|
||||
@@ -247,6 +217,30 @@ export function main() {
|
||||
|
||||
}));
|
||||
|
||||
if (DOM.supportsNativeShadowDOM()) {
|
||||
describe('native shadow dom support', () => {
|
||||
beforeEachBindings(
|
||||
() => { return [bind(ShadowDomStrategy).toValue(new NativeShadowDomStrategy())]; });
|
||||
|
||||
it('should support shadow dom components',
|
||||
inject([AsyncTestCompleter, DomTestbed], (async, tb: DomTestbed) => {
|
||||
tb.compileAndMerge(
|
||||
someComponent,
|
||||
[
|
||||
new ViewDefinition(
|
||||
{componentId: 'someComponent', template: 'hello', directives: []})
|
||||
])
|
||||
.then((protoViewMergeMappings) => {
|
||||
var rootView = tb.createView(protoViewMergeMappings[0]);
|
||||
expect(DOM.getShadowRoot(rootView.hostElement)).toHaveText('hello');
|
||||
async.done();
|
||||
});
|
||||
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,48 +1,57 @@
|
||||
import {Inject, Injectable} from 'angular2/di';
|
||||
import {isPresent} 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 {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 {
|
||||
RenderViewWithFragments,
|
||||
RenderFragmentRef,
|
||||
RenderViewRef,
|
||||
ProtoViewDto,
|
||||
ViewDefinition,
|
||||
EventDispatcher,
|
||||
RenderEventDispatcher,
|
||||
DirectiveMetadata,
|
||||
RenderElementRef
|
||||
RenderElementRef,
|
||||
RenderProtoViewMergeMapping,
|
||||
RenderProtoViewRef
|
||||
} from 'angular2/src/render/api';
|
||||
import {resolveInternalDomView} from 'angular2/src/render/dom/view/view';
|
||||
import {resolveInternalDomFragment} from 'angular2/src/render/dom/view/fragment';
|
||||
import {el, dispatchEvent} from 'angular2/test_lib';
|
||||
|
||||
export class TestView {
|
||||
rawView: DomView;
|
||||
export class TestRootView {
|
||||
viewRef: RenderViewRef;
|
||||
fragments: RenderFragmentRef[];
|
||||
hostElement: Element;
|
||||
events: List<List<any>>;
|
||||
|
||||
constructor(viewRef: RenderViewRef) {
|
||||
this.viewRef = viewRef;
|
||||
this.rawView = resolveInternalDomView(viewRef);
|
||||
constructor(viewWithFragments: RenderViewWithFragments) {
|
||||
this.viewRef = viewWithFragments.viewRef;
|
||||
this.fragments = viewWithFragments.fragmentRefs;
|
||||
this.hostElement = <Element>resolveInternalDomFragment(this.fragments[0])[0];
|
||||
this.events = [];
|
||||
}
|
||||
}
|
||||
|
||||
export class TestRenderElementRef implements RenderElementRef {
|
||||
constructor(public renderView: RenderViewRef, public renderBoundElementIndex: number) {}
|
||||
}
|
||||
|
||||
export function elRef(renderView: RenderViewRef, boundElementIndex: number) {
|
||||
return new TestRenderElementRef(renderView, boundElementIndex);
|
||||
}
|
||||
|
||||
class TestRenderElementRef implements RenderElementRef {
|
||||
constructor(public renderView: RenderViewRef, public boundElementIndex: number) {}
|
||||
}
|
||||
export function rootNodes(view: RenderViewRef) {}
|
||||
|
||||
class LoggingEventDispatcher implements EventDispatcher {
|
||||
class LoggingEventDispatcher implements RenderEventDispatcher {
|
||||
log: List<List<any>>;
|
||||
|
||||
constructor(log: List<List<any>>) { this.log = log; }
|
||||
|
||||
dispatchEvent(elementIndex: number, eventName: string, locals: Map<string, any>) {
|
||||
dispatchRenderEvent(elementIndex: number, eventName: string, locals: Map<string, any>) {
|
||||
this.log.push([elementIndex, eventName, locals]);
|
||||
return true;
|
||||
}
|
||||
@@ -67,71 +76,62 @@ export class DomTestbed {
|
||||
DOM.appendChild(DOM.querySelector(document, 'body'), this.rootEl);
|
||||
}
|
||||
|
||||
compileAll(directivesOrViewDefinitions:
|
||||
List<DirectiveMetadata | ViewDefinition>): 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);
|
||||
}
|
||||
}));
|
||||
compile(host: DirectiveMetadata, componentViews: ViewDefinition[]): Promise<ProtoViewDto[]> {
|
||||
var promises = [this.compiler.compileHost(host)];
|
||||
componentViews.forEach(view => promises.push(this.compiler.compile(view)));
|
||||
return PromiseWrapper.all(promises);
|
||||
}
|
||||
|
||||
_createTestView(viewRef: RenderViewRef) {
|
||||
var testView = new TestView(viewRef);
|
||||
this.renderer.setEventDispatcher(viewRef, new LoggingEventDispatcher(testView.events));
|
||||
merge(protoViews:
|
||||
List<ProtoViewDto | RenderProtoViewRef>): Promise<RenderProtoViewMergeMapping[]> {
|
||||
return this.compiler.mergeProtoViewsRecursively(collectMergeRenderProtoViewsRecurse(
|
||||
<ProtoViewDto>protoViews[0], ListWrapper.slice(protoViews, 1)));
|
||||
}
|
||||
|
||||
compileAndMerge(host: DirectiveMetadata,
|
||||
componentViews: ViewDefinition[]): Promise<RenderProtoViewMergeMapping[]> {
|
||||
return this.compile(host, componentViews).then(protoViewDtos => this.merge(protoViewDtos));
|
||||
}
|
||||
|
||||
_createTestView(viewWithFragments: RenderViewWithFragments) {
|
||||
var testView = new TestRootView(viewWithFragments);
|
||||
this.renderer.setEventDispatcher(viewWithFragments.viewRef,
|
||||
new LoggingEventDispatcher(testView.events));
|
||||
return testView;
|
||||
}
|
||||
|
||||
createRootView(rootProtoView: ProtoViewDto): TestView {
|
||||
var viewRef = this.renderer.createRootHostView(rootProtoView.render, '#root');
|
||||
this.renderer.hydrateView(viewRef);
|
||||
return this._createTestView(viewRef);
|
||||
createView(protoView: RenderProtoViewMergeMapping): TestRootView {
|
||||
var viewWithFragments = this.renderer.createView(protoView.mergedProtoViewRef, 0);
|
||||
this.renderer.hydrateView(viewWithFragments.viewRef);
|
||||
return this._createTestView(viewWithFragments);
|
||||
}
|
||||
|
||||
createComponentView(parentViewRef: RenderViewRef, boundElementIndex: number,
|
||||
componentProtoView: ProtoViewDto): TestView {
|
||||
var componentViewRef = this.renderer.createView(componentProtoView.render);
|
||||
this.renderer.attachComponentView(elRef(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]);
|
||||
views.push(lastView);
|
||||
for (var i = 1; i < protoViews.length; i++) {
|
||||
lastView = this.createComponentView(lastView.viewRef, 0, protoViews[i]);
|
||||
views.push(lastView);
|
||||
}
|
||||
return views;
|
||||
}
|
||||
|
||||
destroyComponentView(parentViewRef: RenderViewRef, boundElementIndex: number,
|
||||
componentView: RenderViewRef) {
|
||||
this.renderer.dehydrateView(componentView);
|
||||
this.renderer.detachComponentView(elRef(parentViewRef, boundElementIndex), componentView);
|
||||
}
|
||||
|
||||
createViewInContainer(parentViewRef: RenderViewRef, boundElementIndex: number, atIndex: number,
|
||||
protoView: ProtoViewDto): TestView {
|
||||
var viewRef = this.renderer.createView(protoView.render);
|
||||
this.renderer.attachViewInContainer(elRef(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(elRef(parentViewRef, boundElementIndex), atIndex, viewRef);
|
||||
this.renderer.destroyView(viewRef);
|
||||
}
|
||||
|
||||
triggerEvent(viewRef: RenderViewRef, boundElementIndex: number, eventName: string) {
|
||||
var element = resolveInternalDomView(viewRef).boundElements[boundElementIndex].element;
|
||||
triggerEvent(elementRef: RenderElementRef, eventName: string) {
|
||||
var element = resolveInternalDomView(elementRef.renderView)
|
||||
.boundElements[elementRef.renderBoundElementIndex];
|
||||
dispatchEvent(element, eventName);
|
||||
}
|
||||
}
|
||||
|
||||
function collectMergeRenderProtoViewsRecurse(current: ProtoViewDto,
|
||||
components: List<ProtoViewDto | RenderProtoViewRef>):
|
||||
List<RenderProtoViewRef | List<any>> {
|
||||
var result = [current.render];
|
||||
current.elementBinders.forEach((elementBinder) => {
|
||||
if (isPresent(elementBinder.nestedProtoView)) {
|
||||
result.push(collectMergeRenderProtoViewsRecurse(elementBinder.nestedProtoView, components));
|
||||
} else if (elementBinder.directives.length > 0) {
|
||||
if (components.length > 0) {
|
||||
var comp = components.shift();
|
||||
if (comp instanceof ProtoViewDto) {
|
||||
result.push(collectMergeRenderProtoViewsRecurse(comp, components));
|
||||
} else {
|
||||
result.push(comp);
|
||||
}
|
||||
} else {
|
||||
result.push(null);
|
||||
}
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
import {
|
||||
describe,
|
||||
beforeEach,
|
||||
it,
|
||||
expect,
|
||||
ddescribe,
|
||||
iit,
|
||||
SpyObject,
|
||||
el,
|
||||
proxy
|
||||
} from 'angular2/test_lib';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
import {Content} from 'angular2/src/render/dom/shadow_dom/content_tag';
|
||||
|
||||
var _scriptStart = `<script start=""></script>`;
|
||||
var _scriptEnd = `<script end=""></script>`;
|
||||
|
||||
export function main() {
|
||||
describe('Content', function() {
|
||||
var parent;
|
||||
var content;
|
||||
|
||||
beforeEach(() => {
|
||||
parent = el(`<div>${_scriptStart}${_scriptEnd}`);
|
||||
content = DOM.firstChild(parent);
|
||||
});
|
||||
|
||||
it("should insert the nodes", () => {
|
||||
var c = new Content(content, '');
|
||||
c.init(null);
|
||||
c.insert([el("<a></a>"), el("<b></b>")])
|
||||
|
||||
expect(DOM.getInnerHTML(parent))
|
||||
.toEqual(`${_scriptStart}<a></a><b></b>${_scriptEnd}`);
|
||||
});
|
||||
|
||||
it("should remove the nodes from the previous insertion", () => {
|
||||
var c = new Content(content, '');
|
||||
c.init(null);
|
||||
c.insert([el("<a></a>")]);
|
||||
c.insert([el("<b></b>")]);
|
||||
|
||||
expect(DOM.getInnerHTML(parent)).toEqual(`${_scriptStart}<b></b>${_scriptEnd}`);
|
||||
});
|
||||
|
||||
it("should insert empty list", () => {
|
||||
var c = new Content(content, '');
|
||||
c.init(null);
|
||||
c.insert([el("<a></a>")]);
|
||||
c.insert([]);
|
||||
|
||||
expect(DOM.getInnerHTML(parent)).toEqual(`${_scriptStart}${_scriptEnd}`);
|
||||
});
|
||||
});
|
||||
}
|
||||
+2
-4
@@ -32,10 +32,8 @@ export function main() {
|
||||
resetShadowDomCache();
|
||||
});
|
||||
|
||||
it('should use the host element as shadow root', () => {
|
||||
var host = el('<div><span>original content</span></div>');
|
||||
expect(strategy.prepareShadowRoot(host)).toBe(host);
|
||||
});
|
||||
it('should report that this is not the native strategy',
|
||||
() => { expect(strategy.hasNativeContentElement()).toBe(false); });
|
||||
|
||||
it('should scope styles', () => {
|
||||
var styleElement = el('<style>.foo {} :host {}</style>');
|
||||
|
||||
+2
-4
@@ -34,10 +34,8 @@ export function main() {
|
||||
resetShadowDomCache();
|
||||
});
|
||||
|
||||
it('should use the host element as shadow root', () => {
|
||||
var host = el('<div><span>original content</span></div>');
|
||||
expect(strategy.prepareShadowRoot(host)).toBe(host);
|
||||
});
|
||||
it('should report that this is not the native strategy',
|
||||
() => { expect(strategy.hasNativeContentElement()).toBe(false); });
|
||||
|
||||
it('should move the style element to the style host', () => {
|
||||
var compileElement = el('<div><style>.one {}</style></div>');
|
||||
|
||||
@@ -1,253 +0,0 @@
|
||||
import {
|
||||
describe,
|
||||
beforeEach,
|
||||
it,
|
||||
expect,
|
||||
ddescribe,
|
||||
iit,
|
||||
SpyObject,
|
||||
el,
|
||||
proxy,
|
||||
stringifyElement
|
||||
} from 'angular2/test_lib';
|
||||
import {IMPLEMENTS, isBlank, isPresent} from 'angular2/src/facade/lang';
|
||||
import {ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
|
||||
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 {DomProtoView} from 'angular2/src/render/dom/view/proto_view';
|
||||
import {DomViewContainer} from 'angular2/src/render/dom/view/view_container';
|
||||
import {DomElement} from 'angular2/src/render/dom/view/element';
|
||||
|
||||
@proxy
|
||||
@IMPLEMENTS(DomProtoView)
|
||||
class FakeProtoView extends SpyObject {
|
||||
constructor(public transitiveContentTagCount: number) { super(DomProtoView); }
|
||||
|
||||
noSuchMethod(i) { super.noSuchMethod(i); }
|
||||
}
|
||||
|
||||
@proxy
|
||||
@IMPLEMENTS(DomView)
|
||||
class FakeView extends SpyObject {
|
||||
boundElements: any[];
|
||||
proto;
|
||||
|
||||
constructor(containers = null, transitiveContentTagCount: number = 1) {
|
||||
super(DomView);
|
||||
this.proto = new FakeProtoView(transitiveContentTagCount);
|
||||
this.boundElements = [];
|
||||
if (isPresent(containers)) {
|
||||
ListWrapper.forEach(containers, (c) => {
|
||||
var element = null;
|
||||
var contentTag = null;
|
||||
var vc = null;
|
||||
if (c instanceof FakeContentTag) {
|
||||
contentTag = c;
|
||||
element = c.contentStartElement;
|
||||
}
|
||||
if (c instanceof FakeViewContainer) {
|
||||
vc = c;
|
||||
element = c.templateElement;
|
||||
}
|
||||
var boundElement = new DomElement(null, element, contentTag);
|
||||
boundElement.viewContainer = vc;
|
||||
this.boundElements.push(boundElement);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
noSuchMethod(i) { super.noSuchMethod(i); }
|
||||
}
|
||||
|
||||
@proxy
|
||||
@IMPLEMENTS(DomViewContainer)
|
||||
class FakeViewContainer extends SpyObject {
|
||||
_nodes;
|
||||
_contentTagContainers;
|
||||
templateElement;
|
||||
|
||||
constructor(templateEl, nodes = null, views = null) {
|
||||
super(DomViewContainer);
|
||||
this.templateElement = templateEl;
|
||||
this._nodes = nodes;
|
||||
this._contentTagContainers = views;
|
||||
}
|
||||
|
||||
nodes() { return this._nodes; }
|
||||
|
||||
contentTagContainers() { return this._contentTagContainers; }
|
||||
|
||||
noSuchMethod(i) { super.noSuchMethod(i); }
|
||||
}
|
||||
|
||||
|
||||
@proxy
|
||||
@IMPLEMENTS(Content)
|
||||
class FakeContentTag extends SpyObject {
|
||||
select;
|
||||
_nodes;
|
||||
contentStartElement;
|
||||
|
||||
constructor(contentEl, select = '', nodes = null) {
|
||||
super(Content);
|
||||
this.contentStartElement = contentEl;
|
||||
this.select = select;
|
||||
this._nodes = nodes;
|
||||
}
|
||||
|
||||
insert(nodes) { this._nodes = nodes; }
|
||||
|
||||
nodes() { return this._nodes; }
|
||||
|
||||
noSuchMethod(i) { super.noSuchMethod(i); }
|
||||
}
|
||||
|
||||
function createLightDom(hostView, shadowView, el) {
|
||||
var lightDom = new LightDom(hostView, el);
|
||||
lightDom.attachShadowDomView(shadowView);
|
||||
return lightDom;
|
||||
}
|
||||
|
||||
export function main() {
|
||||
describe('LightDom', function() {
|
||||
var lightDomView;
|
||||
|
||||
beforeEach(() => { lightDomView = new FakeView(); });
|
||||
|
||||
describe("contentTags", () => {
|
||||
it("should collect unconditional content tags", () => {
|
||||
var tag = new FakeContentTag(el('<script></script>'));
|
||||
var shadowDomView = new FakeView([tag]);
|
||||
|
||||
var lightDom = createLightDom(lightDomView, shadowDomView, el("<div></div>"));
|
||||
|
||||
expect(lightDom.contentTags()).toEqual([tag]);
|
||||
});
|
||||
|
||||
it("should collect content tags from ViewContainers", () => {
|
||||
var tag = new FakeContentTag(el('<script></script>'));
|
||||
var vc = new FakeViewContainer(null, null, [new FakeView([tag])]);
|
||||
var shadowDomView = new FakeView([vc]);
|
||||
var lightDom = createLightDom(lightDomView, shadowDomView, el("<div></div>"));
|
||||
|
||||
expect(lightDom.contentTags()).toEqual([tag]);
|
||||
});
|
||||
|
||||
it("should not walk views that can't have content tags", () => {
|
||||
var tag = new FakeContentTag(el('<script></script>'));
|
||||
var shadowDomView = new FakeView([tag], 0);
|
||||
|
||||
var lightDom = createLightDom(lightDomView, shadowDomView, el("<div></div>"));
|
||||
|
||||
expect(lightDom.contentTags()).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("expandedDomNodes", () => {
|
||||
it("should contain root nodes", () => {
|
||||
var lightDomEl = el("<div><a></a></div>");
|
||||
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 = createLightDom(
|
||||
new FakeView([
|
||||
new FakeViewContainer(DOM.firstChild(lightDomEl), // template element
|
||||
[el('<a></a>')] // light DOM nodes of view container
|
||||
)
|
||||
]),
|
||||
null, lightDomEl);
|
||||
|
||||
expect(toHtml(lightDom.expandedDomNodes())).toEqual(["<a></a>"]);
|
||||
});
|
||||
|
||||
it("should include content nodes", () => {
|
||||
var lightDomEl = el("<div><content></content></div>");
|
||||
var lightDom =
|
||||
createLightDom(new FakeView([
|
||||
new FakeContentTag(DOM.firstChild(lightDomEl), // content element
|
||||
'', // selector
|
||||
[el('<a></a>')] // light DOM nodes of content tag
|
||||
)
|
||||
]),
|
||||
null, lightDomEl);
|
||||
|
||||
expect(toHtml(lightDom.expandedDomNodes())).toEqual(["<a></a>"]);
|
||||
});
|
||||
|
||||
it("should work when the element injector array contains nulls", () => {
|
||||
var lightDomEl = el("<div><a></a></div>")
|
||||
|
||||
var lightDomView = new FakeView();
|
||||
|
||||
var lightDom = createLightDom(lightDomView, new FakeView(), lightDomEl);
|
||||
|
||||
expect(toHtml(lightDom.expandedDomNodes())).toEqual(["<a></a>"]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("redistribute", () => {
|
||||
it("should redistribute nodes between content tags with select property set", () => {
|
||||
var contentA = new FakeContentTag(null, "a");
|
||||
var contentB = new FakeContentTag(null, "b");
|
||||
|
||||
var lightDomEl = el("<div><a>1</a><b>2</b><a>3</a></div>")
|
||||
|
||||
var lightDom =
|
||||
createLightDom(lightDomView, new FakeView([contentA, contentB]), lightDomEl);
|
||||
|
||||
lightDom.redistribute();
|
||||
|
||||
expect(toHtml(contentA.nodes())).toEqual(["<a>1</a>", "<a>3</a>"]);
|
||||
expect(toHtml(contentB.nodes())).toEqual(["<b>2</b>"]);
|
||||
});
|
||||
|
||||
it("should support wildcard content tags", () => {
|
||||
var wildcard = new FakeContentTag(null, '');
|
||||
var contentB = new FakeContentTag(null, "b");
|
||||
|
||||
var lightDomEl = el("<div><a>1</a><b>2</b><a>3</a></div>")
|
||||
|
||||
var lightDom =
|
||||
createLightDom(lightDomView, new FakeView([wildcard, contentB]), lightDomEl);
|
||||
|
||||
lightDom.redistribute();
|
||||
|
||||
expect(toHtml(wildcard.nodes())).toEqual(["<a>1</a>", "<b>2</b>", "<a>3</a>"]);
|
||||
expect(toHtml(contentB.nodes())).toEqual([]);
|
||||
});
|
||||
|
||||
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 = createLightDom(lightDomView, new FakeView([]), lightDomEl);
|
||||
|
||||
lightDom.redistribute();
|
||||
|
||||
expect(DOM.childNodes(lightDomEl).length).toBe(0);
|
||||
});
|
||||
|
||||
it("should remove all not projected nodes", () => {
|
||||
var lightDomEl = el("<div><a>1</a><b>2</b><a>3</a></div>");
|
||||
var bNode = DOM.childNodes(lightDomEl)[1];
|
||||
|
||||
var lightDom =
|
||||
createLightDom(lightDomView, new FakeView([new FakeContentTag(null, "a")]), lightDomEl);
|
||||
|
||||
lightDom.redistribute();
|
||||
|
||||
expect(bNode.parentNode).toBe(null);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function toHtml(nodes) {
|
||||
if (isBlank(nodes)) return [];
|
||||
return ListWrapper.map(nodes, stringifyElement);
|
||||
}
|
||||
@@ -16,19 +16,13 @@ import {
|
||||
NativeShadowDomStrategy
|
||||
} from 'angular2/src/render/dom/shadow_dom/native_shadow_dom_strategy';
|
||||
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
export function main() {
|
||||
var strategy;
|
||||
var strategy: NativeShadowDomStrategy;
|
||||
|
||||
describe('NativeShadowDomStrategy', () => {
|
||||
beforeEach(() => { strategy = new NativeShadowDomStrategy(); });
|
||||
|
||||
if (DOM.supportsNativeShadowDOM()) {
|
||||
it('should use the native shadow root', () => {
|
||||
var host = el('<div><span>original content</span></div>');
|
||||
expect(strategy.prepareShadowRoot(host)).toBe(DOM.getShadowRoot(host));
|
||||
});
|
||||
}
|
||||
it('should report that this is the native strategy',
|
||||
() => { expect(strategy.hasNativeContentElement()).toBe(true); });
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,555 +0,0 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
el,
|
||||
expect,
|
||||
iit,
|
||||
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 {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 {DomTestbed, elRef} from './dom_testbed';
|
||||
|
||||
export function main() {
|
||||
describe('ShadowDom integration tests', function() {
|
||||
var styleHost = DOM.createElement('div');
|
||||
|
||||
var strategies = {
|
||||
"scoped": bind(ShadowDomStrategy).toValue(new EmulatedScopedShadowDomStrategy(styleHost)),
|
||||
"unscoped": bind(ShadowDomStrategy).toValue(new EmulatedUnscopedShadowDomStrategy(styleHost))
|
||||
};
|
||||
if (DOM.supportsNativeShadowDOM()) {
|
||||
StringMapWrapper.set(strategies, "native",
|
||||
bind(ShadowDomStrategy).toValue(new NativeShadowDomStrategy()));
|
||||
}
|
||||
|
||||
StringMapWrapper.forEach(strategies, (strategyBinding, name) => {
|
||||
describe(`${name} shadow dom strategy`, () => {
|
||||
beforeEachBindings(() => { return [strategyBinding, DomTestbed]; });
|
||||
|
||||
// GH-2095 - https://github.com/angular/angular/issues/2095
|
||||
// important as we are adding a content end element during compilation,
|
||||
// which could skrew up text node indices.
|
||||
it('should support text nodes after content tags',
|
||||
inject([DomTestbed, AsyncTestCompleter], (tb, async) => {
|
||||
tb.compileAll([
|
||||
simple,
|
||||
new ViewDefinition({
|
||||
componentId: 'simple',
|
||||
template: '<content></content><p>P,</p>{{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, 'text');
|
||||
expect(tb.rootEl).toHaveText('P,text');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
// important as we are moving style tags around during compilation,
|
||||
// which could skrew up text node indices.
|
||||
it('should support text nodes after style tags',
|
||||
inject([DomTestbed, AsyncTestCompleter], (tb, async) => {
|
||||
tb.compileAll([
|
||||
simple,
|
||||
new ViewDefinition({
|
||||
componentId: 'simple',
|
||||
template: '<style></style><p>P,</p>{{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, 'text');
|
||||
expect(tb.rootEl).toHaveText('P,text');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
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]
|
||||
}),
|
||||
simpleTemplate
|
||||
])
|
||||
.then((protoViews) => {
|
||||
tb.createRootViews(protoViews);
|
||||
|
||||
expect(tb.rootEl).toHaveText('SIMPLE(A)');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should support simple components with text interpolation as direct children',
|
||||
inject([AsyncTestCompleter, DomTestbed], (async, tb) => {
|
||||
tb.compileAll([
|
||||
mainDir,
|
||||
new ViewDefinition({
|
||||
componentId: 'main',
|
||||
template: '<simple>' +
|
||||
'{{text}}' +
|
||||
'</simple>',
|
||||
directives: [simple]
|
||||
}),
|
||||
simpleTemplate
|
||||
])
|
||||
.then((protoViews) => {
|
||||
var cmpView = tb.createRootViews(protoViews)[1];
|
||||
tb.renderer.setText(cmpView.viewRef, 0, 'A');
|
||||
|
||||
expect(tb.rootEl).toHaveText('SIMPLE(A)');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
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]
|
||||
}),
|
||||
emptyTemplate
|
||||
])
|
||||
.then((protoViews) => {
|
||||
tb.createRootViews(protoViews);
|
||||
|
||||
expect(tb.rootEl).toHaveText('');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
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]
|
||||
}),
|
||||
simpleTemplate
|
||||
])
|
||||
.then((protoViews) => {
|
||||
var views = tb.createRootViews(ListWrapper.slice(protoViews, 0, 2));
|
||||
tb.createComponentView(views[1].viewRef, 0, protoViews[2]);
|
||||
|
||||
expect(tb.rootEl).toHaveText('SIMPLE(A)');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
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>' +
|
||||
'<div>C</div>' +
|
||||
'<div class="left">A</div>' +
|
||||
'</multiple-content-tags>',
|
||||
directives: [multipleContentTagsComponent]
|
||||
}),
|
||||
multipleContentTagsTemplate
|
||||
])
|
||||
.then((protoViews) => {
|
||||
tb.createRootViews(protoViews);
|
||||
|
||||
expect(tb.rootEl).toHaveText('(A, BC)');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
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]
|
||||
}),
|
||||
multipleContentTagsTemplate
|
||||
])
|
||||
.then((protoViews) => {
|
||||
tb.createRootViews(protoViews);
|
||||
|
||||
expect(tb.rootEl).toHaveText('(, BAC)');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
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]
|
||||
}),
|
||||
multipleContentTagsTemplate
|
||||
])
|
||||
.then((protoViews) => {
|
||||
var views = tb.createRootViews(protoViews);
|
||||
var childProtoView = protoViews[1].elementBinders[1].nestedProtoView;
|
||||
expect(tb.rootEl).toHaveText('(, B)');
|
||||
|
||||
var childView = tb.createViewInContainer(views[1].viewRef, 1, 0, childProtoView);
|
||||
|
||||
expect(tb.rootEl).toHaveText('(, AB)');
|
||||
|
||||
tb.destroyViewInContainer(views[1].viewRef, 1, 0, childView.viewRef);
|
||||
|
||||
expect(tb.rootEl).toHaveText('(, B)');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
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]
|
||||
}),
|
||||
multipleContentTagsTemplate
|
||||
])
|
||||
.then((protoViews) => {
|
||||
var views = tb.createRootViews(protoViews);
|
||||
var childProtoView = protoViews[1].elementBinders[1].nestedProtoView;
|
||||
|
||||
expect(tb.rootEl).toHaveText('(, B)');
|
||||
|
||||
var childView = tb.createViewInContainer(views[1].viewRef, 1, 0, childProtoView);
|
||||
|
||||
expect(tb.rootEl).toHaveText('(A, B)');
|
||||
|
||||
tb.destroyViewInContainer(views[1].viewRef, 1, 0, childView.viewRef);
|
||||
|
||||
expect(tb.rootEl).toHaveText('(, B)');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
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]
|
||||
}),
|
||||
outerWithIndirectNestedTemplate,
|
||||
simpleTemplate
|
||||
])
|
||||
.then((protoViews) => {
|
||||
tb.createRootViews(protoViews);
|
||||
|
||||
expect(tb.rootEl).toHaveText('OUTER(SIMPLE(AB))');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
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>' +
|
||||
'<div>B</div>' +
|
||||
'<div>C</div>' +
|
||||
'</outer>',
|
||||
directives: [outerComponent, manualViewportDirective]
|
||||
}),
|
||||
outerTemplate,
|
||||
innerTemplate,
|
||||
innerInnerTemplate
|
||||
])
|
||||
.then((protoViews) => {
|
||||
var views = tb.createRootViews(protoViews);
|
||||
var childProtoView = protoViews[1].elementBinders[1].nestedProtoView;
|
||||
|
||||
expect(tb.rootEl).toHaveText('OUTER(INNER(INNERINNER(,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, DomTestbed], (async, tb) => {
|
||||
tb.compileAll([
|
||||
mainDir,
|
||||
new ViewDefinition({
|
||||
componentId: 'main',
|
||||
template: '<conditional-content>' +
|
||||
'<div class="left">A</div>' +
|
||||
'<div>B</div>' +
|
||||
'<div>C</div>' +
|
||||
'</conditional-content>',
|
||||
directives: [conditionalContentComponent]
|
||||
}),
|
||||
conditionalContentTemplate
|
||||
])
|
||||
.then((protoViews) => {
|
||||
var views = tb.createRootViews(protoViews);
|
||||
var childProtoView = protoViews[2].elementBinders[0].nestedProtoView;
|
||||
|
||||
expect(tb.rootEl).toHaveText('(, ABC)');
|
||||
|
||||
var childView = tb.createViewInContainer(views[2].viewRef, 0, 0, childProtoView);
|
||||
|
||||
expect(tb.rootEl).toHaveText('(A, BC)');
|
||||
|
||||
tb.destroyViewInContainer(views[2].viewRef, 0, 0, childView.viewRef);
|
||||
|
||||
expect(tb.rootEl).toHaveText('(, ABC)');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
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]
|
||||
}),
|
||||
tabTemplate
|
||||
])
|
||||
.then((protoViews) => {
|
||||
var views = tb.createRootViews(ListWrapper.slice(protoViews, 0, 2));
|
||||
var tabProtoView = protoViews[2];
|
||||
var tabChildProtoView = tabProtoView.elementBinders[0].nestedProtoView;
|
||||
|
||||
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);
|
||||
|
||||
expect(tb.rootEl).toHaveText('()');
|
||||
|
||||
var tabChildView =
|
||||
tb.createViewInContainer(tab1View.viewRef, 0, 0, tabChildProtoView);
|
||||
|
||||
expect(tb.rootEl).toHaveText('(TAB(0))');
|
||||
|
||||
tb.renderer.dehydrateView(tabChildView.viewRef);
|
||||
tb.renderer.detachViewInContainer(elRef(tab1View.viewRef, 0), 0,
|
||||
tabChildView.viewRef);
|
||||
|
||||
tb.renderer.attachViewInContainer(elRef(tab2View.viewRef, 0), 0,
|
||||
tabChildView.viewRef);
|
||||
tb.renderer.hydrateView(tabChildView.viewRef);
|
||||
|
||||
expect(tb.rootEl).toHaveText('(TAB(1))');
|
||||
|
||||
tb.renderer.dehydrateView(tabChildView.viewRef);
|
||||
tb.renderer.detachViewInContainer(elRef(tab2View.viewRef, 0), 0,
|
||||
tabChildView.viewRef);
|
||||
|
||||
tb.renderer.attachViewInContainer(elRef(tab3View.viewRef, 0), 0,
|
||||
tabChildView.viewRef);
|
||||
tb.renderer.hydrateView(tabChildView.viewRef);
|
||||
|
||||
expect(tb.rootEl).toHaveText('(TAB(2))');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
// Implement once ElementRef support changing a class
|
||||
// it("should redistribute when a class has been added or removed");
|
||||
// it('should not lose focus', () => {
|
||||
// var temp = `<simple>aaa<input type="text" id="focused-input" ng-class="{'aClass' :
|
||||
// showClass}"> bbb</simple>`;
|
||||
//
|
||||
// compile(temp, (view, lc) => {
|
||||
// var input = view.rootNodes[1];
|
||||
// input.focus();
|
||||
//
|
||||
// expect(document.activeElement.id).toEqual("focused-input");
|
||||
//
|
||||
// // update class of input
|
||||
//
|
||||
// expect(document.activeElement.id).toEqual("focused-input");
|
||||
// });
|
||||
//});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
var mainDir = DirectiveMetadata.create(
|
||||
{selector: 'main', id: 'main', type: DirectiveMetadata.COMPONENT_TYPE});
|
||||
|
||||
var simple = DirectiveMetadata.create(
|
||||
{selector: 'simple', id: 'simple', type: DirectiveMetadata.COMPONENT_TYPE});
|
||||
|
||||
var empty = DirectiveMetadata.create(
|
||||
{selector: 'empty', id: 'empty', type: DirectiveMetadata.COMPONENT_TYPE});
|
||||
|
||||
var dynamicComponent = DirectiveMetadata.create(
|
||||
{selector: 'dynamic', id: 'dynamic', type: DirectiveMetadata.COMPONENT_TYPE});
|
||||
|
||||
var multipleContentTagsComponent = DirectiveMetadata.create({
|
||||
selector: 'multiple-content-tags',
|
||||
id: 'multiple-content-tags',
|
||||
type: DirectiveMetadata.COMPONENT_TYPE
|
||||
});
|
||||
|
||||
var manualViewportDirective = DirectiveMetadata.create(
|
||||
{selector: '[manual]', id: 'manual', type: DirectiveMetadata.DIRECTIVE_TYPE});
|
||||
|
||||
var outerWithIndirectNestedComponent = DirectiveMetadata.create({
|
||||
selector: 'outer-with-indirect-nested',
|
||||
id: 'outer-with-indirect-nested',
|
||||
type: DirectiveMetadata.COMPONENT_TYPE
|
||||
});
|
||||
|
||||
var outerComponent = DirectiveMetadata.create(
|
||||
{selector: 'outer', id: 'outer', type: DirectiveMetadata.COMPONENT_TYPE});
|
||||
|
||||
var innerComponent = DirectiveMetadata.create(
|
||||
{selector: 'inner', id: 'inner', type: DirectiveMetadata.COMPONENT_TYPE});
|
||||
|
||||
var innerInnerComponent = DirectiveMetadata.create(
|
||||
{selector: 'innerinner', id: 'innerinner', type: DirectiveMetadata.COMPONENT_TYPE});
|
||||
|
||||
var conditionalContentComponent = DirectiveMetadata.create({
|
||||
selector: 'conditional-content',
|
||||
id: 'conditional-content',
|
||||
type: DirectiveMetadata.COMPONENT_TYPE
|
||||
});
|
||||
|
||||
var autoViewportDirective = DirectiveMetadata.create(
|
||||
{selector: '[auto]', id: 'auto', properties: ['auto'], type: DirectiveMetadata.DIRECTIVE_TYPE});
|
||||
|
||||
var tabComponent =
|
||||
DirectiveMetadata.create({selector: 'tab', id: 'tab', type: DirectiveMetadata.COMPONENT_TYPE});
|
||||
|
||||
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]
|
||||
});
|
||||
@@ -14,13 +14,15 @@ import {
|
||||
import {ProtoViewBuilder} from 'angular2/src/render/dom/view/proto_view_builder';
|
||||
import {ASTWithSource, AST} from 'angular2/change_detection';
|
||||
import {PropertyBindingType, ViewType} from 'angular2/src/render/api';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
export function main() {
|
||||
function emptyExpr() { return new ASTWithSource(new AST(), 'empty', 'empty'); }
|
||||
|
||||
describe('ProtoViewBuilder', () => {
|
||||
var builder;
|
||||
beforeEach(() => { builder = new ProtoViewBuilder(el('<div/>'), ViewType.EMBEDDED); });
|
||||
beforeEach(
|
||||
() => { builder = new ProtoViewBuilder(DOM.createTemplate(''), ViewType.EMBEDDED); });
|
||||
|
||||
if (!IS_DARTIUM) {
|
||||
describe('verification of properties', () => {
|
||||
|
||||
@@ -0,0 +1,291 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
el,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
beforeEachBindings,
|
||||
SpyObject,
|
||||
stringifyElement
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import {isPresent} from 'angular2/src/facade/lang';
|
||||
|
||||
import {DomTestbed} from '../dom_testbed';
|
||||
|
||||
import {
|
||||
ViewDefinition,
|
||||
DirectiveMetadata,
|
||||
RenderProtoViewMergeMapping
|
||||
} from 'angular2/src/render/api';
|
||||
import {bind} from 'angular2/di';
|
||||
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
import {cloneAndQueryProtoView} from 'angular2/src/render/dom/util';
|
||||
import {resolveInternalDomProtoView} from 'angular2/src/render/dom/view/proto_view';
|
||||
|
||||
import {ShadowDomStrategy, NativeShadowDomStrategy} from 'angular2/render';
|
||||
|
||||
export function main() {
|
||||
describe('ProtoViewMerger integration test', () => {
|
||||
beforeEachBindings(() => [DomTestbed]);
|
||||
|
||||
describe('component views', () => {
|
||||
it('should merge a component view',
|
||||
runAndAssert('root', ['a'], ['<root class="ng-binding" idx="0">a</root>']));
|
||||
|
||||
it('should merge component views with interpolation at root level',
|
||||
runAndAssert('root', ['{{a}}'], ['<root class="ng-binding" idx="0">{0}</root>']));
|
||||
|
||||
it('should merge component views with interpolation not at root level',
|
||||
runAndAssert('root', ['<div>{{a}}</div>'], [
|
||||
'<root class="ng-binding" idx="0"><div class="ng-binding" idx="1">{0}</div></root>'
|
||||
]));
|
||||
|
||||
it('should merge component views with bound elements',
|
||||
runAndAssert('root', ['<div #a></div>'], [
|
||||
'<root class="ng-binding" idx="0"><div #a="" class="ng-binding" idx="1"></div></root>'
|
||||
]));
|
||||
});
|
||||
|
||||
describe('embedded views', () => {
|
||||
|
||||
it('should merge embedded views as fragments',
|
||||
runAndAssert('root', ['<template>a</template>'], [
|
||||
'<root class="ng-binding" idx="0"><template class="ng-binding" idx="1"></template></root>',
|
||||
'a'
|
||||
]));
|
||||
|
||||
it('should merge embedded views with interpolation at root level',
|
||||
runAndAssert('root', ['<template>{{a}}</template>'], [
|
||||
'<root class="ng-binding" idx="0"><template class="ng-binding" idx="1"></template></root>',
|
||||
'{0}'
|
||||
]));
|
||||
|
||||
it('should merge embedded views with interpolation not at root level',
|
||||
runAndAssert('root', ['<div *ng-if>{{a}}</div>'], [
|
||||
'<root class="ng-binding" idx="0"><template class="ng-binding" idx="1" ng-if=""></template></root>',
|
||||
'<div *ng-if="" class="ng-binding" idx="2">{0}</div>'
|
||||
]));
|
||||
|
||||
it('should merge embedded views with bound elements',
|
||||
runAndAssert('root', ['<div *ng-if #a></div>'], [
|
||||
'<root class="ng-binding" idx="0"><template class="ng-binding" idx="1" ng-if=""></template></root>',
|
||||
'<div #a="" *ng-if="" class="ng-binding" idx="2"></div>'
|
||||
]));
|
||||
|
||||
});
|
||||
|
||||
describe('projection', () => {
|
||||
|
||||
it('should remove text nodes if there is no ng-content',
|
||||
runAndAssert(
|
||||
'root', ['<a>b</a>', ''],
|
||||
['<root class="ng-binding" idx="0"><a class="ng-binding" idx="1"></a></root>']));
|
||||
|
||||
it('should project static text',
|
||||
runAndAssert(
|
||||
'root', ['<a>b</a>', 'A(<ng-content></ng-content>)'],
|
||||
['<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(b)</a></root>']));
|
||||
|
||||
it('should project text interpolation',
|
||||
runAndAssert(
|
||||
'root', ['<a>{{b}}</a>', 'A(<ng-content></ng-content>)'],
|
||||
['<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A({0})</a></root>']));
|
||||
|
||||
it('should project elements',
|
||||
runAndAssert('root', ['<a><div></div></a>', 'A(<ng-content></ng-content>)'], [
|
||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<div></div>)</a></root>'
|
||||
]));
|
||||
|
||||
it('should project elements using the selector',
|
||||
runAndAssert(
|
||||
'root',
|
||||
[
|
||||
'<a><div class="x">a</div><span></span><div class="x">b</div></a>',
|
||||
'A(<ng-content select=".x"></ng-content>)'
|
||||
],
|
||||
[
|
||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<div class="x">a</div><div class="x">b</div>)</a></root>'
|
||||
]));
|
||||
|
||||
it('should reproject',
|
||||
runAndAssert(
|
||||
'root',
|
||||
['<a>x</a>', 'A(<b><ng-content></ng-content></b>)', 'B(<ng-content></ng-content>)'], [
|
||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<b class="ng-binding" idx="2">B(x)</b>)</a></root>'
|
||||
]));
|
||||
|
||||
it('should reproject by combining selectors',
|
||||
runAndAssert(
|
||||
'root',
|
||||
[
|
||||
'<a><div class="x"></div><div class="x y"></div><div class="y"></div></a>',
|
||||
'A(<b><ng-content select=".x"></ng-content></b>)',
|
||||
'B(<ng-content select=".y"></ng-content>)'
|
||||
],
|
||||
[
|
||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<b class="ng-binding" idx="2">B(<div class="x y"></div>)</b>)</a></root>'
|
||||
]));
|
||||
|
||||
it('should keep non projected embedded views (so that they can be moved manually)',
|
||||
runAndAssert(
|
||||
'root', ['<a><template class="x">b</template></a>', ''],
|
||||
['<root class="ng-binding" idx="0"><a class="ng-binding" idx="1"></a></root>', 'b']));
|
||||
|
||||
it('should project embedded views and match the template element',
|
||||
runAndAssert(
|
||||
'root', ['<a><template class="x">b</template></a>', 'A(<ng-content></ng-content>)'], [
|
||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<template class="x ng-binding" idx="2"></template>)</a></root>',
|
||||
'b'
|
||||
]));
|
||||
|
||||
it('should project embedded views and match the single root element',
|
||||
runAndAssert(
|
||||
'root', ['<a><div class="x" *ng-if></div></a>', 'A(<ng-content></ng-content>)'], [
|
||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<template class="ng-binding" idx="2" ng-if=""></template>)</a></root>',
|
||||
'<div *ng-if="" class="x"></div>'
|
||||
]));
|
||||
|
||||
it('should project nodes using the ng-content in embedded views',
|
||||
runAndAssert('root', ['<a>b</a>', 'A(<ng-content *ng-if></ng-content>)'], [
|
||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<template class="ng-binding" idx="2" ng-if=""></template>)</a></root>',
|
||||
'b'
|
||||
]));
|
||||
|
||||
it('should allow to use wildcard selector after embedded view with non wildcard selector',
|
||||
runAndAssert(
|
||||
'root',
|
||||
[
|
||||
'<a><div class="x">a</div>b</a>',
|
||||
'A(<ng-content select=".x" *ng-if></ng-content>, <ng-content></ng-content>)'
|
||||
],
|
||||
[
|
||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<template class="ng-binding" idx="2" ng-if=""></template>, b)</a></root>',
|
||||
'<div class="x">a</div>'
|
||||
]));
|
||||
|
||||
});
|
||||
|
||||
describe('composition', () => {
|
||||
it('should merge multiple component views',
|
||||
runAndAssert('root', ['<a></a><b></b>', 'c', 'd'], [
|
||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">c</a><b class="ng-binding" idx="2">d</b></root>'
|
||||
]));
|
||||
|
||||
it('should merge multiple embedded views as fragments',
|
||||
runAndAssert('root', ['<div *ng-if></div><span *ng-for></span>'], [
|
||||
'<root class="ng-binding" idx="0"><template class="ng-binding" idx="1" ng-if=""></template><template class="ng-binding" idx="2" ng-for=""></template></root>',
|
||||
'<div *ng-if=""></div>',
|
||||
'<span *ng-for=""></span>'
|
||||
]));
|
||||
|
||||
it('should merge nested embedded views as fragments',
|
||||
runAndAssert('root', ['<div *ng-if><span *ng-for></span></div>'], [
|
||||
'<root class="ng-binding" idx="0"><template class="ng-binding" idx="1" ng-if=""></template></root>',
|
||||
'<div *ng-if=""><template class="ng-binding" idx="2" ng-for=""></template></div>',
|
||||
'<span *ng-for=""></span>'
|
||||
]));
|
||||
|
||||
});
|
||||
|
||||
describe('element index mapping should be grouped by view and view depth first', () => {
|
||||
|
||||
it('should map component views correctly',
|
||||
runAndAssert('root', ['<a></a><b></b>', '<c></c>'], [
|
||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1"><c class="ng-binding" idx="3"></c></a><b class="ng-binding" idx="2"></b></root>'
|
||||
]));
|
||||
|
||||
it('should map moved projected elements correctly',
|
||||
runAndAssert(
|
||||
'root',
|
||||
[
|
||||
'<a><b></b><c></c></a>',
|
||||
'<ng-content select="c"></ng-content><ng-content select="b"></ng-content>'
|
||||
],
|
||||
[
|
||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1"><c class="ng-binding" idx="3"></c><b class="ng-binding" idx="2"></b></a></root>'
|
||||
]));
|
||||
|
||||
});
|
||||
|
||||
describe('text index mapping should be grouped by view and view depth first', () => {
|
||||
|
||||
it('should map component views correctly', runAndAssert('root', ['<a></a>{{b}}', '{{c}}'], [
|
||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">{1}</a>{0}</root>'
|
||||
]));
|
||||
|
||||
it('should map moved projected elements correctly',
|
||||
runAndAssert(
|
||||
'root',
|
||||
[
|
||||
'<a><div x>{{x}}</div><div y>{{y}}</div></a>',
|
||||
'<ng-content select="[y]"></ng-content><ng-content select="[x]"></ng-content>'
|
||||
],
|
||||
[
|
||||
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1"><div class="ng-binding" idx="3" y="">{1}</div><div class="ng-binding" idx="2" x="">{0}</div></a></root>'
|
||||
]));
|
||||
|
||||
});
|
||||
|
||||
describe('native shadow dom support', () => {
|
||||
beforeEachBindings(
|
||||
() => { return [bind(ShadowDomStrategy).toValue(new NativeShadowDomStrategy())]; });
|
||||
|
||||
it('should keep the non projected light dom and wrap the component view into a shadow-root element',
|
||||
runAndAssert('root', ['<a>b</a>', 'c'], [
|
||||
'<root class="ng-binding" idx="0"><shadow-root><a class="ng-binding" idx="1"><shadow-root>c</shadow-root>b</a></shadow-root></root>'
|
||||
]));
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function runAndAssert(hostElementName: string, componentTemplates: string[],
|
||||
expectedFragments: string[]) {
|
||||
var rootComp = DirectiveMetadata.create(
|
||||
{id: 'rootComp', type: DirectiveMetadata.COMPONENT_TYPE, selector: hostElementName});
|
||||
return inject([AsyncTestCompleter, DomTestbed], (async, tb: DomTestbed) => {
|
||||
tb.compileAndMerge(rootComp, componentTemplates.map(template => new ViewDefinition({
|
||||
componentId: 'someComp',
|
||||
template: template,
|
||||
directives: [aComp, bComp, cComp]
|
||||
})))
|
||||
.then((mergeMappings) => {
|
||||
expect(stringify(mergeMappings[0])).toEqual(expectedFragments);
|
||||
async.done();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function stringify(protoViewMergeMapping: RenderProtoViewMergeMapping): string[] {
|
||||
var testView = cloneAndQueryProtoView(
|
||||
resolveInternalDomProtoView(protoViewMergeMapping.mergedProtoViewRef), false);
|
||||
for (var i = 0; i < protoViewMergeMapping.mappedElementIndices.length; i++) {
|
||||
var renderElIdx = protoViewMergeMapping.mappedElementIndices[i];
|
||||
if (isPresent(renderElIdx)) {
|
||||
DOM.setAttribute(testView.boundElements[renderElIdx], 'idx', `${i}`);
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < protoViewMergeMapping.mappedTextIndices.length; i++) {
|
||||
var renderTextIdx = protoViewMergeMapping.mappedTextIndices[i];
|
||||
if (isPresent(renderTextIdx)) {
|
||||
DOM.setText(testView.boundTextNodes[renderTextIdx], `{${i}}`);
|
||||
}
|
||||
}
|
||||
expect(protoViewMergeMapping.fragmentCount).toEqual(testView.fragments.length);
|
||||
return testView.fragments.map(nodes => nodes.map(node => stringifyElement(node)).join(''));
|
||||
}
|
||||
|
||||
var aComp =
|
||||
DirectiveMetadata.create({id: 'aComp', type: DirectiveMetadata.COMPONENT_TYPE, selector: 'a'});
|
||||
var bComp =
|
||||
DirectiveMetadata.create({id: 'bComp', type: DirectiveMetadata.COMPONENT_TYPE, selector: 'b'});
|
||||
var cComp =
|
||||
DirectiveMetadata.create({id: 'cComp', type: DirectiveMetadata.COMPONENT_TYPE, selector: 'c'});
|
||||
@@ -0,0 +1,56 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
el,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
beforeEachBindings,
|
||||
SpyObject,
|
||||
stringifyElement
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import {mergeSelectors} from 'angular2/src/render/dom/view/proto_view_merger';
|
||||
|
||||
export function main() {
|
||||
describe('ProtoViewMerger test', () => {
|
||||
|
||||
describe('mergeSelectors', () => {
|
||||
it('should merge empty selectors', () => {
|
||||
expect(mergeSelectors('', 'a')).toEqual('a');
|
||||
expect(mergeSelectors('a', '')).toEqual('a');
|
||||
expect(mergeSelectors('', '')).toEqual('');
|
||||
});
|
||||
|
||||
it('should merge wildcard selectors', () => {
|
||||
expect(mergeSelectors('*', 'a')).toEqual('a');
|
||||
expect(mergeSelectors('a', '*')).toEqual('a');
|
||||
expect(mergeSelectors('*', '*')).toEqual('*');
|
||||
});
|
||||
|
||||
it('should merge 2 element selectors',
|
||||
() => { expect(mergeSelectors('a', 'b')).toEqual('_not-matchable_'); });
|
||||
|
||||
it('should merge elements and non element selector', () => {
|
||||
expect(mergeSelectors('a', '.b')).toEqual('a.b');
|
||||
expect(mergeSelectors('.b', 'a')).toEqual('a.b');
|
||||
});
|
||||
|
||||
it('should merge attributes', () => {
|
||||
expect(mergeSelectors('[a]', '[b]')).toEqual('[a][b]');
|
||||
expect(mergeSelectors('[a][b]', '[c][d]')).toEqual('[a][b][c][d]');
|
||||
expect(mergeSelectors('[a=1]', '[b=2]')).toEqual('[a=1][b=2]');
|
||||
});
|
||||
|
||||
it('should merge classes', () => {
|
||||
expect(mergeSelectors('.a', '.b')).toEqual('.a.b');
|
||||
expect(mergeSelectors('.a.b', '.c.d')).toEqual('.a.b.c.d');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
@@ -19,9 +19,8 @@ import {isBlank} from 'angular2/src/facade/lang';
|
||||
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 {DomElementBinder} from 'angular2/src/render/dom/view/element_binder';
|
||||
import {DomView} from 'angular2/src/render/dom/view/view';
|
||||
import {DomElement} from 'angular2/src/render/dom/view/element';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
export function main() {
|
||||
@@ -30,54 +29,33 @@ export function main() {
|
||||
if (isBlank(binders)) {
|
||||
binders = [];
|
||||
}
|
||||
var rootEl = el('<div></div>');
|
||||
return new DomProtoView({
|
||||
element: rootEl,
|
||||
elementBinders: binders,
|
||||
transitiveContentTagCount: 0,
|
||||
boundTextNodeCount: 0
|
||||
});
|
||||
var rootEl = DOM.createTemplate('<div></div>');
|
||||
return DomProtoView.create(null, <Element>rootEl, [1], [], binders, null, null, null);
|
||||
}
|
||||
|
||||
function createElementBinder() { return new DomElementBinder({textNodeIndices: []}); }
|
||||
|
||||
function createView(pv = null, boundElementCount = 0) {
|
||||
if (isBlank(pv)) {
|
||||
pv = createProtoView(ListWrapper.createFixedSize(boundElementCount));
|
||||
var elementBinders = ListWrapper.createFixedSize(boundElementCount);
|
||||
for (var i = 0; i < boundElementCount; i++) {
|
||||
elementBinders[i] = createElementBinder();
|
||||
}
|
||||
pv = createProtoView(elementBinders);
|
||||
}
|
||||
var root = el('<div><div></div></div>');
|
||||
var boundElements = [];
|
||||
for (var i = 0; i < boundElementCount; i++) {
|
||||
boundElements.push(new DomElement(pv.elementBinders[i], el('<span></span'), null));
|
||||
boundElements.push(el('<span></span'));
|
||||
}
|
||||
return new DomView(pv, [DOM.childNodes(root)[0]], [], boundElements);
|
||||
return new DomView(pv, [DOM.childNodes(root)[0]], boundElements);
|
||||
}
|
||||
|
||||
function createElementBinder(parentIndex: number = 0, distanceToParent: number = 1) {
|
||||
return new ElementBinder(
|
||||
{parentIndex: parentIndex, distanceToParent: distanceToParent, textNodeIndices: []});
|
||||
}
|
||||
|
||||
describe('getDirectParentElement', () => {
|
||||
|
||||
it('should return the DomElement of the direct parent', () => {
|
||||
var pv = createProtoView([createElementBinder(), createElementBinder(0, 1)]);
|
||||
var view = createView(pv, 2);
|
||||
expect(view.getDirectParentElement(1)).toBe(view.boundElements[0]);
|
||||
});
|
||||
|
||||
it('should return null if the direct parent is not bound', () => {
|
||||
var pv = createProtoView(
|
||||
[createElementBinder(), createElementBinder(), createElementBinder(0, 2)]);
|
||||
var view = createView(pv, 3);
|
||||
expect(view.getDirectParentElement(2)).toBe(null);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('setElementProperty', () => {
|
||||
var el, view;
|
||||
beforeEach(() => {
|
||||
view = createView(null, 1);
|
||||
el = view.boundElements[0].element;
|
||||
el = view.boundElements[0];
|
||||
});
|
||||
|
||||
it('should update the property value', () => {
|
||||
@@ -91,7 +69,7 @@ export function main() {
|
||||
var el, view;
|
||||
beforeEach(() => {
|
||||
view = createView(null, 1);
|
||||
el = view.boundElements[0].element;
|
||||
el = view.boundElements[0];
|
||||
});
|
||||
|
||||
it('should update and remove an attribute', () => {
|
||||
@@ -111,7 +89,7 @@ export function main() {
|
||||
var el, view;
|
||||
beforeEach(() => {
|
||||
view = createView(null, 1);
|
||||
el = view.boundElements[0].element;
|
||||
el = view.boundElements[0];
|
||||
});
|
||||
|
||||
it('should set and remove a class', () => {
|
||||
@@ -135,7 +113,7 @@ export function main() {
|
||||
var el, view;
|
||||
beforeEach(() => {
|
||||
view = createView(null, 1);
|
||||
el = view.boundElements[0].element;
|
||||
el = view.boundElements[0];
|
||||
});
|
||||
|
||||
it('should set and remove styles', () => {
|
||||
|
||||
@@ -206,11 +206,9 @@ export function main() {
|
||||
.then((_) => rtr.navigate('/page/1'))
|
||||
.then((_) => {
|
||||
rootTC.detectChanges();
|
||||
expect(DOM.getAttribute(rootTC.componentViewChildren[1]
|
||||
.componentViewChildren[0]
|
||||
.children[0]
|
||||
.nativeElement,
|
||||
'href'))
|
||||
expect(DOM.getAttribute(
|
||||
rootTC.componentViewChildren[1].componentViewChildren[0].nativeElement,
|
||||
'href'))
|
||||
.toEqual('/page/2');
|
||||
async.done();
|
||||
});
|
||||
@@ -232,7 +230,6 @@ export function main() {
|
||||
expect(DOM.getAttribute(rootTC.componentViewChildren[1]
|
||||
.componentViewChildren[2]
|
||||
.componentViewChildren[0]
|
||||
.children[0]
|
||||
.nativeElement,
|
||||
'href'))
|
||||
.toEqual('/book/1984/page/2');
|
||||
|
||||
Reference in New Issue
Block a user