refactor(core): move core/compiler to core/linker
This commit is contained in:
63
modules/angular2/test/core/linker/compiler_spec.ts
Normal file
63
modules/angular2/test/core/linker/compiler_spec.ts
Normal file
@ -0,0 +1,63 @@
|
||||
import {
|
||||
ddescribe,
|
||||
describe,
|
||||
xdescribe,
|
||||
it,
|
||||
iit,
|
||||
xit,
|
||||
expect,
|
||||
beforeEach,
|
||||
afterEach,
|
||||
AsyncTestCompleter,
|
||||
inject,
|
||||
beforeEachBindings
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import {Component, View, bind} from 'angular2/core';
|
||||
import {SpyProtoViewFactory} from '../spies';
|
||||
import {
|
||||
CompiledHostTemplate,
|
||||
CompiledTemplate,
|
||||
BeginComponentCmd
|
||||
} from 'angular2/src/core/compiler/template_commands';
|
||||
import {Compiler} from 'angular2/src/core/compiler/compiler';
|
||||
import {ProtoViewFactory} from 'angular2/src/core/compiler/proto_view_factory';
|
||||
import {reflector, ReflectionInfo} from 'angular2/src/core/reflection/reflection';
|
||||
import {AppProtoView} from 'angular2/src/core/compiler/view';
|
||||
|
||||
export function main() {
|
||||
describe('Compiler', () => {
|
||||
var compiler: Compiler;
|
||||
var protoViewFactorySpy;
|
||||
var someProtoView;
|
||||
var cht: CompiledHostTemplate;
|
||||
|
||||
beforeEachBindings(() => {
|
||||
protoViewFactorySpy = new SpyProtoViewFactory();
|
||||
someProtoView = new AppProtoView(null, null, null, null, null, null);
|
||||
protoViewFactorySpy.spy('createHost').andReturn(someProtoView);
|
||||
return [bind(ProtoViewFactory).toValue(protoViewFactorySpy), Compiler];
|
||||
});
|
||||
|
||||
beforeEach(inject([Compiler], (_compiler) => {
|
||||
compiler = _compiler;
|
||||
cht = new CompiledHostTemplate(() => new CompiledTemplate(23, null));
|
||||
reflector.registerType(SomeComponent, new ReflectionInfo([cht]));
|
||||
}));
|
||||
|
||||
it('should read the template from an annotation', inject([AsyncTestCompleter], (async) => {
|
||||
compiler.compileInHost(SomeComponent)
|
||||
.then((_) => {
|
||||
expect(protoViewFactorySpy.spy('createHost')).toHaveBeenCalledWith(cht);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should clear the cache', () => {
|
||||
compiler.clearCache();
|
||||
expect(protoViewFactorySpy.spy('clearCache')).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class SomeComponent {}
|
138
modules/angular2/test/core/linker/directive_lifecycle_spec.dart
Normal file
138
modules/angular2/test/core/linker/directive_lifecycle_spec.dart
Normal file
@ -0,0 +1,138 @@
|
||||
library angular2.test.core.compiler.directive_lifecycle_spec;
|
||||
|
||||
import 'package:angular2/test_lib.dart';
|
||||
import 'package:angular2/src/core/compiler/directive_lifecycle_reflector.dart';
|
||||
import 'package:angular2/src/core/compiler/interfaces.dart';
|
||||
|
||||
main() {
|
||||
describe('Create DirectiveMetadata', () {
|
||||
describe('lifecycle', () {
|
||||
|
||||
describe("onChanges", () {
|
||||
it("should be true when the directive has the onChanges method", () {
|
||||
expect(hasLifecycleHook(LifecycleHooks.OnChanges, DirectiveImplementingOnChanges))
|
||||
.toBe(true);
|
||||
});
|
||||
|
||||
it("should be false otherwise", () {
|
||||
expect(hasLifecycleHook(LifecycleHooks.OnChanges, DirectiveNoHooks)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("onDestroy", () {
|
||||
it("should be true when the directive has the onDestroy method", () {
|
||||
expect(hasLifecycleHook(LifecycleHooks.OnDestroy, DirectiveImplementingOnDestroy))
|
||||
.toBe(true);
|
||||
});
|
||||
|
||||
it("should be false otherwise", () {
|
||||
expect(hasLifecycleHook(LifecycleHooks.OnDestroy, DirectiveNoHooks)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("onInit", () {
|
||||
it("should be true when the directive has the onInit method", () {
|
||||
expect(hasLifecycleHook(LifecycleHooks.OnInit, DirectiveImplementingOnInit))
|
||||
.toBe(true);
|
||||
});
|
||||
|
||||
it("should be false otherwise", () {
|
||||
expect(hasLifecycleHook(LifecycleHooks.OnInit, DirectiveNoHooks)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("doCheck", () {
|
||||
it("should be true when the directive has the doCheck method", () {
|
||||
expect(hasLifecycleHook(LifecycleHooks.DoCheck, DirectiveImplementingOnCheck))
|
||||
.toBe(true);
|
||||
});
|
||||
|
||||
it("should be false otherwise", () {
|
||||
expect(hasLifecycleHook(LifecycleHooks.DoCheck, DirectiveNoHooks)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("afterContentInit", () {
|
||||
it("should be true when the directive has the afterContentInit method", () {
|
||||
expect(hasLifecycleHook(LifecycleHooks.AfterContentInit, DirectiveImplementingAfterContentInit))
|
||||
.toBe(true);
|
||||
});
|
||||
|
||||
it("should be false otherwise", () {
|
||||
expect(hasLifecycleHook(LifecycleHooks.AfterContentInit, DirectiveNoHooks))
|
||||
.toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("afterContentChecked", () {
|
||||
it("should be true when the directive has the afterContentChecked method", () {
|
||||
expect(hasLifecycleHook(LifecycleHooks.AfterContentChecked, DirectiveImplementingAfterContentChecked))
|
||||
.toBe(true);
|
||||
});
|
||||
|
||||
it("should be false otherwise", () {
|
||||
expect(hasLifecycleHook(LifecycleHooks.AfterContentChecked, DirectiveNoHooks))
|
||||
.toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe("afterViewInit", () {
|
||||
it("should be true when the directive has the afterViewInit method", () {
|
||||
expect(hasLifecycleHook(LifecycleHooks.AfterViewInit, DirectiveImplementingAfterViewInit))
|
||||
.toBe(true);
|
||||
});
|
||||
|
||||
it("should be false otherwise", () {
|
||||
expect(hasLifecycleHook(LifecycleHooks.AfterViewInit, DirectiveNoHooks)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("afterViewChecked", () {
|
||||
it("should be true when the directive has the afterViewChecked method", () {
|
||||
expect(hasLifecycleHook(LifecycleHooks.AfterViewChecked, DirectiveImplementingAfterViewChecked))
|
||||
.toBe(true);
|
||||
});
|
||||
|
||||
it("should be false otherwise", () {
|
||||
expect(hasLifecycleHook(LifecycleHooks.AfterViewChecked, DirectiveNoHooks))
|
||||
.toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class DirectiveNoHooks {}
|
||||
|
||||
class DirectiveImplementingOnChanges implements OnChanges {
|
||||
onChanges(_) {}
|
||||
}
|
||||
|
||||
class DirectiveImplementingOnCheck implements DoCheck {
|
||||
doCheck() {}
|
||||
}
|
||||
|
||||
class DirectiveImplementingOnInit implements OnInit {
|
||||
onInit() {}
|
||||
}
|
||||
|
||||
class DirectiveImplementingOnDestroy implements OnDestroy {
|
||||
onDestroy() {}
|
||||
}
|
||||
|
||||
class DirectiveImplementingAfterContentInit implements AfterContentInit {
|
||||
afterContentInit() {}
|
||||
}
|
||||
|
||||
class DirectiveImplementingAfterContentChecked implements AfterContentChecked {
|
||||
afterContentChecked() {}
|
||||
}
|
||||
|
||||
class DirectiveImplementingAfterViewInit implements AfterViewInit {
|
||||
afterViewInit() {}
|
||||
}
|
||||
|
||||
class DirectiveImplementingAfterViewChecked implements AfterViewChecked {
|
||||
afterViewChecked() {}
|
||||
}
|
149
modules/angular2/test/core/linker/directive_lifecycle_spec.ts
Normal file
149
modules/angular2/test/core/linker/directive_lifecycle_spec.ts
Normal file
@ -0,0 +1,149 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
xdescribe,
|
||||
ddescribe,
|
||||
describe,
|
||||
el,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
SpyObject,
|
||||
proxy
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import {hasLifecycleHook} from 'angular2/src/core/compiler/directive_lifecycle_reflector';
|
||||
import {LifecycleHooks} from 'angular2/src/core/compiler/interfaces';
|
||||
|
||||
export function main() {
|
||||
describe('Create DirectiveMetadata', () => {
|
||||
describe('lifecycle', () => {
|
||||
|
||||
describe("onChanges", () => {
|
||||
it("should be true when the directive has the onChanges method", () => {
|
||||
expect(hasLifecycleHook(LifecycleHooks.OnChanges, DirectiveWithOnChangesMethod))
|
||||
.toBe(true);
|
||||
});
|
||||
|
||||
it("should be false otherwise", () => {
|
||||
expect(hasLifecycleHook(LifecycleHooks.OnChanges, DirectiveNoHooks)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("onDestroy", () => {
|
||||
it("should be true when the directive has the onDestroy method", () => {
|
||||
expect(hasLifecycleHook(LifecycleHooks.OnDestroy, DirectiveWithOnDestroyMethod))
|
||||
.toBe(true);
|
||||
});
|
||||
|
||||
it("should be false otherwise", () => {
|
||||
expect(hasLifecycleHook(LifecycleHooks.OnDestroy, DirectiveNoHooks)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("onInit", () => {
|
||||
it("should be true when the directive has the onInit method", () => {
|
||||
expect(hasLifecycleHook(LifecycleHooks.OnInit, DirectiveWithOnInitMethod)).toBe(true);
|
||||
});
|
||||
|
||||
it("should be false otherwise", () => {
|
||||
expect(hasLifecycleHook(LifecycleHooks.OnInit, DirectiveNoHooks)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("doCheck", () => {
|
||||
it("should be true when the directive has the doCheck method", () => {
|
||||
expect(hasLifecycleHook(LifecycleHooks.DoCheck, DirectiveWithOnCheckMethod)).toBe(true);
|
||||
});
|
||||
|
||||
it("should be false otherwise", () => {
|
||||
expect(hasLifecycleHook(LifecycleHooks.DoCheck, DirectiveNoHooks)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("afterContentInit", () => {
|
||||
it("should be true when the directive has the afterContentInit method", () => {
|
||||
expect(hasLifecycleHook(LifecycleHooks.AfterContentInit,
|
||||
DirectiveWithAfterContentInitMethod))
|
||||
.toBe(true);
|
||||
});
|
||||
|
||||
it("should be false otherwise", () => {
|
||||
expect(hasLifecycleHook(LifecycleHooks.AfterContentInit, DirectiveNoHooks)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("afterContentChecked", () => {
|
||||
it("should be true when the directive has the afterContentChecked method", () => {
|
||||
expect(hasLifecycleHook(LifecycleHooks.AfterContentChecked,
|
||||
DirectiveWithAfterContentCheckedMethod))
|
||||
.toBe(true);
|
||||
});
|
||||
|
||||
it("should be false otherwise", () => {
|
||||
expect(hasLifecycleHook(LifecycleHooks.AfterContentChecked, DirectiveNoHooks))
|
||||
.toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe("afterViewInit", () => {
|
||||
it("should be true when the directive has the afterViewInit method", () => {
|
||||
expect(hasLifecycleHook(LifecycleHooks.AfterViewInit, DirectiveWithAfterViewInitMethod))
|
||||
.toBe(true);
|
||||
});
|
||||
|
||||
it("should be false otherwise", () => {
|
||||
expect(hasLifecycleHook(LifecycleHooks.AfterViewInit, DirectiveNoHooks)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("afterViewChecked", () => {
|
||||
it("should be true when the directive has the afterViewChecked method", () => {
|
||||
expect(hasLifecycleHook(LifecycleHooks.AfterViewChecked,
|
||||
DirectiveWithAfterViewCheckedMethod))
|
||||
.toBe(true);
|
||||
});
|
||||
|
||||
it("should be false otherwise", () => {
|
||||
expect(hasLifecycleHook(LifecycleHooks.AfterViewChecked, DirectiveNoHooks)).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class DirectiveNoHooks {}
|
||||
|
||||
class DirectiveWithOnChangesMethod {
|
||||
onChanges(_) {}
|
||||
}
|
||||
|
||||
class DirectiveWithOnInitMethod {
|
||||
onInit() {}
|
||||
}
|
||||
|
||||
class DirectiveWithOnCheckMethod {
|
||||
doCheck() {}
|
||||
}
|
||||
|
||||
class DirectiveWithOnDestroyMethod {
|
||||
onDestroy() {}
|
||||
}
|
||||
|
||||
class DirectiveWithAfterContentInitMethod {
|
||||
afterContentInit() {}
|
||||
}
|
||||
|
||||
class DirectiveWithAfterContentCheckedMethod {
|
||||
afterContentChecked() {}
|
||||
}
|
||||
|
||||
class DirectiveWithAfterViewInitMethod {
|
||||
afterViewInit() {}
|
||||
}
|
||||
|
||||
class DirectiveWithAfterViewCheckedMethod {
|
||||
afterViewChecked() {}
|
||||
}
|
189
modules/angular2/test/core/linker/directive_resolver_spec.ts
Normal file
189
modules/angular2/test/core/linker/directive_resolver_spec.ts
Normal file
@ -0,0 +1,189 @@
|
||||
import {ddescribe, describe, it, iit, expect, beforeEach} from 'angular2/test_lib';
|
||||
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
||||
import {
|
||||
DirectiveMetadata,
|
||||
Directive,
|
||||
Input,
|
||||
Output,
|
||||
HostBinding,
|
||||
HostListener,
|
||||
ContentChildren,
|
||||
ContentChildrenMetadata,
|
||||
ViewChildren,
|
||||
ViewChildrenMetadata,
|
||||
ContentChild,
|
||||
ContentChildMetadata,
|
||||
ViewChild,
|
||||
ViewChildMetadata
|
||||
} from 'angular2/src/core/metadata';
|
||||
|
||||
@Directive({selector: 'someDirective'})
|
||||
class SomeDirective {
|
||||
}
|
||||
|
||||
@Directive({selector: 'someChildDirective'})
|
||||
class SomeChildDirective extends SomeDirective {
|
||||
}
|
||||
|
||||
@Directive({selector: 'someDirective', inputs: ['c']})
|
||||
class SomeDirectiveWithProperties {
|
||||
@Input() a;
|
||||
@Input("renamed") b;
|
||||
c;
|
||||
}
|
||||
|
||||
@Directive({selector: 'someDirective', outputs: ['c']})
|
||||
class SomeDirectiveWithOutputs {
|
||||
@Output() a;
|
||||
@Output("renamed") b;
|
||||
c;
|
||||
}
|
||||
|
||||
|
||||
@Directive({selector: 'someDirective'})
|
||||
class SomeDirectiveWithSetterProps {
|
||||
@Input("renamed")
|
||||
set a(value) {
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: 'someDirective'})
|
||||
class SomeDirectiveWithGetterOutputs {
|
||||
@Output("renamed")
|
||||
get a() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: 'someDirective', host: {'[c]': 'c'}})
|
||||
class SomeDirectiveWithHostBindings {
|
||||
@HostBinding() a;
|
||||
@HostBinding("renamed") b;
|
||||
c;
|
||||
}
|
||||
|
||||
@Directive({selector: 'someDirective', host: {'(c)': 'onC()'}})
|
||||
class SomeDirectiveWithHostListeners {
|
||||
@HostListener('a')
|
||||
onA() {
|
||||
}
|
||||
@HostListener('b', ['$event.value'])
|
||||
onB(value) {
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: 'someDirective', queries: {"cs": new ContentChildren("c")}})
|
||||
class SomeDirectiveWithContentChildren {
|
||||
@ContentChildren("a") as: any;
|
||||
c;
|
||||
}
|
||||
|
||||
@Directive({selector: 'someDirective', queries: {"cs": new ViewChildren("c")}})
|
||||
class SomeDirectiveWithViewChildren {
|
||||
@ViewChildren("a") as: any;
|
||||
c;
|
||||
}
|
||||
|
||||
@Directive({selector: 'someDirective', queries: {"c": new ContentChild("c")}})
|
||||
class SomeDirectiveWithContentChild {
|
||||
@ContentChild("a") a: any;
|
||||
c;
|
||||
}
|
||||
|
||||
@Directive({selector: 'someDirective', queries: {"c": new ViewChild("c")}})
|
||||
class SomeDirectiveWithViewChild {
|
||||
@ViewChild("a") a: any;
|
||||
c;
|
||||
}
|
||||
|
||||
class SomeDirectiveWithoutMetadata {}
|
||||
|
||||
export function main() {
|
||||
describe("DirectiveResolver", () => {
|
||||
var resolver;
|
||||
|
||||
beforeEach(() => { resolver = new DirectiveResolver(); });
|
||||
|
||||
it('should read out the Directive metadata', () => {
|
||||
var directiveMetadata = resolver.resolve(SomeDirective);
|
||||
expect(directiveMetadata)
|
||||
.toEqual(new DirectiveMetadata(
|
||||
{selector: 'someDirective', inputs: [], outputs: [], host: {}, queries: {}}));
|
||||
});
|
||||
|
||||
it('should throw if not matching metadata is found', () => {
|
||||
expect(() => { resolver.resolve(SomeDirectiveWithoutMetadata); })
|
||||
.toThrowError('No Directive annotation found on SomeDirectiveWithoutMetadata');
|
||||
});
|
||||
|
||||
it('should not read parent class Directive metadata', function() {
|
||||
var directiveMetadata = resolver.resolve(SomeChildDirective);
|
||||
expect(directiveMetadata)
|
||||
.toEqual(new DirectiveMetadata(
|
||||
{selector: 'someChildDirective', inputs: [], outputs: [], host: {}, queries: {}}));
|
||||
});
|
||||
|
||||
describe('inputs', () => {
|
||||
it('should append directive inputs', () => {
|
||||
var directiveMetadata = resolver.resolve(SomeDirectiveWithProperties);
|
||||
expect(directiveMetadata.inputs).toEqual(['c', 'a', 'b: renamed']);
|
||||
});
|
||||
|
||||
it('should work with getters and setters', () => {
|
||||
var directiveMetadata = resolver.resolve(SomeDirectiveWithSetterProps);
|
||||
expect(directiveMetadata.inputs).toEqual(['a: renamed']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('outputs', () => {
|
||||
it('should append directive outputs', () => {
|
||||
var directiveMetadata = resolver.resolve(SomeDirectiveWithOutputs);
|
||||
expect(directiveMetadata.outputs).toEqual(['c', 'a', 'b: renamed']);
|
||||
});
|
||||
|
||||
it('should work with getters and setters', () => {
|
||||
var directiveMetadata = resolver.resolve(SomeDirectiveWithGetterOutputs);
|
||||
expect(directiveMetadata.outputs).toEqual(['a: renamed']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('host', () => {
|
||||
it('should append host bindings', () => {
|
||||
var directiveMetadata = resolver.resolve(SomeDirectiveWithHostBindings);
|
||||
expect(directiveMetadata.host).toEqual({'[c]': 'c', '[a]': 'a', '[renamed]': 'b'});
|
||||
});
|
||||
|
||||
it('should append host listeners', () => {
|
||||
var directiveMetadata = resolver.resolve(SomeDirectiveWithHostListeners);
|
||||
expect(directiveMetadata.host)
|
||||
.toEqual({'(c)': 'onC()', '(a)': 'onA()', '(b)': 'onB($event.value)'});
|
||||
});
|
||||
});
|
||||
|
||||
describe('queries', () => {
|
||||
it('should append ContentChildren', () => {
|
||||
var directiveMetadata = resolver.resolve(SomeDirectiveWithContentChildren);
|
||||
expect(directiveMetadata.queries)
|
||||
.toEqual({"cs": new ContentChildren("c"), "as": new ContentChildren("a")});
|
||||
});
|
||||
|
||||
it('should append ViewChildren', () => {
|
||||
var directiveMetadata = resolver.resolve(SomeDirectiveWithViewChildren);
|
||||
expect(directiveMetadata.queries)
|
||||
.toEqual({"cs": new ViewChildren("c"), "as": new ViewChildren("a")});
|
||||
});
|
||||
|
||||
it('should append ContentChild', () => {
|
||||
var directiveMetadata = resolver.resolve(SomeDirectiveWithContentChild);
|
||||
expect(directiveMetadata.queries)
|
||||
.toEqual({"c": new ContentChild("c"), "a": new ContentChild("a")});
|
||||
});
|
||||
|
||||
it('should append ViewChild', () => {
|
||||
var directiveMetadata = resolver.resolve(SomeDirectiveWithViewChild);
|
||||
expect(directiveMetadata.queries)
|
||||
.toEqual({"c": new ViewChild("c"), "a": new ViewChild("a")});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
@ -0,0 +1,319 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
xdescribe,
|
||||
describe,
|
||||
el,
|
||||
dispatchEvent,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
beforeEachBindings,
|
||||
it,
|
||||
xit,
|
||||
TestComponentBuilder,
|
||||
RootTestComponent
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import {OnDestroy} from 'angular2/lifecycle_hooks';
|
||||
import {Injector, NgIf} from 'angular2/core';
|
||||
import {inspectElement, By} from 'angular2/src/core/debug';
|
||||
import {Component, View, ViewMetadata} from 'angular2/src/core/metadata';
|
||||
import {DynamicComponentLoader} from 'angular2/src/core/compiler/dynamic_component_loader';
|
||||
import {ElementRef} from 'angular2/src/core/compiler/element_ref';
|
||||
import {DOCUMENT} from 'angular2/src/core/render/render';
|
||||
import {DOM} from 'angular2/src/core/dom/dom_adapter';
|
||||
|
||||
export function main() {
|
||||
describe('DynamicComponentLoader', function() {
|
||||
describe("loading into a location", () => {
|
||||
it('should work',
|
||||
inject(
|
||||
[DynamicComponentLoader, TestComponentBuilder, AsyncTestCompleter],
|
||||
(loader, tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(
|
||||
MyComp, new ViewMetadata(
|
||||
{template: '<location #loc></location>', directives: [Location]}))
|
||||
.createAsync(MyComp)
|
||||
.then((tc) => {
|
||||
|
||||
loader.loadIntoLocation(DynamicallyLoaded, tc.debugElement.elementRef, 'loc')
|
||||
.then(ref => {
|
||||
expect(tc.debugElement.nativeElement)
|
||||
.toHaveText("Location;DynamicallyLoaded;");
|
||||
async.done();
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
it('should return a disposable component ref',
|
||||
inject(
|
||||
[DynamicComponentLoader, TestComponentBuilder, AsyncTestCompleter],
|
||||
(loader, tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(
|
||||
MyComp, new ViewMetadata(
|
||||
{template: '<location #loc></location>', directives: [Location]}))
|
||||
.createAsync(MyComp)
|
||||
.then((tc) => {
|
||||
|
||||
loader.loadIntoLocation(DynamicallyLoaded, tc.debugElement.elementRef, 'loc')
|
||||
.then(ref => {
|
||||
ref.dispose();
|
||||
expect(tc.debugElement.nativeElement).toHaveText("Location;");
|
||||
async.done();
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
it('should allow to dispose even if the location has been removed',
|
||||
inject([DynamicComponentLoader, TestComponentBuilder, AsyncTestCompleter],
|
||||
(loader, tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MyComp, new ViewMetadata({
|
||||
template: '<child-cmp *ng-if="ctxBoolProp"></child-cmp>',
|
||||
directives: [NgIf, ChildComp]
|
||||
}))
|
||||
.overrideView(
|
||||
ChildComp,
|
||||
new ViewMetadata(
|
||||
{template: '<location #loc></location>', directives: [Location]}))
|
||||
.createAsync(MyComp)
|
||||
.then((tc) => {
|
||||
tc.debugElement.componentInstance.ctxBoolProp = true;
|
||||
tc.detectChanges();
|
||||
var childCompEl = tc.debugElement.query(By.css('child-cmp'));
|
||||
loader.loadIntoLocation(DynamicallyLoaded, childCompEl.elementRef, 'loc')
|
||||
.then(ref => {
|
||||
expect(tc.debugElement.nativeElement)
|
||||
.toHaveText("Location;DynamicallyLoaded;");
|
||||
|
||||
tc.debugElement.componentInstance.ctxBoolProp = false;
|
||||
tc.detectChanges();
|
||||
expect(tc.debugElement.nativeElement).toHaveText("");
|
||||
|
||||
ref.dispose();
|
||||
expect(tc.debugElement.nativeElement).toHaveText("");
|
||||
async.done();
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
it('should update host properties',
|
||||
inject([DynamicComponentLoader, TestComponentBuilder, AsyncTestCompleter],
|
||||
(loader, tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(
|
||||
MyComp,
|
||||
new ViewMetadata(
|
||||
{template: '<location #loc></location>', directives: [Location]}))
|
||||
.createAsync(MyComp)
|
||||
.then((tc) => {
|
||||
loader.loadIntoLocation(DynamicallyLoadedWithHostProps,
|
||||
tc.debugElement.elementRef, 'loc')
|
||||
.then(ref => {
|
||||
ref.instance.id = "new value";
|
||||
|
||||
tc.detectChanges();
|
||||
|
||||
var newlyInsertedElement =
|
||||
DOM.childNodes(tc.debugElement.nativeElement)[1];
|
||||
expect((<HTMLElement>newlyInsertedElement).id).toEqual("new value");
|
||||
async.done();
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
it('should throw if the variable does not exist',
|
||||
inject([DynamicComponentLoader, TestComponentBuilder, AsyncTestCompleter],
|
||||
(loader, tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(
|
||||
MyComp,
|
||||
new ViewMetadata(
|
||||
{template: '<location #loc></location>', directives: [Location]}))
|
||||
.createAsync(MyComp)
|
||||
.then((tc) => {
|
||||
expect(() => loader.loadIntoLocation(DynamicallyLoadedWithHostProps,
|
||||
tc.debugElement.elementRef,
|
||||
'someUnknownVariable'))
|
||||
.toThrowError('Could not find variable someUnknownVariable');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
describe("loading next to a location", () => {
|
||||
it('should work',
|
||||
inject([DynamicComponentLoader, TestComponentBuilder, AsyncTestCompleter],
|
||||
(loader, tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MyComp, new ViewMetadata({
|
||||
template: '<div><location #loc></location></div>',
|
||||
directives: [Location]
|
||||
}))
|
||||
.createAsync(MyComp)
|
||||
.then((tc) => {
|
||||
loader.loadNextToLocation(DynamicallyLoaded, tc.debugElement.elementRef)
|
||||
.then(ref => {
|
||||
expect(tc.debugElement.nativeElement).toHaveText("Location;");
|
||||
expect(DOM.nextSibling(tc.debugElement.nativeElement))
|
||||
.toHaveText('DynamicallyLoaded;');
|
||||
|
||||
async.done();
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
it('should return a disposable component ref',
|
||||
inject(
|
||||
[DynamicComponentLoader, TestComponentBuilder, AsyncTestCompleter],
|
||||
(loader, tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MyComp, new ViewMetadata({
|
||||
template: '<div><location #loc></location></div>',
|
||||
directives: [Location]
|
||||
}))
|
||||
.
|
||||
|
||||
createAsync(MyComp)
|
||||
.then((tc) => {
|
||||
loader.loadNextToLocation(DynamicallyLoaded, tc.debugElement.elementRef)
|
||||
.then(ref => {
|
||||
loader.loadNextToLocation(DynamicallyLoaded2, tc.debugElement.elementRef)
|
||||
.then(ref2 => {
|
||||
var firstSibling = DOM.nextSibling(tc.debugElement.nativeElement);
|
||||
var secondSibling = DOM.nextSibling(firstSibling);
|
||||
expect(tc.debugElement.nativeElement).toHaveText("Location;");
|
||||
expect(firstSibling).toHaveText("DynamicallyLoaded;");
|
||||
expect(secondSibling).toHaveText("DynamicallyLoaded2;");
|
||||
|
||||
ref2.dispose();
|
||||
|
||||
firstSibling = DOM.nextSibling(tc.debugElement.nativeElement);
|
||||
secondSibling = DOM.nextSibling(firstSibling);
|
||||
expect(secondSibling).toBeNull();
|
||||
|
||||
async.done();
|
||||
});
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
it('should update host properties',
|
||||
inject([DynamicComponentLoader, TestComponentBuilder, AsyncTestCompleter],
|
||||
(loader, tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MyComp, new ViewMetadata({
|
||||
template: '<div><location #loc></location></div>',
|
||||
directives: [Location]
|
||||
}))
|
||||
|
||||
.createAsync(MyComp)
|
||||
.then((tc) => {
|
||||
|
||||
loader.loadNextToLocation(DynamicallyLoadedWithHostProps,
|
||||
tc.debugElement.elementRef)
|
||||
.then(ref => {
|
||||
ref.instance.id = "new value";
|
||||
|
||||
tc.detectChanges();
|
||||
|
||||
var newlyInsertedElement =
|
||||
DOM.nextSibling(tc.debugElement.nativeElement);
|
||||
expect((<HTMLElement>newlyInsertedElement).id).toEqual("new value");
|
||||
|
||||
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>');
|
||||
DOM.appendChild(doc.body, rootEl);
|
||||
loader.loadAsRoot(ChildComp, null, injector)
|
||||
.then((componentRef) => {
|
||||
var el = new RootTestComponent(componentRef);
|
||||
expect(rootEl.parentNode).toBe(doc.body);
|
||||
|
||||
el.detectChanges();
|
||||
|
||||
expect(rootEl).toHaveText('hello');
|
||||
|
||||
componentRef.instance.ctxProp = 'new';
|
||||
|
||||
el.detectChanges();
|
||||
|
||||
expect(rootEl).toHaveText('new');
|
||||
|
||||
componentRef.dispose();
|
||||
|
||||
expect(rootEl.parentNode).toBeFalsy();
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'child-cmp',
|
||||
})
|
||||
@View({template: '{{ctxProp}}'})
|
||||
class ChildComp {
|
||||
ctxProp: string;
|
||||
constructor() { this.ctxProp = 'hello'; }
|
||||
}
|
||||
|
||||
|
||||
class DynamicallyCreatedComponentService {}
|
||||
|
||||
@Component({selector: 'hello-cmp', viewBindings: [DynamicallyCreatedComponentService]})
|
||||
@View({template: "{{greeting}}"})
|
||||
class DynamicallyCreatedCmp implements OnDestroy {
|
||||
greeting: string;
|
||||
dynamicallyCreatedComponentService: DynamicallyCreatedComponentService;
|
||||
destroyed: boolean = false;
|
||||
|
||||
constructor(a: DynamicallyCreatedComponentService) {
|
||||
this.greeting = "hello";
|
||||
this.dynamicallyCreatedComponentService = a;
|
||||
}
|
||||
|
||||
onDestroy() { this.destroyed = true; }
|
||||
}
|
||||
|
||||
@Component({selector: 'dummy'})
|
||||
@View({template: "DynamicallyLoaded;"})
|
||||
class DynamicallyLoaded {
|
||||
}
|
||||
|
||||
@Component({selector: 'dummy'})
|
||||
@View({template: "DynamicallyLoaded2;"})
|
||||
class DynamicallyLoaded2 {
|
||||
}
|
||||
|
||||
@Component({selector: 'dummy', host: {'[id]': 'id'}})
|
||||
@View({template: "DynamicallyLoadedWithHostProps;"})
|
||||
class DynamicallyLoadedWithHostProps {
|
||||
id: string;
|
||||
|
||||
constructor() { this.id = "default"; }
|
||||
}
|
||||
|
||||
@Component({selector: 'location'})
|
||||
@View({template: "Location;"})
|
||||
class Location {
|
||||
elementRef: ElementRef;
|
||||
|
||||
constructor(elementRef: ElementRef) { this.elementRef = elementRef; }
|
||||
}
|
||||
|
||||
@Component({selector: 'my-comp'})
|
||||
@View({directives: []})
|
||||
class MyComp {
|
||||
ctxBoolProp: boolean;
|
||||
|
||||
constructor() { this.ctxBoolProp = false; }
|
||||
}
|
1044
modules/angular2/test/core/linker/element_injector_spec.ts
Normal file
1044
modules/angular2/test/core/linker/element_injector_spec.ts
Normal file
File diff suppressed because it is too large
Load Diff
308
modules/angular2/test/core/linker/integration_dart_spec.dart
Normal file
308
modules/angular2/test/core/linker/integration_dart_spec.dart
Normal file
@ -0,0 +1,308 @@
|
||||
/// This file contains tests that make sense only in Dart
|
||||
library angular2.test.di.integration_dart_spec;
|
||||
|
||||
import 'package:angular2/angular2.dart';
|
||||
import 'package:angular2/core.dart';
|
||||
import 'package:angular2/src/core/debug.dart';
|
||||
import 'package:angular2/test_lib.dart';
|
||||
import 'package:observe/observe.dart';
|
||||
import 'package:angular2/src/core/change_detection/differs/default_iterable_differ.dart';
|
||||
import 'package:angular2/src/core/change_detection/change_detection.dart';
|
||||
|
||||
class MockException implements Error {
|
||||
var message;
|
||||
var stackTrace;
|
||||
}
|
||||
|
||||
class NonError {
|
||||
var message;
|
||||
}
|
||||
|
||||
void functionThatThrows() {
|
||||
try {
|
||||
throw new MockException();
|
||||
} catch (e, stack) {
|
||||
// If we lose the stack trace the message will no longer match
|
||||
// the first line in the stack
|
||||
e.message = stack.toString().split('\n')[0];
|
||||
e.stackTrace = stack;
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
void functionThatThrowsNonError() {
|
||||
try {
|
||||
throw new NonError();
|
||||
} catch (e, stack) {
|
||||
// If we lose the stack trace the message will no longer match
|
||||
// the first line in the stack
|
||||
e.message = stack.toString().split('\n')[0];
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
main() {
|
||||
describe('TypeLiteral', () {
|
||||
it(
|
||||
'should publish via viewBindings',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tb, async) {
|
||||
tb
|
||||
.overrideView(
|
||||
Dummy,
|
||||
new ViewMetadata(
|
||||
template:
|
||||
'<type-literal-component></type-literal-component>',
|
||||
directives: [TypeLiteralComponent]))
|
||||
.createAsync(Dummy)
|
||||
.then((tc) {
|
||||
tc.detectChanges();
|
||||
expect(asNativeElements(tc.debugElement.componentViewChildren))
|
||||
.toHaveText('[Hello, World]');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
describe('Error handling', () {
|
||||
it(
|
||||
'should preserve Error stack traces thrown from components',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tb, async) {
|
||||
tb
|
||||
.overrideView(
|
||||
Dummy,
|
||||
new ViewMetadata(
|
||||
template: '<throwing-component></throwing-component>',
|
||||
directives: [ThrowingComponent]))
|
||||
.createAsync(Dummy)
|
||||
.catchError((e, stack) {
|
||||
expect(e).toContainError("MockException");
|
||||
expect(e).toContainError("functionThatThrows");
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it(
|
||||
'should preserve non-Error stack traces thrown from components',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tb, async) {
|
||||
tb
|
||||
.overrideView(
|
||||
Dummy,
|
||||
new ViewMetadata(
|
||||
template: '<throwing-component2></throwing-component2>',
|
||||
directives: [ThrowingComponent2]))
|
||||
.createAsync(Dummy)
|
||||
.catchError((e, stack) {
|
||||
expect(e).toContainError("NonError");
|
||||
expect(e).toContainError("functionThatThrows");
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
describe('Property access', () {
|
||||
it(
|
||||
'should distinguish between map and property access',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tb, async) {
|
||||
tb
|
||||
.overrideView(
|
||||
Dummy,
|
||||
new ViewMetadata(
|
||||
template: '<property-access></property-access>',
|
||||
directives: [PropertyAccess]))
|
||||
.createAsync(Dummy)
|
||||
.then((tc) {
|
||||
tc.detectChanges();
|
||||
expect(asNativeElements(tc.debugElement.componentViewChildren))
|
||||
.toHaveText('prop:foo-prop;map:foo-map');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it(
|
||||
'should not fallback on map access if property missing',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tb, async) {
|
||||
tb
|
||||
.overrideView(
|
||||
Dummy,
|
||||
new ViewMetadata(
|
||||
template: '<no-property-access></no-property-access>',
|
||||
directives: [NoPropertyAccess]))
|
||||
.createAsync(Dummy)
|
||||
.then((tc) {
|
||||
expect(() => tc.detectChanges())
|
||||
.toThrowError(new RegExp('property not found'));
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
describe('OnChange', () {
|
||||
it(
|
||||
'should be notified of changes',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tb, async) {
|
||||
tb
|
||||
.overrideView(
|
||||
Dummy,
|
||||
new ViewMetadata(
|
||||
template: '''<on-change [prop]="'hello'"></on-change>''',
|
||||
directives: [OnChangeComponent]))
|
||||
.createAsync(Dummy)
|
||||
.then((tc) {
|
||||
tc.detectChanges();
|
||||
var cmp = tc.debugElement.componentViewChildren[0].inject(OnChangeComponent);
|
||||
expect(cmp.prop).toEqual('hello');
|
||||
expect(cmp.changes.containsKey('prop')).toEqual(true);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
describe("ObservableListDiff", () {
|
||||
it(
|
||||
'should be notified of changes',
|
||||
inject([TestComponentBuilder, Log],
|
||||
fakeAsync((TestComponentBuilder tcb, Log log) {
|
||||
tcb
|
||||
.overrideView(
|
||||
Dummy,
|
||||
new ViewMetadata(
|
||||
template:
|
||||
'''<component-with-observable-list [list]="value"></component-with-observable-list>''',
|
||||
directives: [ComponentWithObservableList]))
|
||||
.createAsync(Dummy)
|
||||
.then((tc) {
|
||||
tc.debugElement.componentInstance.value = new ObservableList.from([1, 2]);
|
||||
|
||||
tc.detectChanges();
|
||||
|
||||
expect(log.result()).toEqual("check");
|
||||
expect(asNativeElements(tc.debugElement.componentViewChildren)).toHaveText('12');
|
||||
|
||||
tc.detectChanges();
|
||||
|
||||
// we did not change the list => no checks
|
||||
expect(log.result()).toEqual("check");
|
||||
|
||||
tc.debugElement.componentInstance.value.add(3);
|
||||
|
||||
flushMicrotasks();
|
||||
|
||||
tc.detectChanges();
|
||||
|
||||
// we changed the list => a check
|
||||
expect(log.result()).toEqual("check; check");
|
||||
expect(asNativeElements(tc.debugElement.componentViewChildren))
|
||||
.toHaveText('123');
|
||||
|
||||
// we replaced the list => a check
|
||||
tc.debugElement.componentInstance.value = new ObservableList.from([5, 6, 7]);
|
||||
|
||||
tc.detectChanges();
|
||||
|
||||
expect(log.result()).toEqual("check; check; check");
|
||||
expect(asNativeElements(tc.debugElement.componentViewChildren))
|
||||
.toHaveText('567');
|
||||
});
|
||||
})));
|
||||
});
|
||||
}
|
||||
|
||||
@Component(selector: 'dummy')
|
||||
class Dummy {
|
||||
dynamic value;
|
||||
}
|
||||
|
||||
@Component(
|
||||
selector: 'type-literal-component',
|
||||
viewBindings: const [
|
||||
const Binding(const TypeLiteral<List<String>>(),
|
||||
toValue: const <String>['Hello', 'World'])
|
||||
])
|
||||
@View(template: '{{list}}')
|
||||
class TypeLiteralComponent {
|
||||
final List<String> list;
|
||||
|
||||
TypeLiteralComponent(this.list);
|
||||
}
|
||||
|
||||
@Component(selector: 'throwing-component')
|
||||
@View(template: '')
|
||||
class ThrowingComponent {
|
||||
ThrowingComponent() {
|
||||
functionThatThrows();
|
||||
}
|
||||
}
|
||||
|
||||
@Component(selector: 'throwing-component2')
|
||||
@View(template: '')
|
||||
class ThrowingComponent2 {
|
||||
ThrowingComponent2() {
|
||||
functionThatThrowsNonError();
|
||||
}
|
||||
}
|
||||
|
||||
@proxy()
|
||||
class PropModel implements Map {
|
||||
final String foo = 'foo-prop';
|
||||
|
||||
operator [](_) => 'foo-map';
|
||||
|
||||
noSuchMethod(_) {
|
||||
throw 'property not found';
|
||||
}
|
||||
}
|
||||
|
||||
@Component(selector: 'property-access')
|
||||
@View(template: '''prop:{{model.foo}};map:{{model['foo']}}''')
|
||||
class PropertyAccess {
|
||||
final model = new PropModel();
|
||||
}
|
||||
|
||||
@Component(selector: 'no-property-access')
|
||||
@View(template: '''{{model.doesNotExist}}''')
|
||||
class NoPropertyAccess {
|
||||
final model = new PropModel();
|
||||
}
|
||||
|
||||
@Component(
|
||||
selector: 'on-change',
|
||||
inputs: const ['prop'])
|
||||
@View(template: '')
|
||||
class OnChangeComponent implements OnChanges {
|
||||
Map changes;
|
||||
String prop;
|
||||
|
||||
@override
|
||||
void onChanges(Map changes) {
|
||||
this.changes = changes;
|
||||
}
|
||||
}
|
||||
|
||||
@Component(
|
||||
selector: 'component-with-observable-list',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
inputs: const ['list'],
|
||||
bindings: const [
|
||||
const Binding(IterableDiffers,
|
||||
toValue: const IterableDiffers(const [
|
||||
const ObservableListDiffFactory(),
|
||||
const DefaultIterableDifferFactory()
|
||||
]))
|
||||
])
|
||||
@View(
|
||||
template:
|
||||
'<span *ng-for="#item of list">{{item}}</span><directive-logging-checks></directive-logging-checks>',
|
||||
directives: const [NgFor, DirectiveLoggingChecks])
|
||||
class ComponentWithObservableList {
|
||||
Iterable list;
|
||||
}
|
||||
|
||||
@Directive(
|
||||
selector: 'directive-logging-checks')
|
||||
class DirectiveLoggingChecks implements DoCheck {
|
||||
Log log;
|
||||
|
||||
DirectiveLoggingChecks(this.log);
|
||||
|
||||
doCheck() => log.add("check");
|
||||
}
|
2268
modules/angular2/test/core/linker/integration_spec.ts
Normal file
2268
modules/angular2/test/core/linker/integration_spec.ts
Normal file
File diff suppressed because it is too large
Load Diff
565
modules/angular2/test/core/linker/projection_integration_spec.ts
Normal file
565
modules/angular2/test/core/linker/projection_integration_spec.ts
Normal file
@ -0,0 +1,565 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
xdescribe,
|
||||
describe,
|
||||
el,
|
||||
dispatchEvent,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
beforeEachBindings,
|
||||
it,
|
||||
xit,
|
||||
containsRegexp,
|
||||
stringifyElement,
|
||||
TestComponentBuilder,
|
||||
RootTestComponent,
|
||||
fakeAsync,
|
||||
tick
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import {DOM} from 'angular2/src/core/dom/dom_adapter';
|
||||
|
||||
import {
|
||||
bind,
|
||||
forwardRef,
|
||||
Component,
|
||||
Directive,
|
||||
ElementRef,
|
||||
TemplateRef,
|
||||
View,
|
||||
ViewContainerRef,
|
||||
ViewEncapsulation,
|
||||
ViewMetadata
|
||||
} from 'angular2/core';
|
||||
import {By} from 'angular2/src/core/debug';
|
||||
|
||||
export function main() {
|
||||
describe('projection', () => {
|
||||
it('should support simple components',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MainComp, new ViewMetadata({
|
||||
template: '<simple>' +
|
||||
'<div>A</div>' +
|
||||
'</simple>',
|
||||
directives: [Simple]
|
||||
}))
|
||||
.createAsync(MainComp)
|
||||
.then((main) => {
|
||||
expect(main.debugElement.nativeElement).toHaveText('SIMPLE(A)');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should support simple components with text interpolation as direct children',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MainComp, new ViewMetadata({
|
||||
template: '{{\'START(\'}}<simple>' +
|
||||
'{{text}}' +
|
||||
'</simple>{{\')END\'}}',
|
||||
directives: [Simple]
|
||||
}))
|
||||
.createAsync(MainComp)
|
||||
.then((main) => {
|
||||
|
||||
main.debugElement.componentInstance.text = 'A';
|
||||
main.detectChanges();
|
||||
expect(main.debugElement.nativeElement).toHaveText('START(SIMPLE(A))END');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should support projecting text interpolation to a non bound element',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(
|
||||
Simple,
|
||||
new ViewMetadata(
|
||||
{template: 'SIMPLE(<div><ng-content></ng-content></div>)', directives: []}))
|
||||
.overrideView(
|
||||
MainComp,
|
||||
new ViewMetadata({template: '<simple>{{text}}</simple>', directives: [Simple]}))
|
||||
.createAsync(MainComp)
|
||||
.then((main) => {
|
||||
|
||||
main.debugElement.componentInstance.text = 'A';
|
||||
main.detectChanges();
|
||||
expect(main.debugElement.nativeElement).toHaveText('SIMPLE(A)');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
it('should support projecting text interpolation to a non bound element with other bound elements after it',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(
|
||||
Simple, new ViewMetadata({
|
||||
template:
|
||||
'SIMPLE(<div><ng-content></ng-content></div><div [tab-index]="0">EL</div>)',
|
||||
directives: []
|
||||
}))
|
||||
.overrideView(
|
||||
MainComp,
|
||||
new ViewMetadata({template: '<simple>{{text}}</simple>', directives: [Simple]}))
|
||||
.createAsync(MainComp)
|
||||
.then((main) => {
|
||||
|
||||
main.debugElement.componentInstance.text = 'A';
|
||||
main.detectChanges();
|
||||
expect(main.debugElement.nativeElement).toHaveText('SIMPLE(AEL)');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should not show the light dom even if there is no content tag',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MainComp,
|
||||
new ViewMetadata({template: '<empty>A</empty>', directives: [Empty]}))
|
||||
.createAsync(MainComp)
|
||||
.then((main) => {
|
||||
|
||||
expect(main.debugElement.nativeElement).toHaveText('');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should support multiple content tags',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MainComp, new ViewMetadata({
|
||||
template: '<multiple-content-tags>' +
|
||||
'<div>B</div>' +
|
||||
'<div>C</div>' +
|
||||
'<div class="left">A</div>' +
|
||||
'</multiple-content-tags>',
|
||||
directives: [MultipleContentTagsComponent]
|
||||
}))
|
||||
.createAsync(MainComp)
|
||||
.then((main) => {
|
||||
|
||||
expect(main.debugElement.nativeElement).toHaveText('(A, BC)');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should redistribute only direct children',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MainComp, new ViewMetadata({
|
||||
template: '<multiple-content-tags>' +
|
||||
'<div>B<div class="left">A</div></div>' +
|
||||
'<div>C</div>' +
|
||||
'</multiple-content-tags>',
|
||||
directives: [MultipleContentTagsComponent]
|
||||
}))
|
||||
.createAsync(MainComp)
|
||||
.then((main) => {
|
||||
|
||||
expect(main.debugElement.nativeElement).toHaveText('(, BAC)');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it("should redistribute direct child viewcontainers when the light dom changes",
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MainComp, new ViewMetadata({
|
||||
template: '<multiple-content-tags>' +
|
||||
'<template manual class="left"><div>A1</div></template>' +
|
||||
'<div>B</div>' +
|
||||
'</multiple-content-tags>',
|
||||
directives: [MultipleContentTagsComponent, ManualViewportDirective]
|
||||
}))
|
||||
.createAsync(MainComp)
|
||||
.then((main) => {
|
||||
|
||||
var viewportDirectives =
|
||||
main.debugElement.queryAll(By.directive(ManualViewportDirective))
|
||||
.map(de => de.inject(ManualViewportDirective));
|
||||
|
||||
expect(main.debugElement.nativeElement).toHaveText('(, B)');
|
||||
viewportDirectives.forEach(d => d.show());
|
||||
expect(main.debugElement.nativeElement).toHaveText('(A1, B)');
|
||||
|
||||
viewportDirectives.forEach(d => d.hide());
|
||||
|
||||
expect(main.debugElement.nativeElement).toHaveText('(, B)');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it("should support nested components",
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MainComp, new ViewMetadata({
|
||||
template: '<outer-with-indirect-nested>' +
|
||||
'<div>A</div>' +
|
||||
'<div>B</div>' +
|
||||
'</outer-with-indirect-nested>',
|
||||
directives: [OuterWithIndirectNestedComponent]
|
||||
}))
|
||||
.createAsync(MainComp)
|
||||
.then((main) => {
|
||||
|
||||
expect(main.debugElement.nativeElement).toHaveText('OUTER(SIMPLE(AB))');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it("should support nesting with content being direct child of a nested component",
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MainComp, new ViewMetadata({
|
||||
template: '<outer>' +
|
||||
'<template manual class="left"><div>A</div></template>' +
|
||||
'<div>B</div>' +
|
||||
'<div>C</div>' +
|
||||
'</outer>',
|
||||
directives: [OuterComponent, ManualViewportDirective],
|
||||
}))
|
||||
.createAsync(MainComp)
|
||||
.then((main) => {
|
||||
|
||||
var viewportDirective =
|
||||
main.debugElement.query(By.directive(ManualViewportDirective))
|
||||
.inject(ManualViewportDirective);
|
||||
|
||||
expect(main.debugElement.nativeElement).toHaveText('OUTER(INNER(INNERINNER(,BC)))');
|
||||
viewportDirective.show();
|
||||
|
||||
expect(main.debugElement.nativeElement).toHaveText('OUTER(INNER(INNERINNER(A,BC)))');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should redistribute when the shadow dom changes',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MainComp, new ViewMetadata({
|
||||
template: '<conditional-content>' +
|
||||
'<div class="left">A</div>' +
|
||||
'<div>B</div>' +
|
||||
'<div>C</div>' +
|
||||
'</conditional-content>',
|
||||
directives: [ConditionalContentComponent]
|
||||
}))
|
||||
.createAsync(MainComp)
|
||||
.then((main) => {
|
||||
|
||||
var viewportDirective =
|
||||
main.debugElement.query(By.directive(ManualViewportDirective))
|
||||
.inject(ManualViewportDirective);
|
||||
|
||||
expect(main.debugElement.nativeElement).toHaveText('(, BC)');
|
||||
|
||||
viewportDirective.show();
|
||||
expect(main.debugElement.nativeElement).toHaveText('(A, BC)');
|
||||
|
||||
viewportDirective.hide();
|
||||
|
||||
expect(main.debugElement.nativeElement).toHaveText('(, BC)');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
// GH-2095 - https://github.com/angular/angular/issues/2095
|
||||
// important as we are removing the ng-content element during compilation,
|
||||
// which could skrew up text node indices.
|
||||
it('should support text nodes after content tags',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb, async) => {
|
||||
|
||||
tcb.overrideView(
|
||||
MainComp,
|
||||
new ViewMetadata(
|
||||
{template: '<simple string-prop="text"></simple>', directives: [Simple]}))
|
||||
.overrideTemplate(Simple, '<ng-content></ng-content><p>P,</p>{{stringProp}}')
|
||||
.createAsync(MainComp)
|
||||
.then((main: RootTestComponent) => {
|
||||
|
||||
main.detectChanges();
|
||||
|
||||
expect(main.debugElement.nativeElement).toHaveText('P,text');
|
||||
async.done();
|
||||
});
|
||||
|
||||
}));
|
||||
|
||||
// important as we are moving style tags around during compilation,
|
||||
// which could skrew up text node indices.
|
||||
it('should support text nodes after style tags',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb, async) => {
|
||||
|
||||
tcb.overrideView(
|
||||
MainComp,
|
||||
new ViewMetadata(
|
||||
{template: '<simple string-prop="text"></simple>', directives: [Simple]}))
|
||||
.overrideTemplate(Simple, '<style></style><p>P,</p>{{stringProp}}')
|
||||
.createAsync(MainComp)
|
||||
.then((main: RootTestComponent) => {
|
||||
|
||||
main.detectChanges();
|
||||
expect(main.debugElement.nativeElement).toHaveText('P,text');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should support moving non projected light dom around',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MainComp, new ViewMetadata({
|
||||
template: '<empty>' +
|
||||
' <template manual><div>A</div></template>' +
|
||||
'</empty>' +
|
||||
'START(<div project></div>)END',
|
||||
directives: [Empty, ProjectDirective, ManualViewportDirective],
|
||||
}))
|
||||
.createAsync(MainComp)
|
||||
.then((main) => {
|
||||
|
||||
var sourceDirective: ManualViewportDirective =
|
||||
main.debugElement.query(By.directive(ManualViewportDirective))
|
||||
.inject(ManualViewportDirective);
|
||||
var projectDirective: ProjectDirective =
|
||||
main.debugElement.query(By.directive(ProjectDirective)).inject(ProjectDirective);
|
||||
expect(main.debugElement.nativeElement).toHaveText('START()END');
|
||||
|
||||
projectDirective.show(sourceDirective.templateRef);
|
||||
expect(main.debugElement.nativeElement).toHaveText('START(A)END');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should support moving projected light dom around',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MainComp, new ViewMetadata({
|
||||
template: '<simple><template manual><div>A</div></template></simple>' +
|
||||
'START(<div project></div>)END',
|
||||
directives: [Simple, ProjectDirective, ManualViewportDirective],
|
||||
}))
|
||||
.createAsync(MainComp)
|
||||
.then((main) => {
|
||||
|
||||
var sourceDirective: ManualViewportDirective =
|
||||
main.debugElement.query(By.directive(ManualViewportDirective))
|
||||
.inject(ManualViewportDirective);
|
||||
var projectDirective: ProjectDirective =
|
||||
main.debugElement.query(By.directive(ProjectDirective)).inject(ProjectDirective);
|
||||
expect(main.debugElement.nativeElement).toHaveText('SIMPLE()START()END');
|
||||
|
||||
projectDirective.show(sourceDirective.templateRef);
|
||||
expect(main.debugElement.nativeElement).toHaveText('SIMPLE()START(A)END');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should support moving ng-content around',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(
|
||||
MainComp, new ViewMetadata({
|
||||
template: '<conditional-content>' +
|
||||
'<div class="left">A</div>' +
|
||||
'<div>B</div>' +
|
||||
'</conditional-content>' +
|
||||
'START(<div project></div>)END',
|
||||
directives:
|
||||
[ConditionalContentComponent, ProjectDirective, ManualViewportDirective]
|
||||
}))
|
||||
.createAsync(MainComp)
|
||||
.then((main) => {
|
||||
|
||||
var sourceDirective: ManualViewportDirective =
|
||||
main.debugElement.query(By.directive(ManualViewportDirective))
|
||||
.inject(ManualViewportDirective);
|
||||
var projectDirective: ProjectDirective =
|
||||
main.debugElement.query(By.directive(ProjectDirective)).inject(ProjectDirective);
|
||||
expect(main.debugElement.nativeElement).toHaveText('(, B)START()END');
|
||||
|
||||
projectDirective.show(sourceDirective.templateRef);
|
||||
expect(main.debugElement.nativeElement).toHaveText('(, B)START(A)END');
|
||||
|
||||
// Stamping ng-content multiple times should not produce the content multiple
|
||||
// times...
|
||||
projectDirective.show(sourceDirective.templateRef);
|
||||
expect(main.debugElement.nativeElement).toHaveText('(, B)START(A)END');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
// Note: This does not use a ng-content element, but
|
||||
// is still important as we are merging proto views independent of
|
||||
// the presence of ng-content elements!
|
||||
it('should still allow to implement a recursive trees',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MainComp,
|
||||
new ViewMetadata({template: '<tree></tree>', directives: [Tree]}))
|
||||
.createAsync(MainComp)
|
||||
.then((main) => {
|
||||
|
||||
main.detectChanges();
|
||||
var manualDirective: ManualViewportDirective =
|
||||
main.debugElement.query(By.directive(ManualViewportDirective))
|
||||
.inject(ManualViewportDirective);
|
||||
expect(main.debugElement.nativeElement).toHaveText('TREE(0:)');
|
||||
manualDirective.show();
|
||||
main.detectChanges();
|
||||
expect(main.debugElement.nativeElement).toHaveText('TREE(0:TREE(1:))');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
if (DOM.supportsNativeShadowDOM()) {
|
||||
it('should support native content projection',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MainComp, new ViewMetadata({
|
||||
template: '<simple-native>' +
|
||||
'<div>A</div>' +
|
||||
'</simple-native>',
|
||||
directives: [SimpleNative]
|
||||
}))
|
||||
.createAsync(MainComp)
|
||||
.then((main) => {
|
||||
|
||||
expect(main.debugElement.nativeElement).toHaveText('SIMPLE(A)');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
it('should support nested conditionals that contain ng-contents',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MainComp, new ViewMetadata({
|
||||
template: `<conditional-text>a</conditional-text>`,
|
||||
directives: [ConditionalTextComponent]
|
||||
}))
|
||||
.createAsync(MainComp)
|
||||
.then((main) => {
|
||||
expect(main.debugElement.nativeElement).toHaveText('MAIN()');
|
||||
|
||||
var viewportElement =
|
||||
main.debugElement.componentViewChildren[0].componentViewChildren[0];
|
||||
viewportElement.inject(ManualViewportDirective).show();
|
||||
expect(main.debugElement.nativeElement).toHaveText('MAIN(FIRST())');
|
||||
|
||||
viewportElement =
|
||||
main.debugElement.componentViewChildren[0].componentViewChildren[1];
|
||||
viewportElement.inject(ManualViewportDirective).show();
|
||||
expect(main.debugElement.nativeElement).toHaveText('MAIN(FIRST(SECOND(a)))');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Component({selector: 'main'})
|
||||
@View({template: '', directives: []})
|
||||
class MainComp {
|
||||
text: string = '';
|
||||
}
|
||||
|
||||
@Component({selector: 'simple', inputs: ['stringProp']})
|
||||
@View({template: 'SIMPLE(<ng-content></ng-content>)', directives: []})
|
||||
class Simple {
|
||||
stringProp: string = '';
|
||||
}
|
||||
|
||||
@Component({selector: 'simple-native'})
|
||||
@View({
|
||||
template: 'SIMPLE(<content></content>)',
|
||||
directives: [],
|
||||
encapsulation: ViewEncapsulation.Native
|
||||
})
|
||||
class SimpleNative {
|
||||
}
|
||||
|
||||
@Component({selector: 'empty'})
|
||||
@View({template: '', directives: []})
|
||||
class Empty {
|
||||
}
|
||||
|
||||
@Component({selector: 'multiple-content-tags'})
|
||||
@View({
|
||||
template: '(<ng-content select=".left"></ng-content>, <ng-content></ng-content>)',
|
||||
directives: []
|
||||
})
|
||||
class MultipleContentTagsComponent {
|
||||
}
|
||||
|
||||
@Directive({selector: '[manual]'})
|
||||
class ManualViewportDirective {
|
||||
constructor(public vc: ViewContainerRef, public templateRef: TemplateRef) {}
|
||||
show() { this.vc.createEmbeddedView(this.templateRef, 0); }
|
||||
hide() { this.vc.clear(); }
|
||||
}
|
||||
|
||||
@Directive({selector: '[project]'})
|
||||
class ProjectDirective {
|
||||
constructor(public vc: ViewContainerRef) {}
|
||||
show(templateRef: TemplateRef) { this.vc.createEmbeddedView(templateRef, 0); }
|
||||
hide() { this.vc.clear(); }
|
||||
}
|
||||
|
||||
@Component({selector: 'outer-with-indirect-nested'})
|
||||
@View({
|
||||
template: 'OUTER(<simple><div><ng-content></ng-content></div></simple>)',
|
||||
directives: [Simple]
|
||||
})
|
||||
class OuterWithIndirectNestedComponent {
|
||||
}
|
||||
|
||||
@Component({selector: 'outer'})
|
||||
@View({
|
||||
template:
|
||||
'OUTER(<inner><ng-content select=".left" class="left"></ng-content><ng-content></ng-content></inner>)',
|
||||
directives: [forwardRef(() => InnerComponent)]
|
||||
})
|
||||
class OuterComponent {
|
||||
}
|
||||
|
||||
@Component({selector: 'inner'})
|
||||
@View({
|
||||
template:
|
||||
'INNER(<innerinner><ng-content select=".left" class="left"></ng-content><ng-content></ng-content></innerinner>)',
|
||||
directives: [forwardRef(() => InnerInnerComponent)]
|
||||
})
|
||||
class InnerComponent {
|
||||
}
|
||||
|
||||
@Component({selector: 'innerinner'})
|
||||
@View({
|
||||
template: 'INNERINNER(<ng-content select=".left"></ng-content>,<ng-content></ng-content>)',
|
||||
directives: []
|
||||
})
|
||||
class InnerInnerComponent {
|
||||
}
|
||||
|
||||
@Component({selector: 'conditional-content'})
|
||||
@View({
|
||||
template:
|
||||
'<div>(<div *manual><ng-content select=".left"></ng-content></div>, <ng-content></ng-content>)</div>',
|
||||
directives: [ManualViewportDirective]
|
||||
})
|
||||
class ConditionalContentComponent {
|
||||
}
|
||||
|
||||
@Component({selector: 'conditional-text'})
|
||||
@View({
|
||||
template:
|
||||
'MAIN(<template manual>FIRST(<template manual>SECOND(<ng-content></ng-content>)</template>)</template>)',
|
||||
directives: [ManualViewportDirective]
|
||||
})
|
||||
class ConditionalTextComponent {
|
||||
}
|
||||
|
||||
@Component({selector: 'tab'})
|
||||
@View({
|
||||
template: '<div><div *manual>TAB(<ng-content></ng-content>)</div></div>',
|
||||
directives: [ManualViewportDirective]
|
||||
})
|
||||
class Tab {
|
||||
}
|
||||
|
||||
@Component({selector: 'tree', inputs: ['depth']})
|
||||
@View({
|
||||
template: 'TREE({{depth}}:<tree *manual [depth]="depth+1"></tree>)',
|
||||
directives: [ManualViewportDirective, Tree]
|
||||
})
|
||||
class Tree {
|
||||
depth = 0;
|
||||
}
|
19
modules/angular2/test/core/linker/proto_view_factory_spec.ts
Normal file
19
modules/angular2/test/core/linker/proto_view_factory_spec.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
xdescribe,
|
||||
ddescribe,
|
||||
describe,
|
||||
el,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
export function main() {
|
||||
describe('ProtoViewFactory', () => {
|
||||
// TODO
|
||||
|
||||
});
|
||||
}
|
943
modules/angular2/test/core/linker/query_integration_spec.ts
Normal file
943
modules/angular2/test/core/linker/query_integration_spec.ts
Normal file
@ -0,0 +1,943 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
el,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
TestComponentBuilder,
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import {isPresent} from 'angular2/src/core/facade/lang';
|
||||
import {ObservableWrapper} from 'angular2/src/core/facade/async';
|
||||
|
||||
import {
|
||||
Component,
|
||||
Directive,
|
||||
Injectable,
|
||||
NgIf,
|
||||
NgFor,
|
||||
Optional,
|
||||
TemplateRef,
|
||||
Query,
|
||||
QueryList,
|
||||
View,
|
||||
ViewQuery,
|
||||
ContentChildren,
|
||||
ViewChildren,
|
||||
ContentChild,
|
||||
ViewChild,
|
||||
AfterContentInit,
|
||||
AfterViewInit,
|
||||
AfterContentChecked,
|
||||
AfterViewChecked
|
||||
} from 'angular2/core';
|
||||
|
||||
import {asNativeElements} from 'angular2/src/core/debug';
|
||||
|
||||
import {BrowserDomAdapter} from 'angular2/src/core/dom/browser_adapter';
|
||||
|
||||
export function main() {
|
||||
BrowserDomAdapter.makeCurrent();
|
||||
describe('Query API', () => {
|
||||
describe("querying by directive type", () => {
|
||||
it('should contain all direct child directives in the light dom (constructor)',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<div text="1"></div>' +
|
||||
'<needs-query text="2"><div text="3">' +
|
||||
'<div text="too-deep"></div>' +
|
||||
'</div></needs-query>' +
|
||||
'<div text="4"></div>';
|
||||
|
||||
tcb.overrideTemplate(MyComp, template)
|
||||
.createAsync(MyComp)
|
||||
.then((view) => {
|
||||
view.detectChanges();
|
||||
|
||||
expect(asNativeElements(view.debugElement.componentViewChildren))
|
||||
.toHaveText('2|3|');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should contain all direct child directives in the content dom',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template =
|
||||
'<needs-content-children #q><div text="foo"></div></needs-content-children>';
|
||||
|
||||
tcb.overrideTemplate(MyComp, template)
|
||||
.createAsync(MyComp)
|
||||
.then((view) => {
|
||||
view.detectChanges();
|
||||
|
||||
var q = view.debugElement.componentViewChildren[0].getLocal('q');
|
||||
|
||||
view.detectChanges();
|
||||
|
||||
expect(q.textDirChildren.length).toEqual(1);
|
||||
expect(q.numberOfChildrenAfterContentInit).toEqual(1);
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should contain the first content child',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template =
|
||||
'<needs-content-child #q><div *ng-if="shouldShow" text="foo"></div></needs-content-child>';
|
||||
|
||||
tcb.overrideTemplate(MyComp, template)
|
||||
.createAsync(MyComp)
|
||||
.then((view) => {
|
||||
view.debugElement.componentInstance.shouldShow = true;
|
||||
view.detectChanges();
|
||||
|
||||
var q = view.debugElement.componentViewChildren[0].getLocal('q');
|
||||
|
||||
expect(q.log).toEqual([["setter", "foo"], ["init", "foo"], ["check", "foo"]]);
|
||||
|
||||
view.debugElement.componentInstance.shouldShow = false;
|
||||
view.detectChanges();
|
||||
|
||||
expect(q.log).toEqual([
|
||||
["setter", "foo"],
|
||||
["init", "foo"],
|
||||
["check", "foo"],
|
||||
["setter", null],
|
||||
["check", null]
|
||||
]);
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should contain the first view child',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<needs-view-child #q></needs-view-child>';
|
||||
|
||||
tcb.overrideTemplate(MyComp, template)
|
||||
.createAsync(MyComp)
|
||||
.then((view) => {
|
||||
view.detectChanges();
|
||||
var q = view.debugElement.componentViewChildren[0].getLocal('q');
|
||||
|
||||
expect(q.log).toEqual([["setter", "foo"], ["init", "foo"], ["check", "foo"]]);
|
||||
|
||||
q.shouldShow = false;
|
||||
view.detectChanges();
|
||||
|
||||
expect(q.log).toEqual([
|
||||
["setter", "foo"],
|
||||
["init", "foo"],
|
||||
["check", "foo"],
|
||||
["setter", null],
|
||||
["check", null]
|
||||
]);
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should contain all directives in the light dom when descendants flag is used',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<div text="1"></div>' +
|
||||
'<needs-query-desc text="2"><div text="3">' +
|
||||
'<div text="4"></div>' +
|
||||
'</div></needs-query-desc>' +
|
||||
'<div text="5"></div>';
|
||||
|
||||
tcb.overrideTemplate(MyComp, template)
|
||||
.createAsync(MyComp)
|
||||
.then((view) => {
|
||||
view.detectChanges();
|
||||
expect(asNativeElements(view.debugElement.componentViewChildren))
|
||||
.toHaveText('2|3|4|');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should contain all directives in the light dom',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<div text="1"></div>' +
|
||||
'<needs-query text="2"><div text="3"></div></needs-query>' +
|
||||
'<div text="4"></div>';
|
||||
|
||||
tcb.overrideTemplate(MyComp, template)
|
||||
.createAsync(MyComp)
|
||||
.then((view) => {
|
||||
view.detectChanges();
|
||||
expect(asNativeElements(view.debugElement.componentViewChildren))
|
||||
.toHaveText('2|3|');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should reflect dynamically inserted directives',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template =
|
||||
'<div text="1"></div>' +
|
||||
'<needs-query text="2"><div *ng-if="shouldShow" [text]="\'3\'"></div></needs-query>' +
|
||||
'<div text="4"></div>';
|
||||
|
||||
tcb.overrideTemplate(MyComp, template)
|
||||
.createAsync(MyComp)
|
||||
.then((view) => {
|
||||
|
||||
view.detectChanges();
|
||||
expect(asNativeElements(view.debugElement.componentViewChildren)).toHaveText('2|');
|
||||
|
||||
view.debugElement.componentInstance.shouldShow = true;
|
||||
view.detectChanges();
|
||||
expect(asNativeElements(view.debugElement.componentViewChildren))
|
||||
.toHaveText('2|3|');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should be cleanly destroyed when a query crosses view boundaries',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template =
|
||||
'<div text="1"></div>' +
|
||||
'<needs-query text="2"><div *ng-if="shouldShow" [text]="\'3\'"></div></needs-query>' +
|
||||
'<div text="4"></div>';
|
||||
|
||||
tcb.overrideTemplate(MyComp, template)
|
||||
.createAsync(MyComp)
|
||||
.then((rtc) => {
|
||||
rtc.debugElement.componentInstance.shouldShow = true;
|
||||
rtc.detectChanges();
|
||||
rtc.destroy();
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should reflect moved directives',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template =
|
||||
'<div text="1"></div>' +
|
||||
'<needs-query text="2"><div *ng-for="var i of list" [text]="i"></div></needs-query>' +
|
||||
'<div text="4"></div>';
|
||||
|
||||
tcb.overrideTemplate(MyComp, template)
|
||||
.createAsync(MyComp)
|
||||
.then((view) => {
|
||||
view.detectChanges();
|
||||
|
||||
expect(asNativeElements(view.debugElement.componentViewChildren))
|
||||
.toHaveText('2|1d|2d|3d|');
|
||||
|
||||
view.debugElement.componentInstance.list = ['3d', '2d'];
|
||||
view.detectChanges();
|
||||
expect(asNativeElements(view.debugElement.componentViewChildren))
|
||||
.toHaveText('2|3d|2d|');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
describe('query for TemplateRef', () => {
|
||||
it('should find TemplateRefs in the light and shadow dom',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<needs-tpl><template var-x="light"></template></needs-tpl>';
|
||||
tcb.overrideTemplate(MyComp, template)
|
||||
.createAsync(MyComp)
|
||||
.then((view) => {
|
||||
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);
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
describe("changes", () => {
|
||||
it('should notify query on change',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<needs-query #q>' +
|
||||
'<div text="1"></div>' +
|
||||
'<div *ng-if="shouldShow" text="2"></div>' +
|
||||
'</needs-query>';
|
||||
|
||||
tcb.overrideTemplate(MyComp, template)
|
||||
.createAsync(MyComp)
|
||||
.then((view) => {
|
||||
var q = view.debugElement.componentViewChildren[0].getLocal("q");
|
||||
view.detectChanges();
|
||||
|
||||
ObservableWrapper.subscribe(q.query.changes, (_) => {
|
||||
expect(q.query.first.text).toEqual("1");
|
||||
expect(q.query.last.text).toEqual("2");
|
||||
async.done();
|
||||
});
|
||||
|
||||
view.debugElement.componentInstance.shouldShow = true;
|
||||
view.detectChanges();
|
||||
});
|
||||
}));
|
||||
|
||||
it("should notify child's query before notifying parent's query",
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<needs-query-desc #q1>' +
|
||||
'<needs-query-desc #q2>' +
|
||||
'<div text="1"></div>' +
|
||||
'</needs-query-desc>' +
|
||||
'</needs-query-desc>';
|
||||
|
||||
tcb.overrideTemplate(MyComp, template)
|
||||
.createAsync(MyComp)
|
||||
.then((view) => {
|
||||
var q1 = view.debugElement.componentViewChildren[0].getLocal("q1");
|
||||
var q2 = view.debugElement.componentViewChildren[0].getLocal("q2");
|
||||
|
||||
var firedQ2 = false;
|
||||
|
||||
ObservableWrapper.subscribe(q2.query.changes, (_) => { firedQ2 = true; });
|
||||
ObservableWrapper.subscribe(q1.query.changes, (_) => {
|
||||
expect(firedQ2).toBe(true);
|
||||
async.done();
|
||||
});
|
||||
|
||||
view.detectChanges();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should correctly clean-up when destroyed together with the directives it is querying',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template =
|
||||
'<needs-query #q *ng-if="shouldShow"><div text="foo"></div></needs-query>';
|
||||
|
||||
tcb.overrideTemplate(MyComp, template)
|
||||
.createAsync(MyComp)
|
||||
.then((view) => {
|
||||
view.debugElement.componentInstance.shouldShow = true;
|
||||
view.detectChanges();
|
||||
|
||||
var q: NeedsQuery = view.debugElement.componentViewChildren[1].getLocal('q');
|
||||
expect(q.query.length).toEqual(1);
|
||||
|
||||
view.debugElement.componentInstance.shouldShow = false;
|
||||
view.detectChanges();
|
||||
|
||||
view.debugElement.componentInstance.shouldShow = true;
|
||||
view.detectChanges();
|
||||
|
||||
var q2: NeedsQuery = view.debugElement.componentViewChildren[1].getLocal('q');
|
||||
|
||||
expect(q2.query.length).toEqual(1);
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
describe("querying by var binding", () => {
|
||||
it('should contain all the child directives in the light dom with the given var binding',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template =
|
||||
'<needs-query-by-var-binding #q>' +
|
||||
'<div *ng-for="#item of list" [text]="item" #text-label="textDir"></div>' +
|
||||
'</needs-query-by-var-binding>';
|
||||
|
||||
tcb.overrideTemplate(MyComp, template)
|
||||
.createAsync(MyComp)
|
||||
.then((view) => {
|
||||
var q = view.debugElement.componentViewChildren[0].getLocal("q");
|
||||
|
||||
view.debugElement.componentInstance.list = ['1d', '2d'];
|
||||
|
||||
view.detectChanges();
|
||||
|
||||
expect(q.query.first.text).toEqual("1d");
|
||||
expect(q.query.last.text).toEqual("2d");
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should support querying by multiple var bindings',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<needs-query-by-var-bindings #q>' +
|
||||
'<div text="one" #text-label1="textDir"></div>' +
|
||||
'<div text="two" #text-label2="textDir"></div>' +
|
||||
'</needs-query-by-var-bindings>';
|
||||
|
||||
tcb.overrideTemplate(MyComp, template)
|
||||
.createAsync(MyComp)
|
||||
.then((view) => {
|
||||
var q = view.debugElement.componentViewChildren[0].getLocal("q");
|
||||
view.detectChanges();
|
||||
|
||||
expect(q.query.first.text).toEqual("one");
|
||||
expect(q.query.last.text).toEqual("two");
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should reflect dynamically inserted directives',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template =
|
||||
'<needs-query-by-var-binding #q>' +
|
||||
'<div *ng-for="#item of list" [text]="item" #text-label="textDir"></div>' +
|
||||
'</needs-query-by-var-binding>';
|
||||
|
||||
tcb.overrideTemplate(MyComp, template)
|
||||
.createAsync(MyComp)
|
||||
.then((view) => {
|
||||
var q = view.debugElement.componentViewChildren[0].getLocal("q");
|
||||
|
||||
view.debugElement.componentInstance.list = ['1d', '2d'];
|
||||
|
||||
view.detectChanges();
|
||||
|
||||
view.debugElement.componentInstance.list = ['2d', '1d'];
|
||||
|
||||
view.detectChanges();
|
||||
|
||||
expect(q.query.last.text).toEqual("1d");
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should contain all the elements in the light dom with the given var binding',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<needs-query-by-var-binding #q>' +
|
||||
'<div template="ng-for: #item of list">' +
|
||||
'<div #text-label>{{item}}</div>' +
|
||||
'</div>' +
|
||||
'</needs-query-by-var-binding>';
|
||||
|
||||
tcb.overrideTemplate(MyComp, template)
|
||||
.createAsync(MyComp)
|
||||
.then((view) => {
|
||||
var q = view.debugElement.componentViewChildren[0].getLocal("q");
|
||||
|
||||
view.debugElement.componentInstance.list = ['1d', '2d'];
|
||||
|
||||
view.detectChanges();
|
||||
|
||||
expect(q.query.first.nativeElement).toHaveText("1d");
|
||||
expect(q.query.last.nativeElement).toHaveText("2d");
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should contain all the elements in the light dom even if they get projected',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<needs-query-and-project #q>' +
|
||||
'<div text="hello"></div><div text="world"></div>' +
|
||||
'</needs-query-and-project>';
|
||||
|
||||
tcb.overrideTemplate(MyComp, template)
|
||||
.createAsync(MyComp)
|
||||
.then((view) => {
|
||||
view.detectChanges();
|
||||
|
||||
expect(asNativeElements(view.debugElement.componentViewChildren))
|
||||
.toHaveText('hello|world|');
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should support querying the view by using a view query',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<needs-view-query-by-var-binding #q></needs-view-query-by-var-binding>';
|
||||
|
||||
tcb.overrideTemplate(MyComp, template)
|
||||
.createAsync(MyComp)
|
||||
.then((view) => {
|
||||
var q: NeedsViewQueryByLabel =
|
||||
view.debugElement.componentViewChildren[0].getLocal("q");
|
||||
view.detectChanges();
|
||||
|
||||
expect(q.query.first.nativeElement).toHaveText("text");
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should contain all child directives in the view dom',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<needs-view-children #q></needs-view-children>';
|
||||
|
||||
tcb.overrideTemplate(MyComp, template)
|
||||
.createAsync(MyComp)
|
||||
.then((view) => {
|
||||
view.detectChanges();
|
||||
|
||||
var q = view.debugElement.componentViewChildren[0].getLocal('q');
|
||||
|
||||
view.detectChanges();
|
||||
|
||||
expect(q.textDirChildren.length).toEqual(1);
|
||||
expect(q.numberOfChildrenAfterViewInit).toEqual(1);
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
describe("querying in the view", () => {
|
||||
it('should contain all the elements in the view with that have the given directive',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<needs-view-query #q><div text="ignoreme"></div></needs-view-query>';
|
||||
|
||||
tcb.overrideTemplate(MyComp, template)
|
||||
.createAsync(MyComp)
|
||||
.then((view) => {
|
||||
var q: NeedsViewQuery = view.debugElement.componentViewChildren[0].getLocal("q");
|
||||
|
||||
view.detectChanges();
|
||||
|
||||
expect(q.query.map((d: TextDirective) => d.text)).toEqual(["1", "2", "3", "4"]);
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should not include directive present on the host element',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<needs-view-query #q text="self"></needs-view-query>';
|
||||
|
||||
tcb.overrideTemplate(MyComp, template)
|
||||
.createAsync(MyComp)
|
||||
.then((view) => {
|
||||
var q: NeedsViewQuery = view.debugElement.componentViewChildren[0].getLocal("q");
|
||||
|
||||
view.detectChanges();
|
||||
|
||||
expect(q.query.map((d: TextDirective) => d.text)).toEqual(["1", "2", "3", "4"]);
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should reflect changes in the component',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<needs-view-query-if #q></needs-view-query-if>';
|
||||
|
||||
tcb.overrideTemplate(MyComp, template)
|
||||
.createAsync(MyComp)
|
||||
.then((view) => {
|
||||
var q: NeedsViewQueryIf = view.debugElement.componentViewChildren[0].getLocal("q");
|
||||
|
||||
view.detectChanges();
|
||||
|
||||
expect(q.query.length).toBe(0);
|
||||
|
||||
q.show = true;
|
||||
view.detectChanges();
|
||||
expect(q.query.length).toBe(1);
|
||||
|
||||
expect(q.query.first.text).toEqual("1");
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should not be affected by other changes in the component',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<needs-view-query-nested-if #q></needs-view-query-nested-if>';
|
||||
|
||||
tcb.overrideTemplate(MyComp, template)
|
||||
.createAsync(MyComp)
|
||||
.then((view) => {
|
||||
var q: NeedsViewQueryNestedIf =
|
||||
view.debugElement.componentViewChildren[0].getLocal("q");
|
||||
|
||||
view.detectChanges();
|
||||
|
||||
expect(q.query.length).toEqual(1);
|
||||
expect(q.query.first.text).toEqual("1");
|
||||
|
||||
q.show = false;
|
||||
view.detectChanges();
|
||||
|
||||
expect(q.query.length).toEqual(1);
|
||||
expect(q.query.first.text).toEqual("1");
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
it('should maintain directives in pre-order depth-first DOM order after dynamic insertion',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<needs-view-query-order #q></needs-view-query-order>';
|
||||
|
||||
tcb.overrideTemplate(MyComp, template)
|
||||
.createAsync(MyComp)
|
||||
.then((view) => {
|
||||
var q: NeedsViewQueryOrder =
|
||||
view.debugElement.componentViewChildren[0].getLocal("q");
|
||||
|
||||
view.detectChanges();
|
||||
|
||||
expect(q.query.map((d: TextDirective) => d.text)).toEqual(["1", "2", "3", "4"]);
|
||||
|
||||
q.list = ["-3", "2"];
|
||||
view.detectChanges();
|
||||
|
||||
|
||||
expect(q.query.map((d: TextDirective) => d.text)).toEqual(["1", "-3", "2", "4"]);
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should maintain directives in pre-order depth-first DOM order after dynamic insertion',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<needs-view-query-order-with-p #q></needs-view-query-order-with-p>';
|
||||
|
||||
tcb.overrideTemplate(MyComp, template)
|
||||
.createAsync(MyComp)
|
||||
.then((view) => {
|
||||
var q: NeedsViewQueryOrderWithParent =
|
||||
view.debugElement.componentViewChildren[0].getLocal("q");
|
||||
|
||||
view.detectChanges();
|
||||
|
||||
expect(q.query.map((d: TextDirective) => d.text)).toEqual(["1", "2", "3", "4"]);
|
||||
|
||||
q.list = ["-3", "2"];
|
||||
view.detectChanges();
|
||||
|
||||
|
||||
expect(q.query.map((d: TextDirective) => d.text)).toEqual(["1", "-3", "2", "4"]);
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should handle long ng-for cycles',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<needs-view-query-order #q></needs-view-query-order>';
|
||||
|
||||
tcb.overrideTemplate(MyComp, template)
|
||||
.createAsync(MyComp)
|
||||
.then((view) => {
|
||||
var q: NeedsViewQueryOrder =
|
||||
view.debugElement.componentViewChildren[0].getLocal('q');
|
||||
|
||||
// no significance to 50, just a reasonably large cycle.
|
||||
for (var i = 0; i < 50; i++) {
|
||||
var newString = i.toString();
|
||||
q.list = [newString];
|
||||
view.detectChanges();
|
||||
|
||||
expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', newString, '4']);
|
||||
}
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should support more than three queries',
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var template = '<needs-four-queries #q><div text="1"></div></needs-four-queries>';
|
||||
|
||||
tcb.overrideTemplate(MyComp, template)
|
||||
.createAsync(MyComp)
|
||||
.then((view) => {
|
||||
view.detectChanges();
|
||||
|
||||
var q = view.debugElement.componentViewChildren[0].getLocal('q');
|
||||
expect(q.query1).toBeDefined();
|
||||
expect(q.query2).toBeDefined();
|
||||
expect(q.query3).toBeDefined();
|
||||
expect(q.query4).toBeDefined();
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Directive({selector: '[text]', inputs: ['text'], exportAs: 'textDir'})
|
||||
@Injectable()
|
||||
class TextDirective {
|
||||
text: string;
|
||||
constructor() {}
|
||||
}
|
||||
|
||||
@Component({selector: 'needs-content-children'})
|
||||
@View({template: ''})
|
||||
class NeedsContentChildren implements AfterContentInit {
|
||||
@ContentChildren(TextDirective) textDirChildren: QueryList<TextDirective>;
|
||||
numberOfChildrenAfterContentInit: number;
|
||||
|
||||
afterContentInit() { this.numberOfChildrenAfterContentInit = this.textDirChildren.length; }
|
||||
}
|
||||
|
||||
@Component({selector: 'needs-view-children'})
|
||||
@View({template: '<div text></div>', directives: [TextDirective]})
|
||||
class NeedsViewChildren implements AfterViewInit {
|
||||
@ViewChildren(TextDirective) textDirChildren: QueryList<TextDirective>;
|
||||
numberOfChildrenAfterViewInit: number;
|
||||
|
||||
afterViewInit() { this.numberOfChildrenAfterViewInit = this.textDirChildren.length; }
|
||||
}
|
||||
|
||||
@Component({selector: 'needs-content-child'})
|
||||
@View({template: ''})
|
||||
class NeedsContentChild implements AfterContentInit, AfterContentChecked {
|
||||
_child: TextDirective;
|
||||
|
||||
@ContentChild(TextDirective)
|
||||
set child(value) {
|
||||
this._child = value;
|
||||
this.log.push(['setter', isPresent(value) ? value.text : null]);
|
||||
}
|
||||
|
||||
get child() { return this._child; }
|
||||
log = [];
|
||||
|
||||
afterContentInit() { this.log.push(["init", isPresent(this.child) ? this.child.text : null]); }
|
||||
|
||||
afterContentChecked() {
|
||||
this.log.push(["check", isPresent(this.child) ? this.child.text : null]);
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'needs-view-child'})
|
||||
@View({
|
||||
template: `
|
||||
<div *ng-if="shouldShow" text="foo"></div>
|
||||
`,
|
||||
directives: [NgIf, TextDirective]
|
||||
})
|
||||
class NeedsViewChild implements AfterViewInit,
|
||||
AfterViewChecked {
|
||||
shouldShow: boolean = true;
|
||||
_child: TextDirective;
|
||||
|
||||
@ViewChild(TextDirective)
|
||||
set child(value) {
|
||||
this._child = value;
|
||||
this.log.push(['setter', isPresent(value) ? value.text : null]);
|
||||
}
|
||||
|
||||
get child() { return this._child; }
|
||||
log = [];
|
||||
|
||||
afterViewInit() { this.log.push(["init", isPresent(this.child) ? this.child.text : null]); }
|
||||
|
||||
afterViewChecked() { this.log.push(["check", isPresent(this.child) ? this.child.text : null]); }
|
||||
}
|
||||
|
||||
|
||||
@Directive({selector: '[dir]'})
|
||||
@Injectable()
|
||||
class InertDirective {
|
||||
constructor() {}
|
||||
}
|
||||
|
||||
@Component({selector: 'needs-query'})
|
||||
@View({
|
||||
directives: [NgFor, TextDirective],
|
||||
template: '<div text="ignoreme"></div><b *ng-for="var dir of query">{{dir.text}}|</b>'
|
||||
})
|
||||
@Injectable()
|
||||
class NeedsQuery {
|
||||
query: QueryList<TextDirective>;
|
||||
constructor(@Query(TextDirective) query: QueryList<TextDirective>) { this.query = query; }
|
||||
}
|
||||
|
||||
@Component({selector: 'needs-four-queries'})
|
||||
@View({template: ''})
|
||||
class NeedsFourQueries {
|
||||
@ContentChild(TextDirective) query1: TextDirective;
|
||||
@ContentChild(TextDirective) query2: TextDirective;
|
||||
@ContentChild(TextDirective) query3: TextDirective;
|
||||
@ContentChild(TextDirective) query4: TextDirective;
|
||||
}
|
||||
|
||||
@Component({selector: 'needs-query-desc'})
|
||||
@View({directives: [NgFor], template: '<div *ng-for="var dir of query">{{dir.text}}|</div>'})
|
||||
@Injectable()
|
||||
class NeedsQueryDesc {
|
||||
query: QueryList<TextDirective>;
|
||||
constructor(@Query(TextDirective, {descendants: true}) query: QueryList<TextDirective>) {
|
||||
this.query = query;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'needs-query-by-var-binding'})
|
||||
@View({directives: [], template: '<ng-content>'})
|
||||
@Injectable()
|
||||
class NeedsQueryByLabel {
|
||||
query: QueryList<any>;
|
||||
constructor(@Query("textLabel", {descendants: true}) query: QueryList<any>) {
|
||||
this.query = query;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'needs-view-query-by-var-binding'})
|
||||
@View({directives: [], template: '<div #text-label>text</div>'})
|
||||
@Injectable()
|
||||
class NeedsViewQueryByLabel {
|
||||
query: QueryList<any>;
|
||||
constructor(@ViewQuery("textLabel") query: QueryList<any>) { this.query = query; }
|
||||
}
|
||||
|
||||
@Component({selector: 'needs-query-by-var-bindings'})
|
||||
@View({directives: [], template: '<ng-content>'})
|
||||
@Injectable()
|
||||
class NeedsQueryByTwoLabels {
|
||||
query: QueryList<any>;
|
||||
constructor(@Query("textLabel1,textLabel2", {descendants: true}) query: QueryList<any>) {
|
||||
this.query = query;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'needs-query-and-project'})
|
||||
@View({
|
||||
directives: [NgFor],
|
||||
template: '<div *ng-for="var dir of query">{{dir.text}}|</div><ng-content></ng-content>'
|
||||
})
|
||||
@Injectable()
|
||||
class NeedsQueryAndProject {
|
||||
query: QueryList<TextDirective>;
|
||||
constructor(@Query(TextDirective) query: QueryList<TextDirective>) { this.query = query; }
|
||||
}
|
||||
|
||||
@Component({selector: 'needs-view-query'})
|
||||
@View({
|
||||
directives: [TextDirective],
|
||||
template: '<div text="1"><div text="2"></div></div>' +
|
||||
'<div text="3"></div><div text="4"></div>'
|
||||
})
|
||||
@Injectable()
|
||||
class NeedsViewQuery {
|
||||
query: QueryList<TextDirective>;
|
||||
constructor(@ViewQuery(TextDirective) query: QueryList<TextDirective>) { this.query = query; }
|
||||
}
|
||||
|
||||
@Component({selector: 'needs-view-query-if'})
|
||||
@View({directives: [NgIf, TextDirective], template: '<div *ng-if="show" text="1"></div>'})
|
||||
@Injectable()
|
||||
class NeedsViewQueryIf {
|
||||
show: boolean;
|
||||
query: QueryList<TextDirective>;
|
||||
constructor(@ViewQuery(TextDirective) query: QueryList<TextDirective>) {
|
||||
this.query = query;
|
||||
this.show = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Component({selector: 'needs-view-query-nested-if'})
|
||||
@View({
|
||||
directives: [NgIf, InertDirective, TextDirective],
|
||||
template: '<div text="1"><div *ng-if="show"><div dir></div></div></div>'
|
||||
})
|
||||
@Injectable()
|
||||
class NeedsViewQueryNestedIf {
|
||||
show: boolean;
|
||||
query: QueryList<TextDirective>;
|
||||
constructor(@ViewQuery(TextDirective) query: QueryList<TextDirective>) {
|
||||
this.query = query;
|
||||
this.show = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'needs-view-query-order'})
|
||||
@View({
|
||||
directives: [NgFor, TextDirective, InertDirective],
|
||||
template: '<div text="1"></div>' +
|
||||
'<div *ng-for="var i of list" [text]="i"></div>' +
|
||||
'<div text="4"></div>'
|
||||
})
|
||||
@Injectable()
|
||||
class NeedsViewQueryOrder {
|
||||
query: QueryList<TextDirective>;
|
||||
list: string[];
|
||||
constructor(@ViewQuery(TextDirective) query: QueryList<TextDirective>) {
|
||||
this.query = query;
|
||||
this.list = ['2', '3'];
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'needs-view-query-order-with-p'})
|
||||
@View({
|
||||
directives: [NgFor, TextDirective, InertDirective],
|
||||
template: '<div dir><div text="1"></div>' +
|
||||
'<div *ng-for="var i of list" [text]="i"></div>' +
|
||||
'<div text="4"></div></div>'
|
||||
})
|
||||
@Injectable()
|
||||
class NeedsViewQueryOrderWithParent {
|
||||
query: QueryList<TextDirective>;
|
||||
list: string[];
|
||||
constructor(@ViewQuery(TextDirective) query: QueryList<TextDirective>) {
|
||||
this.query = query;
|
||||
this.list = ['2', '3'];
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'needs-tpl'})
|
||||
@View({template: '<template var-x="shadow"></template>'})
|
||||
class NeedsTpl {
|
||||
viewQuery: QueryList<TemplateRef>;
|
||||
query: QueryList<TemplateRef>;
|
||||
constructor(@ViewQuery(TemplateRef) viewQuery: QueryList<TemplateRef>,
|
||||
@Query(TemplateRef) query: QueryList<TemplateRef>) {
|
||||
this.viewQuery = viewQuery;
|
||||
this.query = query;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'my-comp'})
|
||||
@View({
|
||||
directives: [
|
||||
NeedsQuery,
|
||||
NeedsQueryDesc,
|
||||
NeedsQueryByLabel,
|
||||
NeedsQueryByTwoLabels,
|
||||
NeedsQueryAndProject,
|
||||
NeedsViewQuery,
|
||||
NeedsViewQueryIf,
|
||||
NeedsViewQueryNestedIf,
|
||||
NeedsViewQueryOrder,
|
||||
NeedsViewQueryByLabel,
|
||||
NeedsViewQueryOrderWithParent,
|
||||
NeedsContentChildren,
|
||||
NeedsViewChildren,
|
||||
NeedsViewChild,
|
||||
NeedsContentChild,
|
||||
NeedsTpl,
|
||||
TextDirective,
|
||||
InertDirective,
|
||||
NgIf,
|
||||
NgFor,
|
||||
NeedsFourQueries
|
||||
]
|
||||
})
|
||||
@Injectable()
|
||||
class MyComp {
|
||||
shouldShow: boolean;
|
||||
list;
|
||||
constructor() {
|
||||
this.shouldShow = false;
|
||||
this.list = ['1d', '2d', '3d'];
|
||||
}
|
||||
}
|
92
modules/angular2/test/core/linker/query_list_spec.ts
Normal file
92
modules/angular2/test/core/linker/query_list_spec.ts
Normal file
@ -0,0 +1,92 @@
|
||||
import {
|
||||
describe,
|
||||
it,
|
||||
expect,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
iit,
|
||||
xit,
|
||||
el,
|
||||
fakeAsync,
|
||||
tick
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import {MapWrapper, ListWrapper, iterateListLike} from 'angular2/src/core/facade/collection';
|
||||
import {StringWrapper} from 'angular2/src/core/facade/lang';
|
||||
import {ObservableWrapper} from 'angular2/src/core/facade/async';
|
||||
import {QueryList} from 'angular2/src/core/compiler/query_list';
|
||||
import {DOM} from 'angular2/src/core/dom/dom_adapter';
|
||||
|
||||
|
||||
export function main() {
|
||||
describe('QueryList', () => {
|
||||
var queryList: QueryList<string>;
|
||||
var log: string;
|
||||
beforeEach(() => {
|
||||
queryList = new QueryList<string>();
|
||||
log = '';
|
||||
});
|
||||
|
||||
function logAppend(item) { log += (log.length == 0 ? '' : ', ') + item; }
|
||||
|
||||
it('should support resetting and iterating over the new objects', () => {
|
||||
queryList.reset(['one']);
|
||||
queryList.reset(['two']);
|
||||
iterateListLike(queryList, logAppend);
|
||||
expect(log).toEqual('two');
|
||||
});
|
||||
|
||||
it('should support length', () => {
|
||||
queryList.reset(['one', 'two']);
|
||||
expect(queryList.length).toEqual(2);
|
||||
});
|
||||
|
||||
it('should support map', () => {
|
||||
queryList.reset(['one', 'two']);
|
||||
expect(queryList.map((x) => x)).toEqual(['one', 'two']);
|
||||
});
|
||||
|
||||
it('should support toString', () => {
|
||||
queryList.reset(['one', 'two']);
|
||||
var listString = queryList.toString();
|
||||
expect(StringWrapper.contains(listString, 'one')).toBeTruthy();
|
||||
expect(StringWrapper.contains(listString, 'two')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should support first and last', () => {
|
||||
queryList.reset(['one', 'two', 'three']);
|
||||
expect(queryList.first).toEqual('one');
|
||||
expect(queryList.last).toEqual('three');
|
||||
});
|
||||
|
||||
if (DOM.supportsDOMEvents()) {
|
||||
describe('simple observable interface', () => {
|
||||
it('should fire callbacks on change', fakeAsync(() => {
|
||||
var fires = 0;
|
||||
ObservableWrapper.subscribe(queryList.changes, (_) => { fires += 1; });
|
||||
|
||||
queryList.notifyOnChanges();
|
||||
tick();
|
||||
|
||||
expect(fires).toEqual(1);
|
||||
|
||||
queryList.notifyOnChanges();
|
||||
tick();
|
||||
|
||||
expect(fires).toEqual(2);
|
||||
}));
|
||||
|
||||
it('should provides query list as an argument', fakeAsync(() => {
|
||||
var recorded;
|
||||
ObservableWrapper.subscribe(queryList.changes, (v: any) => { recorded = v; });
|
||||
|
||||
queryList.reset(["one"]);
|
||||
queryList.notifyOnChanges();
|
||||
tick();
|
||||
|
||||
expect(recorded).toBe(queryList);
|
||||
}));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
60
modules/angular2/test/core/linker/view_container_ref_spec.ts
Normal file
60
modules/angular2/test/core/linker/view_container_ref_spec.ts
Normal file
@ -0,0 +1,60 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
xdescribe,
|
||||
describe,
|
||||
el,
|
||||
dispatchEvent,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
beforeEachBindings,
|
||||
it,
|
||||
xit
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import {SpyView, SpyAppViewManager} from '../spies';
|
||||
import {AppView, AppViewContainer} from 'angular2/src/core/compiler/view';
|
||||
import {ViewContainerRef} from 'angular2/src/core/compiler/view_container_ref';
|
||||
import {ElementRef} from 'angular2/src/core/compiler/element_ref';
|
||||
import {ViewRef} from 'angular2/src/core/compiler/view_ref';
|
||||
|
||||
export function main() {
|
||||
// TODO(tbosch): add missing tests
|
||||
|
||||
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!
|
||||
|
||||
});
|
||||
}
|
515
modules/angular2/test/core/linker/view_manager_spec.ts
Normal file
515
modules/angular2/test/core/linker/view_manager_spec.ts
Normal file
@ -0,0 +1,515 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
xdescribe,
|
||||
describe,
|
||||
el,
|
||||
dispatchEvent,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
beforeEachBindings,
|
||||
it,
|
||||
xit
|
||||
} from 'angular2/test_lib';
|
||||
import {SpyRenderer, SpyAppViewPool, SpyAppViewListener, SpyProtoViewFactory} from '../spies';
|
||||
import {Injector, bind} from 'angular2/core';
|
||||
|
||||
import {AppProtoView, AppView, AppViewContainer} from 'angular2/src/core/compiler/view';
|
||||
import {ProtoViewRef, ViewRef, internalView} from 'angular2/src/core/compiler/view_ref';
|
||||
import {ElementRef} from 'angular2/src/core/compiler/element_ref';
|
||||
import {TemplateRef} from 'angular2/src/core/compiler/template_ref';
|
||||
import {
|
||||
Renderer,
|
||||
RenderViewRef,
|
||||
RenderProtoViewRef,
|
||||
RenderFragmentRef,
|
||||
ViewType,
|
||||
RenderViewWithFragments
|
||||
} from 'angular2/src/core/render/api';
|
||||
import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
|
||||
import {AppViewManagerUtils} from 'angular2/src/core/compiler/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('viewCreated').reset();
|
||||
viewListener.spy('viewDestroyed').reset();
|
||||
renderer.spy('createView').reset();
|
||||
renderer.spy('destroyView').reset();
|
||||
renderer.spy('createRootHostView').reset();
|
||||
renderer.spy('setEventDispatcher').reset();
|
||||
renderer.spy('hydrateView').reset();
|
||||
renderer.spy('dehydrateView').reset();
|
||||
viewPool.spy('returnView').reset();
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
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('viewCreated')).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('viewDestroyed')).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('viewCreated')).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('viewCreated')).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('viewCreated')).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('viewCreated')).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', () => {
|
||||
|
||||
});
|
||||
});
|
||||
}
|
288
modules/angular2/test/core/linker/view_manager_utils_spec.ts
Normal file
288
modules/angular2/test/core/linker/view_manager_utils_spec.ts
Normal file
@ -0,0 +1,288 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
xdescribe,
|
||||
describe,
|
||||
el,
|
||||
dispatchEvent,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
beforeEachBindings,
|
||||
it,
|
||||
xit,
|
||||
Log,
|
||||
SpyObject
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import {
|
||||
SpyChangeDetector,
|
||||
SpyProtoElementInjector,
|
||||
SpyElementInjector,
|
||||
SpyPreBuiltObjects
|
||||
} from '../spies';
|
||||
|
||||
import {Injector, bind} from 'angular2/core';
|
||||
import {isBlank, isPresent} from 'angular2/src/core/facade/lang';
|
||||
|
||||
import {AppProtoView, AppView, AppProtoViewMergeInfo} from 'angular2/src/core/compiler/view';
|
||||
import {ElementBinder} from 'angular2/src/core/compiler/element_binder';
|
||||
import {
|
||||
DirectiveBinding,
|
||||
ElementInjector,
|
||||
PreBuiltObjects,
|
||||
ProtoElementInjector
|
||||
} from 'angular2/src/core/compiler/element_injector';
|
||||
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
||||
import {Component} from 'angular2/src/core/metadata';
|
||||
import {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils';
|
||||
import {ViewType, RenderViewWithFragments} from 'angular2/src/core/render/render';
|
||||
|
||||
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 componentBinding = null;
|
||||
if (nestedProtoView.type === ViewType.COMPONENT) {
|
||||
var annotation = new DirectiveResolver().resolve(SomeComponent);
|
||||
componentBinding = DirectiveBinding.createFromType(SomeComponent, annotation);
|
||||
}
|
||||
return new ElementBinder(0, null, 0, createProtoElInjector(), componentBinding, nestedProtoView);
|
||||
}
|
||||
|
||||
function _createProtoView(type: ViewType, binders: ElementBinder[] = null) {
|
||||
if (isBlank(binders)) {
|
||||
binders = [];
|
||||
}
|
||||
var res = new AppProtoView([], 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 {
|
||||
}
|
72
modules/angular2/test/core/linker/view_pool_spec.ts
Normal file
72
modules/angular2/test/core/linker/view_pool_spec.ts
Normal file
@ -0,0 +1,72 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
xdescribe,
|
||||
describe,
|
||||
el,
|
||||
dispatchEvent,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
beforeEachBindings,
|
||||
it,
|
||||
xit,
|
||||
SpyObject,
|
||||
proxy
|
||||
} from 'angular2/test_lib';
|
||||
import {AppViewPool} from 'angular2/src/core/compiler/view_pool';
|
||||
import {AppProtoView, AppView} from 'angular2/src/core/compiler/view';
|
||||
import {MapWrapper, Map} from 'angular2/src/core/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); }
|
||||
|
||||
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