2016-06-23 09:47:54 -07:00
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
2016-06-08 16:38:52 -07:00
import { CompileDiDependencyMetadata , CompileDirectiveMetadata , CompilePipeMetadata , CompileProviderMetadata , CompileQueryMetadata , CompileTemplateMetadata , CompileTokenMetadata , CompileTypeMetadata } from '@angular/compiler/src/compile_metadata' ;
2016-05-26 14:35:27 -07:00
import { DomElementSchemaRegistry } from '@angular/compiler/src/schema/dom_element_schema_registry' ;
2016-06-08 16:38:52 -07:00
import { ElementSchemaRegistry } from '@angular/compiler/src/schema/element_schema_registry' ;
2016-07-21 11:41:25 -07:00
import { AttrAst , BoundDirectivePropertyAst , BoundElementPropertyAst , BoundEventAst , BoundTextAst , DirectiveAst , ElementAst , EmbeddedTemplateAst , NgContentAst , PropertyBindingType , ProviderAstType , ReferenceAst , TemplateAst , TemplateAstVisitor , TextAst , VariableAst , templateVisitAll } from '@angular/compiler/src/template_parser/template_ast' ;
import { TEMPLATE_TRANSFORMS , TemplateParser , splitClasses } from '@angular/compiler/src/template_parser/template_parser' ;
2016-08-17 16:37:31 -07:00
import { TEST_COMPILER_PROVIDERS } from '@angular/compiler/testing/test_bindings' ;
2016-08-24 17:39:49 -07:00
import { SchemaMetadata , SecurityContext , Type } from '@angular/core' ;
2016-06-08 16:38:52 -07:00
import { Console } from '@angular/core/src/console' ;
2016-07-28 04:54:49 -07:00
import { TestBed } from '@angular/core/testing' ;
2016-06-08 16:38:52 -07:00
import { afterEach , beforeEach , beforeEachProviders , ddescribe , describe , expect , iit , inject , it , xit } from '@angular/core/testing/testing_internal' ;
2015-08-27 16:29:02 -07:00
2016-08-24 17:39:49 -07:00
import { Identifiers , identifierToken , resolveIdentifierToken } from '../../src/identifiers' ;
2016-08-01 12:19:09 -07:00
import { DEFAULT_INTERPOLATION_CONFIG , InterpolationConfig } from '../../src/ml_parser/interpolation_config' ;
2016-08-30 18:07:40 -07:00
import { MockSchemaRegistry } from '../../testing/index' ;
2016-07-21 11:41:25 -07:00
import { unparse } from '../expression_parser/unparser' ;
2015-08-25 15:36:02 -07:00
2016-08-23 10:52:40 -07:00
const someModuleUrl = 'package:someModule' ;
2016-01-06 14:13:44 -08:00
2016-08-23 10:52:40 -07:00
const MOCK_SCHEMA_REGISTRY = [{
2016-06-08 16:38:52 -07:00
provide : ElementSchemaRegistry ,
2016-08-23 10:52:40 -07:00
useValue : new MockSchemaRegistry (
{ 'invalidProp' : false }, { 'mappedAttr' : 'mappedProp' }, { 'unknown' : false , 'un-known' : false }),
2016-06-08 16:38:52 -07:00
}];
2016-02-10 16:54:32 -08:00
2015-08-25 15:36:02 -07:00
export function main() {
2016-06-22 17:43:27 +02:00
var ngIf : CompileDirectiveMetadata ;
2016-05-26 14:35:27 -07:00
var parse :
( template : string , directives : CompileDirectiveMetadata [], pipes? : CompilePipeMetadata []) =>
TemplateAst [];
2016-04-25 19:52:24 -07:00
var console : ArrayConsole ;
2015-09-14 15:59:09 -07:00
2016-02-10 16:54:32 -08:00
function commonBeforeEach() {
2016-07-04 09:37:30 -07:00
beforeEach (() => {
2016-04-25 19:52:24 -07:00
console = new ArrayConsole ();
2016-07-28 04:54:49 -07:00
TestBed . configureCompiler ({ providers : [{ provide : Console , useValue : console }]});
2016-04-25 19:52:24 -07:00
});
2016-06-09 11:04:15 -07:00
beforeEach ( inject ([ TemplateParser ], ( parser : TemplateParser ) => {
2016-01-06 14:13:44 -08:00
var component = CompileDirectiveMetadata . create ({
selector : 'root' ,
2016-08-24 17:39:49 -07:00
type : new CompileTypeMetadata (
2016-08-29 08:52:25 -07:00
{ moduleUrl : someModuleUrl , name : 'Root' , reference : {} as Type < any >}),
2016-01-06 14:13:44 -08:00
isComponent : true
});
ngIf = CompileDirectiveMetadata . create ({
selector : '[ngIf]' ,
2016-08-24 17:39:49 -07:00
type : new CompileTypeMetadata (
2016-08-29 08:52:25 -07:00
{ moduleUrl : someModuleUrl , name : 'NgIf' , reference : {} as Type < any >}),
2016-01-06 14:13:44 -08:00
inputs : [ 'ngIf' ]
});
2016-02-10 16:54:32 -08:00
2016-06-08 16:38:52 -07:00
parse =
( template : string , directives : CompileDirectiveMetadata [],
2016-07-25 03:02:57 -07:00
pipes : CompilePipeMetadata [] = null , schemas : SchemaMetadata [] = []) : TemplateAst [] => {
2016-06-08 16:38:52 -07:00
if ( pipes === null ) {
pipes = [];
}
2016-07-25 03:02:57 -07:00
return parser . parse ( component , template , directives , pipes , schemas , 'TestComp' );
2016-06-08 16:38:52 -07:00
};
2015-09-14 15:59:09 -07:00
}));
2016-02-10 16:54:32 -08:00
}
2015-08-25 15:36:02 -07:00
2016-02-10 16:54:32 -08:00
describe ( 'TemplateParser template transform' , () => {
2016-07-28 04:54:49 -07:00
beforeEach (() => { TestBed . configureCompiler ({ providers : TEST_COMPILER_PROVIDERS }); });
2015-08-25 15:36:02 -07:00
2016-07-04 09:37:30 -07:00
beforeEach (() => {
2016-07-28 04:54:49 -07:00
TestBed . configureCompiler ({
2016-07-04 09:37:30 -07:00
providers :
[{ provide : TEMPLATE_TRANSFORMS , useValue : new FooAstTransformer (), multi : true }]
});
});
2015-11-19 10:51:16 -08:00
2016-02-10 16:54:32 -08:00
describe ( 'single' , () => {
commonBeforeEach ();
2016-06-08 16:38:52 -07:00
it ( 'should transform TemplateAST' , () => {
expect ( humanizeTplAst ( parse ( '<div>' , []))). toEqual ([[ ElementAst , 'foo' ]]);
});
2015-11-19 10:51:16 -08:00
});
2016-02-10 16:54:32 -08:00
describe ( 'multiple' , () => {
2016-07-04 09:37:30 -07:00
beforeEach (() => {
2016-07-28 04:54:49 -07:00
TestBed . configureCompiler ({
2016-07-04 09:37:30 -07:00
providers :
[{ provide : TEMPLATE_TRANSFORMS , useValue : new BarAstTransformer (), multi : true }]
});
});
2016-02-10 16:54:32 -08:00
commonBeforeEach ();
2016-06-08 16:38:52 -07:00
it ( 'should compose transformers' , () => {
expect ( humanizeTplAst ( parse ( '<div>' , []))). toEqual ([[ ElementAst , 'bar' ]]);
});
2016-02-10 16:54:32 -08:00
});
});
2016-05-26 14:35:27 -07:00
describe ( 'TemplateParser Security' , () => {
// Semi-integration test to make sure TemplateParser properly sets the security context.
// Uses the actual DomElementSchemaRegistry.
2016-07-04 09:37:30 -07:00
beforeEach (() => {
2016-07-28 04:54:49 -07:00
TestBed . configureCompiler ({
2016-07-04 09:37:30 -07:00
providers : [
TEST_COMPILER_PROVIDERS ,
{ provide : ElementSchemaRegistry , useClass : DomElementSchemaRegistry }
]
});
});
2016-05-26 14:35:27 -07:00
commonBeforeEach ();
describe ( 'security context' , () => {
function secContext ( tpl : string ) : SecurityContext {
let ast = parse ( tpl , []);
let propBinding = (< ElementAst > ast [ 0 ]). inputs [ 0 ];
return propBinding . securityContext ;
}
it ( 'should set for properties' , () => {
expect ( secContext ( '<div [title]="v">' )). toBe ( SecurityContext . NONE );
expect ( secContext ( '<div [innerHTML]="v">' )). toBe ( SecurityContext . HTML );
});
2016-06-08 16:38:52 -07:00
it ( 'should set for property value bindings' ,
() => { expect ( secContext ( '<div innerHTML="{{v}}">' )). toBe ( SecurityContext . HTML ); });
2016-05-26 14:35:27 -07:00
it ( 'should set for attributes' , () => {
expect ( secContext ( '<a [attr.href]="v">' )). toBe ( SecurityContext . URL );
// NB: attributes below need to change case.
expect ( secContext ( '<a [attr.innerHtml]="v">' )). toBe ( SecurityContext . HTML );
expect ( secContext ( '<a [attr.formaction]="v">' )). toBe ( SecurityContext . URL );
});
it ( 'should set for style' , () => {
expect ( secContext ( '<a [style.backgroundColor]="v">' )). toBe ( SecurityContext . STYLE );
});
});
});
2016-02-10 16:54:32 -08:00
describe ( 'TemplateParser' , () => {
2016-07-04 09:37:30 -07:00
beforeEach (() => {
2016-07-28 04:54:49 -07:00
TestBed . configureCompiler ({ providers : [ TEST_COMPILER_PROVIDERS , MOCK_SCHEMA_REGISTRY ]});
2016-07-04 09:37:30 -07:00
});
2016-02-10 16:54:32 -08:00
commonBeforeEach ();
2015-08-25 15:36:02 -07:00
describe ( 'parse' , () => {
describe ( 'nodes without bindings' , () => {
2016-06-08 16:38:52 -07:00
it ( 'should parse text nodes' , () => {
expect ( humanizeTplAst ( parse ( 'a' , []))). toEqual ([[ TextAst , 'a' ]]);
});
2015-08-25 15:36:02 -07:00
it ( 'should parse elements with attributes' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<div a=b>' , [
]))). toEqual ([[ ElementAst , 'div' ], [ AttrAst , 'a' , 'b' ]]);
2015-08-25 15:36:02 -07:00
});
});
it ( 'should parse ngContent' , () => {
var parsed = parse ( '<ng-content select="a">' , []);
2015-11-10 15:56:25 -08:00
expect ( humanizeTplAst ( parsed )). toEqual ([[ NgContentAst ]]);
2015-08-25 15:36:02 -07:00
});
2015-12-09 10:47:10 -08:00
it ( 'should parse ngContent regardless the namespace' , () => {
var parsed = parse ( '<svg><ng-content></ng-content></svg>' , []);
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parsed )). toEqual ([
[ ElementAst , ':svg:svg' ],
[ NgContentAst ],
]);
2015-12-09 10:47:10 -08:00
});
2015-08-25 15:36:02 -07:00
it ( 'should parse bound text nodes' , () => {
2015-11-10 15:56:25 -08:00
expect ( humanizeTplAst ( parse ( '{{a}}' , []))). toEqual ([[ BoundTextAst , '{{ a }}' ]]);
2015-08-25 15:36:02 -07:00
});
2016-06-20 09:52:41 -07:00
it ( 'should parse with custom interpolation config' ,
inject ([ TemplateParser ], ( parser : TemplateParser ) => {
const component = CompileDirectiveMetadata . create ({
selector : 'test' ,
2016-08-24 17:39:49 -07:00
type : new CompileTypeMetadata (
2016-08-29 08:52:25 -07:00
{ moduleUrl : someModuleUrl , name : 'Test' , reference : {} as Type < any >}),
2016-06-20 09:52:41 -07:00
isComponent : true ,
template : new CompileTemplateMetadata ({ interpolation : [ '{%' , '%}' ]})
});
2016-07-25 03:02:57 -07:00
expect ( humanizeTplAst ( parser . parse ( component , '{%a%}' , [], [], [], 'TestComp' ), {
2016-06-20 09:52:41 -07:00
start : '{%' ,
end : '%}'
})). toEqual ([[ BoundTextAst , '{% a %}' ]]);
}));
2015-08-27 16:29:02 -07:00
describe ( 'bound properties' , () => {
2015-11-10 15:56:25 -08:00
it ( 'should parse mixed case bound properties' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<div [someProp]="v">' , []))). toEqual ([
[ ElementAst , 'div' ],
[ BoundElementPropertyAst , PropertyBindingType . Property , 'someProp' , 'v' , null ]
]);
2015-11-10 15:56:25 -08:00
});
2015-11-23 16:02:19 -08:00
it ( 'should parse dash case bound properties' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<div [some-prop]="v">' , []))). toEqual ([
[ ElementAst , 'div' ],
[ BoundElementPropertyAst , PropertyBindingType . Property , 'some-prop' , 'v' , null ]
]);
2015-08-27 16:29:02 -07:00
});
it ( 'should normalize property names via the element schema' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<div [mappedAttr]="v">' , []))). toEqual ([
[ ElementAst , 'div' ],
[ BoundElementPropertyAst , PropertyBindingType . Property , 'mappedProp' , 'v' , null ]
]);
2015-08-27 16:29:02 -07:00
});
2015-11-10 15:56:25 -08:00
it ( 'should parse mixed case bound attributes' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<div [attr.someAttr]="v">' , []))). toEqual ([
[ ElementAst , 'div' ],
[ BoundElementPropertyAst , PropertyBindingType . Attribute , 'someAttr' , 'v' , null ]
]);
2015-11-10 15:56:25 -08:00
});
2015-08-27 16:29:02 -07:00
it ( 'should parse and dash case bound classes' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<div [class.some-class]="v">' , []))). toEqual ([
[ ElementAst , 'div' ],
[ BoundElementPropertyAst , PropertyBindingType . Class , 'some-class' , 'v' , null ]
]);
2015-11-10 15:56:25 -08:00
});
2015-11-10 15:56:25 -08:00
it ( 'should parse mixed case bound classes' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<div [class.someClass]="v">' , []))). toEqual ([
[ ElementAst , 'div' ],
[ BoundElementPropertyAst , PropertyBindingType . Class , 'someClass' , 'v' , null ]
]);
2015-11-10 15:56:25 -08:00
});
2015-11-23 16:02:19 -08:00
it ( 'should parse mixed case bound styles' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<div [style.someStyle]="v">' , []))). toEqual ([
[ ElementAst , 'div' ],
[ BoundElementPropertyAst , PropertyBindingType . Style , 'someStyle' , 'v' , null ]
]);
2015-11-10 15:56:25 -08:00
});
2015-11-23 16:02:19 -08:00
it ( 'should report invalid prefixes' , () => {
expect (() => parse ( '<p [atTr.foo]>' , []))
. toThrowError (
`Template parse errors: \ nInvalid property name 'atTr.foo' ("<p [ERROR ->][atTr.foo]>"): TestComp@0:3` );
expect (() => parse ( '<p [sTyle.foo]>' , []))
. toThrowError (
`Template parse errors: \ nInvalid property name 'sTyle.foo' ("<p [ERROR ->][sTyle.foo]>"): TestComp@0:3` );
expect (() => parse ( '<p [Class.foo]>' , []))
. toThrowError (
`Template parse errors: \ nInvalid property name 'Class.foo' ("<p [ERROR ->][Class.foo]>"): TestComp@0:3` );
expect (() => parse ( '<p [bar.foo]>' , []))
. toThrowError (
`Template parse errors: \ nInvalid property name 'bar.foo' ("<p [ERROR ->][bar.foo]>"): TestComp@0:3` );
2015-08-27 16:29:02 -07:00
});
2015-08-25 15:36:02 -07:00
2016-07-29 20:16:38 -07:00
describe ( 'errors' , () => {
2016-08-23 10:52:40 -07:00
it ( 'should throw error when binding to an unknown property' , () => {
2016-07-29 20:16:38 -07:00
expect (() => parse ( '<my-component [invalidProp]="bar"></my-component>' , []))
. toThrowError ( `Template parse errors:
Can't bind to 'invalidProp' since it isn't a known property of 'my-component'.
1. If 'my-component' is an Angular component and it has 'invalidProp' input, then verify that it is part of this module.
2016-09-18 18:03:43 -05:00
2. If 'my-component' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '@NgModule.schemas' of this component to suppress this message.
2016-07-29 20:16:38 -07:00
("<my-component [ERROR ->][invalidProp]="bar"></my-component>"): TestComp@0:14` );
});
2016-08-23 10:52:40 -07:00
it ( 'should throw error when binding to an unknown element w/o bindings' , () => {
expect (() => parse ( '<unknown></unknown>' , [])). toThrowError ( `Template parse errors:
'unknown' is not a known element:
1. If 'unknown' is an Angular component, then verify that it is part of this module.
2016-09-18 18:03:43 -05:00
2. If 'unknown' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '@NgModule.schemas' of this component to suppress this message. ("[ERROR ->]<unknown></unknown>"): TestComp@0:0` );
2016-08-23 10:52:40 -07:00
});
it ( 'should throw error when binding to an unknown custom element w/o bindings' , () => {
expect (() => parse ( '<un-known></un-known>' , [])). toThrowError ( `Template parse errors:
'un-known' is not a known element:
1. If 'un-known' is an Angular component, then verify that it is part of this module.
2016-09-18 18:03:43 -05:00
2. If 'un-known' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '@NgModule.schemas' of this component to suppress this message. ("[ERROR ->]<un-known></un-known>"): TestComp@0:0` );
2016-08-23 10:52:40 -07:00
});
2016-07-29 20:16:38 -07:00
});
2015-08-25 15:36:02 -07:00
it ( 'should parse bound properties via [...] and not report them as attributes' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<div [prop]="v">' , []))). toEqual ([
[ ElementAst , 'div' ],
[ BoundElementPropertyAst , PropertyBindingType . Property , 'prop' , 'v' , null ]
]);
2015-08-25 15:36:02 -07:00
});
it ( 'should parse bound properties via bind- and not report them as attributes' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<div bind-prop="v">' , []))). toEqual ([
[ ElementAst , 'div' ],
[ BoundElementPropertyAst , PropertyBindingType . Property , 'prop' , 'v' , null ]
]);
2015-08-25 15:36:02 -07:00
});
it ( 'should parse bound properties via {{...}} and not report them as attributes' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<div prop="{{v}}">' , []))). toEqual ([
[ ElementAst , 'div' ],
[ BoundElementPropertyAst , PropertyBindingType . Property , 'prop' , '{{ v }}' , null ]
]);
2015-08-27 16:29:02 -07:00
});
2016-08-16 14:09:21 -07:00
it ( 'should parse bound properties via bind-animate- and not report them as animation properties' ,
2016-07-07 12:13:52 -07:00
() => {
2016-08-16 14:09:21 -07:00
expect ( humanizeTplAst ( parse ( '<div bind-animate-something="value2">' , []))). toEqual ([
2016-07-07 12:13:52 -07:00
[ ElementAst , 'div' ],
[
BoundElementPropertyAst , PropertyBindingType . Animation , 'something' , 'value2' , null
]
]);
});
2016-08-16 14:09:21 -07:00
it ( 'should throw an error when parsing detects non-bound properties via @ that contain a value' ,
2016-07-08 17:11:12 -07:00
() => {
2016-08-16 14:09:21 -07:00
expect (() => { parse ( '<div @something="value2">' , []); })
. toThrowError (
/Assigning animation triggers via @prop="exp" attributes with an expression is invalid. Use property bindings \(e.g. \[@prop\]="exp"\) or use an attribute without a value \(e.g. @prop\) instead. \("<div \[ERROR ->\]@something="value2">"\): TestComp@0:5/ );
});
2016-07-08 17:11:12 -07:00
2016-08-08 17:08:53 -07:00
it ( 'should not issue a warning when host attributes contain a valid property-bound animation trigger' ,
() => {
var dirA = CompileDirectiveMetadata . create ({
selector : 'div' ,
2016-08-24 17:39:49 -07:00
type : new CompileTypeMetadata (
2016-08-29 08:52:25 -07:00
{ moduleUrl : someModuleUrl , name : 'DirA' , reference : {} as Type < any >}),
2016-08-08 17:08:53 -07:00
host : { '[@prop]' : 'expr' }
});
humanizeTplAst ( parse ( '<div></div>' , [ dirA ]));
expect ( console . warnings . length ). toEqual ( 0 );
});
2016-08-26 19:29:53 +02:00
it ( 'should throw descriptive error when a host binding is not a string expression' , () => {
var dirA = CompileDirectiveMetadata . create ({
selector : 'broken' ,
2016-08-24 17:39:49 -07:00
type : new CompileTypeMetadata (
2016-08-29 08:52:25 -07:00
{ moduleUrl : someModuleUrl , name : 'DirA' , reference : {} as Type < any >}),
2016-08-26 19:29:53 +02:00
host : { '[class.foo]' : null }
});
expect (() => { parse ( '<broken></broken>' , [ dirA ]); })
. toThrowError (
`Template parse errors: \ nValue of the host property binding "class.foo" needs to be a string representing an expression but got "null" (object) ("[ERROR ->]<broken></broken>"): TestComp@0:0, Directive DirA` );
});
it ( 'should throw descriptive error when a host event is not a string expression' , () => {
var dirA = CompileDirectiveMetadata . create ({
selector : 'broken' ,
2016-08-24 17:39:49 -07:00
type : new CompileTypeMetadata (
2016-08-29 08:52:25 -07:00
{ moduleUrl : someModuleUrl , name : 'DirA' , reference : {} as Type < any >}),
2016-08-26 19:29:53 +02:00
host : { '(click)' : null }
});
expect (() => { parse ( '<broken></broken>' , [ dirA ]); })
. toThrowError (
`Template parse errors: \ nValue of the host listener "click" needs to be a string representing an expression but got "null" (object) ("[ERROR ->]<broken></broken>"): TestComp@0:0, Directive DirA` );
});
2016-07-07 12:13:52 -07:00
it ( 'should not issue a warning when an animation property is bound without an expression' ,
() => {
humanizeTplAst ( parse ( '<div @something>' , []));
expect ( console . warnings . length ). toEqual ( 0 );
});
it ( 'should parse bound properties via [@] and not report them as attributes' , () => {
expect ( humanizeTplAst ( parse ( '<div [@something]="value2">' , []))). toEqual ([
[ ElementAst , 'div' ],
[ BoundElementPropertyAst , PropertyBindingType . Animation , 'something' , 'value2' , null ]
]);
});
2015-08-27 16:29:02 -07:00
});
describe ( 'events' , () => {
it ( 'should parse bound events with a target' , () => {
2016-08-01 11:24:49 -07:00
expect ( humanizeTplAst ( parse ( '<div (window:event)="v">' , []))). toEqual ([
[ ElementAst , 'div' ],
[ BoundEventAst , 'event' , 'window' , 'v' ],
]);
});
it ( 'should report an error on empty expression' , () => {
expect (() => parse ( '<div (event)="">' , []))
. toThrowError ( /Empty expressions are not allowed/ );
expect (() => parse ( '<div (event)=" ">' , []))
. toThrowError ( /Empty expressions are not allowed/ );
2015-08-25 15:36:02 -07:00
});
it ( 'should parse bound events via (...) and not report them as attributes' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<div (event)="v">' , [
]))). toEqual ([[ ElementAst , 'div' ], [ BoundEventAst , 'event' , null , 'v' ]]);
2015-08-25 15:36:02 -07:00
});
2015-11-23 16:02:19 -08:00
it ( 'should parse event names case sensitive' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<div (some-event)="v">' , [
]))). toEqual ([[ ElementAst , 'div' ], [ BoundEventAst , 'some-event' , null , 'v' ]]);
expect ( humanizeTplAst ( parse ( '<div (someEvent)="v">' , [
]))). toEqual ([[ ElementAst , 'div' ], [ BoundEventAst , 'someEvent' , null , 'v' ]]);
2015-08-25 15:36:02 -07:00
});
it ( 'should parse bound events via on- and not report them as attributes' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<div on-event="v">' , [
]))). toEqual ([[ ElementAst , 'div' ], [ BoundEventAst , 'event' , null , 'v' ]]);
2015-08-25 15:36:02 -07:00
});
2015-10-13 17:43:15 -07:00
it ( 'should allow events on explicit embedded templates that are emitted by a directive' ,
() => {
var dirA = CompileDirectiveMetadata . create ({
selector : 'template' ,
outputs : [ 'e' ],
2016-08-24 17:39:49 -07:00
type : new CompileTypeMetadata (
2016-08-29 08:52:25 -07:00
{ moduleUrl : someModuleUrl , name : 'DirA' , reference : {} as Type < any >})
2015-10-13 17:43:15 -07:00
});
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<template (e)="f"></template>' , [ dirA ]))). toEqual ([
[ EmbeddedTemplateAst ],
[ BoundEventAst , 'e' , null , 'f' ],
[ DirectiveAst , dirA ],
]);
2015-10-13 17:43:15 -07:00
});
2015-08-27 16:29:02 -07:00
});
describe ( 'bindon' , () => {
2015-08-25 15:36:02 -07:00
it ( 'should parse bound events and properties via [(...)] and not report them as attributes' ,
() => {
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<div [(prop)]="v">' , []))). toEqual ([
[ ElementAst , 'div' ],
[ BoundElementPropertyAst , PropertyBindingType . Property , 'prop' , 'v' , null ],
[ BoundEventAst , 'propChange' , null , 'v = $event' ]
]);
2015-08-25 15:36:02 -07:00
});
it ( 'should parse bound events and properties via bindon- and not report them as attributes' ,
() => {
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<div bindon-prop="v">' , []))). toEqual ([
[ ElementAst , 'div' ],
[ BoundElementPropertyAst , PropertyBindingType . Property , 'prop' , 'v' , null ],
[ BoundEventAst , 'propChange' , null , 'v = $event' ]
]);
2015-08-25 15:36:02 -07:00
});
2015-08-27 16:29:02 -07:00
});
2015-08-25 15:36:02 -07:00
describe ( 'directives' , () => {
2016-04-25 19:52:24 -07:00
it ( 'should order directives by the directives array in the View and match them only once' ,
2015-09-18 10:33:23 -07:00
() => {
2016-01-06 14:13:44 -08:00
var dirA = CompileDirectiveMetadata . create ({
selector : '[a]' ,
2016-08-24 17:39:49 -07:00
type : new CompileTypeMetadata (
2016-08-29 08:52:25 -07:00
{ moduleUrl : someModuleUrl , name : 'DirA' , reference : {} as Type < any >})
2016-01-06 14:13:44 -08:00
});
var dirB = CompileDirectiveMetadata . create ({
selector : '[b]' ,
2016-08-24 17:39:49 -07:00
type : new CompileTypeMetadata (
2016-08-29 08:52:25 -07:00
{ moduleUrl : someModuleUrl , name : 'DirB' , reference : {} as Type < any >})
2016-01-06 14:13:44 -08:00
});
var dirC = CompileDirectiveMetadata . create ({
selector : '[c]' ,
2016-08-24 17:39:49 -07:00
type : new CompileTypeMetadata (
2016-08-29 08:52:25 -07:00
{ moduleUrl : someModuleUrl , name : 'DirC' , reference : {} as Type < any >})
2016-01-06 14:13:44 -08:00
});
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<div a c b a b>' , [ dirA , dirB , dirC ]))). toEqual ([
[ ElementAst , 'div' ], [ AttrAst , 'a' , '' ], [ AttrAst , 'c' , '' ], [ AttrAst , 'b' , '' ],
[ AttrAst , 'a' , '' ], [ AttrAst , 'b' , '' ], [ DirectiveAst , dirA ], [ DirectiveAst , dirB ],
[ DirectiveAst , dirC ]
]);
2015-09-18 10:33:23 -07:00
});
2015-08-25 15:36:02 -07:00
it ( 'should locate directives in property bindings' , () => {
2016-01-06 14:13:44 -08:00
var dirA = CompileDirectiveMetadata . create ({
selector : '[a=b]' ,
2016-08-24 17:39:49 -07:00
type : new CompileTypeMetadata (
2016-08-29 08:52:25 -07:00
{ moduleUrl : someModuleUrl , name : 'DirA' , reference : {} as Type < any >})
2016-01-06 14:13:44 -08:00
});
var dirB = CompileDirectiveMetadata . create ({
selector : '[b]' ,
2016-08-24 17:39:49 -07:00
type : new CompileTypeMetadata (
2016-08-29 08:52:25 -07:00
{ moduleUrl : someModuleUrl , name : 'DirB' , reference : {} as Type < any >})
2016-01-06 14:13:44 -08:00
});
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<div [a]="b">' , [ dirA , dirB ]))). toEqual ([
[ ElementAst , 'div' ],
[ BoundElementPropertyAst , PropertyBindingType . Property , 'a' , 'b' , null ],
[ DirectiveAst , dirA ]
]);
2015-08-25 15:36:02 -07:00
});
2016-02-03 11:35:42 -08:00
it ( 'should locate directives in event bindings' , () => {
2016-01-06 14:13:44 -08:00
var dirA = CompileDirectiveMetadata . create ({
selector : '[a]' ,
2016-08-24 17:39:49 -07:00
type : new CompileTypeMetadata (
2016-08-29 08:52:25 -07:00
{ moduleUrl : someModuleUrl , name : 'DirB' , reference : {} as Type < any >})
2016-01-06 14:13:44 -08:00
});
2016-02-03 11:35:42 -08:00
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<div (a)="b">' , [ dirA ]))). toEqual ([
[ ElementAst , 'div' ], [ BoundEventAst , 'a' , null , 'b' ], [ DirectiveAst , dirA ]
]);
2016-02-03 11:35:42 -08:00
});
2015-08-27 16:29:02 -07:00
it ( 'should parse directive host properties' , () => {
2015-09-18 10:33:23 -07:00
var dirA = CompileDirectiveMetadata . create ({
2015-08-27 16:29:02 -07:00
selector : 'div' ,
2016-08-24 17:39:49 -07:00
type : new CompileTypeMetadata (
2016-08-29 08:52:25 -07:00
{ moduleUrl : someModuleUrl , name : 'DirA' , reference : {} as Type < any >}),
2015-09-18 10:33:23 -07:00
host : { '[a]' : 'expr' }
2015-08-27 16:29:02 -07:00
});
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<div></div>' , [ dirA ]))). toEqual ([
[ ElementAst , 'div' ], [ DirectiveAst , dirA ],
[ BoundElementPropertyAst , PropertyBindingType . Property , 'a' , 'expr' , null ]
]);
2015-08-27 16:29:02 -07:00
});
it ( 'should parse directive host listeners' , () => {
2015-09-18 10:33:23 -07:00
var dirA = CompileDirectiveMetadata . create ({
2015-08-27 16:29:02 -07:00
selector : 'div' ,
2016-08-24 17:39:49 -07:00
type : new CompileTypeMetadata (
2016-08-29 08:52:25 -07:00
{ moduleUrl : someModuleUrl , name : 'DirA' , reference : {} as Type < any >}),
2015-09-18 10:33:23 -07:00
host : { '(a)' : 'expr' }
2015-08-27 16:29:02 -07:00
});
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<div></div>' , [ dirA ]))). toEqual ([
[ ElementAst , 'div' ], [ DirectiveAst , dirA ], [ BoundEventAst , 'a' , null , 'expr' ]
]);
2015-08-27 16:29:02 -07:00
});
it ( 'should parse directive properties' , () => {
2016-01-06 14:13:44 -08:00
var dirA = CompileDirectiveMetadata . create ({
selector : 'div' ,
2016-08-24 17:39:49 -07:00
type : new CompileTypeMetadata (
2016-08-29 08:52:25 -07:00
{ moduleUrl : someModuleUrl , name : 'DirA' , reference : {} as Type < any >}),
2016-01-06 14:13:44 -08:00
inputs : [ 'aProp' ]
});
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<div [aProp]="expr"></div>' , [ dirA ]))). toEqual ([
[ ElementAst , 'div' ], [ DirectiveAst , dirA ],
[ BoundDirectivePropertyAst , 'aProp' , 'expr' ]
]);
2015-08-27 16:29:02 -07:00
});
it ( 'should parse renamed directive properties' , () => {
2016-01-06 14:13:44 -08:00
var dirA = CompileDirectiveMetadata . create ({
selector : 'div' ,
2016-08-24 17:39:49 -07:00
type : new CompileTypeMetadata (
2016-08-29 08:52:25 -07:00
{ moduleUrl : someModuleUrl , name : 'DirA' , reference : {} as Type < any >}),
2016-01-06 14:13:44 -08:00
inputs : [ 'b:a' ]
});
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<div [a]="expr"></div>' , [ dirA ]))). toEqual ([
[ ElementAst , 'div' ], [ DirectiveAst , dirA ], [ BoundDirectivePropertyAst , 'b' , 'expr' ]
]);
2015-08-27 16:29:02 -07:00
});
it ( 'should parse literal directive properties' , () => {
2016-01-06 14:13:44 -08:00
var dirA = CompileDirectiveMetadata . create ({
selector : 'div' ,
2016-08-24 17:39:49 -07:00
type : new CompileTypeMetadata (
2016-08-29 08:52:25 -07:00
{ moduleUrl : someModuleUrl , name : 'DirA' , reference : {} as Type < any >}),
2016-01-06 14:13:44 -08:00
inputs : [ 'a' ]
});
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<div a="literal"></div>' , [ dirA ]))). toEqual ([
[ ElementAst , 'div' ], [ AttrAst , 'a' , 'literal' ], [ DirectiveAst , dirA ],
[ BoundDirectivePropertyAst , 'a' , '"literal"' ]
]);
2015-08-27 16:29:02 -07:00
});
2015-09-18 10:33:23 -07:00
it ( 'should favor explicit bound properties over literal properties' , () => {
2016-01-06 14:13:44 -08:00
var dirA = CompileDirectiveMetadata . create ({
selector : 'div' ,
2016-08-24 17:39:49 -07:00
type : new CompileTypeMetadata (
2016-08-29 08:52:25 -07:00
{ moduleUrl : someModuleUrl , name : 'DirA' , reference : {} as Type < any >}),
2016-01-06 14:13:44 -08:00
inputs : [ 'a' ]
});
2015-11-10 15:56:25 -08:00
expect ( humanizeTplAst ( parse ( '<div a="literal" [a]="\'literal2\'"></div>' , [ dirA ])))
2015-09-18 10:33:23 -07:00
. toEqual ([
2016-06-08 16:38:52 -07:00
[ ElementAst , 'div' ], [ AttrAst , 'a' , 'literal' ], [ DirectiveAst , dirA ],
2015-10-07 09:34:21 -07:00
[ BoundDirectivePropertyAst , 'a' , '"literal2"' ]
2015-09-18 10:33:23 -07:00
]);
});
2015-08-27 16:29:02 -07:00
it ( 'should support optional directive properties' , () => {
2016-01-06 14:13:44 -08:00
var dirA = CompileDirectiveMetadata . create ({
selector : 'div' ,
2016-08-24 17:39:49 -07:00
type : new CompileTypeMetadata (
2016-08-29 08:52:25 -07:00
{ moduleUrl : someModuleUrl , name : 'DirA' , reference : {} as Type < any >}),
2016-01-06 14:13:44 -08:00
inputs : [ 'a' ]
});
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<div></div>' , [ dirA ]))). toEqual ([
[ ElementAst , 'div' ], [ DirectiveAst , dirA ]
]);
2015-08-27 16:29:02 -07:00
});
2015-08-25 15:36:02 -07:00
});
2016-01-06 14:13:44 -08:00
describe ( 'providers' , () => {
2016-08-01 11:24:49 -07:00
var nextProviderId : number ;
2016-01-06 14:13:44 -08:00
function createToken ( value : string ) : CompileTokenMetadata {
2016-08-01 11:24:49 -07:00
let token : CompileTokenMetadata ;
2016-01-06 14:13:44 -08:00
if ( value . startsWith ( 'type:' )) {
2016-08-24 17:39:49 -07:00
const name = value . substring ( 5 );
2016-01-06 14:13:44 -08:00
token = new CompileTokenMetadata ({
2016-08-24 17:39:49 -07:00
identifier : new CompileTypeMetadata (
2016-08-29 08:52:25 -07:00
{ moduleUrl : someModuleUrl , name , reference : name as any as Type < any >})
2016-01-06 14:13:44 -08:00
});
} else {
token = new CompileTokenMetadata ({ value : value });
}
return token ;
}
function createDep ( value : string ) : CompileDiDependencyMetadata {
var isOptional = false ;
if ( value . startsWith ( 'optional:' )) {
isOptional = true ;
value = value . substring ( 9 );
}
var isSelf = false ;
if ( value . startsWith ( 'self:' )) {
isSelf = true ;
value = value . substring ( 5 );
}
var isHost = false ;
if ( value . startsWith ( 'host:' )) {
isHost = true ;
value = value . substring ( 5 );
}
return new CompileDiDependencyMetadata (
{ token : createToken ( value ), isOptional : isOptional , isSelf : isSelf , isHost : isHost });
}
function createProvider (
2016-08-01 11:24:49 -07:00
token : string , { multi = false , deps = []} : { multi? : boolean , deps? : string []} = {}) :
CompileProviderMetadata {
2016-08-24 17:39:49 -07:00
const name = `provider ${ nextProviderId ++ } ` ;
2016-01-06 14:13:44 -08:00
return new CompileProviderMetadata ({
token : createToken ( token ),
multi : multi ,
2016-08-29 08:52:25 -07:00
useClass : new CompileTypeMetadata ({ name , reference : name as any as Type < any >}),
2016-01-06 14:13:44 -08:00
deps : deps.map ( createDep )
});
}
2016-06-08 16:38:52 -07:00
function createDir (
2016-08-01 11:24:49 -07:00
selector : string , { providers = null , viewProviders = null , deps = [], queries = []} : {
2016-06-08 16:38:52 -07:00
providers? : CompileProviderMetadata [],
viewProviders? : CompileProviderMetadata [],
deps? : string [],
queries? : string []
} = {}) : CompileDirectiveMetadata {
2016-01-06 14:13:44 -08:00
var isComponent = ! selector . startsWith ( '[' );
return CompileDirectiveMetadata . create ({
selector : selector ,
2016-08-24 17:39:49 -07:00
type : new CompileTypeMetadata ({
moduleUrl : someModuleUrl ,
name : selector ,
diDeps : deps.map ( createDep ),
2016-08-29 08:52:25 -07:00
reference : selector as any as Type < any >
2016-08-24 17:39:49 -07:00
}),
2016-01-06 14:13:44 -08:00
isComponent : isComponent ,
template : new CompileTemplateMetadata ({ ngContentSelectors : []}),
providers : providers ,
viewProviders : viewProviders ,
2016-06-08 16:38:52 -07:00
queries : queries.map (
( value ) => new CompileQueryMetadata ({ selectors : [ createToken ( value )]}))
2016-01-06 14:13:44 -08:00
});
}
beforeEach (() => { nextProviderId = 0 ; });
it ( 'should provide a component' , () => {
var comp = createDir ( 'my-comp' );
var elAst : ElementAst = < ElementAst > parse ( '<my-comp>' , [ comp ])[ 0 ];
expect ( elAst . providers . length ). toBe ( 1 );
expect ( elAst . providers [ 0 ]. providerType ). toBe ( ProviderAstType . Component );
expect ( elAst . providers [ 0 ]. providers [ 0 ]. useClass ). toBe ( comp . type );
});
it ( 'should provide a directive' , () => {
var dirA = createDir ( '[dirA]' );
var elAst : ElementAst = < ElementAst > parse ( '<div dirA>' , [ dirA ])[ 0 ];
expect ( elAst . providers . length ). toBe ( 1 );
expect ( elAst . providers [ 0 ]. providerType ). toBe ( ProviderAstType . Directive );
expect ( elAst . providers [ 0 ]. providers [ 0 ]. useClass ). toBe ( dirA . type );
});
it ( 'should use the public providers of a directive' , () => {
var provider = createProvider ( 'service' );
var dirA = createDir ( '[dirA]' , { providers : [ provider ]});
var elAst : ElementAst = < ElementAst > parse ( '<div dirA>' , [ dirA ])[ 0 ];
expect ( elAst . providers . length ). toBe ( 2 );
expect ( elAst . providers [ 1 ]. providerType ). toBe ( ProviderAstType . PublicService );
expect ( elAst . providers [ 1 ]. providers ). toEqual ([ provider ]);
});
it ( 'should use the private providers of a component' , () => {
var provider = createProvider ( 'service' );
var comp = createDir ( 'my-comp' , { viewProviders : [ provider ]});
var elAst : ElementAst = < ElementAst > parse ( '<my-comp>' , [ comp ])[ 0 ];
expect ( elAst . providers . length ). toBe ( 2 );
expect ( elAst . providers [ 1 ]. providerType ). toBe ( ProviderAstType . PrivateService );
expect ( elAst . providers [ 1 ]. providers ). toEqual ([ provider ]);
});
it ( 'should support multi providers' , () => {
var provider0 = createProvider ( 'service0' , { multi : true });
var provider1 = createProvider ( 'service1' , { multi : true });
var provider2 = createProvider ( 'service0' , { multi : true });
var dirA = createDir ( '[dirA]' , { providers : [ provider0 , provider1 ]});
var dirB = createDir ( '[dirB]' , { providers : [ provider2 ]});
var elAst : ElementAst = < ElementAst > parse ( '<div dirA dirB>' , [ dirA , dirB ])[ 0 ];
expect ( elAst . providers . length ). toBe ( 4 );
expect ( elAst . providers [ 2 ]. providers ). toEqual ([ provider0 , provider2 ]);
expect ( elAst . providers [ 3 ]. providers ). toEqual ([ provider1 ]);
});
it ( 'should overwrite non multi providers' , () => {
var provider1 = createProvider ( 'service0' );
var provider2 = createProvider ( 'service1' );
var provider3 = createProvider ( 'service0' );
var dirA = createDir ( '[dirA]' , { providers : [ provider1 , provider2 ]});
var dirB = createDir ( '[dirB]' , { providers : [ provider3 ]});
var elAst : ElementAst = < ElementAst > parse ( '<div dirA dirB>' , [ dirA , dirB ])[ 0 ];
expect ( elAst . providers . length ). toBe ( 4 );
expect ( elAst . providers [ 2 ]. providers ). toEqual ([ provider3 ]);
expect ( elAst . providers [ 3 ]. providers ). toEqual ([ provider2 ]);
});
it ( 'should overwrite component providers by directive providers' , () => {
var compProvider = createProvider ( 'service0' );
var dirProvider = createProvider ( 'service0' );
var comp = createDir ( 'my-comp' , { providers : [ compProvider ]});
var dirA = createDir ( '[dirA]' , { providers : [ dirProvider ]});
var elAst : ElementAst = < ElementAst > parse ( '<my-comp dirA>' , [ dirA , comp ])[ 0 ];
expect ( elAst . providers . length ). toBe ( 3 );
expect ( elAst . providers [ 2 ]. providers ). toEqual ([ dirProvider ]);
});
it ( 'should overwrite view providers by directive providers' , () => {
var viewProvider = createProvider ( 'service0' );
var dirProvider = createProvider ( 'service0' );
var comp = createDir ( 'my-comp' , { viewProviders : [ viewProvider ]});
var dirA = createDir ( '[dirA]' , { providers : [ dirProvider ]});
var elAst : ElementAst = < ElementAst > parse ( '<my-comp dirA>' , [ dirA , comp ])[ 0 ];
expect ( elAst . providers . length ). toBe ( 3 );
expect ( elAst . providers [ 2 ]. providers ). toEqual ([ dirProvider ]);
});
it ( 'should overwrite directives by providers' , () => {
var dirProvider = createProvider ( 'type:my-comp' );
var comp = createDir ( 'my-comp' , { providers : [ dirProvider ]});
var elAst : ElementAst = < ElementAst > parse ( '<my-comp>' , [ comp ])[ 0 ];
expect ( elAst . providers . length ). toBe ( 1 );
expect ( elAst . providers [ 0 ]. providers ). toEqual ([ dirProvider ]);
});
2016-07-29 14:45:05 -07:00
it ( 'if mixing multi and non multi providers' , () => {
2016-01-06 14:13:44 -08:00
var provider0 = createProvider ( 'service0' );
var provider1 = createProvider ( 'service0' , { multi : true });
var dirA = createDir ( '[dirA]' , { providers : [ provider0 ]});
var dirB = createDir ( '[dirB]' , { providers : [ provider1 ]});
expect (() => parse ( '<div dirA dirB>' , [ dirA , dirB ]))
. toThrowError (
`Template parse errors: \ n` +
`Mixing multi and non multi provider is not possible for token service0 ("[ERROR ->]<div dirA dirB>"): TestComp@0:0` );
});
2016-04-25 08:39:53 -07:00
it ( 'should sort providers by their DI order' , () => {
2016-01-06 14:13:44 -08:00
var provider0 = createProvider ( 'service0' , { deps : [ 'type:[dir2]' ]});
var provider1 = createProvider ( 'service1' );
var dir2 = createDir ( '[dir2]' , { deps : [ 'service1' ]});
var comp = createDir ( 'my-comp' , { providers : [ provider0 , provider1 ]});
var elAst : ElementAst = < ElementAst > parse ( '<my-comp dir2>' , [ comp , dir2 ])[ 0 ];
expect ( elAst . providers . length ). toBe ( 4 );
expect ( elAst . providers [ 0 ]. providers [ 0 ]. useClass ). toEqual ( comp . type );
expect ( elAst . providers [ 1 ]. providers ). toEqual ([ provider1 ]);
expect ( elAst . providers [ 2 ]. providers [ 0 ]. useClass ). toEqual ( dir2 . type );
expect ( elAst . providers [ 3 ]. providers ). toEqual ([ provider0 ]);
});
2016-04-25 08:39:53 -07:00
it ( 'should sort directives by their DI order' , () => {
var dir0 = createDir ( '[dir0]' , { deps : [ 'type:my-comp' ]});
var dir1 = createDir ( '[dir1]' , { deps : [ 'type:[dir0]' ]});
var dir2 = createDir ( '[dir2]' , { deps : [ 'type:[dir1]' ]});
var comp = createDir ( 'my-comp' );
var elAst : ElementAst =
< ElementAst > parse ( '<my-comp dir2 dir0 dir1>' , [ comp , dir2 , dir0 , dir1 ])[ 0 ];
expect ( elAst . providers . length ). toBe ( 4 );
expect ( elAst . directives [ 0 ]. directive ). toBe ( comp );
expect ( elAst . directives [ 1 ]. directive ). toBe ( dir0 );
expect ( elAst . directives [ 2 ]. directive ). toBe ( dir1 );
expect ( elAst . directives [ 3 ]. directive ). toBe ( dir2 );
});
2016-01-06 14:13:44 -08:00
it ( 'should mark directives and dependencies of directives as eager' , () => {
var provider0 = createProvider ( 'service0' );
var provider1 = createProvider ( 'service1' );
var dirA = createDir ( '[dirA]' , { providers : [ provider0 , provider1 ], deps : [ 'service0' ]});
var elAst : ElementAst = < ElementAst > parse ( '<div dirA>' , [ dirA ])[ 0 ];
expect ( elAst . providers . length ). toBe ( 3 );
expect ( elAst . providers [ 0 ]. providers ). toEqual ([ provider0 ]);
expect ( elAst . providers [ 0 ]. eager ). toBe ( true );
expect ( elAst . providers [ 1 ]. providers [ 0 ]. useClass ). toEqual ( dirA . type );
expect ( elAst . providers [ 1 ]. eager ). toBe ( true );
expect ( elAst . providers [ 2 ]. providers ). toEqual ([ provider1 ]);
expect ( elAst . providers [ 2 ]. eager ). toBe ( false );
});
it ( 'should mark dependencies on parent elements as eager' , () => {
var provider0 = createProvider ( 'service0' );
var provider1 = createProvider ( 'service1' );
var dirA = createDir ( '[dirA]' , { providers : [ provider0 , provider1 ]});
var dirB = createDir ( '[dirB]' , { deps : [ 'service0' ]});
var elAst : ElementAst =
< ElementAst > parse ( '<div dirA><div dirB></div></div>' , [ dirA , dirB ])[ 0 ];
expect ( elAst . providers . length ). toBe ( 3 );
expect ( elAst . providers [ 0 ]. providers [ 0 ]. useClass ). toEqual ( dirA . type );
expect ( elAst . providers [ 0 ]. eager ). toBe ( true );
expect ( elAst . providers [ 1 ]. providers ). toEqual ([ provider0 ]);
expect ( elAst . providers [ 1 ]. eager ). toBe ( true );
expect ( elAst . providers [ 2 ]. providers ). toEqual ([ provider1 ]);
expect ( elAst . providers [ 2 ]. eager ). toBe ( false );
});
it ( 'should mark queried providers as eager' , () => {
var provider0 = createProvider ( 'service0' );
var provider1 = createProvider ( 'service1' );
var dirA =
createDir ( '[dirA]' , { providers : [ provider0 , provider1 ], queries : [ 'service0' ]});
var elAst : ElementAst = < ElementAst > parse ( '<div dirA></div>' , [ dirA ])[ 0 ];
expect ( elAst . providers . length ). toBe ( 3 );
expect ( elAst . providers [ 0 ]. providers [ 0 ]. useClass ). toEqual ( dirA . type );
expect ( elAst . providers [ 0 ]. eager ). toBe ( true );
expect ( elAst . providers [ 1 ]. providers ). toEqual ([ provider0 ]);
expect ( elAst . providers [ 1 ]. eager ). toBe ( true );
expect ( elAst . providers [ 2 ]. providers ). toEqual ([ provider1 ]);
expect ( elAst . providers [ 2 ]. eager ). toBe ( false );
});
it ( 'should not mark dependencies accross embedded views as eager' , () => {
var provider0 = createProvider ( 'service0' );
var dirA = createDir ( '[dirA]' , { providers : [ provider0 ]});
var dirB = createDir ( '[dirB]' , { deps : [ 'service0' ]});
var elAst : ElementAst =
< ElementAst > parse ( '<div dirA><div *ngIf dirB></div></div>' , [ dirA , dirB ])[ 0 ];
expect ( elAst . providers . length ). toBe ( 2 );
expect ( elAst . providers [ 0 ]. providers [ 0 ]. useClass ). toEqual ( dirA . type );
expect ( elAst . providers [ 0 ]. eager ). toBe ( true );
expect ( elAst . providers [ 1 ]. providers ). toEqual ([ provider0 ]);
expect ( elAst . providers [ 1 ]. eager ). toBe ( false );
});
it ( 'should report missing @Self() deps as errors' , () => {
var dirA = createDir ( '[dirA]' , { deps : [ 'self:provider0' ]});
expect (() => parse ( '<div dirA></div>' , [ dirA ]))
2016-06-22 14:58:57 -07:00
. toThrowError (
'Template parse errors:\nNo provider for provider0 ("[ERROR ->]<div dirA></div>"): TestComp@0:0' );
2016-01-06 14:13:44 -08:00
});
it ( 'should change missing @Self() that are optional to nulls' , () => {
var dirA = createDir ( '[dirA]' , { deps : [ 'optional:self:provider0' ]});
var elAst : ElementAst = < ElementAst > parse ( '<div dirA></div>' , [ dirA ])[ 0 ];
expect ( elAst . providers [ 0 ]. providers [ 0 ]. deps [ 0 ]. isValue ). toBe ( true );
expect ( elAst . providers [ 0 ]. providers [ 0 ]. deps [ 0 ]. value ). toBe ( null );
});
it ( 'should report missing @Host() deps as errors' , () => {
var dirA = createDir ( '[dirA]' , { deps : [ 'host:provider0' ]});
expect (() => parse ( '<div dirA></div>' , [ dirA ]))
2016-06-22 14:58:57 -07:00
. toThrowError (
'Template parse errors:\nNo provider for provider0 ("[ERROR ->]<div dirA></div>"): TestComp@0:0' );
2016-01-06 14:13:44 -08:00
});
it ( 'should change missing @Host() that are optional to nulls' , () => {
var dirA = createDir ( '[dirA]' , { deps : [ 'optional:host:provider0' ]});
var elAst : ElementAst = < ElementAst > parse ( '<div dirA></div>' , [ dirA ])[ 0 ];
expect ( elAst . providers [ 0 ]. providers [ 0 ]. deps [ 0 ]. isValue ). toBe ( true );
expect ( elAst . providers [ 0 ]. providers [ 0 ]. deps [ 0 ]. value ). toBe ( null );
});
});
2016-04-25 19:52:24 -07:00
describe ( 'references' , () => {
2015-09-18 10:33:23 -07:00
2016-04-25 19:52:24 -07:00
it ( 'should parse references via #... and not report them as attributes' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<div #a>' , [
]))). toEqual ([[ ElementAst , 'div' ], [ ReferenceAst , 'a' , null ]]);
2015-09-18 10:33:23 -07:00
});
2016-04-25 19:52:24 -07:00
it ( 'should parse references via ref-... and not report them as attributes' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<div ref-a>' , [
]))). toEqual ([[ ElementAst , 'div' ], [ ReferenceAst , 'a' , null ]]);
2016-04-25 19:52:24 -07:00
});
it ( 'should parse camel case references' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<div ref-someA>' , [
]))). toEqual ([[ ElementAst , 'div' ], [ ReferenceAst , 'someA' , null ]]);
2015-09-18 10:33:23 -07:00
});
2016-04-25 19:52:24 -07:00
it ( 'should assign references with empty value to the element' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<div #a></div>' , [
]))). toEqual ([[ ElementAst , 'div' ], [ ReferenceAst , 'a' , null ]]);
2015-09-18 10:33:23 -07:00
});
2016-04-25 19:52:24 -07:00
it ( 'should assign references to directives via exportAs' , () => {
2016-01-06 14:13:44 -08:00
var dirA = CompileDirectiveMetadata . create ({
selector : '[a]' ,
2016-08-24 17:39:49 -07:00
type : new CompileTypeMetadata (
2016-08-29 08:52:25 -07:00
{ moduleUrl : someModuleUrl , name : 'DirA' , reference : {} as Type < any >}),
2016-01-06 14:13:44 -08:00
exportAs : 'dirA'
});
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<div a #a="dirA"></div>' , [ dirA ]))). toEqual ([
[ ElementAst , 'div' ],
[ AttrAst , 'a' , '' ],
[ ReferenceAst , 'a' , identifierToken ( dirA . type )],
[ DirectiveAst , dirA ],
]);
2015-09-18 10:33:23 -07:00
});
2016-04-25 19:52:24 -07:00
it ( 'should report references with values that dont match a directive as errors' , () => {
2015-09-18 10:33:23 -07:00
expect (() => parse ( '<div #a="dirA"></div>' , [])). toThrowError ( `Template parse errors:
2015-11-10 15:56:25 -08:00
There is no directive with "exportAs" set to "dirA" ("<div [ERROR ->]#a="dirA"></div>"): TestComp@0:5` );
2015-09-18 10:33:23 -07:00
});
2016-04-25 19:52:24 -07:00
it ( 'should report invalid reference names' , () => {
2015-11-23 16:02:19 -08:00
expect (() => parse ( '<div #a-b></div>' , [])). toThrowError ( `Template parse errors:
2016-04-25 19:52:24 -07:00
"-" is not allowed in reference names ("<div [ERROR ->]#a-b></div>"): TestComp@0:5` );
2015-11-23 16:02:19 -08:00
});
2016-04-25 19:52:24 -07:00
it ( 'should report variables as errors' , () => {
expect (() => parse ( '<div let-a></div>' , [])). toThrowError ( `Template parse errors:
"let-" is only supported on template elements. ("<div [ERROR ->]let-a></div>"): TestComp@0:5` );
});
2015-09-18 10:33:23 -07:00
2016-06-08 16:38:52 -07:00
it ( 'should report duplicate reference names' , () => {
2016-06-30 14:59:23 -07:00
expect (() => parse ( '<div #a></div><div #a></div>' , []))
. toThrowError ( `Template parse errors:
2016-06-08 16:38:52 -07:00
Reference "#a" is defined several times ("<div #a></div><div [ERROR ->]#a></div>"): TestComp@0:19` );
2016-07-06 14:06:47 -07:00
2016-05-26 23:04:17 +03:00
});
2016-06-30 14:59:23 -07:00
it ( 'should not throw error when there is same reference name in different templates' ,
() => {
expect (() => parse ( '<div #a><template #a><span>OK</span></template></div>' , []))
. not . toThrowError ();
2016-07-06 14:06:47 -07:00
2016-06-30 14:59:23 -07:00
});
2016-05-26 23:04:17 +03:00
2016-04-25 19:52:24 -07:00
it ( 'should assign references with empty value to components' , () => {
2015-09-18 10:33:23 -07:00
var dirA = CompileDirectiveMetadata . create ({
selector : '[a]' ,
isComponent : true ,
2016-08-24 17:39:49 -07:00
type : new CompileTypeMetadata (
2016-08-29 08:52:25 -07:00
{ moduleUrl : someModuleUrl , name : 'DirA' , reference : {} as Type < any >}),
2015-10-28 08:59:19 +01:00
exportAs : 'dirA' ,
template : new CompileTemplateMetadata ({ ngContentSelectors : []})
2015-09-18 10:33:23 -07:00
});
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<div a #a></div>' , [ dirA ]))). toEqual ([
[ ElementAst , 'div' ],
[ AttrAst , 'a' , '' ],
[ ReferenceAst , 'a' , identifierToken ( dirA . type )],
[ DirectiveAst , dirA ],
]);
2015-09-18 10:33:23 -07:00
});
2016-04-25 19:52:24 -07:00
it ( 'should not locate directives in references' , () => {
var dirA = CompileDirectiveMetadata . create ({
selector : '[a]' ,
2016-08-24 17:39:49 -07:00
type : new CompileTypeMetadata (
2016-08-29 08:52:25 -07:00
{ moduleUrl : someModuleUrl , name : 'DirA' , reference : {} as Type < any >})
2016-04-25 19:52:24 -07:00
});
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<div ref-a>' , [ dirA ]))). toEqual ([
[ ElementAst , 'div' ], [ ReferenceAst , 'a' , null ]
]);
2016-04-25 19:52:24 -07:00
});
2015-09-18 10:33:23 -07:00
});
2015-08-25 15:36:02 -07:00
describe ( 'explicit templates' , () => {
it ( 'should create embedded templates for <template> elements' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<template></template>' , [
]))). toEqual ([[ EmbeddedTemplateAst ]]);
expect ( humanizeTplAst ( parse ( '<TEMPLATE></TEMPLATE>' , [
]))). toEqual ([[ EmbeddedTemplateAst ]]);
2015-08-25 15:36:02 -07:00
});
2015-12-08 09:01:15 -08:00
it ( 'should create embedded templates for <template> elements regardless the namespace' ,
() => {
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<svg><template></template></svg>' , []))). toEqual ([
[ ElementAst , ':svg:svg' ],
[ EmbeddedTemplateAst ],
]);
2015-12-08 09:01:15 -08:00
});
2016-04-25 19:52:24 -07:00
it ( 'should support references via #...' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<template #a>' , []))). toEqual ([
2016-08-24 17:39:49 -07:00
[ EmbeddedTemplateAst ],
[ ReferenceAst , 'a' , resolveIdentifierToken ( Identifiers . TemplateRef )]
2016-06-08 16:38:52 -07:00
]);
2016-04-25 19:52:24 -07:00
});
it ( 'should support references via ref-...' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<template ref-a>' , []))). toEqual ([
2016-08-24 17:39:49 -07:00
[ EmbeddedTemplateAst ],
[ ReferenceAst , 'a' , resolveIdentifierToken ( Identifiers . TemplateRef )]
2016-06-08 16:38:52 -07:00
]);
2016-04-25 19:52:24 -07:00
});
it ( 'should parse variables via let-...' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<template let-a="b">' , [
]))). toEqual ([[ EmbeddedTemplateAst ], [ VariableAst , 'a' , 'b' ]]);
2016-04-25 19:52:24 -07:00
});
it ( 'should not locate directives in variables' , () => {
var dirA = CompileDirectiveMetadata . create ({
selector : '[a]' ,
2016-08-24 17:39:49 -07:00
type : new CompileTypeMetadata (
2016-08-29 08:52:25 -07:00
{ moduleUrl : someModuleUrl , name : 'DirA' , reference : {} as Type < any >})
2016-04-25 19:52:24 -07:00
});
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<template let-a="b"></template>' , [ dirA ]))). toEqual ([
[ EmbeddedTemplateAst ], [ VariableAst , 'a' , 'b' ]
]);
2016-04-25 19:52:24 -07:00
});
2015-08-25 15:36:02 -07:00
});
describe ( 'inline templates' , () => {
it ( 'should wrap the element into an EmbeddedTemplateAST' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<div template>' , [
]))). toEqual ([[ EmbeddedTemplateAst ], [ ElementAst , 'div' ]]);
2015-08-25 15:36:02 -07:00
});
2016-07-08 10:09:49 -07:00
it ( 'should wrap the element with data-template attribute into an EmbeddedTemplateAST ' ,
() => {
expect ( humanizeTplAst ( parse ( '<div data-template>' , [
]))). toEqual ([[ EmbeddedTemplateAst ], [ ElementAst , 'div' ]]);
});
2015-08-25 15:36:02 -07:00
it ( 'should parse bound properties' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<div template="ngIf test">' , [ ngIf ]))). toEqual ([
[ EmbeddedTemplateAst ], [ DirectiveAst , ngIf ],
[ BoundDirectivePropertyAst , 'ngIf' , 'test' ], [ ElementAst , 'div' ]
]);
2015-08-25 15:36:02 -07:00
});
2016-08-25 15:21:33 -07:00
it ( 'should report an error on variables declared with #' , () => {
expect (() => humanizeTplAst ( parse ( '<div *ngIf="#a=b">' , [])))
. toThrowError ( /Parser Error: Unexpected token # at column 6/ );
2016-04-25 19:52:24 -07:00
});
it ( 'should parse variables via let ...' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<div *ngIf="let a=b">' , [
]))). toEqual ([[ EmbeddedTemplateAst ], [ VariableAst , 'a' , 'b' ], [ ElementAst , 'div' ]]);
2015-08-25 15:36:02 -07:00
});
describe ( 'directives' , () => {
it ( 'should locate directives in property bindings' , () => {
2016-01-06 14:13:44 -08:00
var dirA = CompileDirectiveMetadata . create ({
selector : '[a=b]' ,
2016-08-24 17:39:49 -07:00
type : new CompileTypeMetadata (
2016-08-29 08:52:25 -07:00
{ moduleUrl : someModuleUrl , name : 'DirA' , reference : {} as Type < any >}),
2016-01-06 14:13:44 -08:00
inputs : [ 'a' ]
});
var dirB = CompileDirectiveMetadata . create ({
selector : '[b]' ,
2016-08-24 17:39:49 -07:00
type : new CompileTypeMetadata (
2016-08-29 08:52:25 -07:00
{ moduleUrl : someModuleUrl , name : 'DirB' , reference : {} as Type < any >})
2016-01-06 14:13:44 -08:00
});
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<div template="a b" b>' , [ dirA , dirB ]))). toEqual ([
[ EmbeddedTemplateAst ], [ DirectiveAst , dirA ], [ BoundDirectivePropertyAst , 'a' , 'b' ],
[ ElementAst , 'div' ], [ AttrAst , 'b' , '' ], [ DirectiveAst , dirB ]
]);
2015-08-25 15:36:02 -07:00
});
2016-04-25 19:52:24 -07:00
it ( 'should not locate directives in variables' , () => {
2016-01-06 14:13:44 -08:00
var dirA = CompileDirectiveMetadata . create ({
2016-04-25 19:52:24 -07:00
selector : '[a]' ,
2016-08-24 17:39:49 -07:00
type : new CompileTypeMetadata (
2016-08-29 08:52:25 -07:00
{ moduleUrl : someModuleUrl , name : 'DirA' , reference : {} as Type < any >})
2016-01-06 14:13:44 -08:00
});
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<div template="let a=b">' , [ dirA ]))). toEqual ([
[ EmbeddedTemplateAst ], [ VariableAst , 'a' , 'b' ], [ ElementAst , 'div' ]
]);
2016-04-25 19:52:24 -07:00
});
it ( 'should not locate directives in references' , () => {
var dirA = CompileDirectiveMetadata . create ({
selector : '[a]' ,
2016-08-24 17:39:49 -07:00
type : new CompileTypeMetadata (
2016-08-29 08:52:25 -07:00
{ moduleUrl : someModuleUrl , name : 'DirA' , reference : {} as Type < any >})
2016-01-06 14:13:44 -08:00
});
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<div ref-a>' , [ dirA ]))). toEqual ([
[ ElementAst , 'div' ], [ ReferenceAst , 'a' , null ]
]);
2015-08-25 15:36:02 -07:00
});
});
it ( 'should work with *... and use the attribute name as property binding name' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<div *ngIf="test">' , [ ngIf ]))). toEqual ([
[ EmbeddedTemplateAst ], [ DirectiveAst , ngIf ],
[ BoundDirectivePropertyAst , 'ngIf' , 'test' ], [ ElementAst , 'div' ]
]);
2015-08-25 15:36:02 -07:00
});
2015-10-26 13:22:45 -07:00
it ( 'should work with *... and empty value' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<div *ngIf>' , [ ngIf ]))). toEqual ([
[ EmbeddedTemplateAst ], [ DirectiveAst , ngIf ],
[ BoundDirectivePropertyAst , 'ngIf' , 'null' ], [ ElementAst , 'div' ]
]);
2015-10-26 13:22:45 -07:00
});
2015-08-25 15:36:02 -07:00
});
});
2015-09-11 13:37:05 -07:00
describe ( 'content projection' , () => {
2016-06-08 15:45:15 -07:00
var compCounter : any /** TODO #9100 */ ;
2016-01-06 14:13:44 -08:00
beforeEach (() => { compCounter = 0 ; });
2016-06-08 16:38:52 -07:00
function createComp (
selector : string , ngContentSelectors : string []) : CompileDirectiveMetadata {
2015-09-18 10:33:23 -07:00
return CompileDirectiveMetadata . create ({
2015-09-11 13:37:05 -07:00
selector : selector ,
isComponent : true ,
2016-08-24 17:39:49 -07:00
type : new CompileTypeMetadata ({
moduleUrl : someModuleUrl ,
name : `SomeComp ${ compCounter ++ } ` ,
2016-08-29 08:52:25 -07:00
reference : {} as Type < any >
2016-08-24 17:39:49 -07:00
}),
2015-09-18 10:33:23 -07:00
template : new CompileTemplateMetadata ({ ngContentSelectors : ngContentSelectors })
2016-07-21 17:12:00 -07:00
});
2015-09-11 13:37:05 -07:00
}
2016-04-29 17:07:28 -07:00
function createDir ( selector : string ) : CompileDirectiveMetadata {
return CompileDirectiveMetadata . create ({
selector : selector ,
2016-08-24 17:39:49 -07:00
type : new CompileTypeMetadata ({
moduleUrl : someModuleUrl ,
name : `SomeDir ${ compCounter ++ } ` ,
2016-08-29 08:52:25 -07:00
reference : {} as Type < any >
2016-08-24 17:39:49 -07:00
})
2016-07-21 17:12:00 -07:00
});
2016-04-29 17:07:28 -07:00
}
2015-09-11 13:37:05 -07:00
describe ( 'project text nodes' , () => {
it ( 'should project text nodes with wildcard selector' , () => {
expect ( humanizeContentProjection ( parse ( '<div>hello</div>' , [ createComp ( 'div' , [ '*' ])])))
. toEqual ([[ 'div' , null ], [ '#text(hello)' , 0 ]]);
});
});
describe ( 'project elements' , () => {
it ( 'should project elements with wildcard selector' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeContentProjection ( parse ( '<div><span></span></div>' , [
createComp ( 'div' , [ '*' ])
]))). toEqual ([[ 'div' , null ], [ 'span' , 0 ]]);
2015-09-11 13:37:05 -07:00
});
it ( 'should project elements with css selector' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeContentProjection ( parse ( '<div><a x></a><b></b></div>' , [
createComp ( 'div' , [ 'a[x]' ])
]))). toEqual ([[ 'div' , null ], [ 'a' , 0 ], [ 'b' , null ]]);
2015-09-11 13:37:05 -07:00
});
});
describe ( 'embedded templates' , () => {
it ( 'should project embedded templates with wildcard selector' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeContentProjection ( parse ( '<div><template></template></div>' , [
createComp ( 'div' , [ '*' ])
]))). toEqual ([[ 'div' , null ], [ 'template' , 0 ]]);
2015-09-11 13:37:05 -07:00
});
it ( 'should project embedded templates with css selector' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeContentProjection ( parse (
'<div><template x></template><template></template></div>' ,
[ createComp ( 'div' , [ 'template[x]' ])])))
2015-09-11 13:37:05 -07:00
. toEqual ([[ 'div' , null ], [ 'template' , 0 ], [ 'template' , null ]]);
});
});
describe ( 'ng-content' , () => {
it ( 'should project ng-content with wildcard selector' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeContentProjection ( parse ( '<div><ng-content></ng-content></div>' , [
createComp ( 'div' , [ '*' ])
]))). toEqual ([[ 'div' , null ], [ 'ng-content' , 0 ]]);
2015-09-11 13:37:05 -07:00
});
it ( 'should project ng-content with css selector' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeContentProjection ( parse (
'<div><ng-content x></ng-content><ng-content></ng-content></div>' ,
[ createComp ( 'div' , [ 'ng-content[x]' ])])))
2015-09-11 13:37:05 -07:00
. toEqual ([[ 'div' , null ], [ 'ng-content' , 0 ], [ 'ng-content' , null ]]);
});
});
it ( 'should project into the first matching ng-content' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeContentProjection ( parse ( '<div>hello<b></b><a></a></div>' , [
createComp ( 'div' , [ 'a' , 'b' , '*' ])
]))). toEqual ([[ 'div' , null ], [ '#text(hello)' , 2 ], [ 'b' , 1 ], [ 'a' , 0 ]]);
2015-09-11 13:37:05 -07:00
});
2015-10-29 16:23:13 -07:00
it ( 'should project into wildcard ng-content last' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeContentProjection ( parse ( '<div>hello<a></a></div>' , [
createComp ( 'div' , [ '*' , 'a' ])
]))). toEqual ([[ 'div' , null ], [ '#text(hello)' , 0 ], [ 'a' , 1 ]]);
2015-10-29 16:23:13 -07:00
});
2015-09-11 13:37:05 -07:00
it ( 'should only project direct child nodes' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeContentProjection ( parse ( '<div><span><a></a></span><a></a></div>' , [
createComp ( 'div' , [ 'a' ])
]))). toEqual ([[ 'div' , null ], [ 'span' , null ], [ 'a' , null ], [ 'a' , 0 ]]);
2015-09-11 13:37:05 -07:00
});
it ( 'should project nodes of nested components' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeContentProjection ( parse ( '<a><b>hello</b></a>' , [
createComp ( 'a' , [ '*' ]), createComp ( 'b' , [ '*' ])
]))). toEqual ([[ 'a' , null ], [ 'b' , 0 ], [ '#text(hello)' , 0 ]]);
2015-09-11 13:37:05 -07:00
});
2015-11-23 16:02:19 -08:00
it ( 'should project children of components with ngNonBindable' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeContentProjection ( parse ( '<div ngNonBindable>{{hello}}<span></span></div>' , [
createComp ( 'div' , [ '*' ])
]))). toEqual ([[ 'div' , null ], [ '#text({{hello}})' , 0 ], [ 'span' , 0 ]]);
2015-09-18 10:33:23 -07:00
});
2016-03-23 14:15:05 -07:00
it ( 'should match the element when there is an inline template' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeContentProjection ( parse ( '<div><b *ngIf="cond"></b></div>' , [
createComp ( 'div' , [ 'a' , 'b' ]), ngIf
]))). toEqual ([[ 'div' , null ], [ 'template' , 1 ], [ 'b' , null ]]);
2016-03-23 14:15:05 -07:00
});
describe ( 'ngProjectAs' , () => {
it ( 'should override elements' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeContentProjection ( parse ( '<div><a ngProjectAs="b"></a></div>' , [
createComp ( 'div' , [ 'a' , 'b' ])
]))). toEqual ([[ 'div' , null ], [ 'a' , 1 ]]);
2016-03-23 14:15:05 -07:00
});
it ( 'should override <ng-content>' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeContentProjection ( parse (
'<div><ng-content ngProjectAs="b"></ng-content></div>' ,
[ createComp ( 'div' , [ 'ng-content' , 'b' ])])))
2016-03-23 14:15:05 -07:00
. toEqual ([[ 'div' , null ], [ 'ng-content' , 1 ]]);
});
it ( 'should override <template>' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeContentProjection ( parse (
'<div><template ngProjectAs="b"></template></div>' ,
[ createComp ( 'div' , [ 'template' , 'b' ])])))
2016-03-23 14:15:05 -07:00
. toEqual ([[ 'div' , null ], [ 'template' , 1 ]]);
});
it ( 'should override inline templates' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeContentProjection ( parse (
'<div><a *ngIf="cond" ngProjectAs="b"></a></div>' ,
[ createComp ( 'div' , [ 'a' , 'b' ]), ngIf ])))
2016-03-23 14:15:05 -07:00
. toEqual ([[ 'div' , null ], [ 'template' , 1 ], [ 'a' , null ]]);
});
});
2016-04-29 17:07:28 -07:00
it ( 'should support other directives before the component' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeContentProjection ( parse ( '<div>hello</div>' , [
createDir ( 'div' ), createComp ( 'div' , [ '*' ])
]))). toEqual ([[ 'div' , null ], [ '#text(hello)' , 0 ]]);
2016-04-29 17:07:28 -07:00
});
2015-09-11 13:37:05 -07:00
});
2015-08-25 15:36:02 -07:00
describe ( 'splitClasses' , () => {
it ( 'should keep an empty class' , () => { expect ( splitClasses ( 'a' )). toEqual ([ 'a' ]); });
it ( 'should split 2 classes' , () => { expect ( splitClasses ( 'a b' )). toEqual ([ 'a' , 'b' ]); });
it ( 'should trim classes' , () => { expect ( splitClasses ( ' a b ' )). toEqual ([ 'a' , 'b' ]); });
});
2015-08-27 16:29:02 -07:00
describe ( 'error cases' , () => {
2015-12-03 14:20:00 -08:00
it ( 'should report when ng-content has content' , () => {
expect (() => parse ( '<ng-content>content</ng-content>' , []))
. toThrowError ( `Template parse errors:
<ng-content> element cannot have content. <ng-content> must be immediately followed by </ng-content> ("[ERROR ->]<ng-content>content</ng-content>"): TestComp@0:0` );
});
2016-06-28 21:53:41 -07:00
it ( 'should treat *attr on a template element as valid' ,
() => { expect (() => parse ( '<template *ngIf>' , [])). not . toThrowError (); });
2016-06-22 17:43:27 +02:00
2016-06-28 21:53:41 -07:00
it ( 'should treat template attribute on a template element as valid' ,
() => { expect (() => parse ( '<template template="ngIf">' , [])). not . toThrowError (); });
2016-06-22 17:43:27 +02:00
it ( 'should report when mutliple *attrs are used on the same element' , () => {
expect (() => parse ( '<div *ngIf *ngFor>' , [])). toThrowError ( `Template parse errors:
Can't have multiple template bindings on one element. Use only one attribute named 'template' or prefixed with * ("<div *ngIf [ERROR ->]*ngFor>"): TestComp@0:11` );
});
it ( 'should report when mix of template and *attrs are used on the same element' , () => {
2016-08-26 18:12:10 +02:00
expect (() => parse ( '<span template="ngIf" *ngFor>' , []))
. toThrowError ( `Template parse errors:
Can't have multiple template bindings on one element. Use only one attribute named 'template' or prefixed with * ("<span template="ngIf" [ERROR ->]*ngFor>"): TestComp@0:22` );
2016-06-22 17:43:27 +02:00
});
2015-10-07 09:34:21 -07:00
it ( 'should report invalid property names' , () => {
2015-11-23 16:02:19 -08:00
expect (() => parse ( '<div [invalidProp]></div>' , [])). toThrowError ( `Template parse errors:
2016-07-29 20:16:38 -07:00
Can't bind to 'invalidProp' since it isn't a known property of 'div'. ("<div [ERROR ->][invalidProp]></div>"): TestComp@0:5` );
2015-08-27 16:29:02 -07:00
});
2016-07-28 02:27:07 -07:00
it ( 'should report invalid host property names' , () => {
var dirA = CompileDirectiveMetadata . create ({
selector : 'div' ,
2016-08-24 17:39:49 -07:00
type : new CompileTypeMetadata (
2016-08-29 08:52:25 -07:00
{ moduleUrl : someModuleUrl , name : 'DirA' , reference : {} as Type < any >}),
2016-07-28 02:27:07 -07:00
host : { '[invalidProp]' : 'someProp' }
});
expect (() => parse ( '<div></div>' , [ dirA ])). toThrowError ( `Template parse errors:
2016-07-29 20:16:38 -07:00
Can't bind to 'invalidProp' since it isn't a known property of 'div'. ("[ERROR ->]<div></div>"): TestComp@0:0, Directive DirA` );
2016-07-28 02:27:07 -07:00
});
2015-08-27 16:29:02 -07:00
it ( 'should report errors in expressions' , () => {
2016-06-22 14:58:57 -07:00
expect (() => parse ( '<div [prop]="a b"></div>' , [])). toThrowError ( `Template parse errors:
2015-11-10 15:56:25 -08:00
Parser Error: Unexpected token 'b' at column 3 in [a b] in TestComp@0:5 ("<div [ERROR ->][prop]="a b"></div>"): TestComp@0:5` );
2015-08-27 16:29:02 -07:00
});
it ( 'should not throw on invalid property names if the property is used by a directive' ,
() => {
2015-09-18 10:33:23 -07:00
var dirA = CompileDirectiveMetadata . create ({
2015-08-27 16:29:02 -07:00
selector : 'div' ,
2016-08-24 17:39:49 -07:00
type : new CompileTypeMetadata (
2016-08-29 08:52:25 -07:00
{ moduleUrl : someModuleUrl , name : 'DirA' , reference : {} as Type < any >}),
2015-09-30 20:59:23 -07:00
inputs : [ 'invalidProp' ]
2015-08-27 16:29:02 -07:00
});
expect (() => parse ( '<div [invalid-prop]></div>' , [ dirA ])). not . toThrow ();
});
it ( 'should not allow more than 1 component per element' , () => {
2015-09-18 10:33:23 -07:00
var dirA = CompileDirectiveMetadata . create ({
2015-09-11 13:37:05 -07:00
selector : 'div' ,
isComponent : true ,
2016-08-24 17:39:49 -07:00
type : new CompileTypeMetadata (
2016-08-29 08:52:25 -07:00
{ moduleUrl : someModuleUrl , name : 'DirA' , reference : {} as Type < any >}),
2015-09-18 10:33:23 -07:00
template : new CompileTemplateMetadata ({ ngContentSelectors : []})
2015-09-11 13:37:05 -07:00
});
2015-09-18 10:33:23 -07:00
var dirB = CompileDirectiveMetadata . create ({
2015-09-11 13:37:05 -07:00
selector : 'div' ,
isComponent : true ,
2016-08-24 17:39:49 -07:00
type : new CompileTypeMetadata (
2016-08-29 08:52:25 -07:00
{ moduleUrl : someModuleUrl , name : 'DirB' , reference : {} as Type < any >}),
2015-09-18 10:33:23 -07:00
template : new CompileTemplateMetadata ({ ngContentSelectors : []})
2015-09-11 13:37:05 -07:00
});
2015-12-03 16:10:20 -08:00
expect (() => parse ( '<div>' , [ dirB , dirA ])). toThrowError ( `Template parse errors:
More than one component: DirB,DirA ("[ERROR ->]<div>"): TestComp@0:0` );
2015-08-27 16:29:02 -07:00
});
2015-10-13 17:43:15 -07:00
it ( 'should not allow components or element bindings nor dom events on explicit embedded templates' ,
2015-08-27 16:29:02 -07:00
() => {
2015-09-18 10:33:23 -07:00
var dirA = CompileDirectiveMetadata . create ({
2015-09-11 13:37:05 -07:00
selector : '[a]' ,
isComponent : true ,
2016-08-24 17:39:49 -07:00
type : new CompileTypeMetadata (
2016-08-29 08:52:25 -07:00
{ moduleUrl : someModuleUrl , name : 'DirA' , reference : {} as Type < any >}),
2015-09-18 10:33:23 -07:00
template : new CompileTemplateMetadata ({ ngContentSelectors : []})
2015-09-11 13:37:05 -07:00
});
2015-08-27 16:29:02 -07:00
expect (() => parse ( '<template [a]="b" (e)="f"></template>' , [ dirA ]))
. toThrowError ( `Template parse errors:
2016-06-23 17:51:39 +02:00
Event binding e not emitted by any directive on an embedded template. Make sure that the event name is spelled correctly and all directives are listed in the "directives" section. ("<template [a]="b" [ERROR ->](e)="f"></template>"): TestComp@0:18
2015-11-10 15:56:25 -08:00
Components on an embedded template: DirA ("[ERROR ->]<template [a]="b" (e)="f"></template>"): TestComp@0:0
2016-06-23 17:51:39 +02:00
Property binding a not used by any directive on an embedded template. Make sure that the property name is spelled correctly and all directives are listed in the "directives" section. ("[ERROR ->]<template [a]="b" (e)="f"></template>"): TestComp@0:0` );
2015-08-27 16:29:02 -07:00
});
it ( 'should not allow components or element bindings on inline embedded templates' , () => {
2015-09-18 10:33:23 -07:00
var dirA = CompileDirectiveMetadata . create ({
2015-09-11 13:37:05 -07:00
selector : '[a]' ,
isComponent : true ,
2016-08-24 17:39:49 -07:00
type : new CompileTypeMetadata (
2016-08-29 08:52:25 -07:00
{ moduleUrl : someModuleUrl , name : 'DirA' , reference : {} as Type < any >}),
2015-09-18 10:33:23 -07:00
template : new CompileTemplateMetadata ({ ngContentSelectors : []})
2015-09-11 13:37:05 -07:00
});
2015-10-07 09:34:21 -07:00
expect (() => parse ( '<div *a="b"></div>' , [ dirA ])). toThrowError ( `Template parse errors:
2015-11-10 15:56:25 -08:00
Components on an embedded template: DirA ("[ERROR ->]<div *a="b"></div>"): TestComp@0:0
2016-06-23 17:51:39 +02:00
Property binding a not used by any directive on an embedded template. Make sure that the property name is spelled correctly and all directives are listed in the "directives" section. ("[ERROR ->]<div *a="b"></div>"): TestComp@0:0` );
2015-08-27 16:29:02 -07:00
});
});
2015-09-18 10:33:23 -07:00
describe ( 'ignore elements' , () => {
2015-10-07 09:34:21 -07:00
it ( 'should ignore <script> elements' , () => {
2015-11-10 15:56:25 -08:00
expect ( humanizeTplAst ( parse ( '<script></script>a' , []))). toEqual ([[ TextAst , 'a' ]]);
2015-09-18 10:33:23 -07:00
});
2015-10-07 09:34:21 -07:00
it ( 'should ignore <style> elements' , () => {
2015-11-10 15:56:25 -08:00
expect ( humanizeTplAst ( parse ( '<style></style>a' , []))). toEqual ([[ TextAst , 'a' ]]);
2015-09-18 10:33:23 -07:00
});
2015-10-14 09:39:40 -07:00
describe ( '<link rel="stylesheet">' , () => {
it ( 'should keep <link rel="stylesheet"> elements if they have an absolute non package: url' ,
() => {
2015-12-03 15:53:44 -08:00
expect ( humanizeTplAst ( parse ( '<link rel="stylesheet" href="http://someurl">a' , [])))
2015-10-14 09:39:40 -07:00
. toEqual ([
2016-06-08 16:38:52 -07:00
[ ElementAst , 'link' ], [ AttrAst , 'rel' , 'stylesheet' ],
[ AttrAst , 'href' , 'http://someurl' ], [ TextAst , 'a' ]
2015-10-14 09:39:40 -07:00
]);
});
it ( 'should keep <link rel="stylesheet"> elements if they have no uri' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<link rel="stylesheet">a' , [
]))). toEqual ([[ ElementAst , 'link' ], [ AttrAst , 'rel' , 'stylesheet' ], [ TextAst , 'a' ]]);
expect ( humanizeTplAst ( parse ( '<link REL="stylesheet">a' , [
]))). toEqual ([[ ElementAst , 'link' ], [ AttrAst , 'REL' , 'stylesheet' ], [ TextAst , 'a' ]]);
2015-10-14 09:39:40 -07:00
});
it ( 'should ignore <link rel="stylesheet"> elements if they have a relative uri' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<link rel="stylesheet" href="./other.css">a' , [
]))). toEqual ([[ TextAst , 'a' ]]);
expect ( humanizeTplAst ( parse ( '<link rel="stylesheet" HREF="./other.css">a' , [
]))). toEqual ([[ TextAst , 'a' ]]);
2015-10-14 09:39:40 -07:00
});
it ( 'should ignore <link rel="stylesheet"> elements if they have a package: uri' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<link rel="stylesheet" href="package:somePackage">a' , [
]))). toEqual ([[ TextAst , 'a' ]]);
2015-10-14 09:39:40 -07:00
});
2015-09-18 10:33:23 -07:00
});
2015-11-23 16:02:19 -08:00
it ( 'should ignore bindings on children of elements with ngNonBindable' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<div ngNonBindable>{{b}}</div>' , [
]))). toEqual ([[ ElementAst , 'div' ], [ AttrAst , 'ngNonBindable' , '' ], [ TextAst , '{{b}}' ]]);
2015-09-18 10:33:23 -07:00
});
2015-09-18 10:33:23 -07:00
2015-11-23 16:02:19 -08:00
it ( 'should keep nested children of elements with ngNonBindable' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<div ngNonBindable><span>{{b}}</span></div>' , []))). toEqual ([
[ ElementAst , 'div' ], [ AttrAst , 'ngNonBindable' , '' ], [ ElementAst , 'span' ],
[ TextAst , '{{b}}' ]
]);
2015-09-18 10:33:23 -07:00
});
2015-11-23 16:02:19 -08:00
it ( 'should ignore <script> elements inside of elements with ngNonBindable' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<div ngNonBindable><script></script>a</div>' , [
]))). toEqual ([[ ElementAst , 'div' ], [ AttrAst , 'ngNonBindable' , '' ], [ TextAst , 'a' ]]);
2015-10-07 09:34:21 -07:00
});
2015-09-18 10:33:23 -07:00
2015-11-23 16:02:19 -08:00
it ( 'should ignore <style> elements inside of elements with ngNonBindable' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<div ngNonBindable><style></style>a</div>' , [
]))). toEqual ([[ ElementAst , 'div' ], [ AttrAst , 'ngNonBindable' , '' ], [ TextAst , 'a' ]]);
2015-10-07 09:34:21 -07:00
});
2015-09-18 10:33:23 -07:00
2015-11-23 16:02:19 -08:00
it ( 'should ignore <link rel="stylesheet"> elements inside of elements with ngNonBindable' ,
2015-09-18 10:33:23 -07:00
() => {
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAst ( parse ( '<div ngNonBindable><link rel="stylesheet">a</div>' , [
]))). toEqual ([[ ElementAst , 'div' ], [ AttrAst , 'ngNonBindable' , '' ], [ TextAst , 'a' ]]);
2015-09-18 10:33:23 -07:00
});
2015-11-23 16:02:19 -08:00
it ( 'should convert <ng-content> elements into regular elements inside of elements with ngNonBindable' ,
2015-09-18 10:33:23 -07:00
() => {
2015-11-23 16:02:19 -08:00
expect ( humanizeTplAst ( parse ( '<div ngNonBindable><ng-content></ng-content>a</div>' , [])))
2015-09-18 10:33:23 -07:00
. toEqual ([
2016-06-08 16:38:52 -07:00
[ ElementAst , 'div' ], [ AttrAst , 'ngNonBindable' , '' ], [ ElementAst , 'ng-content' ],
2015-10-07 09:34:21 -07:00
[ TextAst , 'a' ]
2015-09-18 10:33:23 -07:00
]);
2015-09-18 10:33:23 -07:00
});
});
2015-11-10 15:56:25 -08:00
describe ( 'source spans' , () => {
it ( 'should support ng-content' , () => {
var parsed = parse ( '<ng-content select="a">' , []);
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAstSourceSpans ( parsed )). toEqual ([
[ NgContentAst , '<ng-content select="a">' ]
]);
2015-11-10 15:56:25 -08:00
});
it ( 'should support embedded template' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAstSourceSpans ( parse ( '<template></template>' , [
]))). toEqual ([[ EmbeddedTemplateAst , '<template>' ]]);
2015-11-10 15:56:25 -08:00
});
it ( 'should support element and attributes' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAstSourceSpans ( parse ( '<div key=value>' , []))). toEqual ([
[ ElementAst , 'div' , '<div key=value>' ], [ AttrAst , 'key' , 'value' , 'key=value' ]
]);
2015-11-10 15:56:25 -08:00
});
2016-04-25 19:52:24 -07:00
it ( 'should support references' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAstSourceSpans ( parse ( '<div #a></div>' , [
]))). toEqual ([[ ElementAst , 'div' , '<div #a>' ], [ ReferenceAst , 'a' , null , '#a' ]]);
2016-04-25 19:52:24 -07:00
});
2015-11-10 15:56:25 -08:00
it ( 'should support variables' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAstSourceSpans ( parse ( '<template let-a="b"></template>' , []))). toEqual ([
[ EmbeddedTemplateAst , '<template let-a="b">' ], [ VariableAst , 'a' , 'b' , 'let-a="b"' ]
]);
2015-11-10 15:56:25 -08:00
});
2016-04-25 19:52:24 -07:00
it ( 'should support events' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAstSourceSpans ( parse ( '<div (window:event)="v">' , []))). toEqual ([
[ ElementAst , 'div' , '<div (window:event)="v">' ],
[ BoundEventAst , 'event' , 'window' , 'v' , '(window:event)="v"' ]
]);
2015-11-10 15:56:25 -08:00
});
it ( 'should support element property' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAstSourceSpans ( parse ( '<div [someProp]="v">' , []))). toEqual ([
[ ElementAst , 'div' , '<div [someProp]="v">' ],
[
BoundElementPropertyAst , PropertyBindingType . Property , 'someProp' , 'v' , null ,
'[someProp]="v"'
]
]);
2015-11-10 15:56:25 -08:00
});
it ( 'should support bound text' , () => {
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAstSourceSpans ( parse ( '{{a}}' , [
]))). toEqual ([[ BoundTextAst , '{{ a }}' , '{{a}}' ]]);
2015-11-10 15:56:25 -08:00
});
it ( 'should support text nodes' , () => {
expect ( humanizeTplAstSourceSpans ( parse ( 'a' , []))). toEqual ([[ TextAst , 'a' , 'a' ]]);
});
it ( 'should support directive' , () => {
2016-01-06 14:13:44 -08:00
var dirA = CompileDirectiveMetadata . create ({
selector : '[a]' ,
2016-08-24 17:39:49 -07:00
type : new CompileTypeMetadata (
2016-08-29 08:52:25 -07:00
{ moduleUrl : someModuleUrl , name : 'DirA' , reference : {} as Type < any >})
2016-01-06 14:13:44 -08:00
});
2015-11-10 15:56:25 -08:00
var comp = CompileDirectiveMetadata . create ({
selector : 'div' ,
isComponent : true ,
2016-08-24 17:39:49 -07:00
type : new CompileTypeMetadata (
2016-08-29 08:52:25 -07:00
{ moduleUrl : someModuleUrl , name : 'ZComp' , reference : {} as Type < any >}),
2015-11-10 15:56:25 -08:00
template : new CompileTemplateMetadata ({ ngContentSelectors : []})
});
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAstSourceSpans ( parse ( '<div a>' , [ dirA , comp ]))). toEqual ([
[ ElementAst , 'div' , '<div a>' ], [ AttrAst , 'a' , '' , 'a' ], [ DirectiveAst , dirA , '<div a>' ],
[ DirectiveAst , comp , '<div a>' ]
]);
2015-11-10 15:56:25 -08:00
});
2015-12-09 09:32:15 -08:00
it ( 'should support directive in namespace' , () => {
2016-01-06 14:13:44 -08:00
var tagSel = CompileDirectiveMetadata . create ({
selector : 'circle' ,
2016-08-24 17:39:49 -07:00
type : new CompileTypeMetadata (
2016-08-29 08:52:25 -07:00
{ moduleUrl : someModuleUrl , name : 'elDir' , reference : {} as Type < any >})
2016-01-06 14:13:44 -08:00
});
var attrSel = CompileDirectiveMetadata . create ({
selector : '[href]' ,
2016-08-24 17:39:49 -07:00
type : new CompileTypeMetadata (
2016-08-29 08:52:25 -07:00
{ moduleUrl : someModuleUrl , name : 'attrDir' , reference : {} as Type < any >})
2016-01-06 14:13:44 -08:00
});
2015-12-09 09:32:15 -08:00
expect ( humanizeTplAstSourceSpans (
parse ( '<svg><circle /><use xlink:href="Port" /></svg>' , [ tagSel , attrSel ])))
. toEqual ([
2016-04-29 14:54:46 -07:00
[ ElementAst , ':svg:svg' , '<svg>' ],
[ ElementAst , ':svg:circle' , '<circle />' ],
2015-12-09 09:32:15 -08:00
[ DirectiveAst , tagSel , '<circle />' ],
2016-04-29 14:54:46 -07:00
[ ElementAst , ':svg:use' , '<use xlink:href="Port" />' ],
[ AttrAst , ':xlink:href' , 'Port' , 'xlink:href="Port"' ],
2015-12-09 09:32:15 -08:00
[ DirectiveAst , attrSel , '<use xlink:href="Port" />' ],
]);
});
2015-11-10 15:56:25 -08:00
it ( 'should support directive property' , () => {
2016-01-06 14:13:44 -08:00
var dirA = CompileDirectiveMetadata . create ({
selector : 'div' ,
2016-08-24 17:39:49 -07:00
type : new CompileTypeMetadata (
2016-08-29 08:52:25 -07:00
{ moduleUrl : someModuleUrl , name : 'DirA' , reference : {} as Type < any >}),
2016-01-06 14:13:44 -08:00
inputs : [ 'aProp' ]
});
2016-06-08 16:38:52 -07:00
expect ( humanizeTplAstSourceSpans ( parse ( '<div [aProp]="foo"></div>' , [ dirA ]))). toEqual ([
[ ElementAst , 'div' , '<div [aProp]="foo">' ], [ DirectiveAst , dirA , '<div [aProp]="foo">' ],
[ BoundDirectivePropertyAst , 'aProp' , 'foo' , '[aProp]="foo"' ]
]);
2015-11-10 15:56:25 -08:00
});
});
2015-12-02 10:35:51 -08:00
describe ( 'pipes' , () => {
it ( 'should allow pipes that have been defined as dependencies' , () => {
2016-01-06 14:13:44 -08:00
var testPipe = new CompilePipeMetadata ({
name : 'test' ,
2016-08-24 17:39:49 -07:00
type : new CompileTypeMetadata (
2016-08-29 08:52:25 -07:00
{ moduleUrl : someModuleUrl , name : 'DirA' , reference : {} as Type < any >})
2016-01-06 14:13:44 -08:00
});
2015-12-02 10:35:51 -08:00
expect (() => parse ( '{{a | test}}' , [], [ testPipe ])). not . toThrow ();
});
it ( 'should report pipes as error that have not been defined as dependencies' , () => {
expect (() => parse ( '{{a | test}}' , [])). toThrowError ( `Template parse errors:
The pipe 'test' could not be found ("[ERROR ->]{{a | test}}"): TestComp@0:0` );
});
});
2016-06-30 14:59:23 -07:00
describe ( 'ICU messages' , () => {
it ( 'should expand plural messages' , () => {
const shortForm = '{ count, plural, =0 {small} many {big} }' ;
const expandedForm = '<ng-container [ngPlural]="count">' +
'<template ngPluralCase="=0">small</template>' +
'<template ngPluralCase="many">big</template>' +
'</ng-container>' ;
expect ( humanizeTplAst ( parse ( shortForm , []))). toEqual ( humanizeTplAst ( parse ( expandedForm , [
])));
});
it ( 'should expand other messages' , () => {
const shortForm = '{ sex, gender, =f {foo} other {bar} }' ;
const expandedForm = '<ng-container [ngSwitch]="sex">' +
'<template ngSwitchCase="=f">foo</template>' +
'<template ngSwitchCase="other">bar</template>' +
'</ng-container>' ;
expect ( humanizeTplAst ( parse ( shortForm , []))). toEqual ( humanizeTplAst ( parse ( expandedForm , [
])));
});
it ( 'should be possible to escape ICU messages' , () => {
const escapedForm = 'escaped {{ "{" }} }' ;
expect ( humanizeTplAst ( parse ( escapedForm , []))). toEqual ([
[ BoundTextAst , 'escaped {{ "{" }} }' ],
]);
});
});
2015-08-25 15:36:02 -07:00
});
}
2016-06-20 09:52:41 -07:00
function humanizeTplAst (
templateAsts : TemplateAst [], interpolationConfig? : InterpolationConfig ) : any [] {
const humanizer = new TemplateHumanizer ( false , interpolationConfig );
2015-11-10 15:56:25 -08:00
templateVisitAll ( humanizer , templateAsts );
return humanizer . result ;
}
2016-06-20 09:52:41 -07:00
function humanizeTplAstSourceSpans (
templateAsts : TemplateAst [], interpolationConfig? : InterpolationConfig ) : any [] {
const humanizer = new TemplateHumanizer ( true , interpolationConfig );
2015-08-25 15:36:02 -07:00
templateVisitAll ( humanizer , templateAsts );
return humanizer . result ;
}
class TemplateHumanizer implements TemplateAstVisitor {
result : any [] = [];
2015-11-10 15:56:25 -08:00
2016-06-20 09:52:41 -07:00
constructor (
private includeSourceSpan : boolean ,
private interpolationConfig : InterpolationConfig = DEFAULT_INTERPOLATION_CONFIG ){};
2015-11-10 15:56:25 -08:00
2015-09-01 16:24:23 -07:00
visitNgContent ( ast : NgContentAst , context : any ) : any {
2015-11-10 15:56:25 -08:00
var res = [ NgContentAst ];
this . result . push ( this . _appendContext ( ast , res ));
2015-08-25 15:36:02 -07:00
return null ;
}
2015-09-01 16:24:23 -07:00
visitEmbeddedTemplate ( ast : EmbeddedTemplateAst , context : any ) : any {
2015-11-10 15:56:25 -08:00
var res = [ EmbeddedTemplateAst ];
this . result . push ( this . _appendContext ( ast , res ));
2015-08-25 15:36:02 -07:00
templateVisitAll ( this , ast . attrs );
2015-10-23 16:08:46 -07:00
templateVisitAll ( this , ast . outputs );
2016-04-25 19:52:24 -07:00
templateVisitAll ( this , ast . references );
templateVisitAll ( this , ast . variables );
2015-08-27 16:29:02 -07:00
templateVisitAll ( this , ast . directives );
2015-08-25 15:36:02 -07:00
templateVisitAll ( this , ast . children );
return null ;
}
2015-09-01 16:24:23 -07:00
visitElement ( ast : ElementAst , context : any ) : any {
2015-11-10 15:56:25 -08:00
var res = [ ElementAst , ast . name ];
this . result . push ( this . _appendContext ( ast , res ));
2015-08-25 15:36:02 -07:00
templateVisitAll ( this , ast . attrs );
2015-09-30 20:59:23 -07:00
templateVisitAll ( this , ast . inputs );
templateVisitAll ( this , ast . outputs );
2016-04-25 19:52:24 -07:00
templateVisitAll ( this , ast . references );
2015-08-27 16:29:02 -07:00
templateVisitAll ( this , ast . directives );
2015-08-25 15:36:02 -07:00
templateVisitAll ( this , ast . children );
return null ;
}
2016-04-25 19:52:24 -07:00
visitReference ( ast : ReferenceAst , context : any ) : any {
var res = [ ReferenceAst , ast . name , ast . value ];
this . result . push ( this . _appendContext ( ast , res ));
return null ;
}
2015-09-01 16:24:23 -07:00
visitVariable ( ast : VariableAst , context : any ) : any {
2015-11-10 15:56:25 -08:00
var res = [ VariableAst , ast . name , ast . value ];
this . result . push ( this . _appendContext ( ast , res ));
2015-08-25 15:36:02 -07:00
return null ;
}
2015-09-01 16:24:23 -07:00
visitEvent ( ast : BoundEventAst , context : any ) : any {
2016-07-06 14:06:47 -07:00
var res = [ BoundEventAst , ast . name , ast . target , unparse ( ast . handler , this . interpolationConfig )];
2015-11-10 15:56:25 -08:00
this . result . push ( this . _appendContext ( ast , res ));
2015-08-25 15:36:02 -07:00
return null ;
}
2015-09-01 16:24:23 -07:00
visitElementProperty ( ast : BoundElementPropertyAst , context : any ) : any {
2015-11-10 15:56:25 -08:00
var res = [
2016-07-06 14:06:47 -07:00
BoundElementPropertyAst , ast . type , ast . name , unparse ( ast . value , this . interpolationConfig ),
ast . unit
2015-11-10 15:56:25 -08:00
];
this . result . push ( this . _appendContext ( ast , res ));
2015-08-25 15:36:02 -07:00
return null ;
}
2015-09-01 16:24:23 -07:00
visitAttr ( ast : AttrAst , context : any ) : any {
2015-11-10 15:56:25 -08:00
var res = [ AttrAst , ast . name , ast . value ];
this . result . push ( this . _appendContext ( ast , res ));
2015-08-25 15:36:02 -07:00
return null ;
}
2015-09-01 16:24:23 -07:00
visitBoundText ( ast : BoundTextAst , context : any ) : any {
2016-07-06 14:06:47 -07:00
var res = [ BoundTextAst , unparse ( ast . value , this . interpolationConfig )];
2015-11-10 15:56:25 -08:00
this . result . push ( this . _appendContext ( ast , res ));
2015-08-25 15:36:02 -07:00
return null ;
}
2015-09-01 16:24:23 -07:00
visitText ( ast : TextAst , context : any ) : any {
2015-11-10 15:56:25 -08:00
var res = [ TextAst , ast . value ];
this . result . push ( this . _appendContext ( ast , res ));
2015-08-25 15:36:02 -07:00
return null ;
}
2015-09-01 16:24:23 -07:00
visitDirective ( ast : DirectiveAst , context : any ) : any {
2015-11-10 15:56:25 -08:00
var res = [ DirectiveAst , ast . directive ];
this . result . push ( this . _appendContext ( ast , res ));
2015-09-30 20:59:23 -07:00
templateVisitAll ( this , ast . inputs );
2015-08-27 16:29:02 -07:00
templateVisitAll ( this , ast . hostProperties );
templateVisitAll ( this , ast . hostEvents );
return null ;
}
2015-09-01 16:24:23 -07:00
visitDirectiveProperty ( ast : BoundDirectivePropertyAst , context : any ) : any {
2016-06-20 09:52:41 -07:00
var res = [
2016-07-06 14:06:47 -07:00
BoundDirectivePropertyAst , ast . directiveName , unparse ( ast . value , this . interpolationConfig )
2016-06-20 09:52:41 -07:00
];
2015-11-10 15:56:25 -08:00
this . result . push ( this . _appendContext ( ast , res ));
2015-08-27 16:29:02 -07:00
return null ;
}
2015-11-10 15:56:25 -08:00
private _appendContext ( ast : TemplateAst , input : any []) : any [] {
if ( ! this . includeSourceSpan ) return input ;
input . push ( ast . sourceSpan . toString ());
return input ;
}
2015-08-27 16:29:02 -07:00
}
2015-10-07 09:34:21 -07:00
function sourceInfo ( ast : TemplateAst ) : string {
return ` ${ ast . sourceSpan } : ${ ast . sourceSpan . start } ` ;
}
2015-09-11 13:37:05 -07:00
function humanizeContentProjection ( templateAsts : TemplateAst []) : any [] {
var humanizer = new TemplateContentProjectionHumanizer ();
templateVisitAll ( humanizer , templateAsts );
return humanizer . result ;
}
class TemplateContentProjectionHumanizer implements TemplateAstVisitor {
result : any [] = [];
visitNgContent ( ast : NgContentAst , context : any ) : any {
this . result . push ([ 'ng-content' , ast . ngContentIndex ]);
return null ;
}
visitEmbeddedTemplate ( ast : EmbeddedTemplateAst , context : any ) : any {
this . result . push ([ 'template' , ast . ngContentIndex ]);
templateVisitAll ( this , ast . children );
return null ;
}
visitElement ( ast : ElementAst , context : any ) : any {
this . result . push ([ ast . name , ast . ngContentIndex ]);
templateVisitAll ( this , ast . children );
return null ;
}
2016-04-25 19:52:24 -07:00
visitReference ( ast : ReferenceAst , context : any ) : any { return null ; }
2015-09-11 13:37:05 -07:00
visitVariable ( ast : VariableAst , context : any ) : any { return null ; }
visitEvent ( ast : BoundEventAst , context : any ) : any { return null ; }
visitElementProperty ( ast : BoundElementPropertyAst , context : any ) : any { return null ; }
visitAttr ( ast : AttrAst , context : any ) : any { return null ; }
visitBoundText ( ast : BoundTextAst , context : any ) : any {
2016-07-06 14:06:47 -07:00
this . result . push ([ `#text( ${ unparse ( ast . value ) } )` , ast . ngContentIndex ]);
2015-09-11 13:37:05 -07:00
return null ;
}
visitText ( ast : TextAst , context : any ) : any {
this . result . push ([ `#text( ${ ast . value } )` , ast . ngContentIndex ]);
return null ;
}
visitDirective ( ast : DirectiveAst , context : any ) : any { return null ; }
visitDirectiveProperty ( ast : BoundDirectivePropertyAst , context : any ) : any { return null ; }
}
2015-11-19 10:51:16 -08:00
class FooAstTransformer implements TemplateAstVisitor {
visitNgContent ( ast : NgContentAst , context : any ) : any { throw 'not implemented' ; }
visitEmbeddedTemplate ( ast : EmbeddedTemplateAst , context : any ) : any { throw 'not implemented' ; }
visitElement ( ast : ElementAst , context : any ) : any {
if ( ast . name != 'div' ) return ast ;
2016-06-08 16:38:52 -07:00
return new ElementAst (
'foo' , [], [], [], [], [], [], false , [], ast . ngContentIndex , ast . sourceSpan );
2015-11-19 10:51:16 -08:00
}
2016-04-25 19:52:24 -07:00
visitReference ( ast : ReferenceAst , context : any ) : any { throw 'not implemented' ; }
2015-11-19 10:51:16 -08:00
visitVariable ( ast : VariableAst , context : any ) : any { throw 'not implemented' ; }
visitEvent ( ast : BoundEventAst , context : any ) : any { throw 'not implemented' ; }
visitElementProperty ( ast : BoundElementPropertyAst , context : any ) : any { throw 'not implemented' ; }
visitAttr ( ast : AttrAst , context : any ) : any { throw 'not implemented' ; }
visitBoundText ( ast : BoundTextAst , context : any ) : any { throw 'not implemented' ; }
visitText ( ast : TextAst , context : any ) : any { throw 'not implemented' ; }
visitDirective ( ast : DirectiveAst , context : any ) : any { throw 'not implemented' ; }
visitDirectiveProperty ( ast : BoundDirectivePropertyAst , context : any ) : any {
throw 'not implemented' ;
}
}
class BarAstTransformer extends FooAstTransformer {
visitElement ( ast : ElementAst , context : any ) : any {
if ( ast . name != 'foo' ) return ast ;
2016-06-08 16:38:52 -07:00
return new ElementAst (
'bar' , [], [], [], [], [], [], false , [], ast . ngContentIndex , ast . sourceSpan );
2015-11-19 10:51:16 -08:00
}
}
2016-04-25 19:52:24 -07:00
class ArrayConsole implements Console {
logs : string [] = [];
warnings : string [] = [];
log ( msg : string ) { this . logs . push ( msg ); }
warn ( msg : string ) { this . warnings . push ( msg ); }
2016-04-28 17:50:03 -07:00
}