Skip to content

Commit f91c087

Browse files
committed
feat(TestComponentBuilder): add #overrideBindings and #overrideViewBindings
Closes angular#4052
1 parent 39a6f85 commit f91c087

File tree

4 files changed

+194
-24
lines changed

4 files changed

+194
-24
lines changed
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import {Map, MapWrapper, ListWrapper} from 'angular2/src/core/facade/collection';
2+
import {
3+
Type,
4+
isPresent,
5+
BaseException,
6+
stringify,
7+
isBlank,
8+
print
9+
} from 'angular2/src/core/facade/lang';
10+
import {DirectiveMetadata, ComponentMetadata} from '../core/metadata';
11+
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
12+
13+
export class MockDirectiveResolver extends DirectiveResolver {
14+
private _bindingsOverrides: Map<Type, any[]> = new Map();
15+
private _viewBindingsOverrides: Map<Type, any[]> = new Map();
16+
17+
resolve(type: Type): DirectiveMetadata {
18+
var dm = super.resolve(type);
19+
20+
var bindingsOverride = this._bindingsOverrides.get(type);
21+
var viewBindingsOverride = this._viewBindingsOverrides.get(type);
22+
23+
var bindings = dm.bindings;
24+
if (isPresent(bindingsOverride)) {
25+
bindings = dm.bindings.concat(bindingsOverride);
26+
}
27+
28+
if (dm instanceof ComponentMetadata) {
29+
var viewBindings = dm.viewBindings;
30+
if (isPresent(viewBindingsOverride)) {
31+
viewBindings = dm.viewBindings.concat(viewBindingsOverride);
32+
}
33+
34+
return new ComponentMetadata({
35+
selector: dm.selector,
36+
properties: dm.properties,
37+
events: dm.events,
38+
host: dm.host,
39+
bindings: bindings,
40+
exportAs: dm.exportAs,
41+
compileChildren: dm.compileChildren,
42+
changeDetection: dm.changeDetection,
43+
viewBindings: viewBindings
44+
});
45+
}
46+
47+
return new DirectiveMetadata({
48+
selector: dm.selector,
49+
properties: dm.properties,
50+
events: dm.events,
51+
host: dm.host,
52+
bindings: bindings,
53+
exportAs: dm.exportAs,
54+
compileChildren: dm.compileChildren
55+
});
56+
}
57+
58+
setBindingsOverride(type: Type, bindings: any[]): void {
59+
this._bindingsOverrides.set(type, bindings);
60+
}
61+
62+
setViewBindingsOverride(type: Type, viewBindings: any[]): void {
63+
this._viewBindingsOverrides.set(type, viewBindings);
64+
}
65+
}

modules/angular2/src/test_lib/test_component_builder.ts

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {ListWrapper, MapWrapper} from 'angular2/src/core/facade/collection';
66

77
import {ViewMetadata} from '../core/metadata';
88

9+
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
910
import {ViewResolver} from 'angular2/src/core/compiler/view_resolver';
1011
import {AppView} from 'angular2/src/core/compiler/view';
1112
import {internalView, ViewRef} from 'angular2/src/core/compiler/view_ref';
@@ -47,16 +48,19 @@ var _nextRootElementId = 0;
4748
*/
4849
@Injectable()
4950
export class TestComponentBuilder {
50-
_injector: Injector;
51-
_viewOverrides: Map<Type, ViewMetadata>;
51+
_bindingsOverrides: Map<Type, any[]>;
5252
_directiveOverrides: Map<Type, Map<Type, Type>>;
5353
_templateOverrides: Map<Type, string>;
54+
_viewBindingsOverrides: Map<Type, any[]>;
55+
_viewOverrides: Map<Type, ViewMetadata>;
5456

55-
constructor(injector: Injector) {
56-
this._injector = injector;
57-
this._viewOverrides = new Map();
57+
58+
constructor(private _injector: Injector) {
59+
this._bindingsOverrides = new Map();
5860
this._directiveOverrides = new Map();
5961
this._templateOverrides = new Map();
62+
this._viewBindingsOverrides = new Map();
63+
this._viewOverrides = new Map();
6064
}
6165

6266
_clone(): TestComponentBuilder {
@@ -116,12 +120,53 @@ export class TestComponentBuilder {
116120
return clone;
117121
}
118122

123+
/**
124+
* Overrides one or more injectables configured via `bindings` metadata property of a directive or
125+
* component.
126+
* Very useful when certain bindings need to be mocked out.
127+
*
128+
* The bindings specified via this method are appended to the existing `bindings` causing the
129+
* duplicated bindings to
130+
* be overridden.
131+
*
132+
* @param {Type} component
133+
* @param {any[]} bindings
134+
*
135+
* @return {TestComponentBuilder}
136+
*/
137+
overrideBindings(type: Type, bindings: any[]): TestComponentBuilder {
138+
var clone = this._clone();
139+
clone._bindingsOverrides.set(type, bindings);
140+
return clone;
141+
}
142+
143+
/**
144+
* Overrides one or more injectables configured via `bindings` metadata property of a directive or
145+
* component.
146+
* Very useful when certain bindings need to be mocked out.
147+
*
148+
* The bindings specified via this method are appended to the existing `bindings` causing the
149+
* duplicated bindings to
150+
* be overridden.
151+
*
152+
* @param {Type} component
153+
* @param {any[]} bindings
154+
*
155+
* @return {TestComponentBuilder}
156+
*/
157+
overrideViewBindings(type: Type, bindings: any[]): TestComponentBuilder {
158+
var clone = this._clone();
159+
clone._viewBindingsOverrides.set(type, bindings);
160+
return clone;
161+
}
162+
119163
/**
120164
* Builds and returns a RootTestComponent.
121165
*
122166
* @return {Promise<RootTestComponent>}
123167
*/
124168
createAsync(rootComponentType: Type): Promise<RootTestComponent> {
169+
var mockDirectiveResolver = this._injector.get(DirectiveResolver);
125170
var mockViewResolver = this._injector.get(ViewResolver);
126171
MapWrapper.forEach(this._viewOverrides,
127172
(view, type) => { mockViewResolver.setView(type, view); });
@@ -133,6 +178,11 @@ export class TestComponentBuilder {
133178
});
134179
});
135180

181+
this._bindingsOverrides.forEach((bindings, type) =>
182+
mockDirectiveResolver.setBindingsOverride(type, bindings));
183+
this._viewBindingsOverrides.forEach(
184+
(bindings, type) => mockDirectiveResolver.setViewBindingsOverride(type, bindings));
185+
136186
var rootElId = `root${_nextRootElementId++}`;
137187
var rootEl = el(`<div id="${rootElId}"></div>`);
138188
var doc = this._injector.get(DOCUMENT);

modules/angular2/src/test_lib/test_injector.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import {
3636
EVENT_MANAGER_PLUGINS
3737
} from 'angular2/src/core/render/dom/events/event_manager';
3838

39+
import {MockDirectiveResolver} from 'angular2/src/mock/directive_resolver_mock';
3940
import {MockViewResolver} from 'angular2/src/mock/view_resolver_mock';
4041
import {MockXHR} from 'angular2/src/core/render/xhr_mock';
4142
import {MockLocationStrategy} from 'angular2/src/mock/mock_location_strategy';
@@ -125,6 +126,7 @@ function _getAppBindings() {
125126
bind(APP_VIEW_POOL_CAPACITY).toValue(500),
126127
Compiler,
127128
CompilerCache,
129+
bind(DirectiveResolver).toClass(MockDirectiveResolver),
128130
bind(ViewResolver).toClass(MockViewResolver),
129131
DEFAULT_PIPES,
130132
bind(IterableDiffers).toValue(defaultIterableDiffers),
@@ -133,7 +135,6 @@ function _getAppBindings() {
133135
Log,
134136
ViewLoader,
135137
DynamicComponentLoader,
136-
DirectiveResolver,
137138
PipeResolver,
138139
Parser,
139140
Lexer,

modules/angular2/test/test_lib/test_component_builder_spec.ts

Lines changed: 72 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ import {
1414
TestComponentBuilder
1515
} from 'angular2/test_lib';
1616

17-
import {Injectable, NgIf} from 'angular2/core';
18-
17+
import {Injectable, NgIf, bind} from 'angular2/core';
1918
import {Directive, Component, View, ViewMetadata} from 'angular2/src/core/metadata';
2019

2120
@Component({selector: 'child-comp'})
@@ -48,11 +47,14 @@ class MyIfComp {
4847
@Component({selector: 'child-child-comp'})
4948
@View({template: `<span>ChildChild</span>`})
5049
@Injectable()
51-
class ChildChildComp {}
50+
class ChildChildComp {
51+
}
5252

5353
@Component({selector: 'child-comp'})
54-
@View({template: `<span>Original {{childBinding}}(<child-child-comp></child-child-comp>)</span>`,
55-
directives: [ChildChildComp]})
54+
@View({
55+
template: `<span>Original {{childBinding}}(<child-child-comp></child-child-comp>)</span>`,
56+
directives: [ChildChildComp]
57+
})
5658
@Injectable()
5759
class ChildWithChildComp {
5860
childBinding: string;
@@ -62,7 +64,30 @@ class ChildWithChildComp {
6264
@Component({selector: 'child-child-comp'})
6365
@View({template: `<span>ChildChild Mock</span>`})
6466
@Injectable()
65-
class MockChildChildComp {}
67+
class MockChildChildComp {
68+
}
69+
70+
71+
72+
class FancyService {
73+
value: string = 'real value';
74+
}
75+
76+
class MockFancyService extends FancyService {
77+
value: string = 'mocked out value';
78+
}
79+
80+
@Component({selector: 'my-service-comp', bindings: [FancyService]})
81+
@View({template: `injected value: {{fancyService.value}}`})
82+
class TestBindingsComp {
83+
constructor(private fancyService: FancyService) {}
84+
}
85+
86+
@Component({selector: 'my-service-comp', viewBindings: [FancyService]})
87+
@View({template: `injected value: {{fancyService.value}}`})
88+
class TestViewBindingsComp {
89+
constructor(private fancyService: FancyService) {}
90+
}
6691

6792

6893
export function main() {
@@ -135,17 +160,46 @@ export function main() {
135160

136161

137162
it("should override child component's dependencies",
138-
inject([TestComponentBuilder, AsyncTestCompleter], (tcb, async) => {
139-
140-
tcb.overrideDirective(ParentComp, ChildComp, ChildWithChildComp)
141-
.overrideDirective(ChildWithChildComp, ChildChildComp, MockChildChildComp)
142-
.createAsync(ParentComp)
143-
.then((rootTestComponent) => {
144-
rootTestComponent.detectChanges();
145-
expect(rootTestComponent.nativeElement).toHaveText('Parent(Original Child(ChildChild Mock))');
146-
147-
async.done();
148-
});
149-
}));
163+
inject([TestComponentBuilder, AsyncTestCompleter], (tcb, async) => {
164+
165+
tcb.overrideDirective(ParentComp, ChildComp, ChildWithChildComp)
166+
.overrideDirective(ChildWithChildComp, ChildChildComp, MockChildChildComp)
167+
.createAsync(ParentComp)
168+
.then((rootTestComponent) => {
169+
rootTestComponent.detectChanges();
170+
expect(rootTestComponent.nativeElement)
171+
.toHaveText('Parent(Original Child(ChildChild Mock))');
172+
173+
async.done();
174+
});
175+
}));
176+
177+
it('should override a binding',
178+
inject([TestComponentBuilder, AsyncTestCompleter], (tcb, async) => {
179+
180+
tcb.overrideBindings(TestBindingsComp, [bind(FancyService).toClass(MockFancyService)])
181+
.createAsync(TestBindingsComp)
182+
.then((rootTestComponent) => {
183+
rootTestComponent.detectChanges();
184+
expect(rootTestComponent.nativeElement)
185+
.toHaveText('injected value: mocked out value');
186+
async.done();
187+
});
188+
}));
189+
190+
191+
it('should override a viewBinding',
192+
inject([TestComponentBuilder, AsyncTestCompleter], (tcb, async) => {
193+
194+
tcb.overrideViewBindings(TestViewBindingsComp,
195+
[bind(FancyService).toClass(MockFancyService)])
196+
.createAsync(TestViewBindingsComp)
197+
.then((rootTestComponent) => {
198+
rootTestComponent.detectChanges();
199+
expect(rootTestComponent.nativeElement)
200+
.toHaveText('injected value: mocked out value');
201+
async.done();
202+
});
203+
}));
150204
});
151205
}

0 commit comments

Comments
 (0)