230 lines
6.9 KiB
Plaintext
230 lines
6.9 KiB
Plaintext
import {HeroesComponent} from './heroes.component';
|
|
import {Hero} from './hero';
|
|
import {HeroService} from './hero.service';
|
|
import {User} from './user';
|
|
|
|
describe('HeroesComponent (Test Plan)', () => {
|
|
xit('can be created');
|
|
xit('has expected userName');
|
|
|
|
describe('#onInit', () => {
|
|
xit('HeroService.refresh not called immediately');
|
|
xit('onInit calls HeroService.refresh');
|
|
});
|
|
|
|
describe('#heroes', () => {
|
|
xit('lacks heroes when created');
|
|
xit('has heroes after cache loaded');
|
|
xit('restores heroes after refresh called again');
|
|
|
|
xit('binds view to heroes');
|
|
});
|
|
|
|
describe('#onSelected', () => {
|
|
xit('no hero is selected by default');
|
|
xit('sets the "currentHero"');
|
|
xit('no hero is selected after onRefresh() called');
|
|
|
|
xit('the view of the "currentHero" has the "selected" class (NG2 BUG)');
|
|
xit('the view of a non-selected hero does NOT have the "selected" class');
|
|
});
|
|
|
|
describe('#onDelete', () => {
|
|
xit('removes the supplied hero (only) from the list');
|
|
xit('removes the currentHero from the list if no hero argument');
|
|
xit('is harmless if no supplied or current hero');
|
|
xit('is harmless if hero not in list');
|
|
xit('is harmless if the list is empty');
|
|
xit('the new currentHero is the one after the removed hero');
|
|
xit('the new currentHero is the one before the removed hero if none after');
|
|
|
|
xit('the list view does not contain the "deleted" currentHero');
|
|
});
|
|
});
|
|
|
|
let hc:HeroesComponent;
|
|
let heroData: Hero[]; // fresh heroes for each test
|
|
let mockUser: User;
|
|
let service: HeroService;
|
|
|
|
// get the promise from the refresh spy;
|
|
// casting required because of inadequate d.ts for Jasmine
|
|
let refreshPromise = () => (<any>service.refresh).calls.mostRecent().returnValue;
|
|
|
|
describe('HeroesComponent (no Angular)', () => {
|
|
|
|
beforeEach(()=> {
|
|
heroData = [new Hero(1, 'Foo'), new Hero(2, 'Bar'), new Hero(3, 'Baz')];
|
|
mockUser = new User();
|
|
});
|
|
|
|
beforeEach(()=> {
|
|
service = <any> new HappyHeroService();
|
|
hc = new HeroesComponent(service, mockUser)
|
|
});
|
|
|
|
it('can be created', () => {
|
|
expect(hc instanceof HeroesComponent).toEqual(true); // proof of life
|
|
});
|
|
|
|
it('has expected userName', () => {
|
|
expect(hc.userName).toEqual(mockUser.name);
|
|
});
|
|
|
|
describe('#onInit', () => {
|
|
it('HeroService.refresh not called immediately', () => {
|
|
let spy = <jasmine.Spy><any> service.refresh;
|
|
expect(spy.calls.count()).toEqual(0);
|
|
});
|
|
|
|
it('onInit calls HeroService.refresh', () => {
|
|
let spy = <jasmine.Spy><any> service.refresh;
|
|
hc.ngOnInit(); // Angular framework calls when it creates the component
|
|
expect(spy.calls.count()).toEqual(1);
|
|
});
|
|
})
|
|
|
|
describe('#heroes', () => {
|
|
|
|
it('lacks heroes when created', () => {
|
|
let heroes = hc.heroes;
|
|
expect(heroes.length).toEqual(0); // not filled yet
|
|
});
|
|
|
|
it('has heroes after cache loaded', done => {
|
|
hc.ngOnInit(); // Angular framework calls when it creates the component
|
|
|
|
refreshPromise().then(() => {
|
|
let heroes = hc.heroes; // now the component has heroes to show
|
|
expect(heroes.length).toEqual(heroData.length);
|
|
})
|
|
.then(done, done.fail);
|
|
});
|
|
|
|
it('restores heroes after refresh called again', done => {
|
|
hc.ngOnInit(); // component initialization triggers service
|
|
let heroes: Hero[];
|
|
|
|
refreshPromise().then(() => {
|
|
heroes = hc.heroes; // now the component has heroes to show
|
|
heroes[0].name = 'Wotan';
|
|
heroes.push(new Hero(33, 'Thor'));
|
|
hc.onRefresh();
|
|
})
|
|
.then(() => {
|
|
heroes = hc.heroes; // get it again (don't reuse old array!)
|
|
expect(heroes[0]).not.toEqual('Wotan'); // change reversed
|
|
expect(heroes.length).toEqual(heroData.length); // orig num of heroes
|
|
})
|
|
.then(done, done.fail);
|
|
});
|
|
});
|
|
|
|
describe('#onSelected', () => {
|
|
|
|
it('no hero is selected by default', () => {
|
|
expect(hc.currentHero).not.toBeDefined();
|
|
});
|
|
|
|
it('sets the "currentHero"', () => {
|
|
hc.onSelect(heroData[1]); // select the second hero
|
|
expect(hc.currentHero).toEqual(heroData[1]);
|
|
});
|
|
|
|
it('no hero is selected after onRefresh() called', () => {
|
|
hc.onSelect(heroData[1]); // select the second hero
|
|
hc.onRefresh();
|
|
expect(hc.currentHero).not.toBeDefined();
|
|
});
|
|
});
|
|
|
|
|
|
describe('#onDelete', () => {
|
|
|
|
// Load the heroes asynchronously before each test
|
|
// Getting the async out of the way in the beforeEach
|
|
// means tests can be synchronous
|
|
// Note: could have cheated and simply plugged hc.heroes with fake data
|
|
// that trick would fail if we reimplemented hc.heroes as a readonly property
|
|
beforeEach(done => {
|
|
hc.ngOnInit(); // Angular framework calls when it creates the component
|
|
refreshPromise().then(done, done.fail);
|
|
});
|
|
|
|
it('removes the supplied hero (only) from the list', () => {
|
|
hc.currentHero = heroData[1];
|
|
let hero = heroData[2];
|
|
hc.onDelete(hero);
|
|
|
|
expect(hc.heroes).not.toContain(hero);
|
|
expect(hc.heroes).toContain(heroData[1]); // left current in place
|
|
expect(hc.heroes.length).toEqual(heroData.length - 1);
|
|
});
|
|
|
|
it('removes the currentHero from the list if no hero argument', () => {
|
|
hc.currentHero = heroData[1];
|
|
hc.onDelete();
|
|
expect(hc.heroes).not.toContain(heroData[1]);
|
|
});
|
|
|
|
it('is harmless if no supplied or current hero', () => {
|
|
hc.currentHero = null;
|
|
hc.onDelete();
|
|
expect(hc.heroes.length).toEqual(heroData.length);
|
|
});
|
|
|
|
it('is harmless if hero not in list', () => {
|
|
let hero = heroData[1].clone(); // object reference matters, not id
|
|
hc.onDelete(hero);
|
|
expect(hc.heroes.length).toEqual(heroData.length);
|
|
});
|
|
|
|
// must go async to get hc to clear its heroes list
|
|
it('is harmless if the list is empty', done => {
|
|
let hero = heroData[1];
|
|
heroData = [];
|
|
hc.onRefresh();
|
|
refreshPromise().then(() => {
|
|
hc.onDelete(hero); // shouldn't fail
|
|
})
|
|
.then(done, done.fail);
|
|
});
|
|
|
|
it('the new currentHero is the one after the removed hero', () => {
|
|
hc.currentHero = heroData[1];
|
|
let expectedCurrent = heroData[2];
|
|
hc.onDelete();
|
|
expect(hc.currentHero).toBe(expectedCurrent);
|
|
});
|
|
|
|
it('the new currentHero is the one before the removed hero if none after', () => {
|
|
hc.currentHero = heroData[heroData.length - 1]; // last hero
|
|
let expectedCurrent = heroData[heroData.length - 2]; // penultimate hero
|
|
hc.onDelete();
|
|
expect(hc.currentHero).toBe(expectedCurrent);
|
|
});
|
|
});
|
|
|
|
});
|
|
|
|
|
|
////// Helpers //////
|
|
|
|
class HappyHeroService {
|
|
|
|
constructor() {
|
|
spyOn(this, 'refresh').and.callThrough();
|
|
}
|
|
|
|
heroes: Hero[];
|
|
|
|
refresh() {
|
|
this.heroes = [];
|
|
// updates cached heroes after one JavaScript cycle
|
|
return new Promise((resolve, reject) => {
|
|
this.heroes.push(...heroData);
|
|
resolve(this.heroes);
|
|
});
|
|
}
|
|
}
|