2015-09-11 13:37:05 -07:00
import {
2015-09-14 15:59:09 -07:00
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-14 15:59:09 -07:00
import {
2015-09-18 10:33:23 -07:00
CompileDirectiveMetadata ,
2015-12-02 10:35:51 -08:00
CompilePipeMetadata ,
2015-09-18 10:33:23 -07:00
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-12-02 10:35:51 -08:00
function parse ( template : string , directives : CompileDirectiveMetadata [],
pipes : CompilePipeMetadata [] = null ) : TemplateAst [] {
if ( pipes === null ) {
pipes = [];
}
return parser . parse ( template , directives , pipes , '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-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-27 16:29: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-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">' , [])))
2015-11-10 15:56:25 -08:00
. toEqual ([
2015-10-07 09:34:21 -07:00
[ 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' , () => {
2015-11-10 15:56:25 -08: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' , () => {
expect ( humanizeTplAst ( parse ( '<div [class.someClass]="v">' , [])))
. toEqual ([
[ ElementAst , 'div' ],
[ BoundElementPropertyAst , PropertyBindingType . Class , 'someClass' , 'v' , null ]
]);
});
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
});
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-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
]);
});
2016-02-03 11:35:42 -08:00
it ( 'should locate directives in event bindings' , () => {
var dirA = CompileDirectiveMetadata . create (
{ selector : '[a]' , type : new CompileTypeMetadata ({ name : 'DirB' })});
expect ( humanizeTplAst ( parse ( '<div (a)="b">' , [ dirA ])))
. toEqual (
[[ ElementAst , 'div' ], [ BoundEventAst , 'a' , null , 'b' ], [ DirectiveAst , dirA ]]);
});
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-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-12-02 10:35:51 -08:00
expect ( humanizeTplAstSourceSpans ( parse ( '<div [aProp]="foo"></div>' , [ dirA ])))
2015-11-10 15:56:25 -08:00
. toEqual ([
2015-12-02 10:35:51 -08:00
[ 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' , () => {
var testPipe =
new CompilePipeMetadata ({ name : 'test' , type : new CompileTypeMetadata ({ name : 'DirA' })});
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` );
});
});
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 );
}
}