144 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			144 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| ///// Boiler Plate ////
 | |
| import {bind, Component, Directive, EventEmitter, FORM_DIRECTIVES, View} from 'angular2/angular2';
 | |
| 
 | |
| // Angular 2 Test Bed
 | |
| import {
 | |
|   beforeEachProviders, By, DebugElement, RootTestComponent as RTC,
 | |
|   beforeEach, ddescribe, xdescribe, describe, expect, iit, it, xit // Jasmine wrappers
 | |
| } from 'angular2/testing';
 | |
| 
 | |
| import {injectAsync, injectTcb} from '../test-helpers/test-helpers';
 | |
| 
 | |
| ///// Testing this component ////
 | |
| import {HeroDetailComponent} from './hero-detail.component';
 | |
| import {Hero} from './hero';
 | |
| 
 | |
| describe('HeroDetailComponent', () => {
 | |
| 
 | |
|   it('can be created', () => {
 | |
|     let hc = new HeroDetailComponent()
 | |
|     expect(hc instanceof HeroDetailComponent).toEqual(true); // proof of life
 | |
|   });
 | |
| 
 | |
|   it('parent "currentHero" flows down to HeroDetailComponent', injectTcb( tcb => {
 | |
|     return tcb
 | |
|       .createAsync(TestWrapper)
 | |
|       .then((rootTC:RTC) => {
 | |
|         let hc:HeroDetailComponent = rootTC.componentViewChildren[0].componentInstance;
 | |
|         let hw:TestWrapper = rootTC.componentInstance;
 | |
| 
 | |
|         rootTC.detectChanges(); // trigger view binding
 | |
| 
 | |
|         expect(hw.currentHero).toBe(hc.hero);
 | |
|       });
 | |
|   }));
 | |
| 
 | |
|   it('delete button should raise delete event for parent component', injectTcb( tcb => {
 | |
| 
 | |
|     return tcb
 | |
|       //.overrideTemplate(HeroDetailComponent, '<button (click)="onDelete()" [disabled]="!hero">Delete</button>')
 | |
|       .overrideDirective(TestWrapper, HeroDetailComponent, mockHDC)
 | |
|       .createAsync(TestWrapper)
 | |
|       .then((rootTC:RTC) => {
 | |
| 
 | |
|         let hw:TestWrapper = rootTC.componentInstance;
 | |
|         let hdcElement = rootTC.componentViewChildren[0];
 | |
|         let hdc:HeroDetailComponent = hdcElement.componentInstance;
 | |
| 
 | |
|         rootTC.detectChanges(); // trigger view binding
 | |
| 
 | |
|         // We can watch the HeroComponent.delete EventEmitter's event
 | |
|         let subscription = hdc.delete.toRx().subscribe(() => {
 | |
|           console.log('HeroComponent.delete event raised');
 | |
|           subscription.dispose();
 | |
|         });
 | |
| 
 | |
|         // We can EITHER invoke HeroComponent delete button handler OR
 | |
|         // trigger the 'click' event on the delete HeroComponent button
 | |
|         // BUT DON'T DO BOTH
 | |
| 
 | |
|         // Trigger event
 | |
|         // FRAGILE because assumes precise knowledge of HeroComponent template
 | |
|         hdcElement
 | |
|           .query(By.css('#delete'))
 | |
|           .triggerEventHandler('click', {});
 | |
| 
 | |
|         hw.testCallback = () => {
 | |
|           // if wrapper.onDelete is called, HeroComponent.delete event must have been raised
 | |
|           //console.log('HeroWrapper.onDelete called');
 | |
|           expect(true).toEqual(true);
 | |
|         }
 | |
|         // hc.onDelete();
 | |
|       });
 | |
|   }), 500); // needs some time for event to complete; 100ms is not long enough
 | |
| 
 | |
|   it('update button should modify hero', injectTcb( tcb => {
 | |
| 
 | |
|      return tcb
 | |
|       .createAsync(TestWrapper)
 | |
|       .then((rootTC:RTC) => {
 | |
| 
 | |
|         let hc:HeroDetailComponent = rootTC.componentViewChildren[0].componentInstance;
 | |
|         let hw:TestWrapper = rootTC.componentInstance;
 | |
|         let origNameLength = hw.currentHero.name.length;
 | |
| 
 | |
|         rootTC.detectChanges(); // trigger view binding
 | |
| 
 | |
|         // We can EITHER invoke HeroComponent update button handler OR
 | |
|         // trigger the 'click' event on the HeroComponent update button
 | |
|         // BUT DON'T DO BOTH
 | |
| 
 | |
|         // Trigger event
 | |
|         // FRAGILE because assumes precise knowledge of HeroComponent template
 | |
|         rootTC.componentViewChildren[0]
 | |
|           .componentViewChildren[2]
 | |
|           .triggerEventHandler('click', {});
 | |
| 
 | |
|         // hc.onUpdate(); // Invoke button handler
 | |
|         expect(hw.currentHero.name.length).toBeGreaterThan(origNameLength);
 | |
|       });
 | |
|   }));
 | |
| 
 | |
| });
 | |
| 
 | |
| ///// Test Components ////////
 | |
| 
 | |
| // TestWrapper is a convenient way to communicate w/ HeroDetailComponent in a test
 | |
| @Component({selector: 'hero-wrapper'})
 | |
| @View({
 | |
|   template: `<my-hero-detail [hero]="currentHero" [user-name]="userName" (delete)="onDelete()"></my-hero-detail>`,
 | |
|   directives: [HeroDetailComponent]
 | |
| })
 | |
| class TestWrapper {
 | |
|   currentHero = new Hero(42, 'Cat Woman');
 | |
|   userName = 'Sally';
 | |
|   testCallback() {} // monkey-punched in a test
 | |
|   onDelete() { this.testCallback(); }
 | |
| }
 | |
| 
 | |
| @View({
 | |
|   template: `
 | |
|     <div>
 | |
|       <h2>{{hero.name}} | {{userName}}</h2>
 | |
|       <button id="delete" (click)="onDelete()" [disabled]="!hero">Delete</button>
 | |
|       <button id="update" (click)="onUpdate()" [disabled]="!hero">Update</button>
 | |
|       <div id="id">{{hero.id}}</div>
 | |
|       <input [(ngModel)]="hero.name"/>
 | |
|     </div>`,
 | |
|   directives: [FORM_DIRECTIVES]
 | |
| })
 | |
| class mockHDC //extends HeroDetailComponent { }
 | |
| {
 | |
|     hero: Hero;
 | |
| 
 | |
|   delete = new EventEmitter();
 | |
| 
 | |
|   onDelete() { this.delete.next(this.hero) }
 | |
| 
 | |
|   onUpdate() {
 | |
|     if (this.hero) {
 | |
|       this.hero.name += 'x';
 | |
|     }
 | |
|   }
 | |
|   userName: string;
 | |
| } |