2016-06-23 09:47:54 -07:00
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
2016-06-08 16:38:52 -07:00
import { StaticReflector , StaticReflectorHost , StaticSymbol } from '@angular/compiler-cli/src/static_reflector' ;
2016-07-27 02:05:07 -07:00
import { HostListenerMetadata , animate , group , keyframes , sequence , state , style , transition , trigger } from '@angular/core' ;
2016-06-08 16:38:52 -07:00
import { beforeEach , ddescribe , describe , expect , iit , it } from '@angular/core/testing/testing_internal' ;
2016-04-28 17:50:03 -07:00
import { ListWrapper } from '@angular/facade/src/collection' ;
2016-06-08 16:38:52 -07:00
import { isBlank } from '@angular/facade/src/lang' ;
2016-06-17 13:11:00 -07:00
import { MetadataCollector } from '@angular/tsc-wrapped' ;
import * as ts from 'typescript' ;
2016-07-27 02:05:07 -07:00
2016-06-17 13:11:00 -07:00
// This matches .ts files but not .d.ts files.
const TS_EXT = /(^.|(?!\.d)..)\.ts$/ ;
2016-05-31 09:15:17 -07:00
2016-04-30 12:27:37 -07:00
describe ( 'StaticReflector' , () => {
2016-05-02 09:38:46 -07:00
let noContext = new StaticSymbol ( '' , '' );
2016-04-30 12:27:37 -07:00
let host : StaticReflectorHost ;
let reflector : StaticReflector ;
2016-03-24 10:03:10 -07:00
2016-04-30 12:27:37 -07:00
beforeEach (() => {
host = new MockReflectorHost ();
reflector = new StaticReflector ( host );
2016-03-24 10:03:10 -07:00
});
2016-04-30 12:27:37 -07:00
function simplify ( context : StaticSymbol , value : any ) {
return reflector . simplify ( context , value );
}
it ( 'should get annotations for NgFor' , () => {
let NgFor = host . findDeclaration ( 'angular2/src/common/directives/ng_for' , 'NgFor' );
let annotations = reflector . annotations ( NgFor );
expect ( annotations . length ). toEqual ( 1 );
let annotation = annotations [ 0 ];
expect ( annotation . selector ). toEqual ( '[ngFor][ngForOf]' );
expect ( annotation . inputs ). toEqual ([ 'ngForTrackBy' , 'ngForOf' , 'ngForTemplate' ]);
});
it ( 'should get constructor for NgFor' , () => {
let NgFor = host . findDeclaration ( 'angular2/src/common/directives/ng_for' , 'NgFor' );
let ViewContainerRef =
host . findDeclaration ( 'angular2/src/core/linker/view_container_ref' , 'ViewContainerRef' );
let TemplateRef = host . findDeclaration ( 'angular2/src/core/linker/template_ref' , 'TemplateRef' );
let IterableDiffers = host . findDeclaration (
'angular2/src/core/change_detection/differs/iterable_differs' , 'IterableDiffers' );
let ChangeDetectorRef = host . findDeclaration (
'angular2/src/core/change_detection/change_detector_ref' , 'ChangeDetectorRef' );
let parameters = reflector . parameters ( NgFor );
2016-06-08 16:38:52 -07:00
expect ( parameters ). toEqual ([
[ ViewContainerRef ], [ TemplateRef ], [ IterableDiffers ], [ ChangeDetectorRef ]
]);
2016-04-30 12:27:37 -07:00
});
it ( 'should get annotations for HeroDetailComponent' , () => {
let HeroDetailComponent =
host . findDeclaration ( 'src/app/hero-detail.component' , 'HeroDetailComponent' );
let annotations = reflector . annotations ( HeroDetailComponent );
expect ( annotations . length ). toEqual ( 1 );
let annotation = annotations [ 0 ];
expect ( annotation . selector ). toEqual ( 'my-hero-detail' );
2016-06-08 16:38:52 -07:00
expect ( annotation . directives ). toEqual ([[ host . findDeclaration (
'angular2/src/common/directives/ng_for' , 'NgFor' )]]);
expect ( annotation . animations ). toEqual ([ trigger ( 'myAnimation' , [
state ( 'state1' , style ({ 'background' : 'white' })),
transition (
'* => *' ,
sequence ([ group ([ animate (
'1s 0.5s' ,
keyframes ([ style ({ 'background' : 'blue' }), style ({ 'background' : 'red' })]))])]))
])]);
2016-04-30 12:27:37 -07:00
});
2016-06-02 16:40:38 -07:00
it ( 'should throw and exception for unsupported metadata versions' , () => {
let e = host . findDeclaration ( 'src/version-error' , 'e' );
2016-06-08 16:38:52 -07:00
expect (() => reflector . annotations ( e ))
. toThrow ( new Error (
'Metadata version mismatch for module /tmp/src/version-error.d.ts, found version 100, expected 1' ));
2016-06-02 16:40:38 -07:00
});
2016-04-30 12:27:37 -07:00
it ( 'should get and empty annotation list for an unknown class' , () => {
let UnknownClass = host . findDeclaration ( 'src/app/app.component' , 'UnknownClass' );
let annotations = reflector . annotations ( UnknownClass );
expect ( annotations ). toEqual ([]);
});
it ( 'should get propMetadata for HeroDetailComponent' , () => {
let HeroDetailComponent =
host . findDeclaration ( 'src/app/hero-detail.component' , 'HeroDetailComponent' );
let props = reflector . propMetadata ( HeroDetailComponent );
expect ( props [ 'hero' ]). toBeTruthy ();
2016-07-27 02:05:07 -07:00
expect ( props [ 'onMouseOver' ]). toEqual ([ new HostListenerMetadata ( 'mouseover' , [ '$event' ])]);
2016-04-30 12:27:37 -07:00
});
it ( 'should get an empty object from propMetadata for an unknown class' , () => {
let UnknownClass = host . findDeclaration ( 'src/app/app.component' , 'UnknownClass' );
let properties = reflector . propMetadata ( UnknownClass );
expect ( properties ). toEqual ({});
});
it ( 'should get empty parameters list for an unknown class ' , () => {
let UnknownClass = host . findDeclaration ( 'src/app/app.component' , 'UnknownClass' );
let parameters = reflector . parameters ( UnknownClass );
expect ( parameters ). toEqual ([]);
});
2016-06-03 15:43:09 -07:00
it ( 'should provide context for errors reported by the collector' , () => {
let SomeClass = host . findDeclaration ( 'src/error-reporting' , 'SomeClass' );
2016-06-08 16:38:52 -07:00
expect (() => reflector . annotations ( SomeClass ))
. toThrow ( new Error (
2016-07-11 17:26:35 -07:00
'Error encountered resolving symbol values statically. A reasonable error message (position 13:34 in the original .ts file), resolving symbol ErrorSym in /tmp/src/error-references.d.ts, resolving symbol Link2 in /tmp/src/error-references.d.ts, resolving symbol Link1 in /tmp/src/error-references.d.ts, resolving symbol SomeClass in /tmp/src/error-reporting.d.ts, resolving symbol SomeClass in /tmp/src/error-reporting.d.ts' ));
2016-06-03 15:43:09 -07:00
});
2016-04-30 12:27:37 -07:00
it ( 'should simplify primitive into itself' , () => {
expect ( simplify ( noContext , 1 )). toBe ( 1 );
expect ( simplify ( noContext , true )). toBe ( true );
2016-06-08 16:38:52 -07:00
expect ( simplify ( noContext , 'some value' )). toBe ( 'some value' );
2016-04-30 12:27:37 -07:00
});
2016-06-08 16:38:52 -07:00
it ( 'should simplify an array into a copy of the array' , () => {
expect ( simplify ( noContext , [ 1 , 2 , 3 ])). toEqual ([ 1 , 2 , 3 ]);
});
2016-04-30 12:27:37 -07:00
it ( 'should simplify an object to a copy of the object' , () => {
let expr = { a : 1 , b : 2 , c : 3 };
expect ( simplify ( noContext , expr )). toEqual ( expr );
});
it ( 'should simplify &&' , () => {
2016-06-08 16:38:52 -07:00
expect ( simplify ( noContext , ({ __symbolic : 'binop' , operator : '&&' , left : true , right : true })))
. toBe ( true );
expect ( simplify ( noContext , ({ __symbolic : 'binop' , operator : '&&' , left : true , right : false })))
. toBe ( false );
expect ( simplify ( noContext , ({ __symbolic : 'binop' , operator : '&&' , left : false , right : true })))
. toBe ( false );
expect ( simplify ( noContext , ({ __symbolic : 'binop' , operator : '&&' , left : false , right : false })))
. toBe ( false );
2016-04-30 12:27:37 -07:00
});
it ( 'should simplify ||' , () => {
2016-06-08 16:38:52 -07:00
expect ( simplify ( noContext , ({ __symbolic : 'binop' , operator : '||' , left : true , right : true })))
. toBe ( true );
expect ( simplify ( noContext , ({ __symbolic : 'binop' , operator : '||' , left : true , right : false })))
. toBe ( true );
expect ( simplify ( noContext , ({ __symbolic : 'binop' , operator : '||' , left : false , right : true })))
. toBe ( true );
expect ( simplify ( noContext , ({ __symbolic : 'binop' , operator : '||' , left : false , right : false })))
. toBe ( false );
2016-04-30 12:27:37 -07:00
});
it ( 'should simplify &' , () => {
2016-06-08 16:38:52 -07:00
expect ( simplify ( noContext , ({ __symbolic : 'binop' , operator : '&' , left : 0x22 , right : 0x0F })))
. toBe ( 0x22 & 0x0F );
expect ( simplify ( noContext , ({ __symbolic : 'binop' , operator : '&' , left : 0x22 , right : 0xF0 })))
. toBe ( 0x22 & 0xF0 );
2016-04-30 12:27:37 -07:00
});
it ( 'should simplify |' , () => {
2016-06-08 16:38:52 -07:00
expect ( simplify ( noContext , ({ __symbolic : 'binop' , operator : '|' , left : 0x22 , right : 0x0F })))
. toBe ( 0x22 | 0x0F );
expect ( simplify ( noContext , ({ __symbolic : 'binop' , operator : '|' , left : 0x22 , right : 0xF0 })))
. toBe ( 0x22 | 0xF0 );
2016-04-30 12:27:37 -07:00
});
it ( 'should simplify ^' , () => {
2016-06-08 16:38:52 -07:00
expect ( simplify ( noContext , ({ __symbolic : 'binop' , operator : '|' , left : 0x22 , right : 0x0F })))
. toBe ( 0x22 | 0x0F );
expect ( simplify ( noContext , ({ __symbolic : 'binop' , operator : '|' , left : 0x22 , right : 0xF0 })))
. toBe ( 0x22 | 0xF0 );
2016-04-30 12:27:37 -07:00
});
it ( 'should simplify ==' , () => {
2016-06-08 16:38:52 -07:00
expect ( simplify ( noContext , ({ __symbolic : 'binop' , operator : '==' , left : 0x22 , right : 0x22 })))
. toBe ( 0x22 == 0x22 );
expect ( simplify ( noContext , ({ __symbolic : 'binop' , operator : '==' , left : 0x22 , right : 0xF0 })))
. toBe ( 0x22 == 0xF0 );
2016-04-30 12:27:37 -07:00
});
it ( 'should simplify !=' , () => {
2016-06-08 16:38:52 -07:00
expect ( simplify ( noContext , ({ __symbolic : 'binop' , operator : '!=' , left : 0x22 , right : 0x22 })))
. toBe ( 0x22 != 0x22 );
expect ( simplify ( noContext , ({ __symbolic : 'binop' , operator : '!=' , left : 0x22 , right : 0xF0 })))
. toBe ( 0x22 != 0xF0 );
2016-04-30 12:27:37 -07:00
});
it ( 'should simplify ===' , () => {
2016-06-08 16:38:52 -07:00
expect ( simplify ( noContext , ({ __symbolic : 'binop' , operator : '===' , left : 0x22 , right : 0x22 })))
. toBe ( 0x22 === 0x22 );
expect ( simplify ( noContext , ({ __symbolic : 'binop' , operator : '===' , left : 0x22 , right : 0xF0 })))
. toBe ( 0x22 === 0xF0 );
2016-04-30 12:27:37 -07:00
});
it ( 'should simplify !==' , () => {
2016-06-08 16:38:52 -07:00
expect ( simplify ( noContext , ({ __symbolic : 'binop' , operator : '!==' , left : 0x22 , right : 0x22 })))
. toBe ( 0x22 !== 0x22 );
expect ( simplify ( noContext , ({ __symbolic : 'binop' , operator : '!==' , left : 0x22 , right : 0xF0 })))
. toBe ( 0x22 !== 0xF0 );
2016-04-30 12:27:37 -07:00
});
it ( 'should simplify >' , () => {
2016-06-08 16:38:52 -07:00
expect ( simplify ( noContext , ({ __symbolic : 'binop' , operator : '>' , left : 1 , right : 1 })))
. toBe ( 1 > 1 );
expect ( simplify ( noContext , ({ __symbolic : 'binop' , operator : '>' , left : 1 , right : 0 })))
. toBe ( 1 > 0 );
expect ( simplify ( noContext , ({ __symbolic : 'binop' , operator : '>' , left : 0 , right : 1 })))
. toBe ( 0 > 1 );
2016-04-30 12:27:37 -07:00
});
it ( 'should simplify >=' , () => {
2016-06-08 16:38:52 -07:00
expect ( simplify ( noContext , ({ __symbolic : 'binop' , operator : '>=' , left : 1 , right : 1 })))
. toBe ( 1 >= 1 );
expect ( simplify ( noContext , ({ __symbolic : 'binop' , operator : '>=' , left : 1 , right : 0 })))
. toBe ( 1 >= 0 );
expect ( simplify ( noContext , ({ __symbolic : 'binop' , operator : '>=' , left : 0 , right : 1 })))
. toBe ( 0 >= 1 );
2016-04-30 12:27:37 -07:00
});
it ( 'should simplify <=' , () => {
2016-06-08 16:38:52 -07:00
expect ( simplify ( noContext , ({ __symbolic : 'binop' , operator : '<=' , left : 1 , right : 1 })))
. toBe ( 1 <= 1 );
expect ( simplify ( noContext , ({ __symbolic : 'binop' , operator : '<=' , left : 1 , right : 0 })))
. toBe ( 1 <= 0 );
expect ( simplify ( noContext , ({ __symbolic : 'binop' , operator : '<=' , left : 0 , right : 1 })))
. toBe ( 0 <= 1 );
2016-04-30 12:27:37 -07:00
});
it ( 'should simplify <' , () => {
2016-06-08 16:38:52 -07:00
expect ( simplify ( noContext , ({ __symbolic : 'binop' , operator : '<' , left : 1 , right : 1 })))
. toBe ( 1 < 1 );
expect ( simplify ( noContext , ({ __symbolic : 'binop' , operator : '<' , left : 1 , right : 0 })))
. toBe ( 1 < 0 );
expect ( simplify ( noContext , ({ __symbolic : 'binop' , operator : '<' , left : 0 , right : 1 })))
. toBe ( 0 < 1 );
2016-04-30 12:27:37 -07:00
});
it ( 'should simplify <<' , () => {
2016-06-08 16:38:52 -07:00
expect ( simplify ( noContext , ({ __symbolic : 'binop' , operator : '<<' , left : 0x55 , right : 2 })))
. toBe ( 0x55 << 2 );
2016-04-30 12:27:37 -07:00
});
it ( 'should simplify >>' , () => {
2016-06-08 16:38:52 -07:00
expect ( simplify ( noContext , ({ __symbolic : 'binop' , operator : '>>' , left : 0x55 , right : 2 })))
. toBe ( 0x55 >> 2 );
2016-04-30 12:27:37 -07:00
});
it ( 'should simplify +' , () => {
2016-06-08 16:38:52 -07:00
expect ( simplify ( noContext , ({ __symbolic : 'binop' , operator : '+' , left : 0x55 , right : 2 })))
. toBe ( 0x55 + 2 );
2016-04-30 12:27:37 -07:00
});
it ( 'should simplify -' , () => {
2016-06-08 16:38:52 -07:00
expect ( simplify ( noContext , ({ __symbolic : 'binop' , operator : '-' , left : 0x55 , right : 2 })))
. toBe ( 0x55 - 2 );
2016-04-30 12:27:37 -07:00
});
it ( 'should simplify *' , () => {
2016-06-08 16:38:52 -07:00
expect ( simplify ( noContext , ({ __symbolic : 'binop' , operator : '*' , left : 0x55 , right : 2 })))
. toBe ( 0x55 * 2 );
2016-04-30 12:27:37 -07:00
});
it ( 'should simplify /' , () => {
2016-06-08 16:38:52 -07:00
expect ( simplify ( noContext , ({ __symbolic : 'binop' , operator : '/' , left : 0x55 , right : 2 })))
. toBe ( 0x55 / 2 );
2016-04-30 12:27:37 -07:00
});
it ( 'should simplify %' , () => {
2016-06-08 16:38:52 -07:00
expect ( simplify ( noContext , ({ __symbolic : 'binop' , operator : '%' , left : 0x55 , right : 2 })))
. toBe ( 0x55 % 2 );
2016-04-30 12:27:37 -07:00
});
it ( 'should simplify prefix -' , () => {
2016-06-08 16:38:52 -07:00
expect ( simplify ( noContext , ({ __symbolic : 'pre' , operator : '-' , operand : 2 }))). toBe ( - 2 );
2016-04-30 12:27:37 -07:00
});
it ( 'should simplify prefix ~' , () => {
2016-06-08 16:38:52 -07:00
expect ( simplify ( noContext , ({ __symbolic : 'pre' , operator : '~' , operand : 2 }))). toBe ( ~ 2 );
2016-04-30 12:27:37 -07:00
});
it ( 'should simplify prefix !' , () => {
2016-06-08 16:38:52 -07:00
expect ( simplify ( noContext , ({ __symbolic : 'pre' , operator : '!' , operand : true }))). toBe ( ! true );
expect ( simplify ( noContext , ({ __symbolic : 'pre' , operator : '!' , operand : false }))). toBe ( ! false );
2016-04-30 12:27:37 -07:00
});
it ( 'should simplify an array index' , () => {
2016-06-08 16:38:52 -07:00
expect ( simplify ( noContext , ({ __symbolic : 'index' , expression : [ 1 , 2 , 3 ], index : 2 }))). toBe ( 3 );
2016-04-30 12:27:37 -07:00
});
it ( 'should simplify an object index' , () => {
2016-06-08 16:38:52 -07:00
let expr = { __symbolic : 'select' , expression : { a : 1 , b : 2 , c : 3 }, member : 'b' };
2016-04-30 12:27:37 -07:00
expect ( simplify ( noContext , expr )). toBe ( 2 );
});
it ( 'should simplify a module reference' , () => {
2016-06-08 16:38:52 -07:00
expect ( simplify (
new StaticSymbol ( '/src/cases' , '' ),
({ __symbolic : 'reference' , module : './extern' , name : 's' })))
. toEqual ( 's' );
2016-04-30 12:27:37 -07:00
});
it ( 'should simplify a non existing reference as a static symbol' , () => {
2016-06-08 16:38:52 -07:00
expect ( simplify (
new StaticSymbol ( '/src/cases' , '' ),
({ __symbolic : 'reference' , module : './extern' , name : 'nonExisting' })))
2016-05-02 09:38:46 -07:00
. toEqual ( host . getStaticSymbol ( '/src/extern.d.ts' , 'nonExisting' ));
2016-04-30 12:27:37 -07:00
});
2016-06-13 15:56:51 -07:00
2016-06-30 13:07:17 -07:00
it ( 'should simplify a function reference as a static symbol' , () => {
expect ( simplify (
new StaticSymbol ( '/src/cases' , 'myFunction' ),
({ __symbolic : 'function' , parameters : [ 'a' ], value : []})))
. toEqual ( host . getStaticSymbol ( '/src/cases' , 'myFunction' ));
});
2016-06-13 15:56:51 -07:00
it ( 'should simplify values initialized with a function call' , () => {
expect ( simplify ( new StaticSymbol ( '/tmp/src/function-reference.ts' , '' ), {
__symbolic : 'reference' ,
name : 'one'
})). toEqual ([ 'some-value' ]);
expect ( simplify ( new StaticSymbol ( '/tmp/src/function-reference.ts' , '' ), {
__symbolic : 'reference' ,
2016-07-25 05:29:20 -07:00
name : 'three'
})). toEqual ( 3 );
2016-06-13 15:56:51 -07:00
});
it ( 'should error on direct recursive calls' , () => {
expect (
() => simplify (
new StaticSymbol ( '/tmp/src/function-reference.ts' , '' ),
{ __symbolic : 'reference' , name : 'recursion' }))
. toThrow ( new Error (
2016-07-25 05:29:20 -07:00
'Recursion not supported, resolving symbol recursive in /tmp/src/function-recursive.d.ts, resolving symbol recursion in /tmp/src/function-reference.ts, resolving symbol in /tmp/src/function-reference.ts' ));
2016-06-13 15:56:51 -07:00
});
it ( 'should error on indirect recursive calls' , () => {
expect (
() => simplify (
new StaticSymbol ( '/tmp/src/function-reference.ts' , '' ),
{ __symbolic : 'reference' , name : 'indirectRecursion' }))
. toThrow ( new Error (
2016-07-25 05:29:20 -07:00
'Recursion not supported, resolving symbol indirectRecursion2 in /tmp/src/function-recursive.d.ts, resolving symbol indirectRecursion1 in /tmp/src/function-recursive.d.ts, resolving symbol indirectRecursion in /tmp/src/function-reference.ts, resolving symbol in /tmp/src/function-reference.ts' ));
2016-06-13 15:56:51 -07:00
});
2016-06-17 13:11:00 -07:00
2016-06-13 15:56:51 -07:00
it ( 'should simplify a spread expression' , () => {
expect ( simplify ( new StaticSymbol ( '/tmp/src/spread.ts' , '' ), {
__symbolic : 'reference' ,
name : 'spread'
})). toEqual ([ 0 , 1 , 2 , 3 , 4 , 5 ]);
});
2016-06-17 13:11:00 -07:00
it ( 'should be able to get metadata from a ts file' , () => {
let metadata = reflector . getModuleMetadata ( '/tmp/src/custom-decorator-reference.ts' );
expect ( metadata ). toEqual ({
__symbolic : 'module' ,
version : 1 ,
metadata : {
Foo : {
__symbolic : 'class' ,
decorators : [{
__symbolic : 'call' ,
expression :
{ __symbolic : 'reference' , module : './custom-decorator' , name : 'CustomDecorator' }
}],
members : {
foo : [{
__symbolic : 'property' ,
decorators : [{
__symbolic : 'call' ,
expression : {
__symbolic : 'reference' ,
module : './custom-decorator' ,
name : 'CustomDecorator'
}
}]
}]
}
}
}
});
});
it ( 'should be able to get metadata for a class containing a custom decorator' , () => {
let props = reflector . propMetadata (
host . getStaticSymbol ( '/tmp/src/custom-decorator-reference.ts' , 'Foo' ));
expect ( props ). toEqual ({ foo : []});
});
it ( 'should report an error for invalid function calls' , () => {
expect (
() =>
reflector . annotations ( host . getStaticSymbol ( '/tmp/src/invalid-calls.ts' , 'MyComponent' )))
. toThrow ( new Error (
`Error encountered resolving symbol values statically. Calling function 'someFunction', function calls are not supported. Consider replacing the function or lambda with a reference to an exported function, resolving symbol MyComponent in /tmp/src/invalid-calls.ts, resolving symbol MyComponent in /tmp/src/invalid-calls.ts` ));
});
2016-07-26 10:18:35 -07:00
it ( 'should be able to get metadata for a class containing a static method call' , () => {
const annotations = reflector . annotations (
host . getStaticSymbol ( '/tmp/src/static-method-call.ts' , 'MyComponent' ));
expect ( annotations . length ). toBe ( 1 );
expect ( annotations [ 0 ]. providers ). toEqual ({ provider : 'a' , useValue : 100 });
});
2016-07-27 19:26:59 -07:00
it ( 'should be able to get metadata for a class containing a static field reference' , () => {
const annotations =
reflector . annotations ( host . getStaticSymbol ( '/tmp/src/static-field-reference.ts' , 'Foo' ));
expect ( annotations . length ). toBe ( 1 );
expect ( annotations [ 0 ]. providers ). toEqual ([{ provider : 'a' , useValue : 'Some string' }]);
});
2016-07-28 17:32:29 -07:00
it ( 'should be able to get the metadata for a class calling a method with a conditional expression' ,
() => {
const annotations = reflector . annotations (
host . getStaticSymbol ( '/tmp/src/static-method-call.ts' , 'MyCondComponent' ));
expect ( annotations . length ). toBe ( 1 );
expect ( annotations [ 0 ]. providers ). toEqual ([
[{ provider : 'a' , useValue : '1' }], [{ provider : 'a' , useValue : '2' }]
]);
});
2016-07-29 09:10:45 -07:00
it ( 'should be able to get the metadata for a class calling a method with default parameters' ,
() => {
const annotations = reflector . annotations (
host . getStaticSymbol ( '/tmp/src/static-method-call.ts' , 'MyDefaultsComponent' ));
expect ( annotations . length ). toBe ( 1 );
expect ( annotations [ 0 ]. providers ). toEqual ([[ 'a' , true , false ]]);
});
2016-04-30 12:27:37 -07:00
});
2016-03-24 10:03:10 -07:00
class MockReflectorHost implements StaticReflectorHost {
2016-04-28 21:54:02 -07:00
private staticTypeCache = new Map < string , StaticSymbol >();
2016-06-17 13:11:00 -07:00
private collector = new MetadataCollector ();
constructor () {}
2016-04-28 21:54:02 -07:00
2016-05-01 11:22:39 -07:00
angularImportLocations() {
return {
coreDecorators : 'angular2/src/core/metadata' ,
diDecorators : 'angular2/src/core/di/decorators' ,
diMetadata : 'angular2/src/core/di/metadata' ,
2016-06-13 15:56:51 -07:00
diOpaqueToken : 'angular2/src/core/di/opaque_token' ,
2016-05-31 09:15:17 -07:00
animationMetadata : 'angular2/src/core/animation/metadata' ,
2016-05-01 11:22:39 -07:00
provider : 'angular2/src/core/di/provider'
};
}
2016-05-02 09:38:46 -07:00
getStaticSymbol ( declarationFile : string , name : string ) : StaticSymbol {
2016-04-28 21:54:02 -07:00
var cacheKey = ` ${ declarationFile } : ${ name } ` ;
var result = this . staticTypeCache . get ( cacheKey );
if ( isBlank ( result )) {
2016-05-02 09:38:46 -07:00
result = new StaticSymbol ( declarationFile , name );
2016-04-28 21:54:02 -07:00
this . staticTypeCache . set ( cacheKey , result );
}
return result ;
2016-02-18 10:53:21 -08:00
}
2016-04-28 21:54:02 -07:00
// In tests, assume that symbols are not re-exported
2016-04-28 21:58:51 -07:00
findDeclaration ( modulePath : string , symbolName : string , containingFile? : string ) : StaticSymbol {
2016-04-25 21:29:06 -07:00
function splitPath ( path : string ) : string [] { return path . split ( /\/|\\/g ); }
function resolvePath ( pathParts : string []) : string {
2016-04-30 12:27:37 -07:00
let result : string [] = [];
2016-04-25 21:29:06 -07:00
ListWrapper . forEachWithIndex ( pathParts , ( part , index ) => {
switch ( part ) {
case '' :
case '.' :
if ( index > 0 ) return ;
break ;
case '..' :
if ( index > 0 && result . length != 0 ) result . pop ();
return ;
}
result . push ( part );
});
return result . join ( '/' );
}
function pathTo ( from : string , to : string ) : string {
let result = to ;
if ( to . startsWith ( '.' )) {
let fromParts = splitPath ( from );
fromParts . pop (); // remove the file name.
let toParts = splitPath ( to );
result = resolvePath ( fromParts . concat ( toParts ));
}
return result ;
}
2016-04-28 21:54:02 -07:00
if ( modulePath . indexOf ( '.' ) === 0 ) {
2016-07-26 10:18:35 -07:00
const baseName = pathTo ( containingFile , modulePath );
const tsName = baseName + '.ts' ;
if ( this . getMetadataFor ( tsName )) {
return this . getStaticSymbol ( tsName , symbolName );
}
return this . getStaticSymbol ( baseName + '.d.ts' , symbolName );
2016-04-25 21:29:06 -07:00
}
2016-05-02 09:38:46 -07:00
return this . getStaticSymbol ( '/tmp/' + modulePath + '.d.ts' , symbolName );
2016-04-25 21:29:06 -07:00
}
2016-03-24 10:03:10 -07:00
getMetadataFor ( moduleId : string ) : any {
2016-04-30 12:27:37 -07:00
let data : {[ key : string ] : any } = {
2016-06-08 10:44:04 -07:00
'/tmp/angular2/src/common/forms-deprecated/directives.d.ts' : [{
2016-06-08 16:38:52 -07:00
'__symbolic' : 'module' ,
'version' : 1 ,
'metadata' : {
'FORM_DIRECTIVES' : [
2016-04-30 12:27:37 -07:00
{
2016-06-08 16:38:52 -07:00
'__symbolic' : 'reference' ,
'name' : 'NgFor' ,
'module' : 'angular2/src/common/directives/ng_for'
2016-04-30 12:27:37 -07:00
}
]
}
2016-06-02 16:40:38 -07:00
}],
2016-04-30 12:27:37 -07:00
'/tmp/angular2/src/common/directives/ng_for.d.ts' : {
2016-06-08 16:38:52 -07:00
'__symbolic' : 'module' ,
'version' : 1 ,
'metadata' : {
'NgFor' : {
'__symbolic' : 'class' ,
'decorators' : [
2016-04-30 12:27:37 -07:00
{
2016-06-08 16:38:52 -07:00
'__symbolic' : 'call' ,
'expression' : {
'__symbolic' : 'reference' ,
'name' : 'Directive' ,
'module' : '../../core/metadata'
2016-04-30 12:27:37 -07:00
},
2016-06-08 16:38:52 -07:00
'arguments' : [
2016-04-30 12:27:37 -07:00
{
2016-06-08 16:38:52 -07:00
'selector' : '[ngFor][ngForOf]' ,
'inputs' : [ 'ngForTrackBy' , 'ngForOf' , 'ngForTemplate' ]
2016-04-30 12:27:37 -07:00
}
]
}
],
2016-06-08 16:38:52 -07:00
'members' : {
'__ctor__' : [
2016-04-30 12:27:37 -07:00
{
2016-06-08 16:38:52 -07:00
'__symbolic' : 'constructor' ,
'parameters' : [
2016-04-30 12:27:37 -07:00
{
2016-06-08 16:38:52 -07:00
'__symbolic' : 'reference' ,
'module' : '../../core/linker/view_container_ref' ,
'name' : 'ViewContainerRef'
2016-04-30 12:27:37 -07:00
},
{
2016-06-08 16:38:52 -07:00
'__symbolic' : 'reference' ,
'module' : '../../core/linker/template_ref' ,
'name' : 'TemplateRef'
2016-04-30 12:27:37 -07:00
},
{
2016-06-08 16:38:52 -07:00
'__symbolic' : 'reference' ,
'module' : '../../core/change_detection/differs/iterable_differs' ,
'name' : 'IterableDiffers'
2016-04-30 12:27:37 -07:00
},
{
2016-06-08 16:38:52 -07:00
'__symbolic' : 'reference' ,
'module' : '../../core/change_detection/change_detector_ref' ,
'name' : 'ChangeDetectorRef'
2016-04-30 12:27:37 -07:00
}
]
2016-02-18 10:53:21 -08:00
}
]
}
2016-04-30 12:27:37 -07:00
}
}
},
'/tmp/angular2/src/core/linker/view_container_ref.d.ts' :
2016-06-08 16:38:52 -07:00
{ version : 1 , 'metadata' : { 'ViewContainerRef' : { '__symbolic' : 'class' }}},
2016-04-30 12:27:37 -07:00
'/tmp/angular2/src/core/linker/template_ref.d.ts' :
2016-06-08 16:38:52 -07:00
{ version : 1 , 'module' : './template_ref' , 'metadata' : { 'TemplateRef' : { '__symbolic' : 'class' }}},
2016-04-30 12:27:37 -07:00
'/tmp/angular2/src/core/change_detection/differs/iterable_differs.d.ts' :
2016-06-08 16:38:52 -07:00
{ version : 1 , 'metadata' : { 'IterableDiffers' : { '__symbolic' : 'class' }}},
2016-04-30 12:27:37 -07:00
'/tmp/angular2/src/core/change_detection/change_detector_ref.d.ts' :
2016-06-08 16:38:52 -07:00
{ version : 1 , 'metadata' : { 'ChangeDetectorRef' : { '__symbolic' : 'class' }}},
2016-04-30 12:27:37 -07:00
'/tmp/src/app/hero-detail.component.d.ts' : {
2016-06-08 16:38:52 -07:00
'__symbolic' : 'module' ,
'version' : 1 ,
'metadata' : {
'HeroDetailComponent' : {
'__symbolic' : 'class' ,
'decorators' : [
2016-02-18 10:53:21 -08:00
{
2016-06-08 16:38:52 -07:00
'__symbolic' : 'call' ,
'expression' : {
'__symbolic' : 'reference' ,
'name' : 'Component' ,
'module' : 'angular2/src/core/metadata'
2016-04-30 12:27:37 -07:00
},
2016-06-08 16:38:52 -07:00
'arguments' : [
2016-04-30 12:27:37 -07:00
{
2016-06-08 16:38:52 -07:00
'selector' : 'my-hero-detail' ,
'template' :
'\n <div *ngIf="hero">\n <h2>{{hero.name}} details!</h2>\n <div><label>id: </label>{{hero.id}}</div>\n <div>\n <label>name: </label>\n <input [(ngModel)]="hero.name" placeholder="name"/>\n </div>\n </div>\n' ,
'directives' : [
2016-04-30 12:27:37 -07:00
{
2016-06-08 16:38:52 -07:00
'__symbolic' : 'reference' ,
'name' : 'FORM_DIRECTIVES' ,
'module' : 'angular2/src/common/forms-deprecated/directives'
2016-04-30 12:27:37 -07:00
}
2016-05-31 09:15:17 -07:00
],
2016-06-08 16:38:52 -07:00
'animations' : [{
'__symbolic' : 'call' ,
'expression' : {
'__symbolic' : 'reference' ,
'name' : 'trigger' ,
'module' : 'angular2/src/core/animation/metadata'
2016-05-31 09:15:17 -07:00
},
2016-06-08 16:38:52 -07:00
'arguments' : [
'myAnimation' ,
[{ '__symbolic' : 'call' ,
'expression' : {
'__symbolic' : 'reference' ,
'name' : 'state' ,
'module' : 'angular2/src/core/animation/metadata'
2016-05-31 09:15:17 -07:00
},
2016-06-08 16:38:52 -07:00
'arguments' : [
'state1' ,
{ '__symbolic' : 'call' ,
'expression' : {
'__symbolic' : 'reference' ,
'name' : 'style' ,
'module' : 'angular2/src/core/animation/metadata'
2016-05-31 09:15:17 -07:00
},
2016-06-08 16:38:52 -07:00
'arguments' : [
{ 'background' : 'white' }
2016-05-31 09:15:17 -07:00
]
}
]
}, {
2016-06-08 16:38:52 -07:00
'__symbolic' : 'call' ,
'expression' : {
'__symbolic' : 'reference' ,
'name' : 'transition' ,
'module' : 'angular2/src/core/animation/metadata'
2016-05-31 09:15:17 -07:00
},
2016-06-08 16:38:52 -07:00
'arguments' : [
'* => *' ,
2016-05-31 09:15:17 -07:00
{
2016-06-08 16:38:52 -07:00
'__symbolic' : 'call' ,
'expression' : {
'__symbolic' : 'reference' ,
'name' : 'sequence' ,
'module' : 'angular2/src/core/animation/metadata'
2016-05-31 09:15:17 -07:00
},
2016-06-08 16:38:52 -07:00
'arguments' : [[{ '__symbolic' : 'call' ,
'expression' : {
'__symbolic' : 'reference' ,
'name' : 'group' ,
'module' : 'angular2/src/core/animation/metadata'
2016-05-31 09:15:17 -07:00
},
2016-06-08 16:38:52 -07:00
'arguments' : [[{
'__symbolic' : 'call' ,
'expression' : {
'__symbolic' : 'reference' ,
'name' : 'animate' ,
'module' : 'angular2/src/core/animation/metadata'
2016-05-31 09:15:17 -07:00
},
2016-06-08 16:38:52 -07:00
'arguments' : [
'1s 0.5s' ,
{ '__symbolic' : 'call' ,
'expression' : {
'__symbolic' : 'reference' ,
'name' : 'keyframes' ,
'module' : 'angular2/src/core/animation/metadata'
2016-05-31 09:15:17 -07:00
},
2016-06-08 16:38:52 -07:00
'arguments' : [[{ '__symbolic' : 'call' ,
'expression' : {
'__symbolic' : 'reference' ,
'name' : 'style' ,
'module' : 'angular2/src/core/animation/metadata'
2016-05-31 09:15:17 -07:00
},
2016-06-08 16:38:52 -07:00
'arguments' : [ { 'background' : 'blue' } ]
2016-05-31 09:15:17 -07:00
}, {
2016-06-08 16:38:52 -07:00
'__symbolic' : 'call' ,
'expression' : {
'__symbolic' : 'reference' ,
'name' : 'style' ,
'module' : 'angular2/src/core/animation/metadata'
2016-05-31 09:15:17 -07:00
},
2016-06-08 16:38:52 -07:00
'arguments' : [ { 'background' : 'red' } ]
2016-05-31 09:15:17 -07:00
}]]
}
]
}]]
}]]
}
]
}
]
2016-04-30 12:27:37 -07:00
]
2016-05-31 09:15:17 -07:00
}]
}]
}],
2016-06-08 16:38:52 -07:00
'members' : {
'hero' : [
2016-04-30 12:27:37 -07:00
{
2016-06-08 16:38:52 -07:00
'__symbolic' : 'property' ,
'decorators' : [
2016-02-18 10:53:21 -08:00
{
2016-06-08 16:38:52 -07:00
'__symbolic' : 'call' ,
'expression' : {
'__symbolic' : 'reference' ,
'name' : 'Input' ,
'module' : 'angular2/src/core/metadata'
2016-04-30 12:27:37 -07:00
}
2016-02-18 10:53:21 -08:00
}
2016-04-30 12:27:37 -07:00
]
}
2016-07-27 02:05:07 -07:00
],
'onMouseOver' : [
{
'__symbolic' : 'method' ,
'decorators' : [
{
'__symbolic' : 'call' ,
'expression' : {
'__symbolic' : 'reference' ,
'module' : 'angular2/src/core/metadata' ,
'name' : 'HostListener'
},
'arguments' : [
'mouseover' ,
[
'$event'
]
]
}
]
}
]
2016-04-30 12:27:37 -07:00
}
}
}
},
2016-06-08 16:38:52 -07:00
'/src/extern.d.ts' : { '__symbolic' : 'module' , 'version' : 1 , metadata : { s : 's' }},
'/tmp/src/version-error.d.ts' : { '__symbolic' : 'module' , 'version' : 100 , metadata : { e : 's' }},
2016-06-03 15:43:09 -07:00
'/tmp/src/error-reporting.d.ts' : {
2016-06-08 16:38:52 -07:00
__symbolic : 'module' ,
2016-06-03 15:43:09 -07:00
version : 1 ,
metadata : {
SomeClass : {
2016-06-08 16:38:52 -07:00
__symbolic : 'class' ,
2016-06-03 15:43:09 -07:00
decorators : [
{
2016-06-08 16:38:52 -07:00
__symbolic : 'call' ,
2016-06-03 15:43:09 -07:00
expression : {
2016-06-08 16:38:52 -07:00
__symbolic : 'reference' ,
name : 'Component' ,
module : 'angular2/src/core/metadata'
2016-06-03 15:43:09 -07:00
},
arguments : [
{
directives : [
{
2016-06-08 16:38:52 -07:00
__symbolic : 'reference' ,
module : 'src/error-references' ,
name : 'Link1' ,
2016-06-03 15:43:09 -07:00
}
]
}
]
}
],
}
}
},
'/tmp/src/error-references.d.ts' : {
2016-06-08 16:38:52 -07:00
__symbolic : 'module' ,
2016-06-03 15:43:09 -07:00
version : 1 ,
metadata : {
Link1 : {
2016-06-08 16:38:52 -07:00
__symbolic : 'reference' ,
module : 'src/error-references' ,
name : 'Link2'
2016-06-03 15:43:09 -07:00
},
Link2 : {
2016-06-08 16:38:52 -07:00
__symbolic : 'reference' ,
module : 'src/error-references' ,
name : 'ErrorSym'
2016-06-03 15:43:09 -07:00
},
ErrorSym : {
2016-06-08 16:38:52 -07:00
__symbolic : 'error' ,
message : 'A reasonable error message' ,
2016-06-03 15:43:09 -07:00
line : 12 ,
character : 33
}
}
2016-06-13 15:56:51 -07:00
},
'/tmp/src/function-declaration.d.ts' : {
__symbolic : 'module' ,
version : 1 ,
metadata : {
one : {
__symbolic : 'function' ,
parameters : [ 'a' ],
value : [
{ __symbolic : 'reference' , name : 'a' }
]
},
add : {
__symbolic : 'function' ,
parameters : [ 'a' , 'b' ],
value : {
__symbolic : 'binop' ,
operator : '+' ,
left : { __symbolic : 'reference' , name : 'a' },
2016-07-25 05:29:20 -07:00
right : {
__symbolic : 'binop' ,
operator : '+' ,
left : { __symbolic : 'reference' , name : 'b' },
right : { __symbolic : 'reference' , name : 'oneLiteral' }
}
2016-06-13 15:56:51 -07:00
}
2016-07-25 05:29:20 -07:00
},
oneLiteral : 1
2016-06-13 15:56:51 -07:00
}
},
'/tmp/src/function-reference.ts' : {
__symbolic : 'module' ,
version : 1 ,
metadata : {
one : {
__symbolic : 'call' ,
expression : {
__symbolic : 'reference' ,
module : './function-declaration' ,
name : 'one'
},
arguments : [ 'some-value' ]
},
2016-07-25 05:29:20 -07:00
three : {
2016-06-13 15:56:51 -07:00
__symbolic : 'call' ,
expression : {
__symbolic : 'reference' ,
module : './function-declaration' ,
name : 'add'
},
arguments : [ 1 , 1 ]
},
recursion : {
__symbolic : 'call' ,
expression : {
__symbolic : 'reference' ,
module : './function-recursive' ,
name : 'recursive'
},
arguments : [ 1 ]
},
indirectRecursion : {
__symbolic : 'call' ,
expression : {
__symbolic : 'reference' ,
module : './function-recursive' ,
name : 'indirectRecursion1'
},
arguments : [ 1 ]
}
}
},
'/tmp/src/function-recursive.d.ts' : {
__symbolic : 'modules' ,
version : 1 ,
metadata : {
recursive : {
__symbolic : 'function' ,
parameters : [ 'a' ],
value : {
__symbolic : 'call' ,
expression : {
__symbolic : 'reference' ,
module : './function-recursive' ,
name : 'recursive' ,
},
arguments : [
{
__symbolic : 'reference' ,
name : 'a'
}
]
}
},
indirectRecursion1 : {
__symbolic : 'function' ,
parameters : [ 'a' ],
value : {
__symbolic : 'call' ,
expression : {
__symbolic : 'reference' ,
module : './function-recursive' ,
name : 'indirectRecursion2' ,
},
arguments : [
{
__symbolic : 'reference' ,
name : 'a'
}
]
}
},
indirectRecursion2 : {
__symbolic : 'function' ,
parameters : [ 'a' ],
value : {
__symbolic : 'call' ,
expression : {
__symbolic : 'reference' ,
module : './function-recursive' ,
name : 'indirectRecursion1' ,
},
arguments : [
{
__symbolic : 'reference' ,
name : 'a'
}
]
}
}
},
},
'/tmp/src/spread.ts' : {
__symbolic : 'module' ,
version : 1 ,
metadata : {
spread : [ 0 , { __symbolic : 'spread' , expression : [ 1 , 2 , 3 , 4 ]}, 5 ]
}
2016-06-17 13:11:00 -07:00
},
'/tmp/src/custom-decorator.ts' : `
export function CustomDecorator(): any {
return () => {};
}
` ,
'/tmp/src/custom-decorator-reference.ts' : `
import {CustomDecorator} from './custom-decorator';
@CustomDecorator()
export class Foo {
@CustomDecorator() get foo(): string { return ''; }
}
` ,
'/tmp/src/invalid-calll-definitions.ts' : `
export function someFunction(a: any) {
if (Array.isArray(a)) {
return a;
}
return undefined;
}
` ,
'/tmp/src/invalid-calls.ts' : `
import {someFunction} from './nvalid-calll-definitions.ts';
import {Component} from 'angular2/src/core/metadata';
import {NgIf} from 'angular2/common';
@Component({
selector: 'my-component',
directives: [someFunction([NgIf])]
})
export class MyComponent {}
@someFunction()
@Component({
selector: 'my-component',
directives: [NgIf]
})
export class MyOtherComponent { }
2016-07-26 10:18:35 -07:00
` ,
'/tmp/src/static-method.ts' : `
import {Component} from 'angular2/src/core/metadata';
@Component({
selector: 'stub'
})
export class MyModule {
static with(data: any) {
return { provider: 'a', useValue: data }
}
2016-07-28 17:32:29 -07:00
static condMethod(cond: boolean) {
return [{ provider: 'a', useValue: cond ? '1' : '2'}];
}
2016-07-29 09:10:45 -07:00
static defaultsMethod(a, b = true, c = false) {
return [a, b, c];
}
2016-07-26 10:18:35 -07:00
}
` ,
'/tmp/src/static-method-call.ts' : `
import {Component} from 'angular2/src/core/metadata';
import {MyModule} from './static-method';
@Component({
providers: MyModule.with(100)
})
export class MyComponent { }
2016-07-28 17:32:29 -07:00
@Component({
providers: [MyModule.condMethod(true), MyModule.condMethod(false)]
})
export class MyCondComponent { }
2016-07-29 09:10:45 -07:00
@Component({
providers: [MyModule.defaultsMethod('a')]
})
export class MyDefaultsComponent { }
2016-07-27 19:26:59 -07:00
` ,
'/tmp/src/static-field.ts' : `
import {Injectable} from 'angular2/core';
@Injectable()
export class MyModule {
static VALUE = 'Some string';
}
` ,
'/tmp/src/static-field-reference.ts' : `
import {Component} from 'angular2/src/core/metadata';
import {MyModule} from './static-field';
@Component({
providers: [ { provider: 'a', useValue: MyModule.VALUE } ]
})
export class Foo { }
2016-06-17 13:11:00 -07:00
`
2016-04-30 12:27:37 -07:00
};
2016-06-17 13:11:00 -07:00
if ( data [ moduleId ] && moduleId . match ( TS_EXT )) {
let text = data [ moduleId ];
if ( typeof text === 'string' ) {
let sf = ts . createSourceFile ( moduleId , data [ moduleId ], ts . ScriptTarget . ES5 );
let diagnostics : ts.Diagnostic [] = (< any > sf ). parseDiagnostics ;
if ( diagnostics && diagnostics . length ) {
throw Error ( `Error encountered during parse of file ${ moduleId } ` );
}
return this . collector . getMetadata ( sf );
}
}
2016-04-30 12:27:37 -07:00
return data [ moduleId ];
2016-03-24 10:03:10 -07:00
}
}