feat(tests): add TestComponentBuilder

Adds a TestComponentBuilder for use in component level tests.
For usage examples, see test_component_builder_spec

Closes #1812
This commit is contained in:
Julie Ralph 2015-05-15 16:42:52 -07:00
parent 30b6542fc8
commit c32dbad747
6 changed files with 725 additions and 17 deletions

View File

@ -4,17 +4,18 @@ import {Type, isPresent, BaseException, stringify, isBlank} from 'angular2/src/f
import {View} from 'angular2/src/core/annotations_impl/view'; import {View} from 'angular2/src/core/annotations_impl/view';
import {TemplateResolver} from 'angular2/src/core/compiler/template_resolver'; import {TemplateResolver} from 'angular2/src/core/compiler/template_resolver';
export class MockTemplateResolver extends TemplateResolver { export class MockTemplateResolver extends TemplateResolver {
_templates: Map<Type, View>; _views: Map<Type, View>;
_inlineTemplates: Map<Type, string>; _inlineTemplates: Map<Type, string>;
_templateCache: Map<Type, View>; _viewCache: Map<Type, View>;
_directiveOverrides: Map<Type, Map<Type, Type>>; _directiveOverrides: Map<Type, Map<Type, Type>>;
constructor() { constructor() {
super(); super();
this._templates = MapWrapper.create(); this._views = MapWrapper.create();
this._inlineTemplates = MapWrapper.create(); this._inlineTemplates = MapWrapper.create();
this._templateCache = MapWrapper.create(); this._viewCache = MapWrapper.create();
this._directiveOverrides = MapWrapper.create(); this._directiveOverrides = MapWrapper.create();
} }
@ -26,7 +27,7 @@ export class MockTemplateResolver extends TemplateResolver {
*/ */
setView(component: Type, view: View): void { setView(component: Type, view: View): void {
this._checkOverrideable(component); this._checkOverrideable(component);
MapWrapper.set(this._templates, component, view); MapWrapper.set(this._views, component, view);
} }
/** /**
@ -47,7 +48,7 @@ export class MockTemplateResolver extends TemplateResolver {
* @param {Type} from * @param {Type} from
* @param {Type} to * @param {Type} to
*/ */
overrideTemplateDirective(component: Type, from: Type, to: Type): void { overrideViewDirective(component: Type, from: Type, to: Type): void {
this._checkOverrideable(component); this._checkOverrideable(component);
var overrides = MapWrapper.get(this._directiveOverrides, component); var overrides = MapWrapper.get(this._directiveOverrides, component);
@ -62,20 +63,20 @@ export class MockTemplateResolver extends TemplateResolver {
/** /**
* Returns the {@link View} for a component: * Returns the {@link View} for a component:
* - Set the {@link View} to the overridden template when it exists or fallback to the default * - Set the {@link View} to the overridden view when it exists or fallback to the default
* `TemplateResolver`, * `TemplateResolver`,
* see `setView`. * see `setView`.
* - Override the directives, see `overrideTemplateDirective`. * - Override the directives, see `overrideViewDirective`.
* - Override the @View definition, see `setInlineTemplate`. * - Override the @View definition, see `setInlineTemplate`.
* *
* @param component * @param component
* @returns {ViewDefinition} * @returns {ViewDefinition}
*/ */
resolve(component: Type): View { resolve(component: Type): View {
var view = MapWrapper.get(this._templateCache, component); var view = MapWrapper.get(this._viewCache, component);
if (isPresent(view)) return view; if (isPresent(view)) return view;
view = MapWrapper.get(this._templates, component); view = MapWrapper.get(this._views, component);
if (isBlank(view)) { if (isBlank(view)) {
view = super.resolve(component); view = super.resolve(component);
} }
@ -106,7 +107,7 @@ export class MockTemplateResolver extends TemplateResolver {
view = new View({template: inlineTemplate, templateUrl: null, directives: view.directives}); view = new View({template: inlineTemplate, templateUrl: null, directives: view.directives});
} }
MapWrapper.set(this._templateCache, component, view); MapWrapper.set(this._viewCache, component, view);
return view; return view;
} }
@ -119,7 +120,7 @@ export class MockTemplateResolver extends TemplateResolver {
* @param {Type} component * @param {Type} component
*/ */
_checkOverrideable(component: Type): void { _checkOverrideable(component: Type): void {
var cached = MapWrapper.get(this._templateCache, component); var cached = MapWrapper.get(this._viewCache, component);
if (isPresent(cached)) { if (isPresent(cached)) {
throw new BaseException( throw new BaseException(

View File

@ -23,6 +23,7 @@ import {DOM} from 'angular2/src/dom/dom_adapter';
/** /**
* @exportedAs angular2/test * @exportedAs angular2/test
* TODO(juliemr): Deprecate in favor of TestComponentBuilder
*/ */
@Injectable() @Injectable()
export class TestBed { export class TestBed {
@ -61,7 +62,7 @@ export class TestBed {
* @param {Type} to * @param {Type} to
*/ */
overrideDirective(component: Type, from: Type, to: Type): void { overrideDirective(component: Type, from: Type, to: Type): void {
this._injector.get(TemplateResolver).overrideTemplateDirective(component, from, to); this._injector.get(TemplateResolver).overrideViewDirective(component, from, to);
} }
/** /**
@ -107,6 +108,7 @@ export class TestBed {
/** /**
* Proxy to `AppView` return by `createView` in {@link TestBed} which offers a high level API for * Proxy to `AppView` return by `createView` in {@link TestBed} which offers a high level API for
* tests. * tests.
* TODO(juliemr): Deprecate in favor of TestElement
*/ */
export class ViewProxy { export class ViewProxy {
_componentRef: ComponentRef; _componentRef: ComponentRef;

View File

@ -0,0 +1,343 @@
import {Injector, bind, Injectable} from 'angular2/di';
import {Type, isPresent, BaseException, isBlank} from 'angular2/src/facade/lang';
import {Promise} from 'angular2/src/facade/async';
import {List, ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
import {View} from 'angular2/src/core/annotations_impl/view';
import {ElementInjector} from 'angular2/src/core/compiler/element_injector';
import {TemplateResolver} from 'angular2/src/core/compiler/template_resolver';
import {AppView} from 'angular2/src/core/compiler/view';
import {internalView} from 'angular2/src/core/compiler/view_ref';
import {
DynamicComponentLoader,
ComponentRef
} from 'angular2/src/core/compiler/dynamic_component_loader';
import {ElementRef} from 'angular2/src/core/compiler/element_ref';
import {el} from './utils';
import {DOCUMENT_TOKEN} from 'angular2/src/render/dom/dom_renderer';
import {DOM} from 'angular2/src/dom/dom_adapter';
import {resolveInternalDomView} from 'angular2/src/render/dom/view/view';
/**
* @exportedAs angular2/test
*
* A TestElement contains information from the Angular compiler about an
* element and provides access to the corresponding ElementInjector and
* underlying dom Element, as well as a way to query for children.
*/
export class TestElement {
_elementInjector: ElementInjector;
constructor(private _parentView: AppView, private _boundElementIndex: number) {
this._elementInjector = this._parentView.elementInjectors[this._boundElementIndex];
}
static create(elementRef: ElementRef): TestElement {
return new TestElement(internalView(elementRef.parentView), elementRef.boundElementIndex);
}
get componentInstance(): any {
if (!isPresent(this._elementInjector)) {
return null;
}
return this._elementInjector.getComponent();
}
get dynamicallyCreatedComponentInstance(): any {
if (!isPresent(this._elementInjector)) {
return null;
}
return this._elementInjector.getDynamicallyLoadedComponent();
}
get domElement(): any {
return resolveInternalDomView(this._parentView.render).boundElements[this._boundElementIndex];
}
getDirectiveInstance(directiveIndex: number): any {
return this._elementInjector.getDirectiveAtIndex(directiveIndex);
}
/**
* Get child TestElements from within the Light DOM.
*
* @return {List<TestElement>}
*/
get children(): List<TestElement> {
var thisElementBinder = this._parentView.proto.elementBinders[this._boundElementIndex];
return this._getChildElements(this._parentView, thisElementBinder.index);
}
/**
* Get the root TestElement children of a component. Returns an empty
* list if the current TestElement is not a component root.
*
* @return {List<TestElement>}
*/
get componentViewChildren(): List<TestElement> {
var shadowView = this._parentView.componentChildViews[this._boundElementIndex];
if (!isPresent(shadowView)) {
// The current test element is not a component.
return ListWrapper.create();
}
return this._getChildElements(shadowView, null);
}
triggerEventHandler(eventName, eventObj): void {
this._parentView.triggerEventHandlers(eventName, eventObj, this._boundElementIndex);
}
hasDirective(type: Type): boolean {
if (!isPresent(this._elementInjector)) {
return false;
}
return this._elementInjector.hasDirective(type);
}
inject(type: Type): any {
if (!isPresent(this._elementInjector)) {
return null;
}
return this._elementInjector.get(type);
}
/**
* Return the first descendant TestElememt matching the given predicate
* and scope.
*
* @param {Function: boolean} predicate
* @param {Scope} scope
*
* @return {TestElement}
*/
query(predicate: Function, scope = Scope.all): TestElement {
var results = this.queryAll(predicate, scope);
return results.length > 0 ? results[0] : null;
}
/**
* Return descendant TestElememts matching the given predicate
* and scope.
*
* @param {Function: boolean} predicate
* @param {Scope} scope
*
* @return {List<TestElement>}
*/
queryAll(predicate: Function, scope = Scope.all): List<TestElement> {
var elementsInScope = scope(this);
return ListWrapper.filter(elementsInScope, predicate);
}
_getChildElements(view: AppView, parentBoundElementIndex: number): List<TestElement> {
var els = ListWrapper.create();
var parentElementBinder = null;
if (isPresent(parentBoundElementIndex)) {
parentElementBinder = view.proto.elementBinders[parentBoundElementIndex];
}
for (var i = 0; i < view.proto.elementBinders.length; ++i) {
var binder = view.proto.elementBinders[i];
if (binder.parent == parentElementBinder) {
ListWrapper.push(els, new TestElement(view, i));
var views = view.viewContainers[i];
if (isPresent(views)) {
ListWrapper.forEach(views.views, (nextView) => {
els = ListWrapper.concat(els, this._getChildElements(nextView, null));
});
}
}
}
return els;
}
}
export function inspectElement(elementRef: ElementRef): TestElement {
return TestElement.create(elementRef);
}
/**
* @exportedAs angular2/test
*/
export class RootTestComponent extends TestElement {
_componentRef: ComponentRef;
_componentParentView: AppView;
constructor(componentRef: ComponentRef) {
super(internalView(componentRef.hostView), 0);
this._componentParentView = internalView(componentRef.hostView);
this._componentRef = componentRef;
}
detectChanges(): void {
this._componentParentView.changeDetector.detectChanges();
this._componentParentView.changeDetector.checkNoChanges();
}
destroy(): void { this._componentRef.dispose(); }
}
/**
* @exportedAs angular2/test
*/
export class Scope {
static all(testElement): List<TestElement> {
var scope = ListWrapper.create();
ListWrapper.push(scope, testElement);
ListWrapper.forEach(testElement.children,
(child) => { scope = ListWrapper.concat(scope, Scope.all(child)); });
ListWrapper.forEach(testElement.componentViewChildren,
(child) => { scope = ListWrapper.concat(scope, Scope.all(child)); });
return scope;
}
static light(testElement): List<TestElement> {
var scope = ListWrapper.create();
ListWrapper.forEach(testElement.children, (child) => {
ListWrapper.push(scope, child);
scope = ListWrapper.concat(scope, Scope.light(child));
});
return scope;
}
static view(testElement): List<TestElement> {
var scope = ListWrapper.create();
ListWrapper.forEach(testElement.componentViewChildren, (child) => {
ListWrapper.push(scope, child);
scope = ListWrapper.concat(scope, Scope.light(child));
});
return scope;
}
}
/**
* @exportedAs angular2/test
*/
export class By {
static all(): Function { return (testElement) => true; }
static css(selector: string): Function {
return (testElement) => { return DOM.elementMatches(testElement.domElement, selector); };
}
static directive(type: Type): Function {
return (testElement) => { return testElement.hasDirective(type); };
}
}
/**
* @exportedAs angular2/test
*
* Builds a RootTestComponent for use in component level tests.
*/
@Injectable()
export class TestComponentBuilder {
_injector: Injector;
_viewOverrides: Map<Type, View>;
_directiveOverrides: Map<Type, Map<Type, Type>>;
_templateOverrides: Map<Type, string>;
constructor(injector: Injector) {
this._injector = injector;
this._viewOverrides = MapWrapper.create();
this._directiveOverrides = MapWrapper.create();
this._templateOverrides = MapWrapper.create();
}
_clone(): TestComponentBuilder {
var clone = new TestComponentBuilder(this._injector);
clone._viewOverrides = MapWrapper.clone(this._viewOverrides);
clone._directiveOverrides = MapWrapper.clone(this._directiveOverrides);
clone._templateOverrides = MapWrapper.clone(this._templateOverrides);
return clone;
}
/**
* Overrides only the html of a {@link Component}.
* All the other propoerties of the component's {@link View} are preserved.
*
* @param {Type} component
* @param {string} html
*
* @return {TestComponentBuilder}
*/
overrideTemplate(componentType: Type, template: string): TestComponentBuilder {
var clone = this._clone();
MapWrapper.set(clone._templateOverrides, componentType, template);
return clone;
}
/**
* Overrides a component's {@link View}.
*
* @param {Type} component
* @param {view} View
*
* @return {TestComponentBuilder}
*/
overrideView(componentType: Type, view: View): TestComponentBuilder {
var clone = this._clone();
MapWrapper.set(clone._viewOverrides, componentType, view);
return clone;
}
/**
* Overrides the directives from the component {@link View}.
*
* @param {Type} component
* @param {Type} from
* @param {Type} to
*
* @return {TestComponentBuilder}
*/
overrideDirective(componentType: Type, from: Type, to: Type): TestComponentBuilder {
var clone = this._clone();
var overridesForComponent = MapWrapper.get(clone._directiveOverrides, componentType);
if (!isPresent(overridesForComponent)) {
MapWrapper.set(clone._directiveOverrides, componentType, MapWrapper.create());
overridesForComponent = MapWrapper.get(clone._directiveOverrides, componentType);
}
MapWrapper.set(overridesForComponent, from, to);
return clone;
}
/**
* Builds and returns a RootTestComponent.
*
* @return {Promise<RootTestComponent>}
*/
createAsync(rootComponentType: Type): Promise<RootTestComponent> {
var mockTemplateResolver = this._injector.get(TemplateResolver);
MapWrapper.forEach(this._viewOverrides,
(view, type) => { mockTemplateResolver.setView(type, view); });
MapWrapper.forEach(this._templateOverrides, (template, type) => {
mockTemplateResolver.setInlineTemplate(type, template);
});
MapWrapper.forEach(this._directiveOverrides, (overrides, component) => {
MapWrapper.forEach(overrides, (to, from) => {
mockTemplateResolver.overrideViewDirective(component, from, to);
});
});
var rootEl = el('<div id="root"></div>');
var doc = this._injector.get(DOCUMENT_TOKEN);
// TODO(juliemr): can/should this be optional?
DOM.appendChild(doc.body, rootEl);
return this._injector.get(DynamicComponentLoader)
.loadAsRoot(rootComponentType, '#root', this._injector)
.then((componentRef) => { return new RootTestComponent(componentRef); });
}
}

View File

@ -35,6 +35,7 @@ import {MockXHR} from 'angular2/src/mock/xhr_mock';
import {MockNgZone} from 'angular2/src/mock/ng_zone_mock'; import {MockNgZone} from 'angular2/src/mock/ng_zone_mock';
import {TestBed} from './test_bed'; import {TestBed} from './test_bed';
import {TestComponentBuilder} from './test_component_builder';
import {Injector} from 'angular2/di'; import {Injector} from 'angular2/di';
@ -113,6 +114,7 @@ function _getAppBindings() {
StyleUrlResolver, StyleUrlResolver,
StyleInliner, StyleInliner,
TestBed, TestBed,
TestComponentBuilder,
bind(NgZone).toClass(MockNgZone), bind(NgZone).toClass(MockNgZone),
bind(EventManager) bind(EventManager)
.toFactory((zone) => .toFactory((zone) =>

View File

@ -72,7 +72,7 @@ export function main() {
describe('Directive overriding', () => { describe('Directive overriding', () => {
it('should allow overriding a directive from the default template', () => { it('should allow overriding a directive from the default template', () => {
resolver.overrideTemplateDirective(SomeComponent, SomeDirective, SomeOtherDirective); resolver.overrideViewDirective(SomeComponent, SomeDirective, SomeOtherDirective);
var template = resolver.resolve(SomeComponent); var template = resolver.resolve(SomeComponent);
expect(template.directives.length).toEqual(1); expect(template.directives.length).toEqual(1);
expect(template.directives[0]).toBe(SomeOtherDirective); expect(template.directives[0]).toBe(SomeOtherDirective);
@ -80,14 +80,14 @@ export function main() {
it('should allow overriding a directive from an overriden @View', () => { it('should allow overriding a directive from an overriden @View', () => {
resolver.setView(SomeComponent, new View({directives: [SomeOtherDirective]})); resolver.setView(SomeComponent, new View({directives: [SomeOtherDirective]}));
resolver.overrideTemplateDirective(SomeComponent, SomeOtherDirective, SomeComponent); resolver.overrideViewDirective(SomeComponent, SomeOtherDirective, SomeComponent);
var template = resolver.resolve(SomeComponent); var template = resolver.resolve(SomeComponent);
expect(template.directives.length).toEqual(1); expect(template.directives.length).toEqual(1);
expect(template.directives[0]).toBe(SomeComponent); expect(template.directives[0]).toBe(SomeComponent);
}); });
it('should throw when the overridden directive is not present', () => { it('should throw when the overridden directive is not present', () => {
resolver.overrideTemplateDirective(SomeComponent, SomeOtherDirective, SomeDirective); resolver.overrideViewDirective(SomeComponent, SomeOtherDirective, SomeDirective);
expect(() => { resolver.resolve(SomeComponent); }) expect(() => { resolver.resolve(SomeComponent); })
.toThrowError('Overriden directive SomeOtherDirective not found in the template of SomeComponent'); .toThrowError('Overriden directive SomeOtherDirective not found in the template of SomeComponent');
}); });
@ -95,7 +95,7 @@ export function main() {
it('should not allow overriding a directive after its template has been resolved', () => { it('should not allow overriding a directive after its template has been resolved', () => {
resolver.resolve(SomeComponent); resolver.resolve(SomeComponent);
expect(() => { expect(() => {
resolver.overrideTemplateDirective(SomeComponent, SomeDirective, SomeOtherDirective); resolver.overrideViewDirective(SomeComponent, SomeDirective, SomeOtherDirective);
}).toThrowError('The component SomeComponent has already been compiled, its configuration can not be changed'); }).toThrowError('The component SomeComponent has already been compiled, its configuration can not be changed');
}); });
}); });

View File

@ -0,0 +1,360 @@
import {
AsyncTestCompleter,
beforeEach,
ddescribe,
xdescribe,
describe,
dispatchEvent,
expect,
iit,
inject,
IS_DARTIUM,
beforeEachBindings,
it,
xit
} from 'angular2/test_lib';
import {TestComponentBuilder, By, Scope} from 'angular2/src/test_lib/test_component_builder';
import {DOM} from 'angular2/src/dom/dom_adapter';
import {List, ListWrapper} from 'angular2/src/facade/collection';
import {PromiseWrapper, EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
import {Injectable} from 'angular2/di';
import {
Directive,
Component,
View,
} from 'angular2/annotations';
import * as viewAnn from 'angular2/src/core/annotations_impl/view';
import {NgIf} from 'angular2/src/directives/ng_if';
import {NgFor} from 'angular2/src/directives/ng_for';
@Injectable()
class Logger {
log: List<string>;
constructor() { this.log = ListWrapper.create(); }
add(thing: string) { ListWrapper.push(this.log, thing); }
}
@Directive({selector: '[message]', properties: {'message': 'message'}})
@Injectable()
class MessageDir {
logger: Logger;
constructor(logger: Logger) { this.logger = logger; }
set message(newMessage) { this.logger.add(newMessage); }
}
@Component({selector: 'child-comp'})
@View({
template: `<div class="child" message="child">
<span class="childnested" message="nestedchild">Child</span>
</div>
<span class="child">{{childBinding}}</span>`,
directives: [MessageDir]
})
@Injectable()
class ChildComp {
childBinding: string;
constructor() { this.childBinding = 'Original'; }
}
@Component({selector: 'child-comp'})
@View({template: `<span>Mock</span>`})
@Injectable()
class MockChildComp {
}
@Component({selector: 'parent-comp', appInjector: [Logger]})
@View({
template: `<div class="parent" message="parent">
<span class="parentnested" message="nestedparent">Parent</span>
</div>
<span class="parent">{{parentBinding}}</span>
<child-comp class="child-comp-class"></child-comp>`,
directives: [ChildComp, MessageDir]
})
@Injectable()
class ParentComp {
parentBinding: string;
constructor() { this.parentBinding = 'OriginalParent'; }
}
@Component({selector: 'my-if-comp'})
@View({template: `<span *ng-if="showMore">More</span>`, directives: [NgIf]})
@Injectable()
class MyIfComp {
showMore: boolean = false;
}
@Directive({selector: 'custom-emitter', events: ['myevent']})
@Injectable()
class CustomEmitter {
myevent: EventEmitter;
constructor() { this.myevent = new EventEmitter(); }
}
@Component({selector: 'events-comp'})
@View({
template: `<button (click)="handleClick()"></button>
<custom-emitter (myevent)="handleCustom()"></custom-emitter>`,
directives: [CustomEmitter]
})
@Injectable()
class EventsComp {
clicked: boolean;
customed: boolean;
constructor() {
this.clicked = false;
this.customed = false;
}
handleClick() { this.clicked = true; }
handleCustom() { this.customed = true; }
}
@Component({selector: 'using-for', appInjector: [Logger]})
@View({
template: `<span *ng-for="#thing of stuff">{{thing}}</span>
<ul message="list">
<li *ng-for="#item of stuff">{{item}}</li>
</ul>`,
directives: [NgFor, MessageDir]
})
@Injectable()
class UsingFor {
stuff: List<string>;
constructor() { this.stuff = ['one', 'two', 'three']; }
}
export function main() {
describe('test component builder', function() {
it('should instantiate a component with valid DOM',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb, async) => {
tcb.createAsync(MockChildComp)
.then((rootTestComponent) => {
var childSpans = DOM.querySelectorAll(rootTestComponent.domElement, 'span');
expect(DOM.getInnerHTML(childSpans[0])).toEqual('Mock');
async.done();
});
}));
it('should allow changing members of the component',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb, async) => {
tcb.createAsync(MyIfComp).then((rootTestComponent) => {
rootTestComponent.detectChanges();
var childSpans = DOM.querySelectorAll(rootTestComponent.domElement, 'span');
expect(childSpans.length).toEqual(0);
rootTestComponent.componentInstance.showMore = true;
rootTestComponent.detectChanges();
childSpans = DOM.querySelectorAll(rootTestComponent.domElement, 'span');
expect(childSpans.length).toEqual(1);
async.done();
});
}));
it('should list component child elements',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb, async) => {
tcb.createAsync(ParentComp)
.then((rootTestComponent) => {
rootTestComponent.detectChanges();
var childEls = rootTestComponent.children;
// The root is a lone component, and has no children in the light dom.
expect(childEls.length).toEqual(0);
var rootCompChildren = rootTestComponent.componentViewChildren;
// The root component has 3 elements in its shadow view.
expect(rootCompChildren.length).toEqual(3);
expect(DOM.hasClass(rootCompChildren[0].domElement, 'parent')).toBe(true);
expect(DOM.hasClass(rootCompChildren[1].domElement, 'parent')).toBe(true);
expect(DOM.hasClass(rootCompChildren[2].domElement, 'child-comp-class')).toBe(true);
var nested = rootCompChildren[0].children;
expect(nested.length).toEqual(1);
expect(DOM.hasClass(nested[0].domElement, 'parentnested')).toBe(true);
var childComponent = rootCompChildren[2];
expect(childComponent.children.length).toEqual(0);
var childCompChildren = childComponent.componentViewChildren;
expect(childCompChildren.length).toEqual(2);
expect(DOM.hasClass(childCompChildren[0].domElement, 'child')).toBe(true);
expect(DOM.hasClass(childCompChildren[1].domElement, 'child')).toBe(true);
var childNested = childCompChildren[0].children;
expect(childNested.length).toEqual(1);
expect(DOM.hasClass(childNested[0].domElement, 'childnested')).toBe(true);
async.done();
});
}));
it('should list child elements within viewports',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb, async) => {
tcb.createAsync(UsingFor).then((rootTestComponent) => {
rootTestComponent.detectChanges();
var childEls = rootTestComponent.componentViewChildren;
// TODO should this count include the <template> element?
expect(childEls.length).toEqual(5);
var list = childEls[4];
expect(list.children.length).toEqual(4);
async.done();
});
}));
it('should query child elements',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb, async) => {
tcb.createAsync(ParentComp)
.then((rootTestComponent) => {
rootTestComponent.detectChanges();
var childTestEls = rootTestComponent.queryAll(By.directive(MessageDir));
expect(childTestEls.length).toBe(4);
expect(DOM.hasClass(childTestEls[0].domElement, 'parent')).toBe(true);
expect(DOM.hasClass(childTestEls[1].domElement, 'parentnested')).toBe(true);
expect(DOM.hasClass(childTestEls[2].domElement, 'child')).toBe(true);
expect(DOM.hasClass(childTestEls[3].domElement, 'childnested')).toBe(true);
async.done();
});
}));
it('should query child elements in the light DOM',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb, async) => {
tcb.createAsync(ParentComp)
.then((rootTestComponent) => {
rootTestComponent.detectChanges();
var parentEl = rootTestComponent.componentViewChildren[0];
var childTestEls = parentEl.queryAll(By.directive(MessageDir), Scope.light);
expect(childTestEls.length).toBe(1);
expect(DOM.hasClass(childTestEls[0].domElement, 'parentnested')).toBe(true);
async.done();
});
}));
it('should query child elements in the current component view DOM',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb, async) => {
tcb.createAsync(ParentComp)
.then((rootTestComponent) => {
rootTestComponent.detectChanges();
var childTestEls = rootTestComponent.queryAll(By.directive(MessageDir), Scope.view);
expect(childTestEls.length).toBe(2);
expect(DOM.hasClass(childTestEls[0].domElement, 'parent')).toBe(true);
expect(DOM.hasClass(childTestEls[1].domElement, 'parentnested')).toBe(true);
async.done();
});
}));
it('should allow injecting from the element injector',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb, async) => {
tcb.createAsync(ParentComp)
.then((rootTestComponent) => {
rootTestComponent.detectChanges();
expect(rootTestComponent.componentViewChildren[0].inject(Logger).log)
.toEqual(['parent', 'nestedparent', 'child', 'nestedchild']);
async.done();
});
}));
it('should override a template',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb, async) => {
tcb.overrideTemplate(MockChildComp, '<span>Modified</span>')
.createAsync(MockChildComp)
.then((rootTestComponent) => {
rootTestComponent.detectChanges();
var childSpans = DOM.querySelectorAll(rootTestComponent.domElement, 'span');
expect(childSpans.length).toEqual(1);
expect(DOM.getInnerHTML(childSpans[0])).toEqual('Modified');
async.done();
});
}));
it('should override a view',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb, async) => {
tcb.overrideView(ChildComp,
new viewAnn.View({template: '<span>Modified {{childBinding}}</span>'}))
.createAsync(ChildComp)
.then((rootTestComponent) => {
rootTestComponent.detectChanges();
var childSpans = DOM.querySelectorAll(rootTestComponent.domElement, 'span');
expect(childSpans.length).toEqual(1);
expect(DOM.getInnerHTML(childSpans[0])).toEqual('Modified Original');
async.done();
});
}));
it('should override component dependencies',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb, async) => {
tcb.overrideDirective(ParentComp, ChildComp, MockChildComp)
.createAsync(ParentComp)
.then((rootTestComponent) => {
rootTestComponent.detectChanges();
var childSpans = DOM.querySelectorAll(rootTestComponent.domElement, 'span');
expect(childSpans.length).toEqual(3);
expect(DOM.getInnerHTML(childSpans[2])).toEqual('Mock');
async.done();
});
}));
it('should trigger event handlers',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb, async) => {
tcb.createAsync(EventsComp)
.then((rootTestComponent) => {
rootTestComponent.detectChanges();
expect(rootTestComponent.componentInstance.clicked).toBe(false);
expect(rootTestComponent.componentInstance.customed).toBe(false);
rootTestComponent.componentViewChildren[0].triggerEventHandler('click', {});
expect(rootTestComponent.componentInstance.clicked).toBe(true);
rootTestComponent.componentViewChildren[1].triggerEventHandler('myevent', {});
expect(rootTestComponent.componentInstance.customed).toBe(true);
async.done();
});
}));
});
}