340 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			340 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
import {
 | 
						|
  AsyncTestCompleter,
 | 
						|
  beforeEach,
 | 
						|
  ddescribe,
 | 
						|
  describe,
 | 
						|
  el,
 | 
						|
  expect,
 | 
						|
  iit,
 | 
						|
  inject,
 | 
						|
  IS_DARTIUM,
 | 
						|
  it,
 | 
						|
} from 'angular2/test_lib';
 | 
						|
 | 
						|
import {DOM} from 'angular2/src/dom/dom_adapter';
 | 
						|
import {List, ListWrapper, Map, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
 | 
						|
import {Type, isBlank, stringify, isPresent, BaseException} from 'angular2/src/facade/lang';
 | 
						|
import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
 | 
						|
 | 
						|
import {DomCompiler} from 'angular2/src/render/dom/compiler/compiler';
 | 
						|
import {
 | 
						|
  ProtoViewDto,
 | 
						|
  ViewDefinition,
 | 
						|
  DirectiveMetadata,
 | 
						|
  ViewType,
 | 
						|
  ViewEncapsulation
 | 
						|
} 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';
 | 
						|
import {SharedStylesHost} from 'angular2/src/render/dom/view/shared_styles_host';
 | 
						|
import {TemplateCloner} from 'angular2/src/render/dom/template_cloner';
 | 
						|
 | 
						|
import {MockStep} from './pipeline_spec';
 | 
						|
 | 
						|
export function runCompilerCommonTests() {
 | 
						|
  describe('DomCompiler', function() {
 | 
						|
    var mockStepFactory: MockStepFactory;
 | 
						|
    var sharedStylesHost: SharedStylesHost;
 | 
						|
 | 
						|
    beforeEach(() => {sharedStylesHost = new SharedStylesHost()});
 | 
						|
 | 
						|
    function createCompiler(processElementClosure = null, processStyleClosure = null,
 | 
						|
                            urlData = null) {
 | 
						|
      if (isBlank(urlData)) {
 | 
						|
        urlData = new Map();
 | 
						|
      }
 | 
						|
      var tplLoader = new FakeViewLoader(urlData);
 | 
						|
      mockStepFactory =
 | 
						|
          new MockStepFactory([new MockStep(processElementClosure, processStyleClosure)]);
 | 
						|
      return new DomCompiler(new ElementSchemaRegistry(), new TemplateCloner(-1), mockStepFactory,
 | 
						|
                             tplLoader, sharedStylesHost);
 | 
						|
    }
 | 
						|
 | 
						|
    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');
 | 
						|
           });
 | 
						|
 | 
						|
           var dirMetadata = DirectiveMetadata.create(
 | 
						|
               {id: 'id', selector: 'custom', type: DirectiveMetadata.COMPONENT_TYPE});
 | 
						|
           compiler.compileHost(dirMetadata)
 | 
						|
               .then((protoView) => {
 | 
						|
                 expect(DOM.tagName(DOM.firstChild(DOM.content(templateRoot(protoView))))
 | 
						|
                            .toLowerCase())
 | 
						|
                     .toEqual('custom');
 | 
						|
                 expect(mockStepFactory.viewDef.directives).toEqual([dirMetadata]);
 | 
						|
                 expect(protoView.variableBindings)
 | 
						|
                     .toEqual(MapWrapper.createFromStringMap({'a': 'b'}));
 | 
						|
                 async.done();
 | 
						|
               });
 | 
						|
         }));
 | 
						|
 | 
						|
      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();
 | 
						|
               });
 | 
						|
         }));
 | 
						|
 | 
						|
      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) => {
 | 
						|
                 expect(DOM.getInnerHTML(templateRoot(protoView))).toEqual('inline component');
 | 
						|
                 async.done();
 | 
						|
               });
 | 
						|
         }));
 | 
						|
 | 
						|
      it('should load url templates', inject([AsyncTestCompleter], (async) => {
 | 
						|
           var urlData = MapWrapper.createFromStringMap({'someUrl': 'url component'});
 | 
						|
           var compiler = createCompiler(EMPTY_STEP, null, urlData);
 | 
						|
           compiler.compile(new ViewDefinition({componentId: 'someId', templateAbsUrl: 'someUrl'}))
 | 
						|
               .then((protoView) => {
 | 
						|
                 expect(DOM.getInnerHTML(templateRoot(protoView))).toEqual('url component');
 | 
						|
                 async.done();
 | 
						|
               });
 | 
						|
         }));
 | 
						|
 | 
						|
      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();
 | 
						|
               });
 | 
						|
         }));
 | 
						|
 | 
						|
      it('should report loading errors', inject([AsyncTestCompleter], (async) => {
 | 
						|
           var compiler = createCompiler(EMPTY_STEP, null, new Map());
 | 
						|
           PromiseWrapper.catchError(
 | 
						|
               compiler.compile(
 | 
						|
                   new ViewDefinition({componentId: 'someId', templateAbsUrl: 'someUrl'})),
 | 
						|
               (e) => {
 | 
						|
                 expect(e.message).toEqual(
 | 
						|
                     'Failed to load the template for "someId" : Failed to fetch url "someUrl"');
 | 
						|
                 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) => {
 | 
						|
                 expect(protoView.type).toEqual(ViewType.COMPONENT);
 | 
						|
                 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) => {
 | 
						|
                 expect(protoView.type).toEqual(ViewType.HOST);
 | 
						|
                 async.done();
 | 
						|
               });
 | 
						|
         }));
 | 
						|
 | 
						|
    });
 | 
						|
 | 
						|
    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) => {
 | 
						|
                 expect(DOM.getInnerHTML(templateRoot(protoViewDto))).toEqual('');
 | 
						|
                 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) => {
 | 
						|
                 expect(DOM.getInnerHTML(templateRoot(protoViewDto))).toEqual('');
 | 
						|
                 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) => {
 | 
						|
                   expect(DOM.getInnerHTML(templateRoot(protoViewDto)))
 | 
						|
                       .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();
 | 
						|
               });
 | 
						|
         }));
 | 
						|
 | 
						|
    });
 | 
						|
 | 
						|
  });
 | 
						|
}
 | 
						|
 | 
						|
function templateRoot(protoViewDto: ProtoViewDto): Element {
 | 
						|
  var pv = resolveInternalDomProtoView(protoViewDto.render);
 | 
						|
  return (<Element>pv.cloneableTemplate);
 | 
						|
}
 | 
						|
 | 
						|
class MockStepFactory extends CompileStepFactory {
 | 
						|
  steps: List<CompileStep>;
 | 
						|
  subTaskPromises: List<Promise<any>>;
 | 
						|
  viewDef: ViewDefinition;
 | 
						|
 | 
						|
  constructor(steps) {
 | 
						|
    super();
 | 
						|
    this.steps = steps;
 | 
						|
  }
 | 
						|
  createSteps(viewDef): List<CompileStep> {
 | 
						|
    this.viewDef = viewDef;
 | 
						|
    return this.steps;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
var EMPTY_STEP = (parent, current, control) => {
 | 
						|
  if (isPresent(parent)) {
 | 
						|
    current.inheritedProtoView = parent.inheritedProtoView;
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
class FakeViewLoader extends ViewLoader {
 | 
						|
  _urlData: Map<string, string>;
 | 
						|
  constructor(urlData) {
 | 
						|
    super(null, null, null);
 | 
						|
    this._urlData = urlData;
 | 
						|
  }
 | 
						|
 | 
						|
  load(viewDef): Promise<any> {
 | 
						|
    var styles = isPresent(viewDef.styles) ? viewDef.styles : [];
 | 
						|
    if (isPresent(viewDef.template)) {
 | 
						|
      return PromiseWrapper.resolve(new TemplateAndStyles(viewDef.template, styles));
 | 
						|
    }
 | 
						|
 | 
						|
    if (isPresent(viewDef.templateAbsUrl)) {
 | 
						|
      var content = this._urlData.get(viewDef.templateAbsUrl);
 | 
						|
      return isPresent(content) ?
 | 
						|
                 PromiseWrapper.resolve(new TemplateAndStyles(content, styles)) :
 | 
						|
                 PromiseWrapper.reject(`Failed to fetch url "${viewDef.templateAbsUrl}"`, null);
 | 
						|
    }
 | 
						|
 | 
						|
    throw new BaseException('View should have either the templateUrl or template property set');
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
var someComponent = DirectiveMetadata.create(
 | 
						|
    {selector: 'some-comp', id: 'someComponent', type: DirectiveMetadata.COMPONENT_TYPE});
 |