2016-04-12 12:40:37 -04:00
|
|
|
import {
|
|
|
|
ComponentRef,
|
|
|
|
DynamicComponentLoader,
|
|
|
|
Injector,
|
|
|
|
Injectable,
|
|
|
|
ViewMetadata,
|
|
|
|
ElementRef,
|
|
|
|
EmbeddedViewRef,
|
2016-04-13 20:05:17 -04:00
|
|
|
ChangeDetectorRef,
|
2016-04-12 12:40:37 -04:00
|
|
|
provide
|
|
|
|
} from 'angular2/core';
|
2016-01-06 17:13:44 -05:00
|
|
|
import {DirectiveResolver, ViewResolver} from 'angular2/compiler';
|
2015-05-15 19:42:52 -04:00
|
|
|
|
2015-11-06 20:34:07 -05:00
|
|
|
import {Type, isPresent, isBlank} from 'angular2/src/facade/lang';
|
2016-01-06 17:13:44 -05:00
|
|
|
import {PromiseWrapper} from 'angular2/src/facade/async';
|
2015-11-06 20:34:07 -05:00
|
|
|
import {ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
|
2015-05-15 19:42:52 -04:00
|
|
|
|
|
|
|
import {el} from './utils';
|
|
|
|
|
2015-11-17 18:24:36 -05:00
|
|
|
import {DOCUMENT} from 'angular2/src/platform/dom/dom_tokens';
|
2015-11-19 18:09:34 -05:00
|
|
|
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
|
2015-05-15 19:42:52 -04:00
|
|
|
|
2016-01-14 00:35:21 -05:00
|
|
|
import {DebugNode, DebugElement, getDebugNode} from 'angular2/src/core/debug/debug_node';
|
2015-05-15 19:42:52 -04:00
|
|
|
|
2016-01-06 17:13:44 -05:00
|
|
|
import {tick} from './fake_async';
|
2015-11-03 16:51:33 -05:00
|
|
|
|
|
|
|
/**
|
2015-12-02 14:54:10 -05:00
|
|
|
* Fixture for debugging and testing a component.
|
2015-11-03 16:51:33 -05:00
|
|
|
*/
|
2016-04-13 20:05:17 -04:00
|
|
|
export class ComponentFixture {
|
2015-11-30 15:49:22 -05:00
|
|
|
/**
|
|
|
|
* The DebugElement associated with the root element of this component.
|
|
|
|
*/
|
2015-10-06 09:53:39 -04:00
|
|
|
debugElement: DebugElement;
|
|
|
|
|
2015-11-30 15:49:22 -05:00
|
|
|
/**
|
|
|
|
* The instance of the root component class.
|
|
|
|
*/
|
|
|
|
componentInstance: any;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The native element at the root of the component.
|
|
|
|
*/
|
|
|
|
nativeElement: any;
|
|
|
|
|
2016-01-14 00:35:21 -05:00
|
|
|
/**
|
|
|
|
* The ElementRef for the element at the root of the component.
|
|
|
|
*/
|
|
|
|
elementRef: ElementRef;
|
|
|
|
|
2015-11-30 15:49:22 -05:00
|
|
|
/**
|
2016-04-13 20:05:17 -04:00
|
|
|
* The ComponentRef for the component
|
2015-11-30 15:49:22 -05:00
|
|
|
*/
|
2016-04-13 20:05:17 -04:00
|
|
|
componentRef: ComponentRef;
|
2015-11-30 15:49:22 -05:00
|
|
|
|
|
|
|
/**
|
2016-04-13 20:05:17 -04:00
|
|
|
* The ChangeDetectorRef for the component
|
2015-11-30 15:49:22 -05:00
|
|
|
*/
|
2016-04-13 20:05:17 -04:00
|
|
|
changeDetectorRef: ChangeDetectorRef;
|
2015-05-15 19:42:52 -04:00
|
|
|
|
|
|
|
constructor(componentRef: ComponentRef) {
|
2016-04-13 20:05:17 -04:00
|
|
|
this.changeDetectorRef = componentRef.changeDetectorRef;
|
|
|
|
this.elementRef = componentRef.location;
|
|
|
|
this.debugElement = <DebugElement>getDebugNode(this.elementRef.nativeElement);
|
|
|
|
this.componentInstance = componentRef.instance;
|
|
|
|
this.nativeElement = this.elementRef.nativeElement;
|
|
|
|
this.componentRef = componentRef;
|
2015-05-15 19:42:52 -04:00
|
|
|
}
|
|
|
|
|
2016-04-13 20:05:17 -04:00
|
|
|
/**
|
|
|
|
* Trigger a change detection cycle for the component.
|
|
|
|
*/
|
2016-01-06 17:13:44 -05:00
|
|
|
detectChanges(checkNoChanges: boolean = true): void {
|
2016-04-13 20:05:17 -04:00
|
|
|
this.changeDetectorRef.detectChanges();
|
2016-01-06 17:13:44 -05:00
|
|
|
if (checkNoChanges) {
|
|
|
|
this.checkNoChanges();
|
|
|
|
}
|
2015-05-15 19:42:52 -04:00
|
|
|
}
|
|
|
|
|
2016-04-13 20:05:17 -04:00
|
|
|
checkNoChanges(): void { this.changeDetectorRef.checkNoChanges(); }
|
2016-01-06 17:13:44 -05:00
|
|
|
|
2016-04-13 20:05:17 -04:00
|
|
|
/**
|
|
|
|
* Trigger component destruction.
|
|
|
|
*/
|
|
|
|
destroy(): void { this.componentRef.destroy(); }
|
2015-05-15 19:42:52 -04:00
|
|
|
}
|
|
|
|
|
2015-05-28 18:02:20 -04:00
|
|
|
var _nextRootElementId = 0;
|
2015-05-15 19:42:52 -04:00
|
|
|
|
|
|
|
/**
|
2015-10-31 12:50:19 -04:00
|
|
|
* Builds a ComponentFixture for use in component level tests.
|
2015-05-15 19:42:52 -04:00
|
|
|
*/
|
|
|
|
@Injectable()
|
|
|
|
export class TestComponentBuilder {
|
2015-10-09 20:21:25 -04:00
|
|
|
/** @internal */
|
2015-09-29 14:11:06 -04:00
|
|
|
_bindingsOverrides = new Map<Type, any[]>();
|
2015-10-09 20:21:25 -04:00
|
|
|
/** @internal */
|
2015-09-29 14:11:06 -04:00
|
|
|
_directiveOverrides = new Map<Type, Map<Type, Type>>();
|
2015-10-09 20:21:25 -04:00
|
|
|
/** @internal */
|
2015-09-29 14:11:06 -04:00
|
|
|
_templateOverrides = new Map<Type, string>();
|
2015-10-09 20:21:25 -04:00
|
|
|
/** @internal */
|
2015-09-29 14:11:06 -04:00
|
|
|
_viewBindingsOverrides = new Map<Type, any[]>();
|
2015-10-09 20:21:25 -04:00
|
|
|
/** @internal */
|
2015-09-29 14:11:06 -04:00
|
|
|
_viewOverrides = new Map<Type, ViewMetadata>();
|
|
|
|
|
|
|
|
|
|
|
|
constructor(private _injector: Injector) {}
|
2015-05-15 19:42:52 -04:00
|
|
|
|
2015-10-09 20:21:25 -04:00
|
|
|
/** @internal */
|
2015-05-15 19:42:52 -04:00
|
|
|
_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);
|
2016-01-06 17:13:44 -05:00
|
|
|
clone._bindingsOverrides = MapWrapper.clone(this._bindingsOverrides);
|
|
|
|
clone._viewBindingsOverrides = MapWrapper.clone(this._viewBindingsOverrides);
|
2015-05-15 19:42:52 -04:00
|
|
|
return clone;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-08-14 13:03:45 -04:00
|
|
|
* Overrides only the html of a {@link ComponentMetadata}.
|
|
|
|
* All the other properties of the component's {@link ViewMetadata} are preserved.
|
2015-05-15 19:42:52 -04:00
|
|
|
*
|
|
|
|
* @param {Type} component
|
|
|
|
* @param {string} html
|
|
|
|
*
|
|
|
|
* @return {TestComponentBuilder}
|
|
|
|
*/
|
|
|
|
overrideTemplate(componentType: Type, template: string): TestComponentBuilder {
|
|
|
|
var clone = this._clone();
|
2015-06-17 19:21:40 -04:00
|
|
|
clone._templateOverrides.set(componentType, template);
|
2015-05-15 19:42:52 -04:00
|
|
|
return clone;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-08-14 13:03:45 -04:00
|
|
|
* Overrides a component's {@link ViewMetadata}.
|
2015-05-15 19:42:52 -04:00
|
|
|
*
|
|
|
|
* @param {Type} component
|
|
|
|
* @param {view} View
|
|
|
|
*
|
|
|
|
* @return {TestComponentBuilder}
|
|
|
|
*/
|
2015-08-14 13:03:45 -04:00
|
|
|
overrideView(componentType: Type, view: ViewMetadata): TestComponentBuilder {
|
2015-05-15 19:42:52 -04:00
|
|
|
var clone = this._clone();
|
2015-06-17 19:21:40 -04:00
|
|
|
clone._viewOverrides.set(componentType, view);
|
2015-05-15 19:42:52 -04:00
|
|
|
return clone;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-08-14 13:03:45 -04:00
|
|
|
* Overrides the directives from the component {@link ViewMetadata}.
|
2015-05-15 19:42:52 -04:00
|
|
|
*
|
|
|
|
* @param {Type} component
|
|
|
|
* @param {Type} from
|
|
|
|
* @param {Type} to
|
|
|
|
*
|
|
|
|
* @return {TestComponentBuilder}
|
|
|
|
*/
|
|
|
|
overrideDirective(componentType: Type, from: Type, to: Type): TestComponentBuilder {
|
|
|
|
var clone = this._clone();
|
2015-06-17 19:21:40 -04:00
|
|
|
var overridesForComponent = clone._directiveOverrides.get(componentType);
|
2015-05-15 19:42:52 -04:00
|
|
|
if (!isPresent(overridesForComponent)) {
|
2015-09-29 14:11:06 -04:00
|
|
|
clone._directiveOverrides.set(componentType, new Map<Type, Type>());
|
2015-06-17 19:21:40 -04:00
|
|
|
overridesForComponent = clone._directiveOverrides.get(componentType);
|
2015-05-15 19:42:52 -04:00
|
|
|
}
|
2015-06-17 19:21:40 -04:00
|
|
|
overridesForComponent.set(from, to);
|
2015-05-15 19:42:52 -04:00
|
|
|
return clone;
|
|
|
|
}
|
|
|
|
|
2015-09-08 13:14:57 -04:00
|
|
|
/**
|
2015-10-11 01:11:13 -04:00
|
|
|
* Overrides one or more injectables configured via `providers` metadata property of a directive
|
|
|
|
* or
|
2015-09-08 13:14:57 -04:00
|
|
|
* component.
|
2015-10-11 01:11:13 -04:00
|
|
|
* Very useful when certain providers need to be mocked out.
|
2015-09-08 13:14:57 -04:00
|
|
|
*
|
2015-10-11 01:11:13 -04:00
|
|
|
* The providers specified via this method are appended to the existing `providers` causing the
|
|
|
|
* duplicated providers to
|
2015-09-08 13:14:57 -04:00
|
|
|
* be overridden.
|
|
|
|
*
|
|
|
|
* @param {Type} component
|
2015-10-11 01:11:13 -04:00
|
|
|
* @param {any[]} providers
|
2015-09-08 13:14:57 -04:00
|
|
|
*
|
|
|
|
* @return {TestComponentBuilder}
|
|
|
|
*/
|
2015-10-11 01:11:13 -04:00
|
|
|
overrideProviders(type: Type, providers: any[]): TestComponentBuilder {
|
2015-09-08 13:14:57 -04:00
|
|
|
var clone = this._clone();
|
2015-10-11 01:11:13 -04:00
|
|
|
clone._bindingsOverrides.set(type, providers);
|
2015-09-08 13:14:57 -04:00
|
|
|
return clone;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-10-11 01:11:13 -04:00
|
|
|
* @deprecated
|
|
|
|
*/
|
|
|
|
overrideBindings(type: Type, providers: any[]): TestComponentBuilder {
|
|
|
|
return this.overrideProviders(type, providers);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Overrides one or more injectables configured via `providers` metadata property of a directive
|
|
|
|
* or
|
2015-09-08 13:14:57 -04:00
|
|
|
* component.
|
2015-10-11 01:11:13 -04:00
|
|
|
* Very useful when certain providers need to be mocked out.
|
2015-09-08 13:14:57 -04:00
|
|
|
*
|
2015-10-11 01:11:13 -04:00
|
|
|
* The providers specified via this method are appended to the existing `providers` causing the
|
|
|
|
* duplicated providers to
|
2015-09-08 13:14:57 -04:00
|
|
|
* be overridden.
|
|
|
|
*
|
|
|
|
* @param {Type} component
|
2015-10-11 01:11:13 -04:00
|
|
|
* @param {any[]} providers
|
2015-09-08 13:14:57 -04:00
|
|
|
*
|
|
|
|
* @return {TestComponentBuilder}
|
|
|
|
*/
|
2015-10-11 01:11:13 -04:00
|
|
|
overrideViewProviders(type: Type, providers: any[]): TestComponentBuilder {
|
2015-09-08 13:14:57 -04:00
|
|
|
var clone = this._clone();
|
2015-10-11 01:11:13 -04:00
|
|
|
clone._viewBindingsOverrides.set(type, providers);
|
2015-09-08 13:14:57 -04:00
|
|
|
return clone;
|
|
|
|
}
|
|
|
|
|
2015-10-11 01:11:13 -04:00
|
|
|
/**
|
|
|
|
* @deprecated
|
|
|
|
*/
|
|
|
|
overrideViewBindings(type: Type, providers: any[]): TestComponentBuilder {
|
|
|
|
return this.overrideViewProviders(type, providers);
|
|
|
|
}
|
|
|
|
|
2015-05-15 19:42:52 -04:00
|
|
|
/**
|
2015-10-31 12:50:19 -04:00
|
|
|
* Builds and returns a ComponentFixture.
|
2015-05-15 19:42:52 -04:00
|
|
|
*
|
2015-10-31 12:50:19 -04:00
|
|
|
* @return {Promise<ComponentFixture>}
|
2015-05-15 19:42:52 -04:00
|
|
|
*/
|
2015-10-31 12:50:19 -04:00
|
|
|
createAsync(rootComponentType: Type): Promise<ComponentFixture> {
|
2015-09-08 13:14:57 -04:00
|
|
|
var mockDirectiveResolver = this._injector.get(DirectiveResolver);
|
2015-06-24 04:54:04 -04:00
|
|
|
var mockViewResolver = this._injector.get(ViewResolver);
|
2015-10-08 19:01:18 -04:00
|
|
|
this._viewOverrides.forEach((view, type) => mockViewResolver.setView(type, view));
|
2016-04-12 12:40:37 -04:00
|
|
|
this._templateOverrides.forEach((template, type) =>
|
|
|
|
mockViewResolver.setInlineTemplate(type, template));
|
2015-10-08 19:01:18 -04:00
|
|
|
this._directiveOverrides.forEach((overrides, component) => {
|
|
|
|
overrides.forEach(
|
|
|
|
(to, from) => { mockViewResolver.overrideViewDirective(component, from, to); });
|
2015-05-15 19:42:52 -04:00
|
|
|
});
|
2016-04-12 12:40:37 -04:00
|
|
|
this._bindingsOverrides.forEach((bindings, type) =>
|
|
|
|
mockDirectiveResolver.setBindingsOverride(type, bindings));
|
2015-09-08 13:14:57 -04:00
|
|
|
this._viewBindingsOverrides.forEach(
|
|
|
|
(bindings, type) => mockDirectiveResolver.setViewBindingsOverride(type, bindings));
|
|
|
|
|
2015-05-28 18:02:20 -04:00
|
|
|
var rootElId = `root${_nextRootElementId++}`;
|
|
|
|
var rootEl = el(`<div id="${rootElId}"></div>`);
|
2015-08-11 00:42:47 -04:00
|
|
|
var doc = this._injector.get(DOCUMENT);
|
2015-05-15 19:42:52 -04:00
|
|
|
|
|
|
|
// TODO(juliemr): can/should this be optional?
|
2015-08-06 06:28:36 -04:00
|
|
|
var oldRoots = DOM.querySelectorAll(doc, '[id^=root]');
|
|
|
|
for (var i = 0; i < oldRoots.length; i++) {
|
|
|
|
DOM.remove(oldRoots[i]);
|
|
|
|
}
|
2015-05-15 19:42:52 -04:00
|
|
|
DOM.appendChild(doc.body, rootEl);
|
2015-06-26 18:59:18 -04:00
|
|
|
|
2016-02-19 14:49:31 -05:00
|
|
|
var promise: Promise<ComponentRef> =
|
|
|
|
this._injector.get(DynamicComponentLoader)
|
|
|
|
.loadAsRoot(rootComponentType, `#${rootElId}`, this._injector);
|
2016-04-13 20:05:17 -04:00
|
|
|
return promise.then((componentRef) => { return new ComponentFixture(componentRef); });
|
2015-05-15 19:42:52 -04:00
|
|
|
}
|
2016-01-06 17:13:44 -05:00
|
|
|
|
|
|
|
createFakeAsync(rootComponentType: Type): ComponentFixture {
|
|
|
|
var result;
|
|
|
|
var error;
|
|
|
|
PromiseWrapper.then(this.createAsync(rootComponentType), (_result) => { result = _result; },
|
|
|
|
(_error) => { error = _error; });
|
|
|
|
tick();
|
|
|
|
if (isPresent(error)) {
|
|
|
|
throw error;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
2015-05-15 19:42:52 -04:00
|
|
|
}
|