feature(ShadowDomTransformer): create a compiler step to transform the shadow DOM
This commit is contained in:
@ -70,23 +70,6 @@ export function main() {
|
||||
});
|
||||
});
|
||||
|
||||
it('should use the shadow dom strategy to process the template', (done) => {
|
||||
// TODO(vicb) test in Dart when the bug is fixed
|
||||
// https://code.google.com/p/dart/issues/detail?id=18249
|
||||
if (IS_DARTIUM) {
|
||||
done();
|
||||
return;
|
||||
}
|
||||
var templateHtml = 'processed template';
|
||||
var compiler = createCompiler((parent, current, control) => {
|
||||
current.inheritedProtoView = new ProtoView(current.element, null, null);
|
||||
}, new FakeShadowDomStrategy(templateHtml));
|
||||
compiler.compile(MainComponent, null).then( (protoView) => {
|
||||
expect(DOM.getInnerHTML(protoView.element)).toEqual('processed template');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should load nested components', (done) => {
|
||||
var mainEl = el('<div></div>');
|
||||
var compiler = createCompiler( (parent, current, control) => {
|
||||
@ -244,15 +227,3 @@ class MockStep extends CompileStep {
|
||||
this.processClosure(parent, current, control);
|
||||
}
|
||||
}
|
||||
|
||||
class FakeShadowDomStrategy extends NativeShadowDomStrategy {
|
||||
templateHtml: string;
|
||||
constructor(templateHtml: string) {
|
||||
super();
|
||||
this.templateHtml = templateHtml;
|
||||
}
|
||||
|
||||
processTemplate(template: Element, cmpMetadata: DirectiveMetadata) {
|
||||
DOM.setInnerHTML(template, this.templateHtml);
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,8 @@ import {Template, Decorator, Component} from 'angular2/src/core/annotations/anno
|
||||
export function main() {
|
||||
describe('ElementBindingMarker', () => {
|
||||
|
||||
function createPipeline({textNodeBindings, propertyBindings, variableBindings, eventBindings, directives}={}) {
|
||||
function createPipeline({textNodeBindings, propertyBindings, variableBindings, eventBindings,
|
||||
directives, ignoreBindings}={}) {
|
||||
var reader = new DirectiveMetadataReader();
|
||||
return new CompilePipeline([
|
||||
new MockStep((parent, current, control) => {
|
||||
@ -30,6 +31,9 @@ export function main() {
|
||||
if (isPresent(eventBindings)) {
|
||||
current.eventBindings = eventBindings;
|
||||
}
|
||||
if (isPresent(ignoreBindings)) {
|
||||
current.ignoreBindings = ignoreBindings;
|
||||
}
|
||||
if (isPresent(directives)) {
|
||||
for (var i=0; i<directives.length; i++) {
|
||||
current.addDirective(reader.read(directives[i]));
|
||||
@ -44,6 +48,14 @@ export function main() {
|
||||
assertBinding(results[0], false);
|
||||
});
|
||||
|
||||
it('should not mark elements when ignoreBindings is true', () => {
|
||||
var textNodeBindings = MapWrapper.create();
|
||||
MapWrapper.set(textNodeBindings, 0, 'expr');
|
||||
var results = createPipeline({textNodeBindings: textNodeBindings,
|
||||
ignoreBindings: true}).process(el('<div></div>'));
|
||||
assertBinding(results[0], false);
|
||||
});
|
||||
|
||||
it('should mark elements with text node bindings', () => {
|
||||
var textNodeBindings = MapWrapper.create();
|
||||
MapWrapper.set(textNodeBindings, 0, 'expr');
|
||||
|
@ -3,15 +3,24 @@ import {PropertyBindingParser} from 'angular2/src/core/compiler/pipeline/propert
|
||||
import {CompilePipeline} from 'angular2/src/core/compiler/pipeline/compile_pipeline';
|
||||
import {DOM} from 'angular2/src/facade/dom';
|
||||
import {MapWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
import {CompileElement} from 'angular2/src/core/compiler/pipeline/compile_element';
|
||||
import {CompileStep} from 'angular2/src/core/compiler/pipeline/compile_step'
|
||||
import {CompileControl} from 'angular2/src/core/compiler/pipeline/compile_control';
|
||||
import {Lexer, Parser} from 'angular2/change_detection';
|
||||
|
||||
export function main() {
|
||||
describe('PropertyBindingParser', () => {
|
||||
function createPipeline() {
|
||||
return new CompilePipeline([new PropertyBindingParser(new Parser(new Lexer()), null)]);
|
||||
function createPipeline(ignoreBindings = false) {
|
||||
return new CompilePipeline([
|
||||
new MockStep((parent, current, control) => { current.ignoreBindings = ignoreBindings; }),
|
||||
new PropertyBindingParser(new Parser(new Lexer()), null)]);
|
||||
}
|
||||
|
||||
it('should not parse bindings when ignoreBindings is true', () => {
|
||||
var results = createPipeline(true).process(el('<div [a]="b"></div>'));
|
||||
expect(results[0].propertyBindings).toBe(null);
|
||||
});
|
||||
|
||||
it('should detect [] syntax', () => {
|
||||
var results = createPipeline().process(el('<div [a]="b"></div>'));
|
||||
expect(MapWrapper.get(results[0].propertyBindings, 'a').source).toEqual('b');
|
||||
@ -69,3 +78,14 @@ export function main() {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class MockStep extends CompileStep {
|
||||
processClosure:Function;
|
||||
constructor(process) {
|
||||
super();
|
||||
this.processClosure = process;
|
||||
}
|
||||
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
|
||||
this.processClosure(parent, current, control);
|
||||
}
|
||||
}
|
||||
|
128
modules/angular2/test/core/compiler/pipeline/shadow_dom_transformer_spec.js
vendored
Normal file
128
modules/angular2/test/core/compiler/pipeline/shadow_dom_transformer_spec.js
vendored
Normal file
@ -0,0 +1,128 @@
|
||||
import {describe, beforeEach, expect, it, iit, ddescribe, el} from 'angular2/test_lib';
|
||||
|
||||
import {CompilePipeline} from 'angular2/src/core/compiler/pipeline/compile_pipeline';
|
||||
import {ShadowDomTransformer} from 'angular2/src/core/compiler/pipeline/shadow_dom_transformer';
|
||||
import {Component} from 'angular2/src/core/annotations/annotations';
|
||||
import {DirectiveMetadata} from 'angular2/src/core/compiler/directive_metadata';
|
||||
import {ShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy';
|
||||
import {shimCssText} from 'angular2/src/core/compiler/shadow_dom_emulation/shim_css';
|
||||
|
||||
import {DOM} from 'angular2/src/facade/dom';
|
||||
import {MapWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
export function main() {
|
||||
describe('ShadowDomTransformer', () => {
|
||||
function createPipeline(selector, strategy:ShadowDomStrategy, styleHost) {
|
||||
var component = new Component({selector: selector});
|
||||
var meta = new DirectiveMetadata(null, component, null);
|
||||
var transformer = new ShadowDomTransformer(meta, strategy, styleHost);
|
||||
transformer.clearCache();
|
||||
return new CompilePipeline([transformer]);
|
||||
}
|
||||
|
||||
it('it should set ignoreBindings to true for style elements', () => {
|
||||
var host = DOM.createElement('div');
|
||||
var pipeline = createPipeline('foo', new FakeStrategy(false, false), host);
|
||||
var results = pipeline.process(el('<div><style></style></div>'));
|
||||
expect(results[0].ignoreBindings).toBe(false);
|
||||
expect(results[1].ignoreBindings).toBe(true);
|
||||
});
|
||||
|
||||
describe('css', () => {
|
||||
it('should not extract the styles when extractStyles() is false', () => {
|
||||
var host = DOM.createElement('div');
|
||||
var pipeline = createPipeline('foo', new FakeStrategy(false, false), host);
|
||||
var template = el('<style>.s{}</style>');
|
||||
pipeline.process(template);
|
||||
expect(template).toHaveText('.s{}');
|
||||
});
|
||||
|
||||
it('should move the styles to the host when extractStyles() is true', () => {
|
||||
var host = DOM.createElement('div');
|
||||
var pipeline = createPipeline('foo', new FakeStrategy(true, false), host);
|
||||
var template = el('<div><style>.s{}</style></div>');
|
||||
pipeline.process(template);
|
||||
expect(template).toHaveText('');
|
||||
expect(host).toHaveText('.s{}');
|
||||
});
|
||||
|
||||
it('should preserve original content when moving styles', () => {
|
||||
var host = el('<div>original content</div>');
|
||||
var pipeline = createPipeline('foo', new FakeStrategy(true, false), host);
|
||||
var template = el('<div><style>.s{}</style></div>');
|
||||
pipeline.process(template);
|
||||
expect(template).toHaveText('');
|
||||
expect(host).toHaveText('.s{}original content');
|
||||
});
|
||||
|
||||
it('should move the styles to the host in the original order', () => {
|
||||
var host = DOM.createElement('div');
|
||||
var pipeline = createPipeline('foo', new FakeStrategy(true, false), host);
|
||||
var template = el('<div><style>.s1{}</style><style>.s2{}</style></div>');
|
||||
pipeline.process(template);
|
||||
expect(host).toHaveText('.s1{}.s2{}');
|
||||
});
|
||||
|
||||
it('should shim the styles when shim() and extractStyles() are true', () => {
|
||||
var host = DOM.createElement('div');
|
||||
var pipeline = createPipeline('foo', new FakeStrategy(true, true), host);
|
||||
var template = el('<div><style>.s1{}</style></div>');
|
||||
pipeline.process(template);
|
||||
expect(host).toHaveText(shimCssText('.s1{}', 'foo'));
|
||||
});
|
||||
|
||||
it('should deduplicate styles before moving them when shim() is false', () => {
|
||||
var host = DOM.createElement('div');
|
||||
var pipeline = createPipeline('foo', new FakeStrategy(true, false), host);
|
||||
var template = el('<div><style>.s1{}</style><style>.s1{}</style><style>.s1{}</style></div>');
|
||||
pipeline.process(template);
|
||||
expect(host).toHaveText('.s1{}');
|
||||
});
|
||||
});
|
||||
|
||||
describe('html', () => {
|
||||
it('should add an attribute to all children when shim() is true', () => {
|
||||
var host = DOM.createElement('div');
|
||||
var pipeline = createPipeline('foo', new FakeStrategy(false, true), host);
|
||||
var template = el('<div><span></span></div>');
|
||||
pipeline.process(template);
|
||||
expect(DOM.getOuterHTML(template)).toEqual('<div foo=""><span foo=""></span></div>')
|
||||
});
|
||||
|
||||
it('should not modify the template when shim() is false', () => {
|
||||
var host = DOM.createElement('div');
|
||||
var pipeline = createPipeline('foo', new FakeStrategy(false, false), host);
|
||||
var template = el('<div><span></span></div>');
|
||||
pipeline.process(template);
|
||||
expect(DOM.getOuterHTML(template)).toEqual('<div><span></span></div>')
|
||||
});
|
||||
|
||||
it('should not throw with complex selectors', () => {
|
||||
var host = DOM.createElement('div');
|
||||
var pipeline = createPipeline('foo[bar]', new FakeStrategy(false, true), host);
|
||||
var template = el('<div><span></span></div>');
|
||||
expect(() => pipeline.process(template)).not.toThrow();
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class FakeStrategy extends ShadowDomStrategy {
|
||||
_extractStyles: boolean;
|
||||
_shim: boolean;
|
||||
|
||||
constructor(extractStyles: boolean, shim: boolean) {
|
||||
super();
|
||||
this._extractStyles = extractStyles;
|
||||
this._shim = shim;
|
||||
}
|
||||
|
||||
extractStyles(): boolean {
|
||||
return this._extractStyles;
|
||||
}
|
||||
|
||||
shim(): boolean {
|
||||
return this._shim;
|
||||
}
|
||||
}
|
@ -3,19 +3,27 @@ import {TextInterpolationParser} from 'angular2/src/core/compiler/pipeline/text_
|
||||
import {CompilePipeline} from 'angular2/src/core/compiler/pipeline/compile_pipeline';
|
||||
import {DOM} from 'angular2/src/facade/dom';
|
||||
import {MapWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
import {Lexer, Parser} from 'angular2/change_detection';
|
||||
import {CompileElement} from 'angular2/src/core/compiler/pipeline/compile_element';
|
||||
import {CompileStep} from 'angular2/src/core/compiler/pipeline/compile_step'
|
||||
import {CompileControl} from 'angular2/src/core/compiler/pipeline/compile_control';
|
||||
import {IgnoreChildrenStep} from './pipeline_spec';
|
||||
|
||||
export function main() {
|
||||
describe('TextInterpolationParser', () => {
|
||||
function createPipeline() {
|
||||
function createPipeline(ignoreBindings = false) {
|
||||
return new CompilePipeline([
|
||||
new MockStep((parent, current, control) => { current.ignoreBindings = ignoreBindings; }),
|
||||
new IgnoreChildrenStep(),
|
||||
new TextInterpolationParser(new Parser(new Lexer()), null)
|
||||
]);
|
||||
}
|
||||
|
||||
it('should not look for text interpolation when ignoreBindings is true', () => {
|
||||
var results = createPipeline(true).process(el('<div>{{expr1}}<span></span>{{expr2}}</div>'));
|
||||
expect(results[0].textNodeBindings).toBe(null);
|
||||
});
|
||||
|
||||
it('should find text interpolation in normal elements', () => {
|
||||
var results = createPipeline().process(el('<div>{{expr1}}<span></span>{{expr2}}</div>'));
|
||||
var bindings = results[0].textNodeBindings;
|
||||
@ -55,3 +63,14 @@ export function main() {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class MockStep extends CompileStep {
|
||||
processClosure:Function;
|
||||
constructor(process) {
|
||||
super();
|
||||
this.processClosure = process;
|
||||
}
|
||||
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
|
||||
this.processClosure(parent, current, control);
|
||||
}
|
||||
}
|
||||
|
@ -1,62 +0,0 @@
|
||||
import {describe, xit, it, expect, beforeEach, ddescribe, iit, el} from 'angular2/test_lib';
|
||||
import {NativeShadowDomStrategy, EmulatedShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy';
|
||||
import {DOM} from 'angular2/src/facade/dom';
|
||||
import {Component} from 'angular2/src/core/annotations/annotations';
|
||||
import {DirectiveMetadata} from 'angular2/src/core/compiler/directive_metadata';
|
||||
|
||||
export function main() {
|
||||
describe('Shadow DOM strategy', () => {
|
||||
var strategy,
|
||||
component = new Component({selector: 'mycmp'}),
|
||||
metadata = new DirectiveMetadata(null, component, null);
|
||||
|
||||
describe('Native', () => {
|
||||
beforeEach(() => {
|
||||
strategy = new NativeShadowDomStrategy();
|
||||
});
|
||||
|
||||
it('should leave the styles in the template', () => {
|
||||
var tpl = DOM.createTemplate('<style>.s1{}</style><div>content</div>');
|
||||
strategy.processTemplate(tpl, metadata);
|
||||
expect(tpl.content).toHaveText('.s1{}content');
|
||||
});
|
||||
|
||||
it('should not modify the content of the template', () => {
|
||||
var html = '<p>content<span></span></p>';
|
||||
var tpl = DOM.createTemplate(html);
|
||||
strategy.processTemplate(tpl, metadata);
|
||||
expect(DOM.getInnerHTML(tpl)).toEqual(html);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Emulated', () => {
|
||||
var root;
|
||||
beforeEach(() => {
|
||||
root = el('<div>');
|
||||
strategy = new EmulatedShadowDomStrategy(root);
|
||||
});
|
||||
|
||||
it('should move the styles from the template to the root', () => {
|
||||
var tpl = DOM.createTemplate('<style>.s1{}</style><div>content</div><style>.s2{}</style>');
|
||||
strategy.processTemplate(tpl, metadata);
|
||||
expect(root).toHaveText('.s1[mycmp] {}.s2[mycmp] {}');
|
||||
expect(tpl.content).toHaveText('content');
|
||||
});
|
||||
|
||||
it('should insert the styles as the first children of the host', () => {
|
||||
DOM.setInnerHTML(root, '<p>root content</p>')
|
||||
var tpl = DOM.createTemplate('<style>.s1{}</style><style>.s2{}</style>');
|
||||
strategy.processTemplate(tpl, metadata);
|
||||
expect(root).toHaveText('.s1[mycmp] {}.s2[mycmp] {}root content');
|
||||
});
|
||||
|
||||
it('should add the component selector to all template children', () => {
|
||||
var html = '<p>content<span></span></p>';
|
||||
var processedHtml = '<p mycmp="">content<span mycmp=""></span></p>';
|
||||
var tpl = DOM.createTemplate(html);
|
||||
strategy.processTemplate(tpl, metadata);
|
||||
expect(DOM.getInnerHTML(tpl)).toEqual(processedHtml);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
Reference in New Issue
Block a user