refactor(compiler): remove unused code

BREAKING CHANGE:
- Removes `ChangeDetection`, use a binding for `ChangeDetectorGenConfig` instead
  to configure change detection.
- `RenderElementRef.renderBoundElementIndex` was renamed to `RenderElementRef.boundElementIndex`.
- Removes `ViewLoader`, use `XHRImpl` instead.
This commit is contained in:
Tobias Bosch
2015-10-01 20:47:49 -07:00
parent b154f1a44f
commit d21c7bdf90
81 changed files with 140 additions and 7365 deletions
@@ -1,33 +0,0 @@
import {ddescribe, describe, it, iit, xit, expect, beforeEach, afterEach} from 'angular2/test_lib';
import {SpyProtoChangeDetector} from '../spies';
import {
PreGeneratedChangeDetection,
ChangeDetectorDefinition,
DynamicProtoChangeDetector
} from 'angular2/src/core/change_detection/change_detection';
export function main() {
describe("PreGeneratedChangeDetection", () => {
var proto;
var def;
beforeEach(() => {
proto = new SpyProtoChangeDetector();
def = new ChangeDetectorDefinition('id', null, [], [], [], [], null);
});
it("should return a proto change detector when one is available", () => {
var map = {'id': (def) => proto};
var cd = new PreGeneratedChangeDetection(null, map);
expect(cd.getProtoChangeDetector('id', def)).toBe(proto)
});
it("should delegate to dynamic change detection otherwise", () => {
var cd = new PreGeneratedChangeDetection(null, {});
expect(cd.getProtoChangeDetector('id', def)).toBeAnInstanceOf(DynamicProtoChangeDetector);
});
});
}
@@ -1,17 +0,0 @@
library angular2.test.core.compiler.component_url_mapper_spec;
import 'package:angular2/test_lib.dart';
import 'package:angular2/src/core/compiler/component_url_mapper.dart';
main() {
describe("ComponentUrlMapper", () {
it("should return the URL of the component's library", () {
var mapper = new ComponentUrlMapper();
expect(mapper
.getUrl(SomeComponent)
.endsWith("core/compiler/component_url_mapper_spec.dart")).toBeTrue();
});
});
}
class SomeComponent {}
@@ -1,25 +0,0 @@
import {describe, it, expect, beforeEach, ddescribe, iit, xit, el} from 'angular2/test_lib';
import {
ComponentUrlMapper,
RuntimeComponentUrlMapper
} from 'angular2/src/core/compiler/component_url_mapper';
export function main() {
describe('RuntimeComponentUrlMapper', () => {
it('should return the registered URL', () => {
var url = 'http://path/to/component';
var mapper = new RuntimeComponentUrlMapper();
mapper.setComponentUrl(SomeComponent, url);
expect(mapper.getUrl(SomeComponent)).toEqual(url);
});
it('should fallback to ComponentUrlMapper', () => {
var mapper = new ComponentUrlMapper();
var runtimeMapper = new RuntimeComponentUrlMapper();
expect(runtimeMapper.getUrl(SomeComponent)).toEqual(mapper.getUrl(SomeComponent));
});
});
}
class SomeComponent {}
@@ -1,112 +1,102 @@
library angular2.test.core.compiler.directive_lifecycle_spec;
import 'package:angular2/test_lib.dart';
import 'package:angular2/angular2.dart';
import 'package:angular2/src/core/compiler/element_injector.dart';
import 'package:angular2/src/core/compiler/directive_lifecycle_reflector.dart';
import 'package:angular2/src/core/compiler/interfaces.dart';
main() {
describe('Create DirectiveMetadata', () {
describe('lifecycle', () {
metadata(type, annotation) =>
DirectiveBinding.createFromType(type, annotation).metadata;
describe("onChanges", () {
it("should be true when the directive implements OnChanges", () {
expect(metadata(DirectiveImplementingOnChanges, new Directive())
.callOnChanges).toBe(true);
it("should be true when the directive has the onChanges method", () {
expect(hasLifecycleHook(LifecycleHooks.OnChanges, DirectiveImplementingOnChanges))
.toBe(true);
});
it("should be false otherwise", () {
expect(metadata(DirectiveNoHooks, new Directive()).callOnChanges)
.toBe(false);
expect(hasLifecycleHook(LifecycleHooks.OnChanges, DirectiveNoHooks)).toBe(false);
});
});
describe("onDestroy", () {
it("should be true when the directive implements OnDestroy", () {
expect(metadata(DirectiveImplementingOnDestroy, new Directive())
.callOnDestroy).toBe(true);
it("should be true when the directive has the onDestroy method", () {
expect(hasLifecycleHook(LifecycleHooks.OnDestroy, DirectiveImplementingOnDestroy))
.toBe(true);
});
it("should be false otherwise", () {
expect(metadata(DirectiveNoHooks, new Directive()).callOnDestroy)
.toBe(false);
});
});
describe("doCheck", () {
it("should be true when the directive implements DoCheck", () {
expect(metadata(DirectiveImplementingOnCheck, new Directive())
.callDoCheck).toBe(true);
});
it("should be false otherwise", () {
expect(metadata(DirectiveNoHooks, new Directive()).callDoCheck)
.toBe(false);
expect(hasLifecycleHook(LifecycleHooks.OnDestroy, DirectiveNoHooks)).toBe(false);
});
});
describe("onInit", () {
it("should be true when the directive implements OnInit", () {
expect(metadata(DirectiveImplementingOnInit, new Directive())
.callOnInit).toBe(true);
it("should be true when the directive has the onInit method", () {
expect(hasLifecycleHook(LifecycleHooks.OnInit, DirectiveImplementingOnInit))
.toBe(true);
});
it("should be false otherwise", () {
expect(metadata(DirectiveNoHooks, new Directive()).callOnInit)
.toBe(false);
expect(hasLifecycleHook(LifecycleHooks.OnInit, DirectiveNoHooks)).toBe(false);
});
});
describe("doCheck", () {
it("should be true when the directive has the doCheck method", () {
expect(hasLifecycleHook(LifecycleHooks.DoCheck, DirectiveImplementingOnCheck))
.toBe(true);
});
it("should be false otherwise", () {
expect(hasLifecycleHook(LifecycleHooks.DoCheck, DirectiveNoHooks)).toBe(false);
});
});
describe("afterContentInit", () {
it("should be true when the directive implements AfterContentInit", () {
expect(
metadata(DirectiveImplementingAfterContentInit, new Directive())
.callAfterContentInit).toBe(true);
it("should be true when the directive has the afterContentInit method", () {
expect(hasLifecycleHook(LifecycleHooks.AfterContentInit, DirectiveImplementingAfterContentInit))
.toBe(true);
});
it("should be false otherwise", () {
expect(metadata(DirectiveNoHooks, new Directive())
.callAfterContentInit).toBe(false);
expect(hasLifecycleHook(LifecycleHooks.AfterContentInit, DirectiveNoHooks))
.toBe(false);
});
});
describe("afterContentChecked", () {
it("should be true when the directive implements AfterContentChecked", () {
expect(
metadata(DirectiveImplementingAfterContentChecked, new Directive())
.callAfterContentChecked).toBe(true);
it("should be true when the directive has the afterContentChecked method", () {
expect(hasLifecycleHook(LifecycleHooks.AfterContentChecked, DirectiveImplementingAfterContentChecked))
.toBe(true);
});
it("should be false otherwise", () {
expect(metadata(DirectiveNoHooks, new Directive())
.callAfterContentChecked).toBe(false);
expect(hasLifecycleHook(LifecycleHooks.AfterContentChecked, DirectiveNoHooks))
.toBe(false);
});
});
describe("afterViewInit", () {
it("should be true when the directive implements AfterViewInit", () {
expect(
metadata(DirectiveImplementingAfterViewInit, new Directive())
.callAfterViewInit).toBe(true);
it("should be true when the directive has the afterViewInit method", () {
expect(hasLifecycleHook(LifecycleHooks.AfterViewInit, DirectiveImplementingAfterViewInit))
.toBe(true);
});
it("should be false otherwise", () {
expect(metadata(DirectiveNoHooks, new Directive())
.callAfterViewInit).toBe(false);
expect(hasLifecycleHook(LifecycleHooks.AfterViewInit, DirectiveNoHooks)).toBe(false);
});
});
describe("afterViewChecked", () {
it("should be true when the directive implements AfterViewChecked", () {
expect(
metadata(DirectiveImplementingAfterViewChecked, new Directive())
.callAfterViewChecked).toBe(true);
it("should be true when the directive has the afterViewChecked method", () {
expect(hasLifecycleHook(LifecycleHooks.AfterViewChecked, DirectiveImplementingAfterViewChecked))
.toBe(true);
});
it("should be false otherwise", () {
expect(metadata(DirectiveNoHooks, new Directive())
.callAfterViewChecked).toBe(false);
expect(hasLifecycleHook(LifecycleHooks.AfterViewChecked, DirectiveNoHooks))
.toBe(false);
});
});
});
@@ -13,83 +13,76 @@ import {
proxy
} from 'angular2/test_lib';
import {DirectiveMetadata} from 'angular2/src/core/metadata';
import {DirectiveBinding} from 'angular2/src/core/compiler/element_injector';
import {RenderDirectiveMetadata} from 'angular2/src/core/render/api';
import {hasLifecycleHook} from 'angular2/src/core/compiler/directive_lifecycle_reflector';
import {LifecycleHooks} from 'angular2/src/core/compiler/interfaces';
export function main() {
describe('Create DirectiveMetadata', () => {
describe('lifecycle', () => {
function metadata(type, annotation): RenderDirectiveMetadata {
return DirectiveBinding.createFromType(type, annotation).metadata;
}
describe("onChanges", () => {
it("should be true when the directive has the onChanges method", () => {
expect(metadata(DirectiveWithOnChangesMethod, new DirectiveMetadata({})).callOnChanges)
expect(hasLifecycleHook(LifecycleHooks.OnChanges, DirectiveWithOnChangesMethod))
.toBe(true);
});
it("should be false otherwise", () => {
expect(metadata(DirectiveNoHooks, new DirectiveMetadata()).callOnChanges).toBe(false);
expect(hasLifecycleHook(LifecycleHooks.OnChanges, DirectiveNoHooks)).toBe(false);
});
});
describe("onDestroy", () => {
it("should be true when the directive has the onDestroy method", () => {
expect(metadata(DirectiveWithOnDestroyMethod, new DirectiveMetadata({})).callOnDestroy)
expect(hasLifecycleHook(LifecycleHooks.OnDestroy, DirectiveWithOnDestroyMethod))
.toBe(true);
});
it("should be false otherwise", () => {
expect(metadata(DirectiveNoHooks, new DirectiveMetadata()).callOnDestroy).toBe(false);
expect(hasLifecycleHook(LifecycleHooks.OnDestroy, DirectiveNoHooks)).toBe(false);
});
});
describe("onInit", () => {
it("should be true when the directive has the onInit method", () => {
expect(metadata(DirectiveWithOnInitMethod, new DirectiveMetadata({})).callOnInit)
.toBe(true);
expect(hasLifecycleHook(LifecycleHooks.OnInit, DirectiveWithOnInitMethod)).toBe(true);
});
it("should be false otherwise", () => {
expect(metadata(DirectiveNoHooks, new DirectiveMetadata()).callOnInit).toBe(false);
expect(hasLifecycleHook(LifecycleHooks.OnInit, DirectiveNoHooks)).toBe(false);
});
});
describe("doCheck", () => {
it("should be true when the directive has the doCheck method", () => {
expect(metadata(DirectiveWithOnCheckMethod, new DirectiveMetadata({})).callDoCheck)
.toBe(true);
expect(hasLifecycleHook(LifecycleHooks.DoCheck, DirectiveWithOnCheckMethod)).toBe(true);
});
it("should be false otherwise", () => {
expect(metadata(DirectiveNoHooks, new DirectiveMetadata()).callDoCheck).toBe(false);
expect(hasLifecycleHook(LifecycleHooks.DoCheck, DirectiveNoHooks)).toBe(false);
});
});
describe("afterContentInit", () => {
it("should be true when the directive has the afterContentInit method", () => {
expect(metadata(DirectiveWithAfterContentInitMethod, new DirectiveMetadata({}))
.callAfterContentInit)
expect(hasLifecycleHook(LifecycleHooks.AfterContentInit,
DirectiveWithAfterContentInitMethod))
.toBe(true);
});
it("should be false otherwise", () => {
expect(metadata(DirectiveNoHooks, new DirectiveMetadata()).callAfterContentInit)
.toBe(false);
expect(hasLifecycleHook(LifecycleHooks.AfterContentInit, DirectiveNoHooks)).toBe(false);
});
});
describe("afterContentChecked", () => {
it("should be true when the directive has the afterContentChecked method", () => {
expect(metadata(DirectiveWithAfterContentCheckedMethod, new DirectiveMetadata({}))
.callAfterContentChecked)
expect(hasLifecycleHook(LifecycleHooks.AfterContentChecked,
DirectiveWithAfterContentCheckedMethod))
.toBe(true);
});
it("should be false otherwise", () => {
expect(metadata(DirectiveNoHooks, new DirectiveMetadata()).callAfterContentChecked)
expect(hasLifecycleHook(LifecycleHooks.AfterContentChecked, DirectiveNoHooks))
.toBe(false);
});
});
@@ -97,26 +90,24 @@ export function main() {
describe("afterViewInit", () => {
it("should be true when the directive has the afterViewInit method", () => {
expect(metadata(DirectiveWithAfterViewInitMethod, new DirectiveMetadata({}))
.callAfterViewInit)
expect(hasLifecycleHook(LifecycleHooks.AfterViewInit, DirectiveWithAfterViewInitMethod))
.toBe(true);
});
it("should be false otherwise", () => {
expect(metadata(DirectiveNoHooks, new DirectiveMetadata()).callAfterViewInit).toBe(false);
expect(hasLifecycleHook(LifecycleHooks.AfterViewInit, DirectiveNoHooks)).toBe(false);
});
});
describe("afterViewChecked", () => {
it("should be true when the directive has the afterViewChecked method", () => {
expect(metadata(DirectiveWithAfterViewCheckedMethod, new DirectiveMetadata({}))
.callAfterViewChecked)
expect(hasLifecycleHook(LifecycleHooks.AfterViewChecked,
DirectiveWithAfterViewCheckedMethod))
.toBe(true);
});
it("should be false otherwise", () => {
expect(metadata(DirectiveNoHooks, new DirectiveMetadata()).callAfterViewChecked)
.toBe(false);
expect(hasLifecycleHook(LifecycleHooks.AfterViewChecked, DirectiveNoHooks)).toBe(false);
});
});
});
@@ -62,8 +62,6 @@ import {
PipeTransform,
ChangeDetectorRef,
ChangeDetectionStrategy,
ChangeDetection,
DynamicChangeDetection,
ChangeDetectorGenConfig
} from 'angular2/src/core/change_detection/change_detection';
@@ -35,7 +35,6 @@ import {
ViewMetadata
} from 'angular2/core';
import {By} from 'angular2/src/core/debug';
import {MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE} from 'angular2/src/core/render';
export function main() {
describe('projection', () => {
@@ -421,45 +420,29 @@ export function main() {
}));
}
describe('different proto view storages', () => {
function runTests() {
it('should support nested conditionals that contain ng-contents',
inject(
[TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
tcb.overrideView(MainComp, new ViewMetadata({
template: `<conditional-text>a</conditional-text>`,
directives: [ConditionalTextComponent]
}))
.createAsync(MainComp)
.then((main) => {
expect(main.debugElement.nativeElement).toHaveText('MAIN()');
it('should support nested conditionals that contain ng-contents',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
tcb.overrideView(MainComp, new ViewMetadata({
template: `<conditional-text>a</conditional-text>`,
directives: [ConditionalTextComponent]
}))
.createAsync(MainComp)
.then((main) => {
expect(main.debugElement.nativeElement).toHaveText('MAIN()');
var viewportElement =
main.debugElement.componentViewChildren[0].componentViewChildren[0];
viewportElement.inject(ManualViewportDirective).show();
expect(main.debugElement.nativeElement).toHaveText('MAIN(FIRST())');
var viewportElement =
main.debugElement.componentViewChildren[0].componentViewChildren[0];
viewportElement.inject(ManualViewportDirective).show();
expect(main.debugElement.nativeElement).toHaveText('MAIN(FIRST())');
viewportElement =
main.debugElement.componentViewChildren[0].componentViewChildren[1];
viewportElement.inject(ManualViewportDirective).show();
expect(main.debugElement.nativeElement).toHaveText('MAIN(FIRST(SECOND(a)))');
viewportElement =
main.debugElement.componentViewChildren[0].componentViewChildren[1];
viewportElement.inject(ManualViewportDirective).show();
expect(main.debugElement.nativeElement).toHaveText('MAIN(FIRST(SECOND(a)))');
async.done();
});
}));
}
describe('serialize templates', () => {
beforeEachBindings(() => [bind(MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE).toValue(0)]);
runTests();
});
describe("don't serialize templates", () => {
beforeEachBindings(() => [bind(MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE).toValue(-1)]);
runTests();
});
});
async.done();
});
}));
});
}
@@ -11,133 +11,9 @@ import {
it
} from 'angular2/test_lib';
import {SpyChangeDetection} from '../spies';
import {isBlank, stringify} from 'angular2/src/core/facade/lang';
import {
ChangeDetection,
ChangeDetectorDefinition,
BindingRecord,
DirectiveIndex,
Parser
} from 'angular2/src/core/change_detection/change_detection';
import {
BindingRecordsCreator,
getChangeDetectorDefinitions
} from 'angular2/src/core/compiler/proto_view_factory';
import {Component, Directive} from 'angular2/src/core/metadata';
import {Key, Binding} from 'angular2/core';
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
import {DirectiveBinding} from 'angular2/src/core/compiler/element_injector';
import {
RenderElementBinder,
EventBinding,
RenderDirectiveMetadata,
ViewType,
ProtoViewDto,
DirectiveBinder
} from 'angular2/src/core/render/api';
export function main() {
describe('ProtoViewFactory', () => {
var changeDetection;
var directiveResolver;
// TODO
beforeEach(() => {
directiveResolver = new DirectiveResolver();
changeDetection = new SpyChangeDetection();
changeDetection.prop("generateDetectors", true);
});
function bindDirective(type) {
return DirectiveBinding.createFromType(type, directiveResolver.resolve(type));
}
describe('getChangeDetectorDefinitions', () => {
it('should create a ChangeDetectorDefinition for the root render proto view', () => {
var renderPv = createRenderProtoView();
var defs =
getChangeDetectorDefinitions(bindDirective(MainComponent).metadata, renderPv, [], null);
expect(defs.length).toBe(1);
expect(defs[0].id).toEqual(`${stringify(MainComponent)}_comp_0`);
});
});
describe('BindingRecordsCreator', () => {
var creator: BindingRecordsCreator;
beforeEach(() => { creator = new BindingRecordsCreator(); });
describe('getEventBindingRecords', () => {
it("should return template event records", inject([Parser], (p: Parser) => {
var ast1 = p.parseAction("1", null);
var ast2 = p.parseAction("2", null);
var rec = creator.getEventBindingRecords(
[
new RenderElementBinder(
{eventBindings: [new EventBinding("a", ast1)], directives: []}),
new RenderElementBinder(
{eventBindings: [new EventBinding("b", ast2)], directives: []})
],
[]);
expect(rec).toEqual([
BindingRecord.createForEvent(ast1, "a", 0),
BindingRecord.createForEvent(ast2, "b", 1)
]);
}));
it('should return host event records', inject([Parser], (p: Parser) => {
var ast1 = p.parseAction("1", null);
var rec = creator.getEventBindingRecords(
[
new RenderElementBinder({
eventBindings: [],
directives: [
new DirectiveBinder(
{directiveIndex: 0, eventBindings: [new EventBinding("a", ast1)]})
]
})
],
[RenderDirectiveMetadata.create({id: 'some-id'})]);
expect(rec.length).toEqual(1);
expect(rec[0].target.name).toEqual("a");
expect(rec[0].implicitReceiver).toBeAnInstanceOf(DirectiveIndex);
}));
});
});
});
}
function directiveBinding({metadata}: {metadata?: any} = {}) {
return new DirectiveBinding(Key.get("dummy"), null, null, metadata, [], []);
}
function createRenderProtoView(elementBinders = null, type: ViewType = null,
variableBindings = null) {
if (isBlank(type)) {
type = ViewType.COMPONENT;
}
if (isBlank(elementBinders)) {
elementBinders = [];
}
if (isBlank(variableBindings)) {
variableBindings = new Map();
}
return new ProtoViewDto({
elementBinders: elementBinders,
type: type,
variableBindings: variableBindings,
textBindings: [],
transitiveNgContentCount: 0
});
}
@Component({selector: 'main-comp'})
class MainComponent {
});
}
@@ -26,7 +26,6 @@ import {
RenderProtoViewRef,
RenderFragmentRef,
ViewType,
RenderProtoViewMergeMapping,
RenderViewWithFragments
} from 'angular2/src/core/render/api';
import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
@@ -37,11 +37,7 @@ import {
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
import {Component} from 'angular2/src/core/metadata';
import {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils';
import {
RenderProtoViewMergeMapping,
ViewType,
RenderViewWithFragments
} from 'angular2/src/core/render/render';
import {ViewType, RenderViewWithFragments} from 'angular2/src/core/render/render';
export function main() {
// TODO(tbosch): add more tests here!
@@ -1,21 +0,0 @@
import {RenderDirectiveMetadata} from 'angular2/src/core/render/api';
import {MapWrapper} from 'angular2/src/core/facade/collection';
import {ddescribe, describe, expect, it} from 'angular2/test_lib';
export function main() {
describe('Metadata', () => {
describe('host', () => {
it('should parse host configuration', () => {
var md = RenderDirectiveMetadata.create({
host: MapWrapper.createFromPairs(
[['(event)', 'eventVal'], ['[prop]', 'propVal'], ['attr', 'attrVal']])
});
expect(md.hostListeners).toEqual(MapWrapper.createFromPairs([['event', 'eventVal']]));
expect(md.hostProperties).toEqual(MapWrapper.createFromPairs([['prop', 'propVal']]));
expect(md.hostAttributes).toEqual(MapWrapper.createFromPairs([['attr', 'attrVal']]));
});
});
});
}
@@ -1,9 +0,0 @@
/*
* Runs compiler tests using in-browser DOM adapter.
*/
import {runCompilerCommonTests} from './compiler_common_tests';
export function main() {
runCompilerCommonTests();
}
@@ -1,339 +0,0 @@
import {
AsyncTestCompleter,
beforeEach,
ddescribe,
describe,
el,
expect,
iit,
inject,
it,
} from 'angular2/test_lib';
import {DOM} from 'angular2/src/core/dom/dom_adapter';
import {ListWrapper, Map, MapWrapper, StringMapWrapper} from 'angular2/src/core/facade/collection';
import {Type, isBlank, stringify, isPresent} from 'angular2/src/core/facade/lang';
import {BaseException, WrappedException} from 'angular2/src/core/facade/exceptions';
import {PromiseWrapper, Promise} from 'angular2/src/core/facade/async';
import {DomCompiler} from 'angular2/src/core/render/dom/compiler/compiler';
import {
ProtoViewDto,
ViewDefinition,
RenderDirectiveMetadata,
ViewType,
ViewEncapsulation
} from 'angular2/src/core/render/api';
import {CompileStep} from 'angular2/src/core/render/dom/compiler/compile_step';
import {CompileStepFactory} from 'angular2/src/core/render/dom/compiler/compile_step_factory';
import {ElementSchemaRegistry} from 'angular2/src/core/render/dom/schema/element_schema_registry';
import {ViewLoader, TemplateAndStyles} from 'angular2/src/core/render/dom/compiler/view_loader';
import {resolveInternalDomProtoView} from 'angular2/src/core/render/dom/view/proto_view';
import {SharedStylesHost} from 'angular2/src/core/render/dom/view/shared_styles_host';
import {TemplateCloner} from 'angular2/src/core/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 = RenderDirectiveMetadata.create(
{id: 'id', selector: 'custom', type: RenderDirectiveMetadata.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 = RenderDirectiveMetadata.create({
id: 'id',
selector: 'marquee.jazzy[size=huge]',
type: RenderDirectiveMetadata.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: CompileStep[];
subTaskPromises: Array<Promise<any>>;
viewDef: ViewDefinition;
constructor(steps) {
super();
this.steps = steps;
}
createSteps(viewDef): 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 = RenderDirectiveMetadata.create(
{selector: 'some-comp', id: 'someComponent', type: RenderDirectiveMetadata.COMPONENT_TYPE});
@@ -1,11 +0,0 @@
library angular2.compiler.html5lib_dom_adapter.test;
import 'package:angular2/src/core/dom/html_adapter.dart';
import 'package:angular2/src/test_lib/test_lib.dart' show testSetup;
import 'compiler_common_tests.dart';
void main() {
Html5LibDomAdapter.makeCurrent();
testSetup();
runCompilerCommonTests();
}
@@ -1,251 +0,0 @@
import {describe, beforeEach, it, xit, expect, iit, ddescribe, el} from 'angular2/test_lib';
import {isPresent, isBlank, assertionsEnabled} from 'angular2/src/core/facade/lang';
import {ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/core/facade/collection';
import {DOM} from 'angular2/src/core/dom/dom_adapter';
import {DirectiveParser} from 'angular2/src/core/render/dom/compiler/directive_parser';
import {CompilePipeline} from 'angular2/src/core/render/dom/compiler/compile_pipeline';
import {ViewDefinition, RenderDirectiveMetadata, ViewType} from 'angular2/src/core/render/api';
import {Lexer, Parser} from 'angular2/src/core/change_detection/change_detection';
import {ElementBinderBuilder} from 'angular2/src/core/render/dom/view/proto_view_builder';
import {MockStep} from './pipeline_spec';
export function main() {
describe('DirectiveParser', () => {
var parser, annotatedDirectives;
beforeEach(() => {
annotatedDirectives = [
someComponent,
someComponent2,
someDirective,
someDirectiveIgnoringChildren,
decoratorWithMultipleAttrs,
someDirectiveWithProps,
someDirectiveWithHostProperties,
someDirectiveWithInvalidHostProperties,
someDirectiveWithHostAttributes,
someDirectiveWithEvents,
someDirectiveWithGlobalEvents
];
parser = new Parser(new Lexer());
});
function createPipeline(propertyBindings = null, directives = null) {
if (isBlank(directives)) directives = annotatedDirectives;
return new CompilePipeline([
new MockStep((parent, current, control) => {
if (isPresent(propertyBindings)) {
StringMapWrapper.forEach(propertyBindings, (ast, name) => {
current.bindElement().bindProperty(name, ast);
});
}
}),
new DirectiveParser(parser, directives)
]);
}
function createViewDefinition(): ViewDefinition {
return new ViewDefinition({componentId: 'someComponent'});
}
function process(el, propertyBindings = null, directives = null): ElementBinderBuilder[] {
var pipeline = createPipeline(propertyBindings, directives);
return ListWrapper.map(
pipeline.processElements(el, ViewType.COMPONENT, createViewDefinition()),
(ce) => ce.inheritedElementBinder);
}
it('should not add directives if they are not used', () => {
var results = process(el('<div></div>'));
expect(results[0]).toBe(null);
});
it('should detect directives in attributes', () => {
var results = process(el('<div some-decor></div>'));
expect(results[0].directives[0].directiveIndex)
.toBe(annotatedDirectives.indexOf(someDirective));
});
it('should detect directives with multiple attributes', () => {
var results = process(el('<input type=text control=one></input>'));
expect(results[0].directives[0].directiveIndex)
.toBe(annotatedDirectives.indexOf(decoratorWithMultipleAttrs));
});
it('should compile children by default', () => {
var results = createPipeline().processElements(el('<div some-decor></div>'),
ViewType.COMPONENT, createViewDefinition());
expect(results[0].compileChildren).toEqual(true);
});
it('should stop compiling children when specified in the directive config', () => {
var results = createPipeline().processElements(el('<div some-decor-ignoring-children></div>'),
ViewType.COMPONENT, createViewDefinition());
expect(results[0].compileChildren).toEqual(false);
});
it('should bind directive properties from bound properties', () => {
var results = process(el('<div some-decor-props></div>'),
{'elProp': parser.parseBinding('someExpr', '')});
var directiveBinding = results[0].directives[0];
expect(directiveBinding.propertyBindings.get('dirProp').source).toEqual('someExpr');
});
it('should bind directive properties from attribute values', () => {
var results = process(el('<div some-decor-props el-prop="someValue"></div>'));
var directiveBinding = results[0].directives[0];
var simpleProp = directiveBinding.propertyBindings.get('dirProp');
expect(simpleProp.source).toEqual('someValue');
});
it('should bind host directive properties', () => {
var element = el('<input some-decor-with-host-props>');
var results = process(element);
var directiveBinding = results[0].directives[0];
var ast = directiveBinding.hostPropertyBindings.get('hostProp');
expect(ast.source).toEqual('dirProp');
});
it('should throw when parsing invalid host properties', () => {
expect(() => process(el('<input some-decor-with-invalid-host-props>')))
.toThrowError(
new RegExp('Simple binding expression can only contain field access and constants'));
});
it('should set host element attributes', () => {
var element = el('<input some-decor-with-host-attrs>');
var results = process(element);
expect(DOM.getAttribute(results[0].element, 'attr_name')).toEqual('attr_val');
});
it('should not set host element attribute if an attribute already exists', () => {
var element = el('<input attr_name="initial" some-decor-with-host-attrs>');
var results = process(element);
expect(DOM.getAttribute(results[0].element, 'attr_name')).toEqual('initial');
DOM.removeAttribute(element, 'attr_name');
results = process(element);
expect(DOM.getAttribute(results[0].element, 'attr_name')).toEqual('attr_val');
});
it('should add CSS classes if "class" specified in host element attributes', () => {
var element = el('<input class="foo baz" some-decor-with-host-attrs>');
var results = process(element);
expect(DOM.hasClass(results[0].element, 'foo')).toBeTruthy();
expect(DOM.hasClass(results[0].element, 'bar')).toBeTruthy();
expect(DOM.hasClass(results[0].element, 'baz')).toBeTruthy();
});
it('should read attribute values', () => {
var element = el('<input some-decor-props some-attr="someValue">');
var results = process(element);
expect(results[0].readAttributes.get('some-attr')).toEqual('someValue');
});
it('should bind directive events', () => {
var results = process(el('<div some-decor-events></div>'));
var directiveBinding = results[0].directives[0];
expect(directiveBinding.eventBindings.length).toEqual(1);
var eventBinding = directiveBinding.eventBindings[0];
expect(eventBinding.fullName).toEqual('click');
expect(eventBinding.source.source).toEqual('doIt()');
});
it('should bind directive global events', () => {
var results = process(el('<div some-decor-globalevents></div>'));
var directiveBinding = results[0].directives[0];
expect(directiveBinding.eventBindings.length).toEqual(1);
var eventBinding = directiveBinding.eventBindings[0];
expect(eventBinding.fullName).toEqual('window:resize');
expect(eventBinding.source.source).toEqual('doItGlobal()');
});
// TODO: assertions should be enabled when running tests:
// https://github.com/angular/angular/issues/1340
describe('component directives', () => {
it('should save the component id', () => {
var results = process(el('<some-comp></some-comp>'));
expect(results[0].componentId).toEqual('someComponent');
});
it('should not allow multiple component directives on the same element', () => {
expect(() => {
process(el('<some-comp></some-comp>'), null, [someComponent, someComponentDup]);
}).toThrowError(new RegExp('Only one component directive is allowed per element'));
});
it('should sort the directives and store the component as the first directive', () => {
var results = process(el('<some-comp some-decor></some-comp>'));
expect(annotatedDirectives[results[0].directives[0].directiveIndex].id)
.toEqual('someComponent');
expect(annotatedDirectives[results[0].directives[1].directiveIndex].id)
.toEqual('someDirective');
});
});
});
}
var someComponent = RenderDirectiveMetadata.create(
{selector: 'some-comp', id: 'someComponent', type: RenderDirectiveMetadata.COMPONENT_TYPE});
var someComponentDup = RenderDirectiveMetadata.create(
{selector: 'some-comp', id: 'someComponentDup', type: RenderDirectiveMetadata.COMPONENT_TYPE});
var someComponent2 = RenderDirectiveMetadata.create(
{selector: 'some-comp2', id: 'someComponent2', type: RenderDirectiveMetadata.COMPONENT_TYPE});
var someDirective = RenderDirectiveMetadata.create(
{selector: '[some-decor]', id: 'someDirective', type: RenderDirectiveMetadata.DIRECTIVE_TYPE});
var someDirectiveIgnoringChildren = RenderDirectiveMetadata.create({
selector: '[some-decor-ignoring-children]',
compileChildren: false,
type: RenderDirectiveMetadata.DIRECTIVE_TYPE
});
var decoratorWithMultipleAttrs = RenderDirectiveMetadata.create({
selector: 'input[type=text][control]',
id: 'decoratorWithMultipleAttrs',
type: RenderDirectiveMetadata.DIRECTIVE_TYPE
});
var someDirectiveWithProps = RenderDirectiveMetadata.create(
{selector: '[some-decor-props]', inputs: ['dirProp: elProp'], readAttributes: ['some-attr']});
var someDirectiveWithHostProperties = RenderDirectiveMetadata.create({
selector: '[some-decor-with-host-props]',
host: MapWrapper.createFromStringMap<string>({'[hostProp]': 'dirProp'})
});
var someDirectiveWithInvalidHostProperties = RenderDirectiveMetadata.create({
selector: '[some-decor-with-invalid-host-props]',
host: MapWrapper.createFromStringMap<string>({'[hostProp]': 'dirProp + dirProp2'})
});
var someDirectiveWithHostAttributes = RenderDirectiveMetadata.create({
selector: '[some-decor-with-host-attrs]',
host: MapWrapper.createFromStringMap<string>({'attr_name': 'attr_val', 'class': 'foo bar'})
});
var someDirectiveWithEvents = RenderDirectiveMetadata.create({
selector: '[some-decor-events]',
host: MapWrapper.createFromStringMap<string>({'(click)': 'doIt()'})
});
var someDirectiveWithGlobalEvents = RenderDirectiveMetadata.create({
selector: '[some-decor-globalevents]',
host: MapWrapper.createFromStringMap<string>({'(window:resize)': 'doItGlobal()'})
});
var componentWithNonElementSelector = RenderDirectiveMetadata.create({
id: 'componentWithNonElementSelector',
selector: '[attr]',
type: RenderDirectiveMetadata.COMPONENT_TYPE
});
@@ -1,287 +0,0 @@
import {describe, beforeEach, it, expect, iit, ddescribe, el} from 'angular2/test_lib';
import {ListWrapper, MapWrapper} from 'angular2/src/core/facade/collection';
import {DOM} from 'angular2/src/core/dom/dom_adapter';
import {isPresent, NumberWrapper, StringWrapper} from 'angular2/src/core/facade/lang';
import {CompilePipeline} from 'angular2/src/core/render/dom/compiler/compile_pipeline';
import {CompileElement} from 'angular2/src/core/render/dom/compiler/compile_element';
import {CompileStep} from 'angular2/src/core/render/dom/compiler/compile_step';
import {CompileControl} from 'angular2/src/core/render/dom/compiler/compile_control';
import {ProtoViewBuilder} from 'angular2/src/core/render/dom/view/proto_view_builder';
import {
ProtoViewDto,
ViewType,
ViewEncapsulation,
ViewDefinition
} from 'angular2/src/core/render/api';
export function main() {
describe('compile_pipeline', () => {
function createViewDefinition(): ViewDefinition {
return new ViewDefinition({componentId: 'someComponent'});
}
describe('children compilation', () => {
it('should walk the tree in depth first order including template contents', () => {
var element = el('<div id="1"><template id="2"><span id="3"></span></template></div>');
var step0Log = [];
var results = new CompilePipeline([createLoggerStep(step0Log)])
.processElements(element, ViewType.COMPONENT, createViewDefinition());
expect(step0Log).toEqual(['1', '1<2', '2<3']);
expect(resultIdLog(results)).toEqual(['1', '2', '3']);
});
it('should stop walking the tree when compileChildren is false', () => {
var element = el(
'<div id="1"><template id="2" ignore-children><span id="3"></span></template></div>');
var step0Log = [];
var pipeline = new CompilePipeline([new IgnoreChildrenStep(), createLoggerStep(step0Log)]);
var results = pipeline.processElements(element, ViewType.COMPONENT, createViewDefinition());
expect(step0Log).toEqual(['1', '1<2']);
expect(resultIdLog(results)).toEqual(['1', '2']);
});
});
it('should inherit protoViewBuilders to children', () => {
var element = el('<div><div><span viewroot><span></span></span></div></div>');
var pipeline = new CompilePipeline([
new MockStep((parent, current, control) => {
if (isPresent(DOM.getAttribute(current.element, 'viewroot'))) {
current.inheritedProtoView =
new ProtoViewBuilder(current.element, ViewType.EMBEDDED, ViewEncapsulation.None);
}
})
]);
var results = pipeline.processElements(element, ViewType.COMPONENT, createViewDefinition());
expect(results[0].inheritedProtoView).toBe(results[1].inheritedProtoView);
expect(results[2].inheritedProtoView).toBe(results[3].inheritedProtoView);
});
it('should inherit elementBinderBuilders to children', () => {
var element = el('<div bind><div><span bind><span></span></span></div></div>');
var pipeline = new CompilePipeline([
new MockStep((parent, current, control) => {
if (isPresent(DOM.getAttribute(current.element, 'bind'))) {
current.bindElement();
}
})
]);
var results = pipeline.processElements(element, ViewType.COMPONENT, createViewDefinition());
expect(results[0].inheritedElementBinder).toBe(results[1].inheritedElementBinder);
expect(results[2].inheritedElementBinder).toBe(results[3].inheritedElementBinder);
});
it('should mark root elements as viewRoot', () => {
var rootElement = el('<div></div>');
var results = new CompilePipeline([])
.processElements(rootElement, ViewType.COMPONENT, createViewDefinition());
expect(results[0].isViewRoot).toBe(true);
});
it('should calculate distanceToParent / parent correctly', () => {
var element = el('<div bind><div bind></div><div><div bind></div></div></div>');
var pipeline = new CompilePipeline([
new MockStep((parent, current, control) => {
if (isPresent(DOM.getAttribute(current.element, 'bind'))) {
current.bindElement();
}
})
]);
var results = pipeline.processElements(element, ViewType.COMPONENT, createViewDefinition());
expect(results[0].inheritedElementBinder.distanceToParent).toBe(0);
expect(results[1].inheritedElementBinder.distanceToParent).toBe(1);
expect(results[3].inheritedElementBinder.distanceToParent).toBe(2);
expect(results[1].inheritedElementBinder.parent).toBe(results[0].inheritedElementBinder);
expect(results[3].inheritedElementBinder.parent).toBe(results[0].inheritedElementBinder);
});
it('should not execute further steps when ignoreCurrentElement has been called', () => {
var element = el('<div id="1"><span id="2" ignore-current></span><span id="3"></span></div>');
var logs = [];
var pipeline = new CompilePipeline([
new IgnoreCurrentElementStep(),
createLoggerStep(logs),
]);
var results = pipeline.processElements(element, ViewType.COMPONENT, createViewDefinition());
expect(results.length).toBe(2);
expect(logs).toEqual(['1', '1<3'])
});
describe('control.addParent', () => {
it('should report the new parent to the following processor and the result', () => {
var element = el('<div id="1"><span wrap0="1" id="2"><b id="3"></b></span></div>');
var step0Log = [];
var step1Log = [];
var pipeline =
new CompilePipeline([createWrapperStep('wrap0', step0Log), createLoggerStep(step1Log)]);
var result = pipeline.processElements(element, ViewType.COMPONENT, createViewDefinition());
expect(step0Log).toEqual(['1', '1<2', '2<3']);
expect(step1Log).toEqual(['1', '1<wrap0#0', 'wrap0#0<2', '2<3']);
expect(resultIdLog(result)).toEqual(['1', 'wrap0#0', '2', '3']);
});
it('should allow to add a parent by multiple processors to the same element', () => {
var element =
el('<div id="1"><span wrap0="1" wrap1="1" id="2"><b id="3"></b></span></div>');
var step0Log = [];
var step1Log = [];
var step2Log = [];
var pipeline = new CompilePipeline([
createWrapperStep('wrap0', step0Log),
createWrapperStep('wrap1', step1Log),
createLoggerStep(step2Log)
]);
var result = pipeline.processElements(element, ViewType.COMPONENT, createViewDefinition());
expect(step0Log).toEqual(['1', '1<2', '2<3']);
expect(step1Log).toEqual(['1', '1<wrap0#0', 'wrap0#0<2', '2<3']);
expect(step2Log).toEqual(['1', '1<wrap0#0', 'wrap0#0<wrap1#0', 'wrap1#0<2', '2<3']);
expect(resultIdLog(result)).toEqual(['1', 'wrap0#0', 'wrap1#0', '2', '3']);
});
it('should allow to add a parent by multiple processors to different elements', () => {
var element =
el('<div id="1"><span wrap0="1" id="2"><b id="3" wrap1="1"></b></span></div>');
var step0Log = [];
var step1Log = [];
var step2Log = [];
var pipeline = new CompilePipeline([
createWrapperStep('wrap0', step0Log),
createWrapperStep('wrap1', step1Log),
createLoggerStep(step2Log)
]);
var result = pipeline.processElements(element, ViewType.COMPONENT, createViewDefinition());
expect(step0Log).toEqual(['1', '1<2', '2<3']);
expect(step1Log).toEqual(['1', '1<wrap0#0', 'wrap0#0<2', '2<3']);
expect(step2Log).toEqual(['1', '1<wrap0#0', 'wrap0#0<2', '2<wrap1#0', 'wrap1#0<3']);
expect(resultIdLog(result)).toEqual(['1', 'wrap0#0', '2', 'wrap1#0', '3']);
});
it('should allow to add multiple parents by the same processor', () => {
var element = el('<div id="1"><span wrap0="2" id="2"><b id="3"></b></span></div>');
var step0Log = [];
var step1Log = [];
var pipeline =
new CompilePipeline([createWrapperStep('wrap0', step0Log), createLoggerStep(step1Log)]);
var result = pipeline.processElements(element, ViewType.COMPONENT, createViewDefinition());
expect(step0Log).toEqual(['1', '1<2', '2<3']);
expect(step1Log).toEqual(['1', '1<wrap0#0', 'wrap0#0<wrap0#1', 'wrap0#1<2', '2<3']);
expect(resultIdLog(result)).toEqual(['1', 'wrap0#0', 'wrap0#1', '2', '3']);
});
});
describe('control.addChild', () => {
it('should report the new child to all processors and the result', () => {
var element = el('<div id="1"><div id="2"></div></div>');
var resultLog = [];
var newChild = new CompileElement(el('<div id="3"></div>'));
var pipeline = new CompilePipeline([
new MockStep((parent, current, control) => {
if (StringWrapper.equals(DOM.getAttribute(current.element, 'id'), '1')) {
control.addChild(newChild);
}
}),
createLoggerStep(resultLog)
]);
var result = pipeline.processElements(element, ViewType.COMPONENT, createViewDefinition());
expect(result[2]).toBe(newChild);
expect(resultLog).toEqual(['1', '1<2', '1<3']);
expect(resultIdLog(result)).toEqual(['1', '2', '3']);
});
});
describe('processStyles', () => {
it('should call the steps for every style', () => {
var stepCalls = [];
var pipeline = new CompilePipeline([
new MockStep(null,
(style) => {
stepCalls.push(style);
return style;
})
]);
var result = pipeline.processStyles(['a', 'b']);
expect(result[0]).toEqual('a');
expect(result[1]).toEqual('b');
expect(result).toEqual(stepCalls);
});
});
});
}
export class MockStep implements CompileStep {
constructor(private _processElementClosure: Function,
private _processStyleClosure: Function = null) {}
processElement(parent: CompileElement, current: CompileElement, control: CompileControl) {
if (isPresent(this._processElementClosure)) {
this._processElementClosure(parent, current, control);
}
}
processStyle(style: string): string {
if (isPresent(this._processStyleClosure)) {
return this._processStyleClosure(style);
} else {
return style;
}
}
}
export class IgnoreChildrenStep implements CompileStep {
processElement(parent: CompileElement, current: CompileElement, control: CompileControl) {
var attributeMap = DOM.attributeMap(current.element);
if (attributeMap.has('ignore-children')) {
current.compileChildren = false;
}
}
processStyle(style: string): string { return style; }
}
class IgnoreCurrentElementStep implements CompileStep {
processElement(parent: CompileElement, current: CompileElement, control: CompileControl) {
var attributeMap = DOM.attributeMap(current.element);
if (attributeMap.has('ignore-current')) {
control.ignoreCurrentElement();
}
}
processStyle(style: string): string { return style; }
}
function logEntry(log: string[], parent, current) {
var parentId = '';
if (isPresent(parent)) {
parentId = DOM.getAttribute(parent.element, 'id') + '<';
}
log.push(parentId + DOM.getAttribute(current.element, 'id'));
}
function createLoggerStep(log: string[]) {
return new MockStep((parent, current, control) => { logEntry(log, parent, current); });
}
function createWrapperStep(wrapperId, log) {
var nextElementId = 0;
return new MockStep((parent, current, control) => {
var parentCountStr = DOM.getAttribute(current.element, wrapperId);
if (isPresent(parentCountStr)) {
var parentCount = NumberWrapper.parseInt(parentCountStr, 10);
while (parentCount > 0) {
control.addParent(new CompileElement(el(`<a id="${wrapperId}#${nextElementId++}"></a>`)));
parentCount--;
}
}
logEntry(log, parent, current);
});
}
function resultIdLog(result) {
var idLog = [];
ListWrapper.forEach(result, (current) => { logEntry(idLog, null, current); });
return idLog;
}
@@ -1,230 +0,0 @@
import {describe, beforeEach, it, expect, iit, ddescribe, el} from 'angular2/test_lib';
import {PropertyBindingParser} from 'angular2/src/core/render/dom/compiler/property_binding_parser';
import {CompilePipeline} from 'angular2/src/core/render/dom/compiler/compile_pipeline';
import {MapWrapper, ListWrapper} from 'angular2/src/core/facade/collection';
import {Lexer, Parser} from 'angular2/src/core/change_detection/change_detection';
import {ElementBinderBuilder} from 'angular2/src/core/render/dom/view/proto_view_builder';
import {ViewDefinition, ViewType} from 'angular2/src/core/render/api';
import {MockStep} from './pipeline_spec';
var EMPTY_MAP = new Map();
export function main() {
describe('PropertyBindingParser', () => {
function createPipeline(hasNestedProtoView = false) {
return new CompilePipeline([
new MockStep((parent, current, control) => {
if (hasNestedProtoView) {
current.bindElement().bindNestedProtoView(el('<template></template>'));
}
}),
new PropertyBindingParser(new Parser(new Lexer()))
]);
}
function createViewDefinition(): ViewDefinition {
return new ViewDefinition({componentId: 'someComponent'});
}
function process(element, hasNestedProtoView = false): ElementBinderBuilder[] {
return ListWrapper.map(
createPipeline(hasNestedProtoView)
.processElements(element, ViewType.COMPONENT, createViewDefinition()),
(compileElement) => compileElement.inheritedElementBinder);
}
it('should detect [] syntax', () => {
var results = process(el('<div [a]="b"></div>'));
expect(results[0].propertyBindings.get('a').source).toEqual('b');
});
it('should detect [] syntax with data- prefix', () => {
var results = process(el('<div data-[a]="b"></div>'));
expect(results[0].propertyBindings.get('a').source).toEqual('b');
});
it('should detect [] syntax only if an attribute name starts and ends with []', () => {
expect(process(el('<div z[a]="b"></div>'))[0]).toBe(null);
expect(process(el('<div [a]v="b"></div>'))[0]).toBe(null);
});
it('should throw when [] binding contains interpolation', () => {
expect(() => process(el('<div [a]="a + {{b()}}"></div>'))[0])
.toThrowErrorWith(
'Got interpolation ({{}}) where expression was expected at column 4 in [a + {{b()}}] in someComponent');
});
it('should detect bind- syntax', () => {
var results = process(el('<div bind-a="b"></div>'));
expect(results[0].propertyBindings.get('a').source).toEqual('b');
});
it('should detect bind- syntax with data- prefix', () => {
var results = process(el('<div data-bind-a="b"></div>'));
expect(results[0].propertyBindings.get('a').source).toEqual('b');
});
it('should detect bind- syntax only if an attribute name starts with bind',
() => { expect(process(el('<div _bind-a="b"></div>'))[0]).toEqual(null); });
it('should detect interpolation syntax', () => {
var results = process(el('<div a="{{b}}"></div>'));
expect(results[0].propertyBindings.get('a').source).toEqual('{{b}}');
});
it('should detect interpolation syntax with data- prefix', () => {
var results = process(el('<div data-a="{{b}}"></div>'));
expect(results[0].propertyBindings.get('a').source).toEqual('{{b}}');
});
it('should store property setters as camel case', () => {
var element = el('<div bind-some-prop="1">');
var results = process(element);
expect(results[0].propertyBindings.get('someProp')).toBeTruthy();
});
it('should detect var- syntax', () => {
var results = process(el('<template var-a="b"></template>'));
expect(results[0].variableBindings.get('b')).toEqual('a');
});
it('should detect var- syntax with data- prefix', () => {
var results = process(el('<template data-var-a="b"></template>'));
expect(results[0].variableBindings.get('b')).toEqual('a');
});
it('should store variable binding for a template element on the nestedProtoView', () => {
var results = process(el('<template var-george="washington"></p>'), true);
expect(results[0].variableBindings).toEqual(EMPTY_MAP);
expect(results[0].nestedProtoView.variableBindings.get('washington')).toEqual('george');
});
it('should store variable binding for a non-template element using shorthand syntax on the nestedProtoView',
() => {
var results = process(el('<template #george="washington"></template>'), true);
expect(results[0].variableBindings).toEqual(EMPTY_MAP);
expect(results[0].nestedProtoView.variableBindings.get('washington')).toEqual('george');
});
it('should store variable binding for a non-template element', () => {
var results = process(el('<p var-george="washington"></p>'));
expect(results[0].variableBindings.get('washington')).toEqual('george');
});
it('should store variable binding for a non-template element using shorthand syntax', () => {
var results = process(el('<p #george="washington"></p>'));
expect(results[0].variableBindings.get('washington')).toEqual('george');
});
it('should store a variable binding with an implicit value', () => {
var results = process(el('<p var-george></p>'));
expect(results[0].variableBindings.get('\$implicit')).toEqual('george');
});
it('should store a variable binding with an implicit value using shorthand syntax', () => {
var results = process(el('<p #george></p>'));
expect(results[0].variableBindings.get('\$implicit')).toEqual('george');
});
it('should detect variable bindings only if an attribute name starts with #', () => {
var results = process(el('<p b#george></p>'));
expect(results[0]).toEqual(null);
});
it('should detect () syntax', () => {
var results = process(el('<div (click)="b()"></div>'));
var eventBinding = results[0].eventBindings[0];
expect(eventBinding.source.source).toEqual('b()');
expect(eventBinding.fullName).toEqual('click');
// "(click[])" is not an expected syntax and is only used to validate the regexp
results = process(el('<div (click[])="b()"></div>'));
eventBinding = results[0].eventBindings[0];
expect(eventBinding.source.source).toEqual('b()');
expect(eventBinding.fullName).toEqual('click[]');
});
it('should detect () syntax with data- prefix', () => {
var results = process(el('<div data-(click)="b()"></div>'));
var eventBinding = results[0].eventBindings[0];
expect(eventBinding.source.source).toEqual('b()');
expect(eventBinding.fullName).toEqual('click');
});
it('should detect () syntax only if an attribute name starts and ends with ()', () => {
expect(process(el('<div z(a)="b()"></div>'))[0]).toEqual(null);
expect(process(el('<div (a)v="b()"></div>'))[0]).toEqual(null);
});
it('should parse event handlers using () syntax as actions', () => {
var results = process(el('<div (click)="foo=bar"></div>'));
var eventBinding = results[0].eventBindings[0];
expect(eventBinding.source.source).toEqual('foo=bar');
expect(eventBinding.fullName).toEqual('click');
});
it('should throw when () action contains interpolation', () => {
expect(() => process(el('<div (a)="{{b()}}"></div>'))[0])
.toThrowErrorWith(
'Got interpolation ({{}}) where expression was expected at column 0 in [{{b()}}] in someComponent');
});
it('should detect on- syntax', () => {
var results = process(el('<div on-click="b()"></div>'));
var eventBinding = results[0].eventBindings[0];
expect(eventBinding.source.source).toEqual('b()');
expect(eventBinding.fullName).toEqual('click');
});
it('should detect on- syntax with data- prefix', () => {
var results = process(el('<div data-on-click="b()"></div>'));
var eventBinding = results[0].eventBindings[0];
expect(eventBinding.source.source).toEqual('b()');
expect(eventBinding.fullName).toEqual('click');
});
it('should parse event handlers using on- syntax as actions', () => {
var results = process(el('<div on-click="foo=bar"></div>'));
var eventBinding = results[0].eventBindings[0];
expect(eventBinding.source.source).toEqual('foo=bar');
expect(eventBinding.fullName).toEqual('click');
});
it('should store bound properties as temporal attributes', () => {
var results = createPipeline().processElements(el('<div bind-a="b" [c]="d"></div>'),
ViewType.COMPONENT, createViewDefinition());
expect(results[0].attrs().get('a')).toEqual('b');
expect(results[0].attrs().get('c')).toEqual('d');
});
it('should store variables as temporal attributes', () => {
var results = createPipeline().processElements(el('<div var-a="b" #c="d"></div>'),
ViewType.COMPONENT, createViewDefinition());
expect(results[0].attrs().get('a')).toEqual('b');
expect(results[0].attrs().get('c')).toEqual('d');
});
it('should detect [()] syntax', () => {
var results = process(el('<div [(a)]="b"></div>'));
expect(results[0].propertyBindings.get('a').source).toEqual('b');
expect(results[0].eventBindings[0].source.source).toEqual('b=$event');
});
it('should detect [()] syntax with data- prefix', () => {
var results = process(el('<div data-[(a)]="b"></div>'));
expect(results[0].propertyBindings.get('a').source).toEqual('b');
expect(results[0].eventBindings[0].source.source).toEqual('b=$event');
});
it('should detect bindon- syntax', () => {
var results = process(el('<div bindon-a="b"></div>'));
expect(results[0].propertyBindings.get('a').source).toEqual('b');
expect(results[0].eventBindings[0].source.source).toEqual('b=$event');
});
it('should detect bindon- syntax with data- prefix', () => {
var results = process(el('<div data-bindon-a="b"></div>'));
expect(results[0].propertyBindings.get('a').source).toEqual('b');
expect(results[0].eventBindings[0].source.source).toEqual('b=$event');
});
});
}
@@ -1,135 +0,0 @@
import {
AsyncTestCompleter,
beforeEach,
ddescribe,
describe,
el,
expect,
iit,
inject,
it,
xit,
SpyObject,
} from 'angular2/test_lib';
import {DOM} from 'angular2/src/core/dom/dom_adapter';
import {CompilePipeline} from 'angular2/src/core/render/dom/compiler/compile_pipeline';
import {MapWrapper, ListWrapper} from 'angular2/src/core/facade/collection';
import {
ProtoViewBuilder,
ElementBinderBuilder
} from 'angular2/src/core/render/dom/view/proto_view_builder';
import {ViewDefinition, ViewType, ViewEncapsulation} from 'angular2/src/core/render/api';
import {StyleEncapsulator} from 'angular2/src/core/render/dom/compiler/style_encapsulator';
import {MockStep} from './pipeline_spec';
export function main() {
describe('StyleEncapsulator', () => {
var componentIdCache;
beforeEach(() => { componentIdCache = new Map(); });
function createPipeline(viewDef: ViewDefinition) {
return new CompilePipeline([
new MockStep((parent, current, control) => {
var tagName = DOM.tagName(current.element).toLowerCase();
if (tagName.startsWith('comp-')) {
current.bindElement().setComponentId(tagName);
}
}),
new StyleEncapsulator('someapp', viewDef, componentIdCache)
]);
}
function createViewDefinition(encapsulation: ViewEncapsulation, componentId: string):
ViewDefinition {
return new ViewDefinition({encapsulation: encapsulation, componentId: componentId});
}
function processStyles(encapsulation: ViewEncapsulation, componentId: string, styles: string[]):
string[] {
var viewDef = createViewDefinition(encapsulation, componentId);
return createPipeline(viewDef).processStyles(styles);
}
function processElements(encapsulation: ViewEncapsulation, componentId: string,
template: Element, viewType: ViewType = ViewType.COMPONENT):
ProtoViewBuilder {
var viewDef = createViewDefinition(encapsulation, componentId);
var compileElements = createPipeline(viewDef).processElements(template, viewType, viewDef);
return compileElements[0].inheritedProtoView;
}
describe('ViewEncapsulation.None', () => {
it('should not change the styles', () => {
var cs = processStyles(ViewEncapsulation.None, 'someComponent', ['.one {}']);
expect(cs[0]).toEqual('.one {}');
});
});
describe('ViewEncapsulation.Native', () => {
it('should not change the styles', () => {
var cs = processStyles(ViewEncapsulation.Native, 'someComponent', ['.one {}']);
expect(cs[0]).toEqual('.one {}');
});
});
describe('ViewEncapsulation.Emulated', () => {
it('should scope styles', () => {
var cs = processStyles(ViewEncapsulation.Emulated, 'someComponent', ['.foo {} :host {}']);
expect(cs[0]).toEqual(".foo[_ngcontent-someapp-0] {\n\n}\n\n[_nghost-someapp-0] {\n\n}");
});
it('should return the same style given the same component', () => {
var style = '.foo {} :host {}';
var cs1 = processStyles(ViewEncapsulation.Emulated, 'someComponent', [style]);
var cs2 = processStyles(ViewEncapsulation.Emulated, 'someComponent', [style]);
expect(cs1[0]).toEqual(cs2[0]);
});
it('should return different styles given different components', () => {
var style = '.foo {} :host {}';
var cs1 = processStyles(ViewEncapsulation.Emulated, 'someComponent1', [style]);
var cs2 = processStyles(ViewEncapsulation.Emulated, 'someComponent2', [style]);
expect(cs1[0]).not.toEqual(cs2[0]);
});
it('should add a host attribute to component proto views', () => {
var template = DOM.createTemplate('<div></div>');
var protoViewBuilder =
processElements(ViewEncapsulation.Emulated, 'someComponent', template);
expect(protoViewBuilder.hostAttributes.get('_nghost-someapp-0')).toEqual('');
});
it('should not add a host attribute to embedded proto views', () => {
var template = DOM.createTemplate('<div></div>');
var protoViewBuilder = processElements(ViewEncapsulation.Emulated, 'someComponent',
template, ViewType.EMBEDDED);
expect(protoViewBuilder.hostAttributes.size).toBe(0);
});
it('should not add a host attribute to host proto views', () => {
var template = DOM.createTemplate('<div></div>');
var protoViewBuilder =
processElements(ViewEncapsulation.Emulated, 'someComponent', template, ViewType.HOST);
expect(protoViewBuilder.hostAttributes.size).toBe(0);
});
it('should add an attribute to the content elements', () => {
var template = DOM.createTemplate('<div></div>');
processElements(ViewEncapsulation.Emulated, 'someComponent', template);
expect(DOM.getInnerHTML(template)).toEqual('<div _ngcontent-someapp-0=""></div>');
});
it('should not add an attribute to the content elements for host views', () => {
var template = DOM.createTemplate('<div></div>');
processElements(ViewEncapsulation.Emulated, 'someComponent', template, ViewType.HOST);
expect(DOM.getInnerHTML(template)).toEqual('<div></div>');
});
});
});
}
@@ -1,200 +0,0 @@
import {
AsyncTestCompleter,
beforeEach,
beforeEachBindings,
ddescribe,
describe,
el,
expect,
iit,
inject,
it,
xit,
} from 'angular2/test_lib';
import {StyleInliner} from 'angular2/src/core/render/dom/compiler/style_inliner';
import {isBlank} from 'angular2/src/core/facade/lang';
import {Promise, PromiseWrapper} from 'angular2/src/core/facade/async';
import {Map, MapWrapper} from 'angular2/src/core/facade/collection';
import {XHR} from 'angular2/src/core/render/xhr';
import {bind} from 'angular2/core';
export function main() {
describe('StyleInliner', () => {
beforeEachBindings(() => [
bind(XHR)
.toClass(FakeXHR),
]);
describe('loading', () => {
it('should return a string when there is no import statement',
inject([StyleInliner], (inliner) => {
var css = '.main {}';
var loadedCss = inliner.inlineImports(css, 'http://base');
expect(loadedCss).toEqual(css);
}));
it('should inline @import rules',
inject([XHR, StyleInliner, AsyncTestCompleter], (xhr, inliner, async) => {
xhr.reply('http://base/one.css', '.one {}');
var css = '@import url("one.css");.main {}';
var loadedCss = inliner.inlineImports(css, 'http://base');
expect(loadedCss).toBePromise();
PromiseWrapper.then(loadedCss,
function(css) {
expect(css).toEqual('.one {}\n.main {}');
async.done();
},
function(e) { throw 'fail;' });
}));
it('should support url([unquoted url]) in @import rules',
inject([XHR, StyleInliner, AsyncTestCompleter], (xhr, inliner, async) => {
xhr.reply('http://base/one.css', '.one {}');
var css = '@import url(one.css);.main {}';
var loadedCss = inliner.inlineImports(css, 'http://base');
expect(loadedCss).toBePromise();
PromiseWrapper.then(loadedCss,
function(css) {
expect(css).toEqual('.one {}\n.main {}');
async.done();
},
function(e) { throw 'fail;' });
}));
it('should handle @import error gracefuly',
inject([StyleInliner, AsyncTestCompleter], (inliner, async) => {
var css = '@import "one.css";.main {}';
var loadedCss = inliner.inlineImports(css, 'http://base');
expect(loadedCss).toBePromise();
PromiseWrapper.then(
loadedCss,
function(css) {
expect(css).toEqual('/* failed to import http://base/one.css */\n.main {}');
async.done();
},
function(e) { throw 'fail;' });
}));
it('should inline multiple @import rules',
inject([XHR, StyleInliner, AsyncTestCompleter], (xhr, inliner, async) => {
xhr.reply('http://base/one.css', '.one {}');
xhr.reply('http://base/two.css', '.two {}');
var css = '@import "one.css";@import "two.css";.main {}';
var loadedCss = inliner.inlineImports(css, 'http://base');
expect(loadedCss).toBePromise();
PromiseWrapper.then(loadedCss,
function(css) {
expect(css).toEqual('.one {}\n.two {}\n.main {}');
async.done();
},
function(e) { throw 'fail;' });
}));
it('should inline nested @import rules',
inject([XHR, StyleInliner, AsyncTestCompleter], (xhr, inliner, async) => {
xhr.reply('http://base/one.css', '@import "two.css";.one {}');
xhr.reply('http://base/two.css', '.two {}');
var css = '@import "one.css";.main {}';
var loadedCss = inliner.inlineImports(css, 'http://base/');
expect(loadedCss).toBePromise();
PromiseWrapper.then(loadedCss,
function(css) {
expect(css).toEqual('.two {}\n.one {}\n.main {}');
async.done();
},
function(e) { throw 'fail;' });
}));
it('should handle circular dependencies gracefuly',
inject([XHR, StyleInliner, AsyncTestCompleter], (xhr, inliner, async) => {
xhr.reply('http://base/one.css', '@import "two.css";.one {}');
xhr.reply('http://base/two.css', '@import "one.css";.two {}');
var css = '@import "one.css";.main {}';
var loadedCss = inliner.inlineImports(css, 'http://base/');
expect(loadedCss).toBePromise();
PromiseWrapper.then(loadedCss,
function(css) {
expect(css).toEqual('.two {}\n.one {}\n.main {}');
async.done();
},
function(e) { throw 'fail;' });
}));
it('should handle invalid @import fracefuly',
inject([StyleInliner, AsyncTestCompleter], (inliner, async) => {
// Invalid rule: the url is not quoted
var css = '@import one.css;.main {}';
var loadedCss = inliner.inlineImports(css, 'http://base/');
expect(loadedCss).toBePromise();
PromiseWrapper.then(
loadedCss,
function(css) {
expect(css).toEqual('/* Invalid import rule: "@import one.css;" */.main {}');
async.done();
},
function(e) { throw 'fail;' });
}));
});
describe('media query', () => {
it('should wrap inlined content in media query',
inject([XHR, StyleInliner, AsyncTestCompleter], (xhr, inliner, async) => {
xhr.reply('http://base/one.css', '.one {}');
var css = '@import "one.css" (min-width: 700px) and (orientation: landscape);';
var loadedCss = inliner.inlineImports(css, 'http://base/');
expect(loadedCss).toBePromise();
PromiseWrapper.then(
loadedCss,
function(css) {
expect(css).toEqual(
'@media (min-width: 700px) and (orientation: landscape) {\n.one {}\n}\n');
async.done();
},
function(e) { throw 'fail;' });
}));
});
describe('url rewritting', () => {
it('should rewrite url in inlined content',
inject([XHR, StyleInliner, AsyncTestCompleter], (xhr, inliner, async) => {
// it should rewrite both '@import' and 'url()'
xhr.reply('http://base/one.css',
'@import "./nested/two.css";.one {background-image: url("one.jpg");}');
xhr.reply('http://base/nested/two.css',
'.two {background-image: url("../img/two.jpg");}');
var css = '@import "one.css";';
var loadedCss = inliner.inlineImports(css, 'http://base/');
expect(loadedCss).toBePromise();
PromiseWrapper.then(
loadedCss,
function(css) {
expect(css).toEqual(".two {background-image: url('http://base/img/two.jpg');}\n" +
".one {background-image: url('http://base/one.jpg');}\n");
async.done();
},
function(e) { throw 'fail;' });
}));
});
});
}
class FakeXHR extends XHR {
_responses = new Map<string, string>();
constructor() { super(); }
get(url: string): Promise<string> {
var response = this._responses.get(url);
if (isBlank(response)) {
return PromiseWrapper.reject('xhr error', null);
}
return PromiseWrapper.resolve(response);
}
reply(url: string, response: string) { this._responses.set(url, response); }
}
@@ -1,95 +0,0 @@
import {describe, it, expect, beforeEach, ddescribe, iit, xit, el} from 'angular2/test_lib';
import {StyleUrlResolver} from 'angular2/src/core/render/dom/compiler/style_url_resolver';
import {UrlResolver} from 'angular2/src/core/services/url_resolver';
export function main() {
describe('StyleUrlResolver', () => {
let styleUrlResolver;
beforeEach(() => { styleUrlResolver = new StyleUrlResolver(new UrlResolver()); });
it('should resolve "url()" urls', () => {
var css = `
.foo {
background-image: url("double.jpg");
background-image: url('simple.jpg');
background-image: url(noquote.jpg);
}`;
var expectedCss = `
.foo {
background-image: url('http://ng.io/double.jpg');
background-image: url('http://ng.io/simple.jpg');
background-image: url('http://ng.io/noquote.jpg');
}`;
var resolvedCss = styleUrlResolver.resolveUrls(css, 'http://ng.io');
expect(resolvedCss).toEqual(expectedCss);
});
it('should resolve "@import" urls', () => {
var css = `
@import '1.css';
@import "2.css";
`;
var expectedCss = `
@import 'http://ng.io/1.css';
@import 'http://ng.io/2.css';
`;
var resolvedCss = styleUrlResolver.resolveUrls(css, 'http://ng.io');
expect(resolvedCss).toEqual(expectedCss);
});
it('should resolve "@import url()" urls', () => {
var css = `
@import url('3.css');
@import url("4.css");
@import url(5.css);
`;
var expectedCss = `
@import url('http://ng.io/3.css');
@import url('http://ng.io/4.css');
@import url('http://ng.io/5.css');
`;
var resolvedCss = styleUrlResolver.resolveUrls(css, 'http://ng.io');
expect(resolvedCss).toEqual(expectedCss);
});
it('should support media query in "@import"', () => {
var css = `
@import 'print.css' print;
@import url(print.css) print;
`;
var expectedCss = `
@import 'http://ng.io/print.css' print;
@import url('http://ng.io/print.css') print;
`;
var resolvedCss = styleUrlResolver.resolveUrls(css, 'http://ng.io');
expect(resolvedCss).toEqual(expectedCss);
});
it('should not strip quotes from inlined SVG styles', () => {
var css = `
.selector {
background:rgb(55,71,79) url('data:image/svg+xml;utf8,<?xml version="1.0"?>');
background:rgb(55,71,79) url("data:image/svg+xml;utf8,<?xml version='1.0'?>");
background:rgb(55,71,79) url("/some/data:image");
}
`;
var expectedCss = `
.selector {
background:rgb(55,71,79) url('data:image/svg+xml;utf8,<?xml version="1.0"?>');
background:rgb(55,71,79) url("data:image/svg+xml;utf8,<?xml version='1.0'?>");
background:rgb(55,71,79) url('http://ng.io/some/data:image');
}
`;
var resolvedCss = styleUrlResolver.resolveUrls(css, 'http://ng.io');
expect(resolvedCss).toEqual(expectedCss);
});
});
}
@@ -1,79 +0,0 @@
import {describe, beforeEach, expect, it, iit, ddescribe, el} from 'angular2/test_lib';
import {
TextInterpolationParser
} from 'angular2/src/core/render/dom/compiler/text_interpolation_parser';
import {CompilePipeline} from 'angular2/src/core/render/dom/compiler/compile_pipeline';
import {MapWrapper, ListWrapper} from 'angular2/src/core/facade/collection';
import {Lexer, Parser, ASTWithSource} from 'angular2/src/core/change_detection/change_detection';
import {IgnoreChildrenStep} from './pipeline_spec';
import {
ProtoViewBuilder,
ElementBinderBuilder
} from 'angular2/src/core/render/dom/view/proto_view_builder';
import {DOM} from 'angular2/src/core/dom/dom_adapter';
import {ViewDefinition, ViewType} from 'angular2/src/core/render/api';
export function main() {
describe('TextInterpolationParser', () => {
function createPipeline() {
return new CompilePipeline(
[new IgnoreChildrenStep(), new TextInterpolationParser(new Parser(new Lexer()))]);
}
function createViewDefinition(): ViewDefinition {
return new ViewDefinition({componentId: 'someComponent'});
}
function process(templateString: string): ProtoViewBuilder {
var compileElements = createPipeline().processElements(
DOM.createTemplate(templateString), ViewType.COMPONENT, createViewDefinition());
return compileElements[0].inheritedProtoView;
}
function assertRootTextBinding(protoViewBuilder: ProtoViewBuilder, nodeIndex: number,
expression: string) {
var node = DOM.childNodes(DOM.templateAwareRoot(protoViewBuilder.rootElement))[nodeIndex];
expect(protoViewBuilder.rootTextBindings.get(node).source).toEqual(expression);
}
function assertElementTextBinding(elementBinderBuilder: ElementBinderBuilder, nodeIndex: number,
expression: string) {
var node = DOM.childNodes(DOM.templateAwareRoot(elementBinderBuilder.element))[nodeIndex];
expect(elementBinderBuilder.textBindings.get(node).source).toEqual(expression);
}
it('should find root text interpolations', () => {
var result = process('{{expr1}}{{expr2}}<div></div>{{expr3}}');
assertRootTextBinding(result, 0, "{{expr1}}{{expr2}}");
assertRootTextBinding(result, 2, "{{expr3}}");
});
it('should find text interpolation in normal elements', () => {
var result = process('<div>{{expr1}}<span></span>{{expr2}}</div>');
assertElementTextBinding(result.elements[0], 0, "{{expr1}}");
assertElementTextBinding(result.elements[0], 2, "{{expr2}}");
});
it('should allow multiple expressions', () => {
var result = process('<div>{{expr1}}{{expr2}}</div>');
assertElementTextBinding(result.elements[0], 0, "{{expr1}}{{expr2}}");
});
it('should not interpolate when compileChildren is false', () => {
var results = process('<div>{{included}}<span ignore-children>{{excluded}}</span></div>');
assertElementTextBinding(results.elements[0], 0, "{{included}}");
expect(results.elements.length).toBe(1);
expect(results.elements[0].textBindings.size).toBe(1);
});
it('should allow fixed text before, in between and after expressions', () => {
var result = process('<div>a{{expr1}}b{{expr2}}c</div>');
assertElementTextBinding(result.elements[0], 0, "a{{expr1}}b{{expr2}}c");
});
it('should escape quotes in fixed parts', () => {
var result = process("<div>'\"a{{expr1}}</div>");
assertElementTextBinding(result.elements[0], 0, "'\"a{{expr1}}");
});
});
}
@@ -1,207 +0,0 @@
import {
AsyncTestCompleter,
beforeEach,
ddescribe,
describe,
el,
expect,
iit,
inject,
it,
xit,
} from 'angular2/test_lib';
import {ViewLoader, TemplateAndStyles} from 'angular2/src/core/render/dom/compiler/view_loader';
import {StyleInliner} from 'angular2/src/core/render/dom/compiler/style_inliner';
import {StyleUrlResolver} from 'angular2/src/core/render/dom/compiler/style_url_resolver';
import {UrlResolver} from 'angular2/src/core/services/url_resolver';
import {PromiseWrapper, Promise} from 'angular2/src/core/facade/async';
import {MapWrapper, ListWrapper} from 'angular2/src/core/facade/collection';
import {XHR} from 'angular2/src/core/render/xhr';
import {MockXHR} from 'angular2/src/core/render/xhr_mock';
import {ViewDefinition} from 'angular2/src/core/render/api';
export function main() {
describe('ViewLoader', () => {
var loader: ViewLoader;
var xhr, styleUrlResolver, urlResolver;
beforeEach(() => {
xhr = new MockXHR();
urlResolver = new UrlResolver();
styleUrlResolver = new StyleUrlResolver(urlResolver);
let styleInliner = new StyleInliner(xhr, styleUrlResolver, urlResolver);
loader = new ViewLoader(xhr, styleInliner, styleUrlResolver);
});
describe('html', () => {
it('should load inline templates', inject([AsyncTestCompleter], (async) => {
loader.load(new ViewDefinition({template: 'template template'}))
.then((el) => {
expect(el.template).toEqual('template template');
async.done();
});
}));
it('should load templates through XHR', inject([AsyncTestCompleter], (async) => {
xhr.expect('http://ng.io/foo.html', 'xhr template');
loader.load(new ViewDefinition({templateAbsUrl: 'http://ng.io/foo.html'}))
.then((el) => {
expect(el.template).toEqual('xhr template');
async.done();
});
xhr.flush();
}));
it('should resolve urls in styles', inject([AsyncTestCompleter], (async) => {
xhr.expect('http://ng.io/foo.html',
'<style>.foo { background-image: url("double.jpg"); }</style>');
loader.load(new ViewDefinition({templateAbsUrl: 'http://ng.io/foo.html'}))
.then((el) => {
expect(el.template).toEqual('');
expect(el.styles)
.toEqual([".foo { background-image: url('http://ng.io/double.jpg'); }"]);
async.done();
});
xhr.flush();
}));
it('should inline styles', inject([AsyncTestCompleter], (async) => {
let xhr = new FakeXHR();
xhr.reply('http://ng.io/foo.html', '<style>@import "foo.css";</style>');
xhr.reply('http://ng.io/foo.css', '/* foo.css */');
let styleInliner = new StyleInliner(xhr, styleUrlResolver, urlResolver);
let loader = new ViewLoader(xhr, styleInliner, styleUrlResolver);
loader.load(new ViewDefinition({templateAbsUrl: 'http://ng.io/foo.html'}))
.then((el) => {
expect(el.template).toEqual('');
expect(el.styles).toEqual(["/* foo.css */\n"]);
async.done();
});
}));
it('should throw when no template is defined', () => {
expect(() => loader.load(new ViewDefinition(
{componentId: 'TestComponent', template: null, templateAbsUrl: null})))
.toThrowError(
'View should have either the templateUrl or template property set but none was found for the \'TestComponent\' component');
});
it('should return a rejected Promise when XHR loading fails',
inject([AsyncTestCompleter], (async) => {
xhr.expect('http://ng.io/foo.html', null);
PromiseWrapper.then(
loader.load(new ViewDefinition({templateAbsUrl: 'http://ng.io/foo.html'})),
function(_) { throw 'Unexpected response'; },
function(error) {
expect(error.message).toEqual('Failed to fetch url "http://ng.io/foo.html"');
async.done();
});
xhr.flush();
}));
it('should replace $baseUrl in attributes with the template base url',
inject([AsyncTestCompleter], (async) => {
xhr.expect('http://ng.io/path/foo.html', '<img src="$baseUrl/logo.png">');
loader.load(new ViewDefinition({templateAbsUrl: 'http://ng.io/path/foo.html'}))
.then((el) => {
expect(el.template).toEqual('<img src="http://ng.io/path/logo.png">');
async.done();
});
xhr.flush();
}));
});
describe('css', () => {
it('should load inline styles', inject([AsyncTestCompleter], (async) => {
loader.load(new ViewDefinition({template: 'html', styles: ['style 1', 'style 2']}))
.then((el) => {
expect(el.template).toEqual('html');
expect(el.styles).toEqual(['style 1', 'style 2']);
async.done();
});
}));
it('should resolve urls in inline styles', inject([AsyncTestCompleter], (async) => {
xhr.expect('http://ng.io/foo.html', 'html');
loader.load(new ViewDefinition({
templateAbsUrl: 'http://ng.io/foo.html',
styles: ['.foo { background-image: url("double.jpg"); }']
}))
.then((el) => {
expect(el.template).toEqual('html');
expect(el.styles)
.toEqual([".foo { background-image: url('http://ng.io/double.jpg'); }"]);
async.done();
});
xhr.flush();
}));
it('should load templates through XHR', inject([AsyncTestCompleter], (async) => {
xhr.expect('http://ng.io/foo.html', 'xhr template');
xhr.expect('http://ng.io/foo-1.css', '1');
xhr.expect('http://ng.io/foo-2.css', '2');
loader.load(new ViewDefinition({
templateAbsUrl: 'http://ng.io/foo.html',
styles: ['i1'],
styleAbsUrls: ['http://ng.io/foo-1.css', 'http://ng.io/foo-2.css']
}))
.then((el) => {
expect(el.template).toEqual('xhr template');
expect(el.styles).toEqual(['i1', '1', '2']);
async.done();
});
xhr.flush();
}));
it('should inline styles', inject([AsyncTestCompleter], (async) => {
let xhr = new FakeXHR();
xhr.reply('http://ng.io/foo.html', '<p>template</p>');
xhr.reply('http://ng.io/foo.css', '/* foo.css */');
let styleInliner = new StyleInliner(xhr, styleUrlResolver, urlResolver);
let loader = new ViewLoader(xhr, styleInliner, styleUrlResolver);
loader.load(
new ViewDefinition(
{templateAbsUrl: 'http://ng.io/foo.html', styles: ['@import "foo.css";']}))
.then((el) => {
expect(el.template).toEqual("<p>template</p>");
expect(el.styles).toEqual(["/* foo.css */\n"]);
async.done();
});
}));
it('should return a rejected Promise when XHR loading fails',
inject([AsyncTestCompleter], (async) => {
xhr.expect('http://ng.io/foo.css', null);
PromiseWrapper.then(
loader.load(
new ViewDefinition({template: '', styleAbsUrls: ['http://ng.io/foo.css']})),
function(_) { throw 'Unexpected response'; },
function(error) {
expect(error.message).toEqual('Failed to fetch url "http://ng.io/foo.css"');
async.done();
});
xhr.flush();
}));
});
});
}
class SomeComponent {}
class FakeXHR extends XHR {
_responses = new Map<string, string>();
constructor() { super(); }
get(url: string): Promise<string> {
return this._responses.has(url) ? PromiseWrapper.resolve(this._responses.get(url)) :
PromiseWrapper.reject('xhr error', null);
}
reply(url: string, response: string): void { this._responses.set(url, response); }
}
@@ -1,262 +0,0 @@
import {
describe,
beforeEach,
it,
expect,
iit,
ddescribe,
el,
stringifyElement
} from 'angular2/test_lib';
import {MapWrapper} from 'angular2/src/core/facade/collection';
import {ViewSplitter} from 'angular2/src/core/render/dom/compiler/view_splitter';
import {CompilePipeline} from 'angular2/src/core/render/dom/compiler/compile_pipeline';
import {CompileElement} from 'angular2/src/core/render/dom/compiler/compile_element';
import {ProtoViewDto, ViewType, ViewDefinition} from 'angular2/src/core/render/api';
import {DOM} from 'angular2/src/core/dom/dom_adapter';
import {Lexer, Parser} from 'angular2/src/core/change_detection/change_detection';
export function main() {
describe('ViewSplitter', () => {
function createViewDefinition(): ViewDefinition {
return new ViewDefinition({componentId: 'someComponent'});
}
function createPipeline() {
return new CompilePipeline([new ViewSplitter(new Parser(new Lexer()))]);
}
function proceess(el): CompileElement[] {
return createPipeline().processElements(el, ViewType.COMPONENT, createViewDefinition());
}
describe('<template> elements', () => {
it('should move the content into a new <template> element and mark that as viewRoot', () => {
var rootElement = DOM.createTemplate('<template if="true">a</template>');
var results = proceess(rootElement);
expect(stringifyElement(results[1].element))
.toEqual('<template class="ng-binding" if="true"></template>');
expect(results[1].isViewRoot).toBe(false);
expect(stringifyElement(results[2].element)).toEqual('<template>a</template>');
expect(results[2].isViewRoot).toBe(true);
});
it('should mark the new <template> element as viewRoot', () => {
var rootElement = DOM.createTemplate('<template if="true">a</template>');
var results = proceess(rootElement);
expect(results[2].isViewRoot).toBe(true);
});
it('should not wrap the root element', () => {
var rootElement = DOM.createTemplate('');
var results = proceess(rootElement);
expect(results.length).toBe(1);
expect(stringifyElement(rootElement)).toEqual('<template></template>');
});
it('should copy over the elementDescription', () => {
var rootElement = DOM.createTemplate('<template if="true">a</template>');
var results = proceess(rootElement);
expect(results[2].elementDescription).toBe(results[1].elementDescription);
});
it('should clean out the inheritedElementBinder', () => {
var rootElement = DOM.createTemplate('<template if="true">a</template>');
var results = proceess(rootElement);
expect(results[2].inheritedElementBinder).toBe(null);
});
it('should create a nestedProtoView', () => {
var rootElement = DOM.createTemplate('<template if="true">a</template>');
var results = proceess(rootElement);
expect(results[2].inheritedProtoView).not.toBe(null);
expect(results[2].inheritedProtoView)
.toBe(results[1].inheritedElementBinder.nestedProtoView);
expect(results[2].inheritedProtoView.type).toBe(ViewType.EMBEDDED);
expect(stringifyElement(results[2].inheritedProtoView.rootElement))
.toEqual('<template>a</template>');
});
});
describe('elements with template attribute', () => {
it('should replace the element with an empty <template> element', () => {
var rootElement = DOM.createTemplate('<span template=""></span>');
var originalChild = DOM.firstChild(DOM.content(rootElement));
var results = proceess(rootElement);
expect(results[0].element).toBe(rootElement);
expect(stringifyElement(results[0].element))
.toEqual('<template><template class="ng-binding"></template></template>');
expect(stringifyElement(results[2].element))
.toEqual('<template><span template=""></span></template>');
expect(DOM.firstChild(DOM.content(results[2].element))).toBe(originalChild);
});
it('should work with top-level template node', () => {
var rootElement = DOM.createTemplate('<div template>x</div>');
var originalChild = DOM.content(rootElement).childNodes[0];
var results = proceess(rootElement);
expect(results[0].element).toBe(rootElement);
expect(results[0].isViewRoot).toBe(true);
expect(results[2].isViewRoot).toBe(true);
expect(stringifyElement(results[0].element))
.toEqual('<template><template class="ng-binding"></template></template>');
expect(DOM.firstChild(DOM.content(results[2].element))).toBe(originalChild);
});
it('should mark the element as viewRoot', () => {
var rootElement = DOM.createTemplate('<div template></div>');
var results = proceess(rootElement);
expect(results[2].isViewRoot).toBe(true);
});
it('should add property bindings from the template attribute', () => {
var rootElement = DOM.createTemplate('<div template="some-prop:expr"></div>');
var results = proceess(rootElement);
expect(results[1].inheritedElementBinder.propertyBindings.get('someProp').source)
.toEqual('expr');
expect(results[1].attrs().get('some-prop')).toEqual('expr');
});
it('should add variable mappings from the template attribute to the nestedProtoView', () => {
var rootElement = DOM.createTemplate('<div template="var var-name=mapName"></div>');
var results = proceess(rootElement);
expect(results[2].inheritedProtoView.variableBindings)
.toEqual(MapWrapper.createFromStringMap({'mapName': 'varName'}));
});
it('should add entries without value as attributes to the element', () => {
var rootElement = DOM.createTemplate('<div template="varname"></div>');
var results = proceess(rootElement);
expect(results[1].attrs().get('varname')).toEqual('');
expect(results[1].inheritedElementBinder.propertyBindings).toEqual(new Map());
expect(results[1].inheritedElementBinder.variableBindings).toEqual(new Map());
});
it('should iterate properly after a template dom modification', () => {
var rootElement = DOM.createTemplate('<div template></div><after></after>');
var results = proceess(rootElement);
// 1 root + 2 initial + 2 generated template elements
expect(results.length).toEqual(5);
});
it('should copy over the elementDescription', () => {
var rootElement = DOM.createTemplate('<span template=""></span>');
var results = proceess(rootElement);
expect(results[2].elementDescription).toBe(results[1].elementDescription);
});
it('should clean out the inheritedElementBinder', () => {
var rootElement = DOM.createTemplate('<span template=""></span>');
var results = proceess(rootElement);
expect(results[2].inheritedElementBinder).toBe(null);
});
it('should create a nestedProtoView', () => {
var rootElement = DOM.createTemplate('<span template=""></span>');
var results = proceess(rootElement);
expect(results[2].inheritedProtoView).not.toBe(null);
expect(results[2].inheritedProtoView)
.toBe(results[1].inheritedElementBinder.nestedProtoView);
expect(stringifyElement(results[2].inheritedProtoView.rootElement))
.toEqual('<template><span template=""></span></template>');
});
});
describe('elements with *directive_name attribute', () => {
it('should replace the element with an empty <template> element', () => {
var rootElement = DOM.createTemplate('<span *ng-if></span>');
var originalChild = DOM.firstChild(DOM.content(rootElement));
var results = proceess(rootElement);
expect(results[0].element).toBe(rootElement);
expect(stringifyElement(results[0].element))
.toEqual('<template><template class="ng-binding" ng-if=""></template></template>');
expect(stringifyElement(results[2].element))
.toEqual('<template><span *ng-if=""></span></template>');
expect(DOM.firstChild(DOM.content(results[2].element))).toBe(originalChild);
});
it('should mark the element as viewRoot', () => {
var rootElement = DOM.createTemplate('<div *foo="bar"></div>');
var results = proceess(rootElement);
expect(results[2].isViewRoot).toBe(true);
});
it('should work with top-level template node', () => {
var rootElement = DOM.createTemplate('<div *foo>x</div>');
var originalChild = DOM.content(rootElement).childNodes[0];
var results = proceess(rootElement);
expect(results[0].element).toBe(rootElement);
expect(results[0].isViewRoot).toBe(true);
expect(results[2].isViewRoot).toBe(true);
expect(stringifyElement(results[0].element))
.toEqual('<template><template class="ng-binding" foo=""></template></template>');
expect(DOM.firstChild(DOM.content(results[2].element))).toBe(originalChild);
});
it('should add property bindings from the template attribute', () => {
var rootElement = DOM.createTemplate('<div *prop="expr"></div>');
var results = proceess(rootElement);
expect(results[1].inheritedElementBinder.propertyBindings.get('prop').source)
.toEqual('expr');
expect(results[1].attrs().get('prop')).toEqual('expr');
});
it('should add variable mappings from the template attribute to the nestedProtoView', () => {
var rootElement = DOM.createTemplate('<div *foreach="var varName=mapName"></div>');
var results = proceess(rootElement);
expect(results[2].inheritedProtoView.variableBindings)
.toEqual(MapWrapper.createFromStringMap({'mapName': 'varName'}));
});
it('should add entries without value as attribute to the element', () => {
var rootElement = DOM.createTemplate('<div *varname></div>');
var results = proceess(rootElement);
expect(results[1].attrs().get('varname')).toEqual('');
expect(results[1].inheritedElementBinder.propertyBindings).toEqual(new Map());
expect(results[1].inheritedElementBinder.variableBindings).toEqual(new Map());
});
it('should iterate properly after a template dom modification', () => {
var rootElement = DOM.createTemplate('<div *foo></div><after></after>');
var results = proceess(rootElement);
// 1 root + 2 initial + 2 generated template elements
expect(results.length).toEqual(5);
});
it('should copy over the elementDescription', () => {
var rootElement = DOM.createTemplate('<span *foo></span>');
var results = proceess(rootElement);
expect(results[2].elementDescription).toBe(results[1].elementDescription);
});
it('should clean out the inheritedElementBinder', () => {
var rootElement = DOM.createTemplate('<span *foo></span>');
var results = proceess(rootElement);
expect(results[2].inheritedElementBinder).toBe(null);
});
it('should create a nestedProtoView', () => {
var rootElement = DOM.createTemplate('<span *foo></span>');
var results = proceess(rootElement);
expect(results[2].inheritedProtoView).not.toBe(null);
expect(results[2].inheritedProtoView)
.toBe(results[1].inheritedElementBinder.nestedProtoView);
expect(stringifyElement(results[2].inheritedProtoView.rootElement))
.toEqual('<template><span *foo=""></span></template>');
});
});
});
}
@@ -1,138 +0,0 @@
import {Inject, Injectable} from 'angular2/core';
import {isPresent} from 'angular2/src/core/facade/lang';
import {MapWrapper, ListWrapper, Map} from 'angular2/src/core/facade/collection';
import {PromiseWrapper, Promise} from 'angular2/src/core/facade/async';
import {DOM} from 'angular2/src/core/dom/dom_adapter';
import {DomRenderer} from 'angular2/src/core/render/dom/dom_renderer';
import {DOCUMENT} from 'angular2/src/core/render/dom/dom_tokens';
import {DefaultDomCompiler} from 'angular2/src/core/render/dom/compiler/compiler';
import {
RenderViewWithFragments,
RenderFragmentRef,
RenderViewRef,
ProtoViewDto,
ViewDefinition,
RenderEventDispatcher,
RenderDirectiveMetadata,
RenderElementRef,
RenderProtoViewMergeMapping,
RenderProtoViewRef
} from 'angular2/src/core/render/api';
import {resolveInternalDomView} from 'angular2/src/core/render/dom/view/view';
import {resolveInternalDomFragment} from 'angular2/src/core/render/dom/view/fragment';
import {el, dispatchEvent} from 'angular2/test_lib';
export class TestRootView {
viewRef: RenderViewRef;
fragments: RenderFragmentRef[];
hostElement: Element;
events: any[][];
constructor(viewWithFragments: RenderViewWithFragments) {
this.viewRef = viewWithFragments.viewRef;
this.fragments = viewWithFragments.fragmentRefs;
this.hostElement = <Element>resolveInternalDomFragment(this.fragments[0])[0];
this.events = [];
}
}
export class TestRenderElementRef implements RenderElementRef {
constructor(public renderView: RenderViewRef, public renderBoundElementIndex: number) {}
}
export function elRef(renderView: RenderViewRef, boundElementIndex: number) {
return new TestRenderElementRef(renderView, boundElementIndex);
}
export function rootNodes(view: RenderViewRef) {}
class LoggingEventDispatcher implements RenderEventDispatcher {
log: any[][];
constructor(log: any[][]) { this.log = log; }
dispatchRenderEvent(elementIndex: number, eventName: string, locals: Map<string, any>): boolean {
this.log.push([elementIndex, eventName, locals]);
return true;
}
}
@Injectable()
export class DomTestbed {
renderer: DomRenderer;
compiler: DefaultDomCompiler;
rootEl;
constructor(renderer: DomRenderer, compiler: DefaultDomCompiler, @Inject(DOCUMENT) document) {
this.renderer = renderer;
this.compiler = compiler;
this.rootEl = el('<div id="root" class="rootElem"></div>');
var oldRoots = DOM.querySelectorAll(document, '#root');
for (var i = 0; i < oldRoots.length; i++) {
DOM.remove(oldRoots[i]);
}
DOM.appendChild(DOM.querySelector(document, 'body'), this.rootEl);
}
compile(host: RenderDirectiveMetadata,
componentViews: ViewDefinition[]): Promise<ProtoViewDto[]> {
var promises = [this.compiler.compileHost(host)];
componentViews.forEach(view => promises.push(this.compiler.compile(view)));
return PromiseWrapper.all(promises);
}
merge(protoViews:
Array<ProtoViewDto | RenderProtoViewRef>): Promise<RenderProtoViewMergeMapping> {
return this.compiler.mergeProtoViewsRecursively(collectMergeRenderProtoViewsRecurse(
<ProtoViewDto>protoViews[0], ListWrapper.slice(protoViews, 1)));
}
compileAndMerge(host: RenderDirectiveMetadata,
componentViews: ViewDefinition[]): Promise<RenderProtoViewMergeMapping> {
return this.compile(host, componentViews).then(protoViewDtos => this.merge(protoViewDtos));
}
_createTestView(viewWithFragments: RenderViewWithFragments) {
var testView = new TestRootView(viewWithFragments);
this.renderer.setEventDispatcher(viewWithFragments.viewRef,
new LoggingEventDispatcher(testView.events));
return testView;
}
createView(protoView: RenderProtoViewMergeMapping): TestRootView {
var viewWithFragments = this.renderer.createView(protoView.mergedProtoViewRef, 0);
this.renderer.hydrateView(viewWithFragments.viewRef);
return this._createTestView(viewWithFragments);
}
triggerEvent(elementRef: RenderElementRef, eventName: string) {
var element = resolveInternalDomView(elementRef.renderView)
.boundElements[elementRef.renderBoundElementIndex];
dispatchEvent(element, eventName);
}
}
function collectMergeRenderProtoViewsRecurse(current: ProtoViewDto,
components: Array<ProtoViewDto | RenderProtoViewRef>):
Array<RenderProtoViewRef | any[]> {
var result = [current.render];
current.elementBinders.forEach((elementBinder) => {
if (isPresent(elementBinder.nestedProtoView)) {
result.push(collectMergeRenderProtoViewsRecurse(elementBinder.nestedProtoView, components));
} else if (elementBinder.directives.length > 0) {
if (components.length > 0) {
var comp = components.shift();
if (comp instanceof ProtoViewDto) {
result.push(collectMergeRenderProtoViewsRecurse(comp, components));
} else {
result.push(comp);
}
} else {
result.push(null);
}
}
});
return result;
}
@@ -1,67 +0,0 @@
import {
AsyncTestCompleter,
beforeEach,
ddescribe,
describe,
el,
expect,
iit,
inject,
it,
xit,
beforeEachBindings,
SpyObject,
} from 'angular2/test_lib';
import {DOM} from 'angular2/src/core/dom/dom_adapter';
import {TemplateCloner} from 'angular2/src/core/render/dom/template_cloner';
export function main() {
describe('TemplateCloner', () => {
var cloner: TemplateCloner;
var bigTemplate: Element;
var smallTemplate: Element;
beforeEach(() => {
cloner = new TemplateCloner(1);
bigTemplate = DOM.createTemplate('a<div></div>');
smallTemplate = DOM.createTemplate('a');
});
describe('prepareForClone', () => {
it('should use a reference for small templates',
() => { expect(cloner.prepareForClone(smallTemplate)).toBe(smallTemplate); });
it('should use a reference if the max element count is -1', () => {
cloner = new TemplateCloner(-1);
expect(cloner.prepareForClone(bigTemplate)).toBe(bigTemplate);
});
it('should use a string for big templates', () => {
expect(cloner.prepareForClone(bigTemplate)).toEqual(DOM.getInnerHTML(bigTemplate));
});
});
describe('cloneTemplate', () => {
function shouldReturnTemplateContentNodes(template: Element, importIntoDoc: boolean) {
var clone = cloner.cloneContent(cloner.prepareForClone(template), importIntoDoc);
expect(clone).not.toBe(DOM.content(template));
expect(DOM.getText(DOM.firstChild(clone))).toEqual('a');
}
it('should return template.content nodes (small template, no import)',
() => { shouldReturnTemplateContentNodes(smallTemplate, false); });
it('should return template.content nodes (small template, import)',
() => { shouldReturnTemplateContentNodes(smallTemplate, true); });
it('should return template.content nodes (big template, no import)',
() => { shouldReturnTemplateContentNodes(bigTemplate, false); });
it('should return template.content nodes (big template, import)',
() => { shouldReturnTemplateContentNodes(bigTemplate, true); });
});
});
}
@@ -1,164 +0,0 @@
import {
describe,
ddescribe,
it,
iit,
xit,
xdescribe,
expect,
beforeEach,
el
} from 'angular2/test_lib';
import {
DomElementSchemaRegistry
} from 'angular2/src/core/render/dom/schema/dom_element_schema_registry';
import {TemplateCloner} from 'angular2/src/core/render/dom/template_cloner';
import {ProtoViewBuilder} from 'angular2/src/core/render/dom/view/proto_view_builder';
import {ASTWithSource, AST} from 'angular2/src/core/change_detection/change_detection';
import {PropertyBindingType, ViewType, ViewEncapsulation} from 'angular2/src/core/render/api';
import {DOM} from 'angular2/src/core/dom/dom_adapter';
import {IS_DART} from '../../../../platform';
export function main() {
function emptyExpr() { return new ASTWithSource(new AST(), 'empty', 'empty'); }
describe('ProtoViewBuilder', () => {
var builder;
var templateCloner;
beforeEach(() => {
templateCloner = new TemplateCloner(-1);
builder =
new ProtoViewBuilder(DOM.createTemplate(''), ViewType.EMBEDDED, ViewEncapsulation.None);
});
if (!IS_DART) {
describe('verification of properties', () => {
it('should throw for unknown properties', () => {
builder.bindElement(el('<div/>')).bindProperty('unknownProperty', emptyExpr());
expect(() => builder.build(new DomElementSchemaRegistry(), templateCloner))
.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`);
});
it('should allow unknown properties if a directive uses it', () => {
var binder = builder.bindElement(el('<div/>'));
binder.bindDirective(0).bindProperty('someDirProperty', emptyExpr(), 'directiveProperty');
binder.bindProperty('directiveProperty', emptyExpr());
expect(() => builder.build(new DomElementSchemaRegistry(), templateCloner)).not.toThrow();
});
it('should throw for unknown host properties even if another directive uses it', () => {
var binder = builder.bindElement(el('<div/>'));
binder.bindDirective(0).bindProperty('someDirProperty', emptyExpr(), 'someDirProperty');
binder.bindDirective(1).bindHostProperty('someDirProperty', emptyExpr());
expect(() => builder.build(new DomElementSchemaRegistry()))
.toThrowError(
`Can't bind to 'someDirProperty' since it isn't a known property of the '<div>' element`);
});
it('should allow unknown properties on custom elements', () => {
var binder = builder.bindElement(el('<some-custom/>'));
binder.bindProperty('unknownProperty', emptyExpr());
expect(() => builder.build(new DomElementSchemaRegistry(), templateCloner)).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(new DomElementSchemaRegistry(), templateCloner))
.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`);
});
});
} else {
describe('verification of properties', () => {
// 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 not throw for unknown properties', () => {
builder.bindElement(el('<div/>')).bindProperty('unknownProperty', emptyExpr());
expect(() => builder.build(new DomElementSchemaRegistry(), templateCloner)).not.toThrow();
});
});
}
describe('property normalization', () => {
it('should normalize "innerHtml" to "innerHTML"', () => {
builder.bindElement(el('<div/>')).bindProperty('innerHtml', emptyExpr());
var pv = builder.build(new DomElementSchemaRegistry(), templateCloner);
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(new DomElementSchemaRegistry(), templateCloner);
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(new DomElementSchemaRegistry(), templateCloner);
expect(pv.elementBinders[0].propertyBindings[0].property).toEqual('readOnly');
});
it('should normalize "class" to "className"', () => {
builder.bindElement(el('<div></div>')).bindProperty('class', emptyExpr());
var pv = builder.build(new DomElementSchemaRegistry(), templateCloner);
expect(pv.elementBinders[0].propertyBindings[0].property).toEqual('className');
});
});
describe('property binding', () => {
describe('types', () => {
it('should detect property names', () => {
builder.bindElement(el('<div/>')).bindProperty('tabindex', emptyExpr());
var pv = builder.build(new DomElementSchemaRegistry(), templateCloner);
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(new DomElementSchemaRegistry(), templateCloner);
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(new DomElementSchemaRegistry(), templateCloner);
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(new DomElementSchemaRegistry(), templateCloner);
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(new DomElementSchemaRegistry(), templateCloner);
expect(pv.elementBinders[0].propertyBindings[0].unit).toEqual('someUnit');
});
});
it('should not create a property binding when there is already same directive property binding',
() => {
var binder = builder.bindElement(el('<div/>'));
binder.bindProperty('tabindex', emptyExpr());
binder.bindDirective(0).bindProperty('tabindex', emptyExpr(), 'tabindex');
var pv = builder.build(new DomElementSchemaRegistry(), templateCloner);
expect(pv.elementBinders[0].propertyBindings.length).toEqual(0);
});
});
});
}
@@ -1,346 +0,0 @@
import {
AsyncTestCompleter,
beforeEach,
ddescribe,
describe,
xdescribe,
el,
expect,
iit,
inject,
it,
xit,
beforeEachBindings,
SpyObject,
stringifyElement
} from 'angular2/test_lib';
import {isPresent} from 'angular2/src/core/facade/lang';
import {DomTestbed} from '../../../../core/render/dom/dom_testbed';
import {
ViewDefinition,
RenderDirectiveMetadata,
RenderProtoViewMergeMapping,
ViewEncapsulation,
ViewType
} from 'angular2/src/core/render/api';
import {DOM} from 'angular2/src/core/dom/dom_adapter';
import {cloneAndQueryProtoView} from 'angular2/src/core/render/dom/util';
import {TemplateCloner} from 'angular2/src/core/render/dom/template_cloner';
import {
resolveInternalDomProtoView,
DomProtoView
} from 'angular2/src/core/render/dom/view/proto_view';
import {ProtoViewBuilder} from 'angular2/src/core/render/dom/view/proto_view_builder';
import {ElementSchemaRegistry} from 'angular2/src/core/render/dom/schema/element_schema_registry';
export function main() {
describe('ProtoViewMerger integration test', () => {
beforeEachBindings(() => [DomTestbed]);
describe('component views', () => {
it('should merge a component view',
runAndAssert('root', ['a'], ['<root class="ng-binding" idx="0">a</root>']));
it('should merge component views with interpolation at root level',
runAndAssert('root', ['{{a}}'], ['<root class="ng-binding" idx="0">{0}</root>']));
it('should merge component views with interpolation not at root level',
runAndAssert('root', ['<div>{{a}}</div>'], [
'<root class="ng-binding" idx="0"><div class="ng-binding" idx="1">{0}</div></root>'
]));
it('should merge component views with bound elements',
runAndAssert('root', ['<div #a></div>'], [
'<root class="ng-binding" idx="0"><div #a="" class="ng-binding" idx="1"></div></root>'
]));
});
describe('embedded views', () => {
it('should merge embedded views as fragments',
runAndAssert('root', ['<template>a</template>'], [
'<root class="ng-binding" idx="0"><template class="ng-binding" idx="1"></template></root>',
'a'
]));
it('should merge embedded views with interpolation at root level',
runAndAssert('root', ['<template>{{a}}</template>'], [
'<root class="ng-binding" idx="0"><template class="ng-binding" idx="1"></template></root>',
'{0}'
]));
it('should merge embedded views with interpolation not at root level',
runAndAssert('root', ['<div *ng-if>{{a}}</div>'], [
'<root class="ng-binding" idx="0"><template class="ng-binding" idx="1" ng-if=""></template></root>',
'<div *ng-if="" class="ng-binding" idx="2">{0}</div>'
]));
it('should merge embedded views with bound elements',
runAndAssert('root', ['<div *ng-if #a></div>'], [
'<root class="ng-binding" idx="0"><template class="ng-binding" idx="1" ng-if=""></template></root>',
'<div #a="" *ng-if="" class="ng-binding" idx="2"></div>'
]));
});
describe('projection', () => {
it('should remove text nodes if there is no ng-content',
runAndAssert(
'root', ['<a>b</a>', ''],
['<root class="ng-binding" idx="0"><a class="ng-binding" idx="1"></a></root>']));
it('should project static text',
runAndAssert('root', ['<a>b</a>', 'A(<ng-content></ng-content>)'], [
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<!--[-->b<!--]-->)</a></root>'
]));
it('should project text interpolation',
runAndAssert('root', ['<a>{{b}}</a>', 'A(<ng-content></ng-content>)'], [
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<!--[-->{0}<!--]-->)</a></root>'
]));
it('should project text interpolation to elements without bindings',
runAndAssert('root', ['<a>{{b}}</a>', '<div><ng-content></ng-content></div>'], [
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1"><div class="ng-binding"><!--[-->{0}<!--]--></div></a></root>'
]));
it('should project elements',
runAndAssert('root', ['<a><div></div></a>', 'A(<ng-content></ng-content>)'], [
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<!--[--><div></div><!--]-->)</a></root>'
]));
it('should project elements using the selector',
runAndAssert(
'root',
[
'<a><div class="x">a</div><span></span><div class="x">b</div></a>',
'A(<ng-content select=".x"></ng-content>)'
],
[
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<!--[--><div class="x">a</div><div class="x">b</div><!--]-->)</a></root>'
]));
it('should reproject',
runAndAssert(
'root',
['<a>x</a>', 'A(<b><ng-content></ng-content></b>)', 'B(<ng-content></ng-content>)'], [
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<b class="ng-binding" idx="2">B(<!--[--><!--[-->x<!--]--><!--]-->)</b>)</a></root>'
]));
it('should reproject text interpolation to sibling text nodes',
runAndAssert(
'root',
[
'<a>{{x}}</a>',
'<b>A(<ng-content></ng-content>)</b>)',
'B(<ng-content></ng-content>)'
],
[
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1"><b class="ng-binding" idx="2">B(<!--[-->A(<!--[-->{0}<!--]-->)<!--]-->)</b>)</a></root>'
]));
it('should reproject by combining selectors',
runAndAssert(
'root',
[
'<a><div class="x"></div><div class="x y"></div><div class="y"></div></a>',
'A(<b><ng-content select=".x"></ng-content></b>)',
'B(<ng-content select=".y"></ng-content>)'
],
[
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<b class="ng-binding" idx="2">B(<!--[--><div class="x y"></div><!--]-->)</b>)</a></root>'
]));
it('should keep non projected embedded views as fragments (so that they can be moved manually)',
runAndAssert(
'root', ['<a><template class="x">b</template></a>', ''],
['<root class="ng-binding" idx="0"><a class="ng-binding" idx="1"></a></root>', 'b']));
it('should project embedded views and match the template element',
runAndAssert(
'root', ['<a><template class="x">b</template></a>', 'A(<ng-content></ng-content>)'], [
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<!--[--><template class="x ng-binding" idx="2"></template><!--]-->)</a></root>',
'b'
]));
it('should project nodes using the ng-content in embedded views',
runAndAssert('root', ['<a>b</a>', 'A(<ng-content *ng-if></ng-content>)'], [
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<template class="ng-binding" idx="2" ng-if=""></template>)</a></root>',
'<!--[-->b<!--]-->'
]));
it('should allow to use wildcard selector after embedded view with non wildcard selector',
runAndAssert(
'root',
[
'<a><div class="x">a</div>b</a>',
'A(<ng-content select=".x" *ng-if></ng-content>, <ng-content></ng-content>)'
],
[
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">A(<template class="ng-binding" idx="2" ng-if=""></template>, <!--[-->b<!--]-->)</a></root>',
'<!--[--><div class="x">a</div><!--]-->'
]));
});
describe('composition', () => {
it('should merge multiple component views',
runAndAssert('root', ['<a></a><b></b>', 'c', 'd'], [
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">c</a><b class="ng-binding" idx="2">d</b></root>'
]));
it('should merge multiple embedded views as fragments',
runAndAssert('root', ['<div *ng-if></div><span *ng-for></span>'], [
'<root class="ng-binding" idx="0"><template class="ng-binding" idx="1" ng-if=""></template><template class="ng-binding" idx="2" ng-for=""></template></root>',
'<div *ng-if=""></div>',
'<span *ng-for=""></span>'
]));
it('should merge nested embedded views as fragments',
runAndAssert('root', ['<div *ng-if><span *ng-for></span></div>'], [
'<root class="ng-binding" idx="0"><template class="ng-binding" idx="1" ng-if=""></template></root>',
'<div *ng-if=""><template class="ng-binding" idx="2" ng-for=""></template></div>',
'<span *ng-for=""></span>'
]));
});
describe('element index mapping should be grouped by view and view depth first', () => {
it('should map component views correctly',
runAndAssert('root', ['<a></a><b></b>', '<c></c>'], [
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1"><c class="ng-binding" idx="3"></c></a><b class="ng-binding" idx="2"></b></root>'
]));
it('should map moved projected elements correctly',
runAndAssert(
'root',
[
'<a><b></b><c></c></a>',
'<ng-content select="c"></ng-content><ng-content select="b"></ng-content>'
],
[
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1"><!--[--><c class="ng-binding" idx="3"></c><!--]--><!--[--><b class="ng-binding" idx="2"></b><!--]--></a></root>'
]));
});
describe('text index mapping should be grouped by view and view depth first', () => {
it('should map component views correctly', runAndAssert('root', ['<a></a>{{b}}', '{{c}}'], [
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1">{1}</a>{0}</root>'
]));
it('should map moved projected elements correctly',
runAndAssert(
'root',
[
'<a><div x>{{x}}</div><div y>{{y}}</div></a>',
'<ng-content select="[y]"></ng-content><ng-content select="[x]"></ng-content>'
],
[
'<root class="ng-binding" idx="0"><a class="ng-binding" idx="1"><!--[--><div class="ng-binding" idx="3" y="">{1}</div><!--]--><!--[--><div class="ng-binding" idx="2" x="">{0}</div><!--]--></a></root>'
]));
});
describe('native shadow dom support', () => {
it('should keep the non projected light dom and wrap the component view into a shadow-root element',
runAndAssert('native-root', ['<a>b</a>', 'c'], [
'<native-root class="ng-binding" idx="0"><shadow-root><a class="ng-binding" idx="1"><shadow-root>c</shadow-root>b</a></shadow-root></native-root>'
]));
});
describe('host attributes', () => {
it('should set host attributes while merging',
inject([AsyncTestCompleter, DomTestbed, TemplateCloner], (async, tb: DomTestbed,
cloner: TemplateCloner) => {
tb.compiler.compileHost(rootDirective('root'))
.then((rootProtoViewDto) => {
var builder = new ProtoViewBuilder(DOM.createTemplate(''), ViewType.COMPONENT,
ViewEncapsulation.None);
builder.setHostAttribute('a', 'b');
var componentProtoViewDto = builder.build(new ElementSchemaRegistry(), cloner);
tb.merge([rootProtoViewDto, componentProtoViewDto])
.then(mergeMappings => {
var domPv = resolveInternalDomProtoView(mergeMappings.mergedProtoViewRef);
expect(stringifyElement(templateRoot(domPv)))
.toEqual('<template><root a="b" class="ng-binding"></root></template>');
async.done();
});
});
}));
});
});
}
function templateRoot(pv: DomProtoView) {
return <Element>pv.cloneableTemplate;
}
function runAndAssert(hostElementName: string, componentTemplates: string[],
expectedFragments: string[]) {
var useNativeEncapsulation = hostElementName.startsWith('native-');
var rootComp = rootDirective(hostElementName);
return inject([AsyncTestCompleter, DomTestbed, TemplateCloner], (async, tb: DomTestbed,
cloner: TemplateCloner) => {
tb.compileAndMerge(rootComp, componentTemplates.map(template => componentView(
template, useNativeEncapsulation ?
ViewEncapsulation.Native :
ViewEncapsulation.None)))
.then((mergeMappings) => {
expect(stringify(cloner, mergeMappings)).toEqual(expectedFragments);
async.done();
});
});
}
function rootDirective(hostElementName: string) {
return RenderDirectiveMetadata.create(
{id: 'rootComp', type: RenderDirectiveMetadata.COMPONENT_TYPE, selector: hostElementName});
}
function componentView(template: string,
encapsulation: ViewEncapsulation = ViewEncapsulation.None) {
return new ViewDefinition({
componentId: 'someComp',
template: template,
directives: [aComp, bComp, cComp],
encapsulation: encapsulation
});
}
function stringify(cloner: TemplateCloner, protoViewMergeMapping: RenderProtoViewMergeMapping):
string[] {
var testView = cloneAndQueryProtoView(
cloner, resolveInternalDomProtoView(protoViewMergeMapping.mergedProtoViewRef), false);
for (var i = 0; i < protoViewMergeMapping.mappedElementIndices.length; i++) {
var renderElIdx = protoViewMergeMapping.mappedElementIndices[i];
if (isPresent(renderElIdx)) {
DOM.setAttribute(testView.boundElements[renderElIdx], 'idx', `${i}`);
}
}
for (var i = 0; i < protoViewMergeMapping.mappedTextIndices.length; i++) {
var renderTextIdx = protoViewMergeMapping.mappedTextIndices[i];
if (isPresent(renderTextIdx)) {
DOM.setText(testView.boundTextNodes[renderTextIdx], `{${i}}`);
}
}
expect(protoViewMergeMapping.fragmentCount).toEqual(testView.fragments.length);
return testView.fragments.map(nodes => nodes.map(node => stringifyElement(node)).join(''));
}
var aComp = RenderDirectiveMetadata.create(
{id: 'aComp', type: RenderDirectiveMetadata.COMPONENT_TYPE, selector: 'a'});
var bComp = RenderDirectiveMetadata.create(
{id: 'bComp', type: RenderDirectiveMetadata.COMPONENT_TYPE, selector: 'b'});
var cComp = RenderDirectiveMetadata.create(
{id: 'cComp', type: RenderDirectiveMetadata.COMPONENT_TYPE, selector: 'c'});
@@ -1,143 +0,0 @@
import {
AsyncTestCompleter,
beforeEach,
ddescribe,
xdescribe,
describe,
el,
dispatchEvent,
expect,
iit,
inject,
beforeEachBindings,
it,
xit,
SpyObject,
proxy
} from 'angular2/test_lib';
import {isBlank} from 'angular2/src/core/facade/lang';
import {ListWrapper} from 'angular2/src/core/facade/collection';
import {DomProtoView} from 'angular2/src/core/render/dom/view/proto_view';
import {DomElementBinder} from 'angular2/src/core/render/dom/view/element_binder';
import {DomView} from 'angular2/src/core/render/dom/view/view';
import {DOM} from 'angular2/src/core/dom/dom_adapter';
import {TemplateCloner} from 'angular2/src/core/render/dom/template_cloner';
export function main() {
describe('DomView', () => {
function createProtoView(binders = null) {
if (isBlank(binders)) {
binders = [];
}
var rootEl = DOM.createTemplate('<div></div>');
return DomProtoView.create(new TemplateCloner(-1), null, <Element>rootEl, null, [1], [],
binders, null);
}
function createElementBinder() { return new DomElementBinder({textNodeIndices: []}); }
function createView(pv = null, boundElementCount = 0) {
if (isBlank(pv)) {
var elementBinders = ListWrapper.createFixedSize(boundElementCount);
for (var i = 0; i < boundElementCount; i++) {
elementBinders[i] = createElementBinder();
}
pv = createProtoView(elementBinders);
}
var root = el('<div><div></div></div>');
var boundElements = [];
for (var i = 0; i < boundElementCount; i++) {
boundElements.push(el('<span></span'));
}
return new DomView(pv, [DOM.childNodes(root)[0]], boundElements);
}
describe('setElementProperty', () => {
var el, view;
beforeEach(() => {
view = createView(null, 1);
el = view.boundElements[0];
});
it('should update the property value', () => {
view.setElementProperty(0, 'title', 'Hello');
expect(el.title).toEqual('Hello');
});
});
describe('setElementAttribute', () => {
var el, view;
beforeEach(() => {
view = createView(null, 1);
el = view.boundElements[0];
});
it('should update and remove an attribute', () => {
view.setElementAttribute(0, 'role', 'button');
expect(DOM.getAttribute(el, 'role')).toEqual('button');
view.setElementAttribute(0, 'role', null);
expect(DOM.getAttribute(el, 'role')).toEqual(null);
});
it('should de-normalize attribute names', () => {
view.setElementAttribute(0, 'ariaLabel', 'fancy button');
expect(DOM.getAttribute(el, 'aria-label')).toEqual('fancy button');
});
});
describe('setElementClass', () => {
var el, view;
beforeEach(() => {
view = createView(null, 1);
el = view.boundElements[0];
});
it('should set and remove a class', () => {
view.setElementClass(0, 'active', true);
expect(DOM.hasClass(el, 'active')).toEqual(true);
view.setElementClass(0, 'active', false);
expect(DOM.hasClass(el, 'active')).toEqual(false);
});
it('should not de-normalize class names', () => {
view.setElementClass(0, 'veryActive', true);
view.setElementClass(0, 'very-active', true);
expect(DOM.hasClass(el, 'veryActive')).toEqual(true);
expect(DOM.hasClass(el, 'very-active')).toEqual(true);
view.setElementClass(0, 'veryActive', false);
view.setElementClass(0, 'very-active', false);
expect(DOM.hasClass(el, 'veryActive')).toEqual(false);
expect(DOM.hasClass(el, 'very-active')).toEqual(false);
});
});
describe('setElementStyle', () => {
var el, view;
beforeEach(() => {
view = createView(null, 1);
el = view.boundElements[0];
});
it('should set and remove styles', () => {
view.setElementStyle(0, 'width', '40px');
expect(DOM.getStyle(el, 'width')).toEqual('40px');
view.setElementStyle(0, 'width', null);
expect(DOM.getStyle(el, 'width')).toEqual('');
});
it('should de-normalize style names', () => {
view.setElementStyle(0, 'maxWidth', '40px');
expect(DOM.getStyle(el, 'max-width')).toEqual('40px');
view.setElementStyle(0, 'maxWidth', null);
expect(DOM.getStyle(el, 'max-width')).toEqual('');
});
});
});
}
-15
View File
@@ -21,21 +21,11 @@ class SpyDependencyProvider extends SpyObject implements DependencyProvider {
noSuchMethod(m) => super.noSuchMethod(m);
}
@proxy
class SpyChangeDetection extends SpyObject implements ChangeDetection {
noSuchMethod(m) => super.noSuchMethod(m);
}
@proxy
class SpyChangeDetector extends SpyObject implements ChangeDetector {
noSuchMethod(m) => super.noSuchMethod(m);
}
@proxy
class SpyProtoChangeDetector extends SpyObject implements ProtoChangeDetector {
noSuchMethod(m) => super.noSuchMethod(m);
}
@proxy
class SpyChangeDispatcher extends SpyObject implements ChangeDispatcher {
noSuchMethod(m) => super.noSuchMethod(m);
@@ -52,11 +42,6 @@ class SpyInjector extends SpyObject implements Injector {
noSuchMethod(m) => super.noSuchMethod(m);
}
@proxy
class SpyRenderCompiler extends SpyObject implements RenderCompiler {
noSuchMethod(m) => super.noSuchMethod(m);
}
@proxy
class SpyDirectiveResolver extends SpyObject implements DirectiveResolver {
noSuchMethod(m) => super.noSuchMethod(m);
+1 -14
View File
@@ -1,12 +1,11 @@
import {
ChangeDetection,
ChangeDetector,
ChangeDetectorRef,
ProtoChangeDetector,
DynamicChangeDetector
} from 'angular2/src/core/change_detection/change_detection';
import {RenderCompiler, Renderer, RenderEventDispatcher} from 'angular2/src/core/render/api';
import {Renderer, RenderEventDispatcher} from 'angular2/src/core/render/api';
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
import {AppView} from 'angular2/src/core/compiler/view';
@@ -29,26 +28,14 @@ import {SpyObject, proxy} from 'angular2/test_lib';
export class SpyDependencyProvider extends SpyObject {}
export class SpyChangeDetection extends SpyObject {
constructor() { super(ChangeDetection); }
}
export class SpyChangeDetector extends SpyObject {
constructor() { super(DynamicChangeDetector); }
}
export class SpyProtoChangeDetector extends SpyObject {
constructor() { super(DynamicChangeDetector); }
}
export class SpyChangeDispatcher extends SpyObject {}
export class SpyIterableDifferFactory extends SpyObject {}
export class SpyRenderCompiler extends SpyObject {
constructor() { super(RenderCompiler); }
}
export class SpyDirectiveResolver extends SpyObject {
constructor() { super(DirectiveResolver); }
}
+2 -68
View File
@@ -188,8 +188,6 @@ var NG_API = [
'ComponentRef.instance=',
'ComponentRef.location',
'ComponentRef.location=',
'ComponentUrlMapper',
'ComponentUrlMapper.getUrl()',
'ContentChild',
'ContentChild.descendants',
'ContentChild.first',
@@ -400,8 +398,6 @@ var NG_API = [
'ElementRef.nativeElement',
'ElementRef.parentView',
'ElementRef.parentView=',
'ElementRef.renderBoundElementIndex',
'ElementRef.renderBoundElementIndex=',
'ElementRef.renderView',
'ElementRef.renderView=',
'Output',
@@ -534,7 +530,6 @@ var NG_API = [
'LifeCycle.tick()',
'LowerCasePipe',
'LowerCasePipe.transform()',
'MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE',
'NG_VALIDATORS',
'NgClass',
'NgClass.doCheck()',
@@ -838,52 +833,6 @@ var NG_API = [
'QueryMetadata.selector',
'QueryMetadata.token',
'QueryMetadata.varBindings',
'RenderDirectiveMetadata#COMPONENT_TYPE',
'RenderDirectiveMetadata#DIRECTIVE_TYPE',
'RenderDirectiveMetadata#create()',
'RenderDirectiveMetadata',
'RenderDirectiveMetadata.callAfterContentChecked',
'RenderDirectiveMetadata.callAfterContentChecked=',
'RenderDirectiveMetadata.callAfterContentInit',
'RenderDirectiveMetadata.callAfterContentInit=',
'RenderDirectiveMetadata.callAfterViewChecked',
'RenderDirectiveMetadata.callAfterViewChecked=',
'RenderDirectiveMetadata.callAfterViewInit',
'RenderDirectiveMetadata.callAfterViewInit=',
'RenderDirectiveMetadata.callDoCheck',
'RenderDirectiveMetadata.callDoCheck=',
'RenderDirectiveMetadata.callOnChanges',
'RenderDirectiveMetadata.callOnChanges=',
'RenderDirectiveMetadata.callOnDestroy',
'RenderDirectiveMetadata.callOnDestroy=',
'RenderDirectiveMetadata.callOnInit',
'RenderDirectiveMetadata.callOnInit=',
'RenderDirectiveMetadata.changeDetection',
'RenderDirectiveMetadata.changeDetection=',
'RenderDirectiveMetadata.compileChildren',
'RenderDirectiveMetadata.compileChildren=',
'RenderDirectiveMetadata.outputs',
'RenderDirectiveMetadata.outputs=',
'RenderDirectiveMetadata.exportAs',
'RenderDirectiveMetadata.exportAs=',
'RenderDirectiveMetadata.hostAttributes',
'RenderDirectiveMetadata.hostAttributes=',
'RenderDirectiveMetadata.hostListeners',
'RenderDirectiveMetadata.hostListeners=',
'RenderDirectiveMetadata.hostProperties',
'RenderDirectiveMetadata.hostProperties=',
'RenderDirectiveMetadata.id',
'RenderDirectiveMetadata.id=',
'RenderDirectiveMetadata.inputs',
'RenderDirectiveMetadata.inputs=',
'RenderDirectiveMetadata.queries',
'RenderDirectiveMetadata.queries=',
'RenderDirectiveMetadata.readAttributes',
'RenderDirectiveMetadata.readAttributes=',
'RenderDirectiveMetadata.selector',
'RenderDirectiveMetadata.selector=',
'RenderDirectiveMetadata.type',
'RenderDirectiveMetadata.type=',
'RenderFragmentRef',
'RenderProtoViewRef',
'RenderViewRef',
@@ -1026,21 +975,6 @@ var NG_API = [
'ViewContainerRef.remove()',
'ViewContainerRef.viewManager',
'ViewContainerRef.viewManager=',
'ViewDefinition',
'ViewDefinition.componentId',
'ViewDefinition.componentId=',
'ViewDefinition.directives',
'ViewDefinition.directives=',
'ViewDefinition.encapsulation',
'ViewDefinition.encapsulation=',
'ViewDefinition.styleAbsUrls',
'ViewDefinition.styleAbsUrls=',
'ViewDefinition.styles',
'ViewDefinition.styles=',
'ViewDefinition.template',
'ViewDefinition.template=',
'ViewDefinition.templateAbsUrl',
'ViewDefinition.templateAbsUrl=',
'ViewEncapsulation#Emulated',
'ViewEncapsulation#Native',
'ViewEncapsulation#None',
@@ -1157,8 +1091,8 @@ var NG_API = [
'{RenderTextCmd}.value',
'{RenderTextCmd}.value=',
'{RenderElementRef}',
'{RenderElementRef}.renderBoundElementIndex',
'{RenderElementRef}.renderBoundElementIndex=',
'{RenderElementRef}.boundElementIndex',
'{RenderElementRef}.boundElementIndex=',
'{RenderElementRef}.renderView',
'{RenderElementRef}.renderView=',
'{RenderEventDispatcher}',
@@ -8,6 +8,7 @@ import "package:angular2/test_lib.dart"
inject,
describe,
it,
iit,
expect,
beforeEach,
createTestInjector,