199 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
		
		
			
		
	
	
			199 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
|  | // Test a service when Angular DI is in play | ||
|  | 
 | ||
|  | // Angular 2 Test Bed | ||
|  | import { | ||
|  |   beforeEach, xdescribe, describe, it, xit, // Jasmine wrappers | ||
|  |   beforeEachProviders, inject, injectAsync, | ||
|  | } from 'angular2/testing'; | ||
|  | 
 | ||
|  | import {bind} from 'angular2/core'; | ||
|  | 
 | ||
|  | // Service related imports | ||
|  | import {HeroService} from './hero.service'; | ||
|  | import {BackendService} from './backend.service'; | ||
|  | import {Hero} from './hero'; | ||
|  | 
 | ||
|  | //////  tests //////////// | ||
|  | 
 | ||
|  | describe('HeroService (with angular DI)', () => { | ||
|  | 
 | ||
|  |   beforeEachProviders(() => [HeroService]); | ||
|  | 
 | ||
|  |   describe('creation', () => { | ||
|  | 
 | ||
|  |     beforeEachProviders( () => [bind(BackendService).toValue(null)] ); | ||
|  | 
 | ||
|  |     it('can instantiate the service', | ||
|  |       inject([HeroService], (service: HeroService) => { | ||
|  |         expect(service).toBeDefined(); | ||
|  |       })); | ||
|  | 
 | ||
|  |     it('service.heroes is empty', | ||
|  |       inject([HeroService], (service: HeroService) => { | ||
|  |         expect(service.heroes.length).toEqual(0); | ||
|  |       })); | ||
|  |   }); | ||
|  | 
 | ||
|  |   describe('#refresh', () => { | ||
|  | 
 | ||
|  |     describe('when backend provides data', () => { | ||
|  | 
 | ||
|  |       beforeEach(() => { | ||
|  |         heroData = [new Hero(1, 'Foo'), new Hero(2, 'Bar'), new Hero(3,'Baz')]; | ||
|  |       }); | ||
|  | 
 | ||
|  |       beforeEachProviders(() => | ||
|  |         [bind(BackendService).toClass(HappyBackendService)] | ||
|  |       ); | ||
|  | 
 | ||
|  |       it('refresh promise returns expected # of heroes when fulfilled', | ||
|  |         injectAsync([HeroService], (service: HeroService) => { | ||
|  | 
 | ||
|  |           return service.refresh().then(heroes => | ||
|  |               expect(heroes.length).toEqual(heroData.length) | ||
|  |             ); | ||
|  |         })); | ||
|  | 
 | ||
|  |       it('service.heroes has expected # of heroes when fulfilled', | ||
|  |         injectAsync([HeroService], (service: HeroService) => { | ||
|  | 
 | ||
|  |         return service.refresh().then(() => | ||
|  |             expect(service.heroes.length).toEqual(heroData.length) | ||
|  |           ); | ||
|  |       })); | ||
|  | 
 | ||
|  |       it('service.heroes remains empty until fulfilled', | ||
|  |         inject([HeroService], (service: HeroService) => { | ||
|  | 
 | ||
|  |         service.refresh(); | ||
|  | 
 | ||
|  |         // executed before refresh completes | ||
|  |         expect(service.heroes.length).toEqual(0); | ||
|  |       })); | ||
|  | 
 | ||
|  |       it('service.heroes remains empty when the server returns no data', | ||
|  |         injectAsync([HeroService], (service: HeroService) => { | ||
|  | 
 | ||
|  |         heroData = []; // simulate no heroes from the backend | ||
|  | 
 | ||
|  |         return service.refresh().then(() => | ||
|  |             expect(service.heroes.length).toEqual(0) | ||
|  |           ); | ||
|  |       })); | ||
|  | 
 | ||
|  |       it('resets service.heroes w/ original data after re-refresh', | ||
|  |         injectAsync([HeroService], (service: HeroService) => { | ||
|  | 
 | ||
|  |         let firstHeroes: Hero[]; | ||
|  |         let changedName = 'Gerry Mander'; | ||
|  | 
 | ||
|  |         return service.refresh().then(heroes => { | ||
|  |             firstHeroes = heroes;  // remember array reference | ||
|  | 
 | ||
|  |             // Changes to cache!  Should disappear after refresh | ||
|  |             service.heroes[0].name = changedName; | ||
|  |             service.heroes.push(new Hero(33, 'Hercules')); | ||
|  |             return service.refresh() | ||
|  |           }) | ||
|  |           .then(() => { | ||
|  |             expect(firstHeroes).toBe(service.heroes); // same object | ||
|  |             expect(service.heroes.length).toEqual(heroData.length); // no Hercules | ||
|  |             expect(service.heroes[0].name).not.toEqual(changedName); // reverted name change | ||
|  |           }); | ||
|  |       })); | ||
|  | 
 | ||
|  |       it('clears service.heroes while waiting for re-refresh', | ||
|  |         injectAsync([HeroService], (service: HeroService) => { | ||
|  | 
 | ||
|  |         return service.refresh().then(() => { | ||
|  |             service.refresh(); | ||
|  |             expect(service.heroes.length).toEqual(0); | ||
|  |           }); | ||
|  |       })); | ||
|  |       // the paranoid will verify not only that the array lengths are the same | ||
|  |       // but also that the contents are the same. | ||
|  |       it('service.heroes has expected heroes when fulfilled (paranoia)', | ||
|  |         injectAsync([HeroService], (service: HeroService) => { | ||
|  | 
 | ||
|  |         return service.refresh().then(() => { | ||
|  |             expect(service.heroes.length).toEqual(heroData.length); | ||
|  |             service.heroes.forEach(h => | ||
|  |               expect(heroData.some( | ||
|  |                 // hero instances are not the same objects but | ||
|  |                 // each hero in result matches an original hero by value | ||
|  |                 hd => hd.name === h.name && hd.id === h.id) | ||
|  |               ) | ||
|  |             ); | ||
|  |           }); | ||
|  |       })); | ||
|  | 
 | ||
|  |     }); | ||
|  | 
 | ||
|  |     describe('when backend throws an error', () => { | ||
|  | 
 | ||
|  |       beforeEachProviders(() => | ||
|  |         [bind(BackendService).toClass(FailingBackendService)] | ||
|  |       ); | ||
|  | 
 | ||
|  |       it('returns failed promise with the server error', | ||
|  |         injectAsync([HeroService], (service: HeroService) => { | ||
|  | 
 | ||
|  |         return service.refresh() | ||
|  |           .then(() => fail('refresh should have failed')) | ||
|  |           .catch(err => expect(err).toBe(testError)); | ||
|  |       })); | ||
|  | 
 | ||
|  |       it('resets heroes array to empty', | ||
|  |         injectAsync([HeroService], (service: HeroService) => { | ||
|  | 
 | ||
|  |         return service.refresh() | ||
|  |           .then(() => fail('refresh should have failed')) | ||
|  |           .catch(err => expect(service.heroes.length).toEqual(0)) | ||
|  |       })); | ||
|  |     }); | ||
|  | 
 | ||
|  |      describe('when backend throws an error (spy version)', () => { | ||
|  | 
 | ||
|  |       beforeEachProviders(() => [BackendService]); | ||
|  | 
 | ||
|  |       beforeEach(inject([BackendService], (backend: BackendService) => | ||
|  |         spyOn(backend, 'fetchAllHeroesAsync').and.callFake(() => Promise.reject(testError) | ||
|  |       ))); | ||
|  | 
 | ||
|  |       it('returns failed promise with the server error', | ||
|  |         injectAsync([HeroService], (service: HeroService) => { | ||
|  | 
 | ||
|  |         return service.refresh() | ||
|  |           .then(() => fail('refresh should have failed')) | ||
|  |           .catch(err => expect(err).toBe(testError)); | ||
|  |       })); | ||
|  | 
 | ||
|  |       it('resets heroes array to empty', | ||
|  |         injectAsync([HeroService], (service: HeroService) => { | ||
|  | 
 | ||
|  |         return service.refresh() | ||
|  |           .then(() => fail('refresh should have failed')) | ||
|  |           .catch(err => expect(service.heroes.length).toEqual(0)) | ||
|  |       })); | ||
|  |     }); | ||
|  | 
 | ||
|  |   }); | ||
|  | }); | ||
|  | ///////// test helpers ///////// | ||
|  | var service: HeroService; | ||
|  | var heroData: Hero[]; | ||
|  | 
 | ||
|  | class HappyBackendService { | ||
|  |   // return a promise for fake heroes that resolves as quickly as possible | ||
|  |   fetchAllHeroesAsync = () => | ||
|  |     Promise.resolve<Hero[]>(heroData.map(h => h.clone())); | ||
|  | } | ||
|  | 
 | ||
|  | var testError = 'BackendService.fetchAllHeroesAsync failed on purpose'; | ||
|  | 
 | ||
|  | class FailingBackendService { | ||
|  |   // return a promise that fails as quickly as possible | ||
|  |   fetchAllHeroesAsync = () => | ||
|  |     Promise.reject(testError); | ||
|  | } |