| 
									
										
										
										
											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
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import {AnimationEntryMetadata, ChangeDetectorRef, ComponentFactory, ComponentRef, ComponentResolver, DebugElement, ElementRef, Injectable, Injector, NgZone, NgZoneError, OpaqueToken, ViewMetadata, getDebugNode} from '../index'; | 
					
						
							|  |  |  | import {ObservableWrapper, PromiseCompleter, PromiseWrapper} from '../src/facade/async'; | 
					
						
							|  |  |  | import {BaseException} from '../src/facade/exceptions'; | 
					
						
							|  |  |  | import {scheduleMicroTask} from '../src/facade/lang'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import {tick} from './fake_async'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * 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; | 
					
						
							|  |  |  |   private _completer: PromiseCompleter<any> = null; | 
					
						
							|  |  |  |   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 = | 
					
						
							|  |  |  |           ObservableWrapper.subscribe(ngZone.onUnstable, (_) => { this._isStable = false; }); | 
					
						
							|  |  |  |       this._onMicrotaskEmptySubscription = | 
					
						
							|  |  |  |           ObservableWrapper.subscribe(ngZone.onMicrotaskEmpty, (_) => { | 
					
						
							|  |  |  |             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 = ObservableWrapper.subscribe(ngZone.onStable, (_) => { | 
					
						
							|  |  |  |         this._isStable = true; | 
					
						
							|  |  |  |         // Check whether there are no pending macrotasks in a microtask so that ngZone gets a chance
 | 
					
						
							|  |  |  |         // to update the state of pending macrotasks.
 | 
					
						
							|  |  |  |         scheduleMicroTask(() => { | 
					
						
							|  |  |  |           if (!this.ngZone.hasPendingMacrotasks) { | 
					
						
							|  |  |  |             if (this._completer != null) { | 
					
						
							|  |  |  |               this._completer.resolve(true); | 
					
						
							|  |  |  |               this._completer = null; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       this._onErrorSubscription = ObservableWrapper.subscribe( | 
					
						
							|  |  |  |           ngZone.onError, (error: NgZoneError) => { throw error.error; }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   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) { | 
					
						
							|  |  |  |       throw new BaseException('Cannot call autoDetectChanges when ComponentFixtureNoNgZone is set'); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     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()) { | 
					
						
							|  |  |  |       return PromiseWrapper.resolve(false); | 
					
						
							|  |  |  |     } else if (this._completer !== null) { | 
					
						
							|  |  |  |       return this._completer.promise; | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       this._completer = new PromiseCompleter<any>(); | 
					
						
							|  |  |  |       return this._completer.promise; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Trigger component destruction. | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   destroy(): void { | 
					
						
							|  |  |  |     this.componentRef.destroy(); | 
					
						
							|  |  |  |     if (this._onUnstableSubscription != null) { | 
					
						
							|  |  |  |       ObservableWrapper.dispose(this._onUnstableSubscription); | 
					
						
							|  |  |  |       this._onUnstableSubscription = null; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (this._onStableSubscription != null) { | 
					
						
							|  |  |  |       ObservableWrapper.dispose(this._onStableSubscription); | 
					
						
							|  |  |  |       this._onStableSubscription = null; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (this._onMicrotaskEmptySubscription != null) { | 
					
						
							|  |  |  |       ObservableWrapper.dispose(this._onMicrotaskEmptySubscription); | 
					
						
							|  |  |  |       this._onMicrotaskEmptySubscription = null; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (this._onErrorSubscription != null) { | 
					
						
							|  |  |  |       ObservableWrapper.dispose(this._onErrorSubscription); | 
					
						
							|  |  |  |       this._onErrorSubscription = null; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } |