refactor(render): use render layer fully

Introduces angular2/src/core/compiler/ViewFactory which
extracts ProtoView.instantiate and replaces ViewPool.

Note: This is a work in progress commit to unblock other commits.
There will be follow ups to add unit tests, remove TODOs, …
This commit is contained in:
Tobias Bosch
2015-04-07 20:54:20 -07:00
parent de581ea8b3
commit 50098767fc
60 changed files with 1206 additions and 3341 deletions

View File

@ -16,7 +16,7 @@ import {List, ListWrapper, Map, MapWrapper, StringMapWrapper} from 'angular2/src
import {Type, isBlank, stringify, isPresent} from 'angular2/src/facade/lang';
import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
import {NewCompiler, CompilerCache} from 'angular2/src/core/compiler/compiler';
import {Compiler, CompilerCache} from 'angular2/src/core/compiler/compiler';
import {ProtoView} from 'angular2/src/core/compiler/view';
import {ElementBinder} from 'angular2/src/core/compiler/element_binder';
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
@ -45,7 +45,7 @@ export function main() {
var urlResolver = new FakeUrlResolver();
renderer = new FakeRenderer(renderCompileResults);
protoViewFactory = new FakeProtoViewFactory(protoViewFactoryResults)
return new NewCompiler(
return new Compiler(
reader,
new CompilerCache(),
tplResolver,
@ -373,7 +373,7 @@ export function main() {
],
[rootProtoView, mainProtoView]
);
compiler.compileRoot(null, createDirectiveBinding(reader, MainComponent)).then( (protoView) => {
compiler.compileRoot(null, MainComponent).then( (protoView) => {
expect(protoView).toBe(rootProtoView);
expect(rootProtoView.elementBinders[0].nestedProtoView).toBe(mainProtoView);
async.done();
@ -388,7 +388,7 @@ function createDirectiveBinding(reader, type) {
}
function createProtoView(elementBinders = null) {
var pv = new ProtoView(null, null, null, null, null);
var pv = new ProtoView(null, null, null);
if (isBlank(elementBinders)) {
elementBinders = [];
}

View File

@ -1,7 +1,6 @@
import {describe, ddescribe, it, iit, xit, xdescribe, expect, beforeEach, SpyObject, proxy, el} from 'angular2/test_lib';
import {isBlank, isPresent, IMPLEMENTS} from 'angular2/src/facade/lang';
import {ListWrapper, MapWrapper, List, StringMapWrapper} from 'angular2/src/facade/collection';
import {DOM} from 'angular2/src/dom/dom_adapter';
import {ProtoElementInjector, PreBuiltObjects, DirectiveBinding} from 'angular2/src/core/compiler/element_injector';
import {Parent, Ancestor} from 'angular2/src/core/annotations/visibility';
import {EventEmitter, PropertySetter, Attribute} from 'angular2/src/core/annotations/di';
@ -9,10 +8,11 @@ import {onDestroy} from 'angular2/src/core/annotations/annotations';
import {Optional, Injector, Inject, bind} from 'angular2/di';
import {ProtoView, View} from 'angular2/src/core/compiler/view';
import {ViewContainer} from 'angular2/src/core/compiler/view_container';
import {NgElement} from 'angular2/src/core/dom/element';
import {LightDom, DestinationLightDom} from 'angular2/src/core/compiler/shadow_dom_emulation/light_dom';
import {NgElement} from 'angular2/src/core/compiler/ng_element';
import {Directive} from 'angular2/src/core/annotations/annotations';
import {BindingPropagationConfig} from 'angular2/change_detection';
import {BindingPropagationConfig, Parser, Lexer} from 'angular2/change_detection';
import {ViewRef, Renderer} from 'angular2/src/render/api';
class DummyDirective extends Directive {
constructor({lifecycle} = {}) { super({lifecycle: lifecycle}); }
@ -22,10 +22,6 @@ class DummyDirective extends Directive {
@IMPLEMENTS(View)
class DummyView extends SpyObject {noSuchMethod(m){super.noSuchMethod(m)}}
@proxy
@IMPLEMENTS(LightDom)
class DummyLightDom extends SpyObject {noSuchMethod(m){super.noSuchMethod(m)}}
class SimpleDirective {
}
@ -198,10 +194,10 @@ export function main() {
var proto = new ProtoElementInjector(null, 0, bindings, isPresent(shadowDomAppInjector));
proto.attributes = attributes;
var inj = proto.instantiate(null, null);
var inj = proto.instantiate(null);
var preBuilt = isPresent(preBuiltObjects) ? preBuiltObjects : defaultPreBuiltObjects;
inj.instantiateDirectives(lightDomAppInjector, shadowDomAppInjector, preBuilt);
inj.instantiateDirectives(lightDomAppInjector, null, shadowDomAppInjector, preBuilt);
return inj;
}
@ -211,13 +207,13 @@ export function main() {
var inj = new Injector([]);
var protoParent = new ProtoElementInjector(null, 0, parentBindings);
var parent = protoParent.instantiate(null, null);
var parent = protoParent.instantiate(null);
parent.instantiateDirectives(inj, null, parentPreBuildObjects);
parent.instantiateDirectives(inj, null, null, parentPreBuildObjects);
var protoChild = new ProtoElementInjector(protoParent, 1, childBindings, false, 1);
var child = protoChild.instantiate(parent, null);
child.instantiateDirectives(inj, null, defaultPreBuiltObjects);
var child = protoChild.instantiate(parent);
child.instantiateDirectives(inj, null, null, defaultPreBuiltObjects);
return child;
}
@ -229,12 +225,12 @@ export function main() {
var shadowInj = inj.createChild([]);
var protoParent = new ProtoElementInjector(null, 0, hostBindings, true);
var host = protoParent.instantiate(null, null);
host.instantiateDirectives(inj, shadowInj, hostPreBuildObjects);
var host = protoParent.instantiate(null);
host.instantiateDirectives(inj, null, shadowInj, hostPreBuildObjects);
var protoChild = new ProtoElementInjector(protoParent, 0, shadowBindings, false, 1);
var shadow = protoChild.instantiate(null, host);
shadow.instantiateDirectives(shadowInj, null, null);
var shadow = protoChild.instantiate(null);
shadow.instantiateDirectives(shadowInj, host, null, null);
return shadow;
}
@ -278,9 +274,9 @@ export function main() {
var protoChild1 = new ProtoElementInjector(protoParent, 1, []);
var protoChild2 = new ProtoElementInjector(protoParent, 2, []);
var p = protoParent.instantiate(null, null);
var c1 = protoChild1.instantiate(p, null);
var c2 = protoChild2.instantiate(p, null);
var p = protoParent.instantiate(null);
var c1 = protoChild1.instantiate(p);
var c2 = protoChild2.instantiate(p);
expect(humanize(p, [
[p, 'parent'],
@ -295,8 +291,8 @@ export function main() {
var protoParent = new ProtoElementInjector(null, 0, []);
var protoChild = new ProtoElementInjector(protoParent, 1, [], false, distance);
var p = protoParent.instantiate(null, null);
var c = protoChild.instantiate(p, null);
var p = protoParent.instantiate(null);
var c = protoChild.instantiate(p);
expect(c.directParent()).toEqual(p);
});
@ -306,8 +302,8 @@ export function main() {
var protoParent = new ProtoElementInjector(null, 0, []);
var protoChild = new ProtoElementInjector(protoParent, 1, [], false, distance);
var p = protoParent.instantiate(null, null);
var c = protoChild.instantiate(p, null);
var p = protoParent.instantiate(null);
var c = protoChild.instantiate(p);
expect(c.directParent()).toEqual(null);
});
@ -496,14 +492,14 @@ export function main() {
});
it("should return element", function () {
var element = new NgElement(null);
var element = new NgElement(null, null);
var inj = injector([], null, null, new PreBuiltObjects(null, element, null, null));
expect(inj.get(NgElement)).toEqual(element);
});
it('should return viewContainer', function () {
var viewContainer = new ViewContainer(null, null, null, null, null);
var viewContainer = new ViewContainer(null, null, null, null);
var inj = injector([], null, null, new PreBuiltObjects(null, null, viewContainer, null));
expect(inj.get(ViewContainer)).toEqual(viewContainer);
@ -536,9 +532,9 @@ export function main() {
injWithPrivateComponent.createPrivateComponent(SomeOtherDirective, null);
var shadowDomProtoInjector = new ProtoElementInjector(null, 0, [NeedDirectiveFromAncestor], false);
var shadowDomInj = shadowDomProtoInjector.instantiate(null, injWithPrivateComponent);
var shadowDomInj = shadowDomProtoInjector.instantiate(null);
expect(() => shadowDomInj.instantiateDirectives(appInjector, null, defaultPreBuiltObjects)).
expect(() => shadowDomInj.instantiateDirectives(appInjector, injWithPrivateComponent, null, defaultPreBuiltObjects)).
toThrowError(new RegExp("No provider for SimpleDirective"));
});
@ -547,8 +543,8 @@ export function main() {
injWithPrivateComponent.createPrivateComponent(SimpleDirective, null);
var shadowDomProtoInjector = new ProtoElementInjector(null, 0, [NeedDirectiveFromAncestor], false);
var shadowDomInjector = shadowDomProtoInjector.instantiate(null, injWithPrivateComponent);
shadowDomInjector.instantiateDirectives(appInjector, null, defaultPreBuiltObjects);
var shadowDomInjector = shadowDomProtoInjector.instantiate(null);
shadowDomInjector.instantiateDirectives(appInjector, injWithPrivateComponent, null, defaultPreBuiltObjects);
expect(shadowDomInjector.get(NeedDirectiveFromAncestor)).toBeAnInstanceOf(NeedDirectiveFromAncestor);
expect(shadowDomInjector.get(NeedDirectiveFromAncestor).dependency).toBeAnInstanceOf(SimpleDirective);
@ -566,7 +562,7 @@ export function main() {
expect(inj.getPrivateComponent()).toBe(null);
expect(dir.onDestroyCounter).toBe(1);
inj.instantiateDirectives(null, null, null);
inj.instantiateDirectives(null, null, null, null);
expect(inj.getPrivateComponent()).not.toBe(null);
});
@ -577,15 +573,18 @@ export function main() {
function createpreBuildObject(eventName, eventHandler) {
var handlers = StringMapWrapper.create();
StringMapWrapper.set(handlers, eventName, eventHandler);
var pv = new ProtoView(null, null, null, null);
pv.eventHandlers = [handlers];
var view = new View(pv, null, MapWrapper.create());
var pv = new ProtoView(null, null, null);
pv.bindElement(null, 0, null, null, null);
pv.bindEvent(eventName, new Parser(new Lexer()).parseAction('handler()', ''));
var view = new View(pv, MapWrapper.create());
view.context = new ContextWithHandler(eventHandler);
return new PreBuiltObjects(view, null, null, null);
}
it('should be injectable and callable', () => {
var called = false;
var preBuildObject = createpreBuildObject('click', (e, view) => { called = true;});
var preBuildObject = createpreBuildObject('click', () => { called = true;});
var inj = injector([NeedsEventEmitter], null, null, preBuildObject);
inj.get(NeedsEventEmitter).click();
expect(called).toEqual(true);
@ -593,7 +592,7 @@ export function main() {
it('should be injectable and callable without specifying param type annotation', () => {
var called = false;
var preBuildObject = createpreBuildObject('click', (e, view) => { called = true;});
var preBuildObject = createpreBuildObject('click', () => { called = true;});
var inj = injector([NeedsEventEmitterNoType], null, null, preBuildObject);
inj.get(NeedsEventEmitterNoType).click();
expect(called).toEqual(true);
@ -613,11 +612,17 @@ export function main() {
});
describe('property setter', () => {
it('should be injectable and callable', () => {
var div = el('<div></div>');
var ngElement = new NgElement(div);
var renderer, view;
var preBuildObject = new PreBuiltObjects(null, ngElement, null, null);
beforeEach( () => {
renderer = new FakeRenderer();
var protoView = new ProtoView(renderer, null, null);
view = new View(protoView, MapWrapper.create());
view.render = new ViewRef();
});
it('should be injectable and callable', () => {
var preBuildObject = new PreBuiltObjects(view, null, null, null);
var inj = injector([NeedsPropertySetter], null, null, preBuildObject);
var component = inj.get(NeedsPropertySetter);
component.setProp('foobar');
@ -627,22 +632,21 @@ export function main() {
component.setStyle('40px');
component.setStyleWithUnit(50);
expect(div.title).toEqual('foobar');
expect(DOM.getAttribute(div, 'role')).toEqual('button');
expect(DOM.hasClass(div, 'active')).toEqual(true);
expect(DOM.hasClass(div, 'foo-bar')).toEqual(true);
expect(DOM.getStyle(div, 'width')).toEqual('40px');
expect(DOM.getStyle(div, 'height')).toEqual('50px');
expect(renderer.log[0]).toEqual([view.render, 0, 'title', 'foobar']);
expect(renderer.log[1]).toEqual([view.render, 0, 'attr.role', 'button']);
expect(renderer.log[2]).toEqual([view.render, 0, 'class.active', true]);
expect(renderer.log[3]).toEqual([view.render, 0, 'class.foo-bar', true]);
expect(renderer.log[4]).toEqual([view.render, 0, 'style.width', '40px']);
expect(renderer.log[5]).toEqual([view.render, 0, 'style.height.px', 50]);
});
it('should be injectable and callable without specifying param type annotation', () => {
var div = el('<div></div>');
var preBuildObject = new PreBuiltObjects(null, new NgElement(div), null, null);
var preBuildObject = new PreBuiltObjects(view, null, null, null);
var inj = injector([NeedsPropertySetterNoType], null, null, preBuildObject);
var component = inj.get(NeedsPropertySetterNoType);
component.setProp('foobar');
expect(div.title).toEqual('foobar');
expect(renderer.log[0]).toEqual([view.render, 0, 'title', 'foobar']);
});
});
@ -673,3 +677,22 @@ export function main() {
});
}
class ContextWithHandler {
handler;
constructor(handler) {
this.handler = handler;
}
}
class FakeRenderer extends Renderer {
log:List;
constructor() {
super();
this.log = [];
}
setElementProperty(viewRef, elementIndex, propertyName, value) {
ListWrapper.push(this.log, [viewRef, elementIndex, propertyName, value]);
}
}

View File

@ -5,32 +5,27 @@ import {
xdescribe,
describe,
el,
dispatchEvent,
expect,
iit,
inject,
beforeEachBindings,
it,
xit,
xit
} from 'angular2/test_lib';
import {TestBed} from 'angular2/src/test_lib/test_bed';
import {DOM} from 'angular2/src/dom/dom_adapter';
import {Type, isPresent, BaseException, assertionsEnabled, isJsObject} from 'angular2/src/facade/lang';
import {PromiseWrapper} from 'angular2/src/facade/async';
import {Injector, bind} from 'angular2/di';
import {Lexer, Parser, dynamicChangeDetection,
DynamicChangeDetection, Pipe, PipeRegistry, BindingPropagationConfig, ON_PUSH} from 'angular2/change_detection';
import {dynamicChangeDetection,
ChangeDetection, DynamicChangeDetection, Pipe, PipeRegistry, BindingPropagationConfig, ON_PUSH} from 'angular2/change_detection';
import {Compiler, CompilerCache} from 'angular2/src/core/compiler/compiler';
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
import {ShadowDomStrategy, EmulatedUnscopedShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy';
import {PrivateComponentLocation} from 'angular2/src/core/compiler/private_component_location';
import {PrivateComponentLoader} from 'angular2/src/core/compiler/private_component_loader';
import {TemplateLoader} from 'angular2/src/render/dom/compiler/template_loader';
import {MockTemplateResolver} from 'angular2/src/mock/template_resolver_mock';
import {ComponentUrlMapper} from 'angular2/src/core/compiler/component_url_mapper';
import {UrlResolver} from 'angular2/src/services/url_resolver';
import {StyleUrlResolver} from 'angular2/src/render/dom/shadow_dom/style_url_resolver';
import {EventManager} from 'angular2/src/render/dom/events/event_manager';
import {Decorator, Component, Viewport, DynamicComponent} from 'angular2/src/core/annotations/annotations';
import {Template} from 'angular2/src/core/annotations/template';
@ -43,161 +38,117 @@ import {ViewContainer} from 'angular2/src/core/compiler/view_container';
export function main() {
describe('integration tests', function() {
var directiveMetadataReader, shadowDomStrategy, compiler, tplResolver;
function createCompiler(tplResolver, changedDetection) {
var urlResolver = new UrlResolver();
return new Compiler(changedDetection,
new TemplateLoader(null, null),
directiveMetadataReader,
new Parser(new Lexer()),
new CompilerCache(),
shadowDomStrategy,
tplResolver,
new ComponentUrlMapper(),
urlResolver
);
}
var ctx;
beforeEach( () => {
tplResolver = new MockTemplateResolver();
directiveMetadataReader = new DirectiveMetadataReader();
var urlResolver = new UrlResolver();
shadowDomStrategy = new EmulatedUnscopedShadowDomStrategy(new StyleUrlResolver(urlResolver), null);
compiler = createCompiler(tplResolver, dynamicChangeDetection);
ctx = new MyComp();
});
describe('react to record changes', function() {
var view, ctx, cd;
function createView(pv) {
ctx = new MyComp();
view = pv.instantiate(null, null);
view.hydrate(new Injector([
bind(Compiler).toValue(compiler),
bind(DirectiveMetadataReader).toValue(directiveMetadataReader),
bind(ShadowDomStrategy).toValue(shadowDomStrategy),
bind(EventManager).toValue(null),
PrivateComponentLoader
]), null, null, ctx, null);
cd = view.changeDetector;
}
it('should consume text node changes', inject([AsyncTestCompleter], (async) => {
tplResolver.setTemplate(MyComp, new Template({inline: '<div>{{ctxProp}}</div>'}));
compiler.compile(MyComp).then((pv) => {
createView(pv);
it('should consume text node changes', inject([TestBed, AsyncTestCompleter], (tb, async) => {
tb.overrideTemplate(MyComp, new Template({inline: '<div>{{ctxProp}}</div>'}));
tb.createView(MyComp, {context: ctx}).then((view) => {
ctx.ctxProp = 'Hello World!';
cd.detectChanges();
expect(DOM.getInnerHTML(view.nodes[0])).toEqual('Hello World!');
view.detectChanges();
expect(DOM.getInnerHTML(view.rootNodes[0])).toEqual('Hello World!');
async.done();
});
}));
it('should consume element binding changes', inject([AsyncTestCompleter], (async) => {
tplResolver.setTemplate(MyComp, new Template({inline: '<div [id]="ctxProp"></div>'}));
it('should consume element binding changes', inject([TestBed, AsyncTestCompleter], (tb, async) => {
tb.overrideTemplate(MyComp, new Template({inline: '<div [id]="ctxProp"></div>'}));
compiler.compile(MyComp).then((pv) => {
createView(pv);
tb.createView(MyComp, {context: ctx}).then((view) => {
ctx.ctxProp = 'Hello World!';
cd.detectChanges();
view.detectChanges();
expect(view.nodes[0].id).toEqual('Hello World!');
expect(view.rootNodes[0].id).toEqual('Hello World!');
async.done();
});
}));
it('should consume binding to aria-* attributes', inject([AsyncTestCompleter], (async) => {
tplResolver.setTemplate(MyComp, new Template({inline: '<div [attr.aria-label]="ctxProp"></div>'}));
it('should consume binding to aria-* attributes', inject([TestBed, AsyncTestCompleter], (tb, async) => {
tb.overrideTemplate(MyComp, new Template({inline: '<div [attr.aria-label]="ctxProp"></div>'}));
compiler.compile(MyComp).then((pv) => {
createView(pv);
tb.createView(MyComp, {context: ctx}).then((view) => {
ctx.ctxProp = 'Initial aria label';
cd.detectChanges();
expect(DOM.getAttribute(view.nodes[0], 'aria-label')).toEqual('Initial aria label');
view.detectChanges();
expect(DOM.getAttribute(view.rootNodes[0], 'aria-label')).toEqual('Initial aria label');
ctx.ctxProp = 'Changed aria label';
cd.detectChanges();
expect(DOM.getAttribute(view.nodes[0], 'aria-label')).toEqual('Changed aria label');
view.detectChanges();
expect(DOM.getAttribute(view.rootNodes[0], 'aria-label')).toEqual('Changed aria label');
async.done();
});
}));
it('should consume binding to property names where attr name and property name do not match', inject([AsyncTestCompleter], (async) => {
tplResolver.setTemplate(MyComp, new Template({inline: '<div [tabindex]="ctxNumProp"></div>'}));
it('should consume binding to property names where attr name and property name do not match', inject([TestBed, AsyncTestCompleter], (tb, async) => {
tb.overrideTemplate(MyComp, new Template({inline: '<div [tabindex]="ctxNumProp"></div>'}));
compiler.compile(MyComp).then((pv) => {
createView(pv);
tb.createView(MyComp, {context: ctx}).then((view) => {
cd.detectChanges();
expect(view.nodes[0].tabIndex).toEqual(0);
view.detectChanges();
expect(view.rootNodes[0].tabIndex).toEqual(0);
ctx.ctxNumProp = 5;
cd.detectChanges();
expect(view.nodes[0].tabIndex).toEqual(5);
view.detectChanges();
expect(view.rootNodes[0].tabIndex).toEqual(5);
async.done();
});
}));
it('should consume binding to camel-cased properties using dash-cased syntax in templates', inject([AsyncTestCompleter], (async) => {
tplResolver.setTemplate(MyComp, new Template({inline: '<input [read-only]="ctxBoolProp">'}));
it('should consume binding to camel-cased properties using dash-cased syntax in templates', inject([TestBed, AsyncTestCompleter], (tb, async) => {
tb.overrideTemplate(MyComp, new Template({inline: '<input [read-only]="ctxBoolProp">'}));
compiler.compile(MyComp).then((pv) => {
createView(pv);
tb.createView(MyComp, {context: ctx}).then((view) => {
cd.detectChanges();
expect(view.nodes[0].readOnly).toBeFalsy();
view.detectChanges();
expect(view.rootNodes[0].readOnly).toBeFalsy();
ctx.ctxBoolProp = true;
cd.detectChanges();
expect(view.nodes[0].readOnly).toBeTruthy();
view.detectChanges();
expect(view.rootNodes[0].readOnly).toBeTruthy();
async.done();
});
}));
it('should consume binding to inner-html', inject([AsyncTestCompleter], (async) => {
tplResolver.setTemplate(MyComp, new Template({inline: '<div inner-html="{{ctxProp}}"></div>'}));
it('should consume binding to inner-html', inject([TestBed, AsyncTestCompleter], (tb, async) => {
tb.overrideTemplate(MyComp, new Template({inline: '<div inner-html="{{ctxProp}}"></div>'}));
compiler.compile(MyComp).then((pv) => {
createView(pv);
tb.createView(MyComp, {context: ctx}).then((view) => {
ctx.ctxProp = 'Some <span>HTML</span>';
cd.detectChanges();
expect(DOM.getInnerHTML(view.nodes[0])).toEqual('Some <span>HTML</span>');
view.detectChanges();
expect(DOM.getInnerHTML(view.rootNodes[0])).toEqual('Some <span>HTML</span>');
ctx.ctxProp = 'Some other <div>HTML</div>';
cd.detectChanges();
expect(DOM.getInnerHTML(view.nodes[0])).toEqual('Some other <div>HTML</div>');
view.detectChanges();
expect(DOM.getInnerHTML(view.rootNodes[0])).toEqual('Some other <div>HTML</div>');
async.done();
});
}));
it('should ignore bindings to unknown properties', inject([AsyncTestCompleter], (async) => {
tplResolver.setTemplate(MyComp, new Template({inline: '<div unknown="{{ctxProp}}"></div>'}));
it('should ignore bindings to unknown properties', inject([TestBed, AsyncTestCompleter], (tb, async) => {
tb.overrideTemplate(MyComp, new Template({inline: '<div unknown="{{ctxProp}}"></div>'}));
compiler.compile(MyComp).then((pv) => {
createView(pv);
tb.createView(MyComp, {context: ctx}).then((view) => {
ctx.ctxProp = 'Some value';
cd.detectChanges();
expect(DOM.hasProperty(view.nodes[0], 'unknown')).toBeFalsy();
view.detectChanges();
expect(DOM.hasProperty(view.rootNodes[0], 'unknown')).toBeFalsy();
async.done();
});
}));
it('should consume directive watch expression change.', inject([AsyncTestCompleter], (async) => {
it('should consume directive watch expression change.', inject([TestBed, AsyncTestCompleter], (tb, async) => {
var tpl =
'<div>' +
'<div my-dir [elprop]="ctxProp"></div>' +
@ -205,80 +156,81 @@ export function main() {
'<div my-dir elprop="Hi {{\'there!\'}}"></div>' +
'<div my-dir elprop="One more {{ctxProp}}"></div>' +
'</div>'
tplResolver.setTemplate(MyComp, new Template({inline: tpl, directives: [MyDir]}));
tb.overrideTemplate(MyComp, new Template({inline: tpl, directives: [MyDir]}));
compiler.compile(MyComp).then((pv) => {
createView(pv);
tb.createView(MyComp, {context: ctx}).then((view) => {
ctx.ctxProp = 'Hello World!';
cd.detectChanges();
view.detectChanges();
expect(view.elementInjectors[0].get(MyDir).dirProp).toEqual('Hello World!');
expect(view.elementInjectors[1].get(MyDir).dirProp).toEqual('Hi there!');
expect(view.elementInjectors[2].get(MyDir).dirProp).toEqual('Hi there!');
expect(view.elementInjectors[3].get(MyDir).dirProp).toEqual('One more Hello World!');
expect(view.rawView.elementInjectors[0].get(MyDir).dirProp).toEqual('Hello World!');
expect(view.rawView.elementInjectors[1].get(MyDir).dirProp).toEqual('Hi there!');
expect(view.rawView.elementInjectors[2].get(MyDir).dirProp).toEqual('Hi there!');
expect(view.rawView.elementInjectors[3].get(MyDir).dirProp).toEqual('One more Hello World!');
async.done();
});
}));
it("should support pipes in bindings and bind config", inject([AsyncTestCompleter], (async) => {
tplResolver.setTemplate(MyComp,
new Template({
inline: '<component-with-pipes #comp [prop]="ctxProp | double"></component-with-pipes>',
directives: [ComponentWithPipes]
}));
var registry = new PipeRegistry({
"double" : [new DoublePipeFactory()]
describe('pipes', () => {
beforeEachBindings(() => {
return [bind(ChangeDetection).toFactory(
() => new DynamicChangeDetection(new PipeRegistry({
"double" : [new DoublePipeFactory()]
})),
[]
)];
});
var changeDetection = new DynamicChangeDetection(registry);
var compiler = createCompiler(tplResolver, changeDetection);
compiler.compile(MyComp).then((pv) => {
createView(pv);
ctx.ctxProp = 'a';
cd.detectChanges();
it("should support pipes in bindings and bind config", inject([TestBed, AsyncTestCompleter], (tb, async) => {
tb.overrideTemplate(MyComp,
new Template({
inline: '<component-with-pipes #comp [prop]="ctxProp | double"></component-with-pipes>',
directives: [ComponentWithPipes]
}));
var comp = view.locals.get("comp");
tb.createView(MyComp, {context: ctx}).then((view) => {
// it is doubled twice: once in the binding, second time in the bind config
expect(comp.prop).toEqual('aaaa');
async.done();
});
}));
ctx.ctxProp = 'a';
view.detectChanges();
it('should support nested components.', inject([AsyncTestCompleter], (async) => {
tplResolver.setTemplate(MyComp, new Template({
var comp = view.rawView.locals.get("comp");
// it is doubled twice: once in the binding, second time in the bind config
expect(comp.prop).toEqual('aaaa');
async.done();
});
}));
});
it('should support nested components.', inject([TestBed, AsyncTestCompleter], (tb, async) => {
tb.overrideTemplate(MyComp, new Template({
inline: '<child-cmp></child-cmp>',
directives: [ChildComp]
}));
compiler.compile(MyComp).then((pv) => {
createView(pv);
tb.createView(MyComp, {context: ctx}).then((view) => {
cd.detectChanges();
view.detectChanges();
expect(view.nodes).toHaveText('hello');
expect(view.rootNodes).toHaveText('hello');
async.done();
});
}));
// GH issue 328 - https://github.com/angular/angular/issues/328
it('should support different directive types on a single node', inject([AsyncTestCompleter], (async) => {
tplResolver.setTemplate(MyComp,
it('should support different directive types on a single node', inject([TestBed, AsyncTestCompleter], (tb, async) => {
tb.overrideTemplate(MyComp,
new Template({
inline: '<child-cmp my-dir [elprop]="ctxProp"></child-cmp>',
directives: [MyDir, ChildComp]
}));
compiler.compile(MyComp).then((pv) => {
createView(pv);
tb.createView(MyComp, {context: ctx}).then((view) => {
ctx.ctxProp = 'Hello World!';
cd.detectChanges();
view.detectChanges();
var elInj = view.elementInjectors[0];
var elInj = view.rawView.elementInjectors[0];
expect(elInj.get(MyDir).dirProp).toEqual('Hello World!');
expect(elInj.get(ChildComp).dirProp).toEqual(null);
@ -286,57 +238,54 @@ export function main() {
});
}));
it('should support directives where a binding attribute is not given', inject([AsyncTestCompleter], (async) => {
tplResolver.setTemplate(MyComp,
it('should support directives where a binding attribute is not given', inject([TestBed, AsyncTestCompleter], (tb, async) => {
tb.overrideTemplate(MyComp,
new Template({
// No attribute "el-prop" specified.
inline: '<p my-dir></p>',
directives: [MyDir]
}));
compiler.compile(MyComp).then((pv) => {
createView(pv);
tb.createView(MyComp, {context: ctx}).then((view) => {
async.done();
});
}));
it('should support directives where a selector matches property binding', inject([AsyncTestCompleter], (async) => {
tplResolver.setTemplate(MyComp,
it('should support directives where a selector matches property binding', inject([TestBed, AsyncTestCompleter], (tb, async) => {
tb.overrideTemplate(MyComp,
new Template({
inline: '<p [id]="ctxProp"></p>',
directives: [IdComponent]
}));
compiler.compile(MyComp).then((pv) => {
createView(pv);
tb.createView(MyComp, {context: ctx}).then((view) => {
ctx.ctxProp = 'some_id';
cd.detectChanges();
expect(view.nodes[0].id).toEqual('some_id');
expect(view.nodes).toHaveText('Matched on id with some_id');
view.detectChanges();
expect(view.rootNodes[0].id).toEqual('some_id');
expect(view.rootNodes).toHaveText('Matched on id with some_id');
ctx.ctxProp = 'other_id';
cd.detectChanges();
expect(view.nodes[0].id).toEqual('other_id');
expect(view.nodes).toHaveText('Matched on id with other_id');
view.detectChanges();
expect(view.rootNodes[0].id).toEqual('other_id');
expect(view.rootNodes).toHaveText('Matched on id with other_id');
async.done();
});
}));
it('should support template directives via `<template>` elements.', inject([AsyncTestCompleter], (async) => {
tplResolver.setTemplate(MyComp,
it('should support template directives via `<template>` elements.', inject([TestBed, AsyncTestCompleter], (tb, async) => {
tb.overrideTemplate(MyComp,
new Template({
inline: '<div><template some-viewport var-greeting="some-tmpl"><copy-me>{{greeting}}</copy-me></template></div>',
directives: [SomeViewport]
}));
compiler.compile(MyComp).then((pv) => {
createView(pv);
tb.createView(MyComp, {context: ctx}).then((view) => {
cd.detectChanges();
view.detectChanges();
var childNodesOfWrapper = view.nodes[0].childNodes;
var childNodesOfWrapper = view.rootNodes[0].childNodes;
// 1 template + 2 copies.
expect(childNodesOfWrapper.length).toBe(3);
expect(childNodesOfWrapper[1].childNodes[0].nodeValue).toEqual('hello');
@ -345,18 +294,17 @@ export function main() {
});
}));
it('should support template directives via `template` attribute.', inject([AsyncTestCompleter], (async) => {
tplResolver.setTemplate(MyComp, new Template({
it('should support template directives via `template` attribute.', inject([TestBed, AsyncTestCompleter], (tb, async) => {
tb.overrideTemplate(MyComp, new Template({
inline: '<div><copy-me template="some-viewport: var greeting=some-tmpl">{{greeting}}</copy-me></div>',
directives: [SomeViewport]
}));
compiler.compile(MyComp).then((pv) => {
createView(pv);
tb.createView(MyComp, {context: ctx}).then((view) => {
cd.detectChanges();
view.detectChanges();
var childNodesOfWrapper = view.nodes[0].childNodes;
var childNodesOfWrapper = view.rootNodes[0].childNodes;
// 1 template + 2 copies.
expect(childNodesOfWrapper.length).toBe(3);
expect(childNodesOfWrapper[1].childNodes[0].nodeValue).toEqual('hello');
@ -365,84 +313,79 @@ export function main() {
});
}));
it('should assign the component instance to a var-', inject([AsyncTestCompleter], (async) => {
tplResolver.setTemplate(MyComp, new Template({
it('should assign the component instance to a var-', inject([TestBed, AsyncTestCompleter], (tb, async) => {
tb.overrideTemplate(MyComp, new Template({
inline: '<p><child-cmp var-alice></child-cmp></p>',
directives: [ChildComp]
}));
compiler.compile(MyComp).then((pv) => {
createView(pv);
tb.createView(MyComp, {context: ctx}).then((view) => {
expect(view.locals).not.toBe(null);
expect(view.locals.get('alice')).toBeAnInstanceOf(ChildComp);
expect(view.rawView.locals).not.toBe(null);
expect(view.rawView.locals.get('alice')).toBeAnInstanceOf(ChildComp);
async.done();
})
}));
it('should assign two component instances each with a var-', inject([AsyncTestCompleter], (async) => {
tplResolver.setTemplate(MyComp, new Template({
it('should assign two component instances each with a var-', inject([TestBed, AsyncTestCompleter], (tb, async) => {
tb.overrideTemplate(MyComp, new Template({
inline: '<p><child-cmp var-alice></child-cmp><child-cmp var-bob></p>',
directives: [ChildComp]
}));
compiler.compile(MyComp).then((pv) => {
createView(pv);
tb.createView(MyComp, {context: ctx}).then((view) => {
expect(view.locals).not.toBe(null);
expect(view.locals.get('alice')).toBeAnInstanceOf(ChildComp);
expect(view.locals.get('bob')).toBeAnInstanceOf(ChildComp);
expect(view.locals.get('alice')).not.toBe(view.locals.get('bob'));
expect(view.rawView.locals).not.toBe(null);
expect(view.rawView.locals.get('alice')).toBeAnInstanceOf(ChildComp);
expect(view.rawView.locals.get('bob')).toBeAnInstanceOf(ChildComp);
expect(view.rawView.locals.get('alice')).not.toBe(view.rawView.locals.get('bob'));
async.done();
})
}));
it('should assign the component instance to a var- with shorthand syntax', inject([AsyncTestCompleter], (async) => {
tplResolver.setTemplate(MyComp, new Template({
it('should assign the component instance to a var- with shorthand syntax', inject([TestBed, AsyncTestCompleter], (tb, async) => {
tb.overrideTemplate(MyComp, new Template({
inline: '<child-cmp #alice></child-cmp>',
directives: [ChildComp]
}));
compiler.compile(MyComp).then((pv) => {
createView(pv);
tb.createView(MyComp, {context: ctx}).then((view) => {
expect(view.locals).not.toBe(null);
expect(view.locals.get('alice')).toBeAnInstanceOf(ChildComp);
expect(view.rawView.locals).not.toBe(null);
expect(view.rawView.locals.get('alice')).toBeAnInstanceOf(ChildComp);
async.done();
})
}));
it('should assign the element instance to a user-defined variable', inject([AsyncTestCompleter], (async) => {
tplResolver.setTemplate(MyComp,
it('should assign the element instance to a user-defined variable', inject([TestBed, AsyncTestCompleter], (tb, async) => {
tb.overrideTemplate(MyComp,
new Template({inline: '<p><div var-alice><i>Hello</i></div></p>'}));
compiler.compile(MyComp).then((pv) => {
createView(pv);
expect(view.locals).not.toBe(null);
tb.createView(MyComp, {context: ctx}).then((view) => {
expect(view.rawView.locals).not.toBe(null);
var value = view.locals.get('alice');
var value = view.rawView.locals.get('alice');
expect(value).not.toBe(null);
expect(value.tagName.toLowerCase()).toEqual('div');
expect(value.domElement.tagName.toLowerCase()).toEqual('div');
async.done();
})
}));
it('should assign the element instance to a user-defined variable with camelCase using dash-case', inject([AsyncTestCompleter], (async) => {
tplResolver.setTemplate(MyComp,
it('should assign the element instance to a user-defined variable with camelCase using dash-case', inject([TestBed, AsyncTestCompleter], (tb, async) => {
tb.overrideTemplate(MyComp,
new Template({inline: '<p><div var-super-alice><i>Hello</i></div></p>'}));
compiler.compile(MyComp).then((pv) => {
createView(pv);
expect(view.locals).not.toBe(null);
tb.createView(MyComp, {context: ctx}).then((view) => {
expect(view.rawView.locals).not.toBe(null);
var value = view.locals.get('superAlice');
var value = view.rawView.locals.get('superAlice');
expect(value).not.toBe(null);
expect(value.tagName.toLowerCase()).toEqual('div');
expect(value.domElement.tagName.toLowerCase()).toEqual('div');
async.done();
})
@ -450,49 +393,47 @@ export function main() {
describe("BindingPropagationConfig", () => {
it("can be used to disable the change detection of the component's template",
inject([AsyncTestCompleter], (async) => {
inject([TestBed, AsyncTestCompleter], (tb, async) => {
tplResolver.setTemplate(MyComp, new Template({
tb.overrideTemplate(MyComp, new Template({
inline: '<push-cmp #cmp></push-cmp>',
directives: [[[PushBasedComp]]]
}));
compiler.compile(MyComp).then((pv) => {
createView(pv);
tb.createView(MyComp, {context: ctx}).then((view) => {
var cmp = view.locals.get('cmp');
var cmp = view.rawView.locals.get('cmp');
cd.detectChanges();
view.detectChanges();
expect(cmp.numberOfChecks).toEqual(1);
cd.detectChanges();
view.detectChanges();
expect(cmp.numberOfChecks).toEqual(1);
cmp.propagate();
cd.detectChanges();
view.detectChanges();
expect(cmp.numberOfChecks).toEqual(2);
async.done();
})
}));
it('should not affect updating properties on the component', inject([AsyncTestCompleter], (async) => {
tplResolver.setTemplate(MyComp, new Template({
it('should not affect updating properties on the component', inject([TestBed, AsyncTestCompleter], (tb, async) => {
tb.overrideTemplate(MyComp, new Template({
inline: '<push-cmp [prop]="ctxProp" #cmp></push-cmp>',
directives: [[[PushBasedComp]]]
}));
compiler.compile(MyComp).then((pv) => {
createView(pv);
tb.createView(MyComp, {context: ctx}).then((view) => {
var cmp = view.locals.get('cmp');
var cmp = view.rawView.locals.get('cmp');
ctx.ctxProp = "one";
cd.detectChanges();
view.detectChanges();
expect(cmp.prop).toEqual("one");
ctx.ctxProp = "two";
cd.detectChanges();
view.detectChanges();
expect(cmp.prop).toEqual("two");
async.done();
@ -500,24 +441,23 @@ export function main() {
}));
});
it('should create a component that injects a @Parent', inject([AsyncTestCompleter], (async) => {
tplResolver.setTemplate(MyComp, new Template({
it('should create a component that injects a @Parent', inject([TestBed, AsyncTestCompleter], (tb, async) => {
tb.overrideTemplate(MyComp, new Template({
inline: '<some-directive><cmp-with-parent #child></cmp-with-parent></some-directive>',
directives: [SomeDirective, CompWithParent]
}));
compiler.compile(MyComp).then((pv) => {
createView(pv);
tb.createView(MyComp, {context: ctx}).then((view) => {
var childComponent = view.locals.get('child');
var childComponent = view.rawView.locals.get('child');
expect(childComponent.myParent).toBeAnInstanceOf(SomeDirective);
async.done();
})
}));
it('should create a component that injects an @Ancestor', inject([AsyncTestCompleter], (async) => {
tplResolver.setTemplate(MyComp, new Template({
it('should create a component that injects an @Ancestor', inject([TestBed, AsyncTestCompleter], (tb, async) => {
tb.overrideTemplate(MyComp, new Template({
inline: `
<some-directive>
<p>
@ -527,18 +467,17 @@ export function main() {
directives: [SomeDirective, CompWithAncestor]
}));
compiler.compile(MyComp).then((pv) => {
createView(pv);
tb.createView(MyComp, {context: ctx}).then((view) => {
var childComponent = view.locals.get('child');
var childComponent = view.rawView.locals.get('child');
expect(childComponent.myAncestor).toBeAnInstanceOf(SomeDirective);
async.done();
})
}));
it('should create a component that injects an @Ancestor through viewport directive', inject([AsyncTestCompleter], (async) => {
tplResolver.setTemplate(MyComp, new Template({
it('should create a component that injects an @Ancestor through viewport directive', inject([TestBed, AsyncTestCompleter], (tb, async) => {
tb.overrideTemplate(MyComp, new Template({
inline: `
<some-directive>
<p *if="true">
@ -548,11 +487,10 @@ export function main() {
directives: [SomeDirective, CompWithAncestor, If]
}));
compiler.compile(MyComp).then((pv) => {
createView(pv);
cd.detectChanges();
tb.createView(MyComp, {context: ctx}).then((view) => {
view.detectChanges();
var subview = view.viewContainers[1].get(0);
var subview = view.rawView.viewContainers[1].get(0);
var childComponent = subview.locals.get('child');
expect(childComponent.myAncestor).toBeAnInstanceOf(SomeDirective);
@ -560,16 +498,15 @@ export function main() {
});
}));
it('should support events', inject([AsyncTestCompleter], (async) => {
tplResolver.setTemplate(MyComp, new Template({
it('should support events via EventEmitter', inject([TestBed, AsyncTestCompleter], (tb, async) => {
tb.overrideTemplate(MyComp, new Template({
inline: '<div emitter listener></div>',
directives: [DecoratorEmitingEvent, DecoratorListeningEvent]
}));
compiler.compile(MyComp).then((pv) => {
createView(pv);
tb.createView(MyComp, {context: ctx}).then((view) => {
var injector = view.elementInjectors[0];
var injector = view.rawView.elementInjectors[0];
var emitter = injector.get(DecoratorEmitingEvent);
var listener = injector.get(DecoratorListeningEvent);
@ -585,34 +522,54 @@ export function main() {
});
}));
it('should support dynamic components', inject([AsyncTestCompleter], (async) => {
tplResolver.setTemplate(MyComp, new Template({
if (DOM.supportsDOMEvents()) {
it('should support render events', inject([TestBed, AsyncTestCompleter], (tb, async) => {
tb.overrideTemplate(MyComp, new Template({
inline: '<div listener></div>',
directives: [DecoratorListeningDomEvent]
}));
tb.createView(MyComp, {context: ctx}).then((view) => {
var injector = view.rawView.elementInjectors[0];
var listener = injector.get(DecoratorListeningDomEvent);
dispatchEvent(view.rootNodes[0], 'domEvent');
expect(listener.eventType).toEqual('domEvent');
async.done();
});
}));
}
it('should support dynamic components', inject([TestBed, AsyncTestCompleter], (tb, async) => {
tb.overrideTemplate(MyComp, new Template({
inline: '<dynamic-comp #dynamic></dynamic-comp>',
directives: [DynamicComp]
}));
compiler.compile(MyComp).then((pv) => {
createView(pv);
tb.createView(MyComp, {context: ctx}).then((view) => {
var dynamicComponent = view.locals.get("dynamic");
var dynamicComponent = view.rawView.locals.get("dynamic");
expect(dynamicComponent).toBeAnInstanceOf(DynamicComp);
dynamicComponent.done.then((_) => {
cd.detectChanges();
expect(view.nodes).toHaveText('hello');
view.detectChanges();
expect(view.rootNodes).toHaveText('hello');
async.done();
});
});
}));
it('should support static attributes', inject([AsyncTestCompleter], (async) => {
tplResolver.setTemplate(MyComp, new Template({
it('should support static attributes', inject([TestBed, AsyncTestCompleter], (tb, async) => {
tb.overrideTemplate(MyComp, new Template({
inline: '<input static type="text" title></input>',
directives: [NeedsAttribute]
}));
compiler.compile(MyComp).then((pv) => {
createView(pv);
tb.createView(MyComp, {context: ctx}).then((view) => {
var injector = view.elementInjectors[0];
var injector = view.rawView.elementInjectors[0];
var needsAttribute = injector.get(NeedsAttribute);
expect(needsAttribute.typeAttribute).toEqual('text');
expect(needsAttribute.titleAttribute).toEqual('');
@ -632,9 +589,9 @@ export function main() {
if (assertionsEnabled()) {
function expectCompileError(inlineTpl, errMessage, done) {
tplResolver.setTemplate(MyComp, new Template({inline: inlineTpl}));
PromiseWrapper.then(compiler.compile(MyComp),
function expectCompileError(tb, inlineTpl, errMessage, done) {
tb.overrideTemplate(MyComp, new Template({inline: inlineTpl}));
PromiseWrapper.then(tb.createView(MyComp),
(value) => {
throw new BaseException("Test failure: should not have come here as an exception was expected");
},
@ -645,32 +602,36 @@ export function main() {
);
}
it('should raise an error if no directive is registered for a template with template bindings', inject([AsyncTestCompleter], (async) => {
it('should raise an error if no directive is registered for a template with template bindings', inject([TestBed, AsyncTestCompleter], (tb, async) => {
expectCompileError(
tb,
'<div><div template="if: foo"></div></div>',
'Missing directive to handle \'if\' in <div template="if: foo">',
() => async.done()
);
}));
it('should raise an error for missing template directive (1)', inject([AsyncTestCompleter], (async) => {
it('should raise an error for missing template directive (1)', inject([TestBed, AsyncTestCompleter], (tb, async) => {
expectCompileError(
tb,
'<div><template foo></template></div>',
'Missing directive to handle: <template foo>',
() => async.done()
);
}));
it('should raise an error for missing template directive (2)', inject([AsyncTestCompleter], (async) => {
it('should raise an error for missing template directive (2)', inject([TestBed, AsyncTestCompleter], (tb, async) => {
expectCompileError(
tb,
'<div><template *if="condition"></template></div>',
'Missing directive to handle: <template *if="condition">',
() => async.done()
);
}));
it('should raise an error for missing template directive (3)', inject([AsyncTestCompleter], (async) => {
it('should raise an error for missing template directive (3)', inject([TestBed, AsyncTestCompleter], (tb, async) => {
expectCompileError(
tb,
'<div *if="condition"></div>',
'Missing directive to handle \'if\' in MyComp: <div *if="condition">',
() => async.done()
@ -746,6 +707,8 @@ class PushBasedComp {
}
@Component()
@Template({directives: [
]})
class MyComp {
ctxProp:string;
ctxNumProp;
@ -909,6 +872,22 @@ class DecoratorListeningEvent {
}
}
@Decorator({
selector: '[listener]',
events: {'domEvent': 'onEvent($event.type)'}
})
class DecoratorListeningDomEvent {
eventType: string;
constructor() {
this.eventType = '';
}
onEvent(eventType: string) {
this.eventType = eventType;
}
}
@Component({
selector: '[id]',
bind: {'id': 'id'}

View File

@ -14,7 +14,7 @@ export function main() {
var loader;
beforeEach(() => {
loader = new PrivateComponentLoader(null, null, null, new DirectiveMetadataReader());
loader = new PrivateComponentLoader(null, new DirectiveMetadataReader(), null);
});
describe('Load errors', () => {

View File

@ -1,47 +0,0 @@
import {describe, beforeEach, it, expect, ddescribe, iit, SpyObject, el, proxy} from 'angular2/test_lib';
import {IMPLEMENTS} from 'angular2/src/facade/lang';
import {DOM} from 'angular2/src/dom/dom_adapter';
import {Content} from 'angular2/src/core/compiler/shadow_dom_emulation/content_tag';
import {LightDom} from 'angular2/src/core/compiler/shadow_dom_emulation/light_dom';
@proxy
@IMPLEMENTS(LightDom)
class DummyLightDom extends SpyObject {noSuchMethod(m){super.noSuchMethod(m)}}
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(null, content, '');
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(null, content, '');
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(null, content, '');
c.insert([el("<a></a>")]);
c.insert([]);
expect(DOM.getInnerHTML(parent)).toEqual(`${_scriptStart}${_scriptEnd}`);
});
});
}

View File

@ -1,219 +0,0 @@
import {describe, beforeEach, it, expect, ddescribe, iit, SpyObject, el, proxy} 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/core/compiler/shadow_dom_emulation/content_tag';
import {LightDom} from 'angular2/src/core/compiler/shadow_dom_emulation/light_dom';
import {View} from 'angular2/src/core/compiler/view';
import {ViewContainer} from 'angular2/src/core/compiler/view_container';
@proxy
@IMPLEMENTS(View)
class FakeView {
contentTags;
viewContainers;
constructor(containers = null) {
this.contentTags = [];
this.viewContainers = [];
if (isPresent(containers)) {
ListWrapper.forEach(containers, (c) => {
if (c instanceof FakeContentTag) {
ListWrapper.push(this.contentTags, c);
} else {
ListWrapper.push(this.contentTags, null);
}
if (c instanceof FakeViewContainer) {
ListWrapper.push(this.viewContainers, c);
} else {
ListWrapper.push(this.viewContainers, null);
}
});
}
}
noSuchMethod(i) {
super.noSuchMethod(i);
}
}
@proxy
@IMPLEMENTS(ViewContainer)
class FakeViewContainer {
templateElement;
_nodes;
_contentTagContainers;
constructor(templateEl, nodes = null, views = null) {
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 {
select;
_nodes;
contentStartElement;
constructor(contentEl, select = '', nodes = null) {
this.contentStartElement = contentEl;
this.select = select;
this._nodes = nodes;
}
insert(nodes){
this._nodes = ListWrapper.clone(nodes);
}
nodes() {
return this._nodes;
}
noSuchMethod(i) {
super.noSuchMethod(i);
}
}
export function main() {
describe('LightDom', function() {
var lightDomView;
beforeEach(() => {
lightDomView = new FakeView();
});
describe("contentTags", () => {
it("should collect content tags from element injectors", () => {
var tag = new FakeContentTag(el('<script></script>'));
var shadowDomView = new FakeView([tag]);
var lightDom = new LightDom(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 = new LightDom(lightDomView, shadowDomView,
el("<div></div>"));
expect(lightDom.contentTags()).toEqual([tag]);
});
});
describe("expandedDomNodes", () => {
it("should contain root nodes", () => {
var lightDomEl = el("<div><a></a></div>")
var lightDom = new LightDom(lightDomView, new FakeView(), lightDomEl);
expect(toHtml(lightDom.expandedDomNodes())).toEqual(["<a></a>"]);
});
it("should include view container nodes", () => {
var lightDomEl = el("<div><template></template></div>");
var lightDom = new LightDom(
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 = new LightDom(
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 = new LightDom(
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 = new LightDom(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 = new LightDom(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([]);
});
});
});
}
function toHtml(nodes) {
if (isBlank(nodes)) return [];
return ListWrapper.map(nodes, DOM.getOuterHTML);
}

View File

@ -1,384 +0,0 @@
import {
AsyncTestCompleter,
beforeEach,
ddescribe,
describe,
el,
expect,
iit,
inject,
it,
xit,
} from 'angular2/test_lib';
import {StringMapWrapper, List} from 'angular2/src/facade/collection';
import {Type} from 'angular2/src/facade/lang';
import {DOM} from 'angular2/src/dom/dom_adapter';
import {Injector} from 'angular2/di';
import {Lexer, Parser, ChangeDetector, dynamicChangeDetection} from 'angular2/change_detection';
import {ExceptionHandler} from 'angular2/src/core/exception_handler';
import {Compiler, CompilerCache} from 'angular2/src/core/compiler/compiler';
import {LifeCycle} from 'angular2/src/core/life_cycle/life_cycle';
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
import {ShadowDomStrategy,
NativeShadowDomStrategy,
EmulatedScopedShadowDomStrategy,
EmulatedUnscopedShadowDomStrategy,
} from 'angular2/src/core/compiler/shadow_dom_strategy';
import {TemplateLoader} from 'angular2/src/render/dom/compiler/template_loader';
import {ComponentUrlMapper} from 'angular2/src/core/compiler/component_url_mapper';
import {UrlResolver} from 'angular2/src/services/url_resolver';
import {StyleUrlResolver} from 'angular2/src/render/dom/shadow_dom/style_url_resolver';
import {StyleInliner} from 'angular2/src/render/dom/shadow_dom/style_inliner';
import {MockTemplateResolver} from 'angular2/src/mock/template_resolver_mock';
import {Decorator, Component, Viewport} from 'angular2/src/core/annotations/annotations';
import {Template} from 'angular2/src/core/annotations/template';
import {ViewContainer} from 'angular2/src/core/compiler/view_container';
import {BrowserDomAdapter} from 'angular2/src/dom/browser_adapter';
export function main() {
BrowserDomAdapter.makeCurrent();
describe('integration tests', function() {
var urlResolver;
var styleUrlResolver;
var styleInliner;
var strategies = {
"scoped" : () => new EmulatedScopedShadowDomStrategy(styleInliner, styleUrlResolver, DOM.createElement('div')),
"unscoped" : () => new EmulatedUnscopedShadowDomStrategy(styleUrlResolver, DOM.createElement('div'))
}
if (DOM.supportsNativeShadowDOM()) {
StringMapWrapper.set(strategies, "native", () => new NativeShadowDomStrategy(styleUrlResolver));
}
StringMapWrapper.forEach(strategies,
(strategyFactory, name) => {
describe(`${name} shadow dom strategy`, () => {
var compiler, tplResolver;
beforeEach(() => {
urlResolver = new UrlResolver();
styleUrlResolver = new StyleUrlResolver(urlResolver);
styleInliner = new StyleInliner(null, styleUrlResolver, urlResolver);
tplResolver = new MockTemplateResolver();
compiler = new Compiler(dynamicChangeDetection,
new TemplateLoader(null, null),
new DirectiveMetadataReader(),
new Parser(new Lexer()),
new CompilerCache(),
strategyFactory(),
tplResolver,
new ComponentUrlMapper(),
urlResolver
);
});
function compile(template, directives: List<Type>, assertions) {
tplResolver.setTemplate(MyComp, new Template({
inline: template,
directives: directives
}));
compiler.compile(MyComp)
.then(createView)
.then((view) => {
var lc = new LifeCycle(new ExceptionHandler(), view.changeDetector, false);
assertions(view, lc);
});
}
it('should support simple components', inject([AsyncTestCompleter], (async) => {
var temp = '<simple>' +
'<div>A</div>' +
'</simple>';
compile(temp, [Simple], (view, lc) => {
expect(view.nodes).toHaveText('SIMPLE(A)');
async.done();
});
}));
it('should support multiple content tags', inject([AsyncTestCompleter], (async) => {
var temp = '<multiple-content-tags>' +
'<div>B</div>' +
'<div>C</div>' +
'<div class="left">A</div>' +
'</multiple-content-tags>';
compile(temp, [MultipleContentTagsComponent], (view, lc) => {
expect(view.nodes).toHaveText('(A, BC)');
async.done();
});
}));
it('should redistribute only direct children', inject([AsyncTestCompleter], (async) => {
var temp = '<multiple-content-tags>' +
'<div>B<div class="left">A</div></div>' +
'<div>C</div>' +
'</multiple-content-tags>';
compile(temp, [MultipleContentTagsComponent], (view, lc) => {
expect(view.nodes).toHaveText('(, BAC)');
async.done();
});
}));
it("should redistribute direct child viewcontainers when the light dom changes", inject([AsyncTestCompleter], (async) => {
var temp = '<multiple-content-tags>' +
'<div><div template="manual" class="left">A</div></div>' +
'<div>B</div>' +
'</multiple-content-tags>';
compile(temp, [MultipleContentTagsComponent, ManualViewportDirective], (view, lc) => {
var dir = view.elementInjectors[1].get(ManualViewportDirective);
expect(view.nodes).toHaveText('(, B)');
dir.show();
lc.tick();
expect(view.nodes).toHaveText('(, AB)');
dir.hide();
lc.tick();
expect(view.nodes).toHaveText('(, B)');
async.done();
});
}));
it("should redistribute when the light dom changes", inject([AsyncTestCompleter], (async) => {
var temp = '<multiple-content-tags>' +
'<div template="manual" class="left">A</div>' +
'<div>B</div>' +
'</multiple-content-tags>';
compile(temp, [MultipleContentTagsComponent, ManualViewportDirective], (view, lc) => {
var dir = view.elementInjectors[1].get(ManualViewportDirective);
expect(view.nodes).toHaveText('(, B)');
dir.show();
lc.tick();
expect(view.nodes).toHaveText('(A, B)');
dir.hide();
lc.tick();
expect(view.nodes).toHaveText('(, B)');
async.done();
});
}));
it("should support nested components", inject([AsyncTestCompleter], (async) => {
var temp = '<outer-with-indirect-nested>' +
'<div>A</div>' +
'<div>B</div>' +
'</outer-with-indirect-nested>';
compile(temp, [OuterWithIndirectNestedComponent], (view, lc) => {
expect(view.nodes).toHaveText('OUTER(SIMPLE(AB))');
async.done();
});
}));
it("should support nesting with content being direct child of a nested component", inject([AsyncTestCompleter], (async) => {
var temp = '<outer>' +
'<div template="manual" class="left">A</div>' +
'<div>B</div>' +
'<div>C</div>' +
'</outer>';
compile(temp, [OuterComponent, ManualViewportDirective], (view, lc) => {
var dir = view.elementInjectors[1].get(ManualViewportDirective);
expect(view.nodes).toHaveText('OUTER(INNER(INNERINNER(,BC)))');
dir.show();
lc.tick();
expect(view.nodes).toHaveText('OUTER(INNER(INNERINNER(A,BC)))');
async.done();
});
}));
it('should redistribute when the shadow dom changes', inject([AsyncTestCompleter], (async) => {
var temp = '<conditional-content>' +
'<div class="left">A</div>' +
'<div>B</div>' +
'<div>C</div>' +
'</conditional-content>';
compile(temp, [ConditionalContentComponent, AutoViewportDirective], (view, lc) => {
var cmp = view.elementInjectors[0].get(ConditionalContentComponent);
expect(view.nodes).toHaveText('(, ABC)');
cmp.showLeft();
lc.tick();
expect(view.nodes).toHaveText('(A, BC)');
cmp.hideLeft();
lc.tick();
expect(view.nodes).toHaveText('(, ABC)');
async.done();
});
}));
//Implement once NgElement 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.nodes[1];
// input.focus();
//
// expect(document.activeElement.id).toEqual("focused-input");
//
// // update class of input
//
// expect(document.activeElement.id).toEqual("focused-input");
// });
//});
});
});
});
}
class TestDirectiveMetadataReader extends DirectiveMetadataReader {
shadowDomStrategy;
constructor(shadowDomStrategy) {
super();
this.shadowDomStrategy = shadowDomStrategy;
}
parseShadowDomStrategy(annotation:Component):ShadowDomStrategy{
return this.shadowDomStrategy;
}
}
@Viewport({
selector: '[manual]'
})
class ManualViewportDirective {
viewContainer;
constructor(viewContainer:ViewContainer) {
this.viewContainer = viewContainer;
}
show() { this.viewContainer.create(); }
hide() { this.viewContainer.remove(0); }
}
@Viewport({
selector: '[auto]',
bind: {
'auto': 'auto'
}
})
class AutoViewportDirective {
viewContainer;
constructor(viewContainer:ViewContainer) {
this.viewContainer = viewContainer;
}
set auto(newValue:boolean) {
if (newValue) {
this.viewContainer.create();
} else {
this.viewContainer.remove(0);
}
}
}
@Component({selector: 'simple'})
@Template({inline: 'SIMPLE(<content></content>)'})
class Simple {
}
@Component({selector: 'multiple-content-tags'})
@Template({
inline: '(<content select=".left"></content>, <content></content>)'
})
class MultipleContentTagsComponent {
}
@Component({selector: 'conditional-content'})
@Template({
inline: '<div>(<div *auto="cond"><content select=".left"></content></div>, <content></content>)</div>',
directives: [AutoViewportDirective]
})
class ConditionalContentComponent {
cond:boolean;
constructor() {
this.cond = false;
}
showLeft() { this.cond = true; }
hideLeft() { this.cond = false; }
}
@Component({selector: 'outer-with-indirect-nested'})
@Template({
inline: 'OUTER(<simple><div><content></content></div></simple>)',
directives: [Simple]
})
class OuterWithIndirectNestedComponent {
}
@Component({selector: 'outer'})
@Template({
inline: 'OUTER(<inner><content></content></inner>)',
directives: [InnerComponent]
})
class OuterComponent {
}
@Component({selector: 'inner'})
@Template({
inline: 'INNER(<innerinner><content></content></innerinner>)',
directives: [InnerInnerComponent]
})
class InnerComponent {
}
@Component({selector: 'innerinner'})
@Template({
inline: 'INNERINNER(<content select=".left"></content>,<content></content>)'
})
class InnerInnerComponent {
}
@Component({selector: 'my-comp'})
@Template({
directives: [MultipleContentTagsComponent, ManualViewportDirective,
ConditionalContentComponent, OuterWithIndirectNestedComponent, OuterComponent]
})
class MyComp {
}
function createView(pv) {
var view = pv.instantiate(null, null);
view.hydrate(new Injector([]), null, null, {}, null);
return view;
}

View File

@ -1,130 +0,0 @@
import {
AsyncTestCompleter,
beforeEach,
ddescribe,
describe,
el,
expect,
iit,
inject,
it,
SpyObject,
} from 'angular2/test_lib';
import {
NativeShadowDomStrategy,
EmulatedScopedShadowDomStrategy,
EmulatedUnscopedShadowDomStrategy
} from 'angular2/src/core/compiler/shadow_dom_strategy';
import {UrlResolver} from 'angular2/src/services/url_resolver';
import {StyleUrlResolver} from 'angular2/src/render/dom/shadow_dom/style_url_resolver';
import {StyleInliner} from 'angular2/src/render/dom/shadow_dom/style_inliner';
import {ProtoView} from 'angular2/src/core/compiler/view';
import {XHR} from 'angular2/src/services/xhr';
import {isPresent, isBlank} from 'angular2/src/facade/lang';
import {DOM} from 'angular2/src/dom/dom_adapter';
import {Map, MapWrapper} from 'angular2/src/facade/collection';
import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
import {DynamicProtoChangeDetector} from 'angular2/change_detection';
export function main() {
var strategy;
describe('NativeShadowDomStratgey', () => {
beforeEach(() => {
var urlResolver = new UrlResolver();
var styleUrlResolver = new StyleUrlResolver(urlResolver);
strategy = new NativeShadowDomStrategy(styleUrlResolver);
});
it('should attach the view nodes to the shadow root', () => {
var host = el('<div></div>');
var nodes = el('<div>view</div>');
var pv = new ProtoView(null, nodes, new DynamicProtoChangeDetector(null, null), null);
var view = pv.instantiate(null, null);
strategy.attachTemplate(host, view);
var shadowRoot = DOM.getShadowRoot(host);
expect(isPresent(shadowRoot)).toBeTruthy();
expect(shadowRoot).toHaveText('view');
});
});
describe('EmulatedScopedShadowDomStratgey', () => {
var xhr, styleHost;
beforeEach(() => {
var urlResolver = new UrlResolver();
var styleUrlResolver = new StyleUrlResolver(urlResolver);
xhr = new FakeXHR();
var styleInliner = new StyleInliner(xhr, styleUrlResolver, urlResolver);
styleHost = el('<div></div>');
strategy = new EmulatedScopedShadowDomStrategy(styleInliner, styleUrlResolver, styleHost);
});
it('should attach the view nodes as child of the host element', () => {
var host = el('<div><span>original content</span></div>');
var nodes = el('<div>view</div>');
var pv = new ProtoView(null, nodes, new DynamicProtoChangeDetector(null, null), null);
var view = pv.instantiate(null, null);
strategy.attachTemplate(host, view);
var firstChild = DOM.firstChild(host);
expect(DOM.tagName(firstChild).toLowerCase()).toEqual('div');
expect(firstChild).toHaveText('view');
expect(host).toHaveText('view');
});
});
describe('EmulatedUnscopedShadowDomStratgey', () => {
var styleHost;
beforeEach(() => {
var urlResolver = new UrlResolver();
var styleUrlResolver = new StyleUrlResolver(urlResolver);
styleHost = el('<div></div>');
strategy = new EmulatedUnscopedShadowDomStrategy(styleUrlResolver, styleHost);
});
it('should attach the view nodes as child of the host element', () => {
var host = el('<div><span>original content</span></div>');
var nodes = el('<div>view</div>');
var pv = new ProtoView(null, nodes, new DynamicProtoChangeDetector(null, null), null);
var view = pv.instantiate(null, null);
strategy.attachTemplate(host, view);
var firstChild = DOM.firstChild(host);
expect(DOM.tagName(firstChild).toLowerCase()).toEqual('div');
expect(firstChild).toHaveText('view');
expect(host).toHaveText('view');
});
});
}
class FakeXHR extends XHR {
_responses: Map;
constructor() {
super();
this._responses = MapWrapper.create();
}
get(url: string): Promise<string> {
var response = MapWrapper.get(this._responses, url);
if (isBlank(response)) {
return PromiseWrapper.reject('xhr error');
}
return PromiseWrapper.resolve(response);
}
reply(url: string, response: string) {
MapWrapper.set(this._responses, url, response);
}
}
class SomeComponent {}
class SomeOtherComponent {}

View File

@ -1,251 +0,0 @@
import {describe, xit, it, expect, beforeEach, ddescribe, iit, el, proxy} from 'angular2/test_lib';
import {View, ProtoView} from 'angular2/src/core/compiler/view';
import {ViewContainer} from 'angular2/src/core/compiler/view_container';
import {IMPLEMENTS} from 'angular2/src/facade/lang';
import {DOM} from 'angular2/src/dom/dom_adapter';
import {ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
import {Injector} from 'angular2/di';
import {ProtoElementInjector, ElementInjector} from 'angular2/src/core/compiler/element_injector';
import {NativeShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy';
import {DynamicProtoChangeDetector, ChangeDetector, Lexer, Parser} from 'angular2/change_detection';
function createView(nodes) {
var view = new View(null, nodes, MapWrapper.create());
var cd = new DynamicProtoChangeDetector(null, null).instantiate(view, [], null, []);
view.init(cd, [], [], [], [], [], [], [], [], []);
return view;
}
@proxy
@IMPLEMENTS(ChangeDetector)
class AttachableChangeDetector {
parent;
constructor() {
}
remove() {
this.parent = null;
}
noSuchMethod(i) {
super.noSuchMethod(i);
}
}
@proxy
@IMPLEMENTS(View)
class HydrateAwareFakeView {
isHydrated: boolean;
nodes: List;
changeDetector: ChangeDetector;
rootElementInjectors;
constructor(isHydrated) {
this.isHydrated = isHydrated;
this.nodes = [DOM.createElement('div')];
this.rootElementInjectors = [];
this.changeDetector = new AttachableChangeDetector();
}
hydrated() {
return this.isHydrated;
}
hydrate(_, __, ___, ____, _____) {
this.isHydrated = true;
}
dehydrate() {
this.isHydrated = false;
}
noSuchMethod(i) {
super.noSuchMethod(i);
}
}
export function main() {
describe('ViewContainer', () => {
var viewContainer, parentView, protoView, dom, customViewWithOneNode,
customViewWithTwoNodes, elementInjector;
beforeEach(() => {
dom = el(`<div><stuff></stuff><div insert-after-me></div><stuff></stuff></div>`);
var insertionElement = dom.childNodes[1];
parentView = createView([dom.childNodes[0]]);
protoView = new ProtoView(null, el('<div>hi</div>'), new DynamicProtoChangeDetector(null, null),
new NativeShadowDomStrategy(null));
elementInjector = new ElementInjector(null, null, null);
viewContainer = new ViewContainer(parentView, insertionElement, protoView, elementInjector,
null);
customViewWithOneNode = createView([el('<div>single</div>')]);
customViewWithTwoNodes = createView([el('<div>one</div>'), el('<div>two</div>')]);
});
describe('when dehydrated', () => {
it('should throw if create is called', () => {
expect(() => viewContainer.create()).toThrowError();
});
});
describe('when hydrated', () => {
function textInViewContainer() {
var out = '';
// skipping starting filler, insert-me and final filler.
for (var i = 2; i < dom.childNodes.length - 1; i++) {
if (i != 2) out += ' ';
out += DOM.getInnerHTML(dom.childNodes[i]);
}
return out;
}
beforeEach(() => {
viewContainer.hydrate(new Injector([]), null, null);
var fillerView = createView([el('<filler>filler</filler>')]);
viewContainer.insert(fillerView);
});
it('should create new views from protoView', () => {
viewContainer.create();
expect(textInViewContainer()).toEqual('filler hi');
expect(viewContainer.length).toBe(2);
});
it('should create new views from protoView at index', () => {
viewContainer.create(0);
expect(textInViewContainer()).toEqual('hi filler');
expect(viewContainer.length).toBe(2);
});
it('should insert new views at the end by default', () => {
viewContainer.insert(customViewWithOneNode);
expect(textInViewContainer()).toEqual('filler single');
expect(viewContainer.get(1)).toBe(customViewWithOneNode);
expect(viewContainer.length).toBe(2);
});
it('should insert new views at the given index', () => {
viewContainer.insert(customViewWithOneNode, 0);
expect(textInViewContainer()).toEqual('single filler');
expect(viewContainer.get(0)).toBe(customViewWithOneNode);
expect(viewContainer.length).toBe(2);
});
it('should remove the last view by default', () => {
viewContainer.insert(customViewWithOneNode);
viewContainer.remove();
expect(textInViewContainer()).toEqual('filler');
expect(viewContainer.length).toBe(1);
});
it('should remove the view at a given index', () => {
viewContainer.insert(customViewWithOneNode);
viewContainer.insert(customViewWithTwoNodes);
viewContainer.remove(1);
expect(textInViewContainer()).toEqual('filler one two');
expect(viewContainer.get(1)).toBe(customViewWithTwoNodes);
expect(viewContainer.length).toBe(2);
});
it('should detach the last view by default', () => {
viewContainer.insert(customViewWithOneNode);
expect(viewContainer.length).toBe(2);
var detachedView = viewContainer.detach();
expect(detachedView).toBe(customViewWithOneNode);
expect(textInViewContainer()).toEqual('filler');
expect(viewContainer.length).toBe(1);
});
it('should detach the view at a given index', () => {
viewContainer.insert(customViewWithOneNode);
viewContainer.insert(customViewWithTwoNodes);
expect(viewContainer.length).toBe(3);
var detachedView = viewContainer.detach(1);
expect(detachedView).toBe(customViewWithOneNode);
expect(textInViewContainer()).toEqual('filler one two');
expect(viewContainer.length).toBe(2);
});
it('should keep views hydration state during insert', () => {
var hydratedView = new HydrateAwareFakeView(true);
var dehydratedView = new HydrateAwareFakeView(false);
viewContainer.insert(hydratedView);
viewContainer.insert(dehydratedView);
expect(hydratedView.hydrated()).toBe(true);
expect(dehydratedView.hydrated()).toBe(false);
});
it('should dehydrate on remove', () => {
var hydratedView = new HydrateAwareFakeView(true);
viewContainer.insert(hydratedView);
viewContainer.remove();
expect(hydratedView.hydrated()).toBe(false);
});
it('should keep views hydration state during detach', () => {
var hydratedView = new HydrateAwareFakeView(true);
var dehydratedView = new HydrateAwareFakeView(false);
viewContainer.insert(hydratedView);
viewContainer.insert(dehydratedView);
expect(viewContainer.detach().hydrated()).toBe(false);
expect(viewContainer.detach().hydrated()).toBe(true);
});
it('should support adding/removing views with more than one node', () => {
viewContainer.insert(customViewWithTwoNodes);
viewContainer.insert(customViewWithOneNode);
expect(textInViewContainer()).toEqual('filler one two single');
viewContainer.remove(1);
expect(textInViewContainer()).toEqual('filler single');
});
});
describe('should update injectors and parent views.', () => {
var fancyView;
beforeEach(() => {
var parser = new Parser(new Lexer());
viewContainer.hydrate(new Injector([]), null, null);
var pv = new ProtoView(null, el('<div class="ng-binding">{{}}</div>'),
new DynamicProtoChangeDetector(null, null), new NativeShadowDomStrategy(null));
pv.bindElement(null, 0, new ProtoElementInjector(null, 1, [SomeDirective]));
pv.bindTextNode(0, parser.parseBinding('foo', null));
fancyView = pv.instantiate(null, null);
});
it('hydrating should update rootElementInjectors and parent change detector', () => {
viewContainer.insert(fancyView);
ListWrapper.forEach(fancyView.rootElementInjectors, (inj) =>
expect(inj.parent).toBe(elementInjector));
expect(parentView.changeDetector.lightDomChildren.length).toBe(1);
});
it('dehydrating should update rootElementInjectors and parent change detector', () => {
viewContainer.insert(fancyView);
viewContainer.remove();
ListWrapper.forEach(fancyView.rootElementInjectors, (inj) =>
expect(inj.parent).toBe(null));
expect(parentView.changeDetector.lightDomChildren.length).toBe(0);
expect(viewContainer.length).toBe(0);
});
});
});
}
class SomeDirective {
prop;
constructor() {
this.prop = 'foo';
}
}

View File

@ -1,46 +0,0 @@
import {describe, xit, it, expect, beforeEach, ddescribe, iit, el, proxy} from 'angular2/test_lib';
import {View} from 'angular2/src/core/compiler/view';
import {ViewPool} from 'angular2/src/core/compiler/view_pool';
import {IMPLEMENTS} from 'angular2/src/facade/lang';
@proxy
@IMPLEMENTS(View)
class FakeView {
noSuchMethod(i) {
super.noSuchMethod(i);
}
}
export function main() {
describe('ViewPool', () => {
var viewPool, capacity = 3;
beforeEach(() => {
viewPool = new ViewPool(capacity);
})
it('should return null when there are no views', () => {
expect(viewPool.pop()).toBeNull();
expect(viewPool.length()).toBe(0);
})
it('should support storing and retrieving a view', () => {
var view = new FakeView();
viewPool.push(view);
expect(viewPool.length()).toBe(1);
expect(viewPool.pop()).toBe(view);
expect(viewPool.length()).toBe(0);
})
it('should not store more views that its capacity', () => {
for (var i = 0; i < capacity * 2; i++) viewPool.push(new FakeView());
expect(viewPool.length()).toBe(capacity);
for (var i = 0; i < capacity; i++) {
expect(viewPool.pop()).not.toBe(null);
}
expect(viewPool.pop()).toBeNull();
})
})
}

View File

@ -1,787 +0,0 @@
import {describe, xit, it, expect, beforeEach, ddescribe, iit, el, proxy} from 'angular2/test_lib';
import {ProtoView, ElementPropertyMemento, DirectivePropertyMemento} from 'angular2/src/core/compiler/view';
import {ProtoElementInjector, ElementInjector, DirectiveBinding} from 'angular2/src/core/compiler/element_injector';
import {EmulatedScopedShadowDomStrategy, NativeShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy';
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
import {Component, Decorator, Viewport, Directive, onChange, onAllChangesDone} from 'angular2/src/core/annotations/annotations';
import {Lexer, Parser, DynamicProtoChangeDetector,
ChangeDetector} from 'angular2/change_detection';
import {EventEmitter} from 'angular2/src/core/annotations/di';
import {List, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
import {DOM} from 'angular2/src/dom/dom_adapter';
import {int, IMPLEMENTS} from 'angular2/src/facade/lang';
import {Injector} from 'angular2/di';
import {View} from 'angular2/src/core/compiler/view';
import {ViewContainer} from 'angular2/src/core/compiler/view_container';
import {VmTurnZone} from 'angular2/src/core/zone/vm_turn_zone';
import {EventManager, DomEventsPlugin} from 'angular2/src/render/dom/events/event_manager';
import {reflector} from 'angular2/src/reflection/reflection';
class DummyDirective extends Directive {
constructor({lifecycle} = {}) { super({lifecycle: lifecycle}); }
}
@proxy
@IMPLEMENTS(ViewContainer)
class FakeViewContainer {
templateElement;
constructor(templateElement) {
this.templateElement = templateElement;
}
noSuchMethod(i) {
super.noSuchMethod(i);
}
}
@proxy
@IMPLEMENTS(View)
class FakeView {
noSuchMethod(i) {
super.noSuchMethod(i);
}
}
export function main() {
describe('view', function() {
var parser, someComponentDirective, someViewportDirective;
function createView(protoView, eventManager: EventManager = null) {
var ctx = new MyEvaluationContext();
var view = protoView.instantiate(null, eventManager);
view.hydrate(null, null, null, ctx, null);
return view;
}
beforeEach(() => {
parser = new Parser(new Lexer());
someComponentDirective = readDirectiveBinding(SomeComponent);
someViewportDirective = readDirectiveBinding(SomeViewport);
});
describe('instantiated from protoView', () => {
var view;
beforeEach(() => {
var pv = new ProtoView(null, el('<div id="1"></div>'), new DynamicProtoChangeDetector(null, null), null);
view = pv.instantiate(null, null);
});
it('should be dehydrated by default', () => {
expect(view.hydrated()).toBe(false);
});
it('should be able to be hydrated and dehydrated', () => {
var ctx = new Object();
view.hydrate(null, null, null, ctx, null);
expect(view.hydrated()).toBe(true);
view.dehydrate();
expect(view.hydrated()).toBe(false);
});
it('should hydrate and dehydrate the change detector', () => {
var ctx = new Object();
view.hydrate(null, null, null, ctx, null);
expect(view.changeDetector.hydrated()).toBe(true);
view.dehydrate();
expect(view.changeDetector.hydrated()).toBe(false);
});
it('should use the view pool to reuse views', () => {
var pv = new ProtoView(null, el('<div id="1"></div>'), new DynamicProtoChangeDetector(null, null), null);
var fakeView = new FakeView();
pv.returnToPool(fakeView);
expect(pv.instantiate(null, null)).toBe(fakeView);
});
});
describe('with locals', function() {
var view;
beforeEach(() => {
var pv = new ProtoView(null, el('<div id="1"></div>'), new DynamicProtoChangeDetector(null, null), null);
pv.bindVariable('context-foo', 'template-foo');
view = createView(pv);
});
it('should support setting of declared locals', () => {
view.setLocal('context-foo', 'bar');
expect(view.locals.get('template-foo')).toBe('bar');
});
it('should not throw on undeclared locals', () => {
expect(() => view.setLocal('setMePlease', 'bar')).not.toThrow();
});
it('when dehydrated should set locals to null', () => {
view.setLocal('context-foo', 'bar');
view.dehydrate();
view.hydrate(null, null, null, new Object(), null);
expect(view.locals.get('template-foo')).toBe(null);
});
it('should throw when trying to set on dehydrated view', () => {
view.dehydrate();
expect(() => view.setLocal('context-foo', 'bar')).toThrowError();
});
});
describe('instantiated and hydrated', function() {
function createCollectDomNodesTestCases(useTemplateElement:boolean) {
function templateAwareCreateElement(html) {
return el(useTemplateElement ? `<template>${html}</template>` : html);
}
it('should collect the root node in the ProtoView element', () => {
var pv = new ProtoView(null, templateAwareCreateElement('<div id="1"></div>'),
new DynamicProtoChangeDetector(null, null), null);
var view = pv.instantiate(null, null);
view.hydrate(null, null, null, null, null);
expect(view.nodes.length).toBe(1);
expect(DOM.getAttribute(view.nodes[0], 'id')).toEqual('1');
});
describe('collect elements with property bindings', () => {
it('should collect property bindings on the root element if it has the ng-binding class', () => {
var pv = new ProtoView(null, templateAwareCreateElement('<div [prop]="a" class="ng-binding"></div>'),
new DynamicProtoChangeDetector(null, null), null);
pv.bindElement(null, 0, null);
pv.bindElementProperty(parser.parseBinding('a', null), 'prop', reflector.setter('prop'));
var view = pv.instantiate(null, null);
view.hydrate(null, null, null, null, null);
expect(view.bindElements.length).toEqual(1);
expect(view.bindElements[0]).toBe(view.nodes[0]);
});
it('should collect property bindings on child elements with ng-binding class', () => {
var pv = new ProtoView(null, templateAwareCreateElement('<div><span></span><span class="ng-binding"></span></div>'),
new DynamicProtoChangeDetector(null, null), null);
pv.bindElement(null, 0, null);
pv.bindElementProperty(parser.parseBinding('b', null), 'a', reflector.setter('a'));
var view = pv.instantiate(null, null);
view.hydrate(null, null, null, null, null);
expect(view.bindElements.length).toEqual(1);
expect(view.bindElements[0]).toBe(view.nodes[0].childNodes[1]);
});
});
describe('collect text nodes with bindings', () => {
it('should collect text nodes under the root element', () => {
var pv = new ProtoView(null, templateAwareCreateElement('<div class="ng-binding">{{}}<span></span>{{}}</div>'),
new DynamicProtoChangeDetector(null, null), null);
pv.bindElement(null, 0, null);
pv.bindTextNode(0, parser.parseBinding('a', null));
pv.bindTextNode(2, parser.parseBinding('b', null));
var view = pv.instantiate(null, null);
view.hydrate(null, null, null, null, null);
expect(view.textNodes.length).toEqual(2);
expect(view.textNodes[0]).toBe(view.nodes[0].childNodes[0]);
expect(view.textNodes[1]).toBe(view.nodes[0].childNodes[2]);
});
it('should collect text nodes with bindings on child elements with ng-binding class', () => {
var pv = new ProtoView(null, templateAwareCreateElement('<div><span> </span><span class="ng-binding">{{}}</span></div>'),
new DynamicProtoChangeDetector(null, null), null);
pv.bindElement(null, 0, null);
pv.bindTextNode(0, parser.parseBinding('b', null));
var view = pv.instantiate(null, null);
view.hydrate(null, null, null, null, null);
expect(view.textNodes.length).toEqual(1);
expect(view.textNodes[0]).toBe(view.nodes[0].childNodes[1].childNodes[0]);
});
});
}
describe('inplace instantiation', () => {
it('should be supported.', () => {
var template = el('<div></div>');
var pv = new ProtoView(null, template, new DynamicProtoChangeDetector(null, null),
new NativeShadowDomStrategy(null));
pv.instantiateInPlace = true;
var view = pv.instantiate(null, null);
view.hydrate(null, null, null, null, null);
expect(view.nodes[0]).toBe(template);
});
it('should be off by default.', () => {
var template = el('<div></div>')
var pv = new ProtoView(null, template, new DynamicProtoChangeDetector(null, null),
new NativeShadowDomStrategy(null))
var view = pv.instantiate(null, null);
view.hydrate(null, null, null, null, null);
expect(view.nodes[0]).not.toBe(template);
});
});
describe('collect dom nodes with a regular element as root', () => {
createCollectDomNodesTestCases(false);
});
describe('collect dom nodes with a template element as root', () => {
createCollectDomNodesTestCases(true);
});
describe('create ElementInjectors', () => {
it('should use the directives of the ProtoElementInjector', () => {
var pv = new ProtoView(null, el('<div class="ng-binding"></div>'),
new DynamicProtoChangeDetector(null, null), null);
pv.bindElement(null, 0, new ProtoElementInjector(null, 1, [SomeDirective]));
var view = pv.instantiate(null, null);
view.hydrate(null, null, null, null, null);
expect(view.elementInjectors.length).toBe(1);
expect(view.elementInjectors[0].get(SomeDirective) instanceof SomeDirective).toBe(true);
});
it('should use the correct parent', () => {
var pv = new ProtoView(null, el('<div class="ng-binding"><span class="ng-binding"></span></div>'),
new DynamicProtoChangeDetector(null, null), null);
var protoParent = new ProtoElementInjector(null, 0, [SomeDirective]);
pv.bindElement(null, 0, protoParent);
pv.bindElement(null, 0, new ProtoElementInjector(protoParent, 1, [AnotherDirective]));
var view = pv.instantiate(null, null);
view.hydrate(null, null, null, null, null);
expect(view.elementInjectors.length).toBe(2);
expect(view.elementInjectors[0].get(SomeDirective) instanceof SomeDirective).toBe(true);
expect(view.elementInjectors[1].parent).toBe(view.elementInjectors[0]);
});
it('should not pass the host injector when a parent injector exists', () => {
var pv = new ProtoView(null, el('<div class="ng-binding"><span class="ng-binding"></span></div>'),
new DynamicProtoChangeDetector(null, null), null);
var protoParent = new ProtoElementInjector(null, 0, [SomeDirective]);
pv.bindElement(null, 0, protoParent);
var testProtoElementInjector = new TestProtoElementInjector(protoParent, 1, [AnotherDirective]);
pv.bindElement(null, 0, testProtoElementInjector);
var hostProtoInjector = new ProtoElementInjector(null, 0, []);
var hostInjector = hostProtoInjector.instantiate(null, null);
var view;
expect(() => view = pv.instantiate(hostInjector, null)).not.toThrow();
expect(testProtoElementInjector.parentElementInjector).toBe(view.elementInjectors[0]);
expect(testProtoElementInjector.hostElementInjector).toBeNull();
});
it('should pass the host injector when there is no parent injector', () => {
var pv = new ProtoView(null, el('<div class="ng-binding"><span class="ng-binding"></span></div>'),
new DynamicProtoChangeDetector(null, null), null);
pv.bindElement(null, 0, new ProtoElementInjector(null, 0, [SomeDirective]));
var testProtoElementInjector = new TestProtoElementInjector(null, 1, [AnotherDirective]);
pv.bindElement(null, 0, testProtoElementInjector);
var hostProtoInjector = new ProtoElementInjector(null, 0, []);
var hostInjector = hostProtoInjector.instantiate(null, null);
expect(() => pv.instantiate(hostInjector, null)).not.toThrow();
expect(testProtoElementInjector.parentElementInjector).toBeNull();
expect(testProtoElementInjector.hostElementInjector).toBe(hostInjector);
});
});
describe('collect root element injectors', () => {
it('should collect a single root element injector', () => {
var pv = new ProtoView(null, el('<div class="ng-binding"><span class="ng-binding"></span></div>'),
new DynamicProtoChangeDetector(null, null), null);
var protoParent = new ProtoElementInjector(null, 0, [SomeDirective]);
pv.bindElement(null, 0, protoParent);
pv.bindElement(null, 0, new ProtoElementInjector(protoParent, 1, [AnotherDirective]));
var view = pv.instantiate(null, null);
view.hydrate(null, null, null, null, null);
expect(view.rootElementInjectors.length).toBe(1);
expect(view.rootElementInjectors[0].get(SomeDirective) instanceof SomeDirective).toBe(true);
});
it('should collect multiple root element injectors', () => {
var pv = new ProtoView(null, el('<div><span class="ng-binding"></span><span class="ng-binding"></span></div>'),
new DynamicProtoChangeDetector(null, null), null);
pv.bindElement(null, 0, new ProtoElementInjector(null, 1, [SomeDirective]));
pv.bindElement(null, 0, new ProtoElementInjector(null, 2, [AnotherDirective]));
var view = pv.instantiate(null, null);
view.hydrate(null, null, null, null, null);
expect(view.rootElementInjectors.length).toBe(2)
expect(view.rootElementInjectors[0].get(SomeDirective) instanceof SomeDirective).toBe(true);
expect(view.rootElementInjectors[1].get(AnotherDirective) instanceof AnotherDirective).toBe(true);
});
});
describe('with component views', () => {
var ctx;
function createComponentWithSubPV(subProtoView) {
var pv = new ProtoView(null, el('<cmp class="ng-binding"></cmp>'),
new DynamicProtoChangeDetector(null, null), new NativeShadowDomStrategy(null));
var binder = pv.bindElement(null, 0, new ProtoElementInjector(null, 0, [SomeComponent], true));
binder.componentDirective = someComponentDirective;
binder.nestedProtoView = subProtoView;
return pv;
}
function createNestedView(protoView) {
ctx = new MyEvaluationContext();
var view = protoView.instantiate(null, null);
view.hydrate(new Injector([]), null, null, ctx, null);
return view;
}
it('should expose component services to the component', () => {
var subpv = new ProtoView(null, el('<span></span>'), new DynamicProtoChangeDetector(null, null), null);
var pv = createComponentWithSubPV(subpv);
var view = createNestedView(pv);
var comp = view.rootElementInjectors[0].get(SomeComponent);
expect(comp.service).toBeAnInstanceOf(SomeService);
});
it('should expose component services and component instance to directives in the shadow Dom',
() => {
var subpv = new ProtoView(null,
el('<div dec class="ng-binding">hello shadow dom</div>'),
new DynamicProtoChangeDetector(null, null),
null);
subpv.bindElement(null, 0,
new ProtoElementInjector(null, 0, [ServiceDependentDecorator]));
var pv = createComponentWithSubPV(subpv);
var view = createNestedView(pv);
var subView = view.componentChildViews[0];
var subInj = subView.rootElementInjectors[0];
var subDecorator = subInj.get(ServiceDependentDecorator);
var comp = view.rootElementInjectors[0].get(SomeComponent);
expect(subDecorator).toBeAnInstanceOf(ServiceDependentDecorator);
expect(subDecorator.service).toBe(comp.service);
expect(subDecorator.component).toBe(comp);
});
function expectViewHasNoDirectiveInstances(view) {
view.elementInjectors.forEach((inj) => expect(inj.hasInstances()).toBe(false));
}
it('dehydration should dehydrate child component views too', () => {
var subpv = new ProtoView(null,
el('<div dec class="ng-binding">hello shadow dom</div>'),
new DynamicProtoChangeDetector(null, null),
null);
subpv.bindElement(null, 0,
new ProtoElementInjector(null, 0, [ServiceDependentDecorator]));
var pv = createComponentWithSubPV(subpv);
var view = createNestedView(pv);
view.dehydrate();
expect(view.hydrated()).toBe(false);
expectViewHasNoDirectiveInstances(view);
view.componentChildViews.forEach(
(view) => expectViewHasNoDirectiveInstances(view));
});
it('should create shadow dom (Native Strategy)', () => {
var subpv = new ProtoView(null, el('<span>hello shadow dom</span>'),
new DynamicProtoChangeDetector(null, null),
null);
var pv = createComponentWithSubPV(subpv);
var view = createNestedView(pv);
expect(view.nodes[0].shadowRoot.childNodes[0].childNodes[0].nodeValue).toEqual('hello shadow dom');
});
it('should emulate shadow dom (Emulated Strategy)', () => {
var subpv = new ProtoView(null, el('<span>hello shadow dom</span>'),
new DynamicProtoChangeDetector(null, null), null);
var pv = new ProtoView(null, el('<cmp class="ng-binding"></cmp>'),
new DynamicProtoChangeDetector(null, null), new EmulatedScopedShadowDomStrategy(null, null, null));
var binder = pv.bindElement(null, 0, new ProtoElementInjector(null, 0, [SomeComponent], true));
binder.componentDirective = readDirectiveBinding(SomeComponent);
binder.nestedProtoView = subpv;
var view = createNestedView(pv);
expect(view.nodes[0].childNodes[0].childNodes[0].nodeValue).toEqual('hello shadow dom');
});
});
describe('with template views', () => {
function createViewWithViewport() {
var templateProtoView = new ProtoView(null,
el('<div id="1"></div>'), new DynamicProtoChangeDetector(null, null), null);
var pv = new ProtoView(null, el('<someTmpl class="ng-binding"></someTmpl>'),
new DynamicProtoChangeDetector(null, null), new NativeShadowDomStrategy(null));
var binder = pv.bindElement(null, 0, new ProtoElementInjector(null, 0, [SomeViewport]));
binder.viewportDirective = someViewportDirective;
binder.nestedProtoView = templateProtoView;
return createView(pv);
}
it('should create a ViewContainer for the Viewport directive', () => {
var view = createViewWithViewport();
var tmplComp = view.rootElementInjectors[0].get(SomeViewport);
expect(tmplComp.viewContainer).not.toBe(null);
});
it('dehydration should dehydrate viewcontainers', () => {
var view = createViewWithViewport();
var tmplComp = view.rootElementInjectors[0].get(SomeViewport);
expect(tmplComp.viewContainer.hydrated()).toBe(false);
});
});
if (DOM.supportsDOMEvents()) {
describe('event handlers', () => {
var view, ctx, called, receivedEvent, dispatchedEvent;
function createViewAndContext(protoView) {
view = createView(protoView,
new EventManager([new DomEventsPlugin()], new FakeVmTurnZone()));
ctx = view.context;
called = 0;
receivedEvent = null;
ctx.callMe = (event) => {
called += 1;
receivedEvent = event;
}
}
function dispatchClick(el) {
dispatchedEvent = DOM.createMouseEvent('click');
DOM.dispatchEvent(el, dispatchedEvent);
}
function createProtoView() {
var pv = new ProtoView(null, el('<div class="ng-binding"><div></div></div>'),
new DynamicProtoChangeDetector(null, null), null);
pv.bindElement(null, 0, new TestProtoElementInjector(null, 0, []));
pv.bindEvent('click', parser.parseBinding('callMe($event)', null));
return pv;
}
it('should fire on non-bubbling native events', () => {
createViewAndContext(createProtoView());
dispatchClick(view.nodes[0]);
expect(called).toEqual(1);
expect(receivedEvent).toBe(dispatchedEvent);
});
it('should not fire on a bubbled native events', () => {
createViewAndContext(createProtoView());
dispatchClick(view.nodes[0].firstChild);
// This test passes trivially on webkit browsers due to
// https://bugs.webkit.org/show_bug.cgi?id=122755
expect(called).toEqual(0);
});
it('should not throw if the view is dehydrated', () => {
createViewAndContext(createProtoView());
view.dehydrate();
expect(() => dispatchClick(view.nodes[0])).not.toThrow();
expect(called).toEqual(0);
});
it('should support custom event emitters', () => {
var pv = new ProtoView(null, el('<div class="ng-binding"><div></div></div>'),
new DynamicProtoChangeDetector(null, null), null);
pv.bindElement(null, 0, new TestProtoElementInjector(null, 0, [EventEmitterDirective]));
pv.bindEvent('click', parser.parseBinding('callMe($event)', null));
createViewAndContext(pv);
var dir = view.elementInjectors[0].get(EventEmitterDirective);
var dispatchedEvent = new Object();
dir.click(dispatchedEvent);
expect(receivedEvent).toBe(dispatchedEvent);
expect(called).toEqual(1);
// Should not eval the binding, because custom emitter takes over.
dispatchClick(view.nodes[0]);
expect(called).toEqual(1);
});
it('should bind to directive events', () => {
var pv = new ProtoView(null, el('<div class="ng-binding"></div>'),
new DynamicProtoChangeDetector(null, null), null);
pv.bindElement(null, 0, new ProtoElementInjector(null, 0, [SomeDirectiveWithEventHandler]));
pv.bindEvent('click', parser.parseAction('onEvent($event)', null), 0);
view = createView(pv, new EventManager([new DomEventsPlugin()], new FakeVmTurnZone()));
var directive = view.elementInjectors[0].get(SomeDirectiveWithEventHandler);
expect(directive.event).toEqual(null);
dispatchClick(view.nodes[0]);
expect(directive.event).toBe(dispatchedEvent);
});
});
}
describe('react to record changes', () => {
var view, cd, ctx;
function createViewAndChangeDetector(protoView) {
view = createView(protoView);
ctx = view.context;
cd = view.changeDetector;
}
it('should consume text node changes', () => {
var pv = new ProtoView(null, el('<div class="ng-binding">{{}}</div>'),
new DynamicProtoChangeDetector(null, null), null);
pv.bindElement(null, 0, null);
pv.bindTextNode(0, parser.parseBinding('foo', null));
createViewAndChangeDetector(pv);
ctx.foo = 'buz';
cd.detectChanges();
expect(view.textNodes[0].nodeValue).toEqual('buz');
});
it('should consume element binding changes', () => {
var pv = new ProtoView(null, el('<div class="ng-binding"></div>'),
new DynamicProtoChangeDetector(null, null), null);
pv.bindElement(null, 0, null);
pv.bindElementProperty(parser.parseBinding('foo', null), 'id', reflector.setter('id'));
createViewAndChangeDetector(pv);
ctx.foo = 'buz';
cd.detectChanges();
expect(view.bindElements[0].id).toEqual('buz');
});
it('should consume directive watch expression change', () => {
var pv = new ProtoView(null, el('<div class="ng-binding"></div>'),
new DynamicProtoChangeDetector(null, null), null);
pv.bindElement(null, 0, new ProtoElementInjector(null, 0, [SomeDirective]));
pv.bindDirectiveProperty(0, parser.parseBinding('foo', null), 'prop', reflector.setter('prop'));
createViewAndChangeDetector(pv);
ctx.foo = 'buz';
cd.detectChanges();
expect(view.elementInjectors[0].get(SomeDirective).prop).toEqual('buz');
});
it('should notify a directive about changes after all its properties have been set', () => {
var pv = new ProtoView(null, el('<div class="ng-binding"></div>'),
new DynamicProtoChangeDetector(null, null), null);
pv.bindElement(null, 0, new ProtoElementInjector(null, 0, [
DirectiveBinding.createFromType(DirectiveImplementingOnChange,
new DummyDirective({lifecycle: [onChange]}))
]));
pv.bindDirectiveProperty( 0, parser.parseBinding('a', null), 'a', reflector.setter('a'));
pv.bindDirectiveProperty( 0, parser.parseBinding('b', null), 'b', reflector.setter('b'));
createViewAndChangeDetector(pv);
ctx.a = 100;
ctx.b = 200;
cd.detectChanges();
var directive = view.elementInjectors[0].get(DirectiveImplementingOnChange);
expect(directive.c).toEqual(300);
});
it('should provide a map of updated properties using onChange callback', () => {
var pv = new ProtoView(null, el('<div class="ng-binding"></div>'),
new DynamicProtoChangeDetector(null, null), null);
pv.bindElement(null, 0, new ProtoElementInjector(null, 0, [
DirectiveBinding.createFromType(DirectiveImplementingOnChange,
new DummyDirective({lifecycle: [onChange]}))
]));
pv.bindDirectiveProperty( 0, parser.parseBinding('a', null), 'a', reflector.setter('a'));
pv.bindDirectiveProperty( 0, parser.parseBinding('b', null), 'b', reflector.setter('b'));
createViewAndChangeDetector(pv);
var directive = view.elementInjectors[0].get(DirectiveImplementingOnChange);
ctx.a = 0;
ctx.b = 0;
cd.detectChanges();
expect(directive.changes["a"].currentValue).toEqual(0);
expect(directive.changes["b"].currentValue).toEqual(0);
ctx.a = 100;
cd.detectChanges();
expect(directive.changes["a"].currentValue).toEqual(100);
expect(StringMapWrapper.contains(directive.changes, "b")).toBe(false);
});
it('should invoke the onAllChangesDone callback', () => {
var pv = new ProtoView(null, el('<div class="ng-binding"></div>'),
new DynamicProtoChangeDetector(null, null), null);
pv.bindElement(null, 0, new ProtoElementInjector(null, 0, [
DirectiveBinding.createFromType(DirectiveImplementingOnAllChangesDone,
new DummyDirective({lifecycle: [onAllChangesDone]}))
]));
createViewAndChangeDetector(pv);
cd.detectChanges();
var directive = view.elementInjectors[0].get(DirectiveImplementingOnAllChangesDone);
expect(directive.onAllChangesDoneCalled).toBe(true);
});
});
});
});
}
function readDirectiveBinding(type) {
var meta = new DirectiveMetadataReader().read(type);
return DirectiveBinding.createFromType(type, meta.annotation);
}
class SomeDirective {
prop;
constructor() {
this.prop = 'foo';
}
}
class DirectiveImplementingOnChange {
a;
b;
c;
changes;
onChange(changes) {
this.c = this.a + this.b;
this.changes = changes;
}
}
class DirectiveImplementingOnAllChangesDone {
onAllChangesDoneCalled;
onAllChangesDone() {
this.onAllChangesDoneCalled = true;
}
}
class SomeService {}
@Component({services: [SomeService]})
class SomeComponent {
service: SomeService;
constructor(service: SomeService) {
this.service = service;
}
}
@Decorator({
selector: '[dec]'
})
class ServiceDependentDecorator {
component: SomeComponent;
service: SomeService;
constructor(component: SomeComponent, service: SomeService) {
this.component = component;
this.service = service;
}
}
@Viewport({
selector: 'someTmpl'
})
class SomeViewport {
viewContainer: ViewContainer;
constructor(viewContainer: ViewContainer) {
this.viewContainer = viewContainer;
}
}
class AnotherDirective {
prop:string;
constructor() {
this.prop = 'anotherFoo';
}
}
class EventEmitterDirective {
_clicker:Function;
constructor(@EventEmitter('click') clicker:Function) {
this._clicker = clicker;
}
click(eventData) {
this._clicker(eventData);
}
}
class SomeDirectiveWithEventHandler {
event;
constructor() {
this.event = null;
}
onEvent(event) {
this.event = event;
}
}
class MyEvaluationContext {
foo:string;
a;
b;
callMe;
constructor() {
this.foo = 'bar';
};
}
class TestProtoElementInjector extends ProtoElementInjector {
parentElementInjector: ElementInjector;
hostElementInjector: ElementInjector;
constructor(parent:ProtoElementInjector, index:int, bindings:List, firstBindingIsComponent:boolean = false) {
super(parent, index, bindings, firstBindingIsComponent);
}
instantiate(parent:ElementInjector, host:ElementInjector):ElementInjector {
this.parentElementInjector = parent;
this.hostElementInjector = host;
return super.instantiate(parent, host);
}
}
class FakeVmTurnZone extends VmTurnZone {
constructor() {
super({enableLongStackTrace: false});
}
run(fn) {
fn();
}
runOutsideAngular(fn) {
fn();
}
}

View File

@ -31,7 +31,7 @@ export function main() {
tb.createView(TestComponent, {html: template}).then((view) => {
view.detectChanges();
expect(view.nodes[0].className).toEqual('ng-binding foo');
expect(view.rootNodes[0].className).toEqual('ng-binding foo');
async.done();
});
@ -43,11 +43,11 @@ export function main() {
tb.createView(TestComponent, {html: template}).then((view) => {
view.detectChanges();
expect(view.nodes[0].className).toEqual('ng-binding foo');
expect(view.rootNodes[0].className).toEqual('ng-binding foo');
view.context.condition = false;
view.detectChanges();
expect(view.nodes[0].className).toEqual('ng-binding bar');
expect(view.rootNodes[0].className).toEqual('ng-binding bar');
async.done();
});
@ -59,19 +59,19 @@ export function main() {
tb.createView(TestComponent, {html: template}).then((view) => {
view.detectChanges();
expect(view.nodes[0].className).toEqual('ng-binding foo');
expect(view.rootNodes[0].className).toEqual('ng-binding foo');
StringMapWrapper.set(view.context.expr, 'bar', true);
view.detectChanges();
expect(view.nodes[0].className).toEqual('ng-binding foo bar');
expect(view.rootNodes[0].className).toEqual('ng-binding foo bar');
StringMapWrapper.set(view.context.expr, 'baz', true);
view.detectChanges();
expect(view.nodes[0].className).toEqual('ng-binding foo bar baz');
expect(view.rootNodes[0].className).toEqual('ng-binding foo bar baz');
StringMapWrapper.delete(view.context.expr, 'bar');
view.detectChanges();
expect(view.nodes[0].className).toEqual('ng-binding foo baz');
expect(view.rootNodes[0].className).toEqual('ng-binding foo baz');
async.done();
});
@ -83,15 +83,15 @@ export function main() {
tb.createView(TestComponent, {html: template}).then((view) => {
view.detectChanges();
expect(view.nodes[0].className).toEqual('ng-binding foo');
expect(view.rootNodes[0].className).toEqual('ng-binding foo');
view.context.expr = null;
view.detectChanges();
expect(view.nodes[0].className).toEqual('ng-binding foo');
expect(view.rootNodes[0].className).toEqual('ng-binding foo');
view.context.expr = {'foo': false, 'bar': true};
view.detectChanges();
expect(view.nodes[0].className).toEqual('ng-binding bar');
expect(view.rootNodes[0].className).toEqual('ng-binding bar');
async.done();
});
@ -104,11 +104,11 @@ export function main() {
tb.createView(TestComponent, {html: template}).then((view) => {
StringMapWrapper.set(view.context.expr, 'bar', true);
view.detectChanges();
expect(view.nodes[0].className).toEqual('init foo ng-binding bar');
expect(view.rootNodes[0].className).toEqual('init foo ng-binding bar');
StringMapWrapper.set(view.context.expr, 'foo', false);
view.detectChanges();
expect(view.nodes[0].className).toEqual('init ng-binding bar');
expect(view.rootNodes[0].className).toEqual('init ng-binding bar');
async.done();
});
@ -120,19 +120,19 @@ export function main() {
tb.createView(TestComponent, {html: template}).then((view) => {
view.detectChanges();
expect(view.nodes[0].className).toEqual('init foo ng-binding baz');
expect(view.rootNodes[0].className).toEqual('init foo ng-binding baz');
StringMapWrapper.set(view.context.expr, 'bar', true);
view.detectChanges();
expect(view.nodes[0].className).toEqual('init foo ng-binding baz bar');
expect(view.rootNodes[0].className).toEqual('init foo ng-binding baz bar');
StringMapWrapper.set(view.context.expr, 'foo', false);
view.detectChanges();
expect(view.nodes[0].className).toEqual('init ng-binding baz bar');
expect(view.rootNodes[0].className).toEqual('init ng-binding baz bar');
view.context.condition = false;
view.detectChanges();
expect(view.nodes[0].className).toEqual('init ng-binding bar');
expect(view.rootNodes[0].className).toEqual('init ng-binding bar');
async.done();
});

View File

@ -30,7 +30,7 @@ export function main() {
it('should reflect initial elements', inject([TestBed, AsyncTestCompleter], (tb, async) => {
tb.createView(TestComponent, {html: TEMPLATE}).then((view) => {
view.detectChanges();
expect(DOM.getText(view.nodes[0])).toEqual('1;2;');
expect(DOM.getText(view.rootNodes[0])).toEqual('1;2;');
async.done();
});
}));
@ -42,7 +42,7 @@ export function main() {
ListWrapper.push(view.context.items, 3);
view.detectChanges();
expect(DOM.getText(view.nodes[0])).toEqual('1;2;3;');
expect(DOM.getText(view.rootNodes[0])).toEqual('1;2;3;');
async.done();
});
}));
@ -54,7 +54,7 @@ export function main() {
ListWrapper.removeAt(view.context.items, 1);
view.detectChanges();
expect(DOM.getText(view.nodes[0])).toEqual('1;');
expect(DOM.getText(view.rootNodes[0])).toEqual('1;');
async.done();
});
}));
@ -67,7 +67,7 @@ export function main() {
ListWrapper.push(view.context.items, 1);
view.detectChanges();
expect(DOM.getText(view.nodes[0])).toEqual('2;1;');
expect(DOM.getText(view.rootNodes[0])).toEqual('2;1;');
async.done();
});
}));
@ -81,7 +81,7 @@ export function main() {
view.context.items = [6, 2, 7, 0, 4, 8];
view.detectChanges();
expect(DOM.getText(view.nodes[0])).toEqual('6;2;7;0;4;8;');
expect(DOM.getText(view.rootNodes[0])).toEqual('6;2;7;0;4;8;');
async.done();
});
}));
@ -94,20 +94,20 @@ export function main() {
// INIT
view.context.items = [{'name': 'misko'}, {'name':'shyam'}];
view.detectChanges();
expect(DOM.getText(view.nodes[0])).toEqual('misko;shyam;');
expect(DOM.getText(view.rootNodes[0])).toEqual('misko;shyam;');
// GROW
ListWrapper.push(view.context.items, {'name': 'adam'});
view.detectChanges();
expect(DOM.getText(view.nodes[0])).toEqual('misko;shyam;adam;');
expect(DOM.getText(view.rootNodes[0])).toEqual('misko;shyam;adam;');
// SHRINK
ListWrapper.removeAt(view.context.items, 2);
ListWrapper.removeAt(view.context.items, 0);
view.detectChanges();
expect(DOM.getText(view.nodes[0])).toEqual('shyam;');
expect(DOM.getText(view.rootNodes[0])).toEqual('shyam;');
async.done();
});
}));
@ -116,7 +116,7 @@ export function main() {
var template = '<ul><li template="for #item of null">{{item}};</li></ul>';
tb.createView(TestComponent, {html: template}).then((view) => {
view.detectChanges();
expect(DOM.getText(view.nodes[0])).toEqual('');
expect(DOM.getText(view.rootNodes[0])).toEqual('');
async.done();
});
}));
@ -125,15 +125,15 @@ export function main() {
inject([TestBed, AsyncTestCompleter], (tb, async) => {
tb.createView(TestComponent, {html: TEMPLATE}).then((view) => {
view.detectChanges();
expect(DOM.getText(view.nodes[0])).toEqual('1;2;');
expect(DOM.getText(view.rootNodes[0])).toEqual('1;2;');
view.context.items = null;
view.detectChanges();
expect(DOM.getText(view.nodes[0])).toEqual('');
expect(DOM.getText(view.rootNodes[0])).toEqual('');
view.context.items = [1, 2, 3];
view.detectChanges();
expect(DOM.getText(view.nodes[0])).toEqual('1;2;3;');
expect(DOM.getText(view.rootNodes[0])).toEqual('1;2;3;');
async.done();
});
}));
@ -141,7 +141,7 @@ export function main() {
it('should throw on ref changing to string', inject([TestBed, AsyncTestCompleter], (tb, async) => {
tb.createView(TestComponent, {html: TEMPLATE}).then((view) => {
view.detectChanges();
expect(DOM.getText(view.nodes[0])).toEqual('1;2;');
expect(DOM.getText(view.rootNodes[0])).toEqual('1;2;');
view.context.items = 'whaaa';
expect(() => view.detectChanges()).toThrowError();
@ -154,7 +154,7 @@ export function main() {
var a = new Foo();
view.context.items = [a, a];
view.detectChanges();
expect(DOM.getText(view.nodes[0])).toEqual('foo;foo;');
expect(DOM.getText(view.rootNodes[0])).toEqual('foo;foo;');
async.done();
});
}));
@ -174,11 +174,11 @@ export function main() {
view.detectChanges();
view.detectChanges();
view.detectChanges();
expect(DOM.getText(view.nodes[0])).toEqual('a-2;b-2;|c-1;|');
expect(DOM.getText(view.rootNodes[0])).toEqual('a-2;b-2;|c-1;|');
view.context.items = [['e'], ['f', 'g']];
view.detectChanges();
expect(DOM.getText(view.nodes[0])).toEqual('e-1;|f-2;g-2;|');
expect(DOM.getText(view.rootNodes[0])).toEqual('e-1;|f-2;g-2;|');
async.done();
});
@ -195,11 +195,11 @@ export function main() {
tb.createView(TestComponent, {html: template}).then((view) => {
view.context.items = [['a', 'b'], ['c']];
view.detectChanges();
expect(DOM.getText(view.nodes[0])).toEqual('a-2;b-2;c-1;');
expect(DOM.getText(view.rootNodes[0])).toEqual('a-2;b-2;c-1;');
view.context.items = [['e'], ['f', 'g']];
view.detectChanges();
expect(DOM.getText(view.nodes[0])).toEqual('e-1;f-2;g-2;');
expect(DOM.getText(view.rootNodes[0])).toEqual('e-1;f-2;g-2;');
async.done();
});
}));
@ -212,11 +212,11 @@ export function main() {
tb.createView(TestComponent, {html: template}).then((view) => {
view.context.items = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
view.detectChanges();
expect(DOM.getText(view.nodes[0])).toEqual('0123456789');
expect(DOM.getText(view.rootNodes[0])).toEqual('0123456789');
view.context.items = [1, 2, 6, 7, 4, 3, 5, 8, 9, 0];
view.detectChanges();
expect(DOM.getText(view.nodes[0])).toEqual('0123456789');
expect(DOM.getText(view.rootNodes[0])).toEqual('0123456789');
async.done();
});
}));

View File

@ -28,8 +28,8 @@ export function main() {
tb.createView(TestComponent, {html: html}).then((view) => {
view.detectChanges();
expect(DOM.querySelectorAll(view.nodes[0], 'copy-me').length).toEqual(1);
expect(DOM.getText(view.nodes[0])).toEqual('hello');
expect(DOM.querySelectorAll(view.rootNodes[0], 'copy-me').length).toEqual(1);
expect(DOM.getText(view.rootNodes[0])).toEqual('hello');
async.done();
});
}));
@ -39,8 +39,8 @@ export function main() {
tb.createView(TestComponent, {html: html}).then((view) => {
view.detectChanges();
expect(DOM.querySelectorAll(view.nodes[0], 'copy-me').length).toEqual(1);
expect(DOM.getText(view.nodes[0])).toEqual('hello2');
expect(DOM.querySelectorAll(view.rootNodes[0], 'copy-me').length).toEqual(1);
expect(DOM.getText(view.rootNodes[0])).toEqual('hello2');
async.done();
});
}));
@ -51,18 +51,18 @@ export function main() {
tb.createView(TestComponent, {html: html}).then((view) => {
view.context.booleanCondition = false;
view.detectChanges();
expect(DOM.querySelectorAll(view.nodes[0], 'copy-me').length).toEqual(0);
expect(DOM.getText(view.nodes[0])).toEqual('');
expect(DOM.querySelectorAll(view.rootNodes[0], 'copy-me').length).toEqual(0);
expect(DOM.getText(view.rootNodes[0])).toEqual('');
view.context.booleanCondition = true;
view.detectChanges();
expect(DOM.querySelectorAll(view.nodes[0], 'copy-me').length).toEqual(1);
expect(DOM.getText(view.nodes[0])).toEqual('hello');
expect(DOM.querySelectorAll(view.rootNodes[0], 'copy-me').length).toEqual(1);
expect(DOM.getText(view.rootNodes[0])).toEqual('hello');
view.context.booleanCondition = false;
view.detectChanges();
expect(DOM.querySelectorAll(view.nodes[0], 'copy-me').length).toEqual(0);
expect(DOM.getText(view.nodes[0])).toEqual('');
expect(DOM.querySelectorAll(view.rootNodes[0], 'copy-me').length).toEqual(0);
expect(DOM.getText(view.rootNodes[0])).toEqual('');
async.done();
});
@ -74,28 +74,28 @@ export function main() {
tb.createView(TestComponent, {html: html}).then((view) => {
view.context.booleanCondition = false;
view.detectChanges();
expect(DOM.querySelectorAll(view.nodes[0], 'copy-me').length).toEqual(0);
expect(DOM.getText(view.nodes[0])).toEqual('');
expect(DOM.querySelectorAll(view.rootNodes[0], 'copy-me').length).toEqual(0);
expect(DOM.getText(view.rootNodes[0])).toEqual('');
view.context.booleanCondition = true;
view.detectChanges();
expect(DOM.querySelectorAll(view.nodes[0], 'copy-me').length).toEqual(1);
expect(DOM.getText(view.nodes[0])).toEqual('hello');
expect(DOM.querySelectorAll(view.rootNodes[0], 'copy-me').length).toEqual(1);
expect(DOM.getText(view.rootNodes[0])).toEqual('hello');
view.context.nestedBooleanCondition = false;
view.detectChanges();
expect(DOM.querySelectorAll(view.nodes[0], 'copy-me').length).toEqual(0);
expect(DOM.getText(view.nodes[0])).toEqual('');
expect(DOM.querySelectorAll(view.rootNodes[0], 'copy-me').length).toEqual(0);
expect(DOM.getText(view.rootNodes[0])).toEqual('');
view.context.nestedBooleanCondition = true;
view.detectChanges();
expect(DOM.querySelectorAll(view.nodes[0], 'copy-me').length).toEqual(1);
expect(DOM.getText(view.nodes[0])).toEqual('hello');
expect(DOM.querySelectorAll(view.rootNodes[0], 'copy-me').length).toEqual(1);
expect(DOM.getText(view.rootNodes[0])).toEqual('hello');
view.context.booleanCondition = false;
view.detectChanges();
expect(DOM.querySelectorAll(view.nodes[0], 'copy-me').length).toEqual(0);
expect(DOM.getText(view.nodes[0])).toEqual('');
expect(DOM.querySelectorAll(view.rootNodes[0], 'copy-me').length).toEqual(0);
expect(DOM.getText(view.rootNodes[0])).toEqual('');
async.done();
});
@ -111,19 +111,19 @@ export function main() {
tb.createView(TestComponent, {html: html}).then((view) => {
view.detectChanges();
expect(DOM.querySelectorAll(view.nodes[0], 'copy-me').length).toEqual(3);
expect(DOM.getText(view.nodes[0])).toEqual('helloNumberhelloStringhelloFunction');
expect(DOM.querySelectorAll(view.rootNodes[0], 'copy-me').length).toEqual(3);
expect(DOM.getText(view.rootNodes[0])).toEqual('helloNumberhelloStringhelloFunction');
view.context.numberCondition = 0;
view.detectChanges();
expect(DOM.querySelectorAll(view.nodes[0], 'copy-me').length).toEqual(1);
expect(DOM.getText(view.nodes[0])).toEqual('helloString');
expect(DOM.querySelectorAll(view.rootNodes[0], 'copy-me').length).toEqual(1);
expect(DOM.getText(view.rootNodes[0])).toEqual('helloString');
view.context.numberCondition = 1;
view.context.stringCondition = "bar";
view.detectChanges();
expect(DOM.querySelectorAll(view.nodes[0], 'copy-me').length).toEqual(1);
expect(DOM.getText(view.nodes[0])).toEqual('helloNumber');
expect(DOM.querySelectorAll(view.rootNodes[0], 'copy-me').length).toEqual(1);
expect(DOM.getText(view.rootNodes[0])).toEqual('helloNumber');
async.done();
});
}));
@ -136,13 +136,13 @@ export function main() {
tb.createView(TestComponent, {html: html}).then((view) => {
view.detectChanges();
expect(DOM.querySelectorAll(view.nodes[0], 'copy-me').length).toEqual(1);
expect(DOM.getText(view.nodes[0])).toEqual('hello');
expect(DOM.querySelectorAll(view.rootNodes[0], 'copy-me').length).toEqual(1);
expect(DOM.getText(view.rootNodes[0])).toEqual('hello');
view.context.numberCondition = 2;
view.detectChanges();
expect(DOM.querySelectorAll(view.nodes[0], 'copy-me').length).toEqual(1);
expect(DOM.getText(view.nodes[0])).toEqual('hello');
expect(DOM.querySelectorAll(view.rootNodes[0], 'copy-me').length).toEqual(1);
expect(DOM.getText(view.rootNodes[0])).toEqual('hello');
async.done();
});
@ -154,11 +154,11 @@ export function main() {
tb.createView(TestComponent, {html: html}).then((view) => {
view.detectChanges();
DOM.addClass(view.nodes[0].childNodes[1], "foo");
DOM.addClass(view.rootNodes[0].childNodes[1], "foo");
view.context.numberCondition = 2;
view.detectChanges();
expect(DOM.hasClass(view.nodes[0].childNodes[1], "foo")).toBe(true);
expect(DOM.hasClass(view.rootNodes[0].childNodes[1], "foo")).toBe(true);
async.done();
});
@ -172,8 +172,8 @@ export function main() {
tb.createView(TestComponent, {html: html}).then((view) => {
expect(() => view.detectChanges()).toThrowError();
expect(DOM.querySelectorAll(view.nodes[0], 'copy-me').length).toEqual(0);
expect(DOM.getText(view.nodes[0])).toEqual('');
expect(DOM.querySelectorAll(view.rootNodes[0], 'copy-me').length).toEqual(0);
expect(DOM.getText(view.rootNodes[0])).toEqual('');
async.done();
});
}));

View File

@ -15,7 +15,7 @@ import {DOM} from 'angular2/src/dom/dom_adapter';
import {Decorator, Component} from 'angular2/src/core/annotations/annotations';
import {Template} from 'angular2/src/core/annotations/template';
import {NgElement} from 'angular2/src/core/dom/element';
import {NgElement} from 'angular2/src/core/compiler/ng_element';
import {NonBindable} from 'angular2/src/directives/non_bindable';
@ -27,7 +27,7 @@ export function main() {
var template = '<div>{{text}}<span non-bindable>{{text}}</span></div>';
tb.createView(TestComponent, {html: template}).then((view) => {
view.detectChanges();
expect(DOM.getText(view.nodes[0])).toEqual('foo{{text}}');
expect(DOM.getText(view.rootNodes[0])).toEqual('foo{{text}}');
async.done();
});
}));
@ -36,7 +36,7 @@ export function main() {
var template = '<div non-bindable><span id=child test-dec>{{text}}</span></div>';
tb.createView(TestComponent, {html: template}).then((view) => {
view.detectChanges();
var span = DOM.querySelector(view.nodes[0], '#child');
var span = DOM.querySelector(view.rootNodes[0], '#child');
expect(DOM.hasClass(span, 'compiled')).toBeFalsy();
async.done();
});
@ -46,7 +46,7 @@ export function main() {
var template = '<div><span id=child non-bindable test-dec>{{text}}</span></div>';
tb.createView(TestComponent, {html: template}).then((view) => {
view.detectChanges();
var span = DOM.querySelector(view.nodes[0], '#child');
var span = DOM.querySelector(view.rootNodes[0], '#child');
expect(DOM.hasClass(span, 'compiled')).toBeTruthy();
async.done();
});

View File

@ -31,15 +31,15 @@ export function main() {
tb.createView(TestComponent, {html: template}).then((view) => {
view.detectChanges();
expect(DOM.getText(view.nodes[0])).toEqual('');
expect(DOM.getText(view.rootNodes[0])).toEqual('');
view.context.switchValue = 'a';
view.detectChanges();
expect(DOM.getText(view.nodes[0])).toEqual('when a');
expect(DOM.getText(view.rootNodes[0])).toEqual('when a');
view.context.switchValue = 'b';
view.detectChanges();
expect(DOM.getText(view.nodes[0])).toEqual('when b');
expect(DOM.getText(view.rootNodes[0])).toEqual('when b');
async.done();
});
@ -55,15 +55,15 @@ export function main() {
tb.createView(TestComponent, {html: template}).then((view) => {
view.detectChanges();
expect(DOM.getText(view.nodes[0])).toEqual('when default');
expect(DOM.getText(view.rootNodes[0])).toEqual('when default');
view.context.switchValue = 'a';
view.detectChanges();
expect(DOM.getText(view.nodes[0])).toEqual('when a');
expect(DOM.getText(view.rootNodes[0])).toEqual('when a');
view.context.switchValue = 'b';
view.detectChanges();
expect(DOM.getText(view.nodes[0])).toEqual('when default');
expect(DOM.getText(view.rootNodes[0])).toEqual('when default');
async.done();
});
@ -83,15 +83,15 @@ export function main() {
tb.createView(TestComponent, {html: template}).then((view) => {
view.detectChanges();
expect(DOM.getText(view.nodes[0])).toEqual('when default1;when default2;');
expect(DOM.getText(view.rootNodes[0])).toEqual('when default1;when default2;');
view.context.switchValue = 'a';
view.detectChanges();
expect(DOM.getText(view.nodes[0])).toEqual('when a1;when a2;');
expect(DOM.getText(view.rootNodes[0])).toEqual('when a1;when a2;');
view.context.switchValue = 'b';
view.detectChanges();
expect(DOM.getText(view.nodes[0])).toEqual('when b1;when b2;');
expect(DOM.getText(view.rootNodes[0])).toEqual('when b1;when b2;');
async.done();
});
@ -113,23 +113,23 @@ export function main() {
view.context.when2 = 'b';
view.context.switchValue = 'a';
view.detectChanges();
expect(DOM.getText(view.nodes[0])).toEqual('when 1;');
expect(DOM.getText(view.rootNodes[0])).toEqual('when 1;');
view.context.switchValue = 'b';
view.detectChanges();
expect(DOM.getText(view.nodes[0])).toEqual('when 2;');
expect(DOM.getText(view.rootNodes[0])).toEqual('when 2;');
view.context.switchValue = 'c';
view.detectChanges();
expect(DOM.getText(view.nodes[0])).toEqual('when default;');
expect(DOM.getText(view.rootNodes[0])).toEqual('when default;');
view.context.when1 = 'c';
view.detectChanges();
expect(DOM.getText(view.nodes[0])).toEqual('when 1;');
expect(DOM.getText(view.rootNodes[0])).toEqual('when 1;');
view.context.when1 = 'd';
view.detectChanges();
expect(DOM.getText(view.nodes[0])).toEqual('when default;');
expect(DOM.getText(view.rootNodes[0])).toEqual('when default;');
async.done();
});

View File

@ -25,8 +25,8 @@ import {ControlGroupDirective, ControlDirective, Control, ControlGroup, Optional
DefaultValueAccessor, Validators} from 'angular2/forms';
export function main() {
if (DOM.supportsDOMEvents()) {
describe("integration tests", () => {
describe("integration tests", () => {
if (DOM.supportsDOMEvents()) {
it("should initialize DOM elements with the given form object",
inject([TestBed, AsyncTestCompleter], (tb, async) => {
var ctx = new MyComp(new ControlGroup({
@ -362,8 +362,8 @@ export function main() {
});
}));
});
});
}
}
});
}
@Component({selector: "my-comp"})

View File

@ -1,5 +1,5 @@
import {isBlank, isPresent, BaseException} from 'angular2/src/facade/lang';
import {MapWrapper, ListWrapper, List} from 'angular2/src/facade/collection';
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';
@ -177,7 +177,7 @@ export class LoggingEventDispatcher extends EventDispatcher {
this.log = [];
}
dispatchEvent(
elementIndex:number, eventName:string, locals:List<any>
elementIndex:number, eventName:string, locals:Map<string, any>
) {
ListWrapper.push(this.log, [elementIndex, eventName, locals]);
}

View File

@ -279,7 +279,7 @@ export function main() {
// var temp = `<simple>aaa<input type="text" id="focused-input" ng-class="{'aClass' : showClass}"> bbb</simple>`;
//
// compile(temp, (view, lc) => {
// var input = view.nodes[1];
// var input = view.rootNodes[1];
// input.focus();
//
// expect(document.activeElement.id).toEqual("focused-input");

View File

@ -1,7 +1,7 @@
import {AsyncTestCompleter, inject, ddescribe, describe, it, iit, xit, expect, SpyObject} from 'angular2/test_lib';
import {DOM, DomAdapter} from 'angular2/src/dom/dom_adapter';
import {NgElement} from 'angular2/src/core/dom/element';
import {NgElement} from 'angular2/src/core/compiler/ng_element';
import {Ruler, Rectangle} from 'angular2/src/services/ruler';
import {createRectangle} from './rectangle_mock';
@ -35,7 +35,7 @@ export function main() {
inject([AsyncTestCompleter], (async) => {
var ruler = new Ruler(new DomAdapterMock(createRectangle(10, 20, 200, 100)));
ruler.measure(new NgElement(null)).then((rect) => {
ruler.measure(new FakeNgElement(null)).then((rect) => {
assertDimensions(rect, 10, 210, 20, 120, 200, 100);
async.done();
});
@ -46,7 +46,7 @@ export function main() {
inject([AsyncTestCompleter], (async) => {
var ruler = new Ruler(DOM);
ruler.measure(new NgElement(DOM.createElement('div'))).then((rect) => {
ruler.measure(new FakeNgElement(DOM.createElement('div'))).then((rect) => {
//here we are using an element created in a doc fragment so all the measures will come back as 0
assertDimensions(rect, 0, 0, 0, 0, 0, 0);
async.done();
@ -55,3 +55,16 @@ export function main() {
});
}
class FakeNgElement extends NgElement {
_domElement;
constructor(domElement) {
super(null, null);
this._domElement = domElement;
}
get domElement() {
return this._domElement;
}
}