2015-05-26 12:25:39 -04:00
|
|
|
import {
|
|
|
|
AsyncTestCompleter,
|
|
|
|
beforeEach,
|
|
|
|
ddescribe,
|
|
|
|
describe,
|
|
|
|
el,
|
|
|
|
expect,
|
|
|
|
iit,
|
|
|
|
inject,
|
|
|
|
it,
|
|
|
|
} from 'angular2/test_lib';
|
|
|
|
|
|
|
|
import {DOM} from 'angular2/src/dom/dom_adapter';
|
|
|
|
import {List, ListWrapper, Map, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
2015-06-10 08:40:24 -04:00
|
|
|
import {Type, isBlank, stringify, isPresent, BaseException} from 'angular2/src/facade/lang';
|
2015-05-26 12:25:39 -04:00
|
|
|
import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
|
|
|
|
|
|
|
|
import {DomCompiler} from 'angular2/src/render/dom/compiler/compiler';
|
2015-07-24 18:28:44 -04:00
|
|
|
import {
|
|
|
|
ProtoViewDto,
|
|
|
|
ViewDefinition,
|
|
|
|
DirectiveMetadata,
|
|
|
|
ViewType,
|
|
|
|
ViewEncapsulation
|
|
|
|
} from 'angular2/src/render/api';
|
2015-05-26 12:25:39 -04:00
|
|
|
import {CompileStep} from 'angular2/src/render/dom/compiler/compile_step';
|
|
|
|
import {CompileStepFactory} from 'angular2/src/render/dom/compiler/compile_step_factory';
|
2015-07-29 08:01:18 -04:00
|
|
|
import {ElementSchemaRegistry} from 'angular2/src/render/dom/schema/element_schema_registry';
|
2015-07-24 18:28:44 -04:00
|
|
|
import {ViewLoader, TemplateAndStyles} from 'angular2/src/render/dom/compiler/view_loader';
|
2015-05-26 12:25:39 -04:00
|
|
|
|
|
|
|
import {resolveInternalDomProtoView} from 'angular2/src/render/dom/view/proto_view';
|
2015-07-24 18:28:44 -04:00
|
|
|
import {SharedStylesHost} from 'angular2/src/render/dom/view/shared_styles_host';
|
2015-07-31 13:58:24 -04:00
|
|
|
import {TemplateCloner} from 'angular2/src/render/dom/template_cloner';
|
2015-07-24 18:28:44 -04:00
|
|
|
|
|
|
|
import {MockStep} from './pipeline_spec';
|
2015-05-26 12:25:39 -04:00
|
|
|
|
|
|
|
export function runCompilerCommonTests() {
|
|
|
|
describe('DomCompiler', function() {
|
2015-06-17 14:17:21 -04:00
|
|
|
var mockStepFactory: MockStepFactory;
|
2015-07-24 18:28:44 -04:00
|
|
|
var sharedStylesHost: SharedStylesHost;
|
|
|
|
|
|
|
|
beforeEach(() => {sharedStylesHost = new SharedStylesHost()});
|
2015-05-26 12:25:39 -04:00
|
|
|
|
2015-07-24 18:28:44 -04:00
|
|
|
function createCompiler(processElementClosure = null, processStyleClosure = null,
|
|
|
|
urlData = null) {
|
2015-05-26 12:25:39 -04:00
|
|
|
if (isBlank(urlData)) {
|
2015-06-17 19:21:40 -04:00
|
|
|
urlData = new Map();
|
2015-05-26 12:25:39 -04:00
|
|
|
}
|
2015-06-24 04:43:36 -04:00
|
|
|
var tplLoader = new FakeViewLoader(urlData);
|
2015-07-24 18:28:44 -04:00
|
|
|
mockStepFactory =
|
|
|
|
new MockStepFactory([new MockStep(processElementClosure, processStyleClosure)]);
|
2015-07-31 13:58:24 -04:00
|
|
|
return new DomCompiler(new ElementSchemaRegistry(), new TemplateCloner(-1), mockStepFactory,
|
|
|
|
tplLoader, sharedStylesHost);
|
2015-05-26 12:25:39 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
describe('compile', () => {
|
|
|
|
|
|
|
|
it('should run the steps and build the AppProtoView of the root element',
|
|
|
|
inject([AsyncTestCompleter], (async) => {
|
|
|
|
var compiler = createCompiler((parent, current, control) => {
|
|
|
|
current.inheritedProtoView.bindVariable('b', 'a');
|
|
|
|
});
|
|
|
|
compiler.compile(
|
|
|
|
new ViewDefinition({componentId: 'someComponent', template: '<div></div>'}))
|
|
|
|
.then((protoView) => {
|
|
|
|
expect(protoView.variableBindings)
|
|
|
|
.toEqual(MapWrapper.createFromStringMap({'a': 'b'}));
|
|
|
|
async.done();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
it('should run the steps and build the proto view', inject([AsyncTestCompleter], (async) => {
|
|
|
|
var compiler = createCompiler((parent, current, control) => {
|
|
|
|
current.inheritedProtoView.bindVariable('b', 'a');
|
|
|
|
});
|
|
|
|
|
2015-06-09 06:33:40 -04:00
|
|
|
var dirMetadata = DirectiveMetadata.create(
|
2015-07-24 18:28:44 -04:00
|
|
|
{id: 'id', selector: 'custom', type: DirectiveMetadata.COMPONENT_TYPE});
|
2015-05-26 12:25:39 -04:00
|
|
|
compiler.compileHost(dirMetadata)
|
|
|
|
.then((protoView) => {
|
2015-07-29 20:41:09 -04:00
|
|
|
expect(DOM.tagName(DOM.firstChild(DOM.content(templateRoot(protoView))))
|
2015-07-24 18:28:44 -04:00
|
|
|
.toLowerCase())
|
|
|
|
.toEqual('custom');
|
2015-05-26 12:25:39 -04:00
|
|
|
expect(mockStepFactory.viewDef.directives).toEqual([dirMetadata]);
|
|
|
|
expect(protoView.variableBindings)
|
|
|
|
.toEqual(MapWrapper.createFromStringMap({'a': 'b'}));
|
|
|
|
async.done();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
2015-07-28 14:38:40 -04:00
|
|
|
it('should create element from component selector', inject([AsyncTestCompleter], (async) => {
|
|
|
|
var compiler = createCompiler((parent, current, control) => {
|
|
|
|
current.inheritedProtoView.bindVariable('b', 'a');
|
|
|
|
});
|
|
|
|
|
|
|
|
var dirMetadata = DirectiveMetadata.create({
|
|
|
|
id: 'id',
|
|
|
|
selector: 'marquee.jazzy[size=huge]',
|
|
|
|
type: DirectiveMetadata.COMPONENT_TYPE
|
|
|
|
});
|
|
|
|
|
|
|
|
compiler.compileHost(dirMetadata)
|
|
|
|
.then((protoView) => {
|
|
|
|
let element = DOM.firstChild(DOM.content(templateRoot(protoView)));
|
|
|
|
expect(DOM.tagName(element).toLowerCase()).toEqual('marquee');
|
|
|
|
expect(DOM.hasClass(element, 'jazzy')).toBe(true);
|
|
|
|
expect(DOM.getAttribute(element, 'size')).toEqual('huge');
|
|
|
|
async.done();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
2015-05-26 12:25:39 -04:00
|
|
|
it('should use the inline template and compile in sync',
|
|
|
|
inject([AsyncTestCompleter], (async) => {
|
|
|
|
var compiler = createCompiler(EMPTY_STEP);
|
|
|
|
compiler.compile(
|
|
|
|
new ViewDefinition({componentId: 'someId', template: 'inline component'}))
|
|
|
|
.then((protoView) => {
|
2015-07-29 20:41:09 -04:00
|
|
|
expect(DOM.getInnerHTML(templateRoot(protoView))).toEqual('inline component');
|
2015-05-26 12:25:39 -04:00
|
|
|
async.done();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
it('should load url templates', inject([AsyncTestCompleter], (async) => {
|
|
|
|
var urlData = MapWrapper.createFromStringMap({'someUrl': 'url component'});
|
2015-07-24 18:28:44 -04:00
|
|
|
var compiler = createCompiler(EMPTY_STEP, null, urlData);
|
2015-06-10 08:40:24 -04:00
|
|
|
compiler.compile(new ViewDefinition({componentId: 'someId', templateAbsUrl: 'someUrl'}))
|
2015-05-26 12:25:39 -04:00
|
|
|
.then((protoView) => {
|
2015-07-29 20:41:09 -04:00
|
|
|
expect(DOM.getInnerHTML(templateRoot(protoView))).toEqual('url component');
|
2015-05-26 12:25:39 -04:00
|
|
|
async.done();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
2015-08-05 10:35:59 -04:00
|
|
|
it('should remove script tags from templates', inject([AsyncTestCompleter], (async) => {
|
|
|
|
var compiler = createCompiler(EMPTY_STEP);
|
|
|
|
compiler.compile(new ViewDefinition(
|
|
|
|
{componentId: 'someId', template: '<div></div><script></script>'}))
|
|
|
|
.then((protoView) => {
|
|
|
|
expect(DOM.getInnerHTML(templateRoot(protoView))).toEqual('<div></div>');
|
|
|
|
async.done();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
2015-05-26 12:25:39 -04:00
|
|
|
it('should report loading errors', inject([AsyncTestCompleter], (async) => {
|
2015-07-24 18:28:44 -04:00
|
|
|
var compiler = createCompiler(EMPTY_STEP, null, new Map());
|
2015-05-26 12:25:39 -04:00
|
|
|
PromiseWrapper.catchError(
|
2015-06-10 08:40:24 -04:00
|
|
|
compiler.compile(
|
|
|
|
new ViewDefinition({componentId: 'someId', templateAbsUrl: 'someUrl'})),
|
2015-05-26 12:25:39 -04:00
|
|
|
(e) => {
|
2015-06-01 05:07:14 -04:00
|
|
|
expect(e.message).toEqual(
|
|
|
|
'Failed to load the template for "someId" : Failed to fetch url "someUrl"');
|
2015-05-26 12:25:39 -04:00
|
|
|
async.done();
|
|
|
|
return null;
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
it('should return ProtoViews of type COMPONENT_VIEW_TYPE',
|
|
|
|
inject([AsyncTestCompleter], (async) => {
|
|
|
|
var compiler = createCompiler(EMPTY_STEP);
|
|
|
|
compiler.compile(
|
|
|
|
new ViewDefinition({componentId: 'someId', template: 'inline component'}))
|
|
|
|
.then((protoView) => {
|
2015-06-09 09:20:33 -04:00
|
|
|
expect(protoView.type).toEqual(ViewType.COMPONENT);
|
2015-05-26 12:25:39 -04:00
|
|
|
async.done();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('compileHost', () => {
|
|
|
|
|
|
|
|
it('should return ProtoViews of type HOST_VIEW_TYPE',
|
|
|
|
inject([AsyncTestCompleter], (async) => {
|
|
|
|
var compiler = createCompiler(EMPTY_STEP);
|
|
|
|
compiler.compileHost(someComponent)
|
|
|
|
.then((protoView) => {
|
2015-06-09 09:20:33 -04:00
|
|
|
expect(protoView.type).toEqual(ViewType.HOST);
|
2015-05-26 12:25:39 -04:00
|
|
|
async.done();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
});
|
|
|
|
|
2015-07-24 18:28:44 -04:00
|
|
|
describe('compile styles', () => {
|
|
|
|
it('should run the steps', inject([AsyncTestCompleter], (async) => {
|
|
|
|
var compiler = createCompiler(null, (style) => { return style + 'b {};'; });
|
|
|
|
compiler.compile(new ViewDefinition(
|
|
|
|
{componentId: 'someComponent', template: '', styles: ['a {};']}))
|
|
|
|
.then((protoViewDto) => {
|
|
|
|
expect(sharedStylesHost.getAllStyles()).toEqual(['a {};b {};']);
|
|
|
|
async.done();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
it('should store the styles in the SharedStylesHost for ViewEncapsulation.NONE',
|
|
|
|
inject([AsyncTestCompleter], (async) => {
|
|
|
|
var compiler = createCompiler();
|
|
|
|
compiler.compile(new ViewDefinition({
|
|
|
|
componentId: 'someComponent',
|
|
|
|
template: '',
|
|
|
|
styles: ['a {};'],
|
|
|
|
encapsulation: ViewEncapsulation.NONE
|
|
|
|
}))
|
|
|
|
.then((protoViewDto) => {
|
2015-07-29 20:41:09 -04:00
|
|
|
expect(DOM.getInnerHTML(templateRoot(protoViewDto))).toEqual('');
|
2015-07-24 18:28:44 -04:00
|
|
|
expect(sharedStylesHost.getAllStyles()).toEqual(['a {};']);
|
|
|
|
async.done();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
it('should store the styles in the SharedStylesHost for ViewEncapsulation.EMULATED',
|
|
|
|
inject([AsyncTestCompleter], (async) => {
|
|
|
|
var compiler = createCompiler();
|
|
|
|
compiler.compile(new ViewDefinition({
|
|
|
|
componentId: 'someComponent',
|
|
|
|
template: '',
|
|
|
|
styles: ['a {};'],
|
|
|
|
encapsulation: ViewEncapsulation.EMULATED
|
|
|
|
}))
|
|
|
|
.then((protoViewDto) => {
|
2015-07-29 20:41:09 -04:00
|
|
|
expect(DOM.getInnerHTML(templateRoot(protoViewDto))).toEqual('');
|
2015-07-24 18:28:44 -04:00
|
|
|
expect(sharedStylesHost.getAllStyles()).toEqual(['a {};']);
|
|
|
|
async.done();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
if (DOM.supportsNativeShadowDOM()) {
|
|
|
|
it('should store the styles in the template for ViewEncapsulation.NATIVE',
|
|
|
|
inject([AsyncTestCompleter], (async) => {
|
|
|
|
var compiler = createCompiler();
|
|
|
|
compiler.compile(new ViewDefinition({
|
|
|
|
componentId: 'someComponent',
|
|
|
|
template: '',
|
|
|
|
styles: ['a {};'],
|
|
|
|
encapsulation: ViewEncapsulation.NATIVE
|
|
|
|
}))
|
|
|
|
.then((protoViewDto) => {
|
2015-07-29 20:41:09 -04:00
|
|
|
expect(DOM.getInnerHTML(templateRoot(protoViewDto)))
|
2015-07-24 18:28:44 -04:00
|
|
|
.toEqual('<style>a {};</style>');
|
|
|
|
expect(sharedStylesHost.getAllStyles()).toEqual([]);
|
|
|
|
async.done();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
|
|
|
it('should default to ViewEncapsulation.NONE if no styles are specified',
|
|
|
|
inject([AsyncTestCompleter], (async) => {
|
|
|
|
var compiler = createCompiler();
|
|
|
|
compiler.compile(
|
|
|
|
new ViewDefinition({componentId: 'someComponent', template: '', styles: []}))
|
|
|
|
.then((protoView) => {
|
|
|
|
expect(mockStepFactory.viewDef.encapsulation).toBe(ViewEncapsulation.NONE);
|
|
|
|
async.done();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
it('should default to ViewEncapsulation.EMULATED if styles are specified',
|
|
|
|
inject([AsyncTestCompleter], (async) => {
|
|
|
|
var compiler = createCompiler();
|
|
|
|
compiler.compile(new ViewDefinition(
|
|
|
|
{componentId: 'someComponent', template: '', styles: ['a {};']}))
|
|
|
|
.then((protoView) => {
|
|
|
|
expect(mockStepFactory.viewDef.encapsulation).toBe(ViewEncapsulation.EMULATED);
|
|
|
|
async.done();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('mergeProtoViews', () => {
|
|
|
|
it('should store the styles of the merged ProtoView in the SharedStylesHost',
|
|
|
|
inject([AsyncTestCompleter], (async) => {
|
|
|
|
var compiler = createCompiler();
|
|
|
|
compiler.compile(new ViewDefinition(
|
|
|
|
{componentId: 'someComponent', template: '', styles: ['a {};']}))
|
|
|
|
.then(protoViewDto => compiler.mergeProtoViewsRecursively([protoViewDto.render]))
|
|
|
|
.then(_ => {
|
|
|
|
expect(sharedStylesHost.getAllStyles()).toEqual(['a {};']);
|
|
|
|
async.done();
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
|
|
|
|
});
|
|
|
|
|
2015-05-26 12:25:39 -04:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2015-07-31 13:58:24 -04:00
|
|
|
function templateRoot(protoViewDto: ProtoViewDto): Element {
|
2015-07-29 20:41:09 -04:00
|
|
|
var pv = resolveInternalDomProtoView(protoViewDto.render);
|
2015-07-31 13:58:24 -04:00
|
|
|
return (<Element>pv.cloneableTemplate);
|
2015-07-29 20:41:09 -04:00
|
|
|
}
|
|
|
|
|
2015-05-26 12:25:39 -04:00
|
|
|
class MockStepFactory extends CompileStepFactory {
|
|
|
|
steps: List<CompileStep>;
|
|
|
|
subTaskPromises: List<Promise<any>>;
|
|
|
|
viewDef: ViewDefinition;
|
|
|
|
|
|
|
|
constructor(steps) {
|
|
|
|
super();
|
|
|
|
this.steps = steps;
|
|
|
|
}
|
2015-06-15 09:57:42 -04:00
|
|
|
createSteps(viewDef): List<CompileStep> {
|
2015-05-26 12:25:39 -04:00
|
|
|
this.viewDef = viewDef;
|
|
|
|
return this.steps;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var EMPTY_STEP = (parent, current, control) => {
|
|
|
|
if (isPresent(parent)) {
|
|
|
|
current.inheritedProtoView = parent.inheritedProtoView;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-06-24 04:43:36 -04:00
|
|
|
class FakeViewLoader extends ViewLoader {
|
2015-05-26 12:25:39 -04:00
|
|
|
_urlData: Map<string, string>;
|
|
|
|
constructor(urlData) {
|
2015-06-15 09:57:42 -04:00
|
|
|
super(null, null, null);
|
2015-05-26 12:25:39 -04:00
|
|
|
this._urlData = urlData;
|
|
|
|
}
|
|
|
|
|
2015-07-24 18:28:44 -04:00
|
|
|
load(viewDef): Promise<any> {
|
|
|
|
var styles = isPresent(viewDef.styles) ? viewDef.styles : [];
|
|
|
|
if (isPresent(viewDef.template)) {
|
|
|
|
return PromiseWrapper.resolve(new TemplateAndStyles(viewDef.template, styles));
|
2015-05-26 12:25:39 -04:00
|
|
|
}
|
|
|
|
|
2015-07-24 18:28:44 -04:00
|
|
|
if (isPresent(viewDef.templateAbsUrl)) {
|
|
|
|
var content = this._urlData.get(viewDef.templateAbsUrl);
|
2015-06-01 05:07:14 -04:00
|
|
|
return isPresent(content) ?
|
2015-07-24 18:28:44 -04:00
|
|
|
PromiseWrapper.resolve(new TemplateAndStyles(content, styles)) :
|
|
|
|
PromiseWrapper.reject(`Failed to fetch url "${viewDef.templateAbsUrl}"`, null);
|
2015-05-26 12:25:39 -04:00
|
|
|
}
|
|
|
|
|
2015-06-10 08:40:24 -04:00
|
|
|
throw new BaseException('View should have either the templateUrl or template property set');
|
2015-05-26 12:25:39 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-09 06:33:40 -04:00
|
|
|
var someComponent = DirectiveMetadata.create(
|
2015-05-26 12:25:39 -04:00
|
|
|
{selector: 'some-comp', id: 'someComponent', type: DirectiveMetadata.COMPONENT_TYPE});
|