feat(compiler): introduce schema for elements

Closes #3353
This commit is contained in:
Pawel Kozlowski
2015-07-29 14:01:18 +02:00
parent aae5a4cece
commit d894aa9101
12 changed files with 141 additions and 49 deletions
@@ -26,6 +26,7 @@ import {
} from 'angular2/src/render/api';
import {CompileStep} from 'angular2/src/render/dom/compiler/compile_step';
import {CompileStepFactory} from 'angular2/src/render/dom/compiler/compile_step_factory';
import {ElementSchemaRegistry} from 'angular2/src/render/dom/schema/element_schema_registry';
import {ViewLoader, TemplateAndStyles} from 'angular2/src/render/dom/compiler/view_loader';
import {resolveInternalDomProtoView} from 'angular2/src/render/dom/view/proto_view';
@@ -48,7 +49,8 @@ export function runCompilerCommonTests() {
var tplLoader = new FakeViewLoader(urlData);
mockStepFactory =
new MockStepFactory([new MockStep(processElementClosure, processStyleClosure)]);
return new DomCompiler(mockStepFactory, tplLoader, sharedStylesHost);
return new DomCompiler(new ElementSchemaRegistry(), mockStepFactory, tplLoader,
sharedStylesHost);
}
describe('compile', () => {
@@ -0,0 +1,48 @@
import {
beforeEach,
ddescribe,
xdescribe,
describe,
expect,
iit,
inject,
it,
xit,
IS_DARTIUM
} from 'angular2/test_lib';
import {DomElementSchemaRegistry} from 'angular2/src/render/dom/schema/dom_element_schema_registry';
import {DOM} from 'angular2/src/dom/dom_adapter';
export function main() {
// DOMElementSchema can only be used on the JS side where we can safely
// use reflection for DOM elements
if (IS_DARTIUM) return;
var registry;
beforeEach(() => { registry = new DomElementSchemaRegistry(); });
describe('DOMElementSchema', () => {
it('should detect properties on regular elements', () => {
var divEl = DOM.createElement('div');
expect(registry.hasProperty(divEl, 'id')).toBeTruthy();
expect(registry.hasProperty(divEl, 'title')).toBeTruthy();
expect(registry.hasProperty(divEl, 'unknown')).toBeFalsy();
});
it('should return true for custom-like elements', () => {
var customLikeEl = DOM.createElement('custom-like');
expect(registry.hasProperty(customLikeEl, 'unknown')).toBeTruthy();
});
it('should not re-map property names that are not specified in DOM facade',
() => { expect(registry.getMappedPropName('readonly')).toEqual('readOnly'); });
it('should not re-map property names that are not specified in DOM facade', () => {
expect(registry.getMappedPropName('title')).toEqual('title');
expect(registry.getMappedPropName('exotic-unknown')).toEqual('exotic-unknown');
});
});
}
@@ -11,6 +11,7 @@ import {
IS_DARTIUM
} from 'angular2/test_lib';
import {DomElementSchemaRegistry} from 'angular2/src/render/dom/schema/dom_element_schema_registry';
import {ProtoViewBuilder} from 'angular2/src/render/dom/view/proto_view_builder';
import {ASTWithSource, AST} from 'angular2/src/change_detection/change_detection';
import {PropertyBindingType, ViewType, ViewEncapsulation} from 'angular2/src/render/api';
@@ -31,7 +32,7 @@ export function main() {
it('should throw for unknown properties', () => {
builder.bindElement(el('<div/>')).bindProperty('unknownProperty', emptyExpr());
expect(() => builder.build())
expect(() => builder.build(new DomElementSchemaRegistry()))
.toThrowError(
`Can't bind to 'unknownProperty' since it isn't a known property of the '<div>' element and there are no matching directives with a corresponding property`);
});
@@ -40,20 +41,20 @@ export function main() {
var binder = builder.bindElement(el('<div/>'));
binder.bindDirective(0).bindProperty('someDirProperty', emptyExpr(), 'directiveProperty');
binder.bindProperty('directiveProperty', emptyExpr());
expect(() => builder.build()).not.toThrow();
expect(() => builder.build(new DomElementSchemaRegistry())).not.toThrow();
});
it('should allow unknown properties on custom elements', () => {
var binder = builder.bindElement(el('<some-custom/>'));
binder.bindProperty('unknownProperty', emptyExpr());
expect(() => builder.build()).not.toThrow();
expect(() => builder.build(new DomElementSchemaRegistry())).not.toThrow();
});
it('should throw for unknown properties on custom elements if there is an ng component', () => {
var binder = builder.bindElement(el('<some-custom/>'));
binder.bindProperty('unknownProperty', emptyExpr());
binder.setComponentId('someComponent');
expect(() => builder.build())
expect(() => builder.build(new DomElementSchemaRegistry()))
.toThrowError(
`Can't bind to 'unknownProperty' since it isn't a known property of the '<some-custom>' element and there are no matching directives with a corresponding property`);
});
@@ -65,9 +66,9 @@ export function main() {
// TODO(tbosch): This is just a temporary test that makes sure that the dart server and
// dart browser is in sync. Change this to "not contains notifyBinding"
// when https://github.com/angular/angular/issues/3019 is solved.
it('should throw for unknown properties', () => {
it('should not throw for unknown properties', () => {
builder.bindElement(el('<div/>')).bindProperty('unknownProperty', emptyExpr());
expect(() => builder.build()).not.toThrow();
expect(() => builder.build(new DomElementSchemaRegistry())).not.toThrow();
});
});
@@ -76,19 +77,19 @@ export function main() {
describe('property normalization', () => {
it('should normalize "innerHtml" to "innerHTML"', () => {
builder.bindElement(el('<div/>')).bindProperty('innerHtml', emptyExpr());
var pv = builder.build();
var pv = builder.build(new DomElementSchemaRegistry());
expect(pv.elementBinders[0].propertyBindings[0].property).toEqual('innerHTML');
});
it('should normalize "tabindex" to "tabIndex"', () => {
builder.bindElement(el('<div/>')).bindProperty('tabindex', emptyExpr());
var pv = builder.build();
var pv = builder.build(new DomElementSchemaRegistry());
expect(pv.elementBinders[0].propertyBindings[0].property).toEqual('tabIndex');
});
it('should normalize "readonly" to "readOnly"', () => {
builder.bindElement(el('<input/>')).bindProperty('readonly', emptyExpr());
var pv = builder.build();
var pv = builder.build(new DomElementSchemaRegistry());
expect(pv.elementBinders[0].propertyBindings[0].property).toEqual('readOnly');
});
@@ -97,32 +98,32 @@ export function main() {
describe('property binding types', () => {
it('should detect property names', () => {
builder.bindElement(el('<div/>')).bindProperty('tabindex', emptyExpr());
var pv = builder.build();
var pv = builder.build(new DomElementSchemaRegistry());
expect(pv.elementBinders[0].propertyBindings[0].type).toEqual(PropertyBindingType.PROPERTY);
});
it('should detect attribute names', () => {
builder.bindElement(el('<div/>')).bindProperty('attr.someName', emptyExpr());
var pv = builder.build();
var pv = builder.build(new DomElementSchemaRegistry());
expect(pv.elementBinders[0].propertyBindings[0].type)
.toEqual(PropertyBindingType.ATTRIBUTE);
});
it('should detect class names', () => {
builder.bindElement(el('<div/>')).bindProperty('class.someName', emptyExpr());
var pv = builder.build();
var pv = builder.build(new DomElementSchemaRegistry());
expect(pv.elementBinders[0].propertyBindings[0].type).toEqual(PropertyBindingType.CLASS);
});
it('should detect style names', () => {
builder.bindElement(el('<div/>')).bindProperty('style.someName', emptyExpr());
var pv = builder.build();
var pv = builder.build(new DomElementSchemaRegistry());
expect(pv.elementBinders[0].propertyBindings[0].type).toEqual(PropertyBindingType.STYLE);
});
it('should detect style units', () => {
builder.bindElement(el('<div/>')).bindProperty('style.someName.someUnit', emptyExpr());
var pv = builder.build();
var pv = builder.build(new DomElementSchemaRegistry());
expect(pv.elementBinders[0].propertyBindings[0].unit).toEqual('someUnit');
});
});
@@ -30,6 +30,7 @@ import {DOM} from 'angular2/src/dom/dom_adapter';
import {cloneAndQueryProtoView} from 'angular2/src/render/dom/util';
import {resolveInternalDomProtoView} from 'angular2/src/render/dom/view/proto_view';
import {ProtoViewBuilder} from 'angular2/src/render/dom/view/proto_view_builder';
import {ElementSchemaRegistry} from 'angular2/src/render/dom/schema/element_schema_registry';
export function main() {
describe('ProtoViewMerger integration test', () => {
@@ -247,7 +248,7 @@ export function main() {
var builder = new ProtoViewBuilder(DOM.createTemplate(''), ViewType.COMPONENT,
ViewEncapsulation.NONE);
builder.setHostAttribute('a', 'b');
var componentProtoViewDto = builder.build();
var componentProtoViewDto = builder.build(new ElementSchemaRegistry());
tb.merge([rootProtoViewDto, componentProtoViewDto])
.then(mergeMappings => {
var domPv = resolveInternalDomProtoView(mergeMappings.mergedProtoViewRef);