2016-06-23 12:47:54 -04:00
/ * *
* @license
* Copyright Google Inc . All Rights Reserved .
*
* Use of this source code is governed by an MIT - style license that can be
* found in the LICENSE file at https : //angular.io/license
* /
2016-06-08 19:38:52 -04:00
import { CompileDiDependencyMetadata , CompileDirectiveMetadata , CompilePipeMetadata , CompileProviderMetadata , CompileQueryMetadata , CompileTemplateMetadata , CompileTokenMetadata , CompileTypeMetadata } from '@angular/compiler/src/compile_metadata' ;
2016-05-26 17:35:27 -04:00
import { DomElementSchemaRegistry } from '@angular/compiler/src/schema/dom_element_schema_registry' ;
2016-06-08 19:38:52 -04:00
import { ElementSchemaRegistry } from '@angular/compiler/src/schema/element_schema_registry' ;
import { AttrAst , BoundDirectivePropertyAst , BoundElementPropertyAst , BoundEventAst , BoundTextAst , DirectiveAst , ElementAst , EmbeddedTemplateAst , NgContentAst , PropertyBindingType , ProviderAstType , ReferenceAst , TemplateAst , TemplateAstVisitor , TextAst , VariableAst , templateVisitAll } from '@angular/compiler/src/template_ast' ;
import { TEMPLATE_TRANSFORMS , TemplateParser , splitClasses } from '@angular/compiler/src/template_parser' ;
2016-04-28 20:50:03 -04:00
import { MockSchemaRegistry } from '@angular/compiler/testing' ;
2016-06-08 19:38:52 -04:00
import { Console } from '@angular/core/src/console' ;
import { afterEach , beforeEach , beforeEachProviders , ddescribe , describe , expect , iit , inject , it , xit } from '@angular/core/testing/testing_internal' ;
import { SecurityContext } from '../core_private' ;
import { Identifiers , identifierToken } from '../src/identifiers' ;
2016-06-20 12:52:41 -04:00
import { DEFAULT_INTERPOLATION_CONFIG , InterpolationConfig } from '../src/interpolation_config' ;
2015-08-27 19:29:02 -04:00
2016-01-06 17:13:44 -05:00
import { Unparser } from './expression_parser/unparser' ;
2016-04-28 20:50:03 -04:00
import { TEST_PROVIDERS } from './test_bindings' ;
2015-08-25 18:36:02 -04:00
var expressionUnparser = new Unparser ( ) ;
2016-01-06 17:13:44 -05:00
var someModuleUrl = 'package:someModule' ;
2016-06-08 19:38:52 -04:00
var MOCK_SCHEMA_REGISTRY = [ {
provide : ElementSchemaRegistry ,
useValue : new MockSchemaRegistry ( { 'invalidProp' : false } , { 'mappedAttr' : 'mappedProp' } )
} ] ;
2016-02-10 19:54:32 -05:00
2016-05-26 17:35:27 -04:00
let zeConsole = console ;
2015-08-25 18:36:02 -04:00
export function main() {
2016-06-22 11:43:27 -04:00
var ngIf : CompileDirectiveMetadata ;
2016-05-26 17:35:27 -04:00
var parse :
( template : string , directives : CompileDirectiveMetadata [ ] , pipes? : CompilePipeMetadata [ ] ) = >
TemplateAst [ ] ;
2016-04-25 22:52:24 -04:00
var console : ArrayConsole ;
2016-02-10 19:54:32 -05:00
function commonBeforeEach() {
2016-04-25 22:52:24 -04:00
beforeEachProviders ( ( ) = > {
console = new ArrayConsole ( ) ;
2016-06-02 20:30:40 -04:00
return [ { provide : Console , useValue : console } ] ;
2016-04-25 22:52:24 -04:00
} ) ;
2016-06-09 14:04:15 -04:00
beforeEach ( inject ( [ TemplateParser ] , ( parser : TemplateParser ) = > {
2016-01-06 17:13:44 -05:00
var component = CompileDirectiveMetadata . create ( {
selector : 'root' ,
type : new CompileTypeMetadata ( { module Url : someModuleUrl , name : 'Root' } ) ,
isComponent : true
} ) ;
ngIf = CompileDirectiveMetadata . create ( {
selector : '[ngIf]' ,
type : new CompileTypeMetadata ( { module Url : someModuleUrl , name : 'NgIf' } ) ,
inputs : [ 'ngIf' ]
} ) ;
2016-02-10 19:54:32 -05:00
2016-06-08 19:38:52 -04:00
parse =
( template : string , directives : CompileDirectiveMetadata [ ] ,
pipes : CompilePipeMetadata [ ] = null ) : TemplateAst [ ] = > {
if ( pipes === null ) {
pipes = [ ] ;
}
return parser . parse ( component , template , directives , pipes , 'TestComp' ) ;
} ;
2015-09-14 18:59:09 -04:00
} ) ) ;
2016-02-10 19:54:32 -05:00
}
2015-08-25 18:36:02 -04:00
2016-02-10 19:54:32 -05:00
describe ( 'TemplateParser template transform' , ( ) = > {
beforeEachProviders ( ( ) = > [ TEST_PROVIDERS , MOCK_SCHEMA_REGISTRY ] ) ;
2015-08-25 18:36:02 -04:00
2016-02-10 19:54:32 -05:00
beforeEachProviders (
2016-06-02 20:30:40 -04:00
( ) = > [ { provide : TEMPLATE_TRANSFORMS , useValue : new FooAstTransformer ( ) , multi : true } ] ) ;
2015-11-19 13:51:16 -05:00
2016-02-10 19:54:32 -05:00
describe ( 'single' , ( ) = > {
commonBeforeEach ( ) ;
2016-06-08 19:38:52 -04:00
it ( 'should transform TemplateAST' , ( ) = > {
expect ( humanizeTplAst ( parse ( '<div>' , [ ] ) ) ) . toEqual ( [ [ ElementAst , 'foo' ] ] ) ;
} ) ;
2016-02-10 19:54:32 -05:00
} ) ;
2015-11-19 13:51:16 -05:00
2016-02-10 19:54:32 -05:00
describe ( 'multiple' , ( ) = > {
beforeEachProviders (
2016-06-02 20:30:40 -04:00
( ) = > [ { provide : TEMPLATE_TRANSFORMS , useValue : new BarAstTransformer ( ) , multi : true } ] ) ;
2015-11-19 13:51:16 -05:00
2016-02-10 19:54:32 -05:00
commonBeforeEach ( ) ;
2016-06-08 19:38:52 -04:00
it ( 'should compose transformers' , ( ) = > {
expect ( humanizeTplAst ( parse ( '<div>' , [ ] ) ) ) . toEqual ( [ [ ElementAst , 'bar' ] ] ) ;
} ) ;
2015-11-19 13:51:16 -05:00
} ) ;
2016-02-10 19:54:32 -05:00
} ) ;
2016-05-26 17:35:27 -04:00
describe ( 'TemplateParser Security' , ( ) = > {
// Semi-integration test to make sure TemplateParser properly sets the security context.
// Uses the actual DomElementSchemaRegistry.
beforeEachProviders (
( ) = >
2016-06-02 20:30:40 -04:00
[ TEST_PROVIDERS , { provide : ElementSchemaRegistry , useClass : DomElementSchemaRegistry } ] ) ;
2016-05-26 17:35:27 -04:00
commonBeforeEach ( ) ;
describe ( 'security context' , ( ) = > {
function secContext ( tpl : string ) : SecurityContext {
let ast = parse ( tpl , [ ] ) ;
let propBinding = ( < ElementAst > ast [ 0 ] ) . inputs [ 0 ] ;
return propBinding . securityContext ;
}
it ( 'should set for properties' , ( ) = > {
expect ( secContext ( '<div [title]="v">' ) ) . toBe ( SecurityContext . NONE ) ;
expect ( secContext ( '<div [innerHTML]="v">' ) ) . toBe ( SecurityContext . HTML ) ;
} ) ;
2016-06-08 19:38:52 -04:00
it ( 'should set for property value bindings' ,
( ) = > { expect ( secContext ( '<div innerHTML="{{v}}">' ) ) . toBe ( SecurityContext . HTML ) ; } ) ;
2016-05-26 17:35:27 -04:00
it ( 'should set for attributes' , ( ) = > {
expect ( secContext ( '<a [attr.href]="v">' ) ) . toBe ( SecurityContext . URL ) ;
// NB: attributes below need to change case.
expect ( secContext ( '<a [attr.innerHtml]="v">' ) ) . toBe ( SecurityContext . HTML ) ;
expect ( secContext ( '<a [attr.formaction]="v">' ) ) . toBe ( SecurityContext . URL ) ;
} ) ;
it ( 'should set for style' , ( ) = > {
expect ( secContext ( '<a [style.backgroundColor]="v">' ) ) . toBe ( SecurityContext . STYLE ) ;
} ) ;
} ) ;
} ) ;
2016-02-10 19:54:32 -05:00
describe ( 'TemplateParser' , ( ) = > {
beforeEachProviders ( ( ) = > [ TEST_PROVIDERS , MOCK_SCHEMA_REGISTRY ] ) ;
commonBeforeEach ( ) ;
2015-11-19 13:51:16 -05:00
2015-08-25 18:36:02 -04:00
describe ( 'parse' , ( ) = > {
describe ( 'nodes without bindings' , ( ) = > {
2016-06-08 19:38:52 -04:00
it ( 'should parse text nodes' , ( ) = > {
expect ( humanizeTplAst ( parse ( 'a' , [ ] ) ) ) . toEqual ( [ [ TextAst , 'a' ] ] ) ;
} ) ;
2015-08-25 18:36:02 -04:00
it ( 'should parse elements with attributes' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<div a=b>' , [
] ) ) ) . toEqual ( [ [ ElementAst , 'div' ] , [ AttrAst , 'a' , 'b' ] ] ) ;
2015-08-25 18:36:02 -04:00
} ) ;
} ) ;
it ( 'should parse ngContent' , ( ) = > {
var parsed = parse ( '<ng-content select="a">' , [ ] ) ;
2015-11-10 18:56:25 -05:00
expect ( humanizeTplAst ( parsed ) ) . toEqual ( [ [ NgContentAst ] ] ) ;
2015-08-25 18:36:02 -04:00
} ) ;
2015-12-09 13:47:10 -05:00
it ( 'should parse ngContent regardless the namespace' , ( ) = > {
var parsed = parse ( '<svg><ng-content></ng-content></svg>' , [ ] ) ;
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parsed ) ) . toEqual ( [
[ ElementAst , ':svg:svg' ] ,
[ NgContentAst ] ,
] ) ;
2015-12-09 13:47:10 -05:00
} ) ;
2015-08-25 18:36:02 -04:00
it ( 'should parse bound text nodes' , ( ) = > {
2015-11-10 18:56:25 -05:00
expect ( humanizeTplAst ( parse ( '{{a}}' , [ ] ) ) ) . toEqual ( [ [ BoundTextAst , '{{ a }}' ] ] ) ;
2015-08-25 18:36:02 -04:00
} ) ;
2016-06-20 12:52:41 -04:00
it ( 'should parse with custom interpolation config' ,
inject ( [ TemplateParser ] , ( parser : TemplateParser ) = > {
const component = CompileDirectiveMetadata . create ( {
selector : 'test' ,
type : new CompileTypeMetadata ( { module Url : someModuleUrl , name : 'Test' } ) ,
isComponent : true ,
template : new CompileTemplateMetadata ( { interpolation : [ '{%' , '%}' ] } )
} ) ;
expect ( humanizeTplAst ( parser . parse ( component , '{%a%}' , [ ] , [ ] , 'TestComp' ) , {
start : '{%' ,
end : '%}'
} ) ) . toEqual ( [ [ BoundTextAst , '{% a %}' ] ] ) ;
} ) ) ;
2015-08-27 19:29:02 -04:00
describe ( 'bound properties' , ( ) = > {
2015-08-25 18:36:02 -04:00
2015-11-10 18:56:25 -05:00
it ( 'should parse mixed case bound properties' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<div [someProp]="v">' , [ ] ) ) ) . toEqual ( [
[ ElementAst , 'div' ] ,
[ BoundElementPropertyAst , PropertyBindingType . Property , 'someProp' , 'v' , null ]
] ) ;
2015-11-10 18:56:25 -05:00
} ) ;
2015-11-23 19:02:19 -05:00
it ( 'should parse dash case bound properties' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<div [some-prop]="v">' , [ ] ) ) ) . toEqual ( [
[ ElementAst , 'div' ] ,
[ BoundElementPropertyAst , PropertyBindingType . Property , 'some-prop' , 'v' , null ]
] ) ;
2015-08-25 18:36:02 -04:00
} ) ;
2015-08-27 19:29:02 -04:00
it ( 'should normalize property names via the element schema' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<div [mappedAttr]="v">' , [ ] ) ) ) . toEqual ( [
[ ElementAst , 'div' ] ,
[ BoundElementPropertyAst , PropertyBindingType . Property , 'mappedProp' , 'v' , null ]
] ) ;
2015-08-27 19:29:02 -04:00
} ) ;
2015-11-10 18:56:25 -05:00
it ( 'should parse mixed case bound attributes' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<div [attr.someAttr]="v">' , [ ] ) ) ) . toEqual ( [
[ ElementAst , 'div' ] ,
[ BoundElementPropertyAst , PropertyBindingType . Attribute , 'someAttr' , 'v' , null ]
] ) ;
2015-11-10 18:56:25 -05:00
} ) ;
2015-08-27 19:29:02 -04:00
it ( 'should parse and dash case bound classes' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<div [class.some-class]="v">' , [ ] ) ) ) . toEqual ( [
[ ElementAst , 'div' ] ,
[ BoundElementPropertyAst , PropertyBindingType . Class , 'some-class' , 'v' , null ]
] ) ;
2015-11-10 18:56:25 -05:00
} ) ;
it ( 'should parse mixed case bound classes' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<div [class.someClass]="v">' , [ ] ) ) ) . toEqual ( [
[ ElementAst , 'div' ] ,
[ BoundElementPropertyAst , PropertyBindingType . Class , 'someClass' , 'v' , null ]
] ) ;
2015-08-27 19:29:02 -04:00
} ) ;
2015-11-23 19:02:19 -05:00
it ( 'should parse mixed case bound styles' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<div [style.someStyle]="v">' , [ ] ) ) ) . toEqual ( [
[ ElementAst , 'div' ] ,
[ BoundElementPropertyAst , PropertyBindingType . Style , 'someStyle' , 'v' , null ]
] ) ;
2015-11-10 18:56:25 -05:00
} ) ;
2015-11-23 19:02:19 -05: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 19:29:02 -04:00
} ) ;
it ( 'should parse bound properties via [...] and not report them as attributes' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<div [prop]="v">' , [ ] ) ) ) . toEqual ( [
[ ElementAst , 'div' ] ,
[ BoundElementPropertyAst , PropertyBindingType . Property , 'prop' , 'v' , null ]
] ) ;
2015-08-25 18:36:02 -04:00
} ) ;
it ( 'should parse bound properties via bind- and not report them as attributes' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<div bind-prop="v">' , [ ] ) ) ) . toEqual ( [
[ ElementAst , 'div' ] ,
[ BoundElementPropertyAst , PropertyBindingType . Property , 'prop' , 'v' , null ]
] ) ;
2015-08-25 18:36:02 -04:00
} ) ;
it ( 'should parse bound properties via {{...}} and not report them as attributes' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<div prop="{{v}}">' , [ ] ) ) ) . toEqual ( [
[ ElementAst , 'div' ] ,
[ BoundElementPropertyAst , PropertyBindingType . Property , 'prop' , '{{ v }}' , null ]
] ) ;
2015-08-27 19:29:02 -04:00
} ) ;
2016-05-25 15:46:22 -04:00
it ( 'should parse bound properties via animate- and not report them as attributes' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<div animate-something="value2">' , [ ] ) ) ) . toEqual ( [
[ ElementAst , 'div' ] ,
[ BoundElementPropertyAst , PropertyBindingType . Animation , 'something' , 'value2' , null ]
] ) ;
2016-05-25 15:46:22 -04:00
} ) ;
2015-08-27 19:29:02 -04:00
} ) ;
describe ( 'events' , ( ) = > {
it ( 'should parse bound events with a target' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<div (window:event)="v">' , [
] ) ) ) . toEqual ( [ [ ElementAst , 'div' ] , [ BoundEventAst , 'event' , 'window' , 'v' ] ] ) ;
2015-08-25 18:36:02 -04:00
} ) ;
it ( 'should parse bound events via (...) and not report them as attributes' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<div (event)="v">' , [
] ) ) ) . toEqual ( [ [ ElementAst , 'div' ] , [ BoundEventAst , 'event' , null , 'v' ] ] ) ;
2015-08-25 18:36:02 -04:00
} ) ;
2015-11-23 19:02:19 -05:00
it ( 'should parse event names case sensitive' , ( ) = > {
2016-06-08 19:38:52 -04: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 18:36:02 -04:00
} ) ;
it ( 'should parse bound events via on- and not report them as attributes' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<div on-event="v">' , [
] ) ) ) . toEqual ( [ [ ElementAst , 'div' ] , [ BoundEventAst , 'event' , null , 'v' ] ] ) ;
2015-08-25 18:36:02 -04:00
} ) ;
2015-10-13 20:43:15 -04: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 17:13:44 -05:00
type : new CompileTypeMetadata ( { module Url : someModuleUrl , name : 'DirA' } )
2015-10-13 20:43:15 -04:00
} ) ;
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<template (e)="f"></template>' , [ dirA ] ) ) ) . toEqual ( [
[ EmbeddedTemplateAst ] ,
[ BoundEventAst , 'e' , null , 'f' ] ,
[ DirectiveAst , dirA ] ,
] ) ;
2015-10-13 20:43:15 -04:00
} ) ;
2015-08-27 19:29:02 -04:00
} ) ;
describe ( 'bindon' , ( ) = > {
2015-08-25 18:36:02 -04:00
it ( 'should parse bound events and properties via [(...)] and not report them as attributes' ,
( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<div [(prop)]="v">' , [ ] ) ) ) . toEqual ( [
[ ElementAst , 'div' ] ,
[ BoundElementPropertyAst , PropertyBindingType . Property , 'prop' , 'v' , null ] ,
[ BoundEventAst , 'propChange' , null , 'v = $event' ]
] ) ;
2015-08-25 18:36:02 -04:00
} ) ;
it ( 'should parse bound events and properties via bindon- and not report them as attributes' ,
( ) = > {
2016-06-08 19:38:52 -04: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 18:36:02 -04:00
} ) ;
2015-08-27 19:29:02 -04:00
} ) ;
2015-08-25 18:36:02 -04:00
describe ( 'directives' , ( ) = > {
2016-04-25 22:52:24 -04:00
it ( 'should order directives by the directives array in the View and match them only once' ,
2015-09-18 13:33:23 -04:00
( ) = > {
2016-01-06 17:13:44 -05:00
var dirA = CompileDirectiveMetadata . create ( {
selector : '[a]' ,
type : new CompileTypeMetadata ( { module Url : someModuleUrl , name : 'DirA' } )
} ) ;
var dirB = CompileDirectiveMetadata . create ( {
selector : '[b]' ,
type : new CompileTypeMetadata ( { module Url : someModuleUrl , name : 'DirB' } )
} ) ;
var dirC = CompileDirectiveMetadata . create ( {
selector : '[c]' ,
type : new CompileTypeMetadata ( { module Url : someModuleUrl , name : 'DirC' } )
} ) ;
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<div a c b a b>' , [ dirA , dirB , dirC ] ) ) ) . toEqual ( [
[ ElementAst , 'div' ] , [ AttrAst , 'a' , '' ] , [ AttrAst , 'c' , '' ] , [ AttrAst , 'b' , '' ] ,
[ AttrAst , 'a' , '' ] , [ AttrAst , 'b' , '' ] , [ DirectiveAst , dirA ] , [ DirectiveAst , dirB ] ,
[ DirectiveAst , dirC ]
] ) ;
2015-09-18 13:33:23 -04:00
} ) ;
2015-08-25 18:36:02 -04:00
it ( 'should locate directives in property bindings' , ( ) = > {
2016-01-06 17:13:44 -05:00
var dirA = CompileDirectiveMetadata . create ( {
selector : '[a=b]' ,
type : new CompileTypeMetadata ( { module Url : someModuleUrl , name : 'DirA' } )
} ) ;
var dirB = CompileDirectiveMetadata . create ( {
selector : '[b]' ,
type : new CompileTypeMetadata ( { module Url : someModuleUrl , name : 'DirB' } )
} ) ;
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<div [a]="b">' , [ dirA , dirB ] ) ) ) . toEqual ( [
[ ElementAst , 'div' ] ,
[ BoundElementPropertyAst , PropertyBindingType . Property , 'a' , 'b' , null ] ,
[ DirectiveAst , dirA ]
] ) ;
2015-08-25 18:36:02 -04:00
} ) ;
2016-02-03 14:35:42 -05:00
it ( 'should locate directives in event bindings' , ( ) = > {
2016-01-06 17:13:44 -05:00
var dirA = CompileDirectiveMetadata . create ( {
selector : '[a]' ,
type : new CompileTypeMetadata ( { module Url : someModuleUrl , name : 'DirB' } )
} ) ;
2016-02-03 14:35:42 -05:00
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<div (a)="b">' , [ dirA ] ) ) ) . toEqual ( [
[ ElementAst , 'div' ] , [ BoundEventAst , 'a' , null , 'b' ] , [ DirectiveAst , dirA ]
] ) ;
2016-02-03 14:35:42 -05:00
} ) ;
2015-08-27 19:29:02 -04:00
it ( 'should parse directive host properties' , ( ) = > {
2015-09-18 13:33:23 -04:00
var dirA = CompileDirectiveMetadata . create ( {
2015-08-27 19:29:02 -04:00
selector : 'div' ,
2016-01-06 17:13:44 -05:00
type : new CompileTypeMetadata ( { module Url : someModuleUrl , name : 'DirA' } ) ,
2015-09-18 13:33:23 -04:00
host : { '[a]' : 'expr' }
2015-08-27 19:29:02 -04:00
} ) ;
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<div></div>' , [ dirA ] ) ) ) . toEqual ( [
[ ElementAst , 'div' ] , [ DirectiveAst , dirA ] ,
[ BoundElementPropertyAst , PropertyBindingType . Property , 'a' , 'expr' , null ]
] ) ;
2015-08-27 19:29:02 -04:00
} ) ;
it ( 'should parse directive host listeners' , ( ) = > {
2015-09-18 13:33:23 -04:00
var dirA = CompileDirectiveMetadata . create ( {
2015-08-27 19:29:02 -04:00
selector : 'div' ,
2016-01-06 17:13:44 -05:00
type : new CompileTypeMetadata ( { module Url : someModuleUrl , name : 'DirA' } ) ,
2015-09-18 13:33:23 -04:00
host : { '(a)' : 'expr' }
2015-08-27 19:29:02 -04:00
} ) ;
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<div></div>' , [ dirA ] ) ) ) . toEqual ( [
[ ElementAst , 'div' ] , [ DirectiveAst , dirA ] , [ BoundEventAst , 'a' , null , 'expr' ]
] ) ;
2015-08-27 19:29:02 -04:00
} ) ;
it ( 'should parse directive properties' , ( ) = > {
2016-01-06 17:13:44 -05:00
var dirA = CompileDirectiveMetadata . create ( {
selector : 'div' ,
type : new CompileTypeMetadata ( { module Url : someModuleUrl , name : 'DirA' } ) ,
inputs : [ 'aProp' ]
} ) ;
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<div [aProp]="expr"></div>' , [ dirA ] ) ) ) . toEqual ( [
[ ElementAst , 'div' ] , [ DirectiveAst , dirA ] ,
[ BoundDirectivePropertyAst , 'aProp' , 'expr' ]
] ) ;
2015-08-27 19:29:02 -04:00
} ) ;
it ( 'should parse renamed directive properties' , ( ) = > {
2016-01-06 17:13:44 -05:00
var dirA = CompileDirectiveMetadata . create ( {
selector : 'div' ,
type : new CompileTypeMetadata ( { module Url : someModuleUrl , name : 'DirA' } ) ,
inputs : [ 'b:a' ]
} ) ;
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<div [a]="expr"></div>' , [ dirA ] ) ) ) . toEqual ( [
[ ElementAst , 'div' ] , [ DirectiveAst , dirA ] , [ BoundDirectivePropertyAst , 'b' , 'expr' ]
] ) ;
2015-08-27 19:29:02 -04:00
} ) ;
it ( 'should parse literal directive properties' , ( ) = > {
2016-01-06 17:13:44 -05:00
var dirA = CompileDirectiveMetadata . create ( {
selector : 'div' ,
type : new CompileTypeMetadata ( { module Url : someModuleUrl , name : 'DirA' } ) ,
inputs : [ 'a' ]
} ) ;
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<div a="literal"></div>' , [ dirA ] ) ) ) . toEqual ( [
[ ElementAst , 'div' ] , [ AttrAst , 'a' , 'literal' ] , [ DirectiveAst , dirA ] ,
[ BoundDirectivePropertyAst , 'a' , '"literal"' ]
] ) ;
2015-08-27 19:29:02 -04:00
} ) ;
2015-09-18 13:33:23 -04:00
it ( 'should favor explicit bound properties over literal properties' , ( ) = > {
2016-01-06 17:13:44 -05:00
var dirA = CompileDirectiveMetadata . create ( {
selector : 'div' ,
type : new CompileTypeMetadata ( { module Url : someModuleUrl , name : 'DirA' } ) ,
inputs : [ 'a' ]
} ) ;
2015-11-10 18:56:25 -05:00
expect ( humanizeTplAst ( parse ( '<div a="literal" [a]="\'literal2\'"></div>' , [ dirA ] ) ) )
2015-09-18 13:33:23 -04:00
. toEqual ( [
2016-06-08 19:38:52 -04:00
[ ElementAst , 'div' ] , [ AttrAst , 'a' , 'literal' ] , [ DirectiveAst , dirA ] ,
2015-10-07 12:34:21 -04:00
[ BoundDirectivePropertyAst , 'a' , '"literal2"' ]
2015-09-18 13:33:23 -04:00
] ) ;
} ) ;
2015-08-27 19:29:02 -04:00
it ( 'should support optional directive properties' , ( ) = > {
2016-01-06 17:13:44 -05:00
var dirA = CompileDirectiveMetadata . create ( {
selector : 'div' ,
2016-06-07 12:24:57 -04:00
type : new CompileTypeMetadata ( { module Url : someModuleUrl , name : 'DirA' } ) ,
2016-01-06 17:13:44 -05:00
inputs : [ 'a' ]
} ) ;
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<div></div>' , [ dirA ] ) ) ) . toEqual ( [
[ ElementAst , 'div' ] , [ DirectiveAst , dirA ]
] ) ;
2015-08-27 19:29:02 -04:00
} ) ;
2015-08-25 18:36:02 -04:00
} ) ;
2016-01-06 17:13:44 -05:00
describe ( 'providers' , ( ) = > {
2016-06-08 18:45:15 -04:00
var nextProviderId : any /** TODO #9100 */ ;
2016-01-06 17:13:44 -05:00
function createToken ( value : string ) : CompileTokenMetadata {
2016-06-08 18:45:15 -04:00
var token : any /** TODO #9100 */ ;
2016-01-06 17:13:44 -05:00
if ( value . startsWith ( 'type:' ) ) {
token = new CompileTokenMetadata ( {
identifier :
new CompileTypeMetadata ( { module Url : 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-06-08 19:38:52 -04:00
token : string , { multi = false , deps = /*@ts2dart_const*/ [ ] } :
{ multi? : boolean , deps? : string [ ] } = { } ) : CompileProviderMetadata {
2016-01-06 17:13:44 -05:00
return new CompileProviderMetadata ( {
token : createToken ( token ) ,
multi : multi ,
useClass : new CompileTypeMetadata ( { name : ` provider ${ nextProviderId ++ } ` } ) ,
deps : deps.map ( createDep )
} ) ;
}
2016-06-08 19:38:52 -04: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 17:13:44 -05:00
var isComponent = ! selector . startsWith ( '[' ) ;
return CompileDirectiveMetadata . create ( {
selector : selector ,
type : new CompileTypeMetadata (
{ module Url : someModuleUrl , name : selector , diDeps : deps.map ( createDep ) } ) ,
isComponent : isComponent ,
template : new CompileTemplateMetadata ( { ngContentSelectors : [ ] } ) ,
providers : providers ,
viewProviders : viewProviders ,
2016-06-08 19:38:52 -04:00
queries : queries.map (
( value ) = > new CompileQueryMetadata ( { selectors : [ createToken ( value ) ] } ) )
2016-01-06 17:13:44 -05:00
} ) ;
}
beforeEach ( ( ) = > { nextProviderId = 0 ; } ) ;
it ( 'should provide a component' , ( ) = > {
var comp = createDir ( 'my-comp' ) ;
var elAst : ElementAst = < ElementAst > parse ( '<my-comp>' , [ comp ] ) [ 0 ] ;
expect ( elAst . providers . length ) . toBe ( 1 ) ;
expect ( elAst . providers [ 0 ] . providerType ) . toBe ( ProviderAstType . Component ) ;
expect ( elAst . providers [ 0 ] . providers [ 0 ] . useClass ) . toBe ( comp . type ) ;
} ) ;
it ( 'should provide a directive' , ( ) = > {
var dirA = createDir ( '[dirA]' ) ;
var elAst : ElementAst = < ElementAst > parse ( '<div dirA>' , [ dirA ] ) [ 0 ] ;
expect ( elAst . providers . length ) . toBe ( 1 ) ;
expect ( elAst . providers [ 0 ] . providerType ) . toBe ( ProviderAstType . Directive ) ;
expect ( elAst . providers [ 0 ] . providers [ 0 ] . useClass ) . toBe ( dirA . type ) ;
} ) ;
it ( 'should use the public providers of a directive' , ( ) = > {
var provider = createProvider ( 'service' ) ;
var dirA = createDir ( '[dirA]' , { providers : [ provider ] } ) ;
var elAst : ElementAst = < ElementAst > parse ( '<div dirA>' , [ dirA ] ) [ 0 ] ;
expect ( elAst . providers . length ) . toBe ( 2 ) ;
expect ( elAst . providers [ 1 ] . providerType ) . toBe ( ProviderAstType . PublicService ) ;
expect ( elAst . providers [ 1 ] . providers ) . toEqual ( [ provider ] ) ;
} ) ;
it ( 'should use the private providers of a component' , ( ) = > {
var provider = createProvider ( 'service' ) ;
var comp = createDir ( 'my-comp' , { viewProviders : [ provider ] } ) ;
var elAst : ElementAst = < ElementAst > parse ( '<my-comp>' , [ comp ] ) [ 0 ] ;
expect ( elAst . providers . length ) . toBe ( 2 ) ;
expect ( elAst . providers [ 1 ] . providerType ) . toBe ( ProviderAstType . PrivateService ) ;
expect ( elAst . providers [ 1 ] . providers ) . toEqual ( [ provider ] ) ;
} ) ;
it ( 'should support multi providers' , ( ) = > {
var provider0 = createProvider ( 'service0' , { multi : true } ) ;
var provider1 = createProvider ( 'service1' , { multi : true } ) ;
var provider2 = createProvider ( 'service0' , { multi : true } ) ;
var dirA = createDir ( '[dirA]' , { providers : [ provider0 , provider1 ] } ) ;
var dirB = createDir ( '[dirB]' , { providers : [ provider2 ] } ) ;
var elAst : ElementAst = < ElementAst > parse ( '<div dirA dirB>' , [ dirA , dirB ] ) [ 0 ] ;
expect ( elAst . providers . length ) . toBe ( 4 ) ;
expect ( elAst . providers [ 2 ] . providers ) . toEqual ( [ provider0 , provider2 ] ) ;
expect ( elAst . providers [ 3 ] . providers ) . toEqual ( [ provider1 ] ) ;
} ) ;
it ( 'should overwrite non multi providers' , ( ) = > {
var provider1 = createProvider ( 'service0' ) ;
var provider2 = createProvider ( 'service1' ) ;
var provider3 = createProvider ( 'service0' ) ;
var dirA = createDir ( '[dirA]' , { providers : [ provider1 , provider2 ] } ) ;
var dirB = createDir ( '[dirB]' , { providers : [ provider3 ] } ) ;
var elAst : ElementAst = < ElementAst > parse ( '<div dirA dirB>' , [ dirA , dirB ] ) [ 0 ] ;
expect ( elAst . providers . length ) . toBe ( 4 ) ;
expect ( elAst . providers [ 2 ] . providers ) . toEqual ( [ provider3 ] ) ;
expect ( elAst . providers [ 3 ] . providers ) . toEqual ( [ provider2 ] ) ;
} ) ;
it ( 'should overwrite component providers by directive providers' , ( ) = > {
var compProvider = createProvider ( 'service0' ) ;
var dirProvider = createProvider ( 'service0' ) ;
var comp = createDir ( 'my-comp' , { providers : [ compProvider ] } ) ;
var dirA = createDir ( '[dirA]' , { providers : [ dirProvider ] } ) ;
var elAst : ElementAst = < ElementAst > parse ( '<my-comp dirA>' , [ dirA , comp ] ) [ 0 ] ;
expect ( elAst . providers . length ) . toBe ( 3 ) ;
expect ( elAst . providers [ 2 ] . providers ) . toEqual ( [ dirProvider ] ) ;
} ) ;
it ( 'should overwrite view providers by directive providers' , ( ) = > {
var viewProvider = createProvider ( 'service0' ) ;
var dirProvider = createProvider ( 'service0' ) ;
var comp = createDir ( 'my-comp' , { viewProviders : [ viewProvider ] } ) ;
var dirA = createDir ( '[dirA]' , { providers : [ dirProvider ] } ) ;
var elAst : ElementAst = < ElementAst > parse ( '<my-comp dirA>' , [ dirA , comp ] ) [ 0 ] ;
expect ( elAst . providers . length ) . toBe ( 3 ) ;
expect ( elAst . providers [ 2 ] . providers ) . toEqual ( [ dirProvider ] ) ;
} ) ;
it ( 'should overwrite directives by providers' , ( ) = > {
var dirProvider = createProvider ( 'type:my-comp' ) ;
var comp = createDir ( 'my-comp' , { providers : [ dirProvider ] } ) ;
var elAst : ElementAst = < ElementAst > parse ( '<my-comp>' , [ comp ] ) [ 0 ] ;
expect ( elAst . providers . length ) . toBe ( 1 ) ;
expect ( elAst . providers [ 0 ] . providers ) . toEqual ( [ dirProvider ] ) ;
} ) ;
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 11:39:53 -04:00
it ( 'should sort providers by their DI order' , ( ) = > {
2016-01-06 17:13:44 -05: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 11:39:53 -04: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 17:13:44 -05:00
it ( 'should mark directives and dependencies of directives as eager' , ( ) = > {
var provider0 = createProvider ( 'service0' ) ;
var provider1 = createProvider ( 'service1' ) ;
var dirA = createDir ( '[dirA]' , { providers : [ provider0 , provider1 ] , deps : [ 'service0' ] } ) ;
var elAst : ElementAst = < ElementAst > parse ( '<div dirA>' , [ dirA ] ) [ 0 ] ;
expect ( elAst . providers . length ) . toBe ( 3 ) ;
expect ( elAst . providers [ 0 ] . providers ) . toEqual ( [ provider0 ] ) ;
expect ( elAst . providers [ 0 ] . eager ) . toBe ( true ) ;
expect ( elAst . providers [ 1 ] . providers [ 0 ] . useClass ) . toEqual ( dirA . type ) ;
expect ( elAst . providers [ 1 ] . eager ) . toBe ( true ) ;
expect ( elAst . providers [ 2 ] . providers ) . toEqual ( [ provider1 ] ) ;
expect ( elAst . providers [ 2 ] . eager ) . toBe ( false ) ;
} ) ;
it ( 'should mark dependencies on parent elements as eager' , ( ) = > {
var provider0 = createProvider ( 'service0' ) ;
var provider1 = createProvider ( 'service1' ) ;
var dirA = createDir ( '[dirA]' , { providers : [ provider0 , provider1 ] } ) ;
var dirB = createDir ( '[dirB]' , { deps : [ 'service0' ] } ) ;
var elAst : ElementAst =
< ElementAst > parse ( '<div dirA><div dirB></div></div>' , [ dirA , dirB ] ) [ 0 ] ;
expect ( elAst . providers . length ) . toBe ( 3 ) ;
expect ( elAst . providers [ 0 ] . providers [ 0 ] . useClass ) . toEqual ( dirA . type ) ;
expect ( elAst . providers [ 0 ] . eager ) . toBe ( true ) ;
expect ( elAst . providers [ 1 ] . providers ) . toEqual ( [ provider0 ] ) ;
expect ( elAst . providers [ 1 ] . eager ) . toBe ( true ) ;
expect ( elAst . providers [ 2 ] . providers ) . toEqual ( [ provider1 ] ) ;
expect ( elAst . providers [ 2 ] . eager ) . toBe ( false ) ;
} ) ;
it ( 'should mark queried providers as eager' , ( ) = > {
var provider0 = createProvider ( 'service0' ) ;
var provider1 = createProvider ( 'service1' ) ;
var dirA =
createDir ( '[dirA]' , { providers : [ provider0 , provider1 ] , queries : [ 'service0' ] } ) ;
var elAst : ElementAst = < ElementAst > parse ( '<div dirA></div>' , [ dirA ] ) [ 0 ] ;
expect ( elAst . providers . length ) . toBe ( 3 ) ;
expect ( elAst . providers [ 0 ] . providers [ 0 ] . useClass ) . toEqual ( dirA . type ) ;
expect ( elAst . providers [ 0 ] . eager ) . toBe ( true ) ;
expect ( elAst . providers [ 1 ] . providers ) . toEqual ( [ provider0 ] ) ;
expect ( elAst . providers [ 1 ] . eager ) . toBe ( true ) ;
expect ( elAst . providers [ 2 ] . providers ) . toEqual ( [ provider1 ] ) ;
expect ( elAst . providers [ 2 ] . eager ) . toBe ( false ) ;
} ) ;
it ( 'should not mark dependencies accross embedded views as eager' , ( ) = > {
var provider0 = createProvider ( 'service0' ) ;
var dirA = createDir ( '[dirA]' , { providers : [ provider0 ] } ) ;
var dirB = createDir ( '[dirB]' , { deps : [ 'service0' ] } ) ;
var elAst : ElementAst =
< ElementAst > parse ( '<div dirA><div *ngIf dirB></div></div>' , [ dirA , dirB ] ) [ 0 ] ;
expect ( elAst . providers . length ) . toBe ( 2 ) ;
expect ( elAst . providers [ 0 ] . providers [ 0 ] . useClass ) . toEqual ( dirA . type ) ;
expect ( elAst . providers [ 0 ] . eager ) . toBe ( true ) ;
expect ( elAst . providers [ 1 ] . providers ) . toEqual ( [ provider0 ] ) ;
expect ( elAst . providers [ 1 ] . eager ) . toBe ( false ) ;
} ) ;
it ( 'should report missing @Self() deps as errors' , ( ) = > {
var dirA = createDir ( '[dirA]' , { deps : [ 'self:provider0' ] } ) ;
expect ( ( ) = > parse ( '<div dirA></div>' , [ dirA ] ) )
2016-06-22 17:58:57 -04:00
. toThrowError (
'Template parse errors:\nNo provider for provider0 ("[ERROR ->]<div dirA></div>"): TestComp@0:0' ) ;
2016-01-06 17:13:44 -05:00
} ) ;
it ( 'should change missing @Self() that are optional to nulls' , ( ) = > {
var dirA = createDir ( '[dirA]' , { deps : [ 'optional:self:provider0' ] } ) ;
var elAst : ElementAst = < ElementAst > parse ( '<div dirA></div>' , [ dirA ] ) [ 0 ] ;
expect ( elAst . providers [ 0 ] . providers [ 0 ] . deps [ 0 ] . isValue ) . toBe ( true ) ;
expect ( elAst . providers [ 0 ] . providers [ 0 ] . deps [ 0 ] . value ) . toBe ( null ) ;
} ) ;
it ( 'should report missing @Host() deps as errors' , ( ) = > {
var dirA = createDir ( '[dirA]' , { deps : [ 'host:provider0' ] } ) ;
expect ( ( ) = > parse ( '<div dirA></div>' , [ dirA ] ) )
2016-06-22 17:58:57 -04:00
. toThrowError (
'Template parse errors:\nNo provider for provider0 ("[ERROR ->]<div dirA></div>"): TestComp@0:0' ) ;
2016-01-06 17:13:44 -05:00
} ) ;
it ( 'should change missing @Host() that are optional to nulls' , ( ) = > {
var dirA = createDir ( '[dirA]' , { deps : [ 'optional:host:provider0' ] } ) ;
var elAst : ElementAst = < ElementAst > parse ( '<div dirA></div>' , [ dirA ] ) [ 0 ] ;
expect ( elAst . providers [ 0 ] . providers [ 0 ] . deps [ 0 ] . isValue ) . toBe ( true ) ;
expect ( elAst . providers [ 0 ] . providers [ 0 ] . deps [ 0 ] . value ) . toBe ( null ) ;
} ) ;
} ) ;
2016-04-25 22:52:24 -04:00
describe ( 'references' , ( ) = > {
2015-09-18 13:33:23 -04:00
2016-04-25 22:52:24 -04:00
it ( 'should parse references via #... and not report them as attributes' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<div #a>' , [
] ) ) ) . toEqual ( [ [ ElementAst , 'div' ] , [ ReferenceAst , 'a' , null ] ] ) ;
2015-09-18 13:33:23 -04:00
} ) ;
2016-04-25 22:52:24 -04:00
it ( 'should parse references via ref-... and not report them as attributes' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<div ref-a>' , [
] ) ) ) . toEqual ( [ [ ElementAst , 'div' ] , [ ReferenceAst , 'a' , null ] ] ) ;
2016-04-25 22:52:24 -04:00
} ) ;
it ( 'should parse references via var-... and report them as deprecated' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<div var-a>' , [
] ) ) ) . 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 13:33:23 -04:00
} ) ;
2016-04-25 22:52:24 -04:00
it ( 'should parse camel case references' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<div ref-someA>' , [
] ) ) ) . toEqual ( [ [ ElementAst , 'div' ] , [ ReferenceAst , 'someA' , null ] ] ) ;
2015-09-18 13:33:23 -04:00
} ) ;
2016-04-25 22:52:24 -04:00
it ( 'should assign references with empty value to the element' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<div #a></div>' , [
] ) ) ) . toEqual ( [ [ ElementAst , 'div' ] , [ ReferenceAst , 'a' , null ] ] ) ;
2015-09-18 13:33:23 -04:00
} ) ;
2016-04-25 22:52:24 -04:00
it ( 'should assign references to directives via exportAs' , ( ) = > {
2016-01-06 17:13:44 -05:00
var dirA = CompileDirectiveMetadata . create ( {
selector : '[a]' ,
type : new CompileTypeMetadata ( { module Url : someModuleUrl , name : 'DirA' } ) ,
exportAs : 'dirA'
} ) ;
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<div a #a="dirA"></div>' , [ dirA ] ) ) ) . toEqual ( [
[ ElementAst , 'div' ] ,
[ AttrAst , 'a' , '' ] ,
[ ReferenceAst , 'a' , identifierToken ( dirA . type ) ] ,
[ DirectiveAst , dirA ] ,
] ) ;
2015-09-18 13:33:23 -04:00
} ) ;
2016-04-25 22:52:24 -04:00
it ( 'should report references with values that dont match a directive as errors' , ( ) = > {
2015-09-18 13:33:23 -04:00
expect ( ( ) = > parse ( '<div #a="dirA"></div>' , [ ] ) ) . toThrowError ( ` Template parse errors:
2015-11-10 18:56:25 -05:00
There is no directive with "exportAs" set to "dirA" ( "<div [ERROR ->]#a=" dirA "></div>" ) : TestComp @0 : 5 ` );
2015-09-18 13:33:23 -04:00
} ) ;
2016-04-25 22:52:24 -04:00
it ( 'should report invalid reference names' , ( ) = > {
2015-11-23 19:02:19 -05:00
expect ( ( ) = > parse ( '<div #a-b></div>' , [ ] ) ) . toThrowError ( ` Template parse errors:
2016-04-25 22:52:24 -04:00
"-" is not allowed in reference names ( "<div [ERROR ->]#a-b></div>" ) : TestComp @0 : 5 ` );
2015-11-23 19:02:19 -05:00
} ) ;
2016-04-25 22:52:24 -04: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 13:33:23 -04:00
2016-06-08 19:38:52 -04: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 ` );
2016-05-26 16:04:17 -04:00
} ) ;
2016-06-08 19:38:52 -04:00
it (
'should not throw error when there is same reference name in different templates' ,
( ) = > {
expect ( ( ) = > parse ( '<div #a><template #a><span>OK</span></template></div>' , [ ] ) )
. not . toThrowError ( ) ;
} ) ;
2016-05-26 16:04:17 -04:00
2016-04-25 22:52:24 -04:00
it ( 'should assign references with empty value to components' , ( ) = > {
2015-09-18 13:33:23 -04:00
var dirA = CompileDirectiveMetadata . create ( {
selector : '[a]' ,
isComponent : true ,
2016-01-06 17:13:44 -05:00
type : new CompileTypeMetadata ( { module Url : someModuleUrl , name : 'DirA' } ) ,
2015-10-28 03:59:19 -04:00
exportAs : 'dirA' ,
template : new CompileTemplateMetadata ( { ngContentSelectors : [ ] } )
2015-09-18 13:33:23 -04:00
} ) ;
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<div a #a></div>' , [ dirA ] ) ) ) . toEqual ( [
[ ElementAst , 'div' ] ,
[ AttrAst , 'a' , '' ] ,
[ ReferenceAst , 'a' , identifierToken ( dirA . type ) ] ,
[ DirectiveAst , dirA ] ,
] ) ;
2015-09-18 13:33:23 -04:00
} ) ;
2016-04-25 22:52:24 -04:00
it ( 'should not locate directives in references' , ( ) = > {
var dirA = CompileDirectiveMetadata . create ( {
selector : '[a]' ,
type : new CompileTypeMetadata ( { module Url : someModuleUrl , name : 'DirA' } )
} ) ;
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<div ref-a>' , [ dirA ] ) ) ) . toEqual ( [
[ ElementAst , 'div' ] , [ ReferenceAst , 'a' , null ]
] ) ;
2016-04-25 22:52:24 -04:00
} ) ;
2015-09-18 13:33:23 -04:00
} ) ;
2015-08-25 18:36:02 -04:00
describe ( 'explicit templates' , ( ) = > {
it ( 'should create embedded templates for <template> elements' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<template></template>' , [
] ) ) ) . toEqual ( [ [ EmbeddedTemplateAst ] ] ) ;
expect ( humanizeTplAst ( parse ( '<TEMPLATE></TEMPLATE>' , [
] ) ) ) . toEqual ( [ [ EmbeddedTemplateAst ] ] ) ;
2015-08-25 18:36:02 -04:00
} ) ;
2015-12-08 12:01:15 -05:00
it ( 'should create embedded templates for <template> elements regardless the namespace' ,
( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<svg><template></template></svg>' , [ ] ) ) ) . toEqual ( [
[ ElementAst , ':svg:svg' ] ,
[ EmbeddedTemplateAst ] ,
] ) ;
2015-12-08 12:01:15 -05:00
} ) ;
2016-04-25 22:52:24 -04:00
it ( 'should support references via #...' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<template #a>' , [ ] ) ) ) . toEqual ( [
[ EmbeddedTemplateAst ] , [ ReferenceAst , 'a' , identifierToken ( Identifiers . TemplateRef ) ]
] ) ;
2016-04-25 22:52:24 -04:00
} ) ;
it ( 'should support references via ref-...' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<template ref-a>' , [ ] ) ) ) . toEqual ( [
[ EmbeddedTemplateAst ] , [ ReferenceAst , 'a' , identifierToken ( Identifiers . TemplateRef ) ]
] ) ;
2016-04-25 22:52:24 -04:00
} ) ;
it ( 'should parse variables via let-...' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<template let-a="b">' , [
] ) ) ) . toEqual ( [ [ EmbeddedTemplateAst ] , [ VariableAst , 'a' , 'b' ] ] ) ;
2016-04-25 22:52:24 -04:00
} ) ;
it ( 'should parse variables via var-... and report them as deprecated' , ( ) = > {
2016-06-08 19:38:52 -04:00
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' ) ] ) ;
2016-04-25 22:52:24 -04:00
} ) ;
it ( 'should not locate directives in variables' , ( ) = > {
var dirA = CompileDirectiveMetadata . create ( {
selector : '[a]' ,
type : new CompileTypeMetadata ( { module Url : someModuleUrl , name : 'DirA' } )
} ) ;
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<template let-a="b"></template>' , [ dirA ] ) ) ) . toEqual ( [
[ EmbeddedTemplateAst ] , [ VariableAst , 'a' , 'b' ]
] ) ;
2016-04-25 22:52:24 -04:00
} ) ;
2015-08-25 18:36:02 -04:00
} ) ;
describe ( 'inline templates' , ( ) = > {
it ( 'should wrap the element into an EmbeddedTemplateAST' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<div template>' , [
] ) ) ) . toEqual ( [ [ EmbeddedTemplateAst ] , [ ElementAst , 'div' ] ] ) ;
2015-08-25 18:36:02 -04:00
} ) ;
it ( 'should parse bound properties' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<div template="ngIf test">' , [ ngIf ] ) ) ) . toEqual ( [
[ EmbeddedTemplateAst ] , [ DirectiveAst , ngIf ] ,
[ BoundDirectivePropertyAst , 'ngIf' , 'test' ] , [ ElementAst , 'div' ]
] ) ;
2015-08-25 18:36:02 -04:00
} ) ;
2016-04-25 22:52:24 -04:00
it ( 'should parse variables via #... and report them as deprecated' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<div *ngIf="#a=b">' , [
] ) ) ) . toEqual ( [ [ EmbeddedTemplateAst ] , [ VariableAst , 'a' , 'b' ] , [ ElementAst , 'div' ] ] ) ;
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' ) ] ) ;
2016-04-25 22:52:24 -04:00
} ) ;
it ( 'should parse variables via var ... and report them as deprecated' , ( ) = > {
2016-06-08 19:38:52 -04:00
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' ) ] ) ;
2015-08-25 18:36:02 -04:00
} ) ;
2016-04-25 22:52:24 -04:00
it ( 'should parse variables via let ...' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<div *ngIf="let a=b">' , [
] ) ) ) . toEqual ( [ [ EmbeddedTemplateAst ] , [ VariableAst , 'a' , 'b' ] , [ ElementAst , 'div' ] ] ) ;
2015-08-25 18:36:02 -04:00
} ) ;
describe ( 'directives' , ( ) = > {
it ( 'should locate directives in property bindings' , ( ) = > {
2016-01-06 17:13:44 -05:00
var dirA = CompileDirectiveMetadata . create ( {
selector : '[a=b]' ,
type : new CompileTypeMetadata ( { module Url : someModuleUrl , name : 'DirA' } ) ,
inputs : [ 'a' ]
} ) ;
var dirB = CompileDirectiveMetadata . create ( {
selector : '[b]' ,
type : new CompileTypeMetadata ( { module Url : someModuleUrl , name : 'DirB' } )
} ) ;
2016-06-08 19:38:52 -04: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 18:36:02 -04:00
} ) ;
2016-04-25 22:52:24 -04:00
it ( 'should not locate directives in variables' , ( ) = > {
2016-01-06 17:13:44 -05:00
var dirA = CompileDirectiveMetadata . create ( {
2016-04-25 22:52:24 -04:00
selector : '[a]' ,
2016-01-06 17:13:44 -05:00
type : new CompileTypeMetadata ( { module Url : someModuleUrl , name : 'DirA' } )
} ) ;
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<div template="let a=b">' , [ dirA ] ) ) ) . toEqual ( [
[ EmbeddedTemplateAst ] , [ VariableAst , 'a' , 'b' ] , [ ElementAst , 'div' ]
] ) ;
2016-04-25 22:52:24 -04:00
} ) ;
it ( 'should not locate directives in references' , ( ) = > {
var dirA = CompileDirectiveMetadata . create ( {
selector : '[a]' ,
type : new CompileTypeMetadata ( { module Url : someModuleUrl , name : 'DirA' } )
2016-01-06 17:13:44 -05:00
} ) ;
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<div ref-a>' , [ dirA ] ) ) ) . toEqual ( [
[ ElementAst , 'div' ] , [ ReferenceAst , 'a' , null ]
] ) ;
2015-08-25 18:36:02 -04:00
} ) ;
} ) ;
it ( 'should work with *... and use the attribute name as property binding name' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<div *ngIf="test">' , [ ngIf ] ) ) ) . toEqual ( [
[ EmbeddedTemplateAst ] , [ DirectiveAst , ngIf ] ,
[ BoundDirectivePropertyAst , 'ngIf' , 'test' ] , [ ElementAst , 'div' ]
] ) ;
2015-08-25 18:36:02 -04:00
} ) ;
2015-10-26 16:22:45 -04:00
it ( 'should work with *... and empty value' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<div *ngIf>' , [ ngIf ] ) ) ) . toEqual ( [
[ EmbeddedTemplateAst ] , [ DirectiveAst , ngIf ] ,
[ BoundDirectivePropertyAst , 'ngIf' , 'null' ] , [ ElementAst , 'div' ]
] ) ;
2015-10-26 16:22:45 -04:00
} ) ;
2015-08-25 18:36:02 -04:00
} ) ;
} ) ;
2015-09-11 16:37:05 -04:00
describe ( 'content projection' , ( ) = > {
2016-06-08 18:45:15 -04:00
var compCounter : any /** TODO #9100 */ ;
2016-01-06 17:13:44 -05:00
beforeEach ( ( ) = > { compCounter = 0 ; } ) ;
2016-06-08 19:38:52 -04:00
function createComp (
selector : string , ngContentSelectors : string [ ] ) : CompileDirectiveMetadata {
2015-09-18 13:33:23 -04:00
return CompileDirectiveMetadata . create ( {
2015-09-11 16:37:05 -04:00
selector : selector ,
isComponent : true ,
2016-01-06 17:13:44 -05:00
type :
new CompileTypeMetadata ( { module Url : someModuleUrl , name : ` SomeComp ${ compCounter ++ } ` } ) ,
2015-09-18 13:33:23 -04:00
template : new CompileTemplateMetadata ( { ngContentSelectors : ngContentSelectors } )
2015-09-11 16:37:05 -04:00
} )
}
2016-04-29 20:07:28 -04:00
function createDir ( selector : string ) : CompileDirectiveMetadata {
return CompileDirectiveMetadata . create ( {
selector : selector ,
type :
new CompileTypeMetadata ( { module Url : someModuleUrl , name : ` SomeDir ${ compCounter ++ } ` } )
} )
}
2015-09-11 16:37:05 -04:00
describe ( 'project text nodes' , ( ) = > {
it ( 'should project text nodes with wildcard selector' , ( ) = > {
expect ( humanizeContentProjection ( parse ( '<div>hello</div>' , [ createComp ( 'div' , [ '*' ] ) ] ) ) )
. toEqual ( [ [ 'div' , null ] , [ '#text(hello)' , 0 ] ] ) ;
} ) ;
} ) ;
describe ( 'project elements' , ( ) = > {
it ( 'should project elements with wildcard selector' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeContentProjection ( parse ( '<div><span></span></div>' , [
createComp ( 'div' , [ '*' ] )
] ) ) ) . toEqual ( [ [ 'div' , null ] , [ 'span' , 0 ] ] ) ;
2015-09-11 16:37:05 -04:00
} ) ;
it ( 'should project elements with css selector' , ( ) = > {
2016-06-08 19:38:52 -04: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 16:37:05 -04:00
} ) ;
} ) ;
describe ( 'embedded templates' , ( ) = > {
it ( 'should project embedded templates with wildcard selector' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeContentProjection ( parse ( '<div><template></template></div>' , [
createComp ( 'div' , [ '*' ] )
] ) ) ) . toEqual ( [ [ 'div' , null ] , [ 'template' , 0 ] ] ) ;
2015-09-11 16:37:05 -04:00
} ) ;
it ( 'should project embedded templates with css selector' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeContentProjection ( parse (
'<div><template x></template><template></template></div>' ,
[ createComp ( 'div' , [ 'template[x]' ] ) ] ) ) )
2015-09-11 16:37:05 -04:00
. toEqual ( [ [ 'div' , null ] , [ 'template' , 0 ] , [ 'template' , null ] ] ) ;
} ) ;
} ) ;
describe ( 'ng-content' , ( ) = > {
it ( 'should project ng-content with wildcard selector' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeContentProjection ( parse ( '<div><ng-content></ng-content></div>' , [
createComp ( 'div' , [ '*' ] )
] ) ) ) . toEqual ( [ [ 'div' , null ] , [ 'ng-content' , 0 ] ] ) ;
2015-09-11 16:37:05 -04:00
} ) ;
it ( 'should project ng-content with css selector' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeContentProjection ( parse (
'<div><ng-content x></ng-content><ng-content></ng-content></div>' ,
[ createComp ( 'div' , [ 'ng-content[x]' ] ) ] ) ) )
2015-09-11 16:37:05 -04:00
. toEqual ( [ [ 'div' , null ] , [ 'ng-content' , 0 ] , [ 'ng-content' , null ] ] ) ;
} ) ;
} ) ;
it ( 'should project into the first matching ng-content' , ( ) = > {
2016-06-08 19:38:52 -04: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 16:37:05 -04:00
} ) ;
2015-10-29 19:23:13 -04:00
it ( 'should project into wildcard ng-content last' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeContentProjection ( parse ( '<div>hello<a></a></div>' , [
createComp ( 'div' , [ '*' , 'a' ] )
] ) ) ) . toEqual ( [ [ 'div' , null ] , [ '#text(hello)' , 0 ] , [ 'a' , 1 ] ] ) ;
2015-10-29 19:23:13 -04:00
} ) ;
2015-09-11 16:37:05 -04:00
it ( 'should only project direct child nodes' , ( ) = > {
2016-06-08 19:38:52 -04: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 16:37:05 -04:00
} ) ;
it ( 'should project nodes of nested components' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeContentProjection ( parse ( '<a><b>hello</b></a>' , [
createComp ( 'a' , [ '*' ] ) , createComp ( 'b' , [ '*' ] )
] ) ) ) . toEqual ( [ [ 'a' , null ] , [ 'b' , 0 ] , [ '#text(hello)' , 0 ] ] ) ;
2015-09-11 16:37:05 -04:00
} ) ;
2015-11-23 19:02:19 -05:00
it ( 'should project children of components with ngNonBindable' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeContentProjection ( parse ( '<div ngNonBindable>{{hello}}<span></span></div>' , [
createComp ( 'div' , [ '*' ] )
] ) ) ) . toEqual ( [ [ 'div' , null ] , [ '#text({{hello}})' , 0 ] , [ 'span' , 0 ] ] ) ;
2015-09-18 13:33:23 -04:00
} ) ;
2016-03-23 17:15:05 -04:00
it ( 'should match the element when there is an inline template' , ( ) = > {
2016-06-08 19:38:52 -04: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 17:15:05 -04:00
} ) ;
describe ( 'ngProjectAs' , ( ) = > {
it ( 'should override elements' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeContentProjection ( parse ( '<div><a ngProjectAs="b"></a></div>' , [
createComp ( 'div' , [ 'a' , 'b' ] )
] ) ) ) . toEqual ( [ [ 'div' , null ] , [ 'a' , 1 ] ] ) ;
2016-03-23 17:15:05 -04:00
} ) ;
it ( 'should override <ng-content>' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeContentProjection ( parse (
'<div><ng-content ngProjectAs="b"></ng-content></div>' ,
[ createComp ( 'div' , [ 'ng-content' , 'b' ] ) ] ) ) )
2016-03-23 17:15:05 -04:00
. toEqual ( [ [ 'div' , null ] , [ 'ng-content' , 1 ] ] ) ;
} ) ;
it ( 'should override <template>' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeContentProjection ( parse (
'<div><template ngProjectAs="b"></template></div>' ,
[ createComp ( 'div' , [ 'template' , 'b' ] ) ] ) ) )
2016-03-23 17:15:05 -04:00
. toEqual ( [ [ 'div' , null ] , [ 'template' , 1 ] ] ) ;
} ) ;
it ( 'should override inline templates' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeContentProjection ( parse (
'<div><a *ngIf="cond" ngProjectAs="b"></a></div>' ,
[ createComp ( 'div' , [ 'a' , 'b' ] ) , ngIf ] ) ) )
2016-03-23 17:15:05 -04:00
. toEqual ( [ [ 'div' , null ] , [ 'template' , 1 ] , [ 'a' , null ] ] ) ;
} ) ;
} ) ;
2016-04-29 20:07:28 -04:00
it ( 'should support other directives before the component' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeContentProjection ( parse ( '<div>hello</div>' , [
createDir ( 'div' ) , createComp ( 'div' , [ '*' ] )
] ) ) ) . toEqual ( [ [ 'div' , null ] , [ '#text(hello)' , 0 ] ] ) ;
2016-04-29 20:07:28 -04:00
} ) ;
2015-09-11 16:37:05 -04:00
} ) ;
2015-08-25 18:36:02 -04: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 19:29:02 -04:00
describe ( 'error cases' , ( ) = > {
2015-12-03 17:20:00 -05: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 < / n g - c o n t e n t > ( " [ E R R O R - > ] < n g - c o n t e n t > c o n t e n t < / n g - c o n t e n t > " ) : T e s t C o m p @ 0 : 0 ` ) ;
} ) ;
2016-06-22 11:43:27 -04:00
it ( 'should report when *attr is used on a template element' , ( ) = > {
expect ( ( ) = > parse ( '<template *ngIf>' , [ ] ) ) . toThrowError ( ` Template parse errors:
Can 't have template bindings on a <template> element but the ' * ngIf ' attribute was used ( "<template [ERROR ->]*ngIf>" ) : TestComp @0 : 10 ` );
} ) ;
it ( 'should report when a template attribute is used on a template element' , ( ) = > {
expect ( ( ) = > parse ( '<template template="ngIf">' , [ ] ) ) . toThrowError ( ` Template parse errors:
Can 't have template bindings on a <template> element but the ' template ' attribute was used ( "<template [ERROR ->]template=" ngIf ">" ) : TestComp @0 : 10 ` );
} ) ;
it ( 'should report when mutliple *attrs are used on the same element' , ( ) = > {
expect ( ( ) = > parse ( '<div *ngIf *ngFor>' , [ ] ) ) . toThrowError ( ` Template parse errors:
Can 't have multiple template bindings on one element. Use only one attribute named ' template ' or prefixed with * ( "<div *ngIf [ERROR ->]*ngFor>" ) : TestComp @0 : 11 ` );
} ) ;
it ( 'should report when mix of template and *attrs are used on the same element' , ( ) = > {
expect ( ( ) = > parse ( '<div template="ngIf" *ngFor>' , [ ] ) ) . toThrowError ( ` Template parse errors:
Can 't have multiple template bindings on one element. Use only one attribute named ' template ' or prefixed with * ( "<div template=" ngIf " [ERROR ->]*ngFor>" ) : TestComp @0 : 21 ` );
} ) ;
2015-10-07 12:34:21 -04:00
it ( 'should report invalid property names' , ( ) = > {
2015-11-23 19:02:19 -05: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 19:29:02 -04:00
} ) ;
it ( 'should report errors in expressions' , ( ) = > {
2016-06-22 17:58:57 -04:00
expect ( ( ) = > parse ( '<div [prop]="a b"></div>' , [ ] ) ) . toThrowError ( ` Template parse errors:
2015-11-10 18:56:25 -05: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 19:29:02 -04:00
} ) ;
it ( 'should not throw on invalid property names if the property is used by a directive' ,
( ) = > {
2015-09-18 13:33:23 -04:00
var dirA = CompileDirectiveMetadata . create ( {
2015-08-27 19:29:02 -04:00
selector : 'div' ,
2016-01-06 17:13:44 -05:00
type : new CompileTypeMetadata ( { module Url : someModuleUrl , name : 'DirA' } ) ,
2015-09-30 23:59:23 -04:00
inputs : [ 'invalidProp' ]
2015-08-27 19:29:02 -04:00
} ) ;
expect ( ( ) = > parse ( '<div [invalid-prop]></div>' , [ dirA ] ) ) . not . toThrow ( ) ;
} ) ;
it ( 'should not allow more than 1 component per element' , ( ) = > {
2015-09-18 13:33:23 -04:00
var dirA = CompileDirectiveMetadata . create ( {
2015-09-11 16:37:05 -04:00
selector : 'div' ,
isComponent : true ,
2016-01-06 17:13:44 -05:00
type : new CompileTypeMetadata ( { module Url : someModuleUrl , name : 'DirA' } ) ,
2015-09-18 13:33:23 -04:00
template : new CompileTemplateMetadata ( { ngContentSelectors : [ ] } )
2015-09-11 16:37:05 -04:00
} ) ;
2015-09-18 13:33:23 -04:00
var dirB = CompileDirectiveMetadata . create ( {
2015-09-11 16:37:05 -04:00
selector : 'div' ,
isComponent : true ,
2016-01-06 17:13:44 -05:00
type : new CompileTypeMetadata ( { module Url : someModuleUrl , name : 'DirB' } ) ,
2015-09-18 13:33:23 -04:00
template : new CompileTemplateMetadata ( { ngContentSelectors : [ ] } )
2015-09-11 16:37:05 -04:00
} ) ;
2015-12-03 19:10:20 -05:00
expect ( ( ) = > parse ( '<div>' , [ dirB , dirA ] ) ) . toThrowError ( ` Template parse errors:
More than one component : DirB , DirA ( "[ERROR ->]<div>" ) : TestComp @0 : 0 ` );
2015-08-27 19:29:02 -04:00
} ) ;
2015-10-13 20:43:15 -04:00
it ( 'should not allow components or element bindings nor dom events on explicit embedded templates' ,
2015-08-27 19:29:02 -04:00
( ) = > {
2015-09-18 13:33:23 -04:00
var dirA = CompileDirectiveMetadata . create ( {
2015-09-11 16:37:05 -04:00
selector : '[a]' ,
isComponent : true ,
2016-01-06 17:13:44 -05:00
type : new CompileTypeMetadata ( { module Url : someModuleUrl , name : 'DirA' } ) ,
2015-09-18 13:33:23 -04:00
template : new CompileTemplateMetadata ( { ngContentSelectors : [ ] } )
2015-09-11 16:37:05 -04:00
} ) ;
2015-08-27 19:29:02 -04:00
expect ( ( ) = > parse ( '<template [a]="b" (e)="f"></template>' , [ dirA ] ) )
. toThrowError ( ` Template parse errors:
2015-11-10 18:56:25 -05: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 19:29:02 -04:00
} ) ;
it ( 'should not allow components or element bindings on inline embedded templates' , ( ) = > {
2015-09-18 13:33:23 -04:00
var dirA = CompileDirectiveMetadata . create ( {
2015-09-11 16:37:05 -04:00
selector : '[a]' ,
isComponent : true ,
2016-01-06 17:13:44 -05:00
type : new CompileTypeMetadata ( { module Url : someModuleUrl , name : 'DirA' } ) ,
2015-09-18 13:33:23 -04:00
template : new CompileTemplateMetadata ( { ngContentSelectors : [ ] } )
2015-09-11 16:37:05 -04:00
} ) ;
2015-10-07 12:34:21 -04:00
expect ( ( ) = > parse ( '<div *a="b"></div>' , [ dirA ] ) ) . toThrowError ( ` Template parse errors:
2015-11-10 18:56:25 -05: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 19:29:02 -04:00
} ) ;
} ) ;
2015-09-18 13:33:23 -04:00
describe ( 'ignore elements' , ( ) = > {
2015-10-07 12:34:21 -04:00
it ( 'should ignore <script> elements' , ( ) = > {
2015-11-10 18:56:25 -05:00
expect ( humanizeTplAst ( parse ( '<script></script>a' , [ ] ) ) ) . toEqual ( [ [ TextAst , 'a' ] ] ) ;
2015-09-18 13:33:23 -04:00
} ) ;
2015-10-07 12:34:21 -04:00
it ( 'should ignore <style> elements' , ( ) = > {
2015-11-10 18:56:25 -05:00
expect ( humanizeTplAst ( parse ( '<style></style>a' , [ ] ) ) ) . toEqual ( [ [ TextAst , 'a' ] ] ) ;
2015-09-18 13:33:23 -04:00
} ) ;
2015-10-14 12:39:40 -04:00
describe ( '<link rel="stylesheet">' , ( ) = > {
it ( 'should keep <link rel="stylesheet"> elements if they have an absolute non package: url' ,
( ) = > {
2015-12-03 18:53:44 -05:00
expect ( humanizeTplAst ( parse ( '<link rel="stylesheet" href="http://someurl">a' , [ ] ) ) )
2015-10-14 12:39:40 -04:00
. toEqual ( [
2016-06-08 19:38:52 -04:00
[ ElementAst , 'link' ] , [ AttrAst , 'rel' , 'stylesheet' ] ,
[ AttrAst , 'href' , 'http://someurl' ] , [ TextAst , 'a' ]
2015-10-14 12:39:40 -04:00
] ) ;
} ) ;
it ( 'should keep <link rel="stylesheet"> elements if they have no uri' , ( ) = > {
2016-06-08 19:38:52 -04: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 12:39:40 -04:00
} ) ;
it ( 'should ignore <link rel="stylesheet"> elements if they have a relative uri' , ( ) = > {
2016-06-08 19:38:52 -04: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 12:39:40 -04:00
} ) ;
it ( 'should ignore <link rel="stylesheet"> elements if they have a package: uri' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<link rel="stylesheet" href="package:somePackage">a' , [
] ) ) ) . toEqual ( [ [ TextAst , 'a' ] ] ) ;
2015-10-14 12:39:40 -04:00
} ) ;
2015-09-18 13:33:23 -04:00
} ) ;
2015-11-23 19:02:19 -05:00
it ( 'should ignore bindings on children of elements with ngNonBindable' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<div ngNonBindable>{{b}}</div>' , [
] ) ) ) . toEqual ( [ [ ElementAst , 'div' ] , [ AttrAst , 'ngNonBindable' , '' ] , [ TextAst , '{{b}}' ] ] ) ;
2015-09-18 13:33:23 -04:00
} ) ;
2015-11-23 19:02:19 -05:00
it ( 'should keep nested children of elements with ngNonBindable' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<div ngNonBindable><span>{{b}}</span></div>' , [ ] ) ) ) . toEqual ( [
[ ElementAst , 'div' ] , [ AttrAst , 'ngNonBindable' , '' ] , [ ElementAst , 'span' ] ,
[ TextAst , '{{b}}' ]
] ) ;
2015-09-18 13:33:23 -04:00
} ) ;
2015-11-23 19:02:19 -05:00
it ( 'should ignore <script> elements inside of elements with ngNonBindable' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<div ngNonBindable><script></script>a</div>' , [
] ) ) ) . toEqual ( [ [ ElementAst , 'div' ] , [ AttrAst , 'ngNonBindable' , '' ] , [ TextAst , 'a' ] ] ) ;
2015-10-07 12:34:21 -04:00
} ) ;
2015-09-18 13:33:23 -04:00
2015-11-23 19:02:19 -05:00
it ( 'should ignore <style> elements inside of elements with ngNonBindable' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<div ngNonBindable><style></style>a</div>' , [
] ) ) ) . toEqual ( [ [ ElementAst , 'div' ] , [ AttrAst , 'ngNonBindable' , '' ] , [ TextAst , 'a' ] ] ) ;
2015-10-07 12:34:21 -04:00
} ) ;
2015-09-18 13:33:23 -04:00
2015-11-23 19:02:19 -05:00
it ( 'should ignore <link rel="stylesheet"> elements inside of elements with ngNonBindable' ,
2015-09-18 13:33:23 -04:00
( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAst ( parse ( '<div ngNonBindable><link rel="stylesheet">a</div>' , [
] ) ) ) . toEqual ( [ [ ElementAst , 'div' ] , [ AttrAst , 'ngNonBindable' , '' ] , [ TextAst , 'a' ] ] ) ;
2015-09-18 13:33:23 -04:00
} ) ;
2015-11-23 19:02:19 -05:00
it ( 'should convert <ng-content> elements into regular elements inside of elements with ngNonBindable' ,
2015-09-18 13:33:23 -04:00
( ) = > {
2015-11-23 19:02:19 -05:00
expect ( humanizeTplAst ( parse ( '<div ngNonBindable><ng-content></ng-content>a</div>' , [ ] ) ) )
2015-09-18 13:33:23 -04:00
. toEqual ( [
2016-06-08 19:38:52 -04:00
[ ElementAst , 'div' ] , [ AttrAst , 'ngNonBindable' , '' ] , [ ElementAst , 'ng-content' ] ,
2015-10-07 12:34:21 -04:00
[ TextAst , 'a' ]
2015-09-18 13:33:23 -04:00
] ) ;
2015-09-18 13:33:23 -04:00
} ) ;
} ) ;
2015-11-10 18:56:25 -05:00
describe ( 'source spans' , ( ) = > {
it ( 'should support ng-content' , ( ) = > {
var parsed = parse ( '<ng-content select="a">' , [ ] ) ;
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAstSourceSpans ( parsed ) ) . toEqual ( [
[ NgContentAst , '<ng-content select="a">' ]
] ) ;
2015-11-10 18:56:25 -05:00
} ) ;
it ( 'should support embedded template' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAstSourceSpans ( parse ( '<template></template>' , [
] ) ) ) . toEqual ( [ [ EmbeddedTemplateAst , '<template>' ] ] ) ;
2015-11-10 18:56:25 -05:00
} ) ;
it ( 'should support element and attributes' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAstSourceSpans ( parse ( '<div key=value>' , [ ] ) ) ) . toEqual ( [
[ ElementAst , 'div' , '<div key=value>' ] , [ AttrAst , 'key' , 'value' , 'key=value' ]
] ) ;
2015-11-10 18:56:25 -05:00
} ) ;
2016-04-25 22:52:24 -04:00
it ( 'should support references' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAstSourceSpans ( parse ( '<div #a></div>' , [
] ) ) ) . toEqual ( [ [ ElementAst , 'div' , '<div #a>' ] , [ ReferenceAst , 'a' , null , '#a' ] ] ) ;
2016-04-25 22:52:24 -04:00
} ) ;
2015-11-10 18:56:25 -05:00
it ( 'should support variables' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAstSourceSpans ( parse ( '<template let-a="b"></template>' , [ ] ) ) ) . toEqual ( [
[ EmbeddedTemplateAst , '<template let-a="b">' ] , [ VariableAst , 'a' , 'b' , 'let-a="b"' ]
] ) ;
2015-11-10 18:56:25 -05:00
} ) ;
2016-04-25 22:52:24 -04:00
it ( 'should support events' , ( ) = > {
2016-06-08 19:38:52 -04: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 18:56:25 -05:00
} ) ;
it ( 'should support element property' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAstSourceSpans ( parse ( '<div [someProp]="v">' , [ ] ) ) ) . toEqual ( [
[ ElementAst , 'div' , '<div [someProp]="v">' ] ,
[
BoundElementPropertyAst , PropertyBindingType . Property , 'someProp' , 'v' , null ,
'[someProp]="v"'
]
] ) ;
2015-11-10 18:56:25 -05:00
} ) ;
it ( 'should support bound text' , ( ) = > {
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAstSourceSpans ( parse ( '{{a}}' , [
] ) ) ) . toEqual ( [ [ BoundTextAst , '{{ a }}' , '{{a}}' ] ] ) ;
2015-11-10 18:56:25 -05:00
} ) ;
it ( 'should support text nodes' , ( ) = > {
expect ( humanizeTplAstSourceSpans ( parse ( 'a' , [ ] ) ) ) . toEqual ( [ [ TextAst , 'a' , 'a' ] ] ) ;
} ) ;
it ( 'should support directive' , ( ) = > {
2016-01-06 17:13:44 -05:00
var dirA = CompileDirectiveMetadata . create ( {
selector : '[a]' ,
type : new CompileTypeMetadata ( { module Url : someModuleUrl , name : 'DirA' } )
} ) ;
2015-11-10 18:56:25 -05:00
var comp = CompileDirectiveMetadata . create ( {
selector : 'div' ,
isComponent : true ,
2016-01-06 17:13:44 -05:00
type : new CompileTypeMetadata ( { module Url : someModuleUrl , name : 'ZComp' } ) ,
2015-11-10 18:56:25 -05:00
template : new CompileTemplateMetadata ( { ngContentSelectors : [ ] } )
} ) ;
2016-06-08 19:38:52 -04:00
expect ( humanizeTplAstSourceSpans ( parse ( '<div a>' , [ dirA , comp ] ) ) ) . toEqual ( [
[ ElementAst , 'div' , '<div a>' ] , [ AttrAst , 'a' , '' , 'a' ] , [ DirectiveAst , dirA , '<div a>' ] ,
[ DirectiveAst , comp , '<div a>' ]
] ) ;
2015-11-10 18:56:25 -05:00
} ) ;
2015-12-09 12:32:15 -05:00
it ( 'should support directive in namespace' , ( ) = > {
2016-01-06 17:13:44 -05:00
var tagSel = CompileDirectiveMetadata . create ( {
selector : 'circle' ,
type : new CompileTypeMetadata ( { module Url : someModuleUrl , name : 'elDir' } )
} ) ;
var attrSel = CompileDirectiveMetadata . create ( {
selector : '[href]' ,
type : new CompileTypeMetadata ( { module Url : someModuleUrl , name : 'attrDir' } )
} ) ;
2015-12-09 12:32:15 -05:00
expect ( humanizeTplAstSourceSpans (
parse ( '<svg><circle /><use xlink:href="Port" /></svg>' , [ tagSel , attrSel ] ) ) )
. toEqual ( [
2016-04-29 17:54:46 -04:00
[ ElementAst , ':svg:svg' , '<svg>' ] ,
[ ElementAst , ':svg:circle' , '<circle />' ] ,
2015-12-09 12:32:15 -05:00
[ DirectiveAst , tagSel , '<circle />' ] ,
2016-04-29 17:54:46 -04:00
[ ElementAst , ':svg:use' , '<use xlink:href="Port" />' ] ,
[ AttrAst , ':xlink:href' , 'Port' , 'xlink:href="Port"' ] ,
2015-12-09 12:32:15 -05:00
[ DirectiveAst , attrSel , '<use xlink:href="Port" />' ] ,
] ) ;
} ) ;
2015-11-10 18:56:25 -05:00
it ( 'should support directive property' , ( ) = > {
2016-01-06 17:13:44 -05:00
var dirA = CompileDirectiveMetadata . create ( {
selector : 'div' ,
type : new CompileTypeMetadata ( { module Url : someModuleUrl , name : 'DirA' } ) ,
inputs : [ 'aProp' ]
} ) ;
2016-06-08 19:38:52 -04: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 18:56:25 -05:00
} ) ;
} ) ;
2015-12-02 13:35:51 -05:00
describe ( 'pipes' , ( ) = > {
it ( 'should allow pipes that have been defined as dependencies' , ( ) = > {
2016-01-06 17:13:44 -05:00
var testPipe = new CompilePipeMetadata ( {
name : 'test' ,
type : new CompileTypeMetadata ( { module Url : someModuleUrl , name : 'DirA' } )
} ) ;
2015-12-02 13:35:51 -05: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 18:36:02 -04:00
} ) ;
}
2016-06-20 12:52:41 -04:00
function humanizeTplAst (
templateAsts : TemplateAst [ ] , interpolationConfig? : InterpolationConfig ) : any [ ] {
const humanizer = new TemplateHumanizer ( false , interpolationConfig ) ;
2015-11-10 18:56:25 -05:00
templateVisitAll ( humanizer , templateAsts ) ;
return humanizer . result ;
}
2016-06-20 12:52:41 -04:00
function humanizeTplAstSourceSpans (
templateAsts : TemplateAst [ ] , interpolationConfig? : InterpolationConfig ) : any [ ] {
const humanizer = new TemplateHumanizer ( true , interpolationConfig ) ;
2015-08-25 18:36:02 -04:00
templateVisitAll ( humanizer , templateAsts ) ;
return humanizer . result ;
}
class TemplateHumanizer implements TemplateAstVisitor {
result : any [ ] = [ ] ;
2015-11-10 18:56:25 -05:00
2016-06-20 12:52:41 -04:00
constructor (
private includeSourceSpan : boolean ,
private interpolationConfig : InterpolationConfig = DEFAULT_INTERPOLATION_CONFIG ) { } ;
2015-11-10 18:56:25 -05:00
2015-09-01 19:24:23 -04:00
visitNgContent ( ast : NgContentAst , context : any ) : any {
2015-11-10 18:56:25 -05:00
var res = [ NgContentAst ] ;
this . result . push ( this . _appendContext ( ast , res ) ) ;
2015-08-25 18:36:02 -04:00
return null ;
}
2015-09-01 19:24:23 -04:00
visitEmbeddedTemplate ( ast : EmbeddedTemplateAst , context : any ) : any {
2015-11-10 18:56:25 -05:00
var res = [ EmbeddedTemplateAst ] ;
this . result . push ( this . _appendContext ( ast , res ) ) ;
2015-08-25 18:36:02 -04:00
templateVisitAll ( this , ast . attrs ) ;
2015-10-23 19:08:46 -04:00
templateVisitAll ( this , ast . outputs ) ;
2016-04-25 22:52:24 -04:00
templateVisitAll ( this , ast . references ) ;
templateVisitAll ( this , ast . variables ) ;
2015-08-27 19:29:02 -04:00
templateVisitAll ( this , ast . directives ) ;
2015-08-25 18:36:02 -04:00
templateVisitAll ( this , ast . children ) ;
return null ;
}
2015-09-01 19:24:23 -04:00
visitElement ( ast : ElementAst , context : any ) : any {
2015-11-10 18:56:25 -05:00
var res = [ ElementAst , ast . name ] ;
this . result . push ( this . _appendContext ( ast , res ) ) ;
2015-08-25 18:36:02 -04:00
templateVisitAll ( this , ast . attrs ) ;
2015-09-30 23:59:23 -04:00
templateVisitAll ( this , ast . inputs ) ;
templateVisitAll ( this , ast . outputs ) ;
2016-04-25 22:52:24 -04:00
templateVisitAll ( this , ast . references ) ;
2015-08-27 19:29:02 -04:00
templateVisitAll ( this , ast . directives ) ;
2015-08-25 18:36:02 -04:00
templateVisitAll ( this , ast . children ) ;
return null ;
}
2016-04-25 22:52:24 -04: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 19:24:23 -04:00
visitVariable ( ast : VariableAst , context : any ) : any {
2015-11-10 18:56:25 -05:00
var res = [ VariableAst , ast . name , ast . value ] ;
this . result . push ( this . _appendContext ( ast , res ) ) ;
2015-08-25 18:36:02 -04:00
return null ;
}
2015-09-01 19:24:23 -04:00
visitEvent ( ast : BoundEventAst , context : any ) : any {
2016-06-20 12:52:41 -04:00
var res = [
BoundEventAst , ast . name , ast . target ,
expressionUnparser . unparse ( ast . handler , this . interpolationConfig )
] ;
2015-11-10 18:56:25 -05:00
this . result . push ( this . _appendContext ( ast , res ) ) ;
2015-08-25 18:36:02 -04:00
return null ;
}
2015-09-01 19:24:23 -04:00
visitElementProperty ( ast : BoundElementPropertyAst , context : any ) : any {
2015-11-10 18:56:25 -05:00
var res = [
2016-06-20 12:52:41 -04:00
BoundElementPropertyAst , ast . type , ast . name ,
expressionUnparser . unparse ( ast . value , this . interpolationConfig ) , ast . unit
2015-11-10 18:56:25 -05:00
] ;
this . result . push ( this . _appendContext ( ast , res ) ) ;
2015-08-25 18:36:02 -04:00
return null ;
}
2015-09-01 19:24:23 -04:00
visitAttr ( ast : AttrAst , context : any ) : any {
2015-11-10 18:56:25 -05:00
var res = [ AttrAst , ast . name , ast . value ] ;
this . result . push ( this . _appendContext ( ast , res ) ) ;
2015-08-25 18:36:02 -04:00
return null ;
}
2015-09-01 19:24:23 -04:00
visitBoundText ( ast : BoundTextAst , context : any ) : any {
2016-06-20 12:52:41 -04:00
var res = [ BoundTextAst , expressionUnparser . unparse ( ast . value , this . interpolationConfig ) ] ;
2015-11-10 18:56:25 -05:00
this . result . push ( this . _appendContext ( ast , res ) ) ;
2015-08-25 18:36:02 -04:00
return null ;
}
2015-09-01 19:24:23 -04:00
visitText ( ast : TextAst , context : any ) : any {
2015-11-10 18:56:25 -05:00
var res = [ TextAst , ast . value ] ;
this . result . push ( this . _appendContext ( ast , res ) ) ;
2015-08-25 18:36:02 -04:00
return null ;
}
2015-09-01 19:24:23 -04:00
visitDirective ( ast : DirectiveAst , context : any ) : any {
2015-11-10 18:56:25 -05:00
var res = [ DirectiveAst , ast . directive ] ;
this . result . push ( this . _appendContext ( ast , res ) ) ;
2015-09-30 23:59:23 -04:00
templateVisitAll ( this , ast . inputs ) ;
2015-08-27 19:29:02 -04:00
templateVisitAll ( this , ast . hostProperties ) ;
templateVisitAll ( this , ast . hostEvents ) ;
return null ;
}
2015-09-01 19:24:23 -04:00
visitDirectiveProperty ( ast : BoundDirectivePropertyAst , context : any ) : any {
2016-06-20 12:52:41 -04:00
var res = [
BoundDirectivePropertyAst , ast . directiveName ,
expressionUnparser . unparse ( ast . value , this . interpolationConfig )
] ;
2015-11-10 18:56:25 -05:00
this . result . push ( this . _appendContext ( ast , res ) ) ;
2015-08-27 19:29:02 -04:00
return null ;
}
2015-11-10 18:56:25 -05:00
private _appendContext ( ast : TemplateAst , input : any [ ] ) : any [ ] {
if ( ! this . includeSourceSpan ) return input ;
input . push ( ast . sourceSpan . toString ( ) ) ;
return input ;
}
2015-08-27 19:29:02 -04:00
}
2015-10-07 12:34:21 -04:00
function sourceInfo ( ast : TemplateAst ) : string {
return ` ${ ast . sourceSpan } : ${ ast . sourceSpan . start } ` ;
}
2015-09-11 16:37:05 -04: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 22:52:24 -04:00
visitReference ( ast : ReferenceAst , context : any ) : any { return null ; }
2015-09-11 16:37:05 -04: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 13:51:16 -05:00
class FooAstTransformer implements TemplateAstVisitor {
visitNgContent ( ast : NgContentAst , context : any ) : any { throw 'not implemented' ; }
visitEmbeddedTemplate ( ast : EmbeddedTemplateAst , context : any ) : any { throw 'not implemented' ; }
visitElement ( ast : ElementAst , context : any ) : any {
if ( ast . name != 'div' ) return ast ;
2016-06-08 19:38:52 -04:00
return new ElementAst (
'foo' , [ ] , [ ] , [ ] , [ ] , [ ] , [ ] , false , [ ] , ast . ngContentIndex , ast . sourceSpan ) ;
2015-11-19 13:51:16 -05:00
}
2016-04-25 22:52:24 -04:00
visitReference ( ast : ReferenceAst , context : any ) : any { throw 'not implemented' ; }
2015-11-19 13:51:16 -05:00
visitVariable ( ast : VariableAst , context : any ) : any { throw 'not implemented' ; }
visitEvent ( ast : BoundEventAst , context : any ) : any { throw 'not implemented' ; }
visitElementProperty ( ast : BoundElementPropertyAst , context : any ) : any { throw 'not implemented' ; }
visitAttr ( ast : AttrAst , context : any ) : any { throw 'not implemented' ; }
visitBoundText ( ast : BoundTextAst , context : any ) : any { throw 'not implemented' ; }
visitText ( ast : TextAst , context : any ) : any { throw 'not implemented' ; }
visitDirective ( ast : DirectiveAst , context : any ) : any { throw 'not implemented' ; }
visitDirectiveProperty ( ast : BoundDirectivePropertyAst , context : any ) : any {
throw 'not implemented' ;
}
}
class BarAstTransformer extends FooAstTransformer {
visitElement ( ast : ElementAst , context : any ) : any {
if ( ast . name != 'foo' ) return ast ;
2016-06-08 19:38:52 -04:00
return new ElementAst (
'bar' , [ ] , [ ] , [ ] , [ ] , [ ] , [ ] , false , [ ] , ast . ngContentIndex , ast . sourceSpan ) ;
2015-11-19 13:51:16 -05:00
}
}
2016-04-25 22:52:24 -04: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 20:50:03 -04:00
}