Home Reference Source

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