219 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			219 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| ///// Boiler Plate ////
 | |
| import {bind, By, Component, Directive, EventEmitter, FORM_DIRECTIVES} from 'angular2/angular2';
 | |
| 
 | |
| // Angular 2 Test Bed
 | |
| import {
 | |
| beforeEachProviders, inject, injectAsync, RootTestComponent as RTC,
 | |
| beforeEach, ddescribe, xdescribe, describe, expect, iit, it, xit // Jasmine wrappers
 | |
| } from 'angular2/testing';
 | |
| 
 | |
| import {dispatchEvent, DoneFn, injectTcb, tick} from '../test-helpers/test-helpers';
 | |
| 
 | |
| ///// Testing this component ////
 | |
| import {HeroDetailComponent} from './hero-detail.component';
 | |
| import {Hero} from './hero';
 | |
| 
 | |
| describe('HeroDetailComponent', () => {
 | |
| 
 | |
|   /////////// Component Tests without DOM interaction /////////////
 | |
|   describe('(No DOM)', () => {
 | |
|     it('can be created', () => {
 | |
|       let hdc = new HeroDetailComponent();
 | |
|       expect(hdc instanceof HeroDetailComponent).toEqual(true); // proof of life
 | |
|     });
 | |
| 
 | |
|     it('onDelete method should raise delete event', (done: DoneFn) => {
 | |
|       let hdc = new HeroDetailComponent();
 | |
| 
 | |
|       // Listen for the HeroComponent.delete EventEmitter's event
 | |
|       hdc.delete.toRx().subscribe(() => {
 | |
|         console.log('HeroComponent.delete event raised');
 | |
|         done();  // it must have worked
 | |
|       }, (error: any) => { fail(error); done() });
 | |
| 
 | |
|       hdc.onDelete();
 | |
|     });
 | |
| 
 | |
|     // Disable until toPromise() works again
 | |
|     xit('onDelete method should raise delete event (w/ promise)', (done: DoneFn) => {
 | |
| 
 | |
|       let hdc = new HeroDetailComponent();
 | |
| 
 | |
|       // Listen for the HeroComponent.delete EventEmitter's event
 | |
|       let p = hdc.delete.toRx()
 | |
|         .toPromise()
 | |
|         .then(() => {
 | |
|           console.log('HeroComponent.delete event raised in promise');
 | |
|         })
 | |
|         .then(done, done.fail);
 | |
| 
 | |
|       hdc.delete.toRx()
 | |
|         .subscribe(() => {
 | |
|           console.log('HeroComponent.delete event raised in subscription')
 | |
|         });
 | |
| 
 | |
|       hdc.onDelete();
 | |
| 
 | |
|       // toPromise() does not fulfill until emitter is completed by `return()`
 | |
|       hdc.delete.return();
 | |
|     });
 | |
| 
 | |
|     it('onUpdate method should modify hero', () => {
 | |
|       let hdc = new HeroDetailComponent();
 | |
|       hdc.hero = new Hero(42, 'Cat Woman');
 | |
|       let origNameLength = hdc.hero.name.length;
 | |
| 
 | |
|       hdc.onUpdate();
 | |
|       expect(hdc.hero.name.length).toBeGreaterThan(origNameLength);
 | |
|     });
 | |
|   });
 | |
| 
 | |
| 
 | |
|   /////////// Component tests that check the DOM /////////////
 | |
|   describe('(DOM)', () => {
 | |
|     // Disable until toPromise() works again
 | |
|     xit('Delete button should raise delete event', injectTcb(tcb => {
 | |
| 
 | |
|       // We only care about the button
 | |
|       let template = '<button (click)="onDelete()">Delete</button>';
 | |
| 
 | |
|       return tcb
 | |
|         .overrideTemplate(HeroDetailComponent, template)
 | |
|         .createAsync(HeroDetailComponent)
 | |
|         .then((rootTC: RTC) => {
 | |
|           let hdc: HeroDetailComponent = rootTC.debugElement.componentInstance;
 | |
| 
 | |
|           // // USE PROMISE WRAPPING AN OBSERVABLE UNTIL can get `toPromise` working again
 | |
|           // let p = new Promise<Hero>((resolve) => {
 | |
|           //   // Listen for the HeroComponent.delete EventEmitter's event with observable
 | |
|           //   hdc.delete.toRx().subscribe((hero: Hero) => {
 | |
|           //     console.log('Observable heard HeroComponent.delete event raised');
 | |
|           //     resolve(hero);
 | |
|           //   });
 | |
|           // })
 | |
| 
 | |
|           //Listen for the HeroComponent.delete EventEmitter's event with promise
 | |
|           let p = <Promise<Hero>> hdc.delete.toRx().toPromise()
 | |
|           .then((hero:Hero) => {
 | |
|             console.log('Promise heard HeroComponent.delete event raised');
 | |
|           });
 | |
| 
 | |
|           // trigger the 'click' event on the HeroDetailComponent delete button
 | |
|           let el = rootTC.debugElement.query(By.css('button'));
 | |
|           el.triggerEventHandler('click', null);
 | |
| 
 | |
|           // toPromise() does not fulfill until emitter is completed by `return()`
 | |
|           hdc.delete.return();
 | |
| 
 | |
|           return p;
 | |
|         });
 | |
| 
 | |
|     }));
 | |
| 
 | |
|     it('Update button should modify hero', injectTcb(tcb => {
 | |
| 
 | |
|       let template =
 | |
|         `<div>
 | |
|           <button id="update" (click)="onUpdate()" [disabled]="!hero">Update</button>
 | |
|           <input [(ngModel)]="hero.name"/>
 | |
|         </div>`
 | |
| 
 | |
|       return tcb
 | |
|         .overrideTemplate(HeroDetailComponent, template)
 | |
|         .createAsync(HeroDetailComponent)
 | |
|         .then((rootTC: RTC) => {
 | |
| 
 | |
|           let hdc: HeroDetailComponent = rootTC.debugElement.componentInstance;
 | |
|           hdc.hero = new Hero(42, 'Cat Woman');
 | |
|           let origNameLength = hdc.hero.name.length;
 | |
| 
 | |
|           // trigger the 'click' event on the HeroDetailComponent update button
 | |
|           rootTC.debugElement.query(By.css('#update'))
 | |
|             .triggerEventHandler('click', null);
 | |
| 
 | |
|           expect(hdc.hero.name.length).toBeGreaterThan(origNameLength);
 | |
|         });
 | |
|     }));
 | |
| 
 | |
|     it('Entering hero name in textbox changes hero', injectTcb(tcb => {
 | |
| 
 | |
|       let hdc: HeroDetailComponent
 | |
|       let template = `<input [(ngModel)]="hero.name"/>`
 | |
| 
 | |
|       return tcb
 | |
|         .overrideTemplate(HeroDetailComponent, template)
 | |
|         .createAsync(HeroDetailComponent)
 | |
|         .then((rootTC: RTC) => {
 | |
| 
 | |
|           hdc = rootTC.debugElement.componentInstance;
 | |
| 
 | |
|           hdc.hero = new Hero(42, 'Cat Woman');
 | |
|           rootTC.detectChanges();
 | |
| 
 | |
|           // get the HTML element and change its value in the DOM
 | |
|           var input = rootTC.debugElement.query(By.css('input')).nativeElement;
 | |
|           input.value = "Dog Man"
 | |
|           dispatchEvent(input, 'change'); // event triggers Ng to update model
 | |
| 
 | |
|           rootTC.detectChanges();
 | |
|           // model update hasn't happened yet, despite `detectChanges`
 | |
|           expect(hdc.hero.name).toEqual('Cat Woman');
 | |
| 
 | |
|         })
 | |
|         .then(tick) // must wait a tick for the model update
 | |
|         .then(() => {
 | |
|           expect(hdc.hero.name).toEqual('Dog Man');
 | |
|         });
 | |
|     }));
 | |
| 
 | |
|     // Simulates ...
 | |
|     // 1. change a hero
 | |
|     // 2. select a different hero
 | |
|     // 3  re-select the first hero
 | |
|     // 4. confirm that the change is preserved in HTML
 | |
|     // Reveals 2-way binding bug in alpha-36, fixed in pull #3715 for alpha-37
 | |
| 
 | |
|     it('toggling heroes after modifying name preserves the change on screen', injectTcb(tcb => {
 | |
| 
 | |
|       let hdc: HeroDetailComponent;
 | |
|       let hero1 = new Hero(1, 'Cat Woman');
 | |
|       let hero2 = new Hero(2, 'Goat Boy');
 | |
|       let input: HTMLInputElement;
 | |
|       let rootTC: RTC;
 | |
|       let template = `{{hero.id}} - <input [(ngModel)]="hero.name"/>`
 | |
| 
 | |
|       return tcb
 | |
|         .overrideTemplate(HeroDetailComponent, template)
 | |
|         .createAsync(HeroDetailComponent)
 | |
|         .then((rtc: RTC) => {
 | |
|           rootTC = rtc;
 | |
|           hdc = rootTC.debugElement.componentInstance;
 | |
| 
 | |
|           hdc.hero = hero1; // start with hero1
 | |
|           rootTC.detectChanges();
 | |
| 
 | |
|           // get the HTML element and change its value in the DOM
 | |
|           input = rootTC.debugElement.query(By.css('input')).nativeElement;
 | |
|           input.value = "Dog Man"
 | |
|           dispatchEvent(input, 'change'); // event triggers Ng to update model
 | |
|         })
 | |
|         .then(tick) // must wait a tick for the model update
 | |
|         .then(() => {
 | |
|           expect(hdc.hero.name).toEqual('Dog Man');
 | |
| 
 | |
|           hdc.hero = hero2 // switch to hero2
 | |
|           rootTC.detectChanges();
 | |
| 
 | |
|           hdc.hero = hero1  // switch back to hero1
 | |
|           rootTC.detectChanges();
 | |
| 
 | |
|           // model value will be the same changed value (of course)
 | |
|           expect(hdc.hero.name).toEqual('Dog Man');
 | |
| 
 | |
|           // the view should reflect the same changed value
 | |
|           expect(input.value).toEqual('Dog Man');
 | |
|         });
 | |
|     }));
 | |
|   });
 | |
| });
 |