feat(core): speed up view creation via code gen for view factories.
BREAKING CHANGE: - Platform pipes can only contain types and arrays of types, but no bindings any more. - When using transformers, platform pipes need to be specified explicitly in the pubspec.yaml via the new config option `platform_pipes`. - `Compiler.compileInHost` now returns a `HostViewFactoryRef` - Component view is not yet created when component constructor is called. -> use `onInit` lifecycle callback to access the view of a component - `ViewRef#setLocal` has been moved to new type `EmbeddedViewRef` - `internalView` is gone, use `EmbeddedViewRef.rootNodes` to access the root nodes of an embedded view - `renderer.setElementProperty`, `..setElementStyle`, `..setElementAttribute` now take a native element instead of an ElementRef - `Renderer` interface now operates on plain native nodes, instead of `RenderElementRef`s or `RenderViewRef`s Closes #5993
This commit is contained in:
@@ -13,54 +13,36 @@ import {
|
||||
beforeEachProviders
|
||||
} from 'angular2/testing_internal';
|
||||
|
||||
import {Component, View, provide} from 'angular2/core';
|
||||
import {SpyProtoViewFactory} from '../spies';
|
||||
import {
|
||||
CompiledHostTemplate,
|
||||
CompiledComponentTemplate,
|
||||
BeginComponentCmd
|
||||
} from 'angular2/src/core/linker/template_commands';
|
||||
import {provide} from 'angular2/core';
|
||||
import {Compiler} from 'angular2/src/core/linker/compiler';
|
||||
import {ProtoViewFactory} from 'angular2/src/core/linker/proto_view_factory';
|
||||
import {reflector, ReflectionInfo} from 'angular2/src/core/reflection/reflection';
|
||||
import {AppProtoView} from 'angular2/src/core/linker/view';
|
||||
import {Compiler_} from "angular2/src/core/linker/compiler";
|
||||
import {HostViewFactory} from 'angular2/src/core/linker/view';
|
||||
|
||||
export function main() {
|
||||
describe('Compiler', () => {
|
||||
var compiler: Compiler;
|
||||
var protoViewFactorySpy;
|
||||
var someProtoView;
|
||||
var cht: CompiledHostTemplate;
|
||||
var someHostViewFactory;
|
||||
|
||||
beforeEachProviders(() => {
|
||||
protoViewFactorySpy = new SpyProtoViewFactory();
|
||||
someProtoView = new AppProtoView(null, null, null, null, null, null, null);
|
||||
protoViewFactorySpy.spy('createHost').andReturn(someProtoView);
|
||||
var factory = provide(ProtoViewFactory, {useValue: protoViewFactorySpy});
|
||||
var classProvider = provide(Compiler, {useClass: Compiler_});
|
||||
var providers = [factory, classProvider];
|
||||
return providers;
|
||||
});
|
||||
beforeEachProviders(() => [provide(Compiler, {useClass: Compiler_})]);
|
||||
|
||||
beforeEach(inject([Compiler], (_compiler) => {
|
||||
compiler = _compiler;
|
||||
cht = new CompiledHostTemplate(new CompiledComponentTemplate('aCompId', null, null, null));
|
||||
reflector.registerType(SomeComponent, new ReflectionInfo([cht]));
|
||||
someHostViewFactory = new HostViewFactory(null, null);
|
||||
reflector.registerType(SomeComponent, new ReflectionInfo([someHostViewFactory]));
|
||||
}));
|
||||
|
||||
it('should read the template from an annotation', inject([AsyncTestCompleter], (async) => {
|
||||
it('should read the template from an annotation',
|
||||
inject([AsyncTestCompleter, Compiler], (async, compiler) => {
|
||||
compiler.compileInHost(SomeComponent)
|
||||
.then((_) => {
|
||||
expect(protoViewFactorySpy.spy('createHost')).toHaveBeenCalledWith(cht);
|
||||
.then((hostViewFactoryRef) => {
|
||||
expect(hostViewFactoryRef.internalHostViewFactory).toBe(someHostViewFactory);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should clear the cache', () => {
|
||||
compiler.clearCache();
|
||||
expect(protoViewFactorySpy.spy('clearCache')).toHaveBeenCalled();
|
||||
});
|
||||
it('should clear the cache', inject([Compiler], (compiler) => {
|
||||
// Nothing to assert for now...
|
||||
compiler.clearCache();
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ import {DOM} from 'angular2/src/platform/dom/dom_adapter';
|
||||
import {ComponentFixture_} from "angular2/src/testing/test_component_builder";
|
||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||
import {PromiseWrapper} from 'angular2/src/facade/promise';
|
||||
import {stringify} from 'angular2/src/facade/lang';
|
||||
|
||||
export function main() {
|
||||
describe('DynamicComponentLoader', function() {
|
||||
@@ -164,6 +165,44 @@ export function main() {
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should allow to pass projectable nodes',
|
||||
inject([DynamicComponentLoader, TestComponentBuilder, AsyncTestCompleter],
|
||||
(loader, tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MyComp,
|
||||
new ViewMetadata({template: '<div #loc></div>', directives: []}))
|
||||
.createAsync(MyComp)
|
||||
.then((tc) => {
|
||||
loader.loadIntoLocation(DynamicallyLoadedWithNgContent,
|
||||
tc.debugElement.elementRef, 'loc', null,
|
||||
[[DOM.createTextNode('hello')]])
|
||||
.then(ref => {
|
||||
tc.detectChanges();
|
||||
expect(tc.nativeElement).toHaveText('dynamic(hello)');
|
||||
async.done();
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
it('should throw if not enough projectable nodes are passed in',
|
||||
inject(
|
||||
[DynamicComponentLoader, TestComponentBuilder, AsyncTestCompleter],
|
||||
(loader, tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MyComp,
|
||||
new ViewMetadata({template: '<div #loc></div>', directives: []}))
|
||||
.createAsync(MyComp)
|
||||
.then((tc) => {
|
||||
PromiseWrapper.catchError(
|
||||
loader.loadIntoLocation(DynamicallyLoadedWithNgContent,
|
||||
tc.debugElement.elementRef, 'loc', null, []),
|
||||
(e) => {
|
||||
expect(e.message).toContain(
|
||||
`The component ${stringify(DynamicallyLoadedWithNgContent)} has 1 <ng-content> elements, but only 0 slots were provided`);
|
||||
async.done();
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
describe("loading next to a location", () => {
|
||||
@@ -248,17 +287,37 @@ export function main() {
|
||||
});
|
||||
}));
|
||||
|
||||
it('should allow to pass projectable nodes',
|
||||
inject([DynamicComponentLoader, TestComponentBuilder, AsyncTestCompleter],
|
||||
(loader, tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MyComp, new ViewMetadata({template: '', directives: [Location]}))
|
||||
.createAsync(MyComp)
|
||||
.then((tc) => {
|
||||
loader.loadNextToLocation(DynamicallyLoadedWithNgContent,
|
||||
tc.debugElement.elementRef, null,
|
||||
[[DOM.createTextNode('hello')]])
|
||||
.then(ref => {
|
||||
tc.detectChanges();
|
||||
var newlyInsertedElement =
|
||||
DOM.nextSibling(tc.debugElement.nativeElement);
|
||||
expect(newlyInsertedElement).toHaveText('dynamic(hello)');
|
||||
async.done();
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
describe('loadAsRoot', () => {
|
||||
it('should allow to create, update and destroy components',
|
||||
inject([AsyncTestCompleter, DynamicComponentLoader, DOCUMENT, Injector],
|
||||
(async, loader, doc, injector) => {
|
||||
var rootEl = el('<child-cmp></child-cmp>');
|
||||
var rootEl = createRootElement(doc, 'child-cmp');
|
||||
DOM.appendChild(doc.body, rootEl);
|
||||
loader.loadAsRoot(ChildComp, null, injector)
|
||||
.then((componentRef) => {
|
||||
var el = new ComponentFixture_(componentRef);
|
||||
|
||||
expect(rootEl.parentNode).toBe(doc.body);
|
||||
|
||||
el.detectChanges();
|
||||
@@ -279,11 +338,35 @@ export function main() {
|
||||
});
|
||||
}));
|
||||
|
||||
it('should allow to pass projectable nodes',
|
||||
inject([AsyncTestCompleter, DynamicComponentLoader, DOCUMENT, Injector],
|
||||
(async, loader, doc, injector) => {
|
||||
var rootEl = createRootElement(doc, 'dummy');
|
||||
DOM.appendChild(doc.body, rootEl);
|
||||
loader.loadAsRoot(DynamicallyLoadedWithNgContent, null, injector, null,
|
||||
[[DOM.createTextNode('hello')]])
|
||||
.then((_) => {
|
||||
expect(rootEl).toHaveText('dynamic(hello)');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function createRootElement(doc: any, name: string): any {
|
||||
var nodes = DOM.querySelectorAll(doc, name);
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
DOM.remove(nodes[i]);
|
||||
}
|
||||
var rootEl = el(`<${name}></${name}>`);
|
||||
DOM.appendChild(doc.body, rootEl);
|
||||
return rootEl;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'child-cmp',
|
||||
})
|
||||
@@ -335,6 +418,14 @@ class DynamicallyLoadedWithHostProps {
|
||||
constructor() { this.id = "default"; }
|
||||
}
|
||||
|
||||
@Component({selector: 'dummy'})
|
||||
@View({template: "dynamic(<ng-content></ng-content>)"})
|
||||
class DynamicallyLoadedWithNgContent {
|
||||
id: string;
|
||||
|
||||
constructor() { this.id = "default"; }
|
||||
}
|
||||
|
||||
@Component({selector: 'location'})
|
||||
@View({template: "Location;"})
|
||||
class Location {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,843 @@
|
||||
// TODO(tbosch): clang-format screws this up, see https://github.com/angular/clang-format/issues/11.
|
||||
// Enable clang-format here again when this is fixed.
|
||||
// clang-format off
|
||||
import {
|
||||
describe,
|
||||
ddescribe,
|
||||
it,
|
||||
iit,
|
||||
xit,
|
||||
xdescribe,
|
||||
expect,
|
||||
beforeEach,
|
||||
beforeEachBindings,
|
||||
inject,
|
||||
AsyncTestCompleter,
|
||||
el,
|
||||
containsRegexp
|
||||
} from 'angular2/testing_internal';
|
||||
import {SpyView, SpyElementRef, SpyDirectiveResolver, SpyProtoView, SpyChangeDetector, SpyAppViewManager} from '../spies';
|
||||
import {isBlank, isPresent, stringify, Type} from 'angular2/src/facade/lang';
|
||||
import {ResolvedProvider} from 'angular2/src/core/di';
|
||||
import {
|
||||
ListWrapper,
|
||||
MapWrapper,
|
||||
StringMapWrapper,
|
||||
iterateListLike
|
||||
} from 'angular2/src/facade/collection';
|
||||
import {
|
||||
AppProtoElement,
|
||||
AppElement,
|
||||
DirectiveProvider
|
||||
} from 'angular2/src/core/linker/element';
|
||||
import {ResolvedMetadataCache} from 'angular2/src/core/linker/resolved_metadata_cache';
|
||||
import {DirectiveResolver} from 'angular2/src/core/linker/directive_resolver';
|
||||
import {
|
||||
Attribute,
|
||||
Query,
|
||||
ViewQuery,
|
||||
ComponentMetadata,
|
||||
DirectiveMetadata,
|
||||
ViewEncapsulation
|
||||
} from 'angular2/src/core/metadata';
|
||||
import {OnDestroy, Directive} from 'angular2/core';
|
||||
import {provide, Injector, Provider, Optional, Inject, Injectable, Self, SkipSelf, InjectMetadata, Host, HostMetadata, SkipSelfMetadata} from 'angular2/core';
|
||||
import {ViewContainerRef, ViewContainerRef_} from 'angular2/src/core/linker/view_container_ref';
|
||||
import {TemplateRef, TemplateRef_} from 'angular2/src/core/linker/template_ref';
|
||||
import {ElementRef} from 'angular2/src/core/linker/element_ref';
|
||||
import {DynamicChangeDetector, ChangeDetectorRef, Parser, Lexer} from 'angular2/src/core/change_detection/change_detection';
|
||||
import {ChangeDetectorRef_} from 'angular2/src/core/change_detection/change_detector_ref';
|
||||
import {QueryList} from 'angular2/src/core/linker/query_list';
|
||||
import {AppView, AppProtoView} from "angular2/src/core/linker/view";
|
||||
import {ViewType} from "angular2/src/core/linker/view_type";
|
||||
|
||||
@Directive({selector: ''})
|
||||
class SimpleDirective {}
|
||||
|
||||
class SimpleService {}
|
||||
|
||||
@Directive({selector: ''})
|
||||
class SomeOtherDirective {}
|
||||
|
||||
var _constructionCount;
|
||||
@Directive({selector: ''})
|
||||
class CountingDirective {
|
||||
count: number;
|
||||
constructor() {
|
||||
this.count = _constructionCount;
|
||||
_constructionCount += 1;
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: ''})
|
||||
class FancyCountingDirective extends CountingDirective {
|
||||
constructor() { super(); }
|
||||
}
|
||||
|
||||
@Directive({selector: ''})
|
||||
class NeedsDirective {
|
||||
dependency: SimpleDirective;
|
||||
constructor(@Self() dependency: SimpleDirective) { this.dependency = dependency; }
|
||||
}
|
||||
|
||||
@Directive({selector: ''})
|
||||
class OptionallyNeedsDirective {
|
||||
dependency: SimpleDirective;
|
||||
constructor(@Self() @Optional() dependency: SimpleDirective) { this.dependency = dependency; }
|
||||
}
|
||||
|
||||
@Directive({selector: ''})
|
||||
class NeeedsDirectiveFromHost {
|
||||
dependency: SimpleDirective;
|
||||
constructor(@Host() dependency: SimpleDirective) { this.dependency = dependency; }
|
||||
}
|
||||
|
||||
@Directive({selector: ''})
|
||||
class NeedsDirectiveFromHostShadowDom {
|
||||
dependency: SimpleDirective;
|
||||
constructor(dependency: SimpleDirective) { this.dependency = dependency; }
|
||||
}
|
||||
|
||||
@Directive({selector: ''})
|
||||
class NeedsService {
|
||||
service: any;
|
||||
constructor(@Inject("service") service) { this.service = service; }
|
||||
}
|
||||
|
||||
@Directive({selector: ''})
|
||||
class NeedsServiceFromHost {
|
||||
service: any;
|
||||
constructor(@Host() @Inject("service") service) { this.service = service; }
|
||||
}
|
||||
|
||||
class HasEventEmitter {
|
||||
emitter;
|
||||
constructor() { this.emitter = "emitter"; }
|
||||
}
|
||||
|
||||
@Directive({selector: ''})
|
||||
class NeedsAttribute {
|
||||
typeAttribute;
|
||||
titleAttribute;
|
||||
fooAttribute;
|
||||
constructor(@Attribute('type') typeAttribute: String, @Attribute('title') titleAttribute: String,
|
||||
@Attribute('foo') fooAttribute: String) {
|
||||
this.typeAttribute = typeAttribute;
|
||||
this.titleAttribute = titleAttribute;
|
||||
this.fooAttribute = fooAttribute;
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: ''})
|
||||
class NeedsAttributeNoType {
|
||||
fooAttribute;
|
||||
constructor(@Attribute('foo') fooAttribute) { this.fooAttribute = fooAttribute; }
|
||||
}
|
||||
|
||||
@Directive({selector: ''})
|
||||
class NeedsQuery {
|
||||
query: QueryList<CountingDirective>;
|
||||
constructor(@Query(CountingDirective) query: QueryList<CountingDirective>) { this.query = query; }
|
||||
}
|
||||
|
||||
@Directive({selector: ''})
|
||||
class NeedsViewQuery {
|
||||
query: QueryList<CountingDirective>;
|
||||
constructor(@ViewQuery(CountingDirective) query: QueryList<CountingDirective>) { this.query = query; }
|
||||
}
|
||||
|
||||
@Directive({selector: ''})
|
||||
class NeedsQueryByVarBindings {
|
||||
query: QueryList<any>;
|
||||
constructor(@Query("one,two") query: QueryList<any>) { this.query = query; }
|
||||
}
|
||||
|
||||
@Directive({selector: ''})
|
||||
class NeedsTemplateRefQuery {
|
||||
query: QueryList<TemplateRef>;
|
||||
constructor(@Query(TemplateRef) query: QueryList<TemplateRef>) { this.query = query; }
|
||||
}
|
||||
|
||||
@Directive({selector: ''})
|
||||
class NeedsElementRef {
|
||||
elementRef;
|
||||
constructor(ref: ElementRef) { this.elementRef = ref; }
|
||||
}
|
||||
|
||||
@Directive({selector: ''})
|
||||
class NeedsViewContainer {
|
||||
viewContainer;
|
||||
constructor(vc: ViewContainerRef) { this.viewContainer = vc; }
|
||||
}
|
||||
|
||||
@Directive({selector: ''})
|
||||
class NeedsTemplateRef {
|
||||
templateRef;
|
||||
constructor(ref: TemplateRef) { this.templateRef = ref; }
|
||||
}
|
||||
|
||||
@Directive({selector: ''})
|
||||
class OptionallyInjectsTemplateRef {
|
||||
templateRef;
|
||||
constructor(@Optional() ref: TemplateRef) { this.templateRef = ref; }
|
||||
}
|
||||
|
||||
@Directive({selector: ''})
|
||||
class DirectiveNeedsChangeDetectorRef {
|
||||
constructor(public changeDetectorRef: ChangeDetectorRef) {}
|
||||
}
|
||||
|
||||
@Directive({selector: ''})
|
||||
class ComponentNeedsChangeDetectorRef {
|
||||
constructor(public changeDetectorRef: ChangeDetectorRef) {}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
class PipeNeedsChangeDetectorRef {
|
||||
constructor(public changeDetectorRef: ChangeDetectorRef) {}
|
||||
}
|
||||
|
||||
class A_Needs_B {
|
||||
constructor(dep) {}
|
||||
}
|
||||
|
||||
class B_Needs_A {
|
||||
constructor(dep) {}
|
||||
}
|
||||
|
||||
class DirectiveWithDestroy implements OnDestroy {
|
||||
ngOnDestroyCounter: number;
|
||||
|
||||
constructor() { this.ngOnDestroyCounter = 0; }
|
||||
|
||||
ngOnDestroy() { this.ngOnDestroyCounter++; }
|
||||
}
|
||||
|
||||
@Directive({selector: ''})
|
||||
class D0 {}
|
||||
@Directive({selector: ''})
|
||||
class D1 {}
|
||||
@Directive({selector: ''})
|
||||
class D2 {}
|
||||
@Directive({selector: ''})
|
||||
class D3 {}
|
||||
@Directive({selector: ''})
|
||||
class D4 {}
|
||||
@Directive({selector: ''})
|
||||
class D5 {}
|
||||
@Directive({selector: ''})
|
||||
class D6 {}
|
||||
@Directive({selector: ''})
|
||||
class D7 {}
|
||||
@Directive({selector: ''})
|
||||
class D8 {}
|
||||
@Directive({selector: ''})
|
||||
class D9 {}
|
||||
@Directive({selector: ''})
|
||||
class D10 {}
|
||||
@Directive({selector: ''})
|
||||
class D11 {}
|
||||
@Directive({selector: ''})
|
||||
class D12 {}
|
||||
@Directive({selector: ''})
|
||||
class D13 {}
|
||||
@Directive({selector: ''})
|
||||
class D14 {}
|
||||
@Directive({selector: ''})
|
||||
class D15 {}
|
||||
@Directive({selector: ''})
|
||||
class D16 {}
|
||||
@Directive({selector: ''})
|
||||
class D17 {}
|
||||
@Directive({selector: ''})
|
||||
class D18 {}
|
||||
@Directive({selector: ''})
|
||||
class D19 {}
|
||||
|
||||
export function main() {
|
||||
// An injector with more than 10 providers will switch to the dynamic strategy
|
||||
var dynamicStrategyDirectives = [D0, D1, D2, D3, D4, D5, D6, D7, D8, D9, D10, D11, D12, D13, D14, D15, D16, D17, D18, D19];
|
||||
var resolvedMetadataCache:ResolvedMetadataCache;
|
||||
var mockDirectiveMeta:Map<Type, DirectiveMetadata>;
|
||||
var directiveResolver:SpyDirectiveResolver;
|
||||
var dummyView:AppView;
|
||||
var dummyViewFactory:Function;
|
||||
|
||||
function createView(type: ViewType, containerAppElement:AppElement = null, imperativelyCreatedProviders: ResolvedProvider[] = null, rootInjector: Injector = null, pipes: Type[] = null):AppView {
|
||||
if (isBlank(pipes)) {
|
||||
pipes = [];
|
||||
}
|
||||
var proto = AppProtoView.create(resolvedMetadataCache, type, pipes, {});
|
||||
var cd = new SpyChangeDetector();
|
||||
cd.prop('ref', new ChangeDetectorRef_(<any>cd));
|
||||
|
||||
var view = new AppView(proto, null, <any>new SpyAppViewManager(), [], containerAppElement, imperativelyCreatedProviders, rootInjector, <any> cd);
|
||||
view.init([], [], [], []);
|
||||
return view;
|
||||
}
|
||||
|
||||
function protoAppElement(index, directives: Type[], attributes: {[key:string]:string} = null, dirVariableBindings:{[key:string]:number} = null) {
|
||||
return AppProtoElement.create(resolvedMetadataCache, index, attributes, directives, dirVariableBindings);
|
||||
}
|
||||
|
||||
function appElement(parent: AppElement, directives: Type[],
|
||||
view: AppView = null, embeddedViewFactory: Function = null, attributes: {[key:string]:string} = null, dirVariableBindings:{[key:string]:number} = null) {
|
||||
if (isBlank(view)) {
|
||||
view = dummyView;
|
||||
}
|
||||
var proto = protoAppElement(0, directives, attributes, dirVariableBindings);
|
||||
var el = new AppElement(proto, view, parent, null, embeddedViewFactory);
|
||||
view.appElements.push(el);
|
||||
return el;
|
||||
}
|
||||
|
||||
function parentChildElements(parentDirectives: Type[], childDirectives:Type[], view: AppView = null) {
|
||||
if (isBlank(view)) {
|
||||
view = dummyView;
|
||||
}
|
||||
var parent = appElement(null, parentDirectives, view);
|
||||
var child = appElement(parent, childDirectives, view);
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
function hostShadowElement(hostDirectives: Type[],
|
||||
viewDirectives: Type[]): AppElement {
|
||||
var host = appElement(null, hostDirectives);
|
||||
var view = createView(ViewType.COMPONENT, host);
|
||||
host.attachComponentView(view);
|
||||
|
||||
return appElement(null, viewDirectives, view);
|
||||
}
|
||||
|
||||
function init() {
|
||||
beforeEachBindings(() => {
|
||||
var delegateDirectiveResolver = new DirectiveResolver();
|
||||
directiveResolver = new SpyDirectiveResolver();
|
||||
directiveResolver.spy('resolve').andCallFake( (directiveType) => {
|
||||
var result = mockDirectiveMeta.get(directiveType);
|
||||
if (isBlank(result)) {
|
||||
result = delegateDirectiveResolver.resolve(directiveType);
|
||||
}
|
||||
return result;
|
||||
});
|
||||
return [
|
||||
provide(DirectiveResolver, {useValue: directiveResolver})
|
||||
];
|
||||
});
|
||||
beforeEach(inject([ResolvedMetadataCache], (_metadataCache) => {
|
||||
mockDirectiveMeta = new Map<Type, DirectiveMetadata>();
|
||||
resolvedMetadataCache = _metadataCache;
|
||||
dummyView = createView(ViewType.HOST);
|
||||
dummyViewFactory = () => {};
|
||||
_constructionCount = 0;
|
||||
}));
|
||||
}
|
||||
|
||||
describe("ProtoAppElement", () => {
|
||||
init();
|
||||
|
||||
describe('inline strategy', () => {
|
||||
it("should allow for direct access using getProviderAtIndex", () => {
|
||||
var proto = protoAppElement(0, [SimpleDirective]);
|
||||
|
||||
expect(proto.getProviderAtIndex(0)).toBeAnInstanceOf(DirectiveProvider);
|
||||
expect(() => proto.getProviderAtIndex(-1)).toThrowError('Index -1 is out-of-bounds.');
|
||||
expect(() => proto.getProviderAtIndex(10)).toThrowError('Index 10 is out-of-bounds.');
|
||||
});
|
||||
});
|
||||
|
||||
describe('dynamic strategy', () => {
|
||||
it("should allow for direct access using getProviderAtIndex", () => {
|
||||
var proto = protoAppElement(0, dynamicStrategyDirectives);
|
||||
|
||||
expect(proto.getProviderAtIndex(0)).toBeAnInstanceOf(DirectiveProvider);
|
||||
expect(() => proto.getProviderAtIndex(-1)).toThrowError('Index -1 is out-of-bounds.');
|
||||
expect(() => proto.getProviderAtIndex(dynamicStrategyDirectives.length - 1)).not.toThrow();
|
||||
expect(() => proto.getProviderAtIndex(dynamicStrategyDirectives.length))
|
||||
.toThrowError(`Index ${dynamicStrategyDirectives.length} is out-of-bounds.`);
|
||||
});
|
||||
});
|
||||
|
||||
describe(".create", () => {
|
||||
it("should collect providers from all directives", () => {
|
||||
mockDirectiveMeta.set(SimpleDirective, new DirectiveMetadata({providers: [provide('injectable1', {useValue: 'injectable1'})]}));
|
||||
mockDirectiveMeta.set(SomeOtherDirective, new DirectiveMetadata({
|
||||
providers: [provide('injectable2', {useValue: 'injectable2'})]
|
||||
}));
|
||||
var pel = protoAppElement( 0, [
|
||||
SimpleDirective,
|
||||
SomeOtherDirective
|
||||
]);
|
||||
|
||||
expect(pel.getProviderAtIndex(0).key.token).toBe(SimpleDirective);
|
||||
expect(pel.getProviderAtIndex(1).key.token).toBe(SomeOtherDirective);
|
||||
expect(pel.getProviderAtIndex(2).key.token).toEqual("injectable1");
|
||||
expect(pel.getProviderAtIndex(3).key.token).toEqual("injectable2");
|
||||
});
|
||||
|
||||
it("should collect view providers from the component", () => {
|
||||
mockDirectiveMeta.set(SimpleDirective, new ComponentMetadata({
|
||||
viewProviders: [provide('injectable1', {useValue: 'injectable1'})]
|
||||
}));
|
||||
var pel = protoAppElement(0, [SimpleDirective]);
|
||||
|
||||
expect(pel.getProviderAtIndex(0).key.token).toBe(SimpleDirective);
|
||||
expect(pel.getProviderAtIndex(1).key.token).toEqual("injectable1");
|
||||
});
|
||||
|
||||
it("should flatten nested arrays in viewProviders and providers", () => {
|
||||
mockDirectiveMeta.set(SimpleDirective, new ComponentMetadata({
|
||||
viewProviders: [[[provide('view', {useValue: 'view'})]]],
|
||||
providers: [[[provide('host', {useValue: 'host'})]]]
|
||||
}));
|
||||
var pel = protoAppElement(0, [SimpleDirective]);
|
||||
|
||||
expect(pel.getProviderAtIndex(0).key.token).toBe(SimpleDirective);
|
||||
expect(pel.getProviderAtIndex(1).key.token).toEqual("view");
|
||||
expect(pel.getProviderAtIndex(2).key.token).toEqual("host");
|
||||
});
|
||||
|
||||
it('should support an arbitrary number of providers', () => {
|
||||
var pel = protoAppElement(0, dynamicStrategyDirectives);
|
||||
expect(pel.getProviderAtIndex(0).key.token).toBe(D0);
|
||||
expect(pel.getProviderAtIndex(19).key.token).toBe(D19);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("AppElement", () => {
|
||||
init();
|
||||
|
||||
[{ strategy: 'inline', directives: [] }, { strategy: 'dynamic',
|
||||
directives: dynamicStrategyDirectives }].forEach((context) => {
|
||||
|
||||
var extraDirectives = context['directives'];
|
||||
describe(`${context['strategy']} strategy`, () => {
|
||||
|
||||
describe("injection", () => {
|
||||
it("should instantiate directives that have no dependencies", () => {
|
||||
var directives = ListWrapper.concat([SimpleDirective], extraDirectives);
|
||||
var el = appElement(null, directives);
|
||||
expect(el.get(SimpleDirective)).toBeAnInstanceOf(SimpleDirective);
|
||||
});
|
||||
|
||||
it("should instantiate directives that depend on an arbitrary number of directives", () => {
|
||||
var directives = ListWrapper.concat([SimpleDirective, NeedsDirective], extraDirectives);
|
||||
var el = appElement(null, directives);
|
||||
|
||||
var d = el.get(NeedsDirective);
|
||||
|
||||
expect(d).toBeAnInstanceOf(NeedsDirective);
|
||||
expect(d.dependency).toBeAnInstanceOf(SimpleDirective);
|
||||
});
|
||||
|
||||
it("should instantiate providers that have dependencies with set visibility",
|
||||
function() {
|
||||
mockDirectiveMeta.set(SimpleDirective, new ComponentMetadata({
|
||||
providers: [provide('injectable1', {useValue: 'injectable1'})]
|
||||
}));
|
||||
mockDirectiveMeta.set(SomeOtherDirective, new ComponentMetadata({
|
||||
providers: [
|
||||
provide('injectable1', {useValue:'new-injectable1'}),
|
||||
provide('injectable2', {useFactory:
|
||||
(val) => `${val}-injectable2`,
|
||||
deps: [[new InjectMetadata('injectable1'), new SkipSelfMetadata()]]})
|
||||
]
|
||||
}));
|
||||
var childInj = parentChildElements(
|
||||
ListWrapper.concat([SimpleDirective], extraDirectives),
|
||||
[SomeOtherDirective]
|
||||
);
|
||||
expect(childInj.get('injectable2')).toEqual('injectable1-injectable2');
|
||||
});
|
||||
|
||||
it("should instantiate providers that have dependencies", () => {
|
||||
var providers = [
|
||||
provide('injectable1', {useValue: 'injectable1'}),
|
||||
provide('injectable2', {useFactory:
|
||||
(val) => `${val}-injectable2`,
|
||||
deps: ['injectable1']})
|
||||
];
|
||||
mockDirectiveMeta.set(SimpleDirective, new DirectiveMetadata({providers: providers}));
|
||||
var el = appElement(null, ListWrapper.concat(
|
||||
[SimpleDirective], extraDirectives));
|
||||
|
||||
expect(el.get('injectable2')).toEqual('injectable1-injectable2');
|
||||
});
|
||||
|
||||
it("should instantiate viewProviders that have dependencies", () => {
|
||||
var viewProviders = [
|
||||
provide('injectable1', {useValue: 'injectable1'}),
|
||||
provide('injectable2', {useFactory:
|
||||
(val) => `${val}-injectable2`,
|
||||
deps: ['injectable1']})
|
||||
];
|
||||
|
||||
mockDirectiveMeta.set(SimpleDirective, new ComponentMetadata({
|
||||
viewProviders: viewProviders}));
|
||||
var el = appElement(null, ListWrapper.concat(
|
||||
[SimpleDirective], extraDirectives));
|
||||
expect(el.get('injectable2')).toEqual('injectable1-injectable2');
|
||||
});
|
||||
|
||||
it("should instantiate components that depend on viewProviders providers", () => {
|
||||
mockDirectiveMeta.set(NeedsService, new ComponentMetadata({
|
||||
viewProviders: [provide('service', {useValue: 'service'})]
|
||||
}));
|
||||
var el = appElement(null,
|
||||
ListWrapper.concat([NeedsService], extraDirectives));
|
||||
expect(el.get(NeedsService).service).toEqual('service');
|
||||
});
|
||||
|
||||
it("should instantiate providers lazily", () => {
|
||||
var created = false;
|
||||
mockDirectiveMeta.set(SimpleDirective, new ComponentMetadata({
|
||||
providers: [provide('service', {useFactory: () => created = true})]
|
||||
}));
|
||||
var el = appElement(null,
|
||||
ListWrapper.concat([SimpleDirective],
|
||||
extraDirectives));
|
||||
|
||||
expect(created).toBe(false);
|
||||
|
||||
el.get('service');
|
||||
|
||||
expect(created).toBe(true);
|
||||
});
|
||||
|
||||
it("should instantiate view providers lazily", () => {
|
||||
var created = false;
|
||||
mockDirectiveMeta.set(SimpleDirective, new ComponentMetadata({
|
||||
viewProviders: [provide('service', {useFactory: () => created = true})]
|
||||
}));
|
||||
var el = appElement(null,
|
||||
ListWrapper.concat([SimpleDirective],
|
||||
extraDirectives));
|
||||
|
||||
expect(created).toBe(false);
|
||||
|
||||
el.get('service');
|
||||
|
||||
expect(created).toBe(true);
|
||||
});
|
||||
|
||||
it("should not instantiate other directives that depend on viewProviders providers",
|
||||
() => {
|
||||
mockDirectiveMeta.set(SimpleDirective,
|
||||
new ComponentMetadata({
|
||||
viewProviders: [provide("service", {useValue: "service"})]
|
||||
}));
|
||||
expect(() => { appElement(null, ListWrapper.concat([SimpleDirective, NeedsService], extraDirectives)); })
|
||||
.toThrowError(containsRegexp(
|
||||
`No provider for service! (${stringify(NeedsService) } -> service)`));
|
||||
});
|
||||
|
||||
it("should instantiate directives that depend on providers of other directives", () => {
|
||||
mockDirectiveMeta.set(SimpleDirective, new ComponentMetadata({
|
||||
providers: [provide('service', {useValue: 'hostService'})]})
|
||||
);
|
||||
var shadowInj = hostShadowElement(
|
||||
ListWrapper.concat([SimpleDirective], extraDirectives),
|
||||
ListWrapper.concat([NeedsService], extraDirectives)
|
||||
);
|
||||
expect(shadowInj.get(NeedsService).service).toEqual('hostService');
|
||||
});
|
||||
|
||||
it("should instantiate directives that depend on view providers of a component", () => {
|
||||
mockDirectiveMeta.set(SimpleDirective, new ComponentMetadata({
|
||||
viewProviders: [provide('service', {useValue: 'hostService'})]})
|
||||
);
|
||||
var shadowInj = hostShadowElement(
|
||||
ListWrapper.concat([SimpleDirective], extraDirectives),
|
||||
ListWrapper.concat([NeedsService], extraDirectives)
|
||||
);
|
||||
expect(shadowInj.get(NeedsService).service).toEqual('hostService');
|
||||
});
|
||||
|
||||
it("should instantiate directives in a root embedded view that depend on view providers of a component", () => {
|
||||
mockDirectiveMeta.set(SimpleDirective, new ComponentMetadata({
|
||||
viewProviders: [provide('service', {useValue: 'hostService'})]})
|
||||
);
|
||||
var host = appElement(null, ListWrapper.concat([SimpleDirective], extraDirectives));
|
||||
var componenetView = createView(ViewType.COMPONENT, host);
|
||||
host.attachComponentView(componenetView);
|
||||
|
||||
var anchor = appElement(null, [], componenetView);
|
||||
var embeddedView = createView(ViewType.EMBEDDED, anchor);
|
||||
|
||||
var rootEmbeddedEl = appElement(null, ListWrapper.concat([NeedsService], extraDirectives), embeddedView);
|
||||
expect(rootEmbeddedEl.get(NeedsService).service).toEqual('hostService');
|
||||
});
|
||||
|
||||
it("should instantiate directives that depend on imperatively created injector (bootstrap)", () => {
|
||||
var rootInjector = Injector.resolveAndCreate([
|
||||
provide("service", {useValue: 'appService'})
|
||||
]);
|
||||
var view = createView(ViewType.HOST, null, null, rootInjector);
|
||||
expect(appElement(null, [NeedsService], view).get(NeedsService).service).toEqual('appService');
|
||||
|
||||
expect(() => appElement(null, [NeedsServiceFromHost], view)).toThrowError();
|
||||
});
|
||||
|
||||
it("should instantiate directives that depend on imperatively created providers (root injector)", () => {
|
||||
var imperativelyCreatedProviders = Injector.resolve([
|
||||
provide("service", {useValue: 'appService'})
|
||||
]);
|
||||
var containerAppElement = appElement(null, []);
|
||||
var view = createView(ViewType.HOST, containerAppElement, imperativelyCreatedProviders, null);
|
||||
expect(appElement(null, [NeedsService], view).get(NeedsService).service).toEqual('appService');
|
||||
expect(appElement(null, [NeedsServiceFromHost], view).get(NeedsServiceFromHost).service).toEqual('appService');
|
||||
});
|
||||
|
||||
it("should not instantiate a directive in a view that has a host dependency on providers"+
|
||||
" of the component", () => {
|
||||
mockDirectiveMeta.set(SomeOtherDirective, new DirectiveMetadata({
|
||||
providers: [provide('service', {useValue: 'hostService'})]})
|
||||
);
|
||||
expect(() => {
|
||||
hostShadowElement(
|
||||
ListWrapper.concat([SomeOtherDirective], extraDirectives),
|
||||
ListWrapper.concat([NeedsServiceFromHost], extraDirectives)
|
||||
);
|
||||
}).toThrowError(new RegExp("No provider for service!"));
|
||||
});
|
||||
|
||||
it("should not instantiate a directive in a view that has a host dependency on providers"+
|
||||
" of a decorator directive", () => {
|
||||
mockDirectiveMeta.set(SomeOtherDirective, new DirectiveMetadata({
|
||||
providers: [provide('service', {useValue: 'hostService'})]}));
|
||||
expect(() => {
|
||||
hostShadowElement(
|
||||
ListWrapper.concat([SimpleDirective, SomeOtherDirective], extraDirectives),
|
||||
ListWrapper.concat([NeedsServiceFromHost], extraDirectives)
|
||||
);
|
||||
}).toThrowError(new RegExp("No provider for service!"));
|
||||
});
|
||||
|
||||
it("should get directives", () => {
|
||||
var child = hostShadowElement(
|
||||
ListWrapper.concat([SomeOtherDirective, SimpleDirective], extraDirectives),
|
||||
[NeedsDirectiveFromHostShadowDom]);
|
||||
|
||||
var d = child.get(NeedsDirectiveFromHostShadowDom);
|
||||
|
||||
expect(d).toBeAnInstanceOf(NeedsDirectiveFromHostShadowDom);
|
||||
expect(d.dependency).toBeAnInstanceOf(SimpleDirective);
|
||||
});
|
||||
|
||||
it("should get directives from the host", () => {
|
||||
var child = parentChildElements(ListWrapper.concat([SimpleDirective], extraDirectives),
|
||||
[NeeedsDirectiveFromHost]);
|
||||
|
||||
var d = child.get(NeeedsDirectiveFromHost);
|
||||
|
||||
expect(d).toBeAnInstanceOf(NeeedsDirectiveFromHost);
|
||||
expect(d.dependency).toBeAnInstanceOf(SimpleDirective);
|
||||
});
|
||||
|
||||
it("should throw when a dependency cannot be resolved", () => {
|
||||
expect(() => appElement(null, ListWrapper.concat([NeeedsDirectiveFromHost], extraDirectives)))
|
||||
.toThrowError(containsRegexp(
|
||||
`No provider for ${stringify(SimpleDirective) }! (${stringify(NeeedsDirectiveFromHost) } -> ${stringify(SimpleDirective) })`));
|
||||
});
|
||||
|
||||
it("should inject null when an optional dependency cannot be resolved", () => {
|
||||
var el = appElement(null, ListWrapper.concat([OptionallyNeedsDirective], extraDirectives));
|
||||
var d = el.get(OptionallyNeedsDirective);
|
||||
expect(d.dependency).toEqual(null);
|
||||
});
|
||||
|
||||
it("should allow for direct access using getDirectiveAtIndex", () => {
|
||||
var providers =
|
||||
ListWrapper.concat([SimpleDirective], extraDirectives);
|
||||
|
||||
var el = appElement(null, providers);
|
||||
|
||||
var firsIndexOut = providers.length > 10 ? providers.length : 10;
|
||||
|
||||
expect(el.getDirectiveAtIndex(0)).toBeAnInstanceOf(SimpleDirective);
|
||||
expect(() => el.getDirectiveAtIndex(-1)).toThrowError('Index -1 is out-of-bounds.');
|
||||
expect(() => el.getDirectiveAtIndex(firsIndexOut))
|
||||
.toThrowError(`Index ${firsIndexOut} is out-of-bounds.`);
|
||||
});
|
||||
|
||||
it("should instantiate directives that depend on the containing component", () => {
|
||||
mockDirectiveMeta.set(SimpleDirective, new ComponentMetadata());
|
||||
var shadow = hostShadowElement(ListWrapper.concat([SimpleDirective], extraDirectives),
|
||||
[NeeedsDirectiveFromHost]);
|
||||
|
||||
var d = shadow.get(NeeedsDirectiveFromHost);
|
||||
expect(d).toBeAnInstanceOf(NeeedsDirectiveFromHost);
|
||||
expect(d.dependency).toBeAnInstanceOf(SimpleDirective);
|
||||
});
|
||||
|
||||
it("should not instantiate directives that depend on other directives in the containing component's ElementInjector",
|
||||
() => {
|
||||
mockDirectiveMeta.set(SomeOtherDirective, new ComponentMetadata());
|
||||
expect(() =>
|
||||
{
|
||||
hostShadowElement(
|
||||
ListWrapper.concat([SomeOtherDirective, SimpleDirective], extraDirectives),
|
||||
[NeedsDirective]);
|
||||
})
|
||||
.toThrowError(containsRegexp(
|
||||
`No provider for ${stringify(SimpleDirective) }! (${stringify(NeedsDirective) } -> ${stringify(SimpleDirective) })`));
|
||||
});
|
||||
});
|
||||
|
||||
describe('static attributes', () => {
|
||||
it('should be injectable', () => {
|
||||
var el = appElement(null, ListWrapper.concat([NeedsAttribute], extraDirectives), null, null, {
|
||||
'type': 'text',
|
||||
'title': ''
|
||||
});
|
||||
var needsAttribute = el.get(NeedsAttribute);
|
||||
|
||||
expect(needsAttribute.typeAttribute).toEqual('text');
|
||||
expect(needsAttribute.titleAttribute).toEqual('');
|
||||
expect(needsAttribute.fooAttribute).toEqual(null);
|
||||
});
|
||||
|
||||
it('should be injectable without type annotation', () => {
|
||||
var el = appElement(null, ListWrapper.concat([NeedsAttributeNoType], extraDirectives), null,
|
||||
null, {'foo': 'bar'});
|
||||
var needsAttribute = el.get(NeedsAttributeNoType);
|
||||
|
||||
expect(needsAttribute.fooAttribute).toEqual('bar');
|
||||
});
|
||||
});
|
||||
|
||||
describe("refs", () => {
|
||||
it("should inject ElementRef", () => {
|
||||
var el = appElement(null, ListWrapper.concat([NeedsElementRef], extraDirectives));
|
||||
expect(el.get(NeedsElementRef).elementRef).toBe(el.ref);
|
||||
});
|
||||
|
||||
it("should inject ChangeDetectorRef of the component's view into the component via a proxy", () => {
|
||||
mockDirectiveMeta.set(ComponentNeedsChangeDetectorRef, new ComponentMetadata());
|
||||
var host = appElement(null, ListWrapper.concat([ComponentNeedsChangeDetectorRef], extraDirectives));
|
||||
var view = createView(ViewType.COMPONENT, host);
|
||||
host.attachComponentView(view);
|
||||
host.get(ComponentNeedsChangeDetectorRef).changeDetectorRef.markForCheck();
|
||||
expect((<any>view.changeDetector).spy('markPathToRootAsCheckOnce')).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should inject ChangeDetectorRef of the containing component into directives", () => {
|
||||
mockDirectiveMeta.set(DirectiveNeedsChangeDetectorRef, new DirectiveMetadata());
|
||||
var view = createView(ViewType.HOST);
|
||||
var el = appElement(null, ListWrapper.concat([DirectiveNeedsChangeDetectorRef], extraDirectives), view);
|
||||
expect(el.get(DirectiveNeedsChangeDetectorRef).changeDetectorRef).toBe(view.changeDetector.ref);
|
||||
});
|
||||
|
||||
it('should inject ViewContainerRef', () => {
|
||||
var el = appElement(null, ListWrapper.concat([NeedsViewContainer], extraDirectives));
|
||||
expect(el.get(NeedsViewContainer).viewContainer).toBeAnInstanceOf(ViewContainerRef_);
|
||||
});
|
||||
|
||||
it("should inject TemplateRef", () => {
|
||||
var el = appElement(null, ListWrapper.concat([NeedsTemplateRef], extraDirectives), null, dummyViewFactory);
|
||||
expect(el.get(NeedsTemplateRef).templateRef.elementRef).toBe(el.ref);
|
||||
});
|
||||
|
||||
it("should throw if there is no TemplateRef", () => {
|
||||
expect(() => appElement(null, ListWrapper.concat([NeedsTemplateRef], extraDirectives)))
|
||||
.toThrowError(
|
||||
`No provider for TemplateRef! (${stringify(NeedsTemplateRef) } -> TemplateRef)`);
|
||||
});
|
||||
|
||||
it('should inject null if there is no TemplateRef when the dependency is optional', () => {
|
||||
var el = appElement(null, ListWrapper.concat([OptionallyInjectsTemplateRef], extraDirectives));
|
||||
var instance = el.get(OptionallyInjectsTemplateRef);
|
||||
expect(instance.templateRef).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('queries', () => {
|
||||
function expectDirectives(query: QueryList<any>, type, expectedIndex) {
|
||||
var currentCount = 0;
|
||||
expect(query.length).toEqual(expectedIndex.length);
|
||||
iterateListLike(query, (i) => {
|
||||
expect(i).toBeAnInstanceOf(type);
|
||||
expect(i.count).toBe(expectedIndex[currentCount]);
|
||||
currentCount += 1;
|
||||
});
|
||||
}
|
||||
|
||||
it('should be injectable', () => {
|
||||
var el =
|
||||
appElement(null, ListWrapper.concat([NeedsQuery], extraDirectives));
|
||||
expect(el.get(NeedsQuery).query).toBeAnInstanceOf(QueryList);
|
||||
});
|
||||
|
||||
it('should contain directives on the same injector', () => {
|
||||
var el = appElement(null, ListWrapper.concat([
|
||||
NeedsQuery,
|
||||
CountingDirective
|
||||
], extraDirectives));
|
||||
|
||||
el.ngAfterContentChecked();
|
||||
|
||||
expectDirectives(el.get(NeedsQuery).query, CountingDirective, [0]);
|
||||
});
|
||||
|
||||
it('should contain TemplateRefs on the same injector', () => {
|
||||
var el = appElement(null, ListWrapper.concat([
|
||||
NeedsTemplateRefQuery
|
||||
], extraDirectives), null, dummyViewFactory);
|
||||
|
||||
el.ngAfterContentChecked();
|
||||
|
||||
expect(el.get(NeedsTemplateRefQuery).query.first).toBeAnInstanceOf(TemplateRef_);
|
||||
});
|
||||
|
||||
it('should contain the element when no directives are bound to the var provider', () => {
|
||||
var dirs:Type[] = [NeedsQueryByVarBindings];
|
||||
|
||||
var dirVariableBindings:{[key:string]:number} = {
|
||||
"one": null // element
|
||||
};
|
||||
|
||||
var el = appElement(null, dirs.concat(extraDirectives), null, null, null, dirVariableBindings);
|
||||
|
||||
el.ngAfterContentChecked();
|
||||
|
||||
expect(el.get(NeedsQueryByVarBindings).query.first).toBe(el.ref);
|
||||
});
|
||||
|
||||
it('should contain directives on the same injector when querying by variable providers' +
|
||||
'in the order of var providers specified in the query', () => {
|
||||
var dirs:Type[] = [NeedsQueryByVarBindings, NeedsDirective, SimpleDirective];
|
||||
|
||||
var dirVariableBindings:{[key:string]:number} = {
|
||||
"one": 2, // 2 is the index of SimpleDirective
|
||||
"two": 1 // 1 is the index of NeedsDirective
|
||||
};
|
||||
|
||||
var el = appElement(null, dirs.concat(extraDirectives), null, null, null, dirVariableBindings);
|
||||
|
||||
el.ngAfterContentChecked();
|
||||
|
||||
// NeedsQueryByVarBindings queries "one,two", so SimpleDirective should be before NeedsDirective
|
||||
expect(el.get(NeedsQueryByVarBindings).query.first).toBeAnInstanceOf(SimpleDirective);
|
||||
expect(el.get(NeedsQueryByVarBindings).query.last).toBeAnInstanceOf(NeedsDirective);
|
||||
});
|
||||
|
||||
it('should contain directives on the same and a child injector in construction order', () => {
|
||||
var parent = appElement(null, [NeedsQuery, CountingDirective]);
|
||||
appElement(parent, ListWrapper.concat([CountingDirective], extraDirectives));
|
||||
|
||||
parent.ngAfterContentChecked();
|
||||
|
||||
expectDirectives(parent.get(NeedsQuery).query, CountingDirective, [0, 1]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class ContextWithHandler {
|
||||
handler;
|
||||
constructor(handler) { this.handler = handler; }
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
import {EventConfig} from 'angular2/src/core/linker/event_config';
|
||||
import {ddescribe, describe, expect, it} from 'angular2/testing_internal';
|
||||
|
||||
export function main() {
|
||||
describe('EventConfig', () => {
|
||||
describe('parse', () => {
|
||||
it('should handle short form events', () => {
|
||||
var eventConfig = EventConfig.parse('shortForm');
|
||||
expect(eventConfig.fieldName).toEqual('shortForm');
|
||||
expect(eventConfig.eventName).toEqual('shortForm');
|
||||
expect(eventConfig.isLongForm).toEqual(false);
|
||||
});
|
||||
it('should handle long form events', () => {
|
||||
var eventConfig = EventConfig.parse('fieldName: eventName');
|
||||
expect(eventConfig.fieldName).toEqual('fieldName');
|
||||
expect(eventConfig.eventName).toEqual('eventName');
|
||||
expect(eventConfig.isLongForm).toEqual(true);
|
||||
});
|
||||
});
|
||||
describe('getFullName', () => {
|
||||
it('should handle short form events', () => {
|
||||
var eventConfig = new EventConfig('shortForm', 'shortForm', false);
|
||||
expect(eventConfig.getFullName()).toEqual('shortForm');
|
||||
});
|
||||
it('should handle long form events', () => {
|
||||
var eventConfig = new EventConfig('fieldName', 'eventName', true);
|
||||
expect(eventConfig.getFullName()).toEqual('fieldName:eventName');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -86,18 +86,40 @@ import {
|
||||
import {QueryList} from 'angular2/src/core/linker/query_list';
|
||||
|
||||
import {ViewContainerRef} from 'angular2/src/core/linker/view_container_ref';
|
||||
import {ViewRef, ViewRef_} from 'angular2/src/core/linker/view_ref';
|
||||
import {EmbeddedViewRef} from 'angular2/src/core/linker/view_ref';
|
||||
|
||||
import {Compiler} from 'angular2/src/core/linker/compiler';
|
||||
import {ElementRef, ElementRef_} from 'angular2/src/core/linker/element_ref';
|
||||
import {ElementRef} from 'angular2/src/core/linker/element_ref';
|
||||
import {TemplateRef} from 'angular2/src/core/linker/template_ref';
|
||||
|
||||
import {DomRenderer} from 'angular2/src/platform/dom/dom_renderer';
|
||||
import {Renderer} from 'angular2/src/core/render';
|
||||
import {IS_DART} from 'angular2/src/facade/lang';
|
||||
|
||||
const ANCHOR_ELEMENT = CONST_EXPR(new OpaqueToken('AnchorElement'));
|
||||
|
||||
export function main() {
|
||||
if (IS_DART) {
|
||||
declareTests();
|
||||
} else {
|
||||
describe('no jit', () => {
|
||||
beforeEachProviders(() => [
|
||||
provide(ChangeDetectorGenConfig,
|
||||
{useValue: new ChangeDetectorGenConfig(true, false, false)})
|
||||
]);
|
||||
declareTests();
|
||||
});
|
||||
|
||||
describe('jit', () => {
|
||||
beforeEachProviders(() => [
|
||||
provide(ChangeDetectorGenConfig,
|
||||
{useValue: new ChangeDetectorGenConfig(true, false, true)})
|
||||
]);
|
||||
declareTests();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function declareTests() {
|
||||
describe('integration tests', function() {
|
||||
|
||||
beforeEachProviders(() => [provide(ANCHOR_ELEMENT, {useValue: el('<div></div>')})]);
|
||||
@@ -151,7 +173,6 @@ export function main() {
|
||||
|
||||
.createAsync(MyComp)
|
||||
.then((fixture) => {
|
||||
|
||||
fixture.debugElement.componentInstance.ctxProp = 'Initial aria label';
|
||||
fixture.detectChanges();
|
||||
expect(
|
||||
@@ -306,17 +327,16 @@ export function main() {
|
||||
|
||||
it('should consume directive watch expression change.',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var tpl = '<div>' +
|
||||
var tpl = '<span>' +
|
||||
'<div my-dir [elprop]="ctxProp"></div>' +
|
||||
'<div my-dir elprop="Hi there!"></div>' +
|
||||
'<div my-dir elprop="Hi {{\'there!\'}}"></div>' +
|
||||
'<div my-dir elprop="One more {{ctxProp}}"></div>' +
|
||||
'</div>';
|
||||
'</span>';
|
||||
tcb.overrideView(MyComp, new ViewMetadata({template: tpl, directives: [MyDir]}))
|
||||
|
||||
.createAsync(MyComp)
|
||||
.then((fixture) => {
|
||||
|
||||
fixture.debugElement.componentInstance.ctxProp = 'Hello World!';
|
||||
fixture.detectChanges();
|
||||
|
||||
@@ -677,7 +697,6 @@ export function main() {
|
||||
.createAsync(MyComp)
|
||||
.then((fixture) => {
|
||||
fixture.detectChanges();
|
||||
|
||||
// Get the element at index 2, since index 0 is the <template>.
|
||||
expect(DOM.childNodes(fixture.debugElement.nativeElement)[2])
|
||||
.toHaveText("1-hello");
|
||||
@@ -1085,6 +1104,9 @@ export function main() {
|
||||
dispatchEvent(DOM.getGlobalEventTarget("window"), 'domEvent');
|
||||
expect(globalCounter).toEqual(2);
|
||||
|
||||
// need to destroy to release all remaining global event listeners
|
||||
fixture.destroy();
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
@@ -1850,8 +1872,8 @@ class MyService {
|
||||
class SimpleImperativeViewComponent {
|
||||
done;
|
||||
|
||||
constructor(self: ElementRef, renderer: DomRenderer) {
|
||||
var hostElement = renderer.getNativeElementSync(self);
|
||||
constructor(self: ElementRef, renderer: Renderer) {
|
||||
var hostElement = self.nativeElement;
|
||||
DOM.appendChild(hostElement, el('hello imp view'));
|
||||
}
|
||||
}
|
||||
@@ -2332,10 +2354,10 @@ class ChildConsumingEventBus {
|
||||
@Directive({selector: '[someImpvp]', inputs: ['someImpvp']})
|
||||
@Injectable()
|
||||
class SomeImperativeViewport {
|
||||
view: ViewRef;
|
||||
view: EmbeddedViewRef;
|
||||
anchor;
|
||||
constructor(public vc: ViewContainerRef, public templateRef: TemplateRef,
|
||||
public renderer: DomRenderer, @Inject(ANCHOR_ELEMENT) anchor) {
|
||||
@Inject(ANCHOR_ELEMENT) anchor) {
|
||||
this.view = null;
|
||||
this.anchor = anchor;
|
||||
}
|
||||
@@ -2347,7 +2369,7 @@ class SomeImperativeViewport {
|
||||
}
|
||||
if (value) {
|
||||
this.view = this.vc.createEmbeddedView(this.templateRef);
|
||||
var nodes = this.renderer.getRootNodes((<ViewRef_>this.view).renderFragment);
|
||||
var nodes = this.view.rootNodes;
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
DOM.appendChild(this.anchor, nodes[i]);
|
||||
}
|
||||
|
||||
@@ -34,9 +34,12 @@ import {
|
||||
View,
|
||||
ViewContainerRef,
|
||||
ViewEncapsulation,
|
||||
ViewMetadata
|
||||
ViewMetadata,
|
||||
Scope
|
||||
} from 'angular2/core';
|
||||
import {By} from 'angular2/platform/common_dom';
|
||||
import {
|
||||
By,
|
||||
} from 'angular2/platform/common_dom';
|
||||
|
||||
export function main() {
|
||||
describe('projection', () => {
|
||||
@@ -439,6 +442,7 @@ export function main() {
|
||||
var childNodes = DOM.childNodes(main.debugElement.nativeElement);
|
||||
expect(childNodes[0]).toHaveText('div {color: red}SIMPLE1(A)');
|
||||
expect(childNodes[1]).toHaveText('div {color: blue}SIMPLE2(B)');
|
||||
main.destroy();
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
@@ -521,6 +525,47 @@ export function main() {
|
||||
});
|
||||
}));
|
||||
|
||||
it('should project filled view containers into a view container',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MainComp, new ViewMetadata({
|
||||
template: '<conditional-content>' +
|
||||
'<div class="left">A</div>' +
|
||||
'<template manual class="left">B</template>' +
|
||||
'<div class="left">C</div>' +
|
||||
'<div>D</div>' +
|
||||
'</conditional-content>',
|
||||
directives: [ConditionalContentComponent, ManualViewportDirective]
|
||||
}))
|
||||
.createAsync(MainComp)
|
||||
.then((main) => {
|
||||
var conditionalComp =
|
||||
main.debugElement.query(By.directive(ConditionalContentComponent));
|
||||
var viewViewportDir =
|
||||
conditionalComp.query(By.directive(ManualViewportDirective), Scope.view)
|
||||
.inject(ManualViewportDirective);
|
||||
|
||||
var contentViewportDir =
|
||||
conditionalComp.query(By.directive(ManualViewportDirective), Scope.light)
|
||||
.inject(ManualViewportDirective);
|
||||
|
||||
expect(main.debugElement.nativeElement).toHaveText('(, D)');
|
||||
expect(main.debugElement.nativeElement).toHaveText('(, D)');
|
||||
// first show content viewport, then the view viewport,
|
||||
// i.e. projection needs to take create of already
|
||||
// created views
|
||||
contentViewportDir.show();
|
||||
viewViewportDir.show();
|
||||
expect(main.debugElement.nativeElement).toHaveText('(ABC, D)');
|
||||
|
||||
// hide view viewport, and test that it also hides
|
||||
// the content viewport's views
|
||||
viewViewportDir.hide();
|
||||
expect(main.debugElement.nativeElement).toHaveText('(, D)');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
xdescribe,
|
||||
ddescribe,
|
||||
describe,
|
||||
el,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it
|
||||
} from 'angular2/testing_internal';
|
||||
|
||||
export function main() {
|
||||
describe('ProtoViewFactory', () => {
|
||||
// TODO
|
||||
|
||||
});
|
||||
}
|
||||
@@ -35,7 +35,7 @@ import {
|
||||
AfterViewChecked
|
||||
} from 'angular2/core';
|
||||
import {NgIf, NgFor} from 'angular2/common';
|
||||
import {asNativeElements} from 'angular2/core';
|
||||
import {asNativeElements, ViewContainerRef} from 'angular2/core';
|
||||
|
||||
export function main() {
|
||||
describe('Query API', () => {
|
||||
@@ -99,6 +99,8 @@ export function main() {
|
||||
view.debugElement.componentInstance.shouldShow = false;
|
||||
view.detectChanges();
|
||||
|
||||
// TODO: this fails right now!
|
||||
// -> queries are not dirtied!
|
||||
expect(q.log).toEqual([
|
||||
["setter", "foo"],
|
||||
["init", "foo"],
|
||||
@@ -250,8 +252,11 @@ export function main() {
|
||||
view.detectChanges();
|
||||
var needsTpl: NeedsTpl =
|
||||
view.debugElement.componentViewChildren[0].inject(NeedsTpl);
|
||||
expect(needsTpl.query.first.hasLocal('light')).toBe(true);
|
||||
expect(needsTpl.viewQuery.first.hasLocal('shadow')).toBe(true);
|
||||
|
||||
expect(needsTpl.vc.createEmbeddedView(needsTpl.query.first).hasLocal('light'))
|
||||
.toBe(true);
|
||||
expect(needsTpl.vc.createEmbeddedView(needsTpl.viewQuery.first).hasLocal('shadow'))
|
||||
.toBe(true);
|
||||
|
||||
async.done();
|
||||
});
|
||||
@@ -892,7 +897,7 @@ class NeedsTpl {
|
||||
viewQuery: QueryList<TemplateRef>;
|
||||
query: QueryList<TemplateRef>;
|
||||
constructor(@ViewQuery(TemplateRef) viewQuery: QueryList<TemplateRef>,
|
||||
@Query(TemplateRef) query: QueryList<TemplateRef>) {
|
||||
@Query(TemplateRef) query: QueryList<TemplateRef>, public vc: ViewContainerRef) {
|
||||
this.viewQuery = viewQuery;
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
xdescribe,
|
||||
describe,
|
||||
el,
|
||||
dispatchEvent,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
beforeEachProviders,
|
||||
it,
|
||||
xit
|
||||
} from 'angular2/testing_internal';
|
||||
|
||||
import {SpyView, SpyAppViewManager} from '../spies';
|
||||
import {AppView, AppViewContainer} from 'angular2/src/core/linker/view';
|
||||
import {ViewContainerRef, ViewContainerRef_} from 'angular2/src/core/linker/view_container_ref';
|
||||
import {ElementRef, ElementRef_} from 'angular2/src/core/linker/element_ref';
|
||||
import {ViewRef, ViewRef_} from 'angular2/src/core/linker/view_ref';
|
||||
|
||||
export function main() {
|
||||
// TODO(tbosch): add missing tests
|
||||
|
||||
describe('ViewContainerRef', () => {
|
||||
var location;
|
||||
var view;
|
||||
var viewManager;
|
||||
|
||||
function createViewContainer() { return new ViewContainerRef_(viewManager, location); }
|
||||
|
||||
beforeEach(() => {
|
||||
viewManager = new SpyAppViewManager();
|
||||
view = new SpyView();
|
||||
view.prop("viewContainers", [null]);
|
||||
location = new ElementRef_(new ViewRef_(view), 0, null);
|
||||
});
|
||||
|
||||
describe('length', () => {
|
||||
|
||||
it('should return a 0 length if there is no underlying AppViewContainer', () => {
|
||||
var vc = createViewContainer();
|
||||
expect(vc.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should return the size of the underlying AppViewContainer', () => {
|
||||
var vc = createViewContainer();
|
||||
var appVc = new AppViewContainer();
|
||||
view.prop("viewContainers", [appVc]);
|
||||
appVc.views = [<any>new SpyView()];
|
||||
expect(vc.length).toBe(1);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// TODO: add missing tests here!
|
||||
|
||||
});
|
||||
}
|
||||
@@ -1,520 +0,0 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
xdescribe,
|
||||
describe,
|
||||
el,
|
||||
dispatchEvent,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
beforeEachProviders,
|
||||
it,
|
||||
xit
|
||||
} from 'angular2/testing_internal';
|
||||
import {SpyRenderer, SpyAppViewPool, SpyAppViewListener, SpyProtoViewFactory} from '../spies';
|
||||
import {Injector, provide} from 'angular2/core';
|
||||
|
||||
import {AppProtoView, AppView, AppViewContainer, ViewType} from 'angular2/src/core/linker/view';
|
||||
import {
|
||||
ProtoViewRef,
|
||||
ProtoViewRef_,
|
||||
ViewRef,
|
||||
ViewRef_,
|
||||
internalView
|
||||
} from 'angular2/src/core/linker/view_ref';
|
||||
import {ElementRef} from 'angular2/src/core/linker/element_ref';
|
||||
import {TemplateRef, TemplateRef_} from 'angular2/src/core/linker/template_ref';
|
||||
import {
|
||||
Renderer,
|
||||
RenderViewRef,
|
||||
RenderProtoViewRef,
|
||||
RenderFragmentRef,
|
||||
RenderViewWithFragments
|
||||
} from 'angular2/src/core/render/api';
|
||||
import {AppViewManager, AppViewManager_} from 'angular2/src/core/linker/view_manager';
|
||||
import {AppViewManagerUtils} from 'angular2/src/core/linker/view_manager_utils';
|
||||
|
||||
import {
|
||||
createHostPv,
|
||||
createComponentPv,
|
||||
createEmbeddedPv,
|
||||
createEmptyElBinder,
|
||||
createNestedElBinder,
|
||||
createProtoElInjector
|
||||
} from './view_manager_utils_spec';
|
||||
|
||||
export function main() {
|
||||
// TODO(tbosch): add missing tests
|
||||
|
||||
describe('AppViewManager', () => {
|
||||
var renderer;
|
||||
var utils: AppViewManagerUtils;
|
||||
var viewListener;
|
||||
var viewPool;
|
||||
var linker;
|
||||
var manager: AppViewManager;
|
||||
var createdRenderViews: RenderViewWithFragments[];
|
||||
|
||||
function wrapPv(protoView: AppProtoView): ProtoViewRef { return new ProtoViewRef_(protoView); }
|
||||
|
||||
function wrapView(view: AppView): ViewRef { return new ViewRef_(view); }
|
||||
|
||||
function resetSpies() {
|
||||
viewListener.spy('onViewCreated').reset();
|
||||
viewListener.spy('onViewDestroyed').reset();
|
||||
renderer.spy('createView').reset();
|
||||
renderer.spy('destroyView').reset();
|
||||
renderer.spy('createRootHostView').reset();
|
||||
renderer.spy('setEventDispatcher').reset();
|
||||
renderer.spy('hydrateView').reset();
|
||||
renderer.spy('dehydrateView').reset();
|
||||
viewPool.spy('returnView').reset();
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
renderer = new SpyRenderer();
|
||||
utils = new AppViewManagerUtils();
|
||||
viewListener = new SpyAppViewListener();
|
||||
viewPool = new SpyAppViewPool();
|
||||
linker = new SpyProtoViewFactory();
|
||||
manager = new AppViewManager_(viewPool, viewListener, utils, renderer, linker);
|
||||
createdRenderViews = [];
|
||||
|
||||
renderer.spy('createRootHostView')
|
||||
.andCallFake((_a, renderFragmentCount, _b) => {
|
||||
var fragments = [];
|
||||
for (var i = 0; i < renderFragmentCount; i++) {
|
||||
fragments.push(new RenderFragmentRef());
|
||||
}
|
||||
var rv = new RenderViewWithFragments(new RenderViewRef(), fragments);
|
||||
createdRenderViews.push(rv);
|
||||
return rv;
|
||||
});
|
||||
renderer.spy('createView')
|
||||
.andCallFake((_a, renderFragmentCount) => {
|
||||
var fragments = [];
|
||||
for (var i = 0; i < renderFragmentCount; i++) {
|
||||
fragments.push(new RenderFragmentRef());
|
||||
}
|
||||
var rv = new RenderViewWithFragments(new RenderViewRef(), fragments);
|
||||
createdRenderViews.push(rv);
|
||||
return rv;
|
||||
});
|
||||
viewPool.spy('returnView').andReturn(true);
|
||||
});
|
||||
|
||||
describe('createRootHostView', () => {
|
||||
|
||||
var hostProtoView: AppProtoView;
|
||||
beforeEach(
|
||||
() => { hostProtoView = createHostPv([createNestedElBinder(createComponentPv())]); });
|
||||
|
||||
it('should initialize the ProtoView', () => {
|
||||
manager.createRootHostView(wrapPv(hostProtoView), null, null);
|
||||
expect(linker.spy('initializeProtoViewIfNeeded')).toHaveBeenCalledWith(hostProtoView);
|
||||
});
|
||||
|
||||
it('should create the view', () => {
|
||||
var rootView =
|
||||
internalView(<ViewRef>manager.createRootHostView(wrapPv(hostProtoView), null, null));
|
||||
expect(rootView.proto).toBe(hostProtoView);
|
||||
expect(viewListener.spy('onViewCreated')).toHaveBeenCalledWith(rootView);
|
||||
});
|
||||
|
||||
it('should hydrate the view', () => {
|
||||
var injector = Injector.resolveAndCreate([]);
|
||||
var rootView = internalView(
|
||||
<ViewRef>manager.createRootHostView(wrapPv(hostProtoView), null, injector));
|
||||
expect(rootView.hydrated()).toBe(true);
|
||||
expect(renderer.spy('hydrateView')).toHaveBeenCalledWith(rootView.render);
|
||||
});
|
||||
|
||||
it('should create and set the render view using the component selector', () => {
|
||||
var rootView =
|
||||
internalView(<ViewRef>manager.createRootHostView(wrapPv(hostProtoView), null, null));
|
||||
expect(renderer.spy('createRootHostView'))
|
||||
.toHaveBeenCalledWith(hostProtoView.render,
|
||||
hostProtoView.mergeInfo.embeddedViewCount + 1, 'someComponent');
|
||||
expect(rootView.render).toBe(createdRenderViews[0].viewRef);
|
||||
expect(rootView.renderFragment).toBe(createdRenderViews[0].fragmentRefs[0]);
|
||||
});
|
||||
|
||||
it('should allow to override the selector', () => {
|
||||
var selector = 'someOtherSelector';
|
||||
internalView(<ViewRef>manager.createRootHostView(wrapPv(hostProtoView), selector, null));
|
||||
expect(renderer.spy('createRootHostView'))
|
||||
.toHaveBeenCalledWith(hostProtoView.render,
|
||||
hostProtoView.mergeInfo.embeddedViewCount + 1, selector);
|
||||
});
|
||||
|
||||
it('should set the event dispatcher', () => {
|
||||
var rootView =
|
||||
internalView(<ViewRef>manager.createRootHostView(wrapPv(hostProtoView), null, null));
|
||||
expect(renderer.spy('setEventDispatcher')).toHaveBeenCalledWith(rootView.render, rootView);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('destroyRootHostView', () => {
|
||||
var hostProtoView: AppProtoView;
|
||||
var hostView: AppView;
|
||||
var hostRenderViewRef: RenderViewRef;
|
||||
beforeEach(() => {
|
||||
hostProtoView = createHostPv([createNestedElBinder(createComponentPv())]);
|
||||
hostView =
|
||||
internalView(<ViewRef>manager.createRootHostView(wrapPv(hostProtoView), null, null));
|
||||
hostRenderViewRef = hostView.render;
|
||||
});
|
||||
|
||||
it('should dehydrate', () => {
|
||||
manager.destroyRootHostView(wrapView(hostView));
|
||||
expect(hostView.hydrated()).toBe(false);
|
||||
expect(renderer.spy('dehydrateView')).toHaveBeenCalledWith(hostView.render);
|
||||
});
|
||||
|
||||
it('should destroy the render view', () => {
|
||||
manager.destroyRootHostView(wrapView(hostView));
|
||||
expect(renderer.spy('destroyView')).toHaveBeenCalledWith(hostRenderViewRef);
|
||||
expect(viewListener.spy('onViewDestroyed')).toHaveBeenCalledWith(hostView);
|
||||
});
|
||||
|
||||
it('should not return the view to the pool', () => {
|
||||
manager.destroyRootHostView(wrapView(hostView));
|
||||
expect(viewPool.spy('returnView')).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('createEmbeddedViewInContainer', () => {
|
||||
|
||||
describe('basic functionality', () => {
|
||||
var hostView: AppView;
|
||||
var childProtoView: AppProtoView;
|
||||
var vcRef: ElementRef;
|
||||
var templateRef: TemplateRef;
|
||||
beforeEach(() => {
|
||||
childProtoView = createEmbeddedPv();
|
||||
var hostProtoView = createHostPv(
|
||||
[createNestedElBinder(createComponentPv([createNestedElBinder(childProtoView)]))]);
|
||||
hostView =
|
||||
internalView(<ViewRef>manager.createRootHostView(wrapPv(hostProtoView), null, null));
|
||||
vcRef = hostView.elementRefs[1];
|
||||
templateRef = new TemplateRef_(hostView.elementRefs[1]);
|
||||
resetSpies();
|
||||
});
|
||||
|
||||
it('should initialize the ProtoView', () => {
|
||||
manager.createEmbeddedViewInContainer(vcRef, 0, templateRef);
|
||||
expect(linker.spy('initializeProtoViewIfNeeded')).toHaveBeenCalledWith(childProtoView);
|
||||
});
|
||||
|
||||
describe('create the first view', () => {
|
||||
|
||||
it('should create an AppViewContainer if not yet existing', () => {
|
||||
manager.createEmbeddedViewInContainer(vcRef, 0, templateRef);
|
||||
expect(hostView.viewContainers[1]).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should use an existing nested view', () => {
|
||||
var childView =
|
||||
internalView(manager.createEmbeddedViewInContainer(vcRef, 0, templateRef));
|
||||
expect(childView.proto).toBe(childProtoView);
|
||||
expect(childView).toBe(hostView.views[2]);
|
||||
expect(viewListener.spy('onViewCreated')).not.toHaveBeenCalled();
|
||||
expect(renderer.spy('createView')).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should attach the fragment', () => {
|
||||
var childView =
|
||||
internalView(manager.createEmbeddedViewInContainer(vcRef, 0, templateRef));
|
||||
expect(childView.proto).toBe(childProtoView);
|
||||
expect(hostView.viewContainers[1].views.length).toBe(1);
|
||||
expect(hostView.viewContainers[1].views[0]).toBe(childView);
|
||||
expect(renderer.spy('attachFragmentAfterElement'))
|
||||
.toHaveBeenCalledWith(vcRef, childView.renderFragment);
|
||||
});
|
||||
|
||||
it('should hydrate the view but not the render view', () => {
|
||||
var childView =
|
||||
internalView(manager.createEmbeddedViewInContainer(vcRef, 0, templateRef));
|
||||
expect(childView.hydrated()).toBe(true);
|
||||
expect(renderer.spy('hydrateView')).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not set the EventDispatcher', () => {
|
||||
internalView(manager.createEmbeddedViewInContainer(vcRef, 0, templateRef));
|
||||
expect(renderer.spy('setEventDispatcher')).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('create the second view', () => {
|
||||
var firstChildView;
|
||||
beforeEach(() => {
|
||||
firstChildView =
|
||||
internalView(manager.createEmbeddedViewInContainer(vcRef, 0, templateRef));
|
||||
resetSpies();
|
||||
});
|
||||
|
||||
it('should create a new view', () => {
|
||||
var childView =
|
||||
internalView(manager.createEmbeddedViewInContainer(vcRef, 1, templateRef));
|
||||
expect(childView.proto).toBe(childProtoView);
|
||||
expect(childView).not.toBe(firstChildView);
|
||||
expect(viewListener.spy('onViewCreated')).toHaveBeenCalledWith(childView);
|
||||
expect(renderer.spy('createView'))
|
||||
.toHaveBeenCalledWith(childProtoView.render,
|
||||
childProtoView.mergeInfo.embeddedViewCount + 1);
|
||||
expect(childView.render).toBe(createdRenderViews[1].viewRef);
|
||||
expect(childView.renderFragment).toBe(createdRenderViews[1].fragmentRefs[0]);
|
||||
});
|
||||
|
||||
it('should attach the fragment', () => {
|
||||
var childView =
|
||||
internalView(manager.createEmbeddedViewInContainer(vcRef, 1, templateRef));
|
||||
expect(childView.proto).toBe(childProtoView);
|
||||
expect(hostView.viewContainers[1].views[1]).toBe(childView);
|
||||
expect(renderer.spy('attachFragmentAfterFragment'))
|
||||
.toHaveBeenCalledWith(firstChildView.renderFragment, childView.renderFragment);
|
||||
});
|
||||
|
||||
it('should hydrate the view', () => {
|
||||
var childView =
|
||||
internalView(manager.createEmbeddedViewInContainer(vcRef, 1, templateRef));
|
||||
expect(childView.hydrated()).toBe(true);
|
||||
expect(renderer.spy('hydrateView')).toHaveBeenCalledWith(childView.render);
|
||||
});
|
||||
|
||||
it('should set the EventDispatcher', () => {
|
||||
var childView =
|
||||
internalView(manager.createEmbeddedViewInContainer(vcRef, 1, templateRef));
|
||||
expect(renderer.spy('setEventDispatcher'))
|
||||
.toHaveBeenCalledWith(childView.render, childView);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('create another view when the first view has been returned', () => {
|
||||
beforeEach(() => {
|
||||
internalView(manager.createEmbeddedViewInContainer(vcRef, 0, templateRef));
|
||||
manager.destroyViewInContainer(vcRef, 0);
|
||||
resetSpies();
|
||||
});
|
||||
|
||||
it('should use an existing nested view', () => {
|
||||
var childView =
|
||||
internalView(manager.createEmbeddedViewInContainer(vcRef, 0, templateRef));
|
||||
expect(childView.proto).toBe(childProtoView);
|
||||
expect(childView).toBe(hostView.views[2]);
|
||||
expect(viewListener.spy('onViewCreated')).not.toHaveBeenCalled();
|
||||
expect(renderer.spy('createView')).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('create a host view', () => {
|
||||
|
||||
it('should initialize the ProtoView', () => {
|
||||
var newHostPv = createHostPv([createNestedElBinder(createComponentPv())]);
|
||||
manager.createHostViewInContainer(vcRef, 0, wrapPv(newHostPv), null);
|
||||
expect(linker.spy('initializeProtoViewIfNeeded')).toHaveBeenCalledWith(newHostPv);
|
||||
});
|
||||
|
||||
it('should always create a new view and not use the embedded view', () => {
|
||||
var newHostPv = createHostPv([createNestedElBinder(createComponentPv())]);
|
||||
var newHostView = internalView(
|
||||
<ViewRef>manager.createHostViewInContainer(vcRef, 0, wrapPv(newHostPv), null));
|
||||
expect(newHostView.proto).toBe(newHostPv);
|
||||
expect(newHostView).not.toBe(hostView.views[2]);
|
||||
expect(viewListener.spy('onViewCreated')).toHaveBeenCalledWith(newHostView);
|
||||
expect(renderer.spy('createView'))
|
||||
.toHaveBeenCalledWith(newHostPv.render, newHostPv.mergeInfo.embeddedViewCount + 1);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('destroyViewInContainer', () => {
|
||||
|
||||
describe('basic functionality', () => {
|
||||
var hostView: AppView;
|
||||
var childProtoView: AppProtoView;
|
||||
var vcRef: ElementRef;
|
||||
var templateRef: TemplateRef;
|
||||
var firstChildView: AppView;
|
||||
beforeEach(() => {
|
||||
childProtoView = createEmbeddedPv();
|
||||
var hostProtoView = createHostPv(
|
||||
[createNestedElBinder(createComponentPv([createNestedElBinder(childProtoView)]))]);
|
||||
hostView =
|
||||
internalView(<ViewRef>manager.createRootHostView(wrapPv(hostProtoView), null, null));
|
||||
vcRef = hostView.elementRefs[1];
|
||||
templateRef = new TemplateRef_(hostView.elementRefs[1]);
|
||||
firstChildView =
|
||||
internalView(manager.createEmbeddedViewInContainer(vcRef, 0, templateRef));
|
||||
resetSpies();
|
||||
});
|
||||
|
||||
describe('destroy the first view', () => {
|
||||
it('should dehydrate the app view but not the render view', () => {
|
||||
manager.destroyViewInContainer(vcRef, 0);
|
||||
expect(firstChildView.hydrated()).toBe(false);
|
||||
expect(renderer.spy('dehydrateView')).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should detach', () => {
|
||||
manager.destroyViewInContainer(vcRef, 0);
|
||||
expect(hostView.viewContainers[1].views).toEqual([]);
|
||||
expect(renderer.spy('detachFragment'))
|
||||
.toHaveBeenCalledWith(firstChildView.renderFragment);
|
||||
});
|
||||
|
||||
it('should not return the view to the pool', () => {
|
||||
manager.destroyViewInContainer(vcRef, 0);
|
||||
expect(viewPool.spy('returnView')).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('destroy another view', () => {
|
||||
var secondChildView;
|
||||
beforeEach(() => {
|
||||
secondChildView =
|
||||
internalView(manager.createEmbeddedViewInContainer(vcRef, 1, templateRef));
|
||||
resetSpies();
|
||||
});
|
||||
|
||||
it('should dehydrate', () => {
|
||||
manager.destroyViewInContainer(vcRef, 1);
|
||||
expect(secondChildView.hydrated()).toBe(false);
|
||||
expect(renderer.spy('dehydrateView')).toHaveBeenCalledWith(secondChildView.render);
|
||||
});
|
||||
|
||||
it('should detach', () => {
|
||||
manager.destroyViewInContainer(vcRef, 1);
|
||||
expect(hostView.viewContainers[1].views[0]).toBe(firstChildView);
|
||||
expect(renderer.spy('detachFragment'))
|
||||
.toHaveBeenCalledWith(secondChildView.renderFragment);
|
||||
});
|
||||
|
||||
it('should return the view to the pool', () => {
|
||||
manager.destroyViewInContainer(vcRef, 1);
|
||||
expect(viewPool.spy('returnView')).toHaveBeenCalledWith(secondChildView);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('recursively destroy views in ViewContainers', () => {
|
||||
|
||||
describe('destroy child views when a component is destroyed', () => {
|
||||
var hostView: AppView;
|
||||
var childProtoView: AppProtoView;
|
||||
var vcRef: ElementRef;
|
||||
var templateRef: TemplateRef;
|
||||
var firstChildView: AppView;
|
||||
var secondChildView: AppView;
|
||||
beforeEach(() => {
|
||||
childProtoView = createEmbeddedPv();
|
||||
var hostProtoView = createHostPv(
|
||||
[createNestedElBinder(createComponentPv([createNestedElBinder(childProtoView)]))]);
|
||||
hostView = internalView(
|
||||
<ViewRef>manager.createRootHostView(wrapPv(hostProtoView), null, null));
|
||||
vcRef = hostView.elementRefs[1];
|
||||
templateRef = new TemplateRef_(hostView.elementRefs[1]);
|
||||
firstChildView =
|
||||
internalView(manager.createEmbeddedViewInContainer(vcRef, 0, templateRef));
|
||||
secondChildView =
|
||||
internalView(manager.createEmbeddedViewInContainer(vcRef, 1, templateRef));
|
||||
resetSpies();
|
||||
});
|
||||
|
||||
it('should dehydrate', () => {
|
||||
manager.destroyRootHostView(wrapView(hostView));
|
||||
expect(firstChildView.hydrated()).toBe(false);
|
||||
expect(secondChildView.hydrated()).toBe(false);
|
||||
expect(renderer.spy('dehydrateView')).toHaveBeenCalledWith(hostView.render);
|
||||
expect(renderer.spy('dehydrateView')).toHaveBeenCalledWith(secondChildView.render);
|
||||
});
|
||||
|
||||
it('should detach', () => {
|
||||
manager.destroyRootHostView(wrapView(hostView));
|
||||
expect(hostView.viewContainers[1].views).toEqual([]);
|
||||
expect(renderer.spy('detachFragment'))
|
||||
.toHaveBeenCalledWith(firstChildView.renderFragment);
|
||||
expect(renderer.spy('detachFragment'))
|
||||
.toHaveBeenCalledWith(secondChildView.renderFragment);
|
||||
});
|
||||
|
||||
it('should return the view to the pool', () => {
|
||||
manager.destroyRootHostView(wrapView(hostView));
|
||||
expect(viewPool.spy('returnView')).not.toHaveBeenCalledWith(firstChildView);
|
||||
expect(viewPool.spy('returnView')).toHaveBeenCalledWith(secondChildView);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('destroy child views over multiple levels', () => {
|
||||
var hostView: AppView;
|
||||
var childProtoView: AppProtoView;
|
||||
var nestedChildProtoView: AppProtoView;
|
||||
var vcRef: ElementRef;
|
||||
var templateRef: TemplateRef;
|
||||
var nestedVcRefs: ElementRef[];
|
||||
var childViews: AppView[];
|
||||
var nestedChildViews: AppView[];
|
||||
beforeEach(() => {
|
||||
nestedChildProtoView = createEmbeddedPv();
|
||||
childProtoView = createEmbeddedPv([
|
||||
createNestedElBinder(
|
||||
createComponentPv([createNestedElBinder(nestedChildProtoView)]))
|
||||
]);
|
||||
var hostProtoView = createHostPv(
|
||||
[createNestedElBinder(createComponentPv([createNestedElBinder(childProtoView)]))]);
|
||||
hostView = internalView(
|
||||
<ViewRef>manager.createRootHostView(wrapPv(hostProtoView), null, null));
|
||||
vcRef = hostView.elementRefs[1];
|
||||
templateRef = new TemplateRef_(hostView.elementRefs[1]);
|
||||
nestedChildViews = [];
|
||||
childViews = [];
|
||||
nestedVcRefs = [];
|
||||
for (var i = 0; i < 2; i++) {
|
||||
var view = internalView(manager.createEmbeddedViewInContainer(vcRef, i, templateRef));
|
||||
childViews.push(view);
|
||||
var nestedVcRef = view.elementRefs[view.elementOffset];
|
||||
nestedVcRefs.push(nestedVcRef);
|
||||
for (var j = 0; j < 2; j++) {
|
||||
var nestedView = internalView(
|
||||
manager.createEmbeddedViewInContainer(nestedVcRef, j, templateRef));
|
||||
nestedChildViews.push(nestedView);
|
||||
}
|
||||
}
|
||||
resetSpies();
|
||||
});
|
||||
|
||||
it('should dehydrate all child views', () => {
|
||||
manager.destroyRootHostView(wrapView(hostView));
|
||||
childViews.forEach((childView) => expect(childView.hydrated()).toBe(false));
|
||||
nestedChildViews.forEach((childView) => expect(childView.hydrated()).toBe(false));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('attachViewInContainer', () => {
|
||||
|
||||
});
|
||||
|
||||
describe('detachViewInContainer', () => {
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,293 +0,0 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
xdescribe,
|
||||
describe,
|
||||
el,
|
||||
dispatchEvent,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
beforeEachProviders,
|
||||
it,
|
||||
xit,
|
||||
Log,
|
||||
SpyObject
|
||||
} from 'angular2/testing_internal';
|
||||
|
||||
import {
|
||||
SpyChangeDetector,
|
||||
SpyProtoElementInjector,
|
||||
SpyElementInjector,
|
||||
SpyPreBuiltObjects
|
||||
} from '../spies';
|
||||
|
||||
import {Injector, provide} from 'angular2/core';
|
||||
import {isBlank, isPresent} from 'angular2/src/facade/lang';
|
||||
|
||||
import {
|
||||
AppProtoView,
|
||||
AppView,
|
||||
AppProtoViewMergeInfo,
|
||||
ViewType
|
||||
} from 'angular2/src/core/linker/view';
|
||||
import {ElementBinder} from 'angular2/src/core/linker/element_binder';
|
||||
import {
|
||||
DirectiveProvider,
|
||||
ElementInjector,
|
||||
PreBuiltObjects,
|
||||
ProtoElementInjector
|
||||
} from 'angular2/src/core/linker/element_injector';
|
||||
import {DirectiveResolver} from 'angular2/src/core/linker/directive_resolver';
|
||||
import {Component} from 'angular2/src/core/metadata';
|
||||
import {AppViewManagerUtils} from 'angular2/src/core/linker/view_manager_utils';
|
||||
import {RenderViewWithFragments} from 'angular2/core';
|
||||
|
||||
export function main() {
|
||||
// TODO(tbosch): add more tests here!
|
||||
|
||||
describe('AppViewManagerUtils', () => {
|
||||
|
||||
var utils: AppViewManagerUtils;
|
||||
|
||||
beforeEach(() => { utils = new AppViewManagerUtils(); });
|
||||
|
||||
function createViewWithChildren(pv: AppProtoView): AppView {
|
||||
var renderViewWithFragments = new RenderViewWithFragments(null, [null, null]);
|
||||
return utils.createView(pv, renderViewWithFragments, null, null);
|
||||
}
|
||||
|
||||
describe('shared hydrate functionality', () => {
|
||||
|
||||
it("should hydrate the change detector after hydrating element injectors", () => {
|
||||
var log = new Log();
|
||||
|
||||
var componentProtoView = createComponentPv([createEmptyElBinder()]);
|
||||
var hostView =
|
||||
createViewWithChildren(createHostPv([createNestedElBinder(componentProtoView)]));
|
||||
var componentView = hostView.views[1];
|
||||
|
||||
var spyEi = <any>componentView.elementInjectors[0];
|
||||
spyEi.spy('hydrate').andCallFake(log.fn('hydrate'));
|
||||
|
||||
var spyCd = <any>componentView.changeDetector;
|
||||
spyCd.spy('hydrate').andCallFake(log.fn('hydrateCD'));
|
||||
|
||||
utils.hydrateRootHostView(hostView, createInjector());
|
||||
|
||||
expect(log.result()).toEqual('hydrate; hydrateCD');
|
||||
});
|
||||
|
||||
it("should set up event listeners", () => {
|
||||
var dir = new Object();
|
||||
|
||||
var hostPv =
|
||||
createHostPv([createNestedElBinder(createComponentPv()), createEmptyElBinder()]);
|
||||
var hostView = createViewWithChildren(hostPv);
|
||||
var spyEventAccessor1 = SpyObject.stub({"subscribe": null});
|
||||
SpyObject.stub(
|
||||
hostView.elementInjectors[0],
|
||||
{'getEventEmitterAccessors': [[spyEventAccessor1]], 'getDirectiveAtIndex': dir});
|
||||
var spyEventAccessor2 = SpyObject.stub({"subscribe": null});
|
||||
SpyObject.stub(
|
||||
hostView.elementInjectors[1],
|
||||
{'getEventEmitterAccessors': [[spyEventAccessor2]], 'getDirectiveAtIndex': dir});
|
||||
|
||||
utils.hydrateRootHostView(hostView, createInjector());
|
||||
|
||||
expect(spyEventAccessor1.spy('subscribe')).toHaveBeenCalledWith(hostView, 0, dir);
|
||||
expect(spyEventAccessor2.spy('subscribe')).toHaveBeenCalledWith(hostView, 1, dir);
|
||||
});
|
||||
|
||||
it("should not hydrate element injectors of component views inside of embedded fragments",
|
||||
() => {
|
||||
var hostView = createViewWithChildren(createHostPv([
|
||||
createNestedElBinder(createComponentPv([
|
||||
createNestedElBinder(createEmbeddedPv(
|
||||
[createNestedElBinder(createComponentPv([createEmptyElBinder()]))]))
|
||||
]))
|
||||
]));
|
||||
|
||||
utils.hydrateRootHostView(hostView, createInjector());
|
||||
expect(hostView.elementInjectors.length).toBe(4);
|
||||
expect((<any>hostView.elementInjectors[3]).spy('hydrate')).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
describe('attachViewInContainer', () => {
|
||||
var parentView, contextView, childView;
|
||||
|
||||
function createViews(numInj = 1) {
|
||||
var childPv = createEmbeddedPv([createEmptyElBinder()]);
|
||||
childView = createViewWithChildren(childPv);
|
||||
|
||||
var parentPv = createHostPv([createEmptyElBinder()]);
|
||||
parentView = createViewWithChildren(parentPv);
|
||||
|
||||
var binders = [];
|
||||
for (var i = 0; i < numInj; i++) {
|
||||
binders.push(createEmptyElBinder(i > 0 ? binders[i - 1] : null))
|
||||
}
|
||||
var contextPv = createHostPv(binders);
|
||||
contextView = createViewWithChildren(contextPv);
|
||||
}
|
||||
|
||||
it('should not modify the rootElementInjectors at the given context view', () => {
|
||||
createViews();
|
||||
utils.attachViewInContainer(parentView, 0, contextView, 0, 0, childView);
|
||||
expect(contextView.rootElementInjectors.length).toEqual(1);
|
||||
});
|
||||
|
||||
it('should link the views rootElementInjectors after the elementInjector at the given context',
|
||||
() => {
|
||||
createViews(2);
|
||||
utils.attachViewInContainer(parentView, 0, contextView, 1, 0, childView);
|
||||
expect(childView.rootElementInjectors[0].spy('link'))
|
||||
.toHaveBeenCalledWith(contextView.elementInjectors[0]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('hydrateViewInContainer', () => {
|
||||
var parentView, contextView, childView;
|
||||
|
||||
function createViews() {
|
||||
var parentPv = createHostPv([createEmptyElBinder()]);
|
||||
parentView = createViewWithChildren(parentPv);
|
||||
|
||||
var contextPv = createHostPv([createEmptyElBinder()]);
|
||||
contextView = createViewWithChildren(contextPv);
|
||||
|
||||
var childPv = createEmbeddedPv([createEmptyElBinder()]);
|
||||
childView = createViewWithChildren(childPv);
|
||||
utils.attachViewInContainer(parentView, 0, contextView, 0, 0, childView);
|
||||
}
|
||||
|
||||
it("should instantiate the elementInjectors with the host of the context's elementInjector",
|
||||
() => {
|
||||
createViews();
|
||||
|
||||
utils.hydrateViewInContainer(parentView, 0, contextView, 0, 0, null);
|
||||
expect(childView.rootElementInjectors[0].spy('hydrate'))
|
||||
.toHaveBeenCalledWith(null, contextView.elementInjectors[0].getHost(),
|
||||
childView.preBuiltObjects[0]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('hydrateRootHostView', () => {
|
||||
var hostView;
|
||||
|
||||
function createViews() {
|
||||
var hostPv = createHostPv([createNestedElBinder(createComponentPv())]);
|
||||
hostView = createViewWithChildren(hostPv);
|
||||
}
|
||||
|
||||
it("should instantiate the elementInjectors with the given injector and an empty host element injector",
|
||||
() => {
|
||||
var injector = createInjector();
|
||||
createViews();
|
||||
|
||||
utils.hydrateRootHostView(hostView, injector);
|
||||
expect(hostView.rootElementInjectors[0].spy('hydrate'))
|
||||
.toHaveBeenCalledWith(injector, null, hostView.preBuiltObjects[0]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
export function createInjector() {
|
||||
return Injector.resolveAndCreate([]);
|
||||
}
|
||||
|
||||
function createElementInjector(parent = null) {
|
||||
var host = new SpyElementInjector();
|
||||
var elementInjector = new SpyElementInjector();
|
||||
var _preBuiltObjects = null;
|
||||
var res = SpyObject.stub(elementInjector, {
|
||||
'isExportingComponent': false,
|
||||
'isExportingElement': false,
|
||||
'getEventEmitterAccessors': [],
|
||||
'getHostActionAccessors': [],
|
||||
'getComponent': new Object(),
|
||||
'getHost': host
|
||||
});
|
||||
res.spy('getNestedView').andCallFake(() => _preBuiltObjects.nestedView);
|
||||
res.spy('hydrate')
|
||||
.andCallFake((mperativelyCreatedInjector: Injector, host: ElementInjector,
|
||||
preBuiltObjects: PreBuiltObjects) => { _preBuiltObjects = preBuiltObjects; });
|
||||
res.prop('parent', parent);
|
||||
return res;
|
||||
}
|
||||
|
||||
export function createProtoElInjector(parent: ProtoElementInjector = null): ProtoElementInjector {
|
||||
var pei = new SpyProtoElementInjector();
|
||||
pei.prop("parent", parent);
|
||||
pei.prop("index", 0);
|
||||
pei.spy('instantiate').andCallFake((parentEli) => createElementInjector(parentEli));
|
||||
return <any>pei;
|
||||
}
|
||||
|
||||
export function createEmptyElBinder(parent: ElementBinder = null) {
|
||||
var parentPeli = isPresent(parent) ? parent.protoElementInjector : null;
|
||||
return new ElementBinder(0, null, 0, createProtoElInjector(parentPeli), null, null);
|
||||
}
|
||||
|
||||
export function createNestedElBinder(nestedProtoView: AppProtoView) {
|
||||
var componentProvider = null;
|
||||
if (nestedProtoView.type === ViewType.COMPONENT) {
|
||||
var annotation = new DirectiveResolver().resolve(SomeComponent);
|
||||
componentProvider = DirectiveProvider.createFromType(SomeComponent, annotation);
|
||||
}
|
||||
return new ElementBinder(0, null, 0, createProtoElInjector(), componentProvider, nestedProtoView);
|
||||
}
|
||||
|
||||
function _createProtoView(type: ViewType, binders: ElementBinder[] = null) {
|
||||
if (isBlank(binders)) {
|
||||
binders = [];
|
||||
}
|
||||
var res = new AppProtoView(null, [], type, true, (_) => new SpyChangeDetector(),
|
||||
new Map<string, any>(), null);
|
||||
var mergedElementCount = 0;
|
||||
var mergedEmbeddedViewCount = 0;
|
||||
var mergedViewCount = 1;
|
||||
for (var i = 0; i < binders.length; i++) {
|
||||
var binder = binders[i];
|
||||
binder.protoElementInjector.index = i;
|
||||
mergedElementCount++;
|
||||
var nestedPv = binder.nestedProtoView;
|
||||
if (isPresent(nestedPv)) {
|
||||
mergedElementCount += nestedPv.mergeInfo.elementCount;
|
||||
mergedEmbeddedViewCount += nestedPv.mergeInfo.embeddedViewCount;
|
||||
mergedViewCount += nestedPv.mergeInfo.viewCount;
|
||||
if (nestedPv.type === ViewType.EMBEDDED) {
|
||||
mergedEmbeddedViewCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
var mergeInfo =
|
||||
new AppProtoViewMergeInfo(mergedEmbeddedViewCount, mergedElementCount, mergedViewCount);
|
||||
res.init(null, binders, 0, mergeInfo, new Map<string, number>());
|
||||
return res;
|
||||
}
|
||||
|
||||
export function createHostPv(binders: ElementBinder[] = null) {
|
||||
return _createProtoView(ViewType.HOST, binders);
|
||||
}
|
||||
|
||||
export function createComponentPv(binders: ElementBinder[] = null) {
|
||||
return _createProtoView(ViewType.COMPONENT, binders);
|
||||
}
|
||||
|
||||
export function createEmbeddedPv(binders: ElementBinder[] = null) {
|
||||
return _createProtoView(ViewType.EMBEDDED, binders);
|
||||
}
|
||||
|
||||
|
||||
@Component({selector: 'someComponent'})
|
||||
class SomeComponent {
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
xdescribe,
|
||||
describe,
|
||||
el,
|
||||
dispatchEvent,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
beforeEachProviders,
|
||||
it,
|
||||
xit,
|
||||
SpyObject,
|
||||
proxy
|
||||
} from 'angular2/testing_internal';
|
||||
import {AppViewPool} from 'angular2/src/core/linker/view_pool';
|
||||
import {AppProtoView, AppView} from 'angular2/src/core/linker/view';
|
||||
import {MapWrapper, Map} from 'angular2/src/facade/collection';
|
||||
|
||||
export function main() {
|
||||
describe('AppViewPool', () => {
|
||||
|
||||
function createViewPool({capacity}): AppViewPool { return new AppViewPool(capacity); }
|
||||
|
||||
function createProtoView() {
|
||||
return new AppProtoView(null, null, null, null, null, null, null);
|
||||
}
|
||||
|
||||
function createView(pv) {
|
||||
return new AppView(null, pv, null, null, null, new Map<string, any>(), null, null, null);
|
||||
}
|
||||
|
||||
it('should support multiple AppProtoViews', () => {
|
||||
var vf = createViewPool({capacity: 2});
|
||||
var pv1 = createProtoView();
|
||||
var pv2 = createProtoView();
|
||||
var view1 = createView(pv1);
|
||||
var view2 = createView(pv2);
|
||||
vf.returnView(view1);
|
||||
vf.returnView(view2);
|
||||
|
||||
expect(vf.getView(pv1)).toBe(view1);
|
||||
expect(vf.getView(pv2)).toBe(view2);
|
||||
});
|
||||
|
||||
it('should reuse the newest view that has been returned', () => {
|
||||
var pv = createProtoView();
|
||||
var vf = createViewPool({capacity: 2});
|
||||
var view1 = createView(pv);
|
||||
var view2 = createView(pv);
|
||||
vf.returnView(view1);
|
||||
vf.returnView(view2);
|
||||
|
||||
expect(vf.getView(pv)).toBe(view2);
|
||||
});
|
||||
|
||||
it('should not add views when the capacity has been reached', () => {
|
||||
var pv = createProtoView();
|
||||
var vf = createViewPool({capacity: 2});
|
||||
var view1 = createView(pv);
|
||||
var view2 = createView(pv);
|
||||
var view3 = createView(pv);
|
||||
expect(vf.returnView(view1)).toBe(true);
|
||||
expect(vf.returnView(view2)).toBe(true);
|
||||
expect(vf.returnView(view3)).toBe(false);
|
||||
|
||||
expect(vf.getView(pv)).toBe(view2);
|
||||
expect(vf.getView(pv)).toBe(view1);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user