2015-09-14 15:59:09 -07:00
import {
ddescribe ,
describe ,
it ,
iit ,
xit ,
expect ,
beforeEach ,
afterEach ,
inject ,
2015-10-07 09:34:21 -07:00
beforeEachProviders
2015-10-13 00:29:13 -07:00
} from 'angular2/testing_internal' ;
2015-10-10 22:11:13 -07:00
import { provide } from 'angular2/src/core/di' ;
2015-09-14 15:59:09 -07:00
2015-10-10 22:11:13 -07:00
import { TEST_PROVIDERS } from './test_bindings' ;
2015-11-06 17:34:07 -08:00
import { isPresent } from 'angular2/src/facade/lang' ;
2015-11-19 10:51:16 -08:00
import {
TemplateParser ,
splitClasses ,
TEMPLATE_TRANSFORMS
} from 'angular2/src/compiler/template_parser' ;
2015-09-11 13:37:05 -07:00
import {
2015-09-18 10:33:23 -07:00
CompileDirectiveMetadata ,
CompileTypeMetadata ,
CompileTemplateMetadata
2015-11-05 14:07:57 -08:00
} from 'angular2/src/compiler/directive_metadata' ;
2015-08-25 15:36:02 -07:00
import {
templateVisitAll ,
TemplateAstVisitor ,
TemplateAst ,
NgContentAst ,
EmbeddedTemplateAst ,
ElementAst ,
VariableAst ,
BoundEventAst ,
2015-08-27 16:29:02 -07:00
BoundElementPropertyAst ,
BoundDirectivePropertyAst ,
2015-08-25 15:36:02 -07:00
AttrAst ,
BoundTextAst ,
2015-08-27 16:29:02 -07:00
TextAst ,
PropertyBindingType ,
DirectiveAst
2015-11-05 14:07:57 -08:00
} from 'angular2/src/compiler/template_ast' ;
2015-08-25 15:36:02 -07:00
2015-11-05 14:07:57 -08:00
import { ElementSchemaRegistry } from 'angular2/src/compiler/schema/element_schema_registry' ;
2015-09-14 15:59:09 -07:00
import { MockSchemaRegistry } from './schema_registry_mock' ;
2015-08-27 16:29:02 -07:00
2015-11-05 14:07:57 -08:00
import { Unparser } from '../core/change_detection/parser/unparser' ;
2015-08-25 15:36:02 -07:00
var expressionUnparser = new Unparser ( ) ;
export function main() {
describe ( 'TemplateParser' , ( ) = > {
2015-10-07 09:34:21 -07:00
beforeEachProviders ( ( ) = > [
2015-10-10 22:11:13 -07:00
TEST_PROVIDERS ,
provide ( ElementSchemaRegistry ,
{
2015-10-12 11:30:34 -07:00
useValue : new MockSchemaRegistry ( { 'invalidProp' : false } ,
{ 'mappedAttr' : 'mappedProp' } )
2015-10-10 22:11:13 -07:00
} )
2015-09-14 15:59:09 -07:00
] ) ;
2015-08-25 15:36:02 -07:00
var parser : TemplateParser ;
2015-08-27 16:29:02 -07:00
var ngIf ;
2015-08-25 15:36:02 -07:00
2015-09-14 15:59:09 -07:00
beforeEach ( inject ( [ TemplateParser ] , ( _parser ) = > {
parser = _parser ;
2015-09-30 20:59:23 -07:00
ngIf = CompileDirectiveMetadata . create (
2015-11-23 16:02:19 -08:00
{ selector : '[ngIf]' , type : new CompileTypeMetadata ( { name : 'NgIf' } ) , inputs : [ 'ngIf' ] } ) ;
2015-09-14 15:59:09 -07:00
} ) ) ;
2015-08-25 15:36:02 -07:00
2015-09-18 10:33:23 -07:00
function parse ( template : string , directives : CompileDirectiveMetadata [ ] ) : TemplateAst [ ] {
2015-09-14 15:59:09 -07:00
return parser . parse ( template , directives , 'TestComp' ) ;
2015-08-25 15:36:02 -07:00
}
2015-11-19 10:51:16 -08:00
describe ( 'template transform' , ( ) = > {
beforeEachProviders (
( ) = > [ provide ( TEMPLATE_TRANSFORMS , { useValue : new FooAstTransformer ( ) , multi : true } ) ] ) ;
it ( 'should transform TemplateAST' ,
( ) = > { expect ( humanizeTplAst ( parse ( '<div>' , [ ] ) ) ) . toEqual ( [ [ ElementAst , 'foo' ] ] ) ; } ) ;
describe ( 'multiple' , ( ) = > {
beforeEachProviders (
( ) = > [ provide ( TEMPLATE_TRANSFORMS , { useValue : new BarAstTransformer ( ) , multi : true } ) ] ) ;
it ( 'should compose transformers' ,
( ) = > { expect ( humanizeTplAst ( parse ( '<div>' , [ ] ) ) ) . toEqual ( [ [ ElementAst , 'bar' ] ] ) ; } ) ;
} ) ;
} ) ;
2015-08-25 15:36:02 -07:00
describe ( 'parse' , ( ) = > {
describe ( 'nodes without bindings' , ( ) = > {
2015-10-07 09:34:21 -07:00
it ( 'should parse text nodes' ,
2015-11-10 15:56:25 -08:00
( ) = > { expect ( humanizeTplAst ( parse ( 'a' , [ ] ) ) ) . toEqual ( [ [ TextAst , 'a' ] ] ) ; } ) ;
2015-08-25 15:36:02 -07:00
it ( 'should parse elements with attributes' , ( ) = > {
2015-11-10 15:56:25 -08:00
expect ( humanizeTplAst ( parse ( '<div a=b>' , [ ] ) ) )
2015-10-07 09:34:21 -07:00
. 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>' , [ ] ) ;
expect ( humanizeTplAst ( parsed ) )
. toEqual ( [
[ ElementAst , '@svg:svg' ] ,
[ NgContentAst ] ,
] ) ;
} ) ;
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
} ) ;
2015-08-27 16:29:02 -07:00
describe ( 'bound properties' , ( ) = > {
2015-08-25 15:36:02 -07:00
2015-11-10 15:56:25 -08:00
it ( 'should parse mixed case bound properties' , ( ) = > {
expect ( humanizeTplAst ( parse ( '<div [someProp]="v">' , [ ] ) ) )
. toEqual ( [
[ ElementAst , 'div' ] ,
[ BoundElementPropertyAst , PropertyBindingType . Property , 'someProp' , 'v' , null ]
] ) ;
} ) ;
2015-11-23 16:02:19 -08:00
it ( 'should parse dash case bound properties' , ( ) = > {
2015-11-10 15:56:25 -08:00
expect ( humanizeTplAst ( parse ( '<div [some-prop]="v">' , [ ] ) ) )
2015-08-25 15:36:02 -07:00
. toEqual ( [
2015-10-07 09:34:21 -07:00
[ ElementAst , 'div' ] ,
2015-11-23 16:02:19 -08:00
[ BoundElementPropertyAst , PropertyBindingType . Property , 'some-prop' , 'v' , null ]
2015-08-25 15:36:02 -07:00
] ) ;
} ) ;
2015-08-27 16:29:02 -07:00
it ( 'should normalize property names via the element schema' , ( ) = > {
2015-11-23 16:02:19 -08:00
expect ( humanizeTplAst ( parse ( '<div [mappedAttr]="v">' , [ ] ) ) )
2015-08-27 16:29:02 -07:00
. toEqual ( [
2015-10-07 09:34:21 -07:00
[ 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' , ( ) = > {
expect ( humanizeTplAst ( parse ( '<div [attr.someAttr]="v">' , [ ] ) ) )
. toEqual ( [
[ ElementAst , 'div' ] ,
[ BoundElementPropertyAst , PropertyBindingType . Attribute , 'someAttr' , 'v' , null ]
] ) ;
} ) ;
2015-08-27 16:29:02 -07:00
it ( 'should parse and dash case bound classes' , ( ) = > {
2015-11-10 15:56:25 -08:00
expect ( humanizeTplAst ( parse ( '<div [class.some-class]="v">' , [ ] ) ) )
2015-08-27 16:29:02 -07:00
. toEqual ( [
2015-10-07 09:34:21 -07:00
[ ElementAst , 'div' ] ,
[ BoundElementPropertyAst , PropertyBindingType . Class , 'some-class' , 'v' , null ]
2015-08-27 16:29:02 -07:00
] ) ;
2015-11-10 15:56:25 -08:00
} ) ;
it ( 'should parse mixed case bound classes' , ( ) = > {
expect ( humanizeTplAst ( parse ( '<div [class.someClass]="v">' , [ ] ) ) )
. toEqual ( [
[ ElementAst , 'div' ] ,
[ BoundElementPropertyAst , PropertyBindingType . Class , 'someClass' , 'v' , null ]
] ) ;
2015-08-27 16:29:02 -07:00
} ) ;
2015-11-23 16:02:19 -08:00
it ( 'should parse mixed case bound styles' , ( ) = > {
expect ( humanizeTplAst ( parse ( '<div [style.someStyle]="v">' , [ ] ) ) )
2015-11-10 15:56:25 -08:00
. toEqual ( [
[ ElementAst , 'div' ] ,
[ BoundElementPropertyAst , PropertyBindingType . Style , 'someStyle' , 'v' , null ]
] ) ;
} ) ;
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
} ) ;
it ( 'should parse bound properties via [...] and not report them as attributes' , ( ) = > {
2015-11-10 15:56:25 -08:00
expect ( humanizeTplAst ( parse ( '<div [prop]="v">' , [ ] ) ) )
2015-08-25 15:36:02 -07:00
. toEqual ( [
2015-10-07 09:34:21 -07:00
[ 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' , ( ) = > {
2015-11-10 15:56:25 -08: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' , ( ) = > {
2015-11-10 15:56:25 -08:00
expect ( humanizeTplAst ( parse ( '<div prop="{{v}}">' , [ ] ) ) )
2015-08-25 15:36:02 -07:00
. toEqual ( [
2015-10-07 09:34:21 -07:00
[ ElementAst , 'div' ] ,
[ BoundElementPropertyAst , PropertyBindingType . Property , 'prop' , '{{ v }}' , null ]
2015-08-27 16:29:02 -07:00
] ) ;
} ) ;
} ) ;
describe ( 'events' , ( ) = > {
it ( 'should parse bound events with a target' , ( ) = > {
2015-11-10 15:56:25 -08:00
expect ( humanizeTplAst ( parse ( '<div (window:event)="v">' , [ ] ) ) )
2015-10-07 09:34:21 -07:00
. toEqual ( [ [ ElementAst , 'div' ] , [ BoundEventAst , 'event' , 'window' , 'v' ] ] ) ;
2015-08-25 15:36:02 -07:00
} ) ;
it ( 'should parse bound events via (...) and not report them as attributes' , ( ) = > {
2015-11-10 15:56:25 -08:00
expect ( humanizeTplAst ( parse ( '<div (event)="v">' , [ ] ) ) )
2015-10-07 09:34:21 -07:00
. 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' , ( ) = > {
2015-11-10 15:56:25 -08:00
expect ( humanizeTplAst ( parse ( '<div (some-event)="v">' , [ ] ) ) )
2015-11-23 16:02:19 -08:00
. toEqual ( [ [ ElementAst , 'div' ] , [ BoundEventAst , 'some-event' , null , 'v' ] ] ) ;
2015-11-10 15:56:25 -08:00
expect ( humanizeTplAst ( parse ( '<div (someEvent)="v">' , [ ] ) ) )
2015-10-07 09:34:21 -07:00
. 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' , ( ) = > {
2015-11-10 15:56:25 -08: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' ] ,
type : new CompileTypeMetadata ( { name : 'DirA' } )
} ) ;
2015-11-10 15:56:25 -08:00
expect ( humanizeTplAst ( parse ( '<template (e)="f"></template>' , [ dirA ] ) ) )
2015-10-13 17:43:15 -07:00
. toEqual ( [
2015-10-07 09:34:21 -07:00
[ 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' ,
( ) = > {
2015-11-10 15:56:25 -08:00
expect ( humanizeTplAst ( parse ( '<div [(prop)]="v">' , [ ] ) ) )
2015-08-25 15:36:02 -07:00
. toEqual ( [
2015-10-07 09:34:21 -07:00
[ 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' ,
( ) = > {
2015-11-10 15:56:25 -08: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' , ( ) = > {
2015-09-18 10:33:23 -07:00
it ( 'should locate directives components first and ordered by the directives array in the View' ,
( ) = > {
var dirA = CompileDirectiveMetadata . create (
2015-09-28 10:30:33 -07:00
{ selector : '[a]' , type : new CompileTypeMetadata ( { name : 'DirA' } ) } ) ;
2015-09-18 10:33:23 -07:00
var dirB = CompileDirectiveMetadata . create (
2015-09-28 10:30:33 -07:00
{ selector : '[b]' , type : new CompileTypeMetadata ( { name : 'DirB' } ) } ) ;
2015-09-18 10:33:23 -07:00
var dirC = CompileDirectiveMetadata . create (
2015-09-28 10:30:33 -07:00
{ selector : '[c]' , type : new CompileTypeMetadata ( { name : 'DirC' } ) } ) ;
2015-09-18 10:33:23 -07:00
var comp = CompileDirectiveMetadata . create ( {
selector : 'div' ,
isComponent : true ,
type : new CompileTypeMetadata ( { name : 'ZComp' } ) ,
template : new CompileTemplateMetadata ( { ngContentSelectors : [ ] } )
} ) ;
2015-11-10 15:56:25 -08:00
expect ( humanizeTplAst ( parse ( '<div a c b>' , [ dirA , dirB , dirC , comp ] ) ) )
2015-09-18 10:33:23 -07:00
. toEqual ( [
2015-10-07 09:34:21 -07:00
[ ElementAst , 'div' ] ,
[ AttrAst , 'a' , '' ] ,
[ AttrAst , 'c' , '' ] ,
[ AttrAst , 'b' , '' ] ,
[ DirectiveAst , comp ] ,
[ 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' , ( ) = > {
2015-09-18 10:33:23 -07:00
var dirA = CompileDirectiveMetadata . create (
{ selector : '[a=b]' , type : new CompileTypeMetadata ( { name : 'DirA' } ) } ) ;
var dirB = CompileDirectiveMetadata . create (
{ selector : '[b]' , type : new CompileTypeMetadata ( { name : 'DirB' } ) } ) ;
2015-11-10 15:56:25 -08:00
expect ( humanizeTplAst ( parse ( '<div [a]="b">' , [ dirA , dirB ] ) ) )
2015-08-25 15:36:02 -07:00
. toEqual ( [
2015-10-07 09:34:21 -07:00
[ ElementAst , 'div' ] ,
[ BoundElementPropertyAst , PropertyBindingType . Property , 'a' , 'b' , null ] ,
[ DirectiveAst , dirA ]
2015-08-25 15:36:02 -07: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' ,
2015-09-18 10:33:23 -07:00
type : new CompileTypeMetadata ( { name : 'DirA' } ) ,
host : { '[a]' : 'expr' }
2015-08-27 16:29:02 -07:00
} ) ;
2015-11-10 15:56:25 -08:00
expect ( humanizeTplAst ( parse ( '<div></div>' , [ dirA ] ) ) )
2015-08-27 16:29:02 -07:00
. toEqual ( [
2015-10-07 09:34:21 -07:00
[ 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' ,
2015-09-18 10:33:23 -07:00
type : new CompileTypeMetadata ( { name : 'DirA' } ) ,
host : { '(a)' : 'expr' }
2015-08-27 16:29:02 -07:00
} ) ;
2015-11-10 15:56:25 -08:00
expect ( humanizeTplAst ( parse ( '<div></div>' , [ dirA ] ) ) )
2015-10-07 09:34:21 -07:00
. toEqual (
[ [ ElementAst , 'div' ] , [ DirectiveAst , dirA ] , [ BoundEventAst , 'a' , null , 'expr' ] ] ) ;
2015-08-27 16:29:02 -07:00
} ) ;
it ( 'should parse directive properties' , ( ) = > {
2015-09-30 20:59:23 -07:00
var dirA = CompileDirectiveMetadata . create (
{ selector : 'div' , type : new CompileTypeMetadata ( { name : 'DirA' } ) , inputs : [ 'aProp' ] } ) ;
2015-11-23 16:02:19 -08:00
expect ( humanizeTplAst ( parse ( '<div [aProp]="expr"></div>' , [ dirA ] ) ) )
2015-08-27 16:29:02 -07:00
. toEqual ( [
2015-10-07 09:34:21 -07:00
[ ElementAst , 'div' ] ,
[ DirectiveAst , dirA ] ,
[ BoundDirectivePropertyAst , 'aProp' , 'expr' ]
2015-08-27 16:29:02 -07:00
] ) ;
} ) ;
it ( 'should parse renamed directive properties' , ( ) = > {
2015-09-30 20:59:23 -07:00
var dirA = CompileDirectiveMetadata . create (
{ selector : 'div' , type : new CompileTypeMetadata ( { name : 'DirA' } ) , inputs : [ 'b:a' ] } ) ;
2015-11-10 15:56:25 -08:00
expect ( humanizeTplAst ( parse ( '<div [a]="expr"></div>' , [ dirA ] ) ) )
2015-08-27 16:29:02 -07:00
. toEqual ( [
2015-10-07 09:34:21 -07:00
[ ElementAst , 'div' ] ,
[ DirectiveAst , dirA ] ,
[ BoundDirectivePropertyAst , 'b' , 'expr' ]
2015-08-27 16:29:02 -07:00
] ) ;
} ) ;
it ( 'should parse literal directive properties' , ( ) = > {
2015-09-18 10:33:23 -07:00
var dirA = CompileDirectiveMetadata . create (
2015-09-30 20:59:23 -07:00
{ selector : 'div' , type : new CompileTypeMetadata ( { name : 'DirA' } ) , inputs : [ 'a' ] } ) ;
2015-11-10 15:56:25 -08:00
expect ( humanizeTplAst ( parse ( '<div a="literal"></div>' , [ dirA ] ) ) )
2015-08-27 16:29:02 -07:00
. toEqual ( [
2015-10-07 09:34:21 -07:00
[ 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' , ( ) = > {
var dirA = CompileDirectiveMetadata . create (
2015-09-30 20:59:23 -07:00
{ selector : 'div' , type : new CompileTypeMetadata ( { name : 'DirA' } ) , 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 ( [
2015-10-07 09:34:21 -07:00
[ ElementAst , 'div' ] ,
[ AttrAst , 'a' , 'literal' ] ,
[ DirectiveAst , dirA ] ,
[ 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' , ( ) = > {
2015-09-18 10:33:23 -07:00
var dirA = CompileDirectiveMetadata . create (
2015-09-30 20:59:23 -07:00
{ selector : 'div' , type : new CompileTypeMetadata ( { name : 'DirA' } ) , inputs : [ 'a' ] } ) ;
2015-11-10 15:56:25 -08:00
expect ( humanizeTplAst ( parse ( '<div></div>' , [ dirA ] ) ) )
2015-10-07 09:34:21 -07:00
. toEqual ( [ [ ElementAst , 'div' ] , [ DirectiveAst , dirA ] ] ) ;
2015-08-27 16:29:02 -07:00
} ) ;
2015-08-25 15:36:02 -07:00
} ) ;
2015-09-18 10:33:23 -07:00
describe ( 'variables' , ( ) = > {
it ( 'should parse variables via #... and not report them as attributes' , ( ) = > {
2015-11-10 15:56:25 -08:00
expect ( humanizeTplAst ( parse ( '<div #a>' , [ ] ) ) )
2015-10-07 09:34:21 -07:00
. toEqual ( [ [ ElementAst , 'div' ] , [ VariableAst , 'a' , '' ] ] ) ;
2015-09-18 10:33:23 -07:00
} ) ;
it ( 'should parse variables via var-... and not report them as attributes' , ( ) = > {
2015-11-10 15:56:25 -08:00
expect ( humanizeTplAst ( parse ( '<div var-a>' , [ ] ) ) )
. toEqual ( [ [ ElementAst , 'div' ] , [ VariableAst , 'a' , '' ] ] ) ;
2015-09-18 10:33:23 -07:00
} ) ;
2015-11-23 16:02:19 -08:00
it ( 'should parse camel case variables' , ( ) = > {
expect ( humanizeTplAst ( parse ( '<div var-someA>' , [ ] ) ) )
2015-10-07 09:34:21 -07:00
. toEqual ( [ [ ElementAst , 'div' ] , [ VariableAst , 'someA' , '' ] ] ) ;
2015-09-18 10:33:23 -07:00
} ) ;
2015-09-18 10:33:23 -07:00
it ( 'should assign variables with empty value to the element' , ( ) = > {
2015-11-10 15:56:25 -08:00
expect ( humanizeTplAst ( parse ( '<div #a></div>' , [ ] ) ) )
2015-10-07 09:34:21 -07:00
. toEqual ( [ [ ElementAst , 'div' ] , [ VariableAst , 'a' , '' ] ] ) ;
2015-09-18 10:33:23 -07:00
} ) ;
it ( 'should assign variables to directives via exportAs' , ( ) = > {
var dirA = CompileDirectiveMetadata . create (
{ selector : '[a]' , type : new CompileTypeMetadata ( { name : 'DirA' } ) , exportAs : 'dirA' } ) ;
2015-11-10 15:56:25 -08:00
expect ( humanizeTplAst ( parse ( '<div a #a="dirA"></div>' , [ dirA ] ) ) )
2015-09-18 10:33:23 -07:00
. toEqual ( [
2015-10-07 09:34:21 -07:00
[ ElementAst , 'div' ] ,
[ AttrAst , 'a' , '' ] ,
[ DirectiveAst , dirA ] ,
[ VariableAst , 'a' , 'dirA' ]
2015-09-18 10:33:23 -07:00
] ) ;
} ) ;
it ( 'should report variables with values that dont match a directive as errors' , ( ) = > {
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
} ) ;
2015-11-23 16:02:19 -08:00
it ( 'should report invalid variable names' , ( ) = > {
expect ( ( ) = > parse ( '<div #a-b></div>' , [ ] ) ) . toThrowError ( ` Template parse errors:
"-" is not allowed in variable names ("<div [ERROR ->]#a-b></div>"): TestComp@0:5 ` ) ;
} ) ;
2015-09-18 10:33:23 -07:00
it ( 'should allow variables with values that dont match a directive on embedded template elements' ,
( ) = > {
2015-11-10 15:56:25 -08:00
expect ( humanizeTplAst ( parse ( '<template #a="b"></template>' , [ ] ) ) )
2015-10-07 09:34:21 -07:00
. toEqual ( [ [ EmbeddedTemplateAst ] , [ VariableAst , 'a' , 'b' ] ] ) ;
2015-09-18 10:33:23 -07:00
} ) ;
it ( 'should assign variables with empty value to components' , ( ) = > {
var dirA = CompileDirectiveMetadata . create ( {
selector : '[a]' ,
isComponent : true ,
type : new CompileTypeMetadata ( { name : 'DirA' } ) ,
2015-10-28 08:59:19 +01:00
exportAs : 'dirA' ,
template : new CompileTemplateMetadata ( { ngContentSelectors : [ ] } )
2015-09-18 10:33:23 -07:00
} ) ;
2015-11-10 15:56:25 -08:00
expect ( humanizeTplAst ( parse ( '<div a #a></div>' , [ dirA ] ) ) )
2015-09-18 10:33:23 -07:00
. toEqual ( [
2015-10-07 09:34:21 -07:00
[ ElementAst , 'div' ] ,
[ AttrAst , 'a' , '' ] ,
[ VariableAst , 'a' , '' ] ,
[ DirectiveAst , dirA ] ,
[ VariableAst , 'a' , '' ]
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' , ( ) = > {
2015-11-10 15:56:25 -08:00
expect ( humanizeTplAst ( parse ( '<template></template>' , [ ] ) ) )
. toEqual ( [ [ EmbeddedTemplateAst ] ] ) ;
expect ( humanizeTplAst ( parse ( '<TEMPLATE></TEMPLATE>' , [ ] ) ) )
2015-10-07 09:34:21 -07:00
. 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' ,
( ) = > {
expect ( humanizeTplAst ( parse ( '<svg><template></template></svg>' , [ ] ) ) )
. toEqual ( [
[ ElementAst , '@svg:svg' ] ,
[ EmbeddedTemplateAst ] ,
] ) ;
} ) ;
2015-08-25 15:36:02 -07:00
} ) ;
describe ( 'inline templates' , ( ) = > {
it ( 'should wrap the element into an EmbeddedTemplateAST' , ( ) = > {
2015-11-10 15:56:25 -08:00
expect ( humanizeTplAst ( parse ( '<div template>' , [ ] ) ) )
. toEqual ( [ [ EmbeddedTemplateAst ] , [ ElementAst , 'div' ] ] ) ;
2015-08-25 15:36:02 -07:00
} ) ;
it ( 'should parse bound properties' , ( ) = > {
2015-11-10 15:56:25 -08:00
expect ( humanizeTplAst ( parse ( '<div template="ngIf test">' , [ ngIf ] ) ) )
2015-08-25 15:36:02 -07:00
. toEqual ( [
2015-10-07 09:34:21 -07:00
[ EmbeddedTemplateAst ] ,
[ DirectiveAst , ngIf ] ,
[ BoundDirectivePropertyAst , 'ngIf' , 'test' ] ,
[ ElementAst , 'div' ]
2015-08-25 15:36:02 -07:00
] ) ;
} ) ;
it ( 'should parse variables via #...' , ( ) = > {
2015-11-10 15:56:25 -08:00
expect ( humanizeTplAst ( parse ( '<div template="ngIf #a=b">' , [ ] ) ) )
2015-10-07 09:34:21 -07:00
. toEqual ( [ [ EmbeddedTemplateAst ] , [ VariableAst , 'a' , 'b' ] , [ ElementAst , 'div' ] ] ) ;
2015-08-25 15:36:02 -07:00
} ) ;
it ( 'should parse variables via var ...' , ( ) = > {
2015-11-10 15:56:25 -08:00
expect ( humanizeTplAst ( parse ( '<div template="ngIf var a=b">' , [ ] ) ) )
2015-10-07 09:34:21 -07:00
. toEqual ( [ [ EmbeddedTemplateAst ] , [ VariableAst , 'a' , 'b' ] , [ ElementAst , 'div' ] ] ) ;
2015-08-25 15:36:02 -07:00
} ) ;
describe ( 'directives' , ( ) = > {
it ( 'should locate directives in property bindings' , ( ) = > {
2015-09-30 20:59:23 -07:00
var dirA = CompileDirectiveMetadata . create (
{ selector : '[a=b]' , type : new CompileTypeMetadata ( { name : 'DirA' } ) , inputs : [ 'a' ] } ) ;
2015-09-18 10:33:23 -07:00
var dirB = CompileDirectiveMetadata . create (
{ selector : '[b]' , type : new CompileTypeMetadata ( { name : 'DirB' } ) } ) ;
2015-11-10 15:56:25 -08:00
expect ( humanizeTplAst ( parse ( '<div template="a b" b>' , [ dirA , dirB ] ) ) )
2015-08-25 15:36:02 -07:00
. toEqual ( [
2015-10-07 09:34:21 -07:00
[ EmbeddedTemplateAst ] ,
[ DirectiveAst , dirA ] ,
[ BoundDirectivePropertyAst , 'a' , 'b' ] ,
[ ElementAst , 'div' ] ,
[ AttrAst , 'b' , '' ] ,
[ DirectiveAst , dirB ]
2015-08-25 15:36:02 -07:00
] ) ;
} ) ;
it ( 'should locate directives in variable bindings' , ( ) = > {
2015-09-18 10:33:23 -07:00
var dirA = CompileDirectiveMetadata . create (
{ selector : '[a=b]' , type : new CompileTypeMetadata ( { name : 'DirA' } ) } ) ;
var dirB = CompileDirectiveMetadata . create (
{ selector : '[b]' , type : new CompileTypeMetadata ( { name : 'DirB' } ) } ) ;
2015-11-10 15:56:25 -08:00
expect ( humanizeTplAst ( parse ( '<div template="#a=b" b>' , [ dirA , dirB ] ) ) )
2015-08-25 15:36:02 -07:00
. toEqual ( [
2015-10-07 09:34:21 -07:00
[ EmbeddedTemplateAst ] ,
[ VariableAst , 'a' , 'b' ] ,
[ DirectiveAst , dirA ] ,
[ ElementAst , 'div' ] ,
[ AttrAst , 'b' , '' ] ,
[ DirectiveAst , dirB ]
2015-08-25 15:36:02 -07:00
] ) ;
} ) ;
} ) ;
it ( 'should work with *... and use the attribute name as property binding name' , ( ) = > {
2015-11-23 16:02:19 -08:00
expect ( humanizeTplAst ( parse ( '<div *ngIf="test">' , [ ngIf ] ) ) )
2015-08-25 15:36:02 -07:00
. toEqual ( [
2015-10-07 09:34:21 -07:00
[ 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' , ( ) = > {
2015-11-23 16:02:19 -08:00
expect ( humanizeTplAst ( parse ( '<div *ngIf>' , [ ngIf ] ) ) )
2015-10-26 13:22:45 -07:00
. toEqual ( [
2015-10-07 09:34:21 -07:00
[ 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' , ( ) = > {
2015-10-28 08:59:19 +01: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 ,
2015-09-18 10:33:23 -07:00
type : new CompileTypeMetadata ( { name : 'SomeComp' } ) ,
template : new CompileTemplateMetadata ( { ngContentSelectors : ngContentSelectors } )
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' , ( ) = > {
expect ( humanizeContentProjection (
parse ( '<div><span></span></div>' , [ createComp ( 'div' , [ '*' ] ) ] ) ) )
. toEqual ( [ [ 'div' , null ] , [ 'span' , 0 ] ] ) ;
} ) ;
it ( 'should project elements with css selector' , ( ) = > {
expect ( humanizeContentProjection (
parse ( '<div><a x></a><b></b></div>' , [ createComp ( 'div' , [ 'a[x]' ] ) ] ) ) )
. toEqual ( [ [ 'div' , null ] , [ 'a' , 0 ] , [ 'b' , null ] ] ) ;
} ) ;
} ) ;
describe ( 'embedded templates' , ( ) = > {
it ( 'should project embedded templates with wildcard selector' , ( ) = > {
expect ( humanizeContentProjection (
parse ( '<div><template></template></div>' , [ createComp ( 'div' , [ '*' ] ) ] ) ) )
. toEqual ( [ [ 'div' , null ] , [ 'template' , 0 ] ] ) ;
} ) ;
it ( 'should project embedded templates with css selector' , ( ) = > {
expect ( humanizeContentProjection (
parse ( '<div><template x></template><template></template></div>' ,
[ createComp ( 'div' , [ 'template[x]' ] ) ] ) ) )
. toEqual ( [ [ 'div' , null ] , [ 'template' , 0 ] , [ 'template' , null ] ] ) ;
} ) ;
} ) ;
describe ( 'ng-content' , ( ) = > {
it ( 'should project ng-content with wildcard selector' , ( ) = > {
expect ( humanizeContentProjection (
parse ( '<div><ng-content></ng-content></div>' , [ createComp ( 'div' , [ '*' ] ) ] ) ) )
. toEqual ( [ [ 'div' , null ] , [ 'ng-content' , 0 ] ] ) ;
} ) ;
it ( 'should project ng-content with css selector' , ( ) = > {
expect ( humanizeContentProjection (
parse ( '<div><ng-content x></ng-content><ng-content></ng-content></div>' ,
[ createComp ( 'div' , [ 'ng-content[x]' ] ) ] ) ) )
. toEqual ( [ [ 'div' , null ] , [ 'ng-content' , 0 ] , [ 'ng-content' , null ] ] ) ;
} ) ;
} ) ;
it ( 'should project into the first matching ng-content' , ( ) = > {
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-10-29 16:23:13 -07:00
it ( 'should project into wildcard ng-content last' , ( ) = > {
expect ( humanizeContentProjection (
parse ( '<div>hello<a></a></div>' , [ createComp ( 'div' , [ '*' , 'a' ] ) ] ) ) )
. toEqual ( [ [ 'div' , null ] , [ '#text(hello)' , 0 ] , [ 'a' , 1 ] ] ) ;
} ) ;
2015-09-11 13:37:05 -07:00
it ( 'should only project direct child nodes' , ( ) = > {
expect ( humanizeContentProjection (
parse ( '<div><span><a></a></span><a></a></div>' , [ createComp ( 'div' , [ 'a' ] ) ] ) ) )
. toEqual ( [ [ 'div' , null ] , [ 'span' , null ] , [ 'a' , null ] , [ 'a' , 0 ] ] ) ;
} ) ;
it ( 'should project nodes of nested components' , ( ) = > {
expect ( humanizeContentProjection (
parse ( '<a><b>hello</b></a>' , [ createComp ( 'a' , [ '*' ] ) , createComp ( 'b' , [ '*' ] ) ] ) ) )
. toEqual ( [ [ 'a' , null ] , [ 'b' , 0 ] , [ '#text(hello)' , 0 ] ] ) ;
} ) ;
2015-11-23 16:02:19 -08:00
it ( 'should project children of components with ngNonBindable' , ( ) = > {
expect ( humanizeContentProjection ( parse ( '<div ngNonBindable>{{hello}}<span></span></div>' ,
2015-09-18 10:33:23 -07:00
[ createComp ( 'div' , [ '*' ] ) ] ) ) )
. toEqual ( [ [ 'div' , null ] , [ '#text({{hello}})' , 0 ] , [ 'span' , 0 ] ] ) ;
} ) ;
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 ` ) ;
} ) ;
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:
Can't bind to 'invalidProp' since it isn't a known native property ("<div [ERROR ->][invalidProp]></div>"): TestComp@0:5 ` ) ;
2015-08-27 16:29:02 -07:00
} ) ;
it ( 'should report errors in expressions' , ( ) = > {
2015-09-10 15:25:36 -07:00
expect ( ( ) = > parse ( '<div [prop]="a b"></div>' , [ ] ) ) . toThrowErrorWith ( ` 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' ,
2015-09-18 10:33:23 -07:00
type : new CompileTypeMetadata ( { name : 'DirA' } ) ,
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 ,
2015-09-18 10:33:23 -07:00
type : new CompileTypeMetadata ( { name : 'DirA' } ) ,
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 ,
2015-09-18 10:33:23 -07:00
type : new CompileTypeMetadata ( { name : 'DirB' } ) ,
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 ,
2015-09-18 10:33:23 -07:00
type : new CompileTypeMetadata ( { name : 'DirA' } ) ,
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:
2015-11-10 15:56:25 -08:00
Event binding e not emitted by any directive on an embedded template ("<template [a]="b" [ERROR ->](e)="f"></template>"): TestComp@0:18
Components on an embedded template: DirA ("[ERROR ->]<template [a]="b" (e)="f"></template>"): TestComp@0:0
Property binding a not used by any directive on an embedded template ("[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 ,
2015-09-18 10:33:23 -07:00
type : new CompileTypeMetadata ( { name : 'DirA' } ) ,
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
Property binding a not used by any directive on an embedded template ("[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 ( [
2015-10-07 09:34:21 -07:00
[ ElementAst , 'link' ] ,
[ AttrAst , 'rel' , 'stylesheet' ] ,
2015-11-10 15:56:25 -08:00
[ AttrAst , 'href' , 'http://someurl' ] ,
2015-10-07 09:34:21 -07:00
[ TextAst , 'a' ]
2015-10-14 09:39:40 -07:00
] ) ;
} ) ;
it ( 'should keep <link rel="stylesheet"> elements if they have no uri' , ( ) = > {
2015-12-03 15:53:44 -08:00
expect ( humanizeTplAst ( parse ( '<link rel="stylesheet">a' , [ ] ) ) )
2015-10-07 09:34:21 -07:00
. toEqual ( [ [ ElementAst , 'link' ] , [ AttrAst , 'rel' , 'stylesheet' ] , [ TextAst , 'a' ] ] ) ;
2015-12-03 15:53:44 -08:00
expect ( humanizeTplAst ( parse ( '<link REL="stylesheet">a' , [ ] ) ) )
2015-11-10 15:56:25 -08:00
. 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' , ( ) = > {
2015-12-03 15:53:44 -08:00
expect ( humanizeTplAst ( parse ( '<link rel="stylesheet" href="./other.css">a' , [ ] ) ) )
2015-11-10 15:56:25 -08:00
. toEqual ( [ [ TextAst , 'a' ] ] ) ;
2015-12-03 15:53:44 -08:00
expect ( humanizeTplAst ( parse ( '<link rel="stylesheet" HREF="./other.css">a' , [ ] ) ) )
2015-10-07 09:34:21 -07:00
. toEqual ( [ [ TextAst , 'a' ] ] ) ;
2015-10-14 09:39:40 -07:00
} ) ;
it ( 'should ignore <link rel="stylesheet"> elements if they have a package: uri' , ( ) = > {
2015-12-03 15:53:44 -08:00
expect ( humanizeTplAst ( parse ( '<link rel="stylesheet" href="package:somePackage">a' , [ ] ) ) )
2015-10-07 09:34:21 -07:00
. 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' , ( ) = > {
expect ( humanizeTplAst ( parse ( '<div ngNonBindable>{{b}}</div>' , [ ] ) ) )
. toEqual ( [ [ ElementAst , 'div' ] , [ AttrAst , 'ngNonBindable' , '' ] , [ TextAst , '{{b}}' ] ] ) ;
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' , ( ) = > {
expect ( humanizeTplAst ( parse ( '<div ngNonBindable><span>{{b}}</span></div>' , [ ] ) ) )
2015-09-18 10:33:23 -07:00
. toEqual ( [
2015-10-07 09:34:21 -07:00
[ ElementAst , 'div' ] ,
2015-11-23 16:02:19 -08:00
[ AttrAst , 'ngNonBindable' , '' ] ,
2015-10-07 09:34:21 -07:00
[ 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' , ( ) = > {
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' , ( ) = > {
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
( ) = > {
2015-11-23 16:02:19 -08: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 ( [
2015-10-07 09:34:21 -07:00
[ ElementAst , 'div' ] ,
2015-11-23 16:02:19 -08:00
[ AttrAst , 'ngNonBindable' , '' ] ,
2015-10-07 09:34:21 -07:00
[ ElementAst , 'ng-content' ] ,
[ 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">' , [ ] ) ;
expect ( humanizeTplAstSourceSpans ( parsed ) )
. toEqual ( [ [ NgContentAst , '<ng-content select="a">' ] ] ) ;
} ) ;
it ( 'should support embedded template' , ( ) = > {
expect ( humanizeTplAstSourceSpans ( parse ( '<template></template>' , [ ] ) ) )
. toEqual ( [ [ EmbeddedTemplateAst , '<template>' ] ] ) ;
} ) ;
it ( 'should support element and attributes' , ( ) = > {
expect ( humanizeTplAstSourceSpans ( parse ( '<div key=value>' , [ ] ) ) )
. toEqual (
[ [ ElementAst , 'div' , '<div key=value>' ] , [ AttrAst , 'key' , 'value' , 'key=value' ] ] ) ;
} ) ;
it ( 'should support variables' , ( ) = > {
var dirA = CompileDirectiveMetadata . create (
{ selector : '[a]' , type : new CompileTypeMetadata ( { name : 'DirA' } ) , exportAs : 'dirA' } ) ;
expect ( humanizeTplAstSourceSpans ( parse ( '<div a #a="dirA"></div>' , [ dirA ] ) ) )
. toEqual ( [
[ ElementAst , 'div' , '<div a #a="dirA">' ] ,
[ AttrAst , 'a' , '' , 'a' ] ,
[ DirectiveAst , dirA , '<div a #a="dirA">' ] ,
[ VariableAst , 'a' , 'dirA' , '#a="dirA"' ]
] ) ;
} ) ;
it ( 'should support event' , ( ) = > {
expect ( humanizeTplAstSourceSpans ( parse ( '<div (window:event)="v">' , [ ] ) ) )
. toEqual ( [
[ ElementAst , 'div' , '<div (window:event)="v">' ] ,
[ BoundEventAst , 'event' , 'window' , 'v' , '(window:event)="v"' ]
] ) ;
} ) ;
it ( 'should support element property' , ( ) = > {
expect ( humanizeTplAstSourceSpans ( parse ( '<div [someProp]="v">' , [ ] ) ) )
. toEqual ( [
[ ElementAst , 'div' , '<div [someProp]="v">' ] ,
[
BoundElementPropertyAst ,
PropertyBindingType . Property ,
'someProp' ,
'v' ,
null ,
'[someProp]="v"'
]
] ) ;
} ) ;
it ( 'should support bound text' , ( ) = > {
expect ( humanizeTplAstSourceSpans ( parse ( '{{a}}' , [ ] ) ) )
. toEqual ( [ [ BoundTextAst , '{{ a }}' , '{{a}}' ] ] ) ;
} ) ;
it ( 'should support text nodes' , ( ) = > {
expect ( humanizeTplAstSourceSpans ( parse ( 'a' , [ ] ) ) ) . toEqual ( [ [ TextAst , 'a' , 'a' ] ] ) ;
} ) ;
it ( 'should support directive' , ( ) = > {
var dirA = CompileDirectiveMetadata . create (
{ selector : '[a]' , type : new CompileTypeMetadata ( { name : 'DirA' } ) } ) ;
var comp = CompileDirectiveMetadata . create ( {
selector : 'div' ,
isComponent : true ,
type : new CompileTypeMetadata ( { name : 'ZComp' } ) ,
template : new CompileTemplateMetadata ( { ngContentSelectors : [ ] } )
} ) ;
expect ( humanizeTplAstSourceSpans ( parse ( '<div a>' , [ dirA , comp ] ) ) )
. toEqual ( [
[ ElementAst , 'div' , '<div a>' ] ,
[ AttrAst , 'a' , '' , 'a' ] ,
[ DirectiveAst , comp , '<div a>' ] ,
[ DirectiveAst , dirA , '<div a>' ] ,
] ) ;
} ) ;
2015-12-09 09:32:15 -08:00
it ( 'should support directive in namespace' , ( ) = > {
var tagSel = CompileDirectiveMetadata . create (
{ selector : 'circle' , type : new CompileTypeMetadata ( { name : 'elDir' } ) } ) ;
var attrSel = CompileDirectiveMetadata . create (
{ selector : '[href]' , type : new CompileTypeMetadata ( { name : 'attrDir' } ) } ) ;
expect ( humanizeTplAstSourceSpans (
parse ( '<svg><circle /><use xlink:href="Port" /></svg>' , [ tagSel , attrSel ] ) ) )
. toEqual ( [
[ ElementAst , '@svg:svg' , '<svg>' ] ,
[ ElementAst , '@svg:circle' , '<circle />' ] ,
[ DirectiveAst , tagSel , '<circle />' ] ,
[ ElementAst , '@svg:use' , '<use xlink:href="Port" />' ] ,
[ AttrAst , '@xlink:href' , 'Port' , 'xlink:href="Port"' ] ,
[ DirectiveAst , attrSel , '<use xlink:href="Port" />' ] ,
] ) ;
} ) ;
2015-11-10 15:56:25 -08:00
it ( 'should support directive property' , ( ) = > {
var dirA = CompileDirectiveMetadata . create (
{ selector : 'div' , type : new CompileTypeMetadata ( { name : 'DirA' } ) , inputs : [ 'aProp' ] } ) ;
2015-11-23 16:02:19 -08:00
expect ( humanizeTplAstSourceSpans ( parse ( '<div [aProp]="foo | bar"></div>' , [ dirA ] ) ) )
2015-11-10 15:56:25 -08:00
. toEqual ( [
2015-11-23 16:02:19 -08:00
[ ElementAst , 'div' , '<div [aProp]="foo | bar">' ] ,
[ DirectiveAst , dirA , '<div [aProp]="foo | bar">' ] ,
[ BoundDirectivePropertyAst , 'aProp' , '(foo | bar)' , '[aProp]="foo | bar"' ]
2015-11-10 15:56:25 -08:00
] ) ;
} ) ;
} ) ;
2015-08-25 15:36:02 -07:00
} ) ;
}
2015-11-10 15:56:25 -08:00
function humanizeTplAst ( templateAsts : TemplateAst [ ] ) : any [ ] {
var humanizer = new TemplateHumanizer ( false ) ;
templateVisitAll ( humanizer , templateAsts ) ;
return humanizer . result ;
}
function humanizeTplAstSourceSpans ( templateAsts : TemplateAst [ ] ) : any [ ] {
var humanizer = new TemplateHumanizer ( true ) ;
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
constructor ( private includeSourceSpan : boolean ) { } ;
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 ) ;
2015-08-25 15:36:02 -07:00
templateVisitAll ( this , ast . vars ) ;
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 ) ;
2015-09-18 10:33:23 -07:00
templateVisitAll ( this , ast . exportAsVars ) ;
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
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 {
2015-11-10 15:56:25 -08:00
var res = [ BoundEventAst , ast . name , ast . target , expressionUnparser . unparse ( ast . handler ) ] ;
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 = [
2015-08-27 16:29:02 -07:00
BoundElementPropertyAst ,
ast . type ,
ast . name ,
expressionUnparser . unparse ( ast . value ) ,
2015-10-07 09:34:21 -07:00
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 {
2015-11-10 15:56:25 -08:00
var res = [ BoundTextAst , expressionUnparser . unparse ( 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
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 ) ;
2015-09-18 10:33:23 -07:00
templateVisitAll ( this , ast . exportAsVars ) ;
2015-08-27 16:29:02 -07:00
return null ;
}
2015-09-01 16:24:23 -07:00
visitDirectiveProperty ( ast : BoundDirectivePropertyAst , context : any ) : any {
2015-11-10 15:56:25 -08:00
var res = [ BoundDirectivePropertyAst , ast . directiveName , expressionUnparser . unparse ( ast . value ) ] ;
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 ;
}
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 {
this . result . push ( [ ` #text( ${ expressionUnparser . unparse ( ast . value ) } ) ` , ast . ngContentIndex ] ) ;
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 ;
return new ElementAst ( 'foo' , [ ] , [ ] , [ ] , [ ] , [ ] , [ ] , ast . ngContentIndex , ast . sourceSpan ) ;
}
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 ;
return new ElementAst ( 'bar' , [ ] , [ ] , [ ] , [ ] , [ ] , [ ] , ast . ngContentIndex , ast . sourceSpan ) ;
}
}