151 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			151 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| // Test a service without referencing Angular (no Angular DI)
 | |
| import {HeroService} from './hero.service';
 | |
| import {BackendService} from './backend.service';
 | |
| import {Hero} from './hero';
 | |
| 
 | |
| //////  tests ////////////
 | |
| 
 | |
| describe('HeroService (no-angular)', () => {
 | |
| 
 | |
|   describe('creation', () => {
 | |
|     it('can instantiate the service', () => {
 | |
|       let service = new HeroService(null);
 | |
|       expect(service).toBeDefined();
 | |
|     });
 | |
| 
 | |
|     it('service.heroes is empty', () => {
 | |
|       let service = new HeroService(null);
 | |
|       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')];
 | |
|         service = new HeroService(new HappyBackendService());
 | |
|       });
 | |
| 
 | |
| 
 | |
|       it('refresh promise returns expected # of heroes when fulfilled', done => {
 | |
|         service.refresh().then(heroes =>
 | |
|           expect(heroes.length).toEqual(heroData.length)
 | |
|         )
 | |
|           .then(done, done.fail);
 | |
|       });
 | |
| 
 | |
|       it('service.heroes has expected # of heroes when fulfilled', done => {
 | |
|         service.refresh().then(() =>
 | |
|           expect(service.heroes.length).toEqual(heroData.length)
 | |
|         )
 | |
|           .then(done, done.fail);
 | |
|       });
 | |
| 
 | |
|       it('service.heroes remains empty until fulfilled', () => {
 | |
|         service.refresh();
 | |
| 
 | |
|         // executed before refresh completes
 | |
|         expect(service.heroes.length).toEqual(0);
 | |
|       });
 | |
| 
 | |
|       it('service.heroes remains empty when the server returns no data', done => {
 | |
|         heroData = []; // simulate no heroes from the backend
 | |
| 
 | |
|         service.refresh().then(() =>
 | |
|           expect(service.heroes.length).toEqual(0)
 | |
|         )
 | |
|           .then(done, done.fail);
 | |
|       });
 | |
| 
 | |
|       it('resets service.heroes w/ original data after re-refresh', done => {
 | |
|         let firstHeroes: Hero[];
 | |
|         let changedName = 'Gerry Mander';
 | |
| 
 | |
|         service.refresh().then(() => {
 | |
|           firstHeroes = service.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 array
 | |
|             expect(service.heroes.length).toEqual(heroData.length); // no Hercules
 | |
|             expect(service.heroes[0].name).not.toEqual(changedName); // reverted name change
 | |
|           })
 | |
|           .then(done, done.fail);
 | |
|       });
 | |
| 
 | |
|       it('clears service.heroes while waiting for re-refresh', done => {
 | |
|         service.refresh().then(() => {
 | |
|           service.refresh();
 | |
|           expect(service.heroes.length).toEqual(0);
 | |
|         })
 | |
|           .then(done, done.fail);
 | |
|       });
 | |
| 
 | |
|       // 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)', done => {
 | |
|         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)
 | |
|             )
 | |
|           );
 | |
|         })
 | |
|           .then(done, done.fail);
 | |
|       });
 | |
| 
 | |
|     });
 | |
| 
 | |
|     describe('when backend throws an error', () => {
 | |
| 
 | |
|       beforeEach(() => {
 | |
|         service = new HeroService(new FailingBackendService());
 | |
|       });
 | |
| 
 | |
|       it('returns failed promise with the server error', done => {
 | |
|         service.refresh()
 | |
|           .then(() => fail('refresh should have failed'))
 | |
|           .catch(err => expect(err).toEqual(testError))
 | |
|           .then(done, done.fail);
 | |
|       });
 | |
| 
 | |
|       it('clears service.heroes', done => {
 | |
|         service.refresh()
 | |
|           .then(() => fail('refresh should have failed'))
 | |
|           .catch(err => expect(service.heroes.length).toEqual(0))
 | |
|           .then(done, done.fail);
 | |
|       });
 | |
| 
 | |
|     });
 | |
|   });
 | |
| });
 | |
| 
 | |
| ///////// 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
 | |
|   // force-cast it to <Promise<Hero[]> because of TS typing bug.
 | |
|   fetchAllHeroesAsync = () =>
 | |
|     <Promise<Hero[]>><any>Promise.reject(testError);
 | |
| }
 |