feat(compiler): support sync runtime compile

Adds new abstraction `Compiler` with methods
`compileComponentAsync` and `compileComponentSync`.
This is in preparation of deprecating `ComponentResolver`.

`compileComponentSync` is able to compile components
synchronously given all components either have an inline
template or they have been compiled before.

Also changes `TestComponentBuilder.createSync` to
take a `Type` and use the new `compileComponentSync` method.

Also supports overriding the component metadata even if
the component has already been compiled.

Also fixes #7084 in a better way.

BREAKING CHANGE:
`TestComponentBuilder.createSync` now takes a component type
and throws if not all templates are either inlined
are compiled before via `createAsync`.

Closes #9594
This commit is contained in:
Tobias Bosch
2016-06-24 08:46:43 -07:00
parent 24eb8389d2
commit bf598d6b8b
35 changed files with 1093 additions and 700 deletions

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {CompileTemplateMetadata, CompileTypeMetadata} from '@angular/compiler/src/compile_metadata';
import {CompileDirectiveMetadata, CompileStylesheetMetadata, CompileTemplateMetadata, CompileTypeMetadata} from '@angular/compiler/src/compile_metadata';
import {CompilerConfig} from '@angular/compiler/src/config';
import {DirectiveNormalizer} from '@angular/compiler/src/directive_normalizer';
import {XHR} from '@angular/compiler/src/xhr';
@ -15,6 +15,7 @@ import {ViewEncapsulation} from '@angular/core/src/metadata/view';
import {beforeEach, beforeEachProviders, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
import {AsyncTestCompleter} from '@angular/core/testing/testing_internal';
import {SpyXHR} from './spies';
import {TEST_PROVIDERS} from './test_bindings';
export function main() {
@ -30,176 +31,230 @@ export function main() {
new CompileTypeMetadata({moduleUrl: 'http://some/module/a.js', name: 'SomeComp'});
});
describe('loadTemplate', () => {
describe('inline template', () => {
it('should store the template',
inject(
[AsyncTestCompleter, DirectiveNormalizer],
(async: AsyncTestCompleter, normalizer: DirectiveNormalizer) => {
normalizer
.normalizeTemplate(dirType, new CompileTemplateMetadata({
encapsulation: null,
template: 'a',
templateUrl: null,
styles: [],
styleUrls: ['test.css']
}))
.then((template: CompileTemplateMetadata) => {
expect(template.template).toEqual('a');
expect(template.templateUrl).toEqual('package:some/module/a.js');
async.done();
});
}));
it('should resolve styles on the annotation against the moduleUrl',
inject(
[AsyncTestCompleter, DirectiveNormalizer],
(async: AsyncTestCompleter, normalizer: DirectiveNormalizer) => {
normalizer
.normalizeTemplate(dirType, new CompileTemplateMetadata({
encapsulation: null,
template: '',
templateUrl: null,
styles: [],
styleUrls: ['test.css']
}))
.then((template: CompileTemplateMetadata) => {
expect(template.styleUrls).toEqual(['package:some/module/test.css']);
async.done();
});
}));
it('should resolve styles in the template against the moduleUrl',
inject(
[AsyncTestCompleter, DirectiveNormalizer],
(async: AsyncTestCompleter, normalizer: DirectiveNormalizer) => {
normalizer
.normalizeTemplate(dirType, new CompileTemplateMetadata({
encapsulation: null,
template: '<style>@import test.css</style>',
templateUrl: null,
styles: [],
styleUrls: []
}))
.then((template: CompileTemplateMetadata) => {
expect(template.styleUrls).toEqual(['package:some/module/test.css']);
async.done();
});
}));
it('should use ViewEncapsulation.Emulated by default',
inject(
[AsyncTestCompleter, DirectiveNormalizer],
(async: AsyncTestCompleter, normalizer: DirectiveNormalizer) => {
normalizer
.normalizeTemplate(dirType, new CompileTemplateMetadata({
encapsulation: null,
template: '',
templateUrl: null,
styles: [],
styleUrls: ['test.css']
}))
.then((template: CompileTemplateMetadata) => {
expect(template.encapsulation).toEqual(ViewEncapsulation.Emulated);
async.done();
});
}));
it('should use default encapsulation provided by CompilerConfig',
inject(
[AsyncTestCompleter, CompilerConfig, DirectiveNormalizer],
(async: AsyncTestCompleter, config: CompilerConfig,
normalizer: DirectiveNormalizer) => {
config.defaultEncapsulation = ViewEncapsulation.None;
normalizer
.normalizeTemplate(dirType, new CompileTemplateMetadata({
encapsulation: null,
template: '',
templateUrl: null,
styles: [],
styleUrls: ['test.css']
}))
.then((template: CompileTemplateMetadata) => {
expect(template.encapsulation).toEqual(ViewEncapsulation.None);
async.done();
});
}));
});
describe('templateUrl', () => {
it('should load a template from a url that is resolved against moduleUrl',
inject(
[AsyncTestCompleter, DirectiveNormalizer, XHR],
(async: AsyncTestCompleter, normalizer: DirectiveNormalizer, xhr: MockXHR) => {
xhr.expect('package:some/module/sometplurl.html', 'a');
normalizer
.normalizeTemplate(dirType, new CompileTemplateMetadata({
encapsulation: null,
template: null,
templateUrl: 'sometplurl.html',
styles: [],
styleUrls: ['test.css']
}))
.then((template: CompileTemplateMetadata) => {
expect(template.template).toEqual('a');
expect(template.templateUrl).toEqual('package:some/module/sometplurl.html');
async.done();
});
xhr.flush();
}));
it('should resolve styles on the annotation against the moduleUrl',
inject(
[AsyncTestCompleter, DirectiveNormalizer, XHR],
(async: AsyncTestCompleter, normalizer: DirectiveNormalizer, xhr: MockXHR) => {
xhr.expect('package:some/module/tpl/sometplurl.html', '');
normalizer
.normalizeTemplate(dirType, new CompileTemplateMetadata({
encapsulation: null,
template: null,
templateUrl: 'tpl/sometplurl.html',
styles: [],
styleUrls: ['test.css']
}))
.then((template: CompileTemplateMetadata) => {
expect(template.styleUrls).toEqual(['package:some/module/test.css']);
async.done();
});
xhr.flush();
}));
it('should resolve styles in the template against the templateUrl',
inject(
[AsyncTestCompleter, DirectiveNormalizer, XHR],
(async: AsyncTestCompleter, normalizer: DirectiveNormalizer, xhr: MockXHR) => {
xhr.expect(
'package:some/module/tpl/sometplurl.html', '<style>@import test.css</style>');
normalizer
.normalizeTemplate(dirType, new CompileTemplateMetadata({
encapsulation: null,
template: null,
templateUrl: 'tpl/sometplurl.html',
styles: [],
styleUrls: []
}))
.then((template: CompileTemplateMetadata) => {
expect(template.styleUrls).toEqual(['package:some/module/tpl/test.css']);
async.done();
});
xhr.flush();
}));
});
describe('normalizeDirective', () => {
it('should throw if no template was specified',
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
expect(
() => normalizer.normalizeTemplate(
dirType,
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []})))
.toThrowError('No template specified for component SomeComp');
expect(() => normalizer.normalizeDirective(new CompileDirectiveMetadata({
type: dirType,
isComponent: true,
template:
new CompileTemplateMetadata({encapsulation: null, styles: [], styleUrls: []})
}))).toThrowError('No template specified for component SomeComp');
}));
});
describe('normalizeTemplateSync', () => {
it('should store the template',
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
let template = normalizer.normalizeTemplateSync(dirType, new CompileTemplateMetadata({
encapsulation: null,
template: 'a',
templateUrl: null,
styles: [],
styleUrls: []
}))
expect(template.template).toEqual('a');
expect(template.templateUrl).toEqual('package:some/module/a.js');
}));
it('should resolve styles on the annotation against the moduleUrl',
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
let template = normalizer.normalizeTemplateSync(dirType, new CompileTemplateMetadata({
encapsulation: null,
template: '',
templateUrl: null,
styles: [],
styleUrls: ['test.css']
}))
expect(template.styleUrls).toEqual(['package:some/module/test.css']);
}));
it('should resolve styles in the template against the moduleUrl',
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
let template =
normalizer.normalizeTemplateSync(dirType, new CompileTemplateMetadata({
encapsulation: null,
template: '<style>@import test.css</style>',
templateUrl: null,
styles: [],
styleUrls: []
}))
expect(template.styleUrls).toEqual(['package:some/module/test.css']);
}));
it('should use ViewEncapsulation.Emulated by default',
inject([DirectiveNormalizer], (normalizer: DirectiveNormalizer) => {
let template = normalizer.normalizeTemplateSync(dirType, new CompileTemplateMetadata({
encapsulation: null,
template: '',
templateUrl: null,
styles: [],
styleUrls: ['test.css']
}))
expect(template.encapsulation).toEqual(ViewEncapsulation.Emulated);
}));
it('should use default encapsulation provided by CompilerConfig',
inject(
[CompilerConfig, DirectiveNormalizer],
(config: CompilerConfig, normalizer: DirectiveNormalizer) => {
config.defaultEncapsulation = ViewEncapsulation.None;
let template =
normalizer.normalizeTemplateSync(dirType, new CompileTemplateMetadata({
encapsulation: null,
template: '',
templateUrl: null,
styles: [],
styleUrls: ['test.css']
}))
expect(template.encapsulation).toEqual(ViewEncapsulation.None);
}));
});
describe('templateUrl', () => {
it('should load a template from a url that is resolved against moduleUrl',
inject(
[AsyncTestCompleter, DirectiveNormalizer, XHR],
(async: AsyncTestCompleter, normalizer: DirectiveNormalizer, xhr: MockXHR) => {
xhr.expect('package:some/module/sometplurl.html', 'a');
normalizer
.normalizeTemplateAsync(dirType, new CompileTemplateMetadata({
encapsulation: null,
template: null,
templateUrl: 'sometplurl.html',
styles: [],
styleUrls: ['test.css']
}))
.then((template: CompileTemplateMetadata) => {
expect(template.template).toEqual('a');
expect(template.templateUrl).toEqual('package:some/module/sometplurl.html');
async.done();
});
xhr.flush();
}));
it('should resolve styles on the annotation against the moduleUrl',
inject(
[AsyncTestCompleter, DirectiveNormalizer, XHR],
(async: AsyncTestCompleter, normalizer: DirectiveNormalizer, xhr: MockXHR) => {
xhr.expect('package:some/module/tpl/sometplurl.html', '');
normalizer
.normalizeTemplateAsync(dirType, new CompileTemplateMetadata({
encapsulation: null,
template: null,
templateUrl: 'tpl/sometplurl.html',
styles: [],
styleUrls: ['test.css']
}))
.then((template: CompileTemplateMetadata) => {
expect(template.styleUrls).toEqual(['package:some/module/test.css']);
async.done();
});
xhr.flush();
}));
it('should resolve styles in the template against the templateUrl',
inject(
[AsyncTestCompleter, DirectiveNormalizer, XHR],
(async: AsyncTestCompleter, normalizer: DirectiveNormalizer, xhr: MockXHR) => {
xhr.expect(
'package:some/module/tpl/sometplurl.html', '<style>@import test.css</style>');
normalizer
.normalizeTemplateAsync(dirType, new CompileTemplateMetadata({
encapsulation: null,
template: null,
templateUrl: 'tpl/sometplurl.html',
styles: [],
styleUrls: []
}))
.then((template: CompileTemplateMetadata) => {
expect(template.styleUrls).toEqual(['package:some/module/tpl/test.css']);
async.done();
});
xhr.flush();
}));
});
describe('normalizeExternalStylesheets', () => {
beforeEachProviders(() => [{provide: XHR, useClass: SpyXHR}]);
it('should load an external stylesheet',
inject(
[AsyncTestCompleter, DirectiveNormalizer, XHR],
(async: AsyncTestCompleter, normalizer: DirectiveNormalizer, xhr: SpyXHR) => {
programXhrSpy(xhr, {'package:some/module/test.css': 'a'});
normalizer
.normalizeExternalStylesheets(new CompileTemplateMetadata({
template: '',
templateUrl: '',
styleUrls: ['package:some/module/test.css']
}))
.then((template: CompileTemplateMetadata) => {
expect(template.externalStylesheets.length).toBe(1);
expect(template.externalStylesheets[0]).toEqual(new CompileStylesheetMetadata({
moduleUrl: 'package:some/module/test.css',
styles: ['a'],
styleUrls: []
}));
async.done();
});
}));
it('should load stylesheets referenced by external stylesheets',
inject(
[AsyncTestCompleter, DirectiveNormalizer, XHR],
(async: AsyncTestCompleter, normalizer: DirectiveNormalizer, xhr: SpyXHR) => {
programXhrSpy(xhr, {
'package:some/module/test.css': 'a@import "test2.css"',
'package:some/module/test2.css': 'b'
});
normalizer
.normalizeExternalStylesheets(new CompileTemplateMetadata({
template: '',
templateUrl: '',
styleUrls: ['package:some/module/test.css']
}))
.then((template: CompileTemplateMetadata) => {
expect(template.externalStylesheets.length).toBe(2);
expect(template.externalStylesheets[0]).toEqual(new CompileStylesheetMetadata({
moduleUrl: 'package:some/module/test.css',
styles: ['a'],
styleUrls: ['package:some/module/test2.css']
}));
expect(template.externalStylesheets[1]).toEqual(new CompileStylesheetMetadata({
moduleUrl: 'package:some/module/test2.css',
styles: ['b'],
styleUrls: []
}));
async.done();
});
}));
});
describe('caching', () => {
it('should work for templateUrl',
inject(
[AsyncTestCompleter, DirectiveNormalizer, XHR],
(async: AsyncTestCompleter, normalizer: DirectiveNormalizer, xhr: MockXHR) => {
xhr.expect('package:some/module/cmp.html', 'a');
var templateMeta = new CompileTemplateMetadata({
templateUrl: 'cmp.html',
});
Promise
.all([
normalizer.normalizeTemplateAsync(dirType, templateMeta),
normalizer.normalizeTemplateAsync(dirType, templateMeta)
])
.then((templates: CompileTemplateMetadata[]) => {
expect(templates[0].template).toEqual('a');
expect(templates[1].template).toEqual('a');
async.done();
});
xhr.flush();
}));
});
@ -369,3 +424,14 @@ export function main() {
});
});
}
function programXhrSpy(spy: SpyXHR, results: {[key: string]: string}) {
spy.spy('get').andCallFake((url: string): Promise<any> => {
var result = results[url];
if (result) {
return Promise.resolve(result);
} else {
return Promise.reject(`Unknown mock url ${url}`);
}
});
}

View File

@ -20,11 +20,11 @@ import {TemplateParser} from '@angular/compiler/src/template_parser';
import {createOfflineCompileUrlResolver} from '@angular/compiler/src/url_resolver';
import {MODULE_SUFFIX} from '@angular/compiler/src/util';
import {ViewCompiler} from '@angular/compiler/src/view_compiler/view_compiler';
import {XHR} from '@angular/compiler/src/xhr';
import {Console} from '../core_private';
import {IS_DART, isPresent, print} from '../src/facade/lang';
import {MockSchemaRegistry} from '../testing/schema_registry_mock';
import {MockXHR} from '../testing/xhr_mock';
export class CompA { user: string; }
@ -44,9 +44,8 @@ export var compAMetadata = CompileDirectiveMetadata.create({
})
});
function _createOfflineCompiler(xhr: MockXHR, emitter: OutputEmitter): OfflineCompiler {
function _createOfflineCompiler(xhr: XHR, emitter: OutputEmitter): OfflineCompiler {
var urlResolver = createOfflineCompileUrlResolver();
xhr.when(`${THIS_MODULE_PATH}/offline_compiler_compa.html`, 'Hello World {{user}}!');
var htmlParser = new HtmlParser();
var config = new CompilerConfig({genDebugInfo: true, useJit: true});
var normalizer = new DirectiveNormalizer(xhr, urlResolver, htmlParser, config);
@ -54,24 +53,25 @@ function _createOfflineCompiler(xhr: MockXHR, emitter: OutputEmitter): OfflineCo
normalizer,
new TemplateParser(
new Parser(new Lexer()), new MockSchemaRegistry({}, {}), htmlParser, new Console(), []),
new StyleCompiler(urlResolver), new ViewCompiler(config), emitter, xhr);
new StyleCompiler(urlResolver), new ViewCompiler(config), emitter);
}
export function compileComp(
emitter: OutputEmitter, comp: CompileDirectiveMetadata): Promise<string> {
var xhr = new MockXHR();
var xhr = new MockXhr();
xhr.setResult(`${THIS_MODULE_PATH}/offline_compiler_compa.html`, '');
xhr.setResult(`${THIS_MODULE_PATH}/offline_compiler_compa.css`, '');
var compiler = _createOfflineCompiler(xhr, emitter);
var result = compiler.normalizeDirectiveMetadata(comp).then((normComp) => {
return compiler.compileTemplates([new NormalizedComponentWithViewDirectives(normComp, [], [])])
return compiler
.compileTemplates([new NormalizedComponentWithViewDirectives(normComp, [], [])])[0]
.source;
});
xhr.flush();
return result;
}
export class SimpleJsImportGenerator implements ImportGenerator {
getImportPath(moduleUrlStr: string, importedUrlStr: string): string {
// var moduleAssetUrl = ImportGenerator.parseAssetUrl(moduleUrlStr);
var importedAssetUrl = ImportGenerator.parseAssetUrl(importedUrlStr);
if (isPresent(importedAssetUrl)) {
return `${importedAssetUrl.packageName}/${importedAssetUrl.modulePath}`;
@ -80,3 +80,15 @@ export class SimpleJsImportGenerator implements ImportGenerator {
}
}
}
class MockXhr implements XHR {
results: {[key: string]: string} = {};
setResult(url: string, content: string) { this.results[url] = content; }
get(url: string): Promise<string> {
if (url in this.results) {
return Promise.resolve(this.results[url]);
}
return Promise.reject<any>(`Unknown url ${url}`);
}
}

View File

@ -0,0 +1,108 @@
import {beforeEach, ddescribe, xdescribe, describe, expect, iit, inject, beforeEachProviders, it, xit,} from '@angular/core/testing/testing_internal';
import {Injectable, Component, Input, ViewMetadata, Compiler, ComponentFactory, Injector} from '@angular/core';
import {ConcreteType, stringify} from '../src/facade/lang';
import {fakeAsync, tick, TestComponentBuilder, ComponentFixture} from '@angular/core/testing';
import {XHR, ViewResolver} from '@angular/compiler';
import {MockViewResolver} from '@angular/compiler/testing';
import {SpyXHR} from './spies';
@Component({selector: 'child-cmp', template: 'childComp'})
class ChildComp {
}
@Component({selector: 'some-cmp', template: 'someComp'})
class SomeComp {
}
@Component({selector: 'some-cmp', templateUrl: './someTpl'})
class SomeCompWithUrlTemplate {
}
export function main() {
describe('RuntimeCompiler', () => {
let compiler: Compiler;
let xhr: SpyXHR;
let tcb: TestComponentBuilder;
let viewResolver: MockViewResolver;
let injector: Injector;
beforeEachProviders(() => [{provide: XHR, useValue: new SpyXHR()}]);
beforeEach(inject(
[Compiler, TestComponentBuilder, XHR, ViewResolver, Injector],
(_compiler: Compiler, _tcb: TestComponentBuilder, _xhr: SpyXHR,
_viewResolver: MockViewResolver, _injector: Injector) => {
compiler = _compiler;
tcb = _tcb;
xhr = _xhr;
viewResolver = _viewResolver;
injector = _injector;
}));
describe('clearCacheFor', () => {
it('should support changing the content of a template referenced via templateUrl',
fakeAsync(() => {
xhr.spy('get').andCallFake(() => Promise.resolve('init'));
let compFixture =
tcb.overrideView(SomeComp, new ViewMetadata({templateUrl: '/myComp.html'}))
.createFakeAsync(SomeComp);
expect(compFixture.nativeElement).toHaveText('init');
xhr.spy('get').andCallFake(() => Promise.resolve('new content'));
// Note: overrideView is calling .clearCacheFor...
compFixture = tcb.overrideView(SomeComp, new ViewMetadata({templateUrl: '/myComp.html'}))
.createFakeAsync(SomeComp);
expect(compFixture.nativeElement).toHaveText('new content');
}));
it('should support overwriting inline templates', () => {
let componentFixture = tcb.createSync(SomeComp);
expect(componentFixture.nativeElement).toHaveText('someComp');
componentFixture = tcb.overrideTemplate(SomeComp, 'test').createSync(SomeComp);
expect(componentFixture.nativeElement).toHaveText('test');
});
it('should not update existing compilation results', () => {
viewResolver.setView(
SomeComp,
new ViewMetadata({template: '<child-cmp></child-cmp>', directives: [ChildComp]}));
viewResolver.setInlineTemplate(ChildComp, 'oldChild');
let compFactory = compiler.compileComponentSync(SomeComp);
viewResolver.setInlineTemplate(ChildComp, 'newChild');
compiler.compileComponentSync(SomeComp);
let compRef = compFactory.create(injector);
expect(compRef.location.nativeElement).toHaveText('oldChild');
});
});
describe('compileComponentSync', () => {
it('should throw when using a templateUrl that has not been compiled before', () => {
xhr.spy('get').andCallFake(() => Promise.resolve(''));
expect(() => tcb.createSync(SomeCompWithUrlTemplate))
.toThrowError(
`Can't compile synchronously as ${stringify(SomeCompWithUrlTemplate)} is still being loaded!`);
});
it('should throw when using a templateUrl in a nested component that has not been compiled before',
() => {
xhr.spy('get').andCallFake(() => Promise.resolve(''));
let localTcb =
tcb.overrideView(SomeComp, new ViewMetadata({template: '', directives: [ChildComp]}))
.overrideView(ChildComp, new ViewMetadata({templateUrl: '/someTpl.html'}));
expect(() => localTcb.createSync(SomeComp))
.toThrowError(
`Can't compile synchronously as ${stringify(ChildComp)} is still being loaded!`);
});
it('should allow to use templateUrl components that have been loaded before',
fakeAsync(() => {
xhr.spy('get').andCallFake(() => Promise.resolve('hello'));
tcb.createFakeAsync(SomeCompWithUrlTemplate);
let compFixture = tcb.createSync(SomeCompWithUrlTemplate);
expect(compFixture.nativeElement).toHaveText('hello');
}));
});
});
}

View File

@ -9,7 +9,7 @@
import {beforeEach, ddescribe, xdescribe, describe, expect, iit, inject, beforeEachProviders, it, xit,} from '@angular/core/testing/testing_internal';
import {TestComponentBuilder, ComponentFixtureAutoDetect, ComponentFixtureNoNgZone} from '@angular/compiler/testing';
import {AsyncTestCompleter} from '@angular/core/testing/testing_internal';
import {Injectable, Component, Input, ViewMetadata, ComponentResolver} from '@angular/core';
import {Injectable, Component, Input, ViewMetadata} from '@angular/core';
import {NgIf} from '@angular/common';
import {TimerWrapper} from '../src/facade/async';
import {IS_DART} from '../src/facade/lang';
@ -320,6 +320,15 @@ export function main() {
});
}));
it('should create components synchronously',
inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
let componentFixture =
tcb.overrideTemplate(MockChildComp, '<span>Mock</span>').createSync(MockChildComp);
componentFixture.detectChanges();
expect(componentFixture.nativeElement).toHaveText('Mock');
}));
if (!IS_DART) {
describe('ComponentFixture', () => {
it('should auto detect changes if autoDetectChanges is called',
@ -604,27 +613,6 @@ export function main() {
}));
});
describe('createSync', () => {
it('should create components',
inject(
[ComponentResolver, TestComponentBuilder, AsyncTestCompleter],
(cr: ComponentResolver, tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
cr.resolveComponent(MyIfComp).then((cmpFactory) => {
let componentFixture = tcb.createSync(cmpFactory);
componentFixture.detectChanges();
expect(componentFixture.nativeElement).toHaveText('MyIf()');
componentFixture.componentInstance.showMore = true;
componentFixture.detectChanges();
expect(componentFixture.nativeElement).toHaveText('MyIf(More)');
async.done();
});
}));
});
});
}
});

View File

@ -6,18 +6,19 @@
* found in the LICENSE file at https://angular.io/license
*/
import {beforeEach, ddescribe, describe, expect, iit, it,} from '@angular/core/testing/testing_internal';
import {beforeEach, ddescribe, describe, expect, iit, it, inject,} from '@angular/core/testing/testing_internal';
import {stringify} from '../src/facade/lang';
import {MockViewResolver} from '../testing';
import {Component, ViewMetadata} from '@angular/core';
import {Component, ViewMetadata, Injector} from '@angular/core';
import {isBlank} from '../src/facade/lang';
export function main() {
describe('MockViewResolver', () => {
var viewResolver: MockViewResolver;
beforeEach(() => { viewResolver = new MockViewResolver(); });
beforeEach(inject(
[Injector], (injector: Injector) => { viewResolver = new MockViewResolver(injector); }));
describe('View overriding', () => {
it('should fallback to the default ViewResolver when templates are not overridden', () => {
@ -33,13 +34,12 @@ export function main() {
expect(isBlank(view.directives)).toBe(true);
});
it('should not allow overriding a view after it has been resolved', () => {
it('should allow overriding a view after it has been resolved', () => {
viewResolver.resolve(SomeComponent);
expect(() => {
viewResolver.setView(SomeComponent, new ViewMetadata({template: 'overridden template'}));
})
.toThrowError(
`The component ${stringify(SomeComponent)} has already been compiled, its configuration can not be changed`);
viewResolver.setView(SomeComponent, new ViewMetadata({template: 'overridden template'}));
var view = viewResolver.resolve(SomeComponent);
expect(view.template).toEqual('overridden template');
expect(isBlank(view.directives)).toBe(true);
});
});
@ -58,11 +58,11 @@ export function main() {
expect(view.template).toEqual('overridden template x 2');
});
it('should not allow overriding a view after it has been resolved', () => {
it('should allow overriding a view after it has been resolved', () => {
viewResolver.resolve(SomeComponent);
expect(() => { viewResolver.setInlineTemplate(SomeComponent, 'overridden template'); })
.toThrowError(
`The component ${stringify(SomeComponent)} has already been compiled, its configuration can not be changed`);
viewResolver.setInlineTemplate(SomeComponent, 'overridden template');
var view = viewResolver.resolve(SomeComponent);
expect(view.template).toEqual('overridden template');
});
});
@ -90,13 +90,12 @@ export function main() {
`Overriden directive ${stringify(SomeOtherDirective)} not found in the template of ${stringify(SomeComponent)}`);
});
it('should not allow overriding a directive after its view has been resolved', () => {
it('should allow overriding a directive after its view has been resolved', () => {
viewResolver.resolve(SomeComponent);
expect(() => {
viewResolver.overrideViewDirective(SomeComponent, SomeDirective, SomeOtherDirective);
})
.toThrowError(
`The component ${stringify(SomeComponent)} has already been compiled, its configuration can not be changed`);
viewResolver.overrideViewDirective(SomeComponent, SomeDirective, SomeOtherDirective);
var view = viewResolver.resolve(SomeComponent);
expect(view.directives.length).toEqual(1);
expect(view.directives[0]).toBe(SomeOtherDirective);
});
});
});