| 
									
										
										
										
											2016-06-24 12:41:49 -07:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @license | 
					
						
							|  |  |  |  * Copyright Google Inc. All Rights Reserved. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Use of this source code is governed by an MIT-style license that can be | 
					
						
							|  |  |  |  * found in the LICENSE file at https://angular.io/license
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-30 18:07:40 -07:00
										 |  |  | import {ChangeDetectorRef, ComponentRef, DebugElement, ElementRef, NgZone, getDebugNode} from '@angular/core'; | 
					
						
							|  |  |  | import {scheduleMicroTask} from './facade/lang'; | 
					
						
							| 
									
										
										
										
											2016-06-24 12:41:49 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Fixture for debugging and testing a component. | 
					
						
							| 
									
										
										
										
											2016-06-27 12:27:23 -07:00
										 |  |  |  * | 
					
						
							|  |  |  |  * @stable | 
					
						
							| 
									
										
										
										
											2016-06-24 12:41:49 -07:00
										 |  |  |  */ | 
					
						
							|  |  |  | export class ComponentFixture<T> { | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * The DebugElement associated with the root element of this component. | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   debugElement: DebugElement; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * The instance of the root component class. | 
					
						
							|  |  |  |    */ | 
					
						
							| 
									
										
											  
											
												feat(browser): use AppModules for bootstrap in the browser
This introduces the `BrowserModule` to be used for long form
bootstrap and offline compile bootstrap:
```
@AppModule({
  modules: [BrowserModule],
  precompile: [MainComponent],
  providers: […], // additional providers
  directives: […], // additional platform directives
  pipes: […] // additional platform pipes
})
class MyModule {
  constructor(appRef: ApplicationRef) {
    appRef.bootstrap(MainComponent);
  }
}
// offline compile
import {bootstrapModuleFactory} from ‘@angular/platform-browser’;
bootstrapModuleFactory(MyModuleNgFactory);
// runtime compile long form
import {bootstrapModule} from ‘@angular/platform-browser-dynamic’;
bootstrapModule(MyModule);
```
The short form, `bootstrap(...)`, can now creates a module on the fly,
given `directives`, `pipes, `providers`, `precompile` and `modules`
properties.
Related changes:
- make `SanitizationService`, `SecurityContext` public in `@angular/core` so that the offline compiler can resolve the token
- move `AnimationDriver` to `platform-browser` and make it
  public so that the offline compiler can resolve the token
BREAKING CHANGES:
- short form bootstrap does no longer allow
  to inject compiler internals (i.e. everything 
  from `@angular/compiler). Inject `Compiler` instead.
  To provide custom providers for the compiler,
  create a custom compiler via `browserCompiler({providers: [...]})`
  and pass that into the `bootstrap` method.
											
										 
											2016-06-30 13:07:17 -07:00
										 |  |  |   componentInstance: T; | 
					
						
							| 
									
										
										
										
											2016-06-24 12:41:49 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * The native element at the root of the component. | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   nativeElement: any; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * The ElementRef for the element at the root of the component. | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   elementRef: ElementRef; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * The ComponentRef for the component | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   componentRef: ComponentRef<T>; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * The ChangeDetectorRef for the component | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   changeDetectorRef: ChangeDetectorRef; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * The NgZone in which this component was instantiated. | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   ngZone: NgZone; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   private _autoDetect: boolean; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   private _isStable: boolean = true; | 
					
						
							| 
									
										
										
										
											2016-08-25 23:37:46 +02:00
										 |  |  |   private _isDestroyed: boolean = false; | 
					
						
							| 
									
										
										
										
											2016-08-02 15:53:34 -07:00
										 |  |  |   private _resolve: (result: any) => void; | 
					
						
							|  |  |  |   private _promise: Promise<any> = null; | 
					
						
							| 
									
										
										
										
											2016-06-24 12:41:49 -07:00
										 |  |  |   private _onUnstableSubscription: any /** TODO #9100 */ = null; | 
					
						
							|  |  |  |   private _onStableSubscription: any /** TODO #9100 */ = null; | 
					
						
							|  |  |  |   private _onMicrotaskEmptySubscription: any /** TODO #9100 */ = null; | 
					
						
							|  |  |  |   private _onErrorSubscription: any /** TODO #9100 */ = null; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   constructor(componentRef: ComponentRef<T>, ngZone: NgZone, autoDetect: boolean) { | 
					
						
							|  |  |  |     this.changeDetectorRef = componentRef.changeDetectorRef; | 
					
						
							|  |  |  |     this.elementRef = componentRef.location; | 
					
						
							|  |  |  |     this.debugElement = <DebugElement>getDebugNode(this.elementRef.nativeElement); | 
					
						
							|  |  |  |     this.componentInstance = componentRef.instance; | 
					
						
							|  |  |  |     this.nativeElement = this.elementRef.nativeElement; | 
					
						
							|  |  |  |     this.componentRef = componentRef; | 
					
						
							|  |  |  |     this.ngZone = ngZone; | 
					
						
							|  |  |  |     this._autoDetect = autoDetect; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (ngZone != null) { | 
					
						
							|  |  |  |       this._onUnstableSubscription = | 
					
						
							| 
									
										
										
										
											2016-08-02 15:53:34 -07:00
										 |  |  |           ngZone.onUnstable.subscribe({next: () => { this._isStable = false; }}); | 
					
						
							|  |  |  |       this._onMicrotaskEmptySubscription = ngZone.onMicrotaskEmpty.subscribe({ | 
					
						
							|  |  |  |         next: () => { | 
					
						
							|  |  |  |           if (this._autoDetect) { | 
					
						
							|  |  |  |             // Do a change detection run with checkNoChanges set to true to check
 | 
					
						
							|  |  |  |             // there are no changes on the second run.
 | 
					
						
							|  |  |  |             this.detectChanges(true); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |       this._onStableSubscription = ngZone.onStable.subscribe({ | 
					
						
							|  |  |  |         next: () => { | 
					
						
							|  |  |  |           this._isStable = true; | 
					
						
							|  |  |  |           // Check whether there is a pending whenStable() completer to resolve.
 | 
					
						
							|  |  |  |           if (this._promise !== null) { | 
					
						
							|  |  |  |             // If so check whether there are no pending macrotasks before resolving.
 | 
					
						
							|  |  |  |             // Do this check in the next tick so that ngZone gets a chance to update the state of
 | 
					
						
							|  |  |  |             // pending macrotasks.
 | 
					
						
							|  |  |  |             scheduleMicroTask(() => { | 
					
						
							|  |  |  |               if (!this.ngZone.hasPendingMacrotasks) { | 
					
						
							|  |  |  |                 if (this._promise !== null) { | 
					
						
							|  |  |  |                   this._resolve(true); | 
					
						
							|  |  |  |                   this._resolve = null; | 
					
						
							|  |  |  |                   this._promise = null; | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2016-07-22 16:07:11 -07:00
										 |  |  |               } | 
					
						
							| 
									
										
										
										
											2016-08-02 15:53:34 -07:00
										 |  |  |             }); | 
					
						
							|  |  |  |           } | 
					
						
							| 
									
										
										
										
											2016-07-22 16:07:11 -07:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2016-06-24 12:41:49 -07:00
										 |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-02 15:53:34 -07:00
										 |  |  |       this._onErrorSubscription = | 
					
						
							| 
									
										
										
										
											2016-08-15 16:10:30 -07:00
										 |  |  |           ngZone.onError.subscribe({next: (error: any) => { throw error; }}); | 
					
						
							| 
									
										
										
										
											2016-06-24 12:41:49 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   private _tick(checkNoChanges: boolean) { | 
					
						
							|  |  |  |     this.changeDetectorRef.detectChanges(); | 
					
						
							|  |  |  |     if (checkNoChanges) { | 
					
						
							|  |  |  |       this.checkNoChanges(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Trigger a change detection cycle for the component. | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   detectChanges(checkNoChanges: boolean = true): void { | 
					
						
							|  |  |  |     if (this.ngZone != null) { | 
					
						
							|  |  |  |       // Run the change detection inside the NgZone so that any async tasks as part of the change
 | 
					
						
							|  |  |  |       // detection are captured by the zone and can be waited for in isStable.
 | 
					
						
							|  |  |  |       this.ngZone.run(() => { this._tick(checkNoChanges); }); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       // Running without zone. Just do the change detection.
 | 
					
						
							|  |  |  |       this._tick(checkNoChanges); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Do a change detection run to make sure there were no changes. | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   checkNoChanges(): void { this.changeDetectorRef.checkNoChanges(); } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Set whether the fixture should autodetect changes. | 
					
						
							|  |  |  |    * | 
					
						
							|  |  |  |    * Also runs detectChanges once so that any existing change is detected. | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   autoDetectChanges(autoDetect: boolean = true) { | 
					
						
							|  |  |  |     if (this.ngZone == null) { | 
					
						
							| 
									
										
										
										
											2016-08-25 00:50:16 -07:00
										 |  |  |       throw new Error('Cannot call autoDetectChanges when ComponentFixtureNoNgZone is set'); | 
					
						
							| 
									
										
										
										
											2016-06-24 12:41:49 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |     this._autoDetect = autoDetect; | 
					
						
							|  |  |  |     this.detectChanges(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Return whether the fixture is currently stable or has async tasks that have not been completed | 
					
						
							|  |  |  |    * yet. | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   isStable(): boolean { return this._isStable && !this.ngZone.hasPendingMacrotasks; } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Get a promise that resolves when the fixture is stable. | 
					
						
							|  |  |  |    * | 
					
						
							|  |  |  |    * This can be used to resume testing after events have triggered asynchronous activity or | 
					
						
							|  |  |  |    * asynchronous change detection. | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   whenStable(): Promise<any> { | 
					
						
							|  |  |  |     if (this.isStable()) { | 
					
						
							| 
									
										
										
										
											2016-08-02 15:53:34 -07:00
										 |  |  |       return Promise.resolve(false); | 
					
						
							|  |  |  |     } else if (this._promise !== null) { | 
					
						
							|  |  |  |       return this._promise; | 
					
						
							| 
									
										
										
										
											2016-06-24 12:41:49 -07:00
										 |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2016-08-02 15:53:34 -07:00
										 |  |  |       this._promise = new Promise(res => { this._resolve = res; }); | 
					
						
							|  |  |  |       return this._promise; | 
					
						
							| 
									
										
										
										
											2016-06-24 12:41:49 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Trigger component destruction. | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   destroy(): void { | 
					
						
							| 
									
										
										
										
											2016-08-25 23:37:46 +02:00
										 |  |  |     if (!this._isDestroyed) { | 
					
						
							|  |  |  |       this.componentRef.destroy(); | 
					
						
							|  |  |  |       if (this._onUnstableSubscription != null) { | 
					
						
							|  |  |  |         this._onUnstableSubscription.unsubscribe(); | 
					
						
							|  |  |  |         this._onUnstableSubscription = null; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       if (this._onStableSubscription != null) { | 
					
						
							|  |  |  |         this._onStableSubscription.unsubscribe(); | 
					
						
							|  |  |  |         this._onStableSubscription = null; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       if (this._onMicrotaskEmptySubscription != null) { | 
					
						
							|  |  |  |         this._onMicrotaskEmptySubscription.unsubscribe(); | 
					
						
							|  |  |  |         this._onMicrotaskEmptySubscription = null; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       if (this._onErrorSubscription != null) { | 
					
						
							|  |  |  |         this._onErrorSubscription.unsubscribe(); | 
					
						
							|  |  |  |         this._onErrorSubscription = null; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       this._isDestroyed = true; | 
					
						
							| 
									
										
										
										
											2016-06-24 12:41:49 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } |