2016-04-12 09:40:37 -07:00
import {
ddescribe ,
describe ,
it ,
iit ,
xit ,
expect ,
beforeEach ,
afterEach ,
inject ,
beforeEachProviders
2016-04-28 17:50:03 -07:00
} from '@angular/core/testing/testing_internal' ;
import { provide } from '@angular/core' ;
import { Console } from '@angular/core/src/console' ;
2015-09-14 15:59:09 -07:00
2016-04-12 09:40:37 -07:00
import {
TemplateParser ,
splitClasses ,
TEMPLATE_TRANSFORMS
2016-04-28 17:50:03 -07:00
} from '@angular/compiler/src/template_parser' ;
2016-04-12 09:40:37 -07:00
import {
CompileDirectiveMetadata ,
CompilePipeMetadata ,
CompileTypeMetadata ,
2016-01-06 14:13:44 -08:00
CompileTemplateMetadata ,
CompileProviderMetadata ,
CompileTokenMetadata ,
CompileDiDependencyMetadata ,
CompileQueryMetadata
2016-04-28 17:50:03 -07:00
} from '@angular/compiler/src/compile_metadata' ;
2016-04-12 09:40:37 -07:00
import {
templateVisitAll ,
TemplateAstVisitor ,
TemplateAst ,
NgContentAst ,
EmbeddedTemplateAst ,
ElementAst ,
2016-04-25 19:52:24 -07:00
ReferenceAst ,
2016-04-12 09:40:37 -07:00
VariableAst ,
BoundEventAst ,
BoundElementPropertyAst ,
BoundDirectivePropertyAst ,
AttrAst ,
BoundTextAst ,
TextAst ,
PropertyBindingType ,
2016-01-06 14:13:44 -08:00
DirectiveAst ,
ProviderAstType
2016-04-28 17:50:03 -07:00
} from '@angular/compiler/src/template_ast' ;
import { identifierToken , Identifiers } from '../src/identifiers' ;
2015-08-25 15:36:02 -07:00
2016-04-28 17:50:03 -07:00
import { ElementSchemaRegistry } from '@angular/compiler/src/schema/element_schema_registry' ;
import { MockSchemaRegistry } from '@angular/compiler/testing' ;
2015-08-27 16:29:02 -07:00
2016-01-06 14:13:44 -08:00
import { Unparser } from './expression_parser/unparser' ;
2016-04-28 17:50:03 -07:00
import { TEST_PROVIDERS } from './test_bindings' ;
2015-08-25 15:36:02 -07:00
var expressionUnparser = new Unparser ();
2016-01-06 14:13:44 -08:00
var someModuleUrl = 'package:someModule' ;
2016-04-12 09:40:37 -07:00
var MOCK_SCHEMA_REGISTRY = [
provide (
ElementSchemaRegistry ,
{ useValue : new MockSchemaRegistry ({ 'invalidProp' : false }, { 'mappedAttr' : 'mappedProp' })})
];
2016-02-10 16:54:32 -08:00
2015-08-25 15:36:02 -07:00
export function main() {
2016-02-10 16:54:32 -08:00
var ngIf ;
var parse ;
2016-04-25 19:52:24 -07:00
var console : ArrayConsole ;
2015-09-14 15:59:09 -07:00
2016-02-10 16:54:32 -08:00
function commonBeforeEach() {
2016-04-25 19:52:24 -07:00
beforeEachProviders (() => {
console = new ArrayConsole ();
return [ provide ( Console , { useValue : console })];
});
2016-02-10 16:54:32 -08:00
beforeEach ( inject ([ TemplateParser ], ( parser ) => {
2016-01-06 14:13:44 -08:00
var component = CompileDirectiveMetadata . create ({
selector : 'root' ,
type : new CompileTypeMetadata ({ moduleUrl : someModuleUrl , name : 'Root' }),
isComponent : true
});
ngIf = CompileDirectiveMetadata . create ({
selector : '[ngIf]' ,
type : new CompileTypeMetadata ({ moduleUrl : someModuleUrl , name : 'NgIf' }),
inputs : [ 'ngIf' ]
});
2016-02-10 16:54:32 -08:00
2016-04-12 09:40:37 -07:00
parse = ( template : string , directives : CompileDirectiveMetadata [],
pipes : CompilePipeMetadata [] = null ) : TemplateAst [] => {
if ( pipes === null ) {
pipes = [];
}
2016-01-06 14:13:44 -08:00
return parser . parse ( component , template , directives , pipes , 'TestComp' );
2016-04-12 09:40:37 -07:00
};
2015-09-14 15:59:09 -07:00
}));
2016-02-10 16:54:32 -08:00
}
2015-08-25 15:36:02 -07:00
2016-02-10 16:54:32 -08:00
describe ( 'TemplateParser template transform' , () => {
beforeEachProviders (() => [ TEST_PROVIDERS , MOCK_SCHEMA_REGISTRY ]);
2015-08-25 15:36:02 -07:00
2016-02-10 16:54:32 -08:00
beforeEachProviders (
() => [ provide ( TEMPLATE_TRANSFORMS , { useValue : new FooAstTransformer (), multi : true })]);
2015-11-19 10:51:16 -08:00
2016-02-10 16:54:32 -08:00
describe ( 'single' , () => {
commonBeforeEach ();
2016-04-12 09:40:37 -07:00
it ( 'should transform TemplateAST' ,
() => { expect ( humanizeTplAst ( parse ( '<div>' , []))). toEqual ([[ ElementAst , 'foo' ]]); });
2015-11-19 10:51:16 -08:00
});
2016-02-10 16:54:32 -08:00
describe ( 'multiple' , () => {
beforeEachProviders (
() => [ provide ( TEMPLATE_TRANSFORMS , { useValue : new BarAstTransformer (), multi : true })]);
commonBeforeEach ();
2016-04-12 09:40:37 -07:00
it ( 'should compose transformers' ,
() => { expect ( humanizeTplAst ( parse ( '<div>' , []))). toEqual ([[ ElementAst , 'bar' ]]); });
2016-02-10 16:54:32 -08:00
});
});
describe ( 'TemplateParser' , () => {
beforeEachProviders (() => [ TEST_PROVIDERS , MOCK_SCHEMA_REGISTRY ]);
commonBeforeEach ();
2015-08-25 15:36:02 -07:00
describe ( 'parse' , () => {
describe ( 'nodes without bindings' , () => {
2016-04-12 09:40:37 -07:00
it ( 'should parse text nodes' ,
() => { expect ( humanizeTplAst ( parse ( 'a' , []))). toEqual ([[ TextAst , 'a' ]]); });
2015-08-25 15:36:02 -07:00
it ( 'should parse elements with attributes' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parse ( '<div a=b>' , [])))
. toEqual ([[ ElementAst , 'div' ], [ AttrAst , 'a' , 'b' ]]);
2015-08-25 15:36:02 -07:00
});
});
it ( 'should parse ngContent' , () => {
var parsed = parse ( '<ng-content select="a">' , []);
2015-11-10 15:56:25 -08:00
expect ( humanizeTplAst ( parsed )). toEqual ([[ NgContentAst ]]);
2015-08-25 15:36:02 -07:00
});
2015-12-09 10:47:10 -08:00
it ( 'should parse ngContent regardless the namespace' , () => {
var parsed = parse ( '<svg><ng-content></ng-content></svg>' , []);
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parsed ))
. toEqual ([
2016-04-29 14:54:46 -07:00
[ ElementAst , ':svg:svg' ],
2016-04-12 09:40:37 -07:00
[ NgContentAst ],
]);
2015-12-09 10:47:10 -08:00
});
2015-08-25 15:36:02 -07:00
it ( 'should parse bound text nodes' , () => {
2015-11-10 15:56:25 -08:00
expect ( humanizeTplAst ( parse ( '{{a}}' , []))). toEqual ([[ BoundTextAst , '{{ a }}' ]]);
2015-08-25 15:36:02 -07:00
});
2015-08-27 16:29:02 -07:00
describe ( 'bound properties' , () => {
2015-11-10 15:56:25 -08:00
it ( 'should parse mixed case bound properties' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parse ( '<div [someProp]="v">' , [])))
. toEqual ([
[ ElementAst , 'div' ],
[ BoundElementPropertyAst , PropertyBindingType . Property , 'someProp' , 'v' , null ]
]);
2015-11-10 15:56:25 -08:00
});
2015-11-23 16:02:19 -08:00
it ( 'should parse dash case bound properties' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parse ( '<div [some-prop]="v">' , [])))
. toEqual ([
[ ElementAst , 'div' ],
[ BoundElementPropertyAst , PropertyBindingType . Property , 'some-prop' , 'v' , null ]
]);
2015-08-27 16:29:02 -07:00
});
it ( 'should normalize property names via the element schema' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parse ( '<div [mappedAttr]="v">' , [])))
. toEqual ([
[ ElementAst , 'div' ],
[ BoundElementPropertyAst , PropertyBindingType . Property , 'mappedProp' , 'v' , null ]
]);
2015-08-27 16:29:02 -07:00
});
2015-11-10 15:56:25 -08:00
it ( 'should parse mixed case bound attributes' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parse ( '<div [attr.someAttr]="v">' , [])))
. toEqual ([
[ ElementAst , 'div' ],
[ BoundElementPropertyAst , PropertyBindingType . Attribute , 'someAttr' , 'v' , null ]
]);
2015-11-10 15:56:25 -08:00
});
2015-08-27 16:29:02 -07:00
it ( 'should parse and dash case bound classes' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parse ( '<div [class.some-class]="v">' , [])))
. toEqual ([
[ ElementAst , 'div' ],
[ BoundElementPropertyAst , PropertyBindingType . Class , 'some-class' , 'v' , null ]
]);
2015-11-10 15:56:25 -08:00
});
2015-11-10 15:56:25 -08:00
it ( 'should parse mixed case bound classes' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parse ( '<div [class.someClass]="v">' , [])))
. toEqual ([
[ ElementAst , 'div' ],
[ BoundElementPropertyAst , PropertyBindingType . Class , 'someClass' , 'v' , null ]
]);
2015-11-10 15:56:25 -08:00
});
2015-11-23 16:02:19 -08:00
it ( 'should parse mixed case bound styles' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parse ( '<div [style.someStyle]="v">' , [])))
. toEqual ([
[ ElementAst , 'div' ],
[ BoundElementPropertyAst , PropertyBindingType . Style , 'someStyle' , 'v' , null ]
]);
2015-11-10 15:56:25 -08:00
});
2015-11-23 16:02:19 -08:00
it ( 'should report invalid prefixes' , () => {
expect (() => parse ( '<p [atTr.foo]>' , []))
. toThrowError (
`Template parse errors: \ nInvalid property name 'atTr.foo' ("<p [ERROR ->][atTr.foo]>"): TestComp@0:3` );
expect (() => parse ( '<p [sTyle.foo]>' , []))
. toThrowError (
`Template parse errors: \ nInvalid property name 'sTyle.foo' ("<p [ERROR ->][sTyle.foo]>"): TestComp@0:3` );
expect (() => parse ( '<p [Class.foo]>' , []))
. toThrowError (
`Template parse errors: \ nInvalid property name 'Class.foo' ("<p [ERROR ->][Class.foo]>"): TestComp@0:3` );
expect (() => parse ( '<p [bar.foo]>' , []))
. toThrowError (
`Template parse errors: \ nInvalid property name 'bar.foo' ("<p [ERROR ->][bar.foo]>"): TestComp@0:3` );
2015-08-27 16:29:02 -07:00
});
2015-08-25 15:36:02 -07:00
it ( 'should parse bound properties via [...] and not report them as attributes' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parse ( '<div [prop]="v">' , [])))
. toEqual ([
[ ElementAst , 'div' ],
[ BoundElementPropertyAst , PropertyBindingType . Property , 'prop' , 'v' , null ]
]);
2015-08-25 15:36:02 -07:00
});
it ( 'should parse bound properties via bind- and not report them as attributes' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parse ( '<div bind-prop="v">' , [])))
. toEqual ([
[ ElementAst , 'div' ],
[ BoundElementPropertyAst , PropertyBindingType . Property , 'prop' , 'v' , null ]
]);
2015-08-25 15:36:02 -07:00
});
it ( 'should parse bound properties via {{...}} and not report them as attributes' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parse ( '<div prop="{{v}}">' , [])))
. toEqual ([
[ ElementAst , 'div' ],
[ BoundElementPropertyAst , PropertyBindingType . Property , 'prop' , '{{ v }}' , null ]
]);
2015-08-27 16:29:02 -07:00
});
2016-05-25 12:46:22 -07:00
it ( 'should parse bound properties via animate- and not report them as attributes' , () => {
expect ( humanizeTplAst ( parse ( '<div animate-something="value2">' , [])))
. toEqual ([
[ ElementAst , 'div' ],
[ BoundElementPropertyAst , PropertyBindingType . Animation , 'something' , 'value2' , null ]
]);
});
2015-08-27 16:29:02 -07:00
});
describe ( 'events' , () => {
it ( 'should parse bound events with a target' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parse ( '<div (window:event)="v">' , [])))
. 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' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parse ( '<div (event)="v">' , [])))
. toEqual ([[ ElementAst , 'div' ], [ BoundEventAst , 'event' , null , 'v' ]]);
2015-08-25 15:36:02 -07:00
});
2015-11-23 16:02:19 -08:00
it ( 'should parse event names case sensitive' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parse ( '<div (some-event)="v">' , [])))
. toEqual ([[ ElementAst , 'div' ], [ BoundEventAst , 'some-event' , null , 'v' ]]);
expect ( humanizeTplAst ( parse ( '<div (someEvent)="v">' , [])))
. toEqual ([[ ElementAst , 'div' ], [ BoundEventAst , 'someEvent' , null , 'v' ]]);
2015-08-25 15:36:02 -07:00
});
it ( 'should parse bound events via on- and not report them as attributes' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parse ( '<div on-event="v">' , [])))
. toEqual ([[ ElementAst , 'div' ], [ BoundEventAst , 'event' , null , 'v' ]]);
2015-08-25 15:36:02 -07:00
});
2015-10-13 17:43:15 -07:00
it ( 'should allow events on explicit embedded templates that are emitted by a directive' ,
() => {
var dirA = CompileDirectiveMetadata . create ({
selector : 'template' ,
outputs : [ 'e' ],
2016-01-06 14:13:44 -08:00
type : new CompileTypeMetadata ({ moduleUrl : someModuleUrl , name : 'DirA' })
2015-10-13 17:43:15 -07:00
});
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parse ( '<template (e)="f"></template>' , [ dirA ])))
. toEqual ([
[ EmbeddedTemplateAst ],
[ BoundEventAst , 'e' , null , 'f' ],
[ DirectiveAst , dirA ],
]);
2015-10-13 17:43:15 -07:00
});
2015-08-27 16:29:02 -07:00
});
describe ( 'bindon' , () => {
2015-08-25 15:36:02 -07:00
it ( 'should parse bound events and properties via [(...)] and not report them as attributes' ,
() => {
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parse ( '<div [(prop)]="v">' , [])))
. toEqual ([
[ ElementAst , 'div' ],
[ BoundElementPropertyAst , PropertyBindingType . Property , 'prop' , 'v' , null ],
[ BoundEventAst , 'propChange' , null , 'v = $event' ]
]);
2015-08-25 15:36:02 -07:00
});
it ( 'should parse bound events and properties via bindon- and not report them as attributes' ,
() => {
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parse ( '<div bindon-prop="v">' , [])))
. toEqual ([
[ ElementAst , 'div' ],
[ BoundElementPropertyAst , PropertyBindingType . Property , 'prop' , 'v' , null ],
[ BoundEventAst , 'propChange' , null , 'v = $event' ]
]);
2015-08-25 15:36:02 -07:00
});
2015-08-27 16:29:02 -07:00
});
2015-08-25 15:36:02 -07:00
describe ( 'directives' , () => {
2016-04-25 19:52:24 -07:00
it ( 'should order directives by the directives array in the View and match them only once' ,
2015-09-18 10:33:23 -07:00
() => {
2016-01-06 14:13:44 -08:00
var dirA = CompileDirectiveMetadata . create ({
selector : '[a]' ,
type : new CompileTypeMetadata ({ moduleUrl : someModuleUrl , name : 'DirA' })
});
var dirB = CompileDirectiveMetadata . create ({
selector : '[b]' ,
type : new CompileTypeMetadata ({ moduleUrl : someModuleUrl , name : 'DirB' })
});
var dirC = CompileDirectiveMetadata . create ({
selector : '[c]' ,
type : new CompileTypeMetadata ({ moduleUrl : someModuleUrl , name : 'DirC' })
});
2016-04-25 19:52:24 -07:00
expect ( humanizeTplAst ( parse ( '<div a c b a b>' , [ dirA , dirB , dirC ])))
2016-04-12 09:40:37 -07:00
. toEqual ([
[ ElementAst , 'div' ],
[ AttrAst , 'a' , '' ],
[ AttrAst , 'c' , '' ],
[ AttrAst , 'b' , '' ],
2016-04-25 19:52:24 -07:00
[ AttrAst , 'a' , '' ],
[ AttrAst , 'b' , '' ],
2016-04-12 09:40:37 -07:00
[ DirectiveAst , dirA ],
[ DirectiveAst , dirB ],
[ DirectiveAst , dirC ]
]);
2015-09-18 10:33:23 -07:00
});
2015-08-25 15:36:02 -07:00
it ( 'should locate directives in property bindings' , () => {
2016-01-06 14:13:44 -08:00
var dirA = CompileDirectiveMetadata . create ({
selector : '[a=b]' ,
type : new CompileTypeMetadata ({ moduleUrl : someModuleUrl , name : 'DirA' })
});
var dirB = CompileDirectiveMetadata . create ({
selector : '[b]' ,
type : new CompileTypeMetadata ({ moduleUrl : someModuleUrl , name : 'DirB' })
});
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parse ( '<div [a]="b">' , [ dirA , dirB ])))
. toEqual ([
[ ElementAst , 'div' ],
[ BoundElementPropertyAst , PropertyBindingType . Property , 'a' , 'b' , null ],
[ DirectiveAst , dirA ]
]);
2015-08-25 15:36:02 -07:00
});
2016-02-03 11:35:42 -08:00
it ( 'should locate directives in event bindings' , () => {
2016-01-06 14:13:44 -08:00
var dirA = CompileDirectiveMetadata . create ({
selector : '[a]' ,
type : new CompileTypeMetadata ({ moduleUrl : someModuleUrl , name : 'DirB' })
});
2016-02-03 11:35:42 -08:00
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parse ( '<div (a)="b">' , [ dirA ])))
. toEqual (
[[ ElementAst , 'div' ], [ BoundEventAst , 'a' , null , 'b' ], [ DirectiveAst , dirA ]]);
2016-02-03 11:35:42 -08:00
});
2015-08-27 16:29:02 -07:00
it ( 'should parse directive host properties' , () => {
2015-09-18 10:33:23 -07:00
var dirA = CompileDirectiveMetadata . create ({
2015-08-27 16:29:02 -07:00
selector : 'div' ,
2016-01-06 14:13:44 -08:00
type : new CompileTypeMetadata ({ moduleUrl : someModuleUrl , name : 'DirA' }),
2015-09-18 10:33:23 -07:00
host : { '[a]' : 'expr' }
2015-08-27 16:29:02 -07:00
});
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parse ( '<div></div>' , [ dirA ])))
. toEqual ([
[ ElementAst , 'div' ],
[ DirectiveAst , dirA ],
[ BoundElementPropertyAst , PropertyBindingType . Property , 'a' , 'expr' , null ]
]);
2015-08-27 16:29:02 -07:00
});
it ( 'should parse directive host listeners' , () => {
2015-09-18 10:33:23 -07:00
var dirA = CompileDirectiveMetadata . create ({
2015-08-27 16:29:02 -07:00
selector : 'div' ,
2016-01-06 14:13:44 -08:00
type : new CompileTypeMetadata ({ moduleUrl : someModuleUrl , name : 'DirA' }),
2015-09-18 10:33:23 -07:00
host : { '(a)' : 'expr' }
2015-08-27 16:29:02 -07:00
});
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parse ( '<div></div>' , [ dirA ])))
. toEqual (
[[ ElementAst , 'div' ], [ DirectiveAst , dirA ], [ BoundEventAst , 'a' , null , 'expr' ]]);
2015-08-27 16:29:02 -07:00
});
it ( 'should parse directive properties' , () => {
2016-01-06 14:13:44 -08:00
var dirA = CompileDirectiveMetadata . create ({
selector : 'div' ,
type : new CompileTypeMetadata ({ moduleUrl : someModuleUrl , name : 'DirA' }),
inputs : [ 'aProp' ]
});
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parse ( '<div [aProp]="expr"></div>' , [ dirA ])))
. toEqual ([
[ ElementAst , 'div' ],
[ DirectiveAst , dirA ],
[ BoundDirectivePropertyAst , 'aProp' , 'expr' ]
]);
2015-08-27 16:29:02 -07:00
});
it ( 'should parse renamed directive properties' , () => {
2016-01-06 14:13:44 -08:00
var dirA = CompileDirectiveMetadata . create ({
selector : 'div' ,
type : new CompileTypeMetadata ({ moduleUrl : someModuleUrl , name : 'DirA' }),
inputs : [ 'b:a' ]
});
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parse ( '<div [a]="expr"></div>' , [ dirA ])))
. toEqual ([
[ ElementAst , 'div' ],
[ DirectiveAst , dirA ],
[ BoundDirectivePropertyAst , 'b' , 'expr' ]
]);
2015-08-27 16:29:02 -07:00
});
it ( 'should parse literal directive properties' , () => {
2016-01-06 14:13:44 -08:00
var dirA = CompileDirectiveMetadata . create ({
selector : 'div' ,
type : new CompileTypeMetadata ({ moduleUrl : someModuleUrl , name : 'DirA' }),
inputs : [ 'a' ]
});
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parse ( '<div a="literal"></div>' , [ dirA ])))
. toEqual ([
[ ElementAst , 'div' ],
[ AttrAst , 'a' , 'literal' ],
[ DirectiveAst , dirA ],
[ BoundDirectivePropertyAst , 'a' , '"literal"' ]
]);
2015-08-27 16:29:02 -07:00
});
2015-09-18 10:33:23 -07:00
it ( 'should favor explicit bound properties over literal properties' , () => {
2016-01-06 14:13:44 -08:00
var dirA = CompileDirectiveMetadata . create ({
selector : 'div' ,
type : new CompileTypeMetadata ({ moduleUrl : someModuleUrl , 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 ([
2016-04-12 09:40:37 -07:00
[ ElementAst , 'div' ],
[ AttrAst , 'a' , 'literal' ],
[ DirectiveAst , dirA ],
2015-10-07 09:34:21 -07:00
[ BoundDirectivePropertyAst , 'a' , '"literal2"' ]
2015-09-18 10:33:23 -07:00
]);
});
2015-08-27 16:29:02 -07:00
it ( 'should support optional directive properties' , () => {
2016-01-06 14:13:44 -08:00
var dirA = CompileDirectiveMetadata . create ({
selector : 'div' ,
2015-12-20 09:56:00 -05:00
type : new CompileTypeMetadata ({ moduleUrl : someModuleUrl , name : 'Dir' }),
2016-01-06 14:13:44 -08:00
inputs : [ 'a' ]
});
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parse ( '<div></div>' , [ dirA ])))
. toEqual ([[ ElementAst , 'div' ], [ DirectiveAst , dirA ]]);
2015-08-27 16:29:02 -07:00
});
2015-08-25 15:36:02 -07:00
});
2016-01-06 14:13:44 -08:00
describe ( 'providers' , () => {
var nextProviderId ;
function createToken ( value : string ) : CompileTokenMetadata {
var token ;
if ( value . startsWith ( 'type:' )) {
token = new CompileTokenMetadata ({
identifier :
new CompileTypeMetadata ({ moduleUrl : someModuleUrl , name : value.substring ( 5 )})
});
} else {
token = new CompileTokenMetadata ({ value : value });
}
return token ;
}
function createDep ( value : string ) : CompileDiDependencyMetadata {
var isOptional = false ;
if ( value . startsWith ( 'optional:' )) {
isOptional = true ;
value = value . substring ( 9 );
}
var isSelf = false ;
if ( value . startsWith ( 'self:' )) {
isSelf = true ;
value = value . substring ( 5 );
}
var isHost = false ;
if ( value . startsWith ( 'host:' )) {
isHost = true ;
value = value . substring ( 5 );
}
return new CompileDiDependencyMetadata (
{ token : createToken ( value ), isOptional : isOptional , isSelf : isSelf , isHost : isHost });
}
function createProvider (
2016-04-25 21:47:33 -07:00
token : string , { multi = false , deps = /*@ts2dart_const*/ [
]} : { multi? : boolean , deps? : string []} = {}) : CompileProviderMetadata {
2016-01-06 14:13:44 -08:00
return new CompileProviderMetadata ({
token : createToken ( token ),
multi : multi ,
useClass : new CompileTypeMetadata ({ name : `provider ${ nextProviderId ++ } ` }),
deps : deps.map ( createDep )
});
}
2016-04-25 21:47:33 -07:00
function createDir ( selector : string ,
{ providers = null , viewProviders = null , deps = /*@ts2dart_const*/ [],
queries = /*@ts2dart_const*/ []} : {
providers? : CompileProviderMetadata [],
viewProviders? : CompileProviderMetadata [],
deps? : string [],
queries? : string []
} = {}) : CompileDirectiveMetadata {
2016-01-06 14:13:44 -08:00
var isComponent = ! selector . startsWith ( '[' );
return CompileDirectiveMetadata . create ({
selector : selector ,
type : new CompileTypeMetadata (
{ moduleUrl : someModuleUrl , name : selector , diDeps : deps.map ( createDep )}),
isComponent : isComponent ,
template : new CompileTemplateMetadata ({ ngContentSelectors : []}),
providers : providers ,
viewProviders : viewProviders ,
queries : queries.map (( value ) =>
new CompileQueryMetadata ({ selectors : [ createToken ( value )]}))
});
}
beforeEach (() => { nextProviderId = 0 ; });
it ( 'should provide a component' , () => {
var comp = createDir ( 'my-comp' );
var elAst : ElementAst = < ElementAst > parse ( '<my-comp>' , [ comp ])[ 0 ];
expect ( elAst . providers . length ). toBe ( 1 );
expect ( elAst . providers [ 0 ]. providerType ). toBe ( ProviderAstType . Component );
expect ( elAst . providers [ 0 ]. providers [ 0 ]. useClass ). toBe ( comp . type );
});
it ( 'should provide a directive' , () => {
var dirA = createDir ( '[dirA]' );
var elAst : ElementAst = < ElementAst > parse ( '<div dirA>' , [ dirA ])[ 0 ];
expect ( elAst . providers . length ). toBe ( 1 );
expect ( elAst . providers [ 0 ]. providerType ). toBe ( ProviderAstType . Directive );
expect ( elAst . providers [ 0 ]. providers [ 0 ]. useClass ). toBe ( dirA . type );
});
it ( 'should use the public providers of a directive' , () => {
var provider = createProvider ( 'service' );
var dirA = createDir ( '[dirA]' , { providers : [ provider ]});
var elAst : ElementAst = < ElementAst > parse ( '<div dirA>' , [ dirA ])[ 0 ];
expect ( elAst . providers . length ). toBe ( 2 );
expect ( elAst . providers [ 1 ]. providerType ). toBe ( ProviderAstType . PublicService );
expect ( elAst . providers [ 1 ]. providers ). toEqual ([ provider ]);
});
it ( 'should use the private providers of a component' , () => {
var provider = createProvider ( 'service' );
var comp = createDir ( 'my-comp' , { viewProviders : [ provider ]});
var elAst : ElementAst = < ElementAst > parse ( '<my-comp>' , [ comp ])[ 0 ];
expect ( elAst . providers . length ). toBe ( 2 );
expect ( elAst . providers [ 1 ]. providerType ). toBe ( ProviderAstType . PrivateService );
expect ( elAst . providers [ 1 ]. providers ). toEqual ([ provider ]);
});
it ( 'should support multi providers' , () => {
var provider0 = createProvider ( 'service0' , { multi : true });
var provider1 = createProvider ( 'service1' , { multi : true });
var provider2 = createProvider ( 'service0' , { multi : true });
var dirA = createDir ( '[dirA]' , { providers : [ provider0 , provider1 ]});
var dirB = createDir ( '[dirB]' , { providers : [ provider2 ]});
var elAst : ElementAst = < ElementAst > parse ( '<div dirA dirB>' , [ dirA , dirB ])[ 0 ];
expect ( elAst . providers . length ). toBe ( 4 );
expect ( elAst . providers [ 2 ]. providers ). toEqual ([ provider0 , provider2 ]);
expect ( elAst . providers [ 3 ]. providers ). toEqual ([ provider1 ]);
});
it ( 'should overwrite non multi providers' , () => {
var provider1 = createProvider ( 'service0' );
var provider2 = createProvider ( 'service1' );
var provider3 = createProvider ( 'service0' );
var dirA = createDir ( '[dirA]' , { providers : [ provider1 , provider2 ]});
var dirB = createDir ( '[dirB]' , { providers : [ provider3 ]});
var elAst : ElementAst = < ElementAst > parse ( '<div dirA dirB>' , [ dirA , dirB ])[ 0 ];
expect ( elAst . providers . length ). toBe ( 4 );
expect ( elAst . providers [ 2 ]. providers ). toEqual ([ provider3 ]);
expect ( elAst . providers [ 3 ]. providers ). toEqual ([ provider2 ]);
});
it ( 'should overwrite component providers by directive providers' , () => {
var compProvider = createProvider ( 'service0' );
var dirProvider = createProvider ( 'service0' );
var comp = createDir ( 'my-comp' , { providers : [ compProvider ]});
var dirA = createDir ( '[dirA]' , { providers : [ dirProvider ]});
var elAst : ElementAst = < ElementAst > parse ( '<my-comp dirA>' , [ dirA , comp ])[ 0 ];
expect ( elAst . providers . length ). toBe ( 3 );
expect ( elAst . providers [ 2 ]. providers ). toEqual ([ dirProvider ]);
});
it ( 'should overwrite view providers by directive providers' , () => {
var viewProvider = createProvider ( 'service0' );
var dirProvider = createProvider ( 'service0' );
var comp = createDir ( 'my-comp' , { viewProviders : [ viewProvider ]});
var dirA = createDir ( '[dirA]' , { providers : [ dirProvider ]});
var elAst : ElementAst = < ElementAst > parse ( '<my-comp dirA>' , [ dirA , comp ])[ 0 ];
expect ( elAst . providers . length ). toBe ( 3 );
expect ( elAst . providers [ 2 ]. providers ). toEqual ([ dirProvider ]);
});
it ( 'should overwrite directives by providers' , () => {
var dirProvider = createProvider ( 'type:my-comp' );
var comp = createDir ( 'my-comp' , { providers : [ dirProvider ]});
var elAst : ElementAst = < ElementAst > parse ( '<my-comp>' , [ comp ])[ 0 ];
expect ( elAst . providers . length ). toBe ( 1 );
expect ( elAst . providers [ 0 ]. providers ). toEqual ([ dirProvider ]);
});
it ( 'should throw if mixing multi and non multi providers' , () => {
var provider0 = createProvider ( 'service0' );
var provider1 = createProvider ( 'service0' , { multi : true });
var dirA = createDir ( '[dirA]' , { providers : [ provider0 ]});
var dirB = createDir ( '[dirB]' , { providers : [ provider1 ]});
expect (() => parse ( '<div dirA dirB>' , [ dirA , dirB ]))
. toThrowError (
`Template parse errors: \ n` +
`Mixing multi and non multi provider is not possible for token service0 ("[ERROR ->]<div dirA dirB>"): TestComp@0:0` );
});
2016-04-25 08:39:53 -07:00
it ( 'should sort providers by their DI order' , () => {
2016-01-06 14:13:44 -08:00
var provider0 = createProvider ( 'service0' , { deps : [ 'type:[dir2]' ]});
var provider1 = createProvider ( 'service1' );
var dir2 = createDir ( '[dir2]' , { deps : [ 'service1' ]});
var comp = createDir ( 'my-comp' , { providers : [ provider0 , provider1 ]});
var elAst : ElementAst = < ElementAst > parse ( '<my-comp dir2>' , [ comp , dir2 ])[ 0 ];
expect ( elAst . providers . length ). toBe ( 4 );
expect ( elAst . providers [ 0 ]. providers [ 0 ]. useClass ). toEqual ( comp . type );
expect ( elAst . providers [ 1 ]. providers ). toEqual ([ provider1 ]);
expect ( elAst . providers [ 2 ]. providers [ 0 ]. useClass ). toEqual ( dir2 . type );
expect ( elAst . providers [ 3 ]. providers ). toEqual ([ provider0 ]);
});
2016-04-25 08:39:53 -07:00
it ( 'should sort directives by their DI order' , () => {
var dir0 = createDir ( '[dir0]' , { deps : [ 'type:my-comp' ]});
var dir1 = createDir ( '[dir1]' , { deps : [ 'type:[dir0]' ]});
var dir2 = createDir ( '[dir2]' , { deps : [ 'type:[dir1]' ]});
var comp = createDir ( 'my-comp' );
var elAst : ElementAst =
< ElementAst > parse ( '<my-comp dir2 dir0 dir1>' , [ comp , dir2 , dir0 , dir1 ])[ 0 ];
expect ( elAst . providers . length ). toBe ( 4 );
expect ( elAst . directives [ 0 ]. directive ). toBe ( comp );
expect ( elAst . directives [ 1 ]. directive ). toBe ( dir0 );
expect ( elAst . directives [ 2 ]. directive ). toBe ( dir1 );
expect ( elAst . directives [ 3 ]. directive ). toBe ( dir2 );
});
2016-01-06 14:13:44 -08:00
it ( 'should mark directives and dependencies of directives as eager' , () => {
var provider0 = createProvider ( 'service0' );
var provider1 = createProvider ( 'service1' );
var dirA = createDir ( '[dirA]' , { providers : [ provider0 , provider1 ], deps : [ 'service0' ]});
var elAst : ElementAst = < ElementAst > parse ( '<div dirA>' , [ dirA ])[ 0 ];
expect ( elAst . providers . length ). toBe ( 3 );
expect ( elAst . providers [ 0 ]. providers ). toEqual ([ provider0 ]);
expect ( elAst . providers [ 0 ]. eager ). toBe ( true );
expect ( elAst . providers [ 1 ]. providers [ 0 ]. useClass ). toEqual ( dirA . type );
expect ( elAst . providers [ 1 ]. eager ). toBe ( true );
expect ( elAst . providers [ 2 ]. providers ). toEqual ([ provider1 ]);
expect ( elAst . providers [ 2 ]. eager ). toBe ( false );
});
it ( 'should mark dependencies on parent elements as eager' , () => {
var provider0 = createProvider ( 'service0' );
var provider1 = createProvider ( 'service1' );
var dirA = createDir ( '[dirA]' , { providers : [ provider0 , provider1 ]});
var dirB = createDir ( '[dirB]' , { deps : [ 'service0' ]});
var elAst : ElementAst =
< ElementAst > parse ( '<div dirA><div dirB></div></div>' , [ dirA , dirB ])[ 0 ];
expect ( elAst . providers . length ). toBe ( 3 );
expect ( elAst . providers [ 0 ]. providers [ 0 ]. useClass ). toEqual ( dirA . type );
expect ( elAst . providers [ 0 ]. eager ). toBe ( true );
expect ( elAst . providers [ 1 ]. providers ). toEqual ([ provider0 ]);
expect ( elAst . providers [ 1 ]. eager ). toBe ( true );
expect ( elAst . providers [ 2 ]. providers ). toEqual ([ provider1 ]);
expect ( elAst . providers [ 2 ]. eager ). toBe ( false );
});
it ( 'should mark queried providers as eager' , () => {
var provider0 = createProvider ( 'service0' );
var provider1 = createProvider ( 'service1' );
var dirA =
createDir ( '[dirA]' , { providers : [ provider0 , provider1 ], queries : [ 'service0' ]});
var elAst : ElementAst = < ElementAst > parse ( '<div dirA></div>' , [ dirA ])[ 0 ];
expect ( elAst . providers . length ). toBe ( 3 );
expect ( elAst . providers [ 0 ]. providers [ 0 ]. useClass ). toEqual ( dirA . type );
expect ( elAst . providers [ 0 ]. eager ). toBe ( true );
expect ( elAst . providers [ 1 ]. providers ). toEqual ([ provider0 ]);
expect ( elAst . providers [ 1 ]. eager ). toBe ( true );
expect ( elAst . providers [ 2 ]. providers ). toEqual ([ provider1 ]);
expect ( elAst . providers [ 2 ]. eager ). toBe ( false );
});
it ( 'should not mark dependencies accross embedded views as eager' , () => {
var provider0 = createProvider ( 'service0' );
var dirA = createDir ( '[dirA]' , { providers : [ provider0 ]});
var dirB = createDir ( '[dirB]' , { deps : [ 'service0' ]});
var elAst : ElementAst =
< ElementAst > parse ( '<div dirA><div *ngIf dirB></div></div>' , [ dirA , dirB ])[ 0 ];
expect ( elAst . providers . length ). toBe ( 2 );
expect ( elAst . providers [ 0 ]. providers [ 0 ]. useClass ). toEqual ( dirA . type );
expect ( elAst . providers [ 0 ]. eager ). toBe ( true );
expect ( elAst . providers [ 1 ]. providers ). toEqual ([ provider0 ]);
expect ( elAst . providers [ 1 ]. eager ). toBe ( false );
});
it ( 'should report missing @Self() deps as errors' , () => {
var dirA = createDir ( '[dirA]' , { deps : [ 'self:provider0' ]});
expect (() => parse ( '<div dirA></div>' , [ dirA ]))
. toThrowErrorWith (
'No provider for provider0 ("[ERROR ->]<div dirA></div>"): TestComp@0:0' );
});
it ( 'should change missing @Self() that are optional to nulls' , () => {
var dirA = createDir ( '[dirA]' , { deps : [ 'optional:self:provider0' ]});
var elAst : ElementAst = < ElementAst > parse ( '<div dirA></div>' , [ dirA ])[ 0 ];
expect ( elAst . providers [ 0 ]. providers [ 0 ]. deps [ 0 ]. isValue ). toBe ( true );
expect ( elAst . providers [ 0 ]. providers [ 0 ]. deps [ 0 ]. value ). toBe ( null );
});
it ( 'should report missing @Host() deps as errors' , () => {
var dirA = createDir ( '[dirA]' , { deps : [ 'host:provider0' ]});
expect (() => parse ( '<div dirA></div>' , [ dirA ]))
. toThrowErrorWith (
'No provider for provider0 ("[ERROR ->]<div dirA></div>"): TestComp@0:0' );
});
it ( 'should change missing @Host() that are optional to nulls' , () => {
var dirA = createDir ( '[dirA]' , { deps : [ 'optional:host:provider0' ]});
var elAst : ElementAst = < ElementAst > parse ( '<div dirA></div>' , [ dirA ])[ 0 ];
expect ( elAst . providers [ 0 ]. providers [ 0 ]. deps [ 0 ]. isValue ). toBe ( true );
expect ( elAst . providers [ 0 ]. providers [ 0 ]. deps [ 0 ]. value ). toBe ( null );
});
});
2016-04-25 19:52:24 -07:00
describe ( 'references' , () => {
2015-09-18 10:33:23 -07:00
2016-04-25 19:52:24 -07:00
it ( 'should parse references via #... and not report them as attributes' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parse ( '<div #a>' , [])))
2016-04-25 19:52:24 -07:00
. toEqual ([[ ElementAst , 'div' ], [ ReferenceAst , 'a' , null ]]);
2015-09-18 10:33:23 -07:00
});
2016-04-25 19:52:24 -07:00
it ( 'should parse references via ref-... and not report them as attributes' , () => {
expect ( humanizeTplAst ( parse ( '<div ref-a>' , [])))
. toEqual ([[ ElementAst , 'div' ], [ ReferenceAst , 'a' , null ]]);
});
it ( 'should parse references via var-... and report them as deprecated' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parse ( '<div var-a>' , [])))
2016-04-25 19:52:24 -07:00
. toEqual ([[ ElementAst , 'div' ], [ ReferenceAst , 'a' , null ]]);
expect ( console . warnings )
. toEqual ([
[
'Template parse warnings:' ,
'"var-" on non <template> elements is deprecated. Use "ref-" instead! ("<div [ERROR ->]var-a>"): TestComp@0:5'
]. join ( '\n' )
]);
2015-09-18 10:33:23 -07:00
});
2016-04-25 19:52:24 -07:00
it ( 'should parse camel case references' , () => {
expect ( humanizeTplAst ( parse ( '<div ref-someA>' , [])))
. toEqual ([[ ElementAst , 'div' ], [ ReferenceAst , 'someA' , null ]]);
2015-09-18 10:33:23 -07:00
});
2016-04-25 19:52:24 -07:00
it ( 'should assign references with empty value to the element' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parse ( '<div #a></div>' , [])))
2016-04-25 19:52:24 -07:00
. toEqual ([[ ElementAst , 'div' ], [ ReferenceAst , 'a' , null ]]);
2015-09-18 10:33:23 -07:00
});
2016-04-25 19:52:24 -07:00
it ( 'should assign references to directives via exportAs' , () => {
2016-01-06 14:13:44 -08:00
var dirA = CompileDirectiveMetadata . create ({
selector : '[a]' ,
type : new CompileTypeMetadata ({ moduleUrl : someModuleUrl , name : 'DirA' }),
exportAs : 'dirA'
});
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parse ( '<div a #a="dirA"></div>' , [ dirA ])))
. toEqual ([
[ ElementAst , 'div' ],
[ AttrAst , 'a' , '' ],
2016-04-25 19:52:24 -07:00
[ ReferenceAst , 'a' , identifierToken ( dirA . type )],
2016-04-12 09:40:37 -07:00
[ DirectiveAst , dirA ],
]);
2015-09-18 10:33:23 -07:00
});
2016-04-25 19:52:24 -07:00
it ( 'should report references with values that dont match a directive as errors' , () => {
2015-09-18 10:33:23 -07:00
expect (() => parse ( '<div #a="dirA"></div>' , [])). toThrowError ( `Template parse errors:
2015-11-10 15:56:25 -08:00
There is no directive with "exportAs" set to "dirA" ("<div [ERROR ->]#a="dirA"></div>"): TestComp@0:5` );
2015-09-18 10:33:23 -07:00
});
2016-04-25 19:52:24 -07:00
it ( 'should report invalid reference names' , () => {
2015-11-23 16:02:19 -08:00
expect (() => parse ( '<div #a-b></div>' , [])). toThrowError ( `Template parse errors:
2016-04-25 19:52:24 -07:00
"-" is not allowed in reference names ("<div [ERROR ->]#a-b></div>"): TestComp@0:5` );
2015-11-23 16:02:19 -08:00
});
2016-04-25 19:52:24 -07:00
it ( 'should report variables as errors' , () => {
expect (() => parse ( '<div let-a></div>' , [])). toThrowError ( `Template parse errors:
"let-" is only supported on template elements. ("<div [ERROR ->]let-a></div>"): TestComp@0:5` );
});
2015-09-18 10:33:23 -07:00
2016-05-26 23:04:17 +03:00
it ( 'should report duplicate reference names' , () => {
expect (() => parse ( '<div #a></div><div #a></div>' , []))
. toThrowError ( `Template parse errors:
Reference "#a" is defined several times ("<div #a></div><div [ERROR ->]#a></div>"): TestComp@0:19` );
});
it ( 'should not throw error when there is same reference name in different templates' , () => {
expect (() => parse ( '<div #a><template #a><span>OK</span></template></div>' , [])). not . toThrowError ();
});
2016-04-25 19:52:24 -07:00
it ( 'should assign references with empty value to components' , () => {
2015-09-18 10:33:23 -07:00
var dirA = CompileDirectiveMetadata . create ({
selector : '[a]' ,
isComponent : true ,
2016-01-06 14:13:44 -08:00
type : new CompileTypeMetadata ({ moduleUrl : someModuleUrl , name : 'DirA' }),
2015-10-28 08:59:19 +01:00
exportAs : 'dirA' ,
template : new CompileTemplateMetadata ({ ngContentSelectors : []})
2015-09-18 10:33:23 -07:00
});
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parse ( '<div a #a></div>' , [ dirA ])))
. toEqual ([
[ ElementAst , 'div' ],
[ AttrAst , 'a' , '' ],
2016-04-25 19:52:24 -07:00
[ ReferenceAst , 'a' , identifierToken ( dirA . type )],
2016-04-12 09:40:37 -07:00
[ DirectiveAst , dirA ],
]);
2015-09-18 10:33:23 -07:00
});
2016-04-25 19:52:24 -07:00
it ( 'should not locate directives in references' , () => {
var dirA = CompileDirectiveMetadata . create ({
selector : '[a]' ,
type : new CompileTypeMetadata ({ moduleUrl : someModuleUrl , name : 'DirA' })
});
expect ( humanizeTplAst ( parse ( '<div ref-a>' , [ dirA ])))
. toEqual ([[ ElementAst , 'div' ], [ ReferenceAst , 'a' , null ]]);
});
2015-09-18 10:33:23 -07:00
});
2015-08-25 15:36:02 -07:00
describe ( 'explicit templates' , () => {
it ( 'should create embedded templates for <template> elements' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parse ( '<template></template>' , [])))
. toEqual ([[ EmbeddedTemplateAst ]]);
expect ( humanizeTplAst ( parse ( '<TEMPLATE></TEMPLATE>' , [])))
. toEqual ([[ EmbeddedTemplateAst ]]);
2015-08-25 15:36:02 -07:00
});
2015-12-08 09:01:15 -08:00
it ( 'should create embedded templates for <template> elements regardless the namespace' ,
() => {
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parse ( '<svg><template></template></svg>' , [])))
. toEqual ([
2016-04-29 14:54:46 -07:00
[ ElementAst , ':svg:svg' ],
2016-04-12 09:40:37 -07:00
[ EmbeddedTemplateAst ],
]);
2015-12-08 09:01:15 -08:00
});
2016-04-25 19:52:24 -07:00
it ( 'should support references via #...' , () => {
expect ( humanizeTplAst ( parse ( '<template #a>' , [])))
. toEqual ([
[ EmbeddedTemplateAst ],
[ ReferenceAst , 'a' , identifierToken ( Identifiers . TemplateRef )]
]);
});
it ( 'should support references via ref-...' , () => {
expect ( humanizeTplAst ( parse ( '<template ref-a>' , [])))
. toEqual ([
[ EmbeddedTemplateAst ],
[ ReferenceAst , 'a' , identifierToken ( Identifiers . TemplateRef )]
]);
});
it ( 'should parse variables via let-...' , () => {
expect ( humanizeTplAst ( parse ( '<template let-a="b">' , [])))
. toEqual ([[ EmbeddedTemplateAst ], [ VariableAst , 'a' , 'b' ]]);
});
it ( 'should parse variables via var-... and report them as deprecated' , () => {
expect ( humanizeTplAst ( parse ( '<template var-a="b">' , [])))
. toEqual ([[ EmbeddedTemplateAst ], [ VariableAst , 'a' , 'b' ]]);
expect ( console . warnings )
. toEqual ([
[
'Template parse warnings:' ,
'"var-" on <template> elements is deprecated. Use "let-" instead! ("<template [ERROR ->]var-a="b">"): TestComp@0:10'
]. join ( '\n' )
]);
});
it ( 'should not locate directives in variables' , () => {
var dirA = CompileDirectiveMetadata . create ({
selector : '[a]' ,
type : new CompileTypeMetadata ({ moduleUrl : someModuleUrl , name : 'DirA' })
});
expect ( humanizeTplAst ( parse ( '<template let-a="b"></template>' , [ dirA ])))
. toEqual ([[ EmbeddedTemplateAst ], [ VariableAst , 'a' , 'b' ]]);
});
2015-08-25 15:36:02 -07:00
});
describe ( 'inline templates' , () => {
it ( 'should wrap the element into an EmbeddedTemplateAST' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parse ( '<div template>' , [])))
. toEqual ([[ EmbeddedTemplateAst ], [ ElementAst , 'div' ]]);
2015-08-25 15:36:02 -07:00
});
it ( 'should parse bound properties' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parse ( '<div template="ngIf test">' , [ ngIf ])))
. toEqual ([
[ EmbeddedTemplateAst ],
[ DirectiveAst , ngIf ],
[ BoundDirectivePropertyAst , 'ngIf' , 'test' ],
[ ElementAst , 'div' ]
]);
2015-08-25 15:36:02 -07:00
});
2016-04-25 19:52:24 -07:00
it ( 'should parse variables via #... and report them as deprecated' , () => {
expect ( humanizeTplAst ( parse ( '<div *ngIf="#a=b">' , [])))
2016-04-12 09:40:37 -07:00
. toEqual ([[ EmbeddedTemplateAst ], [ VariableAst , 'a' , 'b' ], [ ElementAst , 'div' ]]);
2016-04-25 19:52:24 -07:00
expect ( console . warnings )
. toEqual ([
[
'Template parse warnings:' ,
'"#" inside of expressions is deprecated. Use "let" instead! ("<div [ERROR ->]*ngIf="#a=b">"): TestComp@0:5'
]. join ( '\n' )
]);
2015-08-25 15:36:02 -07:00
});
2016-04-25 19:52:24 -07:00
it ( 'should parse variables via var ... and report them as deprecated' , () => {
expect ( humanizeTplAst ( parse ( '<div *ngIf="var a=b">' , [])))
. toEqual ([[ EmbeddedTemplateAst ], [ VariableAst , 'a' , 'b' ], [ ElementAst , 'div' ]]);
expect ( console . warnings )
. toEqual ([
[
'Template parse warnings:' ,
'"var" inside of expressions is deprecated. Use "let" instead! ("<div [ERROR ->]*ngIf="var a=b">"): TestComp@0:5'
]. join ( '\n' )
]);
});
it ( 'should parse variables via let ...' , () => {
expect ( humanizeTplAst ( parse ( '<div *ngIf="let a=b">' , [])))
2016-04-12 09:40:37 -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' , () => {
2016-01-06 14:13:44 -08:00
var dirA = CompileDirectiveMetadata . create ({
selector : '[a=b]' ,
type : new CompileTypeMetadata ({ moduleUrl : someModuleUrl , name : 'DirA' }),
inputs : [ 'a' ]
});
var dirB = CompileDirectiveMetadata . create ({
selector : '[b]' ,
type : new CompileTypeMetadata ({ moduleUrl : someModuleUrl , name : 'DirB' })
});
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parse ( '<div template="a b" b>' , [ dirA , dirB ])))
. toEqual ([
[ EmbeddedTemplateAst ],
[ DirectiveAst , dirA ],
[ BoundDirectivePropertyAst , 'a' , 'b' ],
[ ElementAst , 'div' ],
[ AttrAst , 'b' , '' ],
[ DirectiveAst , dirB ]
]);
2015-08-25 15:36:02 -07:00
});
2016-04-25 19:52:24 -07:00
it ( 'should not locate directives in variables' , () => {
2016-01-06 14:13:44 -08:00
var dirA = CompileDirectiveMetadata . create ({
2016-04-25 19:52:24 -07:00
selector : '[a]' ,
2016-01-06 14:13:44 -08:00
type : new CompileTypeMetadata ({ moduleUrl : someModuleUrl , name : 'DirA' })
});
2016-04-25 19:52:24 -07:00
expect ( humanizeTplAst ( parse ( '<div template="let a=b">' , [ dirA ])))
. toEqual ([[ EmbeddedTemplateAst ], [ VariableAst , 'a' , 'b' ], [ ElementAst , 'div' ]]);
});
it ( 'should not locate directives in references' , () => {
var dirA = CompileDirectiveMetadata . create ({
selector : '[a]' ,
type : new CompileTypeMetadata ({ moduleUrl : someModuleUrl , name : 'DirA' })
2016-01-06 14:13:44 -08:00
});
2016-04-25 19:52:24 -07:00
expect ( humanizeTplAst ( parse ( '<div ref-a>' , [ dirA ])))
. toEqual ([[ ElementAst , 'div' ], [ ReferenceAst , 'a' , null ]]);
2015-08-25 15:36:02 -07:00
});
});
it ( 'should work with *... and use the attribute name as property binding name' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parse ( '<div *ngIf="test">' , [ ngIf ])))
. toEqual ([
[ EmbeddedTemplateAst ],
[ DirectiveAst , ngIf ],
[ BoundDirectivePropertyAst , 'ngIf' , 'test' ],
[ ElementAst , 'div' ]
]);
2015-08-25 15:36:02 -07:00
});
2015-10-26 13:22:45 -07:00
it ( 'should work with *... and empty value' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parse ( '<div *ngIf>' , [ ngIf ])))
. toEqual ([
[ EmbeddedTemplateAst ],
[ DirectiveAst , ngIf ],
[ BoundDirectivePropertyAst , 'ngIf' , 'null' ],
[ ElementAst , 'div' ]
]);
2015-10-26 13:22:45 -07:00
});
2015-12-20 09:56:00 -05:00
it ( 'should work with *... and :' , () => {
var dirA = CompileDirectiveMetadata . create (
{ selector : '[a]' , type : new CompileTypeMetadata ({ name : 'DirA' }), inputs : [ 'a' , 'b' ]});
expect ( humanizeTplAst ( parse ( '<div *a="b: foo">' , [ dirA ])))
. toEqual ([
[ EmbeddedTemplateAst ],
[ DirectiveAst , dirA ],
[ BoundDirectivePropertyAst , 'a' , 'b' ],
[ ElementAst , 'div' ]
]);
});
2015-08-25 15:36:02 -07:00
});
});
2015-09-11 13:37:05 -07:00
describe ( 'content projection' , () => {
2016-01-06 14:13:44 -08:00
var compCounter ;
beforeEach (() => { compCounter = 0 ; });
2016-04-12 09:40:37 -07:00
function createComp ( selector : string ,
ngContentSelectors : string []) : CompileDirectiveMetadata {
2015-09-18 10:33:23 -07:00
return CompileDirectiveMetadata . create ({
2015-09-11 13:37:05 -07:00
selector : selector ,
isComponent : true ,
2016-01-06 14:13:44 -08:00
type :
new CompileTypeMetadata ({ moduleUrl : someModuleUrl , name : `SomeComp ${ compCounter ++ } ` }),
2015-09-18 10:33:23 -07:00
template : new CompileTemplateMetadata ({ ngContentSelectors : ngContentSelectors })
2015-09-11 13:37:05 -07:00
})
}
2016-04-29 17:07:28 -07:00
function createDir ( selector : string ) : CompileDirectiveMetadata {
return CompileDirectiveMetadata . create ({
selector : selector ,
type :
new CompileTypeMetadata ({ moduleUrl : someModuleUrl , name : `SomeDir ${ compCounter ++ } ` })
})
}
2015-09-11 13:37:05 -07:00
describe ( 'project text nodes' , () => {
it ( 'should project text nodes with wildcard selector' , () => {
expect ( humanizeContentProjection ( parse ( '<div>hello</div>' , [ createComp ( 'div' , [ '*' ])])))
. toEqual ([[ 'div' , null ], [ '#text(hello)' , 0 ]]);
});
});
describe ( 'project elements' , () => {
it ( 'should project elements with wildcard selector' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeContentProjection (
parse ( '<div><span></span></div>' , [ createComp ( 'div' , [ '*' ])])))
. toEqual ([[ 'div' , null ], [ 'span' , 0 ]]);
2015-09-11 13:37:05 -07:00
});
it ( 'should project elements with css selector' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeContentProjection (
parse ( '<div><a x></a><b></b></div>' , [ createComp ( 'div' , [ 'a[x]' ])])))
. toEqual ([[ 'div' , null ], [ 'a' , 0 ], [ 'b' , null ]]);
2015-09-11 13:37:05 -07:00
});
});
describe ( 'embedded templates' , () => {
it ( 'should project embedded templates with wildcard selector' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeContentProjection (
parse ( '<div><template></template></div>' , [ createComp ( 'div' , [ '*' ])])))
. toEqual ([[ 'div' , null ], [ 'template' , 0 ]]);
2015-09-11 13:37:05 -07:00
});
it ( 'should project embedded templates with css selector' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeContentProjection (
parse ( '<div><template x></template><template></template></div>' ,
[ createComp ( 'div' , [ 'template[x]' ])])))
2015-09-11 13:37:05 -07:00
. toEqual ([[ 'div' , null ], [ 'template' , 0 ], [ 'template' , null ]]);
});
});
describe ( 'ng-content' , () => {
it ( 'should project ng-content with wildcard selector' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeContentProjection (
parse ( '<div><ng-content></ng-content></div>' , [ createComp ( 'div' , [ '*' ])])))
. toEqual ([[ 'div' , null ], [ 'ng-content' , 0 ]]);
2015-09-11 13:37:05 -07:00
});
it ( 'should project ng-content with css selector' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeContentProjection (
parse ( '<div><ng-content x></ng-content><ng-content></ng-content></div>' ,
[ createComp ( 'div' , [ 'ng-content[x]' ])])))
2015-09-11 13:37:05 -07:00
. toEqual ([[ 'div' , null ], [ 'ng-content' , 0 ], [ 'ng-content' , null ]]);
});
});
it ( 'should project into the first matching ng-content' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeContentProjection (
parse ( '<div>hello<b></b><a></a></div>' , [ createComp ( 'div' , [ 'a' , 'b' , '*' ])])))
. toEqual ([[ 'div' , null ], [ '#text(hello)' , 2 ], [ 'b' , 1 ], [ 'a' , 0 ]]);
2015-09-11 13:37:05 -07:00
});
2015-10-29 16:23:13 -07:00
it ( 'should project into wildcard ng-content last' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeContentProjection (
parse ( '<div>hello<a></a></div>' , [ createComp ( 'div' , [ '*' , 'a' ])])))
. toEqual ([[ 'div' , null ], [ '#text(hello)' , 0 ], [ 'a' , 1 ]]);
2015-10-29 16:23:13 -07:00
});
2015-09-11 13:37:05 -07:00
it ( 'should only project direct child nodes' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeContentProjection (
parse ( '<div><span><a></a></span><a></a></div>' , [ createComp ( 'div' , [ 'a' ])])))
. toEqual ([[ 'div' , null ], [ 'span' , null ], [ 'a' , null ], [ 'a' , 0 ]]);
2015-09-11 13:37:05 -07:00
});
it ( 'should project nodes of nested components' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeContentProjection (
parse ( '<a><b>hello</b></a>' , [ createComp ( 'a' , [ '*' ]), createComp ( 'b' , [ '*' ])])))
. toEqual ([[ 'a' , null ], [ 'b' , 0 ], [ '#text(hello)' , 0 ]]);
2015-09-11 13:37:05 -07:00
});
2015-11-23 16:02:19 -08:00
it ( 'should project children of components with ngNonBindable' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeContentProjection ( parse ( '<div ngNonBindable>{{hello}}<span></span></div>' ,
[ createComp ( 'div' , [ '*' ])])))
. toEqual ([[ 'div' , null ], [ '#text({{hello}})' , 0 ], [ 'span' , 0 ]]);
2015-09-18 10:33:23 -07:00
});
2016-03-23 14:15:05 -07:00
it ( 'should match the element when there is an inline template' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeContentProjection (
parse ( '<div><b *ngIf="cond"></b></div>' , [ createComp ( 'div' , [ 'a' , 'b' ]), ngIf ])))
. toEqual ([[ 'div' , null ], [ 'template' , 1 ], [ 'b' , null ]]);
2016-03-23 14:15:05 -07:00
});
describe ( 'ngProjectAs' , () => {
it ( 'should override elements' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeContentProjection (
parse ( '<div><a ngProjectAs="b"></a></div>' , [ createComp ( 'div' , [ 'a' , 'b' ])])))
. toEqual ([[ 'div' , null ], [ 'a' , 1 ]]);
2016-03-23 14:15:05 -07:00
});
it ( 'should override <ng-content>' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeContentProjection (
parse ( '<div><ng-content ngProjectAs="b"></ng-content></div>' ,
[ createComp ( 'div' , [ 'ng-content' , 'b' ])])))
2016-03-23 14:15:05 -07:00
. toEqual ([[ 'div' , null ], [ 'ng-content' , 1 ]]);
});
it ( 'should override <template>' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeContentProjection ( parse ( '<div><template ngProjectAs="b"></template></div>' ,
[ createComp ( 'div' , [ 'template' , 'b' ])])))
2016-03-23 14:15:05 -07:00
. toEqual ([[ 'div' , null ], [ 'template' , 1 ]]);
});
it ( 'should override inline templates' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeContentProjection ( parse ( '<div><a *ngIf="cond" ngProjectAs="b"></a></div>' ,
[ createComp ( 'div' , [ 'a' , 'b' ]), ngIf ])))
2016-03-23 14:15:05 -07:00
. toEqual ([[ 'div' , null ], [ 'template' , 1 ], [ 'a' , null ]]);
});
});
2016-04-29 17:07:28 -07:00
it ( 'should support other directives before the component' , () => {
expect ( humanizeContentProjection (
parse ( '<div>hello</div>' , [ createDir ( 'div' ), createComp ( 'div' , [ '*' ])])))
. toEqual ([[ 'div' , null ], [ '#text(hello)' , 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' ,
2016-01-06 14:13:44 -08:00
type : new CompileTypeMetadata ({ moduleUrl : someModuleUrl , 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 ,
2016-01-06 14:13:44 -08:00
type : new CompileTypeMetadata ({ moduleUrl : someModuleUrl , name : 'DirA' }),
2015-09-18 10:33:23 -07:00
template : new CompileTemplateMetadata ({ ngContentSelectors : []})
2015-09-11 13:37:05 -07:00
});
2015-09-18 10:33:23 -07:00
var dirB = CompileDirectiveMetadata . create ({
2015-09-11 13:37:05 -07:00
selector : 'div' ,
isComponent : true ,
2016-01-06 14:13:44 -08:00
type : new CompileTypeMetadata ({ moduleUrl : someModuleUrl , name : 'DirB' }),
2015-09-18 10:33:23 -07:00
template : new CompileTemplateMetadata ({ ngContentSelectors : []})
2015-09-11 13:37:05 -07:00
});
2015-12-03 16:10:20 -08:00
expect (() => parse ( '<div>' , [ dirB , dirA ])). toThrowError ( `Template parse errors:
More than one component: DirB,DirA ("[ERROR ->]<div>"): TestComp@0:0` );
2015-08-27 16:29:02 -07:00
});
2015-10-13 17:43:15 -07:00
it ( 'should not allow components or element bindings nor dom events on explicit embedded templates' ,
2015-08-27 16:29:02 -07:00
() => {
2015-09-18 10:33:23 -07:00
var dirA = CompileDirectiveMetadata . create ({
2015-09-11 13:37:05 -07:00
selector : '[a]' ,
isComponent : true ,
2016-01-06 14:13:44 -08:00
type : new CompileTypeMetadata ({ moduleUrl : someModuleUrl , name : 'DirA' }),
2015-09-18 10:33:23 -07:00
template : new CompileTemplateMetadata ({ ngContentSelectors : []})
2015-09-11 13:37:05 -07:00
});
2015-08-27 16:29:02 -07:00
expect (() => parse ( '<template [a]="b" (e)="f"></template>' , [ dirA ]))
. toThrowError ( `Template parse errors:
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 ,
2016-01-06 14:13:44 -08:00
type : new CompileTypeMetadata ({ moduleUrl : someModuleUrl , name : 'DirA' }),
2015-09-18 10:33:23 -07:00
template : new CompileTemplateMetadata ({ ngContentSelectors : []})
2015-09-11 13:37:05 -07:00
});
2015-10-07 09:34:21 -07:00
expect (() => parse ( '<div *a="b"></div>' , [ dirA ])). toThrowError ( `Template parse errors:
2015-11-10 15:56:25 -08:00
Components on an embedded template: DirA ("[ERROR ->]<div *a="b"></div>"): TestComp@0:0
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 ([
2016-04-12 09:40:37 -07:00
[ ElementAst , 'link' ],
[ AttrAst , 'rel' , 'stylesheet' ],
[ AttrAst , 'href' , 'http://someurl' ],
[ TextAst , 'a' ]
2015-10-14 09:39:40 -07:00
]);
});
it ( 'should keep <link rel="stylesheet"> elements if they have no uri' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parse ( '<link rel="stylesheet">a' , [])))
. toEqual ([[ ElementAst , 'link' ], [ AttrAst , 'rel' , 'stylesheet' ], [ TextAst , 'a' ]]);
expect ( humanizeTplAst ( parse ( '<link REL="stylesheet">a' , [])))
. toEqual ([[ ElementAst , 'link' ], [ AttrAst , 'REL' , 'stylesheet' ], [ TextAst , 'a' ]]);
2015-10-14 09:39:40 -07:00
});
it ( 'should ignore <link rel="stylesheet"> elements if they have a relative uri' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parse ( '<link rel="stylesheet" href="./other.css">a' , [])))
. toEqual ([[ TextAst , 'a' ]]);
expect ( humanizeTplAst ( parse ( '<link rel="stylesheet" HREF="./other.css">a' , [])))
. toEqual ([[ TextAst , 'a' ]]);
2015-10-14 09:39:40 -07:00
});
it ( 'should ignore <link rel="stylesheet"> elements if they have a package: uri' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parse ( '<link rel="stylesheet" href="package:somePackage">a' , [])))
. toEqual ([[ TextAst , 'a' ]]);
2015-10-14 09:39:40 -07:00
});
2015-09-18 10:33:23 -07:00
});
2015-11-23 16:02:19 -08:00
it ( 'should ignore bindings on children of elements with ngNonBindable' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parse ( '<div ngNonBindable>{{b}}</div>' , [])))
. toEqual ([[ ElementAst , 'div' ], [ AttrAst , 'ngNonBindable' , '' ], [ TextAst , '{{b}}' ]]);
2015-09-18 10:33:23 -07:00
});
2015-09-18 10:33:23 -07:00
2015-11-23 16:02:19 -08:00
it ( 'should keep nested children of elements with ngNonBindable' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parse ( '<div ngNonBindable><span>{{b}}</span></div>' , [])))
. toEqual ([
[ ElementAst , 'div' ],
[ AttrAst , 'ngNonBindable' , '' ],
[ ElementAst , 'span' ],
[ TextAst , '{{b}}' ]
]);
2015-09-18 10:33:23 -07:00
});
2015-11-23 16:02:19 -08:00
it ( 'should ignore <script> elements inside of elements with ngNonBindable' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parse ( '<div ngNonBindable><script></script>a</div>' , [])))
. toEqual ([[ ElementAst , 'div' ], [ AttrAst , 'ngNonBindable' , '' ], [ TextAst , 'a' ]]);
2015-10-07 09:34:21 -07:00
});
2015-09-18 10:33:23 -07:00
2015-11-23 16:02:19 -08:00
it ( 'should ignore <style> elements inside of elements with ngNonBindable' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parse ( '<div ngNonBindable><style></style>a</div>' , [])))
. toEqual ([[ ElementAst , 'div' ], [ AttrAst , 'ngNonBindable' , '' ], [ TextAst , 'a' ]]);
2015-10-07 09:34:21 -07:00
});
2015-09-18 10:33:23 -07:00
2015-11-23 16:02:19 -08:00
it ( 'should ignore <link rel="stylesheet"> elements inside of elements with ngNonBindable' ,
2015-09-18 10:33:23 -07:00
() => {
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAst ( parse ( '<div ngNonBindable><link rel="stylesheet">a</div>' , [])))
. toEqual ([[ ElementAst , 'div' ], [ AttrAst , 'ngNonBindable' , '' ], [ TextAst , 'a' ]]);
2015-09-18 10:33:23 -07:00
});
2015-11-23 16:02:19 -08:00
it ( 'should convert <ng-content> elements into regular elements inside of elements with ngNonBindable' ,
2015-09-18 10:33:23 -07:00
() => {
2015-11-23 16:02:19 -08:00
expect ( humanizeTplAst ( parse ( '<div ngNonBindable><ng-content></ng-content>a</div>' , [])))
2015-09-18 10:33:23 -07:00
. toEqual ([
2016-04-12 09:40:37 -07:00
[ ElementAst , 'div' ],
[ AttrAst , 'ngNonBindable' , '' ],
[ ElementAst , 'ng-content' ],
2015-10-07 09:34:21 -07:00
[ TextAst , 'a' ]
2015-09-18 10:33:23 -07:00
]);
2015-09-18 10:33:23 -07:00
});
});
2015-11-10 15:56:25 -08:00
describe ( 'source spans' , () => {
it ( 'should support ng-content' , () => {
var parsed = parse ( '<ng-content select="a">' , []);
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAstSourceSpans ( parsed ))
. toEqual ([[ NgContentAst , '<ng-content select="a">' ]]);
2015-11-10 15:56:25 -08:00
});
it ( 'should support embedded template' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAstSourceSpans ( parse ( '<template></template>' , [])))
. toEqual ([[ EmbeddedTemplateAst , '<template>' ]]);
2015-11-10 15:56:25 -08:00
});
it ( 'should support element and attributes' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAstSourceSpans ( parse ( '<div key=value>' , [])))
. toEqual (
[[ ElementAst , 'div' , '<div key=value>' ], [ AttrAst , 'key' , 'value' , 'key=value' ]]);
2015-11-10 15:56:25 -08:00
});
2016-04-25 19:52:24 -07:00
it ( 'should support references' , () => {
expect ( humanizeTplAstSourceSpans ( parse ( '<div #a></div>' , [])))
. toEqual ([[ ElementAst , 'div' , '<div #a>' ], [ ReferenceAst , 'a' , null , '#a' ]]);
});
2015-11-10 15:56:25 -08:00
it ( 'should support variables' , () => {
2016-04-25 19:52:24 -07:00
expect ( humanizeTplAstSourceSpans ( parse ( '<template let-a="b"></template>' , [])))
2016-04-12 09:40:37 -07:00
. toEqual ([
2016-04-25 19:52:24 -07:00
[ EmbeddedTemplateAst , '<template let-a="b">' ],
[ VariableAst , 'a' , 'b' , 'let-a="b"' ]
2016-04-12 09:40:37 -07:00
]);
2015-11-10 15:56:25 -08:00
});
2016-04-25 19:52:24 -07:00
it ( 'should support events' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAstSourceSpans ( parse ( '<div (window:event)="v">' , [])))
. toEqual ([
[ ElementAst , 'div' , '<div (window:event)="v">' ],
[ BoundEventAst , 'event' , 'window' , 'v' , '(window:event)="v"' ]
]);
2015-11-10 15:56:25 -08:00
});
it ( 'should support element property' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAstSourceSpans ( parse ( '<div [someProp]="v">' , [])))
. toEqual ([
[ ElementAst , 'div' , '<div [someProp]="v">' ],
[
BoundElementPropertyAst ,
PropertyBindingType . Property ,
'someProp' ,
'v' ,
null ,
'[someProp]="v"'
]
]);
2015-11-10 15:56:25 -08:00
});
it ( 'should support bound text' , () => {
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAstSourceSpans ( parse ( '{{a}}' , [])))
. toEqual ([[ BoundTextAst , '{{ a }}' , '{{a}}' ]]);
2015-11-10 15:56:25 -08:00
});
it ( 'should support text nodes' , () => {
expect ( humanizeTplAstSourceSpans ( parse ( 'a' , []))). toEqual ([[ TextAst , 'a' , 'a' ]]);
});
it ( 'should support directive' , () => {
2016-01-06 14:13:44 -08:00
var dirA = CompileDirectiveMetadata . create ({
selector : '[a]' ,
type : new CompileTypeMetadata ({ moduleUrl : someModuleUrl , name : 'DirA' })
});
2015-11-10 15:56:25 -08:00
var comp = CompileDirectiveMetadata . create ({
selector : 'div' ,
isComponent : true ,
2016-01-06 14:13:44 -08:00
type : new CompileTypeMetadata ({ moduleUrl : someModuleUrl , name : 'ZComp' }),
2015-11-10 15:56:25 -08:00
template : new CompileTemplateMetadata ({ ngContentSelectors : []})
});
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAstSourceSpans ( parse ( '<div a>' , [ dirA , comp ])))
. toEqual ([
[ ElementAst , 'div' , '<div a>' ],
[ AttrAst , 'a' , '' , 'a' ],
[ DirectiveAst , dirA , '<div a>' ],
2016-04-25 19:52:24 -07:00
[ DirectiveAst , comp , '<div a>' ]
2016-04-12 09:40:37 -07:00
]);
2015-11-10 15:56:25 -08:00
});
2015-12-09 09:32:15 -08:00
it ( 'should support directive in namespace' , () => {
2016-01-06 14:13:44 -08:00
var tagSel = CompileDirectiveMetadata . create ({
selector : 'circle' ,
type : new CompileTypeMetadata ({ moduleUrl : someModuleUrl , name : 'elDir' })
});
var attrSel = CompileDirectiveMetadata . create ({
selector : '[href]' ,
type : new CompileTypeMetadata ({ moduleUrl : someModuleUrl , name : 'attrDir' })
});
2015-12-09 09:32:15 -08:00
expect ( humanizeTplAstSourceSpans (
parse ( '<svg><circle /><use xlink:href="Port" /></svg>' , [ tagSel , attrSel ])))
. toEqual ([
2016-04-29 14:54:46 -07:00
[ ElementAst , ':svg:svg' , '<svg>' ],
[ ElementAst , ':svg:circle' , '<circle />' ],
2015-12-09 09:32:15 -08:00
[ DirectiveAst , tagSel , '<circle />' ],
2016-04-29 14:54:46 -07:00
[ ElementAst , ':svg:use' , '<use xlink:href="Port" />' ],
[ AttrAst , ':xlink:href' , 'Port' , 'xlink:href="Port"' ],
2015-12-09 09:32:15 -08:00
[ DirectiveAst , attrSel , '<use xlink:href="Port" />' ],
]);
});
2015-11-10 15:56:25 -08:00
it ( 'should support directive property' , () => {
2016-01-06 14:13:44 -08:00
var dirA = CompileDirectiveMetadata . create ({
selector : 'div' ,
type : new CompileTypeMetadata ({ moduleUrl : someModuleUrl , name : 'DirA' }),
inputs : [ 'aProp' ]
});
2016-04-12 09:40:37 -07:00
expect ( humanizeTplAstSourceSpans ( parse ( '<div [aProp]="foo"></div>' , [ dirA ])))
. toEqual ([
[ ElementAst , 'div' , '<div [aProp]="foo">' ],
[ DirectiveAst , dirA , '<div [aProp]="foo">' ],
[ BoundDirectivePropertyAst , 'aProp' , 'foo' , '[aProp]="foo"' ]
]);
2015-11-10 15:56:25 -08:00
});
});
2015-12-02 10:35:51 -08:00
describe ( 'pipes' , () => {
it ( 'should allow pipes that have been defined as dependencies' , () => {
2016-01-06 14:13:44 -08:00
var testPipe = new CompilePipeMetadata ({
name : 'test' ,
type : new CompileTypeMetadata ({ moduleUrl : someModuleUrl , name : 'DirA' })
});
2015-12-02 10:35:51 -08:00
expect (() => parse ( '{{a | test}}' , [], [ testPipe ])). not . toThrow ();
});
it ( 'should report pipes as error that have not been defined as dependencies' , () => {
expect (() => parse ( '{{a | test}}' , [])). toThrowError ( `Template parse errors:
The pipe 'test' could not be found ("[ERROR ->]{{a | test}}"): TestComp@0:0` );
});
});
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 );
2016-04-25 19:52:24 -07:00
templateVisitAll ( this , ast . references );
templateVisitAll ( this , ast . variables );
2015-08-27 16:29:02 -07:00
templateVisitAll ( this , ast . directives );
2015-08-25 15:36:02 -07:00
templateVisitAll ( this , ast . children );
return null ;
}
2015-09-01 16:24:23 -07:00
visitElement ( ast : ElementAst , context : any ) : any {
2015-11-10 15:56:25 -08:00
var res = [ ElementAst , ast . name ];
this . result . push ( this . _appendContext ( ast , res ));
2015-08-25 15:36:02 -07:00
templateVisitAll ( this , ast . attrs );
2015-09-30 20:59:23 -07:00
templateVisitAll ( this , ast . inputs );
templateVisitAll ( this , ast . outputs );
2016-04-25 19:52:24 -07:00
templateVisitAll ( this , ast . references );
2015-08-27 16:29:02 -07:00
templateVisitAll ( this , ast . directives );
2015-08-25 15:36:02 -07:00
templateVisitAll ( this , ast . children );
return null ;
}
2016-04-25 19:52:24 -07:00
visitReference ( ast : ReferenceAst , context : any ) : any {
var res = [ ReferenceAst , ast . name , ast . value ];
this . result . push ( this . _appendContext ( ast , res ));
return null ;
}
2015-09-01 16:24:23 -07:00
visitVariable ( ast : VariableAst , context : any ) : any {
2015-11-10 15:56:25 -08:00
var res = [ VariableAst , ast . name , ast . value ];
this . result . push ( this . _appendContext ( ast , res ));
2015-08-25 15:36:02 -07:00
return null ;
}
2015-09-01 16:24:23 -07:00
visitEvent ( ast : BoundEventAst , context : any ) : any {
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 = [
2016-04-12 09:40:37 -07:00
BoundElementPropertyAst ,
ast . type ,
ast . name ,
expressionUnparser . unparse ( ast . value ),
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 );
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 ;
}
2016-04-25 19:52:24 -07:00
visitReference ( ast : ReferenceAst , context : any ) : any { return null ; }
2015-09-11 13:37:05 -07:00
visitVariable ( ast : VariableAst , context : any ) : any { return null ; }
visitEvent ( ast : BoundEventAst , context : any ) : any { return null ; }
visitElementProperty ( ast : BoundElementPropertyAst , context : any ) : any { return null ; }
visitAttr ( ast : AttrAst , context : any ) : any { return null ; }
visitBoundText ( ast : BoundTextAst , context : any ) : any {
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 ;
2016-04-18 13:24:42 -07:00
return new ElementAst ( 'foo' , [], [], [], [], [], [], false , [], ast . ngContentIndex ,
ast . sourceSpan );
2015-11-19 10:51:16 -08:00
}
2016-04-25 19:52:24 -07:00
visitReference ( ast : ReferenceAst , context : any ) : any { throw 'not implemented' ; }
2015-11-19 10:51:16 -08:00
visitVariable ( ast : VariableAst , context : any ) : any { throw 'not implemented' ; }
visitEvent ( ast : BoundEventAst , context : any ) : any { throw 'not implemented' ; }
visitElementProperty ( ast : BoundElementPropertyAst , context : any ) : any { throw 'not implemented' ; }
visitAttr ( ast : AttrAst , context : any ) : any { throw 'not implemented' ; }
visitBoundText ( ast : BoundTextAst , context : any ) : any { throw 'not implemented' ; }
visitText ( ast : TextAst , context : any ) : any { throw 'not implemented' ; }
visitDirective ( ast : DirectiveAst , context : any ) : any { throw 'not implemented' ; }
visitDirectiveProperty ( ast : BoundDirectivePropertyAst , context : any ) : any {
throw 'not implemented' ;
}
}
class BarAstTransformer extends FooAstTransformer {
visitElement ( ast : ElementAst , context : any ) : any {
if ( ast . name != 'foo' ) return ast ;
2016-04-18 13:24:42 -07:00
return new ElementAst ( 'bar' , [], [], [], [], [], [], false , [], ast . ngContentIndex ,
ast . sourceSpan );
2015-11-19 10:51:16 -08:00
}
}
2016-04-25 19:52:24 -07:00
class ArrayConsole implements Console {
logs : string [] = [];
warnings : string [] = [];
log ( msg : string ) { this . logs . push ( msg ); }
warn ( msg : string ) { this . warnings . push ( msg ); }
2016-04-28 17:50:03 -07:00
}