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);
|
||
|
}
|