test(TestBed): initial implementation

This commit is contained in:
Victor Berchet 2015-03-30 16:37:33 +02:00
parent 57e308dd46
commit 438c2b31e4
9 changed files with 218 additions and 13 deletions

View File

@ -2,6 +2,7 @@ export * from './src/core/annotations/visibility';
export * from './src/core/compiler/interfaces';
export * from './src/core/annotations/template';
export * from './src/core/application';
export * from './src/core/application_tokens';
export * from './src/core/annotations/di';
export * from './src/core/compiler/compiler';

View File

@ -28,6 +28,13 @@ import {StyleInliner} from 'angular2/src/render/dom/shadow_dom/style_inliner';
import {Component} from 'angular2/src/core/annotations/annotations';
import {PrivateComponentLoader} from 'angular2/src/core/compiler/private_component_loader';
import {TestabilityRegistry, Testability} from 'angular2/src/core/testability/testability';
import {
appViewToken,
appChangeDetectorToken,
appElementToken,
appComponentAnnotatedTypeToken,
appDocumentToken,
} from './application_tokens';
var _rootInjector: Injector;
@ -37,12 +44,6 @@ var _rootBindings = [
TestabilityRegistry
];
export var appViewToken = new OpaqueToken('AppView');
export var appChangeDetectorToken = new OpaqueToken('AppChangeDetector');
export var appElementToken = new OpaqueToken('AppElement');
export var appComponentAnnotatedTypeToken = new OpaqueToken('AppComponentAnnotatedType');
export var appDocumentToken = new OpaqueToken('AppDocument');
function _injectorBindings(appComponentType): List<Binding> {
return [
bind(appDocumentToken).toValue(DOM.defaultDoc()),

View File

@ -0,0 +1,7 @@
import {OpaqueToken} from 'angular2/di';
export var appViewToken = new OpaqueToken('AppView');
export var appChangeDetectorToken = new OpaqueToken('AppChangeDetector');
export var appElementToken = new OpaqueToken('AppElement');
export var appComponentAnnotatedTypeToken = new OpaqueToken('AppComponentAnnotatedType');
export var appDocumentToken = new OpaqueToken('AppDocument');

View File

@ -0,0 +1,15 @@
import {VmTurnZone} from 'angular2/src/core/zone/vm_turn_zone';
export class MockVmTurnZone extends VmTurnZone {
constructor() {
super({enableLongStackTrace: false});
}
run(fn) {
fn();
}
runOutsideAngular(fn) {
fn();
}
}

View File

@ -0,0 +1,8 @@
import 'dart:mirrors';
Type getTypeOf(instance) => instance.runtimeType;
dynamic instantiateType(Type type, [List params = const []]) {
var cm = reflectClass(type);
return cm.newInstance(new Symbol(''), params).reflectee;
}

View File

@ -0,0 +1,9 @@
export function getTypeOf(instance) {
return instance.constructor;
}
export function instantiateType(type: Function, params: Array = []) {
var instance = Object.create(type.prototype);
instance.constructor.apply(instance, params);
return instance;
}

View File

@ -0,0 +1,131 @@
import {Injector} from 'angular2/di';
import {Type, isPresent, BaseException} from 'angular2/src/facade/lang';
import {Promise} from 'angular2/src/facade/async';
import {isBlank} from 'angular2/src/facade/lang';
import {List} from 'angular2/src/facade/collection';
import {Template} from 'angular2/src/core/annotations/template';
import {TemplateResolver} from 'angular2/src/core/compiler/template_resolver';
import {Compiler} from 'angular2/src/core/compiler/compiler';
import {View} from 'angular2/src/core/compiler/view';
import {EventManager} from 'angular2/src/render/dom/events/event_manager';
import {queryView} from './utils';
import {instantiateType, getTypeOf} from './lang_utils';
export class TestBed {
_injector: Injector;
constructor(injector: Injector) {
this._injector = injector;
}
/**
* Overrides the [Template] of a [Component].
*
* @see setInlineTemplate() to only override the html
*
* @param {Type} component
* @param {Template} template
*/
overrideTemplate(component: Type, template: Template): void {
this._injector.get(TemplateResolver).setTemplate(component, template);
}
/**
* Overrides only the html of a [Component].
* All the other propoerties of the component's [Template] are preserved.
*
* @param {Type} component
* @param {string} html
*/
setInlineTemplate(component: Type, html: string): void {
this._injector.get(TemplateResolver).setInlineTemplate(component, html);
}
/**
* Overrides the directives from the component [Template].
*
* @param {Type} component
* @param {Type} from
* @param {Type} to
*/
overrideDirective(component: Type, from: Type, to: Type): void {
this._injector.get(TemplateResolver).overrideTemplateDirective(component, from, to);
}
/**
* Creates a [View] for the given component.
*
* Only either a component or a context needs to be specified but both can be provided for
* advanced use cases (ie subclassing the context).
*
* @param {Type} component
* @param {*} context
* @param {string} html Use as the component template when specified (shortcut for setInlineTemplate)
* @return {Promise<ViewProxy>}
*/
createView(component: Type,
{context = null, html = null}: {context:any, html: string} = {}): Promise<View> {
if (isBlank(component) && isBlank(context)) {
throw new BaseException('You must specified at least a component or a context');
}
if (isBlank(component)) {
component = getTypeOf(context);
} else if (isBlank(context)) {
context = instantiateType(component);
}
if (isPresent(html)) {
this.setInlineTemplate(component, html);
}
return this._injector.get(Compiler).compile(component).then((pv) => {
var eventManager = this._injector.get(EventManager);
var view = pv.instantiate(null, eventManager);
view.hydrate(this._injector, null, null, context, null);
return new ViewProxy(view);
});
}
}
/**
* Proxy to [View] return by [TestBed.createView] which offers a high level API for tests.
*/
export class ViewProxy {
_view: View;
constructor(view: View) {
this._view = view;
}
get context(): any {
return this._view.context;
}
get nodes(): List {
return this._view.nodes;
}
detectChanges(): void {
this._view.changeDetector.detectChanges();
}
querySelector(selector) {
return queryView(this._view, selector);
}
/**
* @returns {View} return the underlying [View].
*
* Prefer using the other methods which hide implementation details.
*/
get rawView(): View {
return this._view;
}
}

View File

@ -1,4 +1,5 @@
import {bind} from 'angular2/di';
import {Compiler, CompilerCache} from 'angular2/src/core/compiler/compiler';
import {Reflector, reflector} from 'angular2/src/reflection/reflection';
import {Parser, Lexer, ChangeDetection, dynamicChangeDetection} from 'angular2/change_detection';
@ -6,13 +7,25 @@ import {ExceptionHandler} from 'angular2/src/core/exception_handler';
import {TemplateLoader} from 'angular2/src/render/dom/compiler/template_loader';
import {TemplateResolver} from 'angular2/src/core/compiler/template_resolver';
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
import {ShadowDomStrategy, NativeShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy';
import {ShadowDomStrategy, EmulatedUnscopedShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy';
import {XHR} from 'angular2/src/services/xhr';
import {XHRMock} from 'angular2/src/mock/xhr_mock';
import {ComponentUrlMapper} from 'angular2/src/core/compiler/component_url_mapper';
import {UrlResolver} from 'angular2/src/services/url_resolver';
import {StyleUrlResolver} from 'angular2/src/render/dom/shadow_dom/style_url_resolver';
import {StyleInliner} from 'angular2/src/render/dom/shadow_dom/style_inliner';
import {VmTurnZone} from 'angular2/src/core/zone/vm_turn_zone';
import {DOM} from 'angular2/src/dom/dom_adapter';
import {appDocumentToken} from 'angular2/src/core/application_tokens';
import {EventManager, DomEventsPlugin} from 'angular2/src/render/dom/events/event_manager';
import {MockTemplateResolver} from 'angular2/src/mock/template_resolver_mock';
import {XHRMock} from 'angular2/src/mock/xhr_mock';
import {MockVmTurnZone} from 'angular2/src/mock/vm_turn_zone_mock';
import {TestBed} from './test_bed';
import {Injector} from 'angular2/di';
@ -40,11 +53,23 @@ function _getRootBindings() {
* @returns {*[]}
*/
function _getAppBindings() {
var appDoc;
// The document is only available in browser environment
try {
appDoc = DOM.defaultDoc();
} catch(e) {
appDoc = null;
}
return [
bind(ShadowDomStrategy).toClass(NativeShadowDomStrategy),
bind(appDocumentToken).toValue(appDoc),
bind(ShadowDomStrategy).toFactory(
(styleUrlResolver, doc) => new EmulatedUnscopedShadowDomStrategy(styleUrlResolver, doc.head),
[StyleUrlResolver, appDocumentToken]),
Compiler,
CompilerCache,
TemplateResolver,
bind(TemplateResolver).toClass(MockTemplateResolver),
bind(ChangeDetection).toValue(dynamicChangeDetection),
TemplateLoader,
DirectiveMetadataReader,
@ -55,7 +80,15 @@ function _getAppBindings() {
ComponentUrlMapper,
UrlResolver,
StyleUrlResolver,
StyleInliner
StyleInliner,
TestBed,
bind(VmTurnZone).toClass(MockVmTurnZone),
bind(EventManager).toFactory((zone) => {
var plugins = [
new DomEventsPlugin(),
];
return new EventManager(plugins, zone);
}, [VmTurnZone]),
];
}

View File

@ -10,8 +10,8 @@ import {
xdescribe,
xit,
} from 'angular2/test_lib';
import {bootstrap, appDocumentToken, appElementToken}
from 'angular2/src/core/application';
import {bootstrap} from 'angular2/src/core/application';
import {appDocumentToken, appElementToken} from 'angular2/src/core/application_tokens';
import {Component, Decorator} from 'angular2/src/core/annotations/annotations';
import {DOM} from 'angular2/src/dom/dom_adapter';
import {ListWrapper} from 'angular2/src/facade/collection';