Skip to content

Commit 57e308d

Browse files
committed
test(MockTemplateResolver): allow directive overriding
1 parent c922b5a commit 57e308d

File tree

2 files changed

+235
-9
lines changed

2 files changed

+235
-9
lines changed
Lines changed: 118 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,137 @@
11
import {Map, MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
2-
import {Type, isPresent} from 'angular2/src/facade/lang';
2+
import {Type, isPresent, BaseException, stringify, isBlank} from 'angular2/src/facade/lang';
33

44
import {Template} from 'angular2/src/core/annotations/template';
55
import {TemplateResolver} from 'angular2/src/core/compiler/template_resolver';
66

77
export class MockTemplateResolver extends TemplateResolver {
8-
_cmpTemplates: Map;
8+
_templates: Map<Type, Template>;
9+
_inlineTemplates: Map<Type, string>;
10+
_templateCache: Map<Type, Template>;
11+
_directiveOverrides: Map<Type, Type>;
912

1013
constructor() {
1114
super();
12-
this._cmpTemplates = MapWrapper.create();
15+
this._templates = MapWrapper.create();
16+
this._inlineTemplates = MapWrapper.create();
17+
this._templateCache = MapWrapper.create();
18+
this._directiveOverrides = MapWrapper.create();
1319
}
1420

15-
setTemplate(component: Type, template: Template) {
16-
MapWrapper.set(this._cmpTemplates, component, template);
21+
/**
22+
* Overrides the [Template] for a component.
23+
*
24+
* @param {Type} component
25+
* @param {Template} template
26+
*/
27+
setTemplate(component: Type, template: Template): void {
28+
this._checkOverrideable(component);
29+
MapWrapper.set(this._templates, component, template);
1730
}
1831

32+
/**
33+
* Overrides the inline template for a component - other configuration remains unchanged.
34+
*
35+
* @param {Type} component
36+
* @param {string} template
37+
*/
38+
setInlineTemplate(component: Type, template: string): void {
39+
this._checkOverrideable(component);
40+
MapWrapper.set(this._inlineTemplates, component, template);
41+
}
42+
43+
/**
44+
* Overrides a directive from the component [Template].
45+
*
46+
* @param {Type} component
47+
* @param {Type} from
48+
* @param {Type} to
49+
*/
50+
overrideTemplateDirective(component: Type, from: Type, to: Type): void {
51+
this._checkOverrideable(component);
52+
53+
var overrides = MapWrapper.get(this._directiveOverrides, component);
54+
55+
if (isBlank(overrides)) {
56+
overrides = MapWrapper.create();
57+
MapWrapper.set(this._directiveOverrides, component, overrides);
58+
}
59+
60+
MapWrapper.set(overrides, from, to);
61+
}
62+
63+
/**
64+
* Returns the [Template] for a component:
65+
* - Set the [Template] to the overridden template when it exists or fallback to the default
66+
* [TemplateResolver], see [setTemplate]
67+
* - Override the directives, see [overrideTemplateDirective]
68+
* - Override the template definition, see [setInlineTemplate]
69+
*
70+
* @param component
71+
* @returns {Template}
72+
*/
1973
resolve(component: Type): Template {
20-
var override = MapWrapper.get(this._cmpTemplates, component);
74+
var template = MapWrapper.get(this._templateCache, component);
75+
if (isPresent(template)) return template;
2176

22-
if (isPresent(override)) {
23-
return override;
77+
template = MapWrapper.get(this._templates, component);
78+
if (isBlank(template)) {
79+
template = super.resolve(component);
2480
}
2581

26-
return super.resolve(component);
82+
var directives = template.directives;
83+
var overrides = MapWrapper.get(this._directiveOverrides, component);
84+
85+
if (isPresent(overrides) && isPresent(directives)) {
86+
directives = ListWrapper.clone(template.directives);
87+
MapWrapper.forEach(overrides, (to, from) => {
88+
var srcIndex = directives.indexOf(from);
89+
if (srcIndex == -1) {
90+
throw new BaseException(`Overriden directive ${stringify(from)} not found in the template of ${stringify(component)}`);
91+
}
92+
directives[srcIndex] = to;
93+
});
94+
template = new Template({
95+
inline: template.inline,
96+
url: template.url,
97+
directives: directives,
98+
formatters: template.formatters,
99+
source: template.source,
100+
locale: template.locale,
101+
device: template.device,
102+
});
103+
}
104+
105+
var inlineTemplate = MapWrapper.get(this._inlineTemplates, component);
106+
if (isPresent(inlineTemplate)) {
107+
template = new Template({
108+
inline: inlineTemplate,
109+
url: null,
110+
directives: template.directives,
111+
formatters: template.formatters,
112+
source: template.source,
113+
locale: template.locale,
114+
device: template.device,
115+
});
116+
}
117+
118+
MapWrapper.set(this._templateCache, component, template);
119+
return template;
120+
}
121+
122+
/**
123+
* Once a component has been compiled, the ProtoView is stored in the compiler cache.
124+
*
125+
* Then it should not be possible to override the component configuration after the component
126+
* has been compiled.
127+
*
128+
* @param {Type} component
129+
*/
130+
_checkOverrideable(component: Type): void {
131+
var cached = MapWrapper.get(this._templateCache, component);
132+
133+
if (isPresent(cached)) {
134+
throw new BaseException(`The component ${stringify(component)} has already been compiled, its configuration can not be changed`);
135+
}
27136
}
28137
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import {
2+
beforeEach,
3+
ddescribe,
4+
describe,
5+
el,
6+
expect,
7+
iit,
8+
it,
9+
} from 'angular2/test_lib';
10+
11+
import {MockTemplateResolver} from 'angular2/src/mock/template_resolver_mock';
12+
13+
import {Component} from 'angular2/src/core/annotations/annotations';
14+
import {Template} from 'angular2/src/core/annotations/template';
15+
16+
import {isBlank} from 'angular2/src/facade/lang';
17+
18+
export function main() {
19+
describe('MockTemplateResolver', () => {
20+
var resolver;
21+
22+
beforeEach(() => {
23+
resolver = new MockTemplateResolver();
24+
});
25+
26+
describe('Template overriding', () => {
27+
it('should fallback to the default TemplateResolver when templates are not overridden', () => {
28+
var template = resolver.resolve(SomeComponent);
29+
expect(template.inline).toEqual('template');
30+
expect(template.directives).toEqual([SomeDirective]);
31+
});
32+
33+
it('should allow overriding a template', () => {
34+
resolver.setTemplate(SomeComponent, new Template({inline: 'overridden template'}));
35+
var template = resolver.resolve(SomeComponent);
36+
expect(template.inline).toEqual('overridden template');
37+
expect(isBlank(template.directives)).toBe(true);
38+
39+
});
40+
41+
it('should not allow overriding a template after it has been resolved', () => {
42+
resolver.resolve(SomeComponent);
43+
expect(() => {
44+
resolver.setTemplate(SomeComponent, new Template({inline: 'overridden template'}));
45+
}).toThrowError('The component SomeComponent has already been compiled, its configuration can not be changed');
46+
});
47+
});
48+
49+
describe('inline definition overriding', () => {
50+
it('should allow overriding the default Template', () => {
51+
resolver.setInlineTemplate(SomeComponent, 'overridden template');
52+
var template = resolver.resolve(SomeComponent);
53+
expect(template.inline).toEqual('overridden template');
54+
expect(template.directives).toEqual([SomeDirective]);
55+
});
56+
57+
it('should allow overriding an overriden template', () => {
58+
resolver.setTemplate(SomeComponent, new Template({inline: 'overridden template'}));
59+
resolver.setInlineTemplate(SomeComponent, 'overridden template x 2');
60+
var template = resolver.resolve(SomeComponent);
61+
expect(template.inline).toEqual('overridden template x 2');
62+
});
63+
64+
it('should not allow overriding a template after it has been resolved', () => {
65+
resolver.resolve(SomeComponent);
66+
expect(() => {
67+
resolver.setInlineTemplate(SomeComponent, 'overridden template');
68+
}).toThrowError('The component SomeComponent has already been compiled, its configuration can not be changed');
69+
});
70+
});
71+
72+
73+
describe('Directive overriding', () => {
74+
it('should allow overriding a directive from the default template', () => {
75+
resolver.overrideTemplateDirective(SomeComponent, SomeDirective, SomeOtherDirective);
76+
var template = resolver.resolve(SomeComponent);
77+
expect(template.directives.length).toEqual(1);
78+
expect(template.directives[0]).toBe(SomeOtherDirective);
79+
});
80+
81+
it('should allow overriding a directive from an overriden template', () => {
82+
resolver.setTemplate(SomeComponent, new Template({directives: [SomeOtherDirective]}));
83+
resolver.overrideTemplateDirective(SomeComponent, SomeOtherDirective, SomeComponent);
84+
var template = resolver.resolve(SomeComponent);
85+
expect(template.directives.length).toEqual(1);
86+
expect(template.directives[0]).toBe(SomeComponent);
87+
});
88+
89+
it('should throw when the overridden directive is not present', () => {
90+
resolver.overrideTemplateDirective(SomeComponent, SomeOtherDirective, SomeDirective);
91+
expect(() => { resolver.resolve(SomeComponent); })
92+
.toThrowError('Overriden directive SomeOtherDirective not found in the template of SomeComponent');
93+
});
94+
95+
it('should not allow overriding a directive after its template has been resolved', () => {
96+
resolver.resolve(SomeComponent);
97+
expect(() => {
98+
resolver.overrideTemplateDirective(SomeComponent, SomeDirective, SomeOtherDirective);
99+
}).toThrowError('The component SomeComponent has already been compiled, its configuration can not be changed');
100+
});
101+
});
102+
});
103+
}
104+
105+
@Component({selector: 'cmp'})
106+
@Template({
107+
inline: 'template',
108+
directives: [SomeDirective],
109+
})
110+
class SomeComponent {
111+
}
112+
113+
class SomeDirective {
114+
}
115+
116+
class SomeOtherDirective {
117+
}

0 commit comments

Comments
 (0)