feat(TestComponentBuilder): add #overrideBindings and #overrideViewBindings
Closes #4052
This commit is contained in:
parent
39a6f85e95
commit
f91c087c46
@ -0,0 +1,65 @@
|
|||||||
|
import {Map, MapWrapper, ListWrapper} from 'angular2/src/core/facade/collection';
|
||||||
|
import {
|
||||||
|
Type,
|
||||||
|
isPresent,
|
||||||
|
BaseException,
|
||||||
|
stringify,
|
||||||
|
isBlank,
|
||||||
|
print
|
||||||
|
} from 'angular2/src/core/facade/lang';
|
||||||
|
import {DirectiveMetadata, ComponentMetadata} from '../core/metadata';
|
||||||
|
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
||||||
|
|
||||||
|
export class MockDirectiveResolver extends DirectiveResolver {
|
||||||
|
private _bindingsOverrides: Map<Type, any[]> = new Map();
|
||||||
|
private _viewBindingsOverrides: Map<Type, any[]> = new Map();
|
||||||
|
|
||||||
|
resolve(type: Type): DirectiveMetadata {
|
||||||
|
var dm = super.resolve(type);
|
||||||
|
|
||||||
|
var bindingsOverride = this._bindingsOverrides.get(type);
|
||||||
|
var viewBindingsOverride = this._viewBindingsOverrides.get(type);
|
||||||
|
|
||||||
|
var bindings = dm.bindings;
|
||||||
|
if (isPresent(bindingsOverride)) {
|
||||||
|
bindings = dm.bindings.concat(bindingsOverride);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dm instanceof ComponentMetadata) {
|
||||||
|
var viewBindings = dm.viewBindings;
|
||||||
|
if (isPresent(viewBindingsOverride)) {
|
||||||
|
viewBindings = dm.viewBindings.concat(viewBindingsOverride);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ComponentMetadata({
|
||||||
|
selector: dm.selector,
|
||||||
|
properties: dm.properties,
|
||||||
|
events: dm.events,
|
||||||
|
host: dm.host,
|
||||||
|
bindings: bindings,
|
||||||
|
exportAs: dm.exportAs,
|
||||||
|
compileChildren: dm.compileChildren,
|
||||||
|
changeDetection: dm.changeDetection,
|
||||||
|
viewBindings: viewBindings
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DirectiveMetadata({
|
||||||
|
selector: dm.selector,
|
||||||
|
properties: dm.properties,
|
||||||
|
events: dm.events,
|
||||||
|
host: dm.host,
|
||||||
|
bindings: bindings,
|
||||||
|
exportAs: dm.exportAs,
|
||||||
|
compileChildren: dm.compileChildren
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setBindingsOverride(type: Type, bindings: any[]): void {
|
||||||
|
this._bindingsOverrides.set(type, bindings);
|
||||||
|
}
|
||||||
|
|
||||||
|
setViewBindingsOverride(type: Type, viewBindings: any[]): void {
|
||||||
|
this._viewBindingsOverrides.set(type, viewBindings);
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,7 @@ import {ListWrapper, MapWrapper} from 'angular2/src/core/facade/collection';
|
|||||||
|
|
||||||
import {ViewMetadata} from '../core/metadata';
|
import {ViewMetadata} from '../core/metadata';
|
||||||
|
|
||||||
|
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
||||||
import {ViewResolver} from 'angular2/src/core/compiler/view_resolver';
|
import {ViewResolver} from 'angular2/src/core/compiler/view_resolver';
|
||||||
import {AppView} from 'angular2/src/core/compiler/view';
|
import {AppView} from 'angular2/src/core/compiler/view';
|
||||||
import {internalView, ViewRef} from 'angular2/src/core/compiler/view_ref';
|
import {internalView, ViewRef} from 'angular2/src/core/compiler/view_ref';
|
||||||
@ -47,16 +48,19 @@ var _nextRootElementId = 0;
|
|||||||
*/
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class TestComponentBuilder {
|
export class TestComponentBuilder {
|
||||||
_injector: Injector;
|
_bindingsOverrides: Map<Type, any[]>;
|
||||||
_viewOverrides: Map<Type, ViewMetadata>;
|
|
||||||
_directiveOverrides: Map<Type, Map<Type, Type>>;
|
_directiveOverrides: Map<Type, Map<Type, Type>>;
|
||||||
_templateOverrides: Map<Type, string>;
|
_templateOverrides: Map<Type, string>;
|
||||||
|
_viewBindingsOverrides: Map<Type, any[]>;
|
||||||
|
_viewOverrides: Map<Type, ViewMetadata>;
|
||||||
|
|
||||||
constructor(injector: Injector) {
|
|
||||||
this._injector = injector;
|
constructor(private _injector: Injector) {
|
||||||
this._viewOverrides = new Map();
|
this._bindingsOverrides = new Map();
|
||||||
this._directiveOverrides = new Map();
|
this._directiveOverrides = new Map();
|
||||||
this._templateOverrides = new Map();
|
this._templateOverrides = new Map();
|
||||||
|
this._viewBindingsOverrides = new Map();
|
||||||
|
this._viewOverrides = new Map();
|
||||||
}
|
}
|
||||||
|
|
||||||
_clone(): TestComponentBuilder {
|
_clone(): TestComponentBuilder {
|
||||||
@ -116,12 +120,53 @@ export class TestComponentBuilder {
|
|||||||
return clone;
|
return clone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides one or more injectables configured via `bindings` metadata property of a directive or
|
||||||
|
* component.
|
||||||
|
* Very useful when certain bindings need to be mocked out.
|
||||||
|
*
|
||||||
|
* The bindings specified via this method are appended to the existing `bindings` causing the
|
||||||
|
* duplicated bindings to
|
||||||
|
* be overridden.
|
||||||
|
*
|
||||||
|
* @param {Type} component
|
||||||
|
* @param {any[]} bindings
|
||||||
|
*
|
||||||
|
* @return {TestComponentBuilder}
|
||||||
|
*/
|
||||||
|
overrideBindings(type: Type, bindings: any[]): TestComponentBuilder {
|
||||||
|
var clone = this._clone();
|
||||||
|
clone._bindingsOverrides.set(type, bindings);
|
||||||
|
return clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides one or more injectables configured via `bindings` metadata property of a directive or
|
||||||
|
* component.
|
||||||
|
* Very useful when certain bindings need to be mocked out.
|
||||||
|
*
|
||||||
|
* The bindings specified via this method are appended to the existing `bindings` causing the
|
||||||
|
* duplicated bindings to
|
||||||
|
* be overridden.
|
||||||
|
*
|
||||||
|
* @param {Type} component
|
||||||
|
* @param {any[]} bindings
|
||||||
|
*
|
||||||
|
* @return {TestComponentBuilder}
|
||||||
|
*/
|
||||||
|
overrideViewBindings(type: Type, bindings: any[]): TestComponentBuilder {
|
||||||
|
var clone = this._clone();
|
||||||
|
clone._viewBindingsOverrides.set(type, bindings);
|
||||||
|
return clone;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds and returns a RootTestComponent.
|
* Builds and returns a RootTestComponent.
|
||||||
*
|
*
|
||||||
* @return {Promise<RootTestComponent>}
|
* @return {Promise<RootTestComponent>}
|
||||||
*/
|
*/
|
||||||
createAsync(rootComponentType: Type): Promise<RootTestComponent> {
|
createAsync(rootComponentType: Type): Promise<RootTestComponent> {
|
||||||
|
var mockDirectiveResolver = this._injector.get(DirectiveResolver);
|
||||||
var mockViewResolver = this._injector.get(ViewResolver);
|
var mockViewResolver = this._injector.get(ViewResolver);
|
||||||
MapWrapper.forEach(this._viewOverrides,
|
MapWrapper.forEach(this._viewOverrides,
|
||||||
(view, type) => { mockViewResolver.setView(type, view); });
|
(view, type) => { mockViewResolver.setView(type, view); });
|
||||||
@ -133,6 +178,11 @@ export class TestComponentBuilder {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this._bindingsOverrides.forEach((bindings, type) =>
|
||||||
|
mockDirectiveResolver.setBindingsOverride(type, bindings));
|
||||||
|
this._viewBindingsOverrides.forEach(
|
||||||
|
(bindings, type) => mockDirectiveResolver.setViewBindingsOverride(type, bindings));
|
||||||
|
|
||||||
var rootElId = `root${_nextRootElementId++}`;
|
var rootElId = `root${_nextRootElementId++}`;
|
||||||
var rootEl = el(`<div id="${rootElId}"></div>`);
|
var rootEl = el(`<div id="${rootElId}"></div>`);
|
||||||
var doc = this._injector.get(DOCUMENT);
|
var doc = this._injector.get(DOCUMENT);
|
||||||
|
@ -36,6 +36,7 @@ import {
|
|||||||
EVENT_MANAGER_PLUGINS
|
EVENT_MANAGER_PLUGINS
|
||||||
} from 'angular2/src/core/render/dom/events/event_manager';
|
} from 'angular2/src/core/render/dom/events/event_manager';
|
||||||
|
|
||||||
|
import {MockDirectiveResolver} from 'angular2/src/mock/directive_resolver_mock';
|
||||||
import {MockViewResolver} from 'angular2/src/mock/view_resolver_mock';
|
import {MockViewResolver} from 'angular2/src/mock/view_resolver_mock';
|
||||||
import {MockXHR} from 'angular2/src/core/render/xhr_mock';
|
import {MockXHR} from 'angular2/src/core/render/xhr_mock';
|
||||||
import {MockLocationStrategy} from 'angular2/src/mock/mock_location_strategy';
|
import {MockLocationStrategy} from 'angular2/src/mock/mock_location_strategy';
|
||||||
@ -125,6 +126,7 @@ function _getAppBindings() {
|
|||||||
bind(APP_VIEW_POOL_CAPACITY).toValue(500),
|
bind(APP_VIEW_POOL_CAPACITY).toValue(500),
|
||||||
Compiler,
|
Compiler,
|
||||||
CompilerCache,
|
CompilerCache,
|
||||||
|
bind(DirectiveResolver).toClass(MockDirectiveResolver),
|
||||||
bind(ViewResolver).toClass(MockViewResolver),
|
bind(ViewResolver).toClass(MockViewResolver),
|
||||||
DEFAULT_PIPES,
|
DEFAULT_PIPES,
|
||||||
bind(IterableDiffers).toValue(defaultIterableDiffers),
|
bind(IterableDiffers).toValue(defaultIterableDiffers),
|
||||||
@ -133,7 +135,6 @@ function _getAppBindings() {
|
|||||||
Log,
|
Log,
|
||||||
ViewLoader,
|
ViewLoader,
|
||||||
DynamicComponentLoader,
|
DynamicComponentLoader,
|
||||||
DirectiveResolver,
|
|
||||||
PipeResolver,
|
PipeResolver,
|
||||||
Parser,
|
Parser,
|
||||||
Lexer,
|
Lexer,
|
||||||
|
@ -14,8 +14,7 @@ import {
|
|||||||
TestComponentBuilder
|
TestComponentBuilder
|
||||||
} from 'angular2/test_lib';
|
} from 'angular2/test_lib';
|
||||||
|
|
||||||
import {Injectable, NgIf} from 'angular2/core';
|
import {Injectable, NgIf, bind} from 'angular2/core';
|
||||||
|
|
||||||
import {Directive, Component, View, ViewMetadata} from 'angular2/src/core/metadata';
|
import {Directive, Component, View, ViewMetadata} from 'angular2/src/core/metadata';
|
||||||
|
|
||||||
@Component({selector: 'child-comp'})
|
@Component({selector: 'child-comp'})
|
||||||
@ -48,11 +47,14 @@ class MyIfComp {
|
|||||||
@Component({selector: 'child-child-comp'})
|
@Component({selector: 'child-child-comp'})
|
||||||
@View({template: `<span>ChildChild</span>`})
|
@View({template: `<span>ChildChild</span>`})
|
||||||
@Injectable()
|
@Injectable()
|
||||||
class ChildChildComp {}
|
class ChildChildComp {
|
||||||
|
}
|
||||||
|
|
||||||
@Component({selector: 'child-comp'})
|
@Component({selector: 'child-comp'})
|
||||||
@View({template: `<span>Original {{childBinding}}(<child-child-comp></child-child-comp>)</span>`,
|
@View({
|
||||||
directives: [ChildChildComp]})
|
template: `<span>Original {{childBinding}}(<child-child-comp></child-child-comp>)</span>`,
|
||||||
|
directives: [ChildChildComp]
|
||||||
|
})
|
||||||
@Injectable()
|
@Injectable()
|
||||||
class ChildWithChildComp {
|
class ChildWithChildComp {
|
||||||
childBinding: string;
|
childBinding: string;
|
||||||
@ -62,7 +64,30 @@ class ChildWithChildComp {
|
|||||||
@Component({selector: 'child-child-comp'})
|
@Component({selector: 'child-child-comp'})
|
||||||
@View({template: `<span>ChildChild Mock</span>`})
|
@View({template: `<span>ChildChild Mock</span>`})
|
||||||
@Injectable()
|
@Injectable()
|
||||||
class MockChildChildComp {}
|
class MockChildChildComp {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class FancyService {
|
||||||
|
value: string = 'real value';
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockFancyService extends FancyService {
|
||||||
|
value: string = 'mocked out value';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({selector: 'my-service-comp', bindings: [FancyService]})
|
||||||
|
@View({template: `injected value: {{fancyService.value}}`})
|
||||||
|
class TestBindingsComp {
|
||||||
|
constructor(private fancyService: FancyService) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({selector: 'my-service-comp', viewBindings: [FancyService]})
|
||||||
|
@View({template: `injected value: {{fancyService.value}}`})
|
||||||
|
class TestViewBindingsComp {
|
||||||
|
constructor(private fancyService: FancyService) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
@ -142,10 +167,39 @@ export function main() {
|
|||||||
.createAsync(ParentComp)
|
.createAsync(ParentComp)
|
||||||
.then((rootTestComponent) => {
|
.then((rootTestComponent) => {
|
||||||
rootTestComponent.detectChanges();
|
rootTestComponent.detectChanges();
|
||||||
expect(rootTestComponent.nativeElement).toHaveText('Parent(Original Child(ChildChild Mock))');
|
expect(rootTestComponent.nativeElement)
|
||||||
|
.toHaveText('Parent(Original Child(ChildChild Mock))');
|
||||||
|
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('should override a binding',
|
||||||
|
inject([TestComponentBuilder, AsyncTestCompleter], (tcb, async) => {
|
||||||
|
|
||||||
|
tcb.overrideBindings(TestBindingsComp, [bind(FancyService).toClass(MockFancyService)])
|
||||||
|
.createAsync(TestBindingsComp)
|
||||||
|
.then((rootTestComponent) => {
|
||||||
|
rootTestComponent.detectChanges();
|
||||||
|
expect(rootTestComponent.nativeElement)
|
||||||
|
.toHaveText('injected value: mocked out value');
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('should override a viewBinding',
|
||||||
|
inject([TestComponentBuilder, AsyncTestCompleter], (tcb, async) => {
|
||||||
|
|
||||||
|
tcb.overrideViewBindings(TestViewBindingsComp,
|
||||||
|
[bind(FancyService).toClass(MockFancyService)])
|
||||||
|
.createAsync(TestViewBindingsComp)
|
||||||
|
.then((rootTestComponent) => {
|
||||||
|
rootTestComponent.detectChanges();
|
||||||
|
expect(rootTestComponent.nativeElement)
|
||||||
|
.toHaveText('injected value: mocked out value');
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user