src/contacts-new/ContactRepository.spec.ts
import {WapiClient} from '@quobis/wapi-client';
import {expect} from 'chai';
import nanoid from 'nanoid';
import {TestScheduler} from 'rxjs/testing';
import sinon, {StubbedInstance, stubConstructor} from 'ts-sinon';
import {User} from '../users-new/User';
import {UserRepository} from '../users-new/UserRepository';
import {UserRole} from '../users-new/UserRole';
import {WacRequest} from '../wac-proxy/wac-stack/WacRequest';
import {ContactService} from '../wac-proxy/wac-stack/contact/ContactService';
import {AddressBookEventBuilder, ContactDtoBuilder} from '../wac-proxy/wac-stack/contact/builders';
import {AddressBookType, ContactDto} from '../wac-proxy/wac-stack/contact/types';
import {PresenceService} from '../wac-proxy/wac-stack/presence/PresenceService';
import {UserService} from '../wac-proxy/wac-stack/user/UserService';
import {Contact} from './Contact';
import {ContactRepository} from './ContactRepository';
import {ContactType} from './ContactType';
function createContactModel(data: ContactDto, {
type = ContactType.CONTACTS,
user = undefined,
}: {type?: ContactType; user?: User} = {}): Contact {
return {
id: data.id,
name: data.name,
emails: [],
phones: [],
favorite: data.favorite || false,
type,
user,
};
}
function createUserModel(id: string, online = false): User {
return {
id,
domain: '',
username: '',
phone: '',
email: '',
alias: '',
role: UserRole.USER,
online,
activity: 'away',
mood: 'unknown',
note: '',
avatar: '',
displayName: '',
};
}
describe('contacts-new/ContactRepository', () => {
let testScheduler: TestScheduler;
let contactService: StubbedInstance<ContactService>;
let userRepository: StubbedInstance<UserRepository>;
beforeEach(() => {
testScheduler = new TestScheduler((actual, expected) => {
expect(actual).to.deep.equal(expected);
});
const wacRequest = stubConstructor(WacRequest, stubConstructor(WapiClient));
contactService = stubConstructor(ContactService, wacRequest);
userRepository = stubConstructor(UserRepository,
stubConstructor(PresenceService, wacRequest),
stubConstructor(UserService, wacRequest),
);
});
it('should be lazily created', () => {
new ContactRepository(contactService, userRepository);
sinon.assert.notCalled(contactService.getContacts$);
});
it('should initialize the contact list with initial data', () => {
const CONTACT_DTO_1 = ContactDtoBuilder.create().build();
const CONTACT_DTO_2 = ContactDtoBuilder.create().build();
const CONTACT_DTO_3 = ContactDtoBuilder.create().build();
const CONTACT_DTO_4 = ContactDtoBuilder.create().build();
const INITIAL_ADDRESS_BOOKS = [{
id: 'id1',
name: 'name1',
type: AddressBookType.CONTACTS,
contacts: [CONTACT_DTO_1, CONTACT_DTO_2],
}, {
id: 'id2',
name: 'name2',
type: AddressBookType.DOMAIN,
contacts: [CONTACT_DTO_3],
}, {
id: 'id3',
name: 'name3',
type: AddressBookType.GROUPS,
contacts: [CONTACT_DTO_3, CONTACT_DTO_4],
}];
testScheduler.run(({expectObservable, cold, hot}) => {
const contactRespository = new ContactRepository(contactService, userRepository);
contactService.getContacts$.returns(cold('a|', {a: INITIAL_ADDRESS_BOOKS}));
sinon.stub(contactService, 'contactEvent$').value(hot(''));
expectObservable(contactRespository.contacts$).toBe('a', {
a: [
createContactModel(CONTACT_DTO_1),
createContactModel(CONTACT_DTO_2),
createContactModel(CONTACT_DTO_3, {type: ContactType.DOMAIN}),
createContactModel(CONTACT_DTO_3, {type: ContactType.GROUPS}),
createContactModel(CONTACT_DTO_4, {type: ContactType.GROUPS}),
],
});
});
});
it('should update the contact list when create, update and delete events are received', () => {
const CONTACT_DTO = ContactDtoBuilder.create().build();
const CREATE_CONTACT_EVENT = AddressBookEventBuilder.create()
.withMethod('POST')
.withBody('id1', AddressBookType.CONTACTS, CONTACT_DTO)
.build();
const UPDATE_CONTACT_EVENT = AddressBookEventBuilder.create()
.withMethod('PUT')
.withBody('id1', AddressBookType.CONTACTS, {...CONTACT_DTO, name: 'updatedName'})
.build();
const DELETE_CONTACT_EVENT = AddressBookEventBuilder.create()
.withMethod('DELETE')
.withBody('id1', AddressBookType.CONTACTS, CONTACT_DTO)
.build();
testScheduler.run(({expectObservable, cold, hot}) => {
contactService.getContacts$.returns(cold('a|', {a: []}));
sinon.stub(contactService, 'contactEvent$').value(hot('^-bcd', {
b: CREATE_CONTACT_EVENT,
c: UPDATE_CONTACT_EVENT,
d: DELETE_CONTACT_EVENT,
}));
const contactRespository = new ContactRepository(contactService, userRepository);
expectObservable(contactRespository.contacts$).toBe('a-bcd', {
a: [],
b: [createContactModel(CONTACT_DTO)],
c: [createContactModel({...CONTACT_DTO, name: 'updatedName'})],
d: [],
});
});
});
it('should update the contact list when events of contacts included with different types are received', () => {
const createDomainContact = (contact: ContactDto): Contact =>
createContactModel(contact, {type: ContactType.DOMAIN});
const createGroupContact = (contact: ContactDto): Contact =>
createContactModel(contact, {type: ContactType.GROUPS});
const CONTACT_DTO = ContactDtoBuilder.create().build();
const CREATE_CONTACT_EVENT = AddressBookEventBuilder.create()
.withMethod('POST')
.withBody('id1', AddressBookType.DOMAIN, CONTACT_DTO)
.build();
const CREATE_CONTACT_EVENT2 = AddressBookEventBuilder.create()
.withMethod('POST')
.withBody('id2', AddressBookType.GROUPS, CONTACT_DTO)
.build();
const UPDATE_CONTACT_EVENT = AddressBookEventBuilder.create()
.withMethod('PUT')
.withBody('id2', AddressBookType.GROUPS, {...CONTACT_DTO, name: 'updatedName'})
.build();
const DELETE_CONTACT_EVENT = AddressBookEventBuilder.create()
.withMethod('DELETE')
.withBody('id2', AddressBookType.GROUPS, CONTACT_DTO)
.build();
testScheduler.run(({expectObservable, cold, hot}) => {
contactService.getContacts$.returns(cold('a|', {a: []}));
sinon.stub(contactService, 'contactEvent$').value(hot('^-bcde', {
b: CREATE_CONTACT_EVENT,
c: CREATE_CONTACT_EVENT2,
d: UPDATE_CONTACT_EVENT,
e: DELETE_CONTACT_EVENT,
}));
const contactRespository = new ContactRepository(contactService, userRepository);
expectObservable(contactRespository.contacts$).toBe('a-bcde', {
a: [],
b: [createDomainContact(CONTACT_DTO)],
c: [createDomainContact(CONTACT_DTO), createGroupContact(CONTACT_DTO)],
d: [createDomainContact(CONTACT_DTO), createGroupContact({...CONTACT_DTO, name: 'updatedName'})],
e: [createDomainContact(CONTACT_DTO)],
});
});
});
it('should react to changes in the associated user', () => {
const ID1 = nanoid();
const ID2 = nanoid();
const CONTACT_DTO_1 = ContactDtoBuilder.create().withAssociatedUserId(ID1).build();
const CONTACT_DTO_2 = ContactDtoBuilder.create().withAssociatedUserId(ID2).build();
const INITIAL_ADDRESS_BOOKS = [{
id: 'id1',
name: 'name1',
type: AddressBookType.CONTACTS,
contacts: [CONTACT_DTO_1, CONTACT_DTO_2],
}];
const USER = createUserModel(ID1);
const USER_2 = createUserModel(ID2);
const USER_UPDATED = createUserModel(ID1, true);
testScheduler.run(({expectObservable, cold, hot}) => {
contactService.getContacts$.returns(cold('a|', {a: INITIAL_ADDRESS_BOOKS}));
userRepository.getUser$.withArgs(ID1).returns(hot('^b 15ms c', {
b: USER,
c: USER_UPDATED,
}));
userRepository.getUser$.withArgs(ID2).returns(hot('^b', {
b: USER_2,
}));
const contactRespository = new ContactRepository(contactService, userRepository);
expectObservable(contactRespository.contacts$).toBe('11ms b 15ms c', {
b: [createContactModel(CONTACT_DTO_1, {user: USER}), createContactModel(CONTACT_DTO_2, {user: USER_2})],
c: [createContactModel(CONTACT_DTO_1, {user: USER_UPDATED}), createContactModel(CONTACT_DTO_2, {user: USER_2})],
});
});
});
});