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-08-19 15:51:01 -04:00
import { CommonModule } from '@angular/common' ;
2017-04-28 14:50:45 -04:00
import { Compiler , ComponentFactory , ErrorHandler , EventEmitter , Host , Inject , Injectable , InjectionToken , Injector , NO_ERRORS_SCHEMA , NgModule , NgModuleRef , OnDestroy , ReflectiveInjector , SkipSelf } from '@angular/core' ;
2016-08-02 18:53:34 -04:00
import { ChangeDetectionStrategy , ChangeDetectorRef , PipeTransform } from '@angular/core/src/change_detection/change_detection' ;
2017-01-27 16:19:00 -05:00
import { getDebugContext } from '@angular/core/src/errors' ;
2016-08-16 19:48:32 -04:00
import { ComponentFactoryResolver } from '@angular/core/src/linker/component_factory_resolver' ;
2016-04-28 20:50:03 -04:00
import { ElementRef } from '@angular/core/src/linker/element_ref' ;
2016-08-02 18:53:34 -04:00
import { QueryList } from '@angular/core/src/linker/query_list' ;
2017-02-28 02:08:19 -05:00
import { TemplateRef } from '@angular/core/src/linker/template_ref' ;
2016-08-02 18:53:34 -04:00
import { ViewContainerRef } from '@angular/core/src/linker/view_container_ref' ;
import { EmbeddedViewRef } from '@angular/core/src/linker/view_ref' ;
2016-08-16 16:59:06 -04:00
import { Attribute , Component , ContentChildren , Directive , HostBinding , HostListener , Input , Output , Pipe } from '@angular/core/src/metadata' ;
2016-09-27 20:12:25 -04:00
import { TestBed , async , fakeAsync , getTestBed , tick } from '@angular/core/testing' ;
2016-08-02 18:53:34 -04:00
import { getDOM } from '@angular/platform-browser/src/dom/dom_adapter' ;
2017-02-14 19:14:40 -05:00
import { DOCUMENT } from '@angular/platform-browser/src/dom/dom_tokens' ;
2017-03-02 15:12:46 -05:00
import { dispatchEvent , el } from '@angular/platform-browser/testing/src/browser_util' ;
import { expect } from '@angular/platform-browser/testing/src/matchers' ;
2017-03-21 11:15:10 -04:00
2017-03-02 12:37:01 -05:00
import { stringify } from '../../src/util' ;
2015-05-21 19:30:07 -04:00
2017-01-03 19:54:46 -05:00
const ANCHOR_ELEMENT = new InjectionToken ( 'AnchorElement' ) ;
2015-06-03 14:02:51 -04:00
2015-05-21 19:30:07 -04:00
export function main() {
2017-02-28 02:08:19 -05:00
describe ( 'jit' , ( ) = > { declareTests ( { useJit : true } ) ; } ) ;
2017-02-02 18:01:35 -05:00
2017-02-28 02:08:19 -05:00
describe ( 'no jit' , ( ) = > { declareTests ( { useJit : false } ) ; } ) ;
2015-12-02 13:35:51 -05:00
}
2017-02-15 00:03:18 -05:00
2017-02-28 02:08:19 -05:00
function declareTests ( { useJit } : { useJit : boolean } ) {
2015-05-21 19:30:07 -04:00
describe ( 'integration tests' , function ( ) {
2017-02-15 00:03:18 -05:00
beforeEach ( ( ) = > { TestBed . configureCompiler ( { useJit } ) ; } ) ;
2015-06-03 14:02:51 -04:00
2015-05-21 19:30:07 -04:00
describe ( 'react to record changes' , function ( ) {
2016-08-17 10:15:35 -04:00
it ( 'should consume text node changes' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp ] } ) ;
const template = '<div>{{ctxProp}}</div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxProp = 'Hello World!' ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
2016-09-09 15:04:38 -04:00
expect ( fixture . nativeElement ) . toHaveText ( 'Hello World!' ) ;
2016-08-17 10:15:35 -04:00
} ) ;
2015-05-21 19:30:07 -04:00
2016-08-17 10:15:35 -04:00
it ( 'should update text node with a blank string when interpolation evaluates to null' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp ] } ) ;
const template = '<div>{{null}}{{ctxProp}}</div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2017-03-29 12:34:45 -04:00
fixture . componentInstance . ctxProp = null ! ;
2015-07-24 10:29:50 -04:00
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
2016-09-09 15:04:38 -04:00
expect ( fixture . nativeElement ) . toHaveText ( '' ) ;
2016-08-17 10:15:35 -04:00
} ) ;
2015-05-21 19:30:07 -04:00
2016-11-04 13:55:21 -04:00
it ( 'should allow both null and undefined in expressions' , ( ) = > {
const template = '<div>{{null == undefined}}|{{null === undefined}}</div>' ;
const fixture = TestBed . configureTestingModule ( { declarations : [ MyComp ] } )
. overrideComponent ( MyComp , { set : { template } } )
. createComponent ( MyComp ) ;
fixture . detectChanges ( ) ;
expect ( fixture . nativeElement ) . toHaveText ( 'true|false' ) ;
} ) ;
2016-11-07 15:23:03 -05:00
it ( 'should support an arbitrary number of interpolations in an element' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp ] } ) ;
const template =
` <div>before{{'0'}}a{{'1'}}b{{'2'}}c{{'3'}}d{{'4'}}e{{'5'}}f{{'6'}}g{{'7'}}h{{'8'}}i{{'9'}}j{{'10'}}after</div> ` ;
const fixture =
TestBed . overrideComponent ( MyComp , { set : { template } } ) . createComponent ( MyComp ) ;
fixture . detectChanges ( ) ;
expect ( fixture . nativeElement ) . toHaveText ( 'before0a1b2c3d4e5f6g7h8i9j10after' ) ;
} ) ;
it ( 'should use a blank string when interpolation evaluates to null or undefined with an arbitrary number of interpolations' ,
( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp ] } ) ;
const template =
` <div>0{{null}}a{{undefined}}b{{null}}c{{undefined}}d{{null}}e{{undefined}}f{{null}}g{{undefined}}h{{null}}i{{undefined}}j{{null}}1</div> ` ;
const fixture =
TestBed . overrideComponent ( MyComp , { set : { template } } ) . createComponent ( MyComp ) ;
fixture . detectChanges ( ) ;
expect ( fixture . nativeElement ) . toHaveText ( '0abcdefghij1' ) ;
} ) ;
2016-08-17 10:15:35 -04:00
it ( 'should consume element binding changes' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp ] } ) ;
const template = '<div [id]="ctxProp"></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2015-05-21 19:30:07 -04:00
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxProp = 'Hello World!' ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
2015-09-13 13:05:18 -04:00
2017-07-19 16:58:23 -04:00
expect ( getDOM ( ) . getProperty ( fixture . debugElement . children [ 0 ] . nativeElement , 'id' ) )
. toEqual ( 'Hello World!' ) ;
2016-08-17 10:15:35 -04:00
} ) ;
it ( 'should consume binding to aria-* attributes' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp ] } ) ;
const template = '<div [attr.aria-label]="ctxProp"></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxProp = 'Initial aria label' ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
expect ( getDOM ( ) . getAttribute ( fixture . debugElement . children [ 0 ] . nativeElement , 'aria-label' ) )
. toEqual ( 'Initial aria label' ) ;
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxProp = 'Changed aria label' ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
expect ( getDOM ( ) . getAttribute ( fixture . debugElement . children [ 0 ] . nativeElement , 'aria-label' ) )
. toEqual ( 'Changed aria label' ) ;
} ) ;
it ( 'should remove an attribute when attribute expression evaluates to null' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp ] } ) ;
const template = '<div [attr.foo]="ctxProp"></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxProp = 'bar' ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
expect ( getDOM ( ) . getAttribute ( fixture . debugElement . children [ 0 ] . nativeElement , 'foo' ) )
. toEqual ( 'bar' ) ;
2017-03-29 12:34:45 -04:00
fixture . componentInstance . ctxProp = null ! ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
expect ( getDOM ( ) . hasAttribute ( fixture . debugElement . children [ 0 ] . nativeElement , 'foo' ) )
. toBeFalsy ( ) ;
} ) ;
it ( 'should remove style when when style expression evaluates to null' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp ] } ) ;
const template = '<div [style.height.px]="ctxProp"></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxProp = '10' ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
expect ( getDOM ( ) . getStyle ( fixture . debugElement . children [ 0 ] . nativeElement , 'height' ) )
. toEqual ( '10px' ) ;
2017-03-29 12:34:45 -04:00
fixture . componentInstance . ctxProp = null ! ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
expect ( getDOM ( ) . getStyle ( fixture . debugElement . children [ 0 ] . nativeElement , 'height' ) )
. toEqual ( '' ) ;
} ) ;
2015-11-04 04:16:47 -05:00
2015-05-21 19:30:07 -04:00
it ( 'should consume binding to property names where attr name and property name do not match' ,
2016-08-17 10:15:35 -04:00
( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp ] } ) ;
const template = '<div [tabindex]="ctxNumProp"></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
fixture . detectChanges ( ) ;
2017-07-19 16:58:23 -04:00
expect ( getDOM ( ) . getProperty ( fixture . debugElement . children [ 0 ] . nativeElement , 'tabIndex' ) )
. toEqual ( 0 ) ;
2016-08-17 10:15:35 -04:00
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxNumProp = 5 ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
2017-07-19 16:58:23 -04:00
expect ( getDOM ( ) . getProperty ( fixture . debugElement . children [ 0 ] . nativeElement , 'tabIndex' ) )
. toEqual ( 5 ) ;
2016-08-17 10:15:35 -04:00
} ) ;
it ( 'should consume binding to camel-cased properties' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp ] } ) ;
const template = '<input [readOnly]="ctxBoolProp">' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
fixture . detectChanges ( ) ;
2017-07-19 16:58:23 -04:00
expect ( getDOM ( ) . getProperty ( fixture . debugElement . children [ 0 ] . nativeElement , 'readOnly' ) )
. toBeFalsy ( ) ;
2016-08-17 10:15:35 -04:00
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxBoolProp = true ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
2017-07-19 16:58:23 -04:00
expect ( getDOM ( ) . getProperty ( fixture . debugElement . children [ 0 ] . nativeElement , 'readOnly' ) )
. toBeTruthy ( ) ;
2016-08-17 10:15:35 -04:00
} ) ;
2015-05-21 19:30:07 -04:00
2016-08-17 10:15:35 -04:00
it ( 'should consume binding to innerHtml' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp ] } ) ;
const template = '<div innerHtml="{{ctxProp}}"></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxProp = 'Some <span>HTML</span>' ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
expect ( getDOM ( ) . getInnerHTML ( fixture . debugElement . children [ 0 ] . nativeElement ) )
. toEqual ( 'Some <span>HTML</span>' ) ;
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxProp = 'Some other <div>HTML</div>' ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
expect ( getDOM ( ) . getInnerHTML ( fixture . debugElement . children [ 0 ] . nativeElement ) )
. toEqual ( 'Some other <div>HTML</div>' ) ;
} ) ;
2015-05-21 19:30:07 -04:00
2016-08-17 10:15:35 -04:00
it ( 'should consume binding to className using class alias' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp ] } ) ;
const template = '<div class="initial" [class]="ctxProp"></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2015-05-21 19:30:07 -04:00
2016-11-12 08:08:58 -05:00
const nativeEl = fixture . debugElement . children [ 0 ] . nativeElement ;
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxProp = 'foo bar' ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
2015-05-21 19:30:07 -04:00
2016-08-17 10:15:35 -04:00
expect ( nativeEl ) . toHaveCssClass ( 'foo' ) ;
expect ( nativeEl ) . toHaveCssClass ( 'bar' ) ;
expect ( nativeEl ) . not . toHaveCssClass ( 'initial' ) ;
} ) ;
2015-05-21 19:30:07 -04:00
2016-11-09 18:21:27 -05:00
it ( 'should consume binding to htmlFor using for alias' , ( ) = > {
const template = '<label [for]="ctxProp"></label>' ;
const fixture = TestBed . configureTestingModule ( { declarations : [ MyComp ] } )
. overrideComponent ( MyComp , { set : { template } } )
. createComponent ( MyComp ) ;
const nativeEl = fixture . debugElement . children [ 0 ] . nativeElement ;
fixture . debugElement . componentInstance . ctxProp = 'foo' ;
fixture . detectChanges ( ) ;
2017-07-19 16:58:23 -04:00
expect ( getDOM ( ) . getProperty ( nativeEl , 'htmlFor' ) ) . toBe ( 'foo' ) ;
2016-11-09 18:21:27 -05:00
} ) ;
2016-08-17 10:15:35 -04:00
it ( 'should consume directive watch expression change.' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , MyDir ] } ) ;
const template = '<span>' +
'<div my-dir [elprop]="ctxProp"></div>' +
'<div my-dir elprop="Hi there!"></div>' +
'<div my-dir elprop="Hi {{\'there!\'}}"></div>' +
'<div my-dir elprop="One more {{ctxProp}}"></div>' +
'</span>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxProp = 'Hello World!' ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
2016-11-12 08:08:58 -05:00
const containerSpan = fixture . debugElement . children [ 0 ] ;
2016-08-17 10:15:35 -04:00
expect ( containerSpan . children [ 0 ] . injector . get ( MyDir ) . dirProp ) . toEqual ( 'Hello World!' ) ;
expect ( containerSpan . children [ 1 ] . injector . get ( MyDir ) . dirProp ) . toEqual ( 'Hi there!' ) ;
expect ( containerSpan . children [ 2 ] . injector . get ( MyDir ) . dirProp ) . toEqual ( 'Hi there!' ) ;
expect ( containerSpan . children [ 3 ] . injector . get ( MyDir ) . dirProp )
. toEqual ( 'One more Hello World!' ) ;
} ) ;
2015-05-21 19:30:07 -04:00
2016-08-17 10:15:35 -04:00
describe ( 'pipes' , ( ) = > {
2017-02-09 17:59:57 -05:00
it ( 'should support pipes in bindings' , ( ) = > {
2016-08-17 10:15:35 -04:00
TestBed . configureTestingModule ( { declarations : [ MyComp , MyDir , DoublePipe ] } ) ;
const template = '<div my-dir #dir="mydir" [elprop]="ctxProp | double"></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2015-05-21 19:30:07 -04:00
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxProp = 'a' ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
2015-05-21 19:30:07 -04:00
2017-03-29 12:34:45 -04:00
const dir = fixture . debugElement . children [ 0 ] . references ! [ 'dir' ] ;
2016-08-17 10:15:35 -04:00
expect ( dir . dirProp ) . toEqual ( 'aa' ) ;
} ) ;
} ) ;
2015-05-21 19:30:07 -04:00
2016-08-17 10:15:35 -04:00
it ( 'should support nested components.' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , ChildComp ] } ) ;
const template = '<child-cmp></child-cmp>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2015-05-21 19:30:07 -04:00
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
2015-05-21 19:30:07 -04:00
2016-09-09 15:04:38 -04:00
expect ( fixture . nativeElement ) . toHaveText ( 'hello' ) ;
2016-08-17 10:15:35 -04:00
} ) ;
2015-05-21 19:30:07 -04:00
2016-08-17 10:15:35 -04:00
// GH issue 328 - https://github.com/angular/angular/issues/328
it ( 'should support different directive types on a single node' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , ChildComp , MyDir ] } ) ;
const template = '<child-cmp my-dir [elprop]="ctxProp"></child-cmp>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2015-05-21 19:30:07 -04:00
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxProp = 'Hello World!' ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
2015-05-21 19:30:07 -04:00
2016-11-12 08:08:58 -05:00
const tc = fixture . debugElement . children [ 0 ] ;
2015-05-21 19:30:07 -04:00
2016-08-17 10:15:35 -04:00
expect ( tc . injector . get ( MyDir ) . dirProp ) . toEqual ( 'Hello World!' ) ;
expect ( tc . injector . get ( ChildComp ) . dirProp ) . toEqual ( null ) ;
} ) ;
2015-08-12 04:44:46 -04:00
2016-08-17 10:15:35 -04:00
it ( 'should support directives where a binding attribute is not given' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , MyDir ] } ) ;
const template = '<p my-dir></p>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
} ) ;
2015-05-21 19:30:07 -04:00
2016-08-17 10:15:35 -04:00
it ( 'should execute a given directive once, even if specified multiple times' , ( ) = > {
TestBed . configureTestingModule (
{ declarations : [ MyComp , DuplicateDir , DuplicateDir , [ DuplicateDir , [ DuplicateDir ] ] ] } ) ;
const template = '<p no-duplicate></p>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-09-09 15:04:38 -04:00
expect ( fixture . nativeElement ) . toHaveText ( 'noduplicate' ) ;
2015-05-21 19:30:07 -04:00
} ) ;
2016-08-17 10:15:35 -04:00
it ( 'should support directives where a selector matches property binding' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , IdDir ] } ) ;
const template = '<p [id]="ctxProp"></p>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2015-05-21 19:30:07 -04:00
2016-11-12 08:08:58 -05:00
const tc = fixture . debugElement . children [ 0 ] ;
const idDir = tc . injector . get ( IdDir ) ;
2015-05-21 19:30:07 -04:00
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxProp = 'some_id' ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
expect ( idDir . id ) . toEqual ( 'some_id' ) ;
2015-05-21 19:30:07 -04:00
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxProp = 'other_id' ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
expect ( idDir . id ) . toEqual ( 'other_id' ) ;
} ) ;
2015-05-21 19:30:07 -04:00
2016-08-17 10:15:35 -04:00
it ( 'should support directives where a selector matches event binding' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , EventDir ] } ) ;
const template = '<p (customEvent)="doNothing()"></p>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2015-06-23 17:02:01 -04:00
2016-11-12 08:08:58 -05:00
const tc = fixture . debugElement . children [ 0 ] ;
2016-08-17 10:15:35 -04:00
expect ( tc . injector . get ( EventDir ) ) . not . toBe ( null ) ;
} ) ;
2015-05-21 19:30:07 -04:00
2016-08-17 10:15:35 -04:00
it ( 'should read directives metadata from their binding token' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , PrivateImpl , NeedsPublicApi ] } ) ;
const template = '<div public-api><div needs-public-api></div></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
} ) ;
2015-05-21 19:30:07 -04:00
2017-01-09 16:16:46 -05:00
it ( 'should support template directives via `<ng-template>` elements.' , ( ) = > {
2016-08-17 10:15:35 -04:00
TestBed . configureTestingModule ( { declarations : [ MyComp , SomeViewport ] } ) ;
const template =
2017-01-09 16:16:46 -05:00
'<ng-template some-viewport let-greeting="someTmpl"><span>{{greeting}}</span></ng-template>' ;
2016-08-17 10:15:35 -04:00
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2015-05-21 19:30:07 -04:00
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
2015-05-21 19:30:07 -04:00
2016-11-12 08:08:58 -05:00
const childNodesOfWrapper = getDOM ( ) . childNodes ( fixture . nativeElement ) ;
2016-08-17 10:15:35 -04:00
// 1 template + 2 copies.
expect ( childNodesOfWrapper . length ) . toBe ( 3 ) ;
expect ( childNodesOfWrapper [ 1 ] ) . toHaveText ( 'hello' ) ;
expect ( childNodesOfWrapper [ 2 ] ) . toHaveText ( 'again' ) ;
} ) ;
2015-05-21 19:30:07 -04:00
2016-08-17 10:15:35 -04:00
it ( 'should not share empty context for template directives - issue #10045' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , PollutedContext , NoContext ] } ) ;
const template =
2017-01-09 16:16:46 -05:00
'<ng-template pollutedContext let-foo="bar">{{foo}}</ng-template><ng-template noContext let-foo="bar">{{foo}}</ng-template>' ;
2016-08-17 10:15:35 -04:00
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2015-05-21 19:30:07 -04:00
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
2016-09-09 15:04:38 -04:00
expect ( fixture . nativeElement ) . toHaveText ( 'baz' ) ;
2016-08-17 10:15:35 -04:00
} ) ;
2015-06-16 14:16:08 -04:00
2016-08-17 10:15:35 -04:00
it ( 'should not detach views in ViewContainers when the parent view is destroyed.' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , SomeViewport ] } ) ;
const template =
2017-01-09 16:16:46 -05:00
'<div *ngIf="ctxBoolProp"><ng-template some-viewport let-greeting="someTmpl"><span>{{greeting}}</span></ng-template></div>' ;
2016-08-17 10:15:35 -04:00
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2015-05-21 19:30:07 -04:00
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxBoolProp = true ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
2016-02-03 14:35:42 -05:00
2016-11-12 08:08:58 -05:00
const ngIfEl = fixture . debugElement . children [ 0 ] ;
const someViewport : SomeViewport = ngIfEl . childNodes [ 0 ] . injector . get ( SomeViewport ) ;
2016-08-17 10:15:35 -04:00
expect ( someViewport . container . length ) . toBe ( 2 ) ;
expect ( ngIfEl . children . length ) . toBe ( 2 ) ;
2015-05-21 19:30:07 -04:00
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxBoolProp = false ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
2015-05-21 19:30:07 -04:00
2016-08-17 10:15:35 -04:00
expect ( someViewport . container . length ) . toBe ( 2 ) ;
expect ( fixture . debugElement . children . length ) . toBe ( 0 ) ;
} ) ;
2016-08-08 12:11:35 -04:00
2017-01-09 16:16:46 -05:00
it ( 'should use a comment while stamping out `<ng-template>` elements.' , ( ) = > {
const fixture =
TestBed . configureTestingModule ( { declarations : [ MyComp ] } )
. overrideComponent ( MyComp , { set : { template : '<ng-template></ng-template>' } } )
. createComponent ( MyComp ) ;
2016-05-04 14:53:12 -04:00
2016-11-12 08:08:58 -05:00
const childNodesOfWrapper = getDOM ( ) . childNodes ( fixture . nativeElement ) ;
2016-08-17 10:15:35 -04:00
expect ( childNodesOfWrapper . length ) . toBe ( 1 ) ;
expect ( getDOM ( ) . isCommentNode ( childNodesOfWrapper [ 0 ] ) ) . toBe ( true ) ;
} ) ;
2015-11-20 16:46:37 -05:00
2016-08-17 10:15:35 -04:00
it ( 'should support template directives via `template` attribute.' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , SomeViewport ] } ) ;
const template =
2016-08-23 13:52:40 -04:00
'<span template="some-viewport: let greeting=someTmpl">{{greeting}}</span>' ;
2016-08-17 10:15:35 -04:00
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2015-05-21 19:30:07 -04:00
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
2016-11-12 08:08:58 -05:00
const childNodesOfWrapper = getDOM ( ) . childNodes ( fixture . nativeElement ) ;
2016-08-17 10:15:35 -04:00
// 1 template + 2 copies.
expect ( childNodesOfWrapper . length ) . toBe ( 3 ) ;
expect ( childNodesOfWrapper [ 1 ] ) . toHaveText ( 'hello' ) ;
expect ( childNodesOfWrapper [ 2 ] ) . toHaveText ( 'again' ) ;
} ) ;
it ( 'should allow to transplant TemplateRefs into other ViewContainers' , ( ) = > {
2016-08-19 15:51:01 -04:00
TestBed . configureTestingModule ( {
declarations : [
MyComp , SomeDirective , CompWithHost , ToolbarComponent , ToolbarViewContainer , ToolbarPart
] ,
2016-08-23 13:52:40 -04:00
imports : [ CommonModule ] ,
schemas : [ NO_ERRORS_SCHEMA ] ,
2016-08-19 15:51:01 -04:00
} ) ;
2016-08-17 10:15:35 -04:00
const template =
2017-01-09 16:16:46 -05:00
'<some-directive><toolbar><ng-template toolbarpart let-toolbarProp="toolbarProp">{{ctxProp}},{{toolbarProp}},<cmp-with-host></cmp-with-host></ng-template></toolbar></some-directive>' ;
2016-08-17 10:15:35 -04:00
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxProp = 'From myComp' ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
2016-09-09 15:04:38 -04:00
expect ( fixture . nativeElement )
2016-08-17 10:15:35 -04:00
. toHaveText ( 'TOOLBAR(From myComp,From toolbar,Component with an injected host)' ) ;
} ) ;
2015-05-21 19:30:07 -04:00
2016-06-08 19:38:52 -04:00
describe ( 'reference bindings' , ( ) = > {
2016-08-17 10:15:35 -04:00
it ( 'should assign a component to a ref-' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , ChildComp ] } ) ;
const template = '<p><child-cmp ref-alice></child-cmp></p>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2017-03-29 12:34:45 -04:00
expect ( fixture . debugElement . children [ 0 ] . children [ 0 ] . references ! [ 'alice' ] )
2016-08-17 10:15:35 -04:00
. toBeAnInstanceOf ( ChildComp ) ;
} ) ;
it ( 'should assign a directive to a ref-' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , ExportDir ] } ) ;
const template = '<div><div export-dir #localdir="dir"></div></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2017-03-29 12:34:45 -04:00
expect ( fixture . debugElement . children [ 0 ] . children [ 0 ] . references ! [ 'localdir' ] )
2016-08-17 10:15:35 -04:00
. toBeAnInstanceOf ( ExportDir ) ;
} ) ;
2015-05-21 19:30:07 -04:00
2016-01-06 17:13:44 -05:00
it ( 'should make the assigned component accessible in property bindings, even if they were declared before the component' ,
2016-08-17 10:15:35 -04:00
( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , ChildComp ] } ) ;
const template =
2017-01-09 16:16:46 -05:00
'<ng-template [ngIf]="true">{{alice.ctxProp}}</ng-template>|{{alice.ctxProp}}|<child-cmp ref-alice></child-cmp>' ;
2016-08-17 10:15:35 -04:00
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
fixture . detectChanges ( ) ;
2016-09-09 15:04:38 -04:00
expect ( fixture . nativeElement ) . toHaveText ( 'hello|hello|hello' ) ;
2016-08-17 10:15:35 -04:00
} ) ;
it ( 'should assign two component instances each with a ref-' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , ChildComp ] } ) ;
const template =
'<p><child-cmp ref-alice></child-cmp><child-cmp ref-bob></child-cmp></p>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-11-12 08:08:58 -05:00
const pEl = fixture . debugElement . children [ 0 ] ;
2016-08-17 10:15:35 -04:00
2017-03-29 12:34:45 -04:00
const alice = pEl . children [ 0 ] . references ! [ 'alice' ] ;
const bob = pEl . children [ 1 ] . references ! [ 'bob' ] ;
2016-08-17 10:15:35 -04:00
expect ( alice ) . toBeAnInstanceOf ( ChildComp ) ;
expect ( bob ) . toBeAnInstanceOf ( ChildComp ) ;
expect ( alice ) . not . toBe ( bob ) ;
} ) ;
it ( 'should assign the component instance to a ref- with shorthand syntax' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , ChildComp ] } ) ;
const template = '<child-cmp #alice></child-cmp>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2017-03-29 12:34:45 -04:00
expect ( fixture . debugElement . children [ 0 ] . references ! [ 'alice' ] )
. toBeAnInstanceOf ( ChildComp ) ;
2016-08-17 10:15:35 -04:00
} ) ;
it ( 'should assign the element instance to a user-defined variable' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp ] } ) ;
const template = '<div><div ref-alice><i>Hello</i></div></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2017-03-29 12:34:45 -04:00
const value = fixture . debugElement . children [ 0 ] . children [ 0 ] . references ! [ 'alice' ] ;
2016-08-17 10:15:35 -04:00
expect ( value ) . not . toBe ( null ) ;
expect ( value . tagName . toLowerCase ( ) ) . toEqual ( 'div' ) ;
} ) ;
it ( 'should assign the TemplateRef to a user-defined variable' , ( ) = > {
2017-01-09 16:16:46 -05:00
const fixture =
TestBed . configureTestingModule ( { declarations : [ MyComp ] } )
2017-03-01 11:02:37 -05:00
. overrideComponent (
MyComp , { set : { template : '<ng-template ref-alice></ng-template>' } } )
2017-01-09 16:16:46 -05:00
. createComponent ( MyComp ) ;
2016-08-17 10:15:35 -04:00
2017-03-29 12:34:45 -04:00
const value = fixture . debugElement . childNodes [ 0 ] . references ! [ 'alice' ] ;
2017-02-02 18:01:35 -05:00
expect ( value . createEmbeddedView ) . toBeTruthy ( ) ;
2016-08-17 10:15:35 -04:00
} ) ;
it ( 'should preserve case' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , ChildComp ] } ) ;
const template = '<p><child-cmp ref-superAlice></child-cmp></p>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2017-03-29 12:34:45 -04:00
expect ( fixture . debugElement . children [ 0 ] . children [ 0 ] . references ! [ 'superAlice' ] )
2016-08-17 10:15:35 -04:00
. toBeAnInstanceOf ( ChildComp ) ;
} ) ;
2016-04-25 22:52:24 -04:00
} ) ;
2015-06-15 21:05:29 -04:00
2016-04-25 22:52:24 -04:00
describe ( 'variables' , ( ) = > {
2017-02-09 17:59:57 -05:00
it ( 'should allow to use variables in a for loop' , ( ) = > {
2016-08-17 10:15:35 -04:00
const template =
2017-01-09 16:16:46 -05:00
'<ng-template ngFor [ngForOf]="[1]" let-i><child-cmp-no-template #cmp></child-cmp-no-template>{{i}}-{{cmp.ctxProp}}</ng-template>' ;
const fixture =
TestBed . configureTestingModule ( { declarations : [ MyComp , ChildCompNoTemplate ] } )
. overrideComponent ( MyComp , { set : { template } } )
. createComponent ( MyComp ) ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
2017-01-09 16:16:46 -05:00
// Get the element at index 2, since index 0 is the <ng-template>.
2016-09-09 15:04:38 -04:00
expect ( getDOM ( ) . childNodes ( fixture . nativeElement ) [ 2 ] ) . toHaveText ( '1-hello' ) ;
2016-08-17 10:15:35 -04:00
} ) ;
2015-06-04 16:45:08 -04:00
} ) ;
2015-05-21 19:30:07 -04:00
2016-06-08 19:38:52 -04:00
describe ( 'OnPush components' , ( ) = > {
2016-01-06 17:13:44 -05:00
2016-08-17 10:15:35 -04:00
it ( 'should use ChangeDetectorRef to manually request a check' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , [ [ PushCmpWithRef ] ] ] } ) ;
const template = '<push-cmp-with-ref #cmp></push-cmp-with-ref>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2015-05-21 19:30:07 -04:00
2017-03-29 12:34:45 -04:00
const cmp = fixture . debugElement . children [ 0 ] . references ! [ 'cmp' ] ;
2015-05-21 19:30:07 -04:00
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
expect ( cmp . numberOfChecks ) . toEqual ( 1 ) ;
2015-05-21 19:30:07 -04:00
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
expect ( cmp . numberOfChecks ) . toEqual ( 1 ) ;
2015-05-21 19:30:07 -04:00
2016-08-17 10:15:35 -04:00
cmp . propagate ( ) ;
2015-05-21 19:30:07 -04:00
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
expect ( cmp . numberOfChecks ) . toEqual ( 2 ) ;
} ) ;
2015-05-21 19:30:07 -04:00
2016-08-17 10:15:35 -04:00
it ( 'should be checked when its bindings got updated' , ( ) = > {
2016-08-19 15:51:01 -04:00
TestBed . configureTestingModule (
{ declarations : [ MyComp , PushCmp , EventCmp ] , imports : [ CommonModule ] } ) ;
2016-08-17 10:15:35 -04:00
const template = '<push-cmp [prop]="ctxProp" #cmp></push-cmp>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2015-05-21 19:30:07 -04:00
2017-03-29 12:34:45 -04:00
const cmp = fixture . debugElement . children [ 0 ] . references ! [ 'cmp' ] ;
2015-05-21 19:30:07 -04:00
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxProp = 'one' ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
expect ( cmp . numberOfChecks ) . toEqual ( 1 ) ;
2015-05-21 19:30:07 -04:00
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxProp = 'two' ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
expect ( cmp . numberOfChecks ) . toEqual ( 2 ) ;
} ) ;
2015-05-21 19:30:07 -04:00
2016-04-28 20:50:03 -04:00
if ( getDOM ( ) . supportsDOMEvents ( ) ) {
2016-06-08 19:38:52 -04:00
it ( 'should allow to destroy a component from within a host event handler' ,
2016-08-17 10:15:35 -04:00
fakeAsync ( ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , [ [ PushCmpWithHostEvent ] ] ] } ) ;
const template = '<push-cmp-with-host-event></push-cmp-with-host-event>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-04-18 19:04:35 -04:00
tick ( ) ;
fixture . detectChanges ( ) ;
2016-02-17 15:12:10 -05:00
2016-11-12 08:08:58 -05:00
const cmpEl = fixture . debugElement . children [ 0 ] ;
const cmp : PushCmpWithHostEvent = cmpEl . injector . get ( PushCmpWithHostEvent ) ;
2016-06-14 17:53:01 -04:00
cmp . ctxCallback = ( _ : any ) = > fixture . destroy ( ) ;
2016-02-17 15:12:10 -05:00
2016-04-18 19:04:35 -04:00
expect ( ( ) = > cmpEl . triggerEventHandler ( 'click' , < Event > { } ) ) . not . toThrow ( ) ;
2016-08-17 10:15:35 -04:00
} ) ) ;
2016-02-17 15:12:10 -05:00
}
2016-08-17 10:15:35 -04:00
it ( 'should be checked when an event is fired' , ( ) = > {
2016-08-19 15:51:01 -04:00
TestBed . configureTestingModule (
{ declarations : [ MyComp , PushCmp , EventCmp ] , imports : [ CommonModule ] } ) ;
2016-08-17 10:15:35 -04:00
const template = '<push-cmp [prop]="ctxProp" #cmp></push-cmp>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-11-12 08:08:58 -05:00
const cmpEl = fixture . debugElement . children [ 0 ] ;
const cmp = cmpEl . componentInstance ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
fixture . detectChanges ( ) ;
expect ( cmp . numberOfChecks ) . toEqual ( 1 ) ;
// regular element
2017-03-21 13:44:11 -04:00
cmpEl . children [ 0 ] . triggerEventHandler ( 'click' , < Event > { } ) ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
fixture . detectChanges ( ) ;
expect ( cmp . numberOfChecks ) . toEqual ( 2 ) ;
// element inside of an *ngIf
cmpEl . children [ 1 ] . triggerEventHandler ( 'click' , < Event > { } ) ;
fixture . detectChanges ( ) ;
fixture . detectChanges ( ) ;
expect ( cmp . numberOfChecks ) . toEqual ( 3 ) ;
// element inside a nested component
cmpEl . children [ 2 ] . children [ 0 ] . triggerEventHandler ( 'click' , < Event > { } ) ;
fixture . detectChanges ( ) ;
fixture . detectChanges ( ) ;
expect ( cmp . numberOfChecks ) . toEqual ( 4 ) ;
2017-03-21 13:44:11 -04:00
// host element
cmpEl . triggerEventHandler ( 'click' , < Event > { } ) ;
fixture . detectChanges ( ) ;
fixture . detectChanges ( ) ;
expect ( cmp . numberOfChecks ) . toEqual ( 5 ) ;
2016-08-17 10:15:35 -04:00
} ) ;
it ( 'should not affect updating properties on the component' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , [ [ PushCmpWithRef ] ] ] } ) ;
const template = '<push-cmp-with-ref [prop]="ctxProp" #cmp></push-cmp-with-ref>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2017-03-29 12:34:45 -04:00
const cmp = fixture . debugElement . children [ 0 ] . references ! [ 'cmp' ] ;
2016-08-17 10:15:35 -04:00
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxProp = 'one' ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
expect ( cmp . prop ) . toEqual ( 'one' ) ;
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxProp = 'two' ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
expect ( cmp . prop ) . toEqual ( 'two' ) ;
} ) ;
2015-08-06 13:39:02 -04:00
2016-04-28 20:50:03 -04:00
if ( getDOM ( ) . supportsDOMEvents ( ) ) {
2017-02-09 17:59:57 -05:00
it ( 'should be checked when an async pipe requests a check' , fakeAsync ( ( ) = > {
TestBed . configureTestingModule (
{ declarations : [ MyComp , PushCmpWithAsyncPipe ] , imports : [ CommonModule ] } ) ;
const template = '<push-cmp-with-async #cmp></push-cmp-with-async>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
tick ( ) ;
2017-03-29 12:34:45 -04:00
const cmp : PushCmpWithAsyncPipe =
fixture . debugElement . children [ 0 ] . references ! [ 'cmp' ] ;
2017-02-09 17:59:57 -05:00
fixture . detectChanges ( ) ;
expect ( cmp . numberOfChecks ) . toEqual ( 1 ) ;
fixture . detectChanges ( ) ;
fixture . detectChanges ( ) ;
expect ( cmp . numberOfChecks ) . toEqual ( 1 ) ;
cmp . resolve ( 2 ) ;
tick ( ) ;
fixture . detectChanges ( ) ;
expect ( cmp . numberOfChecks ) . toEqual ( 2 ) ;
} ) ) ;
2015-08-06 13:39:02 -04:00
}
2015-05-21 19:30:07 -04:00
} ) ;
2016-08-17 10:15:35 -04:00
it ( 'should create a component that injects an @Host' , ( ) = > {
2016-08-23 13:52:40 -04:00
TestBed . configureTestingModule ( {
declarations : [ MyComp , SomeDirective , CompWithHost ] ,
schemas : [ NO_ERRORS_SCHEMA ] ,
} ) ;
2016-08-17 10:15:35 -04:00
const template = `
2015-05-21 19:30:07 -04:00
< some - directive >
< p >
2015-07-29 14:26:09 -04:00
< cmp - with - host # child > < / c m p - w i t h - h o s t >
2015-05-21 19:30:07 -04:00
< / p >
2016-08-17 10:15:35 -04:00
< / s o m e - d i r e c t i v e > ` ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2015-05-21 19:30:07 -04:00
2016-11-12 08:08:58 -05:00
const childComponent =
2017-03-29 12:34:45 -04:00
fixture . debugElement . children [ 0 ] . children [ 0 ] . children [ 0 ] . references ! [ 'child' ] ;
2016-08-17 10:15:35 -04:00
expect ( childComponent . myHost ) . toBeAnInstanceOf ( SomeDirective ) ;
} ) ;
2015-05-21 19:30:07 -04:00
2016-08-17 10:15:35 -04:00
it ( 'should create a component that injects an @Host through viewcontainer directive' , ( ) = > {
2016-08-23 13:52:40 -04:00
TestBed . configureTestingModule ( {
declarations : [ MyComp , SomeDirective , CompWithHost ] ,
schemas : [ NO_ERRORS_SCHEMA ] ,
} ) ;
2016-08-17 10:15:35 -04:00
const template = `
2015-05-21 19:30:07 -04:00
< some - directive >
2015-11-23 19:02:19 -05:00
< p * ngIf = "true" >
2015-07-29 14:26:09 -04:00
< cmp - with - host # child > < / c m p - w i t h - h o s t >
2015-05-21 19:30:07 -04:00
< / p >
2016-08-17 10:15:35 -04:00
< / s o m e - d i r e c t i v e > ` ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2015-06-23 17:02:01 -04:00
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
2015-05-21 19:30:07 -04:00
2016-11-12 08:08:58 -05:00
const tc = fixture . debugElement . children [ 0 ] . children [ 0 ] . children [ 0 ] ;
2015-05-21 19:30:07 -04:00
2017-03-29 12:34:45 -04:00
const childComponent = tc . references ! [ 'child' ] ;
2016-08-17 10:15:35 -04:00
expect ( childComponent . myHost ) . toBeAnInstanceOf ( SomeDirective ) ;
} ) ;
2015-05-21 19:30:07 -04:00
2016-08-19 15:51:01 -04:00
it ( 'should support events via EventEmitter on regular elements' , async ( ( ) = > {
TestBed . configureTestingModule (
{ declarations : [ MyComp , DirectiveEmittingEvent , DirectiveListeningEvent ] } ) ;
const template = '<div emitter listener></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-08-17 10:15:35 -04:00
2016-11-12 08:08:58 -05:00
const tc = fixture . debugElement . children [ 0 ] ;
const emitter = tc . injector . get ( DirectiveEmittingEvent ) ;
const listener = tc . injector . get ( DirectiveListeningEvent ) ;
2016-08-19 15:51:01 -04:00
expect ( listener . msg ) . toEqual ( '' ) ;
2016-11-12 08:08:58 -05:00
let eventCount = 0 ;
2016-08-19 15:51:01 -04:00
emitter . event . subscribe ( {
next : ( ) = > {
eventCount ++ ;
if ( eventCount === 1 ) {
expect ( listener . msg ) . toEqual ( 'fired !' ) ;
fixture . destroy ( ) ;
emitter . fireEvent ( 'fired again !' ) ;
} else {
expect ( listener . msg ) . toEqual ( 'fired !' ) ;
}
}
} ) ;
2016-08-17 10:15:35 -04:00
2016-08-19 15:51:01 -04:00
emitter . fireEvent ( 'fired !' ) ;
} ) ) ;
2015-05-21 19:30:07 -04:00
2016-08-19 15:51:01 -04:00
it ( 'should support events via EventEmitter on template elements' , async ( ( ) = > {
2017-01-09 16:16:46 -05:00
const fixture =
TestBed
. configureTestingModule (
{ declarations : [ MyComp , DirectiveEmittingEvent , DirectiveListeningEvent ] } )
. overrideComponent ( MyComp , {
set : {
template :
'<ng-template emitter listener (event)="ctxProp=$event"></ng-template>'
}
} )
. createComponent ( MyComp ) ;
2015-10-13 20:43:15 -04:00
2016-11-12 08:08:58 -05:00
const tc = fixture . debugElement . childNodes [ 0 ] ;
2015-05-21 19:30:07 -04:00
2016-11-12 08:08:58 -05:00
const emitter = tc . injector . get ( DirectiveEmittingEvent ) ;
const myComp = fixture . debugElement . injector . get ( MyComp ) ;
const listener = tc . injector . get ( DirectiveListeningEvent ) ;
2015-05-21 19:30:07 -04:00
2016-08-19 15:51:01 -04:00
myComp . ctxProp = '' ;
expect ( listener . msg ) . toEqual ( '' ) ;
2015-05-21 19:30:07 -04:00
2016-08-19 15:51:01 -04:00
emitter . event . subscribe ( {
next : ( ) = > {
expect ( listener . msg ) . toEqual ( 'fired !' ) ;
expect ( myComp . ctxProp ) . toEqual ( 'fired !' ) ;
}
} ) ;
2015-05-21 19:30:07 -04:00
2016-08-19 15:51:01 -04:00
emitter . fireEvent ( 'fired !' ) ;
} ) ) ;
2015-05-21 19:30:07 -04:00
2016-08-19 15:51:01 -04:00
it ( 'should support [()] syntax' , async ( ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , DirectiveWithTwoWayBinding ] } ) ;
const template = '<div [(control)]="ctxProp" two-way></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-11-12 08:08:58 -05:00
const tc = fixture . debugElement . children [ 0 ] ;
const dir = tc . injector . get ( DirectiveWithTwoWayBinding ) ;
2015-05-21 19:30:07 -04:00
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxProp = 'one' ;
2016-08-19 15:51:01 -04:00
fixture . detectChanges ( ) ;
2015-05-21 19:30:07 -04:00
2016-08-19 15:51:01 -04:00
expect ( dir . control ) . toEqual ( 'one' ) ;
2015-05-21 19:30:07 -04:00
2016-09-09 15:04:38 -04:00
dir . controlChange . subscribe (
{ next : ( ) = > { expect ( fixture . componentInstance . ctxProp ) . toEqual ( 'two' ) ; } } ) ;
2015-05-21 19:30:07 -04:00
2016-08-19 15:51:01 -04:00
dir . triggerChange ( 'two' ) ;
} ) ) ;
2016-08-17 10:15:35 -04:00
it ( 'should support render events' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , DirectiveListeningDomEvent ] } ) ;
const template = '<div listener></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2015-05-21 19:30:07 -04:00
2016-11-12 08:08:58 -05:00
const tc = fixture . debugElement . children [ 0 ] ;
const listener = tc . injector . get ( DirectiveListeningDomEvent ) ;
2016-08-17 10:15:35 -04:00
dispatchEvent ( tc . nativeElement , 'domEvent' ) ;
expect ( listener . eventTypes ) . toEqual ( [
'domEvent' , 'body_domEvent' , 'document_domEvent' , 'window_domEvent'
] ) ;
fixture . destroy ( ) ;
listener . eventTypes = [ ] ;
dispatchEvent ( tc . nativeElement , 'domEvent' ) ;
expect ( listener . eventTypes ) . toEqual ( [ ] ) ;
} ) ;
it ( 'should support render global events' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , DirectiveListeningDomEvent ] } ) ;
const template = '<div listener></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2017-02-14 19:14:40 -05:00
const doc = TestBed . get ( DOCUMENT ) ;
2016-08-17 10:15:35 -04:00
2016-11-12 08:08:58 -05:00
const tc = fixture . debugElement . children [ 0 ] ;
const listener = tc . injector . get ( DirectiveListeningDomEvent ) ;
2017-02-14 19:14:40 -05:00
dispatchEvent ( getDOM ( ) . getGlobalEventTarget ( doc , 'window' ) , 'domEvent' ) ;
2016-08-17 10:15:35 -04:00
expect ( listener . eventTypes ) . toEqual ( [ 'window_domEvent' ] ) ;
listener . eventTypes = [ ] ;
2017-02-14 19:14:40 -05:00
dispatchEvent ( getDOM ( ) . getGlobalEventTarget ( doc , 'document' ) , 'domEvent' ) ;
2016-08-17 10:15:35 -04:00
expect ( listener . eventTypes ) . toEqual ( [ 'document_domEvent' , 'window_domEvent' ] ) ;
fixture . destroy ( ) ;
listener . eventTypes = [ ] ;
2017-02-14 19:14:40 -05:00
dispatchEvent ( getDOM ( ) . getGlobalEventTarget ( doc , 'body' ) , 'domEvent' ) ;
2016-08-17 10:15:35 -04:00
expect ( listener . eventTypes ) . toEqual ( [ ] ) ;
} ) ;
2016-11-08 18:46:55 -05:00
it ( 'should support updating host element via hostAttributes on root elements' , ( ) = > {
@Component ( { host : { 'role' : 'button' } , template : '' } )
class ComponentUpdatingHostAttributes {
}
TestBed . configureTestingModule ( { declarations : [ ComponentUpdatingHostAttributes ] } ) ;
const fixture = TestBed . createComponent ( ComponentUpdatingHostAttributes ) ;
fixture . detectChanges ( ) ;
expect ( getDOM ( ) . getAttribute ( fixture . debugElement . nativeElement , 'role' ) ) . toEqual ( 'button' ) ;
} ) ;
it ( 'should support updating host element via hostAttributes on host elements' , ( ) = > {
2016-08-17 10:15:35 -04:00
TestBed . configureTestingModule ( { declarations : [ MyComp , DirectiveUpdatingHostAttributes ] } ) ;
const template = '<div update-host-attributes></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
fixture . detectChanges ( ) ;
expect ( getDOM ( ) . getAttribute ( fixture . debugElement . children [ 0 ] . nativeElement , 'role' ) )
. toEqual ( 'button' ) ;
} ) ;
it ( 'should support updating host element via hostProperties' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , DirectiveUpdatingHostProperties ] } ) ;
const template = '<div update-host-properties></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-11-12 08:08:58 -05:00
const tc = fixture . debugElement . children [ 0 ] ;
const updateHost = tc . injector . get ( DirectiveUpdatingHostProperties ) ;
2016-08-17 10:15:35 -04:00
updateHost . id = 'newId' ;
fixture . detectChanges ( ) ;
2017-07-19 16:58:23 -04:00
expect ( getDOM ( ) . getProperty ( tc . nativeElement , 'id' ) ) . toEqual ( 'newId' ) ;
2016-08-17 10:15:35 -04:00
} ) ;
2015-05-21 19:30:07 -04:00
2017-02-09 17:59:57 -05:00
it ( 'should not use template variables for expressions in hostProperties' , ( ) = > {
@Directive ( { selector : '[host-properties]' , host : { '[id]' : 'id' , '[title]' : 'unknownProp' } } )
class DirectiveWithHostProps {
id = 'one' ;
}
2017-02-02 18:01:35 -05:00
2017-02-09 17:59:57 -05:00
const fixture =
TestBed . configureTestingModule ( { declarations : [ MyComp , DirectiveWithHostProps ] } )
. overrideComponent (
MyComp ,
{ set : { template : ` <div *ngFor="let id of ['forId']" host-properties></div> ` } } )
. createComponent ( MyComp ) ;
fixture . detectChanges ( ) ;
2017-02-02 18:01:35 -05:00
2017-02-09 17:59:57 -05:00
const tc = fixture . debugElement . children [ 0 ] ;
expect ( tc . properties [ 'id' ] ) . toBe ( 'one' ) ;
expect ( tc . properties [ 'title' ] ) . toBe ( undefined ) ;
} ) ;
2016-10-20 18:24:58 -04:00
it ( 'should not allow pipes in hostProperties' , ( ) = > {
@Directive ( { selector : '[host-properties]' , host : { '[id]' : 'id | uppercase' } } )
class DirectiveWithHostProps {
}
TestBed . configureTestingModule ( { declarations : [ MyComp , DirectiveWithHostProps ] } ) ;
const template = '<div host-properties></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
expect ( ( ) = > TestBed . createComponent ( MyComp ) )
. toThrowError ( /Host binding expression cannot contain pipes/ ) ;
} ) ;
2017-02-09 17:59:57 -05:00
it ( 'should not use template variables for expressions in hostListeners' , ( ) = > {
2016-10-20 18:24:58 -04:00
@Directive ( { selector : '[host-listener]' , host : { '(click)' : 'doIt(id, unknownProp)' } } )
class DirectiveWithHostListener {
id = 'one' ;
receivedArgs : any [ ] ;
doIt ( . . . args : any [ ] ) { this . receivedArgs = args ; }
}
const fixture =
TestBed . configureTestingModule ( { declarations : [ MyComp , DirectiveWithHostListener ] } )
. overrideComponent (
MyComp ,
{ set : { template : ` <div *ngFor="let id of ['forId']" host-listener></div> ` } } )
. createComponent ( MyComp ) ;
fixture . detectChanges ( ) ;
2016-11-12 08:08:58 -05:00
const tc = fixture . debugElement . children [ 0 ] ;
2016-10-20 18:24:58 -04:00
tc . triggerEventHandler ( 'click' , { } ) ;
2016-11-12 08:08:58 -05:00
const dir : DirectiveWithHostListener = tc . injector . get ( DirectiveWithHostListener ) ;
2016-10-20 18:24:58 -04:00
expect ( dir . receivedArgs ) . toEqual ( [ 'one' , undefined ] ) ;
} ) ;
it ( 'should not allow pipes in hostListeners' , ( ) = > {
@Directive ( { selector : '[host-listener]' , host : { '(click)' : 'doIt() | somePipe' } } )
class DirectiveWithHostListener {
}
TestBed . configureTestingModule ( { declarations : [ MyComp , DirectiveWithHostListener ] } ) ;
const template = '<div host-listener></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
expect ( ( ) = > TestBed . createComponent ( MyComp ) )
. toThrowError ( /Cannot have a pipe in an action expression/ ) ;
} ) ;
2016-04-28 20:50:03 -04:00
if ( getDOM ( ) . supportsDOMEvents ( ) ) {
2016-08-17 10:15:35 -04:00
it ( 'should support preventing default on render events' , ( ) = > {
TestBed . configureTestingModule ( {
declarations :
[ MyComp , DirectiveListeningDomEventPrevent , DirectiveListeningDomEventNoPrevent ]
} ) ;
const template =
'<input type="checkbox" listenerprevent><input type="checkbox" listenernoprevent>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-11-12 08:08:58 -05:00
const dispatchedEvent = getDOM ( ) . createMouseEvent ( 'click' ) ;
const dispatchedEvent2 = getDOM ( ) . createMouseEvent ( 'click' ) ;
2016-08-17 10:15:35 -04:00
getDOM ( ) . dispatchEvent ( fixture . debugElement . children [ 0 ] . nativeElement , dispatchedEvent ) ;
getDOM ( ) . dispatchEvent ( fixture . debugElement . children [ 1 ] . nativeElement , dispatchedEvent2 ) ;
expect ( getDOM ( ) . isPrevented ( dispatchedEvent ) ) . toBe ( true ) ;
expect ( getDOM ( ) . isPrevented ( dispatchedEvent2 ) ) . toBe ( false ) ;
expect ( getDOM ( ) . getChecked ( fixture . debugElement . children [ 0 ] . nativeElement ) ) . toBeFalsy ( ) ;
expect ( getDOM ( ) . getChecked ( fixture . debugElement . children [ 1 ] . nativeElement ) ) . toBeTruthy ( ) ;
} ) ;
2015-05-21 19:30:07 -04:00
}
2016-08-17 10:15:35 -04:00
it ( 'should support render global events from multiple directives' , ( ) = > {
TestBed . configureTestingModule (
{ declarations : [ MyComp , DirectiveListeningDomEvent , DirectiveListeningDomEventOther ] } ) ;
const template = '<div *ngIf="ctxBoolProp" listener listenerother></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2017-02-14 19:14:40 -05:00
const doc = TestBed . get ( DOCUMENT ) ;
2016-08-17 10:15:35 -04:00
globalCounter = 0 ;
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxBoolProp = true ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
2016-11-12 08:08:58 -05:00
const tc = fixture . debugElement . children [ 0 ] ;
2016-08-17 10:15:35 -04:00
2016-11-12 08:08:58 -05:00
const listener = tc . injector . get ( DirectiveListeningDomEvent ) ;
const listenerother = tc . injector . get ( DirectiveListeningDomEventOther ) ;
2017-02-14 19:14:40 -05:00
dispatchEvent ( getDOM ( ) . getGlobalEventTarget ( doc , 'window' ) , 'domEvent' ) ;
2016-08-17 10:15:35 -04:00
expect ( listener . eventTypes ) . toEqual ( [ 'window_domEvent' ] ) ;
expect ( listenerother . eventType ) . toEqual ( 'other_domEvent' ) ;
expect ( globalCounter ) . toEqual ( 1 ) ;
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxBoolProp = false ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
2017-02-14 19:14:40 -05:00
dispatchEvent ( getDOM ( ) . getGlobalEventTarget ( doc , 'window' ) , 'domEvent' ) ;
2016-08-17 10:15:35 -04:00
expect ( globalCounter ) . toEqual ( 1 ) ;
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxBoolProp = true ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
2017-02-14 19:14:40 -05:00
dispatchEvent ( getDOM ( ) . getGlobalEventTarget ( doc , 'window' ) , 'domEvent' ) ;
2016-08-17 10:15:35 -04:00
expect ( globalCounter ) . toEqual ( 2 ) ;
// need to destroy to release all remaining global event listeners
fixture . destroy ( ) ;
} ) ;
2015-05-21 19:30:07 -04:00
2017-03-21 11:15:10 -04:00
describe ( 'ViewContainerRef.createComponent' , ( ) = > {
2016-08-16 19:48:32 -04:00
beforeEach ( ( ) = > {
// we need a module to declarate ChildCompUsingService as an entryComponent otherwise the
// factory doesn't get created
@NgModule ( {
2016-08-17 10:15:35 -04:00
declarations : [ MyComp , DynamicViewport , ChildCompUsingService ] ,
2016-08-23 13:52:40 -04:00
entryComponents : [ ChildCompUsingService ] ,
schemas : [ NO_ERRORS_SCHEMA ] ,
2016-08-16 19:48:32 -04:00
} )
class MyModule {
}
TestBed . configureTestingModule ( { imports : [ MyModule ] } ) ;
TestBed . overrideComponent (
MyComp , { add : { template : '<div><dynamic-vp #dynamic></dynamic-vp></div>' } } ) ;
} ) ;
2017-03-21 11:15:10 -04:00
it ( 'should allow to create a component at any bound location' , async ( ( ) = > {
2016-11-12 08:08:58 -05:00
const fixture = TestBed . configureTestingModule ( { schemas : [ NO_ERRORS_SCHEMA ] } )
. createComponent ( MyComp ) ;
const tc = fixture . debugElement . children [ 0 ] . children [ 0 ] ;
const dynamicVp : DynamicViewport = tc . injector . get ( DynamicViewport ) ;
2016-11-01 17:21:40 -04:00
dynamicVp . create ( ) ;
fixture . detectChanges ( ) ;
expect ( fixture . debugElement . children [ 0 ] . children [ 1 ] . nativeElement )
. toHaveText ( 'dynamic greet' ) ;
} ) ) ;
2017-03-21 11:15:10 -04:00
it ( 'should allow to create multiple components at a location' , async ( ( ) = > {
2016-11-12 08:08:58 -05:00
const fixture = TestBed . configureTestingModule ( { schemas : [ NO_ERRORS_SCHEMA ] } )
. createComponent ( MyComp ) ;
const tc = fixture . debugElement . children [ 0 ] . children [ 0 ] ;
const dynamicVp : DynamicViewport = tc . injector . get ( DynamicViewport ) ;
2016-11-01 17:21:40 -04:00
dynamicVp . create ( ) ;
dynamicVp . create ( ) ;
fixture . detectChanges ( ) ;
expect ( fixture . debugElement . children [ 0 ] . children [ 1 ] . nativeElement )
. toHaveText ( 'dynamic greet' ) ;
expect ( fixture . debugElement . children [ 0 ] . children [ 2 ] . nativeElement )
. toHaveText ( 'dynamic greet' ) ;
2016-08-16 19:48:32 -04:00
} ) ) ;
2017-03-21 11:15:10 -04:00
it ( 'should create a component that has been freshly compiled' , ( ) = > {
@Component ( { template : '' } )
class RootComp {
constructor ( public vc : ViewContainerRef ) { }
}
@NgModule ( {
declarations : [ RootComp ] ,
providers : [ { provide : 'someToken' , useValue : 'someRootValue' } ] ,
} )
class RootModule {
}
@Component ( { template : '' } )
class MyComp {
constructor ( @Inject ( 'someToken' ) public someToken : string ) { }
}
@NgModule ( {
declarations : [ MyComp ] ,
providers : [ { provide : 'someToken' , useValue : 'someValue' } ] ,
} )
class MyModule {
}
const compFixture =
TestBed . configureTestingModule ( { imports : [ RootModule ] } ) . createComponent ( RootComp ) ;
const compiler = < Compiler > TestBed . get ( Compiler ) ;
const myCompFactory =
< ComponentFactory < MyComp > > compiler . compileModuleAndAllComponentsSync ( MyModule )
. componentFactories [ 0 ] ;
// Note: the ComponentFactory was created directly via the compiler, i.e. it
// does not have an association to an NgModuleRef.
// -> expect the providers of the module that the view container belongs to.
const compRef = compFixture . componentInstance . vc . createComponent ( myCompFactory ) ;
expect ( compRef . instance . someToken ) . toBe ( 'someRootValue' ) ;
} ) ;
it ( 'should create a component with the passed NgModuleRef' , ( ) = > {
@Component ( { template : '' } )
class RootComp {
constructor ( public vc : ViewContainerRef ) { }
}
@Component ( { template : '' } )
class MyComp {
constructor ( @Inject ( 'someToken' ) public someToken : string ) { }
}
@NgModule ( {
declarations : [ RootComp , MyComp ] ,
entryComponents : [ MyComp ] ,
providers : [ { provide : 'someToken' , useValue : 'someRootValue' } ] ,
} )
class RootModule {
}
@NgModule ( { providers : [ { provide : 'someToken' , useValue : 'someValue' } ] } )
class MyModule {
}
const compFixture =
TestBed . configureTestingModule ( { imports : [ RootModule ] } ) . createComponent ( RootComp ) ;
const compiler = < Compiler > TestBed . get ( Compiler ) ;
const myModule = compiler . compileModuleSync ( MyModule ) . create ( TestBed . get ( NgModuleRef ) ) ;
const myCompFactory = ( < ComponentFactoryResolver > TestBed . get ( ComponentFactoryResolver ) )
. resolveComponentFactory ( MyComp ) ;
// Note: MyComp was declared as entryComponent in the RootModule,
// but we pass MyModule to the createComponent call.
// -> expect the providers of MyModule!
const compRef = compFixture . componentInstance . vc . createComponent (
myCompFactory , undefined , undefined , undefined , myModule ) ;
expect ( compRef . instance . someToken ) . toBe ( 'someValue' ) ;
} ) ;
it ( 'should create a component with the NgModuleRef of the ComponentFactoryResolver' , ( ) = > {
@Component ( { template : '' } )
class RootComp {
constructor ( public vc : ViewContainerRef ) { }
}
@NgModule ( {
declarations : [ RootComp ] ,
providers : [ { provide : 'someToken' , useValue : 'someRootValue' } ] ,
} )
class RootModule {
}
@Component ( { template : '' } )
class MyComp {
constructor ( @Inject ( 'someToken' ) public someToken : string ) { }
}
@NgModule ( {
declarations : [ MyComp ] ,
entryComponents : [ MyComp ] ,
providers : [ { provide : 'someToken' , useValue : 'someValue' } ] ,
} )
class MyModule {
}
const compFixture =
TestBed . configureTestingModule ( { imports : [ RootModule ] } ) . createComponent ( RootComp ) ;
const compiler = < Compiler > TestBed . get ( Compiler ) ;
const myModule = compiler . compileModuleSync ( MyModule ) . create ( TestBed . get ( NgModuleRef ) ) ;
const myCompFactory = myModule . componentFactoryResolver . resolveComponentFactory ( MyComp ) ;
// Note: MyComp was declared as entryComponent in MyModule,
// and we don't pass an explicit ModuleRef to the createComponent call.
// -> expect the providers of MyModule!
const compRef = compFixture . componentInstance . vc . createComponent ( myCompFactory ) ;
expect ( compRef . instance . someToken ) . toBe ( 'someValue' ) ;
} ) ;
2015-05-21 19:30:07 -04:00
} ) ;
2016-08-17 10:15:35 -04:00
it ( 'should support static attributes' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , NeedsAttribute ] } ) ;
const template = '<input static type="text" title>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-11-12 08:08:58 -05:00
const tc = fixture . debugElement . children [ 0 ] ;
const needsAttribute = tc . injector . get ( NeedsAttribute ) ;
2016-08-17 10:15:35 -04:00
expect ( needsAttribute . typeAttribute ) . toEqual ( 'text' ) ;
expect ( needsAttribute . staticAttribute ) . toEqual ( '' ) ;
expect ( needsAttribute . fooAttribute ) . toEqual ( null ) ;
} ) ;
2016-06-20 12:52:41 -04:00
2016-08-17 10:15:35 -04:00
it ( 'should support custom interpolation' , ( ) = > {
TestBed . configureTestingModule ( {
2016-08-19 15:51:01 -04:00
declarations : [
MyComp , ComponentWithCustomInterpolationA , ComponentWithCustomInterpolationB ,
ComponentWithDefaultInterpolation
]
2016-08-17 10:15:35 -04:00
} ) ;
const template = ` <div>{{ctxProp}}</div>
2016-06-20 12:52:41 -04:00
< cmp - with - custom - interpolation - a > < / c m p - w i t h - c u s t o m - i n t e r p o l a t i o n - a >
2016-08-17 10:15:35 -04:00
< cmp - with - custom - interpolation - b > < / c m p - w i t h - c u s t o m - i n t e r p o l a t i o n - b > ` ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxProp = 'Default Interpolation' ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
2016-09-09 15:04:38 -04:00
expect ( fixture . nativeElement )
2016-08-17 10:15:35 -04:00
. toHaveText (
'Default Interpolation\nCustom Interpolation A\nCustom Interpolation B (Default Interpolation)' ) ;
} ) ;
2015-05-21 19:30:07 -04:00
} ) ;
2016-06-08 19:38:52 -04:00
describe ( 'dependency injection' , ( ) = > {
2016-08-17 10:15:35 -04:00
it ( 'should support bindings' , ( ) = > {
2016-08-23 13:52:40 -04:00
TestBed . configureTestingModule ( {
declarations : [ MyComp , DirectiveProvidingInjectable , DirectiveConsumingInjectable ] ,
schemas : [ NO_ERRORS_SCHEMA ] ,
} ) ;
2016-08-17 10:15:35 -04:00
const template = `
2015-07-09 14:40:09 -04:00
< directive - providing - injectable >
2015-05-21 19:30:07 -04:00
< directive - consuming - injectable # consuming >
< / d i r e c t i v e - c o n s u m i n g - i n j e c t a b l e >
< / d i r e c t i v e - p r o v i d i n g - i n j e c t a b l e >
2016-08-17 10:15:35 -04:00
` ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2017-03-29 12:34:45 -04:00
const comp = fixture . debugElement . children [ 0 ] . children [ 0 ] . references ! [ 'consuming' ] ;
2016-08-17 10:15:35 -04:00
expect ( comp . injectable ) . toBeAnInstanceOf ( InjectableService ) ;
} ) ;
2015-05-21 19:30:07 -04:00
2016-08-17 10:15:35 -04:00
it ( 'should support viewProviders' , ( ) = > {
TestBed . configureTestingModule ( {
2016-08-23 13:52:40 -04:00
declarations : [ MyComp , DirectiveProvidingInjectableInView , DirectiveConsumingInjectable ] ,
schemas : [ NO_ERRORS_SCHEMA ] ,
2016-08-17 10:15:35 -04:00
} ) ;
const template = `
2015-05-21 19:30:07 -04:00
< directive - consuming - injectable # consuming >
< / d i r e c t i v e - c o n s u m i n g - i n j e c t a b l e >
2016-08-17 10:15:35 -04:00
` ;
TestBed . overrideComponent ( DirectiveProvidingInjectableInView , { set : { template } } ) ;
const fixture = TestBed . createComponent ( DirectiveProvidingInjectableInView ) ;
2015-05-21 19:30:07 -04:00
2017-03-29 12:34:45 -04:00
const comp = fixture . debugElement . children [ 0 ] . references ! [ 'consuming' ] ;
2016-08-17 10:15:35 -04:00
expect ( comp . injectable ) . toBeAnInstanceOf ( InjectableService ) ;
} ) ;
it ( 'should support unbounded lookup' , ( ) = > {
TestBed . configureTestingModule ( {
declarations : [
MyComp , DirectiveProvidingInjectable , DirectiveContainingDirectiveConsumingAnInjectable ,
DirectiveConsumingInjectableUnbounded
2016-08-23 13:52:40 -04:00
] ,
schemas : [ NO_ERRORS_SCHEMA ] ,
2016-08-17 10:15:35 -04:00
} ) ;
const template = `
2015-05-21 19:30:07 -04:00
< directive - providing - injectable >
< directive - containing - directive - consuming - an - injectable # dir >
< / d i r e c t i v e - c o n t a i n i n g - d i r e c t i v e - c o n s u m i n g - a n - i n j e c t a b l e >
< / d i r e c t i v e - p r o v i d i n g - i n j e c t a b l e >
2016-08-17 10:15:35 -04:00
` ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
TestBed . overrideComponent ( DirectiveContainingDirectiveConsumingAnInjectable , {
set : {
template : `
2015-05-21 19:30:07 -04:00
< directive - consuming - injectable - unbounded > < / d i r e c t i v e - c o n s u m i n g - i n j e c t a b l e - u n b o u n d e d >
2016-08-17 10:15:35 -04:00
`
}
} ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2015-05-21 19:30:07 -04:00
2017-03-29 12:34:45 -04:00
const comp = fixture . debugElement . children [ 0 ] . children [ 0 ] . references ! [ 'dir' ] ;
2016-08-17 10:15:35 -04:00
expect ( comp . directive . injectable ) . toBeAnInstanceOf ( InjectableService ) ;
} ) ;
2015-05-21 19:56:02 -04:00
2016-08-17 10:15:35 -04:00
it ( 'should support the event-bus scenario' , ( ) = > {
TestBed . configureTestingModule ( {
declarations : [
MyComp , GrandParentProvidingEventBus , ParentProvidingEventBus , ChildConsumingEventBus
2016-08-23 13:52:40 -04:00
] ,
schemas : [ NO_ERRORS_SCHEMA ] ,
2016-08-17 10:15:35 -04:00
} ) ;
const template = `
2015-05-21 19:56:02 -04:00
< grand - parent - providing - event - bus >
< parent - providing - event - bus >
< child - consuming - event - bus >
< / c h i l d - c o n s u m i n g - e v e n t - b u s >
< / p a r e n t - p r o v i d i n g - e v e n t - b u s >
< / g r a n d - p a r e n t - p r o v i d i n g - e v e n t - b u s >
2016-08-17 10:15:35 -04:00
` ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-11-12 08:08:58 -05:00
const gpComp = fixture . debugElement . children [ 0 ] ;
const parentComp = gpComp . children [ 0 ] ;
const childComp = parentComp . children [ 0 ] ;
2016-08-17 10:15:35 -04:00
2016-11-12 08:08:58 -05:00
const grandParent = gpComp . injector . get ( GrandParentProvidingEventBus ) ;
const parent = parentComp . injector . get ( ParentProvidingEventBus ) ;
const child = childComp . injector . get ( ChildConsumingEventBus ) ;
2016-08-17 10:15:35 -04:00
expect ( grandParent . bus . name ) . toEqual ( 'grandparent' ) ;
expect ( parent . bus . name ) . toEqual ( 'parent' ) ;
expect ( parent . grandParentBus ) . toBe ( grandParent . bus ) ;
expect ( child . bus ) . toBe ( parent . bus ) ;
} ) ;
2016-06-08 19:38:52 -04:00
2016-08-17 10:15:35 -04:00
it ( 'should instantiate bindings lazily' , ( ) = > {
TestBed . configureTestingModule ( {
2016-08-23 13:52:40 -04:00
declarations : [ MyComp , DirectiveConsumingInjectable , ComponentProvidingLoggingInjectable ] ,
schemas : [ NO_ERRORS_SCHEMA ] ,
2016-08-17 10:15:35 -04:00
} ) ;
const template = `
2015-07-13 19:28:44 -04:00
< component - providing - logging - injectable # providing >
2015-11-23 19:02:19 -05:00
< directive - consuming - injectable * ngIf = "ctxBoolProp" >
2015-07-13 19:28:44 -04:00
< / d i r e c t i v e - c o n s u m i n g - i n j e c t a b l e >
< / c o m p o n e n t - p r o v i d i n g - l o g g i n g - i n j e c t a b l e >
2016-08-17 10:15:35 -04:00
` ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2017-03-29 12:34:45 -04:00
const providing = fixture . debugElement . children [ 0 ] . references ! [ 'providing' ] ;
2016-08-17 10:15:35 -04:00
expect ( providing . created ) . toBe ( false ) ;
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxBoolProp = true ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
expect ( providing . created ) . toBe ( true ) ;
} ) ;
2015-05-21 19:30:07 -04:00
} ) ;
2016-06-08 19:38:52 -04:00
describe ( 'corner cases' , ( ) = > {
2016-08-17 10:15:35 -04:00
it ( 'should remove script tags from templates' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp ] } ) ;
const template = `
2015-08-05 10:35:59 -04:00
< script > alert ( "Ooops" ) ; < / script >
2016-08-17 10:15:35 -04:00
< div > before < script > alert ( "Ooops" ) ; < / script > < span > inside < / span > after < / div > ` ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-07-28 13:39:10 -04:00
2016-09-09 15:04:38 -04:00
expect ( getDOM ( ) . querySelectorAll ( fixture . nativeElement , 'script' ) . length ) . toEqual ( 0 ) ;
2016-08-17 10:15:35 -04:00
} ) ;
2016-07-28 13:39:10 -04:00
2016-08-17 10:15:35 -04:00
it ( 'should throw when using directives without selector' , ( ) = > {
@Directive ( { } )
class SomeDirective {
}
2016-07-28 13:39:10 -04:00
2016-08-19 15:51:01 -04:00
@Component ( { selector : 'comp' , template : '' } )
2016-08-17 10:15:35 -04:00
class SomeComponent {
}
TestBed . configureTestingModule ( { declarations : [ MyComp , SomeDirective , SomeComponent ] } ) ;
expect ( ( ) = > TestBed . createComponent ( MyComp ) )
. toThrowError ( ` Directive ${ stringify ( SomeDirective ) } has no selector, please add it! ` ) ;
} ) ;
2016-07-28 13:39:10 -04:00
2017-02-15 11:36:49 -05:00
it ( 'should use a default element name for components without selectors' , ( ) = > {
2017-03-29 12:34:45 -04:00
let noSelectorComponentFactory : ComponentFactory < SomeComponent > = undefined ! ;
2016-07-28 13:39:10 -04:00
2016-08-19 15:51:01 -04:00
@Component ( { template : '----' } )
class NoSelectorComponent {
}
@Component ( { selector : 'some-comp' , template : '' , entryComponents : [ NoSelectorComponent ] } )
class SomeComponent {
constructor ( componentFactoryResolver : ComponentFactoryResolver ) {
// grab its own component factory
noSelectorComponentFactory =
2017-03-29 12:34:45 -04:00
componentFactoryResolver . resolveComponentFactory ( NoSelectorComponent ) ! ;
2016-08-19 15:51:01 -04:00
}
}
TestBed . configureTestingModule ( { declarations : [ SomeComponent , NoSelectorComponent ] } ) ;
// get the factory
TestBed . createComponent ( SomeComponent ) ;
expect ( noSelectorComponentFactory . selector ) . toBe ( 'ng-component' ) ;
2017-03-14 19:26:17 -04:00
2016-08-19 15:51:01 -04:00
expect (
getDOM ( )
2017-03-14 19:26:17 -04:00
. nodeName ( noSelectorComponentFactory . create ( Injector . NULL ) . location . nativeElement )
2016-08-19 15:51:01 -04:00
. toLowerCase ( ) )
. toEqual ( 'ng-component' ) ;
} ) ;
2015-08-05 10:35:59 -04:00
} ) ;
2017-02-28 02:08:19 -05:00
describe ( 'error handling' , ( ) = > {
2016-08-17 10:15:35 -04:00
it ( 'should report a meaningful error when a directive is missing annotation' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , SomeDirectiveMissingAnnotation ] } ) ;
2015-06-16 12:45:03 -04:00
2016-08-17 10:15:35 -04:00
expect ( ( ) = > TestBed . createComponent ( MyComp ) )
. toThrowError (
2017-03-14 20:12:18 -04:00
` Unexpected value ' ${ stringify ( SomeDirectiveMissingAnnotation ) } ' declared by the module 'DynamicTestModule'. Please add a @Pipe/@Directive/@Component annotation. ` ) ;
2016-08-17 10:15:35 -04:00
} ) ;
2015-05-21 19:30:07 -04:00
2016-08-17 10:15:35 -04:00
it ( 'should report a meaningful error when a component is missing view annotation' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , ComponentWithoutView ] } ) ;
try {
TestBed . createComponent ( ComponentWithoutView ) ;
expect ( true ) . toBe ( false ) ;
} catch ( e ) {
expect ( e . message ) . toContain (
` No template specified for component ${ stringify ( ComponentWithoutView ) } ` ) ;
}
} ) ;
2015-05-26 12:45:15 -04:00
2016-08-17 10:15:35 -04:00
it ( 'should provide an error context when an error happens in DI' , ( ) = > {
2016-08-23 13:52:40 -04:00
TestBed . configureTestingModule ( {
declarations : [ MyComp , DirectiveThrowingAnError ] ,
schemas : [ NO_ERRORS_SCHEMA ] ,
} ) ;
2016-08-17 10:15:35 -04:00
const template = ` <directive-throwing-error></directive-throwing-error> ` ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
try {
TestBed . createComponent ( MyComp ) ;
throw 'Should throw' ;
} catch ( e ) {
2017-01-27 16:19:00 -05:00
const c = getDebugContext ( e ) ;
2016-08-17 10:15:35 -04:00
expect ( getDOM ( ) . nodeName ( c . componentRenderElement ) . toUpperCase ( ) ) . toEqual ( 'DIV' ) ;
expect ( ( < Injector > c . injector ) . get ) . toBeTruthy ( ) ;
}
} ) ;
2015-07-22 15:00:35 -04:00
2016-08-17 10:15:35 -04:00
it ( 'should provide an error context when an error happens in change detection' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , DirectiveThrowingAnError ] } ) ;
const template = ` <input [value]="one.two.three" #local> ` ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
try {
fixture . detectChanges ( ) ;
throw 'Should throw' ;
} catch ( e ) {
2017-01-27 16:19:00 -05:00
const c = getDebugContext ( e ) ;
2016-08-17 10:15:35 -04:00
expect ( getDOM ( ) . nodeName ( c . renderNode ) . toUpperCase ( ) ) . toEqual ( 'INPUT' ) ;
expect ( getDOM ( ) . nodeName ( c . componentRenderElement ) . toUpperCase ( ) ) . toEqual ( 'DIV' ) ;
expect ( ( < Injector > c . injector ) . get ) . toBeTruthy ( ) ;
2016-09-09 15:04:38 -04:00
expect ( c . context ) . toBe ( fixture . componentInstance ) ;
2016-08-17 10:15:35 -04:00
expect ( c . references [ 'local' ] ) . toBeDefined ( ) ;
}
} ) ;
2015-07-23 21:01:34 -04:00
it ( 'should provide an error context when an error happens in change detection (text node)' ,
2016-08-17 10:15:35 -04:00
( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp ] } ) ;
const template = ` <div>{{one.two.three}}</div> ` ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
try {
fixture . detectChanges ( ) ;
throw 'Should throw' ;
} catch ( e ) {
2017-01-27 16:19:00 -05:00
const c = getDebugContext ( e ) ;
2016-08-17 10:15:35 -04:00
expect ( c . renderNode ) . toBeTruthy ( ) ;
}
} ) ;
2015-07-23 21:01:34 -04:00
2016-04-28 20:50:03 -04:00
if ( getDOM ( ) . supportsDOMEvents ( ) ) { // this is required to use fakeAsync
2015-07-27 18:47:42 -04:00
it ( 'should provide an error context when an error happens in an event handler' ,
2016-08-17 10:15:35 -04:00
fakeAsync ( ( ) = > {
2016-08-23 13:52:40 -04:00
TestBed . configureTestingModule ( {
declarations : [ MyComp , DirectiveEmittingEvent , DirectiveListeningEvent ] ,
schemas : [ NO_ERRORS_SCHEMA ] ,
} ) ;
2016-08-17 10:15:35 -04:00
const template = ` <span emitter listener (event)="throwError()" #local></span> ` ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-04-18 19:04:35 -04:00
tick ( ) ;
2016-11-12 08:08:58 -05:00
const tc = fixture . debugElement . children [ 0 ] ;
2016-05-26 12:34:04 -04:00
2017-04-28 14:50:45 -04:00
const errorHandler = tc . injector . get ( ErrorHandler ) ;
let err : any ;
spyOn ( errorHandler , 'handleError' ) . and . callFake ( ( e : any ) = > err = e ) ;
tc . injector . get ( DirectiveEmittingEvent ) . fireEvent ( 'boom' ) ;
expect ( err ) . toBeTruthy ( ) ;
const c = getDebugContext ( err ) ;
expect ( getDOM ( ) . nodeName ( c . renderNode ) . toUpperCase ( ) ) . toEqual ( 'SPAN' ) ;
expect ( getDOM ( ) . nodeName ( c . componentRenderElement ) . toUpperCase ( ) ) . toEqual ( 'DIV' ) ;
expect ( ( < Injector > c . injector ) . get ) . toBeTruthy ( ) ;
expect ( c . context ) . toBe ( fixture . componentInstance ) ;
expect ( c . references [ 'local' ] ) . toBeDefined ( ) ;
2016-08-17 10:15:35 -04:00
} ) ) ;
2015-07-27 18:47:42 -04:00
}
2016-08-17 10:15:35 -04:00
} ) ;
2015-05-21 19:30:07 -04:00
2016-08-17 10:15:35 -04:00
it ( 'should support imperative views' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , SimpleImperativeViewComponent ] } ) ;
const template = '<simple-imp-cmp></simple-imp-cmp>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2015-05-21 19:30:07 -04:00
2016-09-09 15:04:38 -04:00
expect ( fixture . nativeElement ) . toHaveText ( 'hello imp view' ) ;
2015-05-21 19:30:07 -04:00
} ) ;
2016-08-17 10:15:35 -04:00
it ( 'should support moving embedded views around' , ( ) = > {
TestBed . configureTestingModule ( {
declarations : [ MyComp , SomeImperativeViewport ] ,
providers : [ { provide : ANCHOR_ELEMENT , useValue : el ( '<div></div>' ) } ] ,
} ) ;
const template = '<div><div *someImpvp="ctxBoolProp">hello</div></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const anchorElement = getTestBed ( ) . get ( ANCHOR_ELEMENT ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2015-05-21 19:30:07 -04:00
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
expect ( anchorElement ) . toHaveText ( '' ) ;
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxBoolProp = true ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
expect ( anchorElement ) . toHaveText ( 'hello' ) ;
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxBoolProp = false ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
2016-09-09 15:04:38 -04:00
expect ( fixture . nativeElement ) . toHaveText ( '' ) ;
2016-08-17 10:15:35 -04:00
} ) ;
2015-06-03 14:02:51 -04:00
2015-07-30 20:58:56 -04:00
describe ( 'Property bindings' , ( ) = > {
2016-08-17 10:15:35 -04:00
it ( 'should throw on bindings to unknown properties' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp ] } ) ;
const template = '<div unknown="{{ctxProp}}"></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
try {
TestBed . createComponent ( MyComp ) ;
throw 'Should throw' ;
} catch ( e ) {
2017-03-14 12:16:15 -04:00
expect ( e . message ) . toMatch (
/Template parse errors:\nCan't bind to 'unknown' since it isn't a known property of 'div'. \("<div \[ERROR ->\]unknown="{{ctxProp}}"><\/div>"\): .*MyComp.html@0:5/ ) ;
2016-08-17 10:15:35 -04:00
}
} ) ;
2015-07-10 17:55:48 -04:00
2016-07-21 20:12:00 -04:00
it ( 'should not throw for property binding to a non-existing property when there is a matching directive property' ,
2016-08-17 10:15:35 -04:00
( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , MyDir ] } ) ;
const template = '<div my-dir [elprop]="ctxProp"></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
expect ( ( ) = > TestBed . createComponent ( MyComp ) ) . not . toThrow ( ) ;
} ) ;
it ( 'should not be created when there is a directive with the same property' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , DirectiveWithTitle ] } ) ;
const template = '<span [title]="ctxProp"></span>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxProp = 'TITLE' ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
2016-11-12 08:08:58 -05:00
const el = getDOM ( ) . querySelector ( fixture . nativeElement , 'span' ) ;
2017-02-15 00:03:18 -05:00
expect ( el . title ) . toBeFalsy ( ) ;
2016-08-17 10:15:35 -04:00
} ) ;
2015-07-30 20:58:56 -04:00
2016-08-17 10:15:35 -04:00
it ( 'should work when a directive uses hostProperty to update the DOM element' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , DirectiveWithTitleAndHostProperty ] } ) ;
const template = '<span [title]="ctxProp"></span>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2015-07-30 20:58:56 -04:00
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxProp = 'TITLE' ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
2016-11-12 08:08:58 -05:00
const el = getDOM ( ) . querySelector ( fixture . nativeElement , 'span' ) ;
2017-07-19 16:58:23 -04:00
expect ( getDOM ( ) . getProperty ( el , 'title' ) ) . toEqual ( 'TITLE' ) ;
2016-08-17 10:15:35 -04:00
} ) ;
2015-07-30 20:58:56 -04:00
} ) ;
2015-08-20 18:11:12 -04:00
describe ( 'logging property updates' , ( ) = > {
2016-08-17 10:15:35 -04:00
it ( 'should reflect property values as attributes' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , MyDir ] } ) ;
const template = '<div>' +
'<div my-dir [elprop]="ctxProp"></div>' +
'</div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxProp = 'hello' ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
2016-09-09 15:04:38 -04:00
expect ( getDOM ( ) . getInnerHTML ( fixture . nativeElement ) )
2016-08-17 10:15:35 -04:00
. toContain ( 'ng-reflect-dir-prop="hello"' ) ;
} ) ;
2015-11-19 14:14:44 -05:00
2017-02-08 06:36:43 -05:00
it ( ` should work with prop names containing ' $ ' ` , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ ParentCmp , SomeCmpWithInput ] } ) ;
const fixture = TestBed . createComponent ( ParentCmp ) ;
fixture . detectChanges ( ) ;
2017-02-16 16:55:55 -05:00
expect ( getDOM ( ) . getInnerHTML ( fixture . nativeElement ) ) . toContain ( 'ng-reflect-test_="hello"' ) ;
2017-02-08 06:36:43 -05:00
} ) ;
2016-08-17 10:15:35 -04:00
it ( 'should reflect property values on template comments' , ( ) = > {
2017-01-09 16:16:46 -05:00
const fixture =
TestBed . configureTestingModule ( { declarations : [ MyComp ] } )
. overrideComponent (
MyComp , { set : { template : '<ng-template [ngIf]="ctxBoolProp"></ng-template>' } } )
. createComponent ( MyComp ) ;
2016-07-12 13:26:54 -04:00
2016-09-09 15:04:38 -04:00
fixture . componentInstance . ctxBoolProp = true ;
2016-08-17 10:15:35 -04:00
fixture . detectChanges ( ) ;
2016-09-09 15:04:38 -04:00
expect ( getDOM ( ) . getInnerHTML ( fixture . nativeElement ) )
2016-08-17 10:15:35 -04:00
. toContain ( '"ng\-reflect\-ng\-if"\: "true"' ) ;
} ) ;
it ( 'should indicate when toString() throws' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , MyDir ] } ) ;
const template = '<div my-dir [elprop]="toStringThrow"></div>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
fixture . detectChanges ( ) ;
2016-09-09 15:04:38 -04:00
expect ( getDOM ( ) . getInnerHTML ( fixture . nativeElement ) ) . toContain ( '[ERROR]' ) ;
2016-08-17 10:15:35 -04:00
} ) ;
2015-08-20 18:11:12 -04:00
} ) ;
2015-06-17 10:05:35 -04:00
2015-09-03 18:10:48 -04:00
describe ( 'property decorators' , ( ) = > {
2016-08-17 10:15:35 -04:00
it ( 'should support property decorators' , ( ) = > {
2016-08-23 13:52:40 -04:00
TestBed . configureTestingModule ( {
declarations : [ MyComp , DirectiveWithPropDecorators ] ,
schemas : [ NO_ERRORS_SCHEMA ] ,
} ) ;
2016-08-17 10:15:35 -04:00
const template = '<with-prop-decorators elProp="aaa"></with-prop-decorators>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
fixture . detectChanges ( ) ;
2016-11-12 08:08:58 -05:00
const dir = fixture . debugElement . children [ 0 ] . injector . get ( DirectiveWithPropDecorators ) ;
2016-08-17 10:15:35 -04:00
expect ( dir . dirProp ) . toEqual ( 'aaa' ) ;
} ) ;
2015-09-03 18:10:48 -04:00
2016-08-17 10:15:35 -04:00
it ( 'should support host binding decorators' , ( ) = > {
2016-08-23 13:52:40 -04:00
TestBed . configureTestingModule ( {
declarations : [ MyComp , DirectiveWithPropDecorators ] ,
schemas : [ NO_ERRORS_SCHEMA ] ,
} ) ;
2016-08-17 10:15:35 -04:00
const template = '<with-prop-decorators></with-prop-decorators>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
fixture . detectChanges ( ) ;
2016-11-12 08:08:58 -05:00
const dir = fixture . debugElement . children [ 0 ] . injector . get ( DirectiveWithPropDecorators ) ;
2016-08-17 10:15:35 -04:00
dir . myAttr = 'aaa' ;
fixture . detectChanges ( ) ;
expect ( getDOM ( ) . getOuterHTML ( fixture . debugElement . children [ 0 ] . nativeElement ) )
. toContain ( 'my-attr="aaa"' ) ;
} ) ;
2015-09-03 18:10:48 -04:00
2016-04-28 20:50:03 -04:00
if ( getDOM ( ) . supportsDOMEvents ( ) ) {
2016-08-17 10:15:35 -04:00
it ( 'should support event decorators' , fakeAsync ( ( ) = > {
2016-08-23 13:52:40 -04:00
TestBed . configureTestingModule ( {
declarations : [ MyComp , DirectiveWithPropDecorators ] ,
schemas : [ NO_ERRORS_SCHEMA ] ,
} ) ;
2016-08-17 10:15:35 -04:00
const template = ` <with-prop-decorators (elEvent)="ctxProp='called'"> ` ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-04-18 19:04:35 -04:00
tick ( ) ;
2015-09-03 18:10:48 -04:00
2016-11-12 08:08:58 -05:00
const emitter =
2016-06-14 17:53:01 -04:00
fixture . debugElement . children [ 0 ] . injector . get ( DirectiveWithPropDecorators ) ;
2016-04-18 19:04:35 -04:00
emitter . fireEvent ( 'fired !' ) ;
2015-09-03 18:10:48 -04:00
2016-04-18 19:04:35 -04:00
tick ( ) ;
2015-09-03 18:10:48 -04:00
2016-09-09 15:04:38 -04:00
expect ( fixture . componentInstance . ctxProp ) . toEqual ( 'called' ) ;
2016-08-17 10:15:35 -04:00
} ) ) ;
it ( 'should support host listener decorators' , ( ) = > {
2016-08-23 13:52:40 -04:00
TestBed . configureTestingModule ( {
declarations : [ MyComp , DirectiveWithPropDecorators ] ,
schemas : [ NO_ERRORS_SCHEMA ] ,
} ) ;
2016-08-17 10:15:35 -04:00
const template = '<with-prop-decorators></with-prop-decorators>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
fixture . detectChanges ( ) ;
2016-11-12 08:08:58 -05:00
const dir = fixture . debugElement . children [ 0 ] . injector . get ( DirectiveWithPropDecorators ) ;
const native = fixture . debugElement . children [ 0 ] . nativeElement ;
2016-08-17 10:15:35 -04:00
getDOM ( ) . dispatchEvent ( native , getDOM ( ) . createMouseEvent ( 'click' ) ) ;
expect ( dir . target ) . toBe ( native ) ;
} ) ;
2015-09-03 18:10:48 -04:00
}
2015-10-06 20:03:37 -04:00
2016-08-17 10:15:35 -04:00
it ( 'should support defining views in the component decorator' , ( ) = > {
2016-08-23 13:52:40 -04:00
TestBed . configureTestingModule ( {
declarations : [ MyComp , ComponentWithTemplate ] ,
imports : [ CommonModule ] ,
schemas : [ NO_ERRORS_SCHEMA ] ,
} ) ;
2016-08-17 10:15:35 -04:00
const template = '<component-with-template></component-with-template>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
fixture . detectChanges ( ) ;
2016-11-12 08:08:58 -05:00
const native = fixture . debugElement . children [ 0 ] . nativeElement ;
2016-08-17 10:15:35 -04:00
expect ( native ) . toHaveText ( 'No View Decorator: 123' ) ;
} ) ;
2015-09-03 18:10:48 -04:00
} ) ;
2015-10-27 16:16:27 -04:00
2015-11-19 14:14:44 -05:00
2016-04-28 20:50:03 -04:00
if ( getDOM ( ) . supportsDOMEvents ( ) ) {
2015-10-27 16:16:27 -04:00
describe ( 'svg' , ( ) = > {
2016-08-17 10:15:35 -04:00
it ( 'should support svg elements' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp ] } ) ;
const template = '<svg><use xlink:href="Port" /></svg>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-11-12 08:08:58 -05:00
const el = fixture . nativeElement ;
const svg = getDOM ( ) . childNodes ( el ) [ 0 ] ;
const use = getDOM ( ) . childNodes ( svg ) [ 0 ] ;
2016-08-17 10:15:35 -04:00
expect ( getDOM ( ) . getProperty ( < Element > svg , 'namespaceURI' ) )
. toEqual ( 'http://www.w3.org/2000/svg' ) ;
expect ( getDOM ( ) . getProperty ( < Element > use , 'namespaceURI' ) )
. toEqual ( 'http://www.w3.org/2000/svg' ) ;
2016-11-12 08:08:58 -05:00
const firstAttribute = getDOM ( ) . getProperty ( < Element > use , 'attributes' ) [ 0 ] ;
2016-08-17 10:15:35 -04:00
expect ( firstAttribute . name ) . toEqual ( 'xlink:href' ) ;
expect ( firstAttribute . namespaceURI ) . toEqual ( 'http://www.w3.org/1999/xlink' ) ;
} ) ;
it ( 'should support foreignObjects with document fragments' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp ] } ) ;
const template =
'<svg><foreignObject><xhtml:div><p>Test</p></xhtml:div></foreignObject></svg>' ;
TestBed . overrideComponent ( MyComp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( MyComp ) ;
2016-11-12 08:08:58 -05:00
const el = fixture . nativeElement ;
const svg = getDOM ( ) . childNodes ( el ) [ 0 ] ;
const foreignObject = getDOM ( ) . childNodes ( svg ) [ 0 ] ;
const p = getDOM ( ) . childNodes ( foreignObject ) [ 0 ] ;
2016-08-17 10:15:35 -04:00
expect ( getDOM ( ) . getProperty ( < Element > svg , 'namespaceURI' ) )
. toEqual ( 'http://www.w3.org/2000/svg' ) ;
expect ( getDOM ( ) . getProperty ( < Element > foreignObject , 'namespaceURI' ) )
. toEqual ( 'http://www.w3.org/2000/svg' ) ;
expect ( getDOM ( ) . getProperty ( < Element > p , 'namespaceURI' ) )
. toEqual ( 'http://www.w3.org/1999/xhtml' ) ;
} ) ;
2015-10-27 16:16:27 -04:00
} ) ;
2016-01-08 15:01:29 -05:00
describe ( 'attributes' , ( ) = > {
2016-08-17 10:15:35 -04:00
it ( 'should support attributes with namespace' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , SomeCmp ] } ) ;
const template = '<svg:use xlink:href="#id" />' ;
TestBed . overrideComponent ( SomeCmp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( SomeCmp ) ;
2016-11-12 08:08:58 -05:00
const useEl = getDOM ( ) . firstChild ( fixture . nativeElement ) ;
2016-08-17 10:15:35 -04:00
expect ( getDOM ( ) . getAttributeNS ( useEl , 'http://www.w3.org/1999/xlink' , 'href' ) )
. toEqual ( '#id' ) ;
} ) ;
it ( 'should support binding to attributes with namespace' , ( ) = > {
TestBed . configureTestingModule ( { declarations : [ MyComp , SomeCmp ] } ) ;
const template = '<svg:use [attr.xlink:href]="value" />' ;
TestBed . overrideComponent ( SomeCmp , { set : { template } } ) ;
const fixture = TestBed . createComponent ( SomeCmp ) ;
2016-11-12 08:08:58 -05:00
const cmp = fixture . componentInstance ;
const useEl = getDOM ( ) . firstChild ( fixture . nativeElement ) ;
2016-08-17 10:15:35 -04:00
cmp . value = '#id' ;
fixture . detectChanges ( ) ;
expect ( getDOM ( ) . getAttributeNS ( useEl , 'http://www.w3.org/1999/xlink' , 'href' ) )
. toEqual ( '#id' ) ;
cmp . value = null ;
fixture . detectChanges ( ) ;
expect ( getDOM ( ) . hasAttributeNS ( useEl , 'http://www.w3.org/1999/xlink' , 'href' ) )
. toEqual ( false ) ;
} ) ;
2016-01-08 15:01:29 -05:00
} ) ;
2015-10-27 16:16:27 -04:00
}
2015-05-21 19:30:07 -04:00
} ) ;
}
2016-06-20 12:52:41 -04:00
@Component ( { selector : 'cmp-with-default-interpolation' , template : ` {{text}} ` } )
class ComponentWithDefaultInterpolation {
text = 'Default Interpolation' ;
}
@Component ( {
selector : 'cmp-with-custom-interpolation-a' ,
template : ` <div>{%text%}</div> ` ,
interpolation : [ '{%' , '%}' ]
} )
class ComponentWithCustomInterpolationA {
text = 'Custom Interpolation A' ;
}
@Component ( {
selector : 'cmp-with-custom-interpolation-b' ,
template :
` <div>{**text%}</div> (<cmp-with-default-interpolation></cmp-with-default-interpolation>) ` ,
2016-08-19 15:51:01 -04:00
interpolation : [ '{**' , '%}' ]
2016-06-20 12:52:41 -04:00
} )
class ComponentWithCustomInterpolationB {
text = 'Custom Interpolation B' ;
}
2015-05-21 19:30:07 -04:00
@Injectable ( )
class MyService {
greeting : string ;
constructor ( ) { this . greeting = 'hello' ; }
}
2016-03-08 16:36:48 -05:00
@Component ( { selector : 'simple-imp-cmp' , template : '' } )
2015-05-21 19:30:07 -04:00
class SimpleImperativeViewComponent {
2016-06-14 17:53:01 -04:00
done : any ;
2015-05-21 19:30:07 -04:00
2017-02-02 18:01:35 -05:00
constructor ( self : ElementRef ) {
2016-11-12 08:08:58 -05:00
const hostElement = self . nativeElement ;
2016-04-28 20:50:03 -04:00
getDOM ( ) . appendChild ( hostElement , el ( 'hello imp view' ) ) ;
2015-05-21 19:30:07 -04:00
}
}
@Directive ( { selector : 'dynamic-vp' } )
class DynamicViewport {
2016-11-01 17:21:40 -04:00
private componentFactory : ComponentFactory < ChildCompUsingService > ;
private injector : Injector ;
constructor ( private vc : ViewContainerRef , componentFactoryResolver : ComponentFactoryResolver ) {
2016-11-12 08:08:58 -05:00
const myService = new MyService ( ) ;
2015-05-21 19:30:07 -04:00
myService . greeting = 'dynamic greet' ;
2015-06-29 14:15:49 -04:00
2016-11-01 17:21:40 -04:00
this . injector = ReflectiveInjector . resolveAndCreate (
2016-06-08 19:38:52 -04:00
[ { provide : MyService , useValue : myService } ] , vc . injector ) ;
2017-03-29 12:34:45 -04:00
this . componentFactory =
componentFactoryResolver . resolveComponentFactory ( ChildCompUsingService ) ! ;
2015-05-21 19:30:07 -04:00
}
2016-11-01 17:21:40 -04:00
create() { this . vc . createComponent ( this . componentFactory , this . vc . length , this . injector ) ; }
2015-05-21 19:30:07 -04:00
}
2015-09-30 23:59:23 -04:00
@Directive ( { selector : '[my-dir]' , inputs : [ 'dirProp: elprop' ] , exportAs : 'mydir' } )
2015-05-21 19:30:07 -04:00
class MyDir {
dirProp : string ;
constructor ( ) { this . dirProp = '' ; }
}
2015-09-30 23:59:23 -04:00
@Directive ( { selector : '[title]' , inputs : [ 'title' ] } )
2015-07-30 20:58:56 -04:00
class DirectiveWithTitle {
title : string ;
}
2015-09-30 23:59:23 -04:00
@Directive ( { selector : '[title]' , inputs : [ 'title' ] , host : { '[title]' : 'title' } } )
2015-07-30 20:58:56 -04:00
class DirectiveWithTitleAndHostProperty {
title : string ;
}
2016-04-29 15:04:03 -04:00
@Component ( { selector : 'event-cmp' , template : '<div (click)="noop()"></div>' } )
class EventCmp {
noop() { }
}
2016-03-08 16:36:48 -05:00
@Component ( {
selector : 'push-cmp' ,
inputs : [ 'prop' ] ,
2017-03-21 13:44:11 -04:00
host : { '(click)' : 'true' } ,
2016-03-08 16:36:48 -05:00
changeDetection : ChangeDetectionStrategy.OnPush ,
2016-04-29 15:04:03 -04:00
template :
2016-08-19 15:51:01 -04:00
'{{field}}<div (click)="noop()"></div><div *ngIf="true" (click)="noop()"></div><event-cmp></event-cmp>'
2016-03-08 16:36:48 -05:00
} )
2015-05-21 19:30:07 -04:00
class PushCmp {
numberOfChecks : number ;
2016-06-14 17:53:01 -04:00
prop : any ;
2015-05-21 19:30:07 -04:00
constructor ( ) { this . numberOfChecks = 0 ; }
2016-04-29 15:04:03 -04:00
noop() { }
2015-05-21 19:30:07 -04:00
get field() {
this . numberOfChecks ++ ;
2016-06-08 19:38:52 -04:00
return 'fixed' ;
2015-05-21 19:30:07 -04:00
}
}
2015-08-26 14:44:59 -04:00
@Component ( {
selector : 'push-cmp-with-ref' ,
2015-09-30 23:59:23 -04:00
inputs : [ 'prop' ] ,
2016-03-08 16:36:48 -05:00
changeDetection : ChangeDetectionStrategy.OnPush ,
template : '{{field}}'
2015-08-26 14:44:59 -04:00
} )
2015-05-21 19:30:07 -04:00
class PushCmpWithRef {
numberOfChecks : number ;
ref : ChangeDetectorRef ;
2016-06-14 17:53:01 -04:00
prop : any ;
2015-05-21 19:30:07 -04:00
constructor ( ref : ChangeDetectorRef ) {
this . numberOfChecks = 0 ;
this . ref = ref ;
}
get field() {
this . numberOfChecks ++ ;
2016-06-08 19:38:52 -04:00
return 'fixed' ;
2015-05-21 19:30:07 -04:00
}
2015-08-28 13:08:18 -04:00
propagate() { this . ref . markForCheck ( ) ; }
2015-05-21 19:30:07 -04:00
}
2016-02-17 15:12:10 -05:00
@Component ( {
selector : 'push-cmp-with-host-event' ,
host : { '(click)' : 'ctxCallback($event)' } ,
changeDetection : ChangeDetectionStrategy.OnPush ,
template : ''
} )
class PushCmpWithHostEvent {
2016-06-14 17:53:01 -04:00
ctxCallback : Function = ( _ : any ) = > { } ;
2016-02-17 15:12:10 -05:00
}
2015-11-02 19:03:42 -05:00
@Component ( {
selector : 'push-cmp-with-async' ,
changeDetection : ChangeDetectionStrategy.OnPush ,
2016-08-19 15:51:01 -04:00
template : '{{field | async}}'
2015-11-02 19:03:42 -05:00
} )
2015-08-06 13:39:02 -04:00
class PushCmpWithAsyncPipe {
numberOfChecks : number = 0 ;
2016-08-02 18:53:34 -04:00
resolve : ( result : any ) = > void ;
2015-08-06 13:39:02 -04:00
promise : Promise < any > ;
constructor ( ) {
2016-08-02 18:53:34 -04:00
this . promise = new Promise ( ( resolve ) = > { this . resolve = resolve ; } ) ;
2015-08-06 13:39:02 -04:00
}
get field() {
this . numberOfChecks ++ ;
return this . promise ;
}
}
2016-08-19 15:51:01 -04:00
@Component ( { selector : 'my-comp' , template : '' } )
2015-05-21 19:30:07 -04:00
class MyComp {
ctxProp : string ;
2015-11-19 14:14:44 -05:00
ctxNumProp : number ;
ctxBoolProp : boolean ;
2016-07-12 13:26:54 -04:00
toStringThrow = { toString : function ( ) { throw 'boom' ; } } ;
2015-05-21 19:30:07 -04:00
constructor ( ) {
this . ctxProp = 'initial value' ;
this . ctxNumProp = 0 ;
this . ctxBoolProp = false ;
}
2015-07-27 18:47:42 -04:00
throwError() { throw 'boom' ; }
2015-05-21 19:30:07 -04:00
}
2016-03-08 16:36:48 -05:00
@Component ( {
selector : 'child-cmp' ,
inputs : [ 'dirProp' ] ,
viewProviders : [ MyService ] ,
template : '{{ctxProp}}'
} )
2015-05-21 19:30:07 -04:00
class ChildComp {
ctxProp : string ;
2017-03-29 12:34:45 -04:00
dirProp : string | null ;
2015-05-21 19:30:07 -04:00
constructor ( service : MyService ) {
this . ctxProp = service . greeting ;
this . dirProp = null ;
}
}
2016-08-19 15:51:01 -04:00
@Component ( { selector : 'child-cmp-no-template' , template : '' } )
2015-06-15 21:05:29 -04:00
class ChildCompNoTemplate {
ctxProp : string = 'hello' ;
}
2016-03-08 16:36:48 -05:00
@Component ( { selector : 'child-cmp-svc' , template : '{{ctxProp}}' } )
2015-05-21 19:30:07 -04:00
class ChildCompUsingService {
ctxProp : string ;
constructor ( service : MyService ) { this . ctxProp = service . greeting ; }
}
@Directive ( { selector : 'some-directive' } )
class SomeDirective {
}
class SomeDirectiveMissingAnnotation { }
2016-03-08 16:36:48 -05:00
@Component ( {
selector : 'cmp-with-host' ,
template : '<p>Component with an injected host</p>' ,
} )
2015-07-29 14:26:09 -04:00
class CompWithHost {
myHost : SomeDirective ;
constructor ( @Host ( ) someComp : SomeDirective ) { this . myHost = someComp ; }
2015-05-21 19:30:07 -04:00
}
2015-10-11 01:11:13 -04:00
@Component ( { selector : '[child-cmp2]' , viewProviders : [ MyService ] } )
2015-05-21 19:30:07 -04:00
class ChildComp2 {
ctxProp : string ;
2017-03-29 12:34:45 -04:00
dirProp : string | null ;
2015-05-21 19:30:07 -04:00
constructor ( service : MyService ) {
this . ctxProp = service . greeting ;
this . dirProp = null ;
}
}
2016-04-28 17:00:31 -04:00
class SomeViewportContext {
constructor ( public someTmpl : string ) { }
}
2015-05-21 19:30:07 -04:00
@Directive ( { selector : '[some-viewport]' } )
class SomeViewport {
2016-05-04 14:53:12 -04:00
constructor ( public container : ViewContainerRef , templateRef : TemplateRef < SomeViewportContext > ) {
2016-04-28 17:00:31 -04:00
container . createEmbeddedView ( templateRef , new SomeViewportContext ( 'hello' ) ) ;
container . createEmbeddedView ( templateRef , new SomeViewportContext ( 'again' ) ) ;
2015-05-21 19:30:07 -04:00
}
}
2016-08-08 12:11:35 -04:00
@Directive ( { selector : '[pollutedContext]' } )
class PollutedContext {
constructor ( private tplRef : TemplateRef < any > , private vcRef : ViewContainerRef ) {
const evRef = this . vcRef . createEmbeddedView ( this . tplRef ) ;
evRef . context . bar = 'baz' ;
}
}
@Directive ( { selector : '[noContext]' } )
class NoContext {
constructor ( private tplRef : TemplateRef < any > , private vcRef : ViewContainerRef ) {
this . vcRef . createEmbeddedView ( this . tplRef ) ;
}
}
2015-08-07 14:41:38 -04:00
@Pipe ( { name : 'double' } )
2015-11-17 13:09:23 -05:00
class DoublePipe implements PipeTransform , OnDestroy {
ngOnDestroy() { }
2016-06-14 17:53:01 -04:00
transform ( value : any ) { return ` ${ value } ${ value } ` ; }
2015-05-21 19:30:07 -04:00
}
2015-09-30 23:59:23 -04:00
@Directive ( { selector : '[emitter]' , outputs : [ 'event' ] } )
2015-12-04 11:02:19 -05:00
class DirectiveEmittingEvent {
2015-05-21 19:30:07 -04:00
msg : string ;
2015-10-24 21:48:43 -04:00
event : EventEmitter < any > ;
2015-05-21 19:30:07 -04:00
constructor ( ) {
this . msg = '' ;
this . event = new EventEmitter ( ) ;
}
2016-08-02 18:53:34 -04:00
fireEvent ( msg : string ) { this . event . emit ( msg ) ; }
2015-05-21 19:30:07 -04:00
}
2015-06-09 06:33:40 -04:00
@Directive ( { selector : '[update-host-attributes]' , host : { 'role' : 'button' } } )
2015-05-21 19:30:07 -04:00
class DirectiveUpdatingHostAttributes {
}
2015-06-09 06:33:40 -04:00
@Directive ( { selector : '[update-host-properties]' , host : { '[id]' : 'id' } } )
2015-05-21 19:30:07 -04:00
class DirectiveUpdatingHostProperties {
id : string ;
2016-06-08 19:38:52 -04:00
constructor ( ) { this . id = 'one' ; }
2015-05-21 19:30:07 -04:00
}
2015-06-09 06:33:40 -04:00
@Directive ( { selector : '[listener]' , host : { '(event)' : 'onEvent($event)' } } )
2015-05-21 19:30:07 -04:00
class DirectiveListeningEvent {
msg : string ;
constructor ( ) { this . msg = '' ; }
onEvent ( msg : string ) { this . msg = msg ; }
}
@Directive ( {
selector : '[listener]' ,
2015-06-09 06:33:40 -04:00
host : {
'(domEvent)' : 'onEvent($event.type)' ,
'(window:domEvent)' : 'onWindowEvent($event.type)' ,
'(document:domEvent)' : 'onDocumentEvent($event.type)' ,
'(body:domEvent)' : 'onBodyEvent($event.type)'
2015-05-21 19:30:07 -04:00
}
} )
class DirectiveListeningDomEvent {
2015-09-01 11:52:54 -04:00
eventTypes : string [ ] = [ ] ;
onEvent ( eventType : string ) { this . eventTypes . push ( eventType ) ; }
2016-06-08 19:38:52 -04:00
onWindowEvent ( eventType : string ) { this . eventTypes . push ( 'window_' + eventType ) ; }
onDocumentEvent ( eventType : string ) { this . eventTypes . push ( 'document_' + eventType ) ; }
onBodyEvent ( eventType : string ) { this . eventTypes . push ( 'body_' + eventType ) ; }
2015-05-21 19:30:07 -04:00
}
2016-11-12 08:08:58 -05:00
let globalCounter = 0 ;
2015-06-12 10:50:45 -04:00
@Directive ( { selector : '[listenerother]' , host : { '(window:domEvent)' : 'onEvent($event.type)' } } )
2015-05-21 19:30:07 -04:00
class DirectiveListeningDomEventOther {
eventType : string ;
constructor ( ) { this . eventType = '' ; }
onEvent ( eventType : string ) {
globalCounter ++ ;
2016-06-08 19:38:52 -04:00
this . eventType = 'other_' + eventType ;
2015-05-21 19:30:07 -04:00
}
}
2015-06-09 06:33:40 -04:00
@Directive ( { selector : '[listenerprevent]' , host : { '(click)' : 'onEvent($event)' } } )
2015-05-21 19:30:07 -04:00
class DirectiveListeningDomEventPrevent {
2016-06-14 17:53:01 -04:00
onEvent ( event : any ) { return false ; }
2015-05-21 19:30:07 -04:00
}
2015-06-09 06:33:40 -04:00
@Directive ( { selector : '[listenernoprevent]' , host : { '(click)' : 'onEvent($event)' } } )
2015-05-21 19:30:07 -04:00
class DirectiveListeningDomEventNoPrevent {
2016-06-14 17:53:01 -04:00
onEvent ( event : any ) { return true ; }
2015-05-21 19:30:07 -04:00
}
2015-09-30 23:59:23 -04:00
@Directive ( { selector : '[id]' , inputs : [ 'id' ] } )
2015-05-21 19:30:07 -04:00
class IdDir {
id : string ;
}
2016-02-03 14:35:42 -05:00
@Directive ( { selector : '[customEvent]' } )
class EventDir {
@Output ( ) customEvent = new EventEmitter ( ) ;
doSomething() { }
}
2015-05-21 19:30:07 -04:00
@Directive ( { selector : '[static]' } )
class NeedsAttribute {
2016-06-14 17:53:01 -04:00
typeAttribute : string ;
staticAttribute : string ;
fooAttribute : string ;
2016-06-08 19:38:52 -04:00
constructor (
2016-06-14 17:53:01 -04:00
@Attribute ( 'type' ) typeAttribute : string , @Attribute ( 'static' ) staticAttribute : string ,
@Attribute ( 'foo' ) fooAttribute : string ) {
2015-05-21 19:30:07 -04:00
this . typeAttribute = typeAttribute ;
2015-08-20 04:12:46 -04:00
this . staticAttribute = staticAttribute ;
2015-05-21 19:30:07 -04:00
this . fooAttribute = fooAttribute ;
}
}
@Injectable ( )
class PublicApi {
}
2015-09-18 13:33:23 -04:00
@Directive ( {
selector : '[public-api]' ,
2016-07-30 22:18:14 -04:00
providers : [ { provide : PublicApi , useExisting : PrivateImpl , deps : [ ] } ]
2015-09-18 13:33:23 -04:00
} )
2015-05-21 19:30:07 -04:00
class PrivateImpl extends PublicApi {
}
@Directive ( { selector : '[needs-public-api]' } )
class NeedsPublicApi {
2015-07-29 14:26:09 -04:00
constructor ( @Host ( ) api : PublicApi ) { expect ( api instanceof PrivateImpl ) . toBe ( true ) ; }
2015-05-21 19:30:07 -04:00
}
2016-04-28 17:00:31 -04:00
class ToolbarContext {
constructor ( public toolbarProp : string ) { }
}
2015-05-21 19:30:07 -04:00
@Directive ( { selector : '[toolbarpart]' } )
class ToolbarPart {
2016-04-28 17:00:31 -04:00
templateRef : TemplateRef < ToolbarContext > ;
constructor ( templateRef : TemplateRef < ToolbarContext > ) { this . templateRef = templateRef ; }
2015-05-21 19:30:07 -04:00
}
2015-11-23 19:02:19 -05:00
@Directive ( { selector : '[toolbarVc]' , inputs : [ 'toolbarVc' ] } )
2015-05-21 19:30:07 -04:00
class ToolbarViewContainer {
2017-02-15 00:03:18 -05:00
constructor ( public vc : ViewContainerRef ) { }
2015-05-21 19:30:07 -04:00
set toolbarVc ( part : ToolbarPart ) {
2016-04-28 17:00:31 -04:00
this . vc . createEmbeddedView ( part . templateRef , new ToolbarContext ( 'From toolbar' ) , 0 ) ;
2015-05-21 19:30:07 -04:00
}
}
2016-03-08 16:36:48 -05:00
@Component ( {
selector : 'toolbar' ,
2016-04-25 22:52:24 -04:00
template : 'TOOLBAR(<div *ngFor="let part of query" [toolbarVc]="part"></div>)' ,
2015-05-21 19:30:07 -04:00
} )
class ToolbarComponent {
2016-08-15 19:07:55 -04:00
@ContentChildren ( ToolbarPart ) query : QueryList < ToolbarPart > ;
2017-02-15 00:03:18 -05:00
ctxProp : string = 'hello world' ;
2015-05-21 19:30:07 -04:00
2017-02-15 00:03:18 -05:00
constructor ( ) { }
2015-05-21 19:30:07 -04:00
}
2015-10-10 22:56:22 -04:00
@Directive ( { selector : '[two-way]' , inputs : [ 'control' ] , outputs : [ 'controlChange' ] } )
2015-05-21 19:30:07 -04:00
class DirectiveWithTwoWayBinding {
2015-10-10 22:56:22 -04:00
controlChange = new EventEmitter ( ) ;
2016-06-14 17:53:01 -04:00
control : any = null ;
2015-05-21 19:30:07 -04:00
2016-08-02 18:53:34 -04:00
triggerChange ( value : any ) { this . controlChange . emit ( value ) ; }
2015-05-21 19:30:07 -04:00
}
@Injectable ( )
class InjectableService {
}
2015-07-13 19:28:44 -04:00
function createInjectableWithLogging ( inj : Injector ) {
inj . get ( ComponentProvidingLoggingInjectable ) . created = true ;
return new InjectableService ( ) ;
}
@Component ( {
selector : 'component-providing-logging-injectable' ,
2016-07-30 22:18:14 -04:00
providers :
[ { provide : InjectableService , useFactory : createInjectableWithLogging , deps : [ Injector ] } ] ,
2016-03-08 16:36:48 -05:00
template : ''
2015-07-13 19:28:44 -04:00
} )
class ComponentProvidingLoggingInjectable {
created : boolean = false ;
}
2015-10-11 01:11:13 -04:00
@Directive ( { selector : 'directive-providing-injectable' , providers : [ [ InjectableService ] ] } )
2015-05-21 19:30:07 -04:00
class DirectiveProvidingInjectable {
}
2016-03-08 16:36:48 -05:00
@Component ( {
selector : 'directive-providing-injectable' ,
viewProviders : [ [ InjectableService ] ] ,
template : ''
} )
2015-05-21 19:30:07 -04:00
class DirectiveProvidingInjectableInView {
}
2015-07-29 18:01:22 -04:00
@Component ( {
selector : 'directive-providing-injectable' ,
2016-07-30 22:18:14 -04:00
providers : [ { provide : InjectableService , useValue : 'host' } ] ,
viewProviders : [ { provide : InjectableService , useValue : 'view' } ] ,
2016-03-08 16:36:48 -05:00
template : ''
2015-07-29 18:01:22 -04:00
} )
class DirectiveProvidingInjectableInHostAndView {
}
2015-06-04 06:15:34 -04:00
2016-03-08 16:36:48 -05:00
@Component ( { selector : 'directive-consuming-injectable' , template : '' } )
2015-05-21 19:30:07 -04:00
class DirectiveConsumingInjectable {
2016-06-14 17:53:01 -04:00
injectable : any ;
2015-05-21 19:30:07 -04:00
2016-06-14 17:53:01 -04:00
constructor ( @Host ( ) @Inject ( InjectableService ) injectable : any ) { this . injectable = injectable ; }
2015-05-21 19:30:07 -04:00
}
@Component ( { selector : 'directive-containing-directive-consuming-an-injectable' } )
class DirectiveContainingDirectiveConsumingAnInjectable {
2016-06-14 17:53:01 -04:00
directive : any ;
2015-05-21 19:30:07 -04:00
}
2016-03-08 16:36:48 -05:00
@Component ( { selector : 'directive-consuming-injectable-unbounded' , template : '' } )
2015-05-21 19:30:07 -04:00
class DirectiveConsumingInjectableUnbounded {
2016-06-14 17:53:01 -04:00
injectable : any ;
2015-05-21 19:30:07 -04:00
2016-06-08 19:38:52 -04:00
constructor (
injectable : InjectableService ,
@SkipSelf ( ) parent : DirectiveContainingDirectiveConsumingAnInjectable ) {
2015-05-21 19:30:07 -04:00
this . injectable = injectable ;
parent . directive = this ;
}
}
2015-05-21 19:56:02 -04:00
class EventBus {
parentEventBus : EventBus ;
name : string ;
constructor ( parentEventBus : EventBus , name : string ) {
this . parentEventBus = parentEventBus ;
this . name = name ;
}
}
@Directive ( {
selector : 'grand-parent-providing-event-bus' ,
2017-03-29 12:34:45 -04:00
providers : [ { provide : EventBus , useValue : new EventBus ( null ! , 'grandparent' ) } ]
2015-05-21 19:56:02 -04:00
} )
class GrandParentProvidingEventBus {
bus : EventBus ;
constructor ( bus : EventBus ) { this . bus = bus ; }
}
2016-06-14 17:53:01 -04:00
function createParentBus ( peb : EventBus ) {
2016-06-08 19:38:52 -04:00
return new EventBus ( peb , 'parent' ) ;
2015-05-21 19:56:02 -04:00
}
@Component ( {
selector : 'parent-providing-event-bus' ,
2016-09-12 22:14:17 -04:00
providers : [ { provide : EventBus , useFactory : createParentBus , deps : [ [ EventBus , new SkipSelf ( ) ] ] } ] ,
2016-06-02 20:30:40 -04:00
template : ` <child-consuming-event-bus></child-consuming-event-bus> `
2015-05-21 19:56:02 -04:00
} )
class ParentProvidingEventBus {
bus : EventBus ;
grandParentBus : EventBus ;
2015-07-29 14:26:09 -04:00
constructor ( bus : EventBus , @SkipSelf ( ) grandParentBus : EventBus ) {
2015-05-21 19:56:02 -04:00
this . bus = bus ;
this . grandParentBus = grandParentBus ;
}
}
@Directive ( { selector : 'child-consuming-event-bus' } )
class ChildConsumingEventBus {
bus : EventBus ;
2015-07-29 14:26:09 -04:00
constructor ( @SkipSelf ( ) bus : EventBus ) { this . bus = bus ; }
2015-05-21 19:56:02 -04:00
}
2015-06-03 14:02:51 -04:00
2015-11-23 19:02:19 -05:00
@Directive ( { selector : '[someImpvp]' , inputs : [ 'someImpvp' ] } )
2015-06-03 14:02:51 -04:00
class SomeImperativeViewport {
2017-03-29 12:34:45 -04:00
view : EmbeddedViewRef < Object > | null ;
2016-06-14 17:53:01 -04:00
anchor : any ;
2016-06-08 19:38:52 -04:00
constructor (
public vc : ViewContainerRef , public templateRef : TemplateRef < Object > ,
2016-06-14 17:53:01 -04:00
@Inject ( ANCHOR_ELEMENT ) anchor : any ) {
2015-06-03 14:02:51 -04:00
this . view = null ;
this . anchor = anchor ;
}
set someImpvp ( value : boolean ) {
2017-02-15 00:03:18 -05:00
if ( this . view ) {
2015-06-16 12:45:03 -04:00
this . vc . clear ( ) ;
2015-06-03 14:02:51 -04:00
this . view = null ;
}
2017-02-15 00:03:18 -05:00
2015-06-03 14:02:51 -04:00
if ( value ) {
2015-07-17 11:03:40 -04:00
this . view = this . vc . createEmbeddedView ( this . templateRef ) ;
2016-11-12 08:08:58 -05:00
const nodes = this . view . rootNodes ;
for ( let i = 0 ; i < nodes . length ; i ++ ) {
2016-04-28 20:50:03 -04:00
getDOM ( ) . appendChild ( this . anchor , nodes [ i ] ) ;
2015-06-03 14:02:51 -04:00
}
}
}
}
2015-06-04 16:45:08 -04:00
@Directive ( { selector : '[export-dir]' , exportAs : 'dir' } )
class ExportDir {
2015-06-09 06:33:40 -04:00
}
2015-06-16 12:45:03 -04:00
@Component ( { selector : 'comp' } )
class ComponentWithoutView {
}
2015-06-16 14:16:08 -04:00
@Directive ( { selector : '[no-duplicate]' } )
class DuplicateDir {
2015-07-28 09:56:18 -04:00
constructor ( elRef : ElementRef ) {
2016-04-28 20:50:03 -04:00
getDOM ( ) . setText ( elRef . nativeElement , getDOM ( ) . getText ( elRef . nativeElement ) + 'noduplicate' ) ;
2015-06-16 14:16:08 -04:00
}
}
@Directive ( { selector : '[no-duplicate]' } )
class OtherDuplicateDir {
2015-07-28 09:56:18 -04:00
constructor ( elRef : ElementRef ) {
2016-06-08 19:38:52 -04:00
getDOM ( ) . setText (
elRef . nativeElement , getDOM ( ) . getText ( elRef . nativeElement ) + 'othernoduplicate' ) ;
2015-06-16 14:16:08 -04:00
}
}
2015-07-22 15:00:35 -04:00
@Directive ( { selector : 'directive-throwing-error' } )
class DirectiveThrowingAnError {
2016-08-25 03:50:16 -04:00
constructor ( ) { throw new Error ( 'BOOM' ) ; }
2015-07-22 15:00:35 -04:00
}
2015-09-03 18:10:48 -04:00
2015-10-06 20:03:37 -04:00
@Component ( {
selector : 'component-with-template' ,
2016-04-25 22:52:24 -04:00
template : ` No View Decorator: <div *ngFor="let item of items">{{item}}</div> `
2015-10-06 20:03:37 -04:00
} )
2015-10-06 22:14:45 -04:00
class ComponentWithTemplate {
2015-10-06 20:03:37 -04:00
items = [ 1 , 2 , 3 ] ;
}
2015-09-03 18:10:48 -04:00
@Directive ( { selector : 'with-prop-decorators' } )
class DirectiveWithPropDecorators {
2016-06-14 17:53:01 -04:00
target : any ;
2015-09-04 17:07:16 -04:00
2015-11-23 19:02:19 -05:00
@Input ( 'elProp' ) dirProp : string ;
2015-09-30 23:59:23 -04:00
@Output ( 'elEvent' ) event = new EventEmitter ( ) ;
2015-09-03 18:10:48 -04:00
2016-06-08 19:38:52 -04:00
@HostBinding ( 'attr.my-attr' ) myAttr : string ;
@HostListener ( 'click' , [ '$event.target' ] )
2016-06-14 17:53:01 -04:00
onClick ( target : any ) { this . target = target ; }
2015-09-04 17:07:16 -04:00
2016-08-02 18:53:34 -04:00
fireEvent ( msg : any ) { this . event . emit ( msg ) ; }
2015-09-11 16:45:31 -04:00
}
2016-01-08 15:01:29 -05:00
@Component ( { selector : 'some-cmp' } )
class SomeCmp {
value : any ;
}
2017-02-08 06:36:43 -05:00
@Component ( {
selector : 'parent-cmp' ,
template : ` <cmp [test $ ]="name"></cmp> ` ,
} )
export class ParentCmp {
name : string = 'hello' ;
}
@Component ( { selector : 'cmp' , template : '' } )
class SomeCmpWithInput {
@Input ( ) test$ : any ;
}