ringcentral-call-control
Version:
[](https://coveralls.io/github/ringcentral/ringcentral-call-control-js?branch=master) [ • 16.3 kB
text/typescript
import { RingCentralCallControl } from '../src/index';
import { USER_AGENT } from '../src/userAgent';
import * as mock from './mock/sdk';
import * as extensionInfo from './mock/data/extensionInfo.json';
import * as telephonySessionInboundProceedingMessage from './mock/data/telephonySessionInboundProceedingMessage.json';
import * as telephonySessionOutboundSetupMessage from './mock/data/telephonySessionOutboundSetupMessage.json';
import * as telephonySessionOutboundDisconnectedMessage from './mock/data/telephonySessionOutboundDisconnectedMessage.json';
import * as telephonySessionOutboundData from './mock/data/telephonySessionOutbound.json';
let sdk;
let rcCallControl;
function getMockEventMessage({
template,
telephonySessionId,
sequence,
code,
reason,
party,
partyId,
}: {
template: any;
telephonySessionId?: string;
sequence?: number;
code?: string;
reason?: string;
partyId?: string;
party?: any;
}) {
let partyData = party || template.body.parties[0];
return {
...template,
body: {
...template.body,
telephonySessionId: telephonySessionId || template.body.telephonySessionId,
sequence: sequence || template.body.sequence,
parties: [{
...partyData,
id: partyId || partyData.id,
status: {
...partyData.status,
code: code || partyData.status.code,
reason: reason || partyData.status.reason,
},
}]
},
};
}
describe('RingCentral Call Control :: Index', () => {
beforeAll(async () => {
mock.mockAuthentication();
sdk = mock.createSDK();
await sdk.platform().login({ username: '...', password: '...' });
});
describe('Initialize', () => {
beforeAll(async () => {
mock.mockDevice();
mock.mockExtensionInfo();
mock.mockPresence();
mock.mockTelephoneSession();
rcCallControl = new RingCentralCallControl({ sdk });
await rcCallControl.initialize();
});
it('should be ready after initialized', () => {
expect(rcCallControl.ready).toEqual(true);
});
it('should have default userAgent after initialized', () => {
expect(rcCallControl.requestOptions.userAgent).toEqual(USER_AGENT);
});
it('should not initialize duplicate', () => {
rcCallControl.initialize();
expect(rcCallControl._initializePromise).toEqual(null);
});
it('should load devices after initialized', () => {
expect(rcCallControl.devices.length).toEqual(1);
});
it('should load sessions after initialized', () => {
expect(rcCallControl.sessions.length).toEqual(1);
expect(Array.from(rcCallControl.sessionsMap.keys()).length).toEqual(1);
});
it('should delete session after get disconnected event', () => {
rcCallControl.onNotificationEvent(telephonySessionOutboundDisconnectedMessage);
expect(rcCallControl.sessions.length).toEqual(0);
});
it('should load extensionInfo after initialized', () => {
expect(rcCallControl.extensionId).toEqual(String(extensionInfo.id));
expect(rcCallControl.accountId).toEqual(String(extensionInfo.account.id));
});
it('should refresh devices successfully', async () => {
mock.mockDevice({ records: [] });
await rcCallControl.refreshDevices();
expect(rcCallControl.devices.length).toEqual(0);
mock.mockDevice();
await rcCallControl.refreshDevices();
expect(rcCallControl.devices.length).toEqual(1);
});
});
describe('Initialize without preload', () => {
beforeAll(async () => {
rcCallControl = new RingCentralCallControl({
sdk,
preloadDevices: false,
preloadSessions: false,
extensionInfo: extensionInfo as any,
});
await rcCallControl.initialize();
});
it('should be ready after initialized', () => {
expect(rcCallControl.ready).toEqual(true);
expect(rcCallControl.devices.length).toEqual(0);
expect(rcCallControl.sessions.length).toEqual(0);
expect(rcCallControl.extensionId).toEqual(String(extensionInfo.id));
expect(rcCallControl.accountId).toEqual(String(extensionInfo.account.id));
});
});
describe('Initialize with API failed', () => {
beforeAll(async () => {
rcCallControl = new RingCentralCallControl({ sdk });
await rcCallControl.initialize();
});
it('should be ready after initialized', () => {
expect(rcCallControl.ready).toEqual(true);
});
});
describe('Initialize with User Agent', () => {
beforeAll(async () => {
rcCallControl = new RingCentralCallControl({ sdk, userAgent: 'TestAgent' });
await rcCallControl.initialize();
});
it('should be ready after initialized', () => {
expect(rcCallControl.requestOptions.userAgent).toEqual(`TestAgent ${USER_AGENT}`);
});
});
describe('Restore sessions', () => {
it('should restore session as new session', async () => {
const callControl = new RingCentralCallControl({
sdk,
preloadDevices: false,
preloadSessions: false,
extensionInfo: extensionInfo as any,
});
await callControl.initialize();
expect(callControl.sessions.length).toEqual(0);
callControl.restoreSessions([
{
...telephonySessionOutboundData,
extensionId: String(extensionInfo.id),
accountId: String(extensionInfo.account.id),
}
]);
expect(callControl.sessions.length).toEqual(1);
});
it('should restore session with existed session', async () => {
mock.mockPresence();
mock.mockTelephoneSession();
const callControl = new RingCentralCallControl({
sdk,
preloadDevices: false,
extensionInfo: extensionInfo as any,
});
await callControl.initialize();
expect(callControl.sessions.length).toEqual(1);
const oldSession = callControl.sessions[0];
callControl.restoreSessions([
{
...telephonySessionOutboundData,
extensionId: String(extensionInfo.id),
accountId: String(extensionInfo.account.id),
}
]);
expect(callControl.sessions.length).toEqual(1);
expect(callControl.sessions[0]).toEqual(oldSession);
});
});
describe('Notification Event', () => {
beforeAll(async () => {
mock.mockDevice();
mock.mockExtensionInfo()
mock.mockPresence({ activeCalls: [] });
mock.mockTelephoneSession();
rcCallControl = new RingCentralCallControl({ sdk });
await rcCallControl.initialize();
});
it('should load empty sessions firstly', () => {
expect(rcCallControl.sessions.length).toEqual(0);
});
it('should not handle no telephony session event', () => {
rcCallControl.onNotificationEvent({
event: '/restapi/v1.0/account/170848004/telephony/sessions',
body: {}
});
expect(rcCallControl.sessions.length).toEqual(0);
});
it('should not handle no telephony session id', () => {
rcCallControl.onNotificationEvent({ event: '/restapi/v1.0/account/170848004/extension/170848004' });
expect(rcCallControl.sessions.length).toEqual(0);
});
it('should not create new sessions when get telephony session disconnected event', () => {
let newEventTriggered = false;
rcCallControl.once('new', () => {
newEventTriggered = true;
});
rcCallControl.onNotificationEvent(getMockEventMessage({
template: telephonySessionOutboundDisconnectedMessage,
telephonySessionId: '12345',
partyId: '12345-1',
}));
expect(rcCallControl.sessions.length).toEqual(0);
expect(newEventTriggered).toEqual(false);
});
it('should create new session and new event when get telephony session event', () => {
let newEventTriggered = false;
rcCallControl.once('new', () => {
newEventTriggered = true;
});
rcCallControl.onNotificationEvent(getMockEventMessage({
template: telephonySessionOutboundSetupMessage,
telephonySessionId: '123456',
partyId: '123456-1',
}));
expect(rcCallControl.sessions.length).toEqual(1);
expect(rcCallControl.sessions[0].parties.length).toEqual(1);
expect(newEventTriggered).toEqual(true);
});
it('should update session when get telephony session updated event', () => {
rcCallControl.onNotificationEvent(getMockEventMessage({
template: telephonySessionInboundProceedingMessage,
telephonySessionId: '123456',
partyId: '123456-2',
}));
expect(rcCallControl.sessions.length).toEqual(1);
expect(rcCallControl.sessions[0].parties.length).toEqual(2);
});
it('should not update current party when get telephony session with outdated sequence', () => {
const oldStatus = rcCallControl.sessions[0].parties[0].status;
rcCallControl.onNotificationEvent(getMockEventMessage({
template: telephonySessionOutboundSetupMessage,
telephonySessionId: '123456',
partyId: '123456-1',
sequence: telephonySessionOutboundSetupMessage.body.sequence - 1,
code: 'Disconnected',
}));
expect(rcCallControl.sessions.length).toEqual(1);
expect(rcCallControl.sessions[0].parties[0].status.code).toEqual(oldStatus.code);
});
it('should not update session when get telephony session with outdated sequence', () => {
const oldStatus = rcCallControl.sessions[0].parties[1].status;
rcCallControl.onNotificationEvent(getMockEventMessage({
template: telephonySessionInboundProceedingMessage,
telephonySessionId: '123456',
partyId: '123456-2',
sequence: telephonySessionInboundProceedingMessage.body.sequence - 1,
code: 'Disconnected',
}));
expect(rcCallControl.sessions.length).toEqual(1);
expect(rcCallControl.sessions[0].parties[1].status.code).toEqual(oldStatus.code);
});
it('should not update session when get telephony session no updated event', () => {
const oldStatus = rcCallControl.sessions[0].party.status;
rcCallControl.onNotificationEvent(getMockEventMessage({
template: telephonySessionInboundProceedingMessage,
telephonySessionId: '123456',
partyId: '123456-2',
}));
expect(rcCallControl.sessions.length).toEqual(1);
expect(rcCallControl.sessions[0].party.status.code).toEqual(oldStatus.code);
});
it('should not delete session when get telephony session disconnected event with CallSwitch reason', () => {
rcCallControl.onNotificationEvent(getMockEventMessage({
template: telephonySessionOutboundDisconnectedMessage,
telephonySessionId: '123456',
partyId: '123456-1',
reason: 'CallSwitch',
}));
expect(rcCallControl.sessions.length).toEqual(1);
expect(rcCallControl.sessions[0].party.status.reason).toEqual('CallSwitch');
});
it('should delete session when get telephony session disconnected event', () => {
rcCallControl.onNotificationEvent(getMockEventMessage({
template: telephonySessionOutboundDisconnectedMessage,
telephonySessionId: '123456',
partyId: '123456-1',
}));
expect(rcCallControl.sessions.length).toEqual(0);
});
it('should not add session after get telephony session Setup event with wrong sequence', () => {
rcCallControl.onNotificationEvent(getMockEventMessage({
template: telephonySessionOutboundDisconnectedMessage,
telephonySessionId: '1234567',
partyId: '1234567-1',
sequence: 3,
}));
expect(rcCallControl.sessions.length).toEqual(0);
rcCallControl.onNotificationEvent(getMockEventMessage({
template: telephonySessionOutboundSetupMessage,
telephonySessionId: '1234567',
partyId: '1234567-1',
sequence: 2,
}));
expect(rcCallControl.sessions.length).toEqual(0);
});
it('should clear expired sequence data when get a new telephony session event', () => {
const sequenceDataMap = rcCallControl.eventSequenceMap;
sequenceDataMap['old-telephone-session-id-1'] = {
sequence: 1,
telephoneSessionId: 'old-telephone-session-id',
updatedAt: Date.now() - 61000,
};
rcCallControl.onNotificationEvent(getMockEventMessage({
template: telephonySessionOutboundDisconnectedMessage,
telephonySessionId: '123456',
partyId: '123456-1',
}));
expect(!!sequenceDataMap['old-telephone-session-id-1']).toEqual(false);
});
it('should emit new event when get telephony session first my party', () => {
let newEventTriggered = false;
rcCallControl.once('new', () => {
newEventTriggered = true;
});
const notPartiesMessage = getMockEventMessage({
template: telephonySessionOutboundSetupMessage,
telephonySessionId: '12345678',
partyId: '12345678-1',
party: {
...telephonySessionInboundProceedingMessage.body.parties[0],
}
});
rcCallControl.onNotificationEvent(notPartiesMessage);
expect(rcCallControl.sessions.length).toEqual(1);
expect(!!rcCallControl.sessions[0].party).toEqual(false);
expect(rcCallControl.sessions[0].otherParties.length).toEqual(1);
expect(rcCallControl.sessions[0].parties.length).toEqual(1);
expect(newEventTriggered).toEqual(false);
rcCallControl.onNotificationEvent(getMockEventMessage({
template: telephonySessionOutboundSetupMessage,
telephonySessionId: '12345678',
partyId: '12345678-2',
}));
expect(newEventTriggered).toEqual(true);
});
it('should not clear expired sequence data when telephony session is still existed', () => {
const sequenceDataMap = rcCallControl.eventSequenceMap;
sequenceDataMap['12345678-2'].updatedAt = Date.now() - 61000;
rcCallControl.onNotificationEvent(getMockEventMessage({
template: telephonySessionOutboundDisconnectedMessage,
telephonySessionId: '123456',
partyId: '123456-1',
}));
expect(!!sequenceDataMap['12345678-2']).toEqual(true);
});
});
describe('Create call', () => {
beforeAll(async () => {
mock.mockDevice();
mock.mockExtensionInfo()
mock.mockPresence({ activeCalls: [] });
mock.mockTelephoneSession();
rcCallControl = new RingCentralCallControl({ sdk });
await rcCallControl.initialize();
});
it('should create call session successfully', async () => {
mock.mockTelephoneSessionCallOut();
const deviceId = rcCallControl.devices.filter(d => d.status === 'Online')[0].id;
const session = await rcCallControl.createCall(deviceId, { phoneNumber: '+12345678900' });
expect(rcCallControl.sessions.length).toEqual(1);
expect(rcCallControl.sessions[0].id).toEqual(session.id);
});
it('should delete call when get disconnected event', async () => {
rcCallControl.onNotificationEvent(telephonySessionOutboundDisconnectedMessage);
expect(rcCallControl.sessions.length).toEqual(0);
});
});
describe('Create conference', () => {
beforeAll(async () => {
mock.mockDevice();
mock.mockExtensionInfo()
mock.mockPresence({ activeCalls: [] });
mock.mockTelephoneSession();
rcCallControl = new RingCentralCallControl({ sdk });
await rcCallControl.initialize();
});
it('should create conference successfully', async () => {
mock.mockTelephoneConference();
const session = await rcCallControl.createConference();
expect(rcCallControl.sessions.length).toEqual(1);
expect(rcCallControl.sessions[0].parties.length).toEqual(0);
});
});
describe('Account Level', () => {
beforeAll(async () => {
mock.mockDevice();
mock.mockExtensionInfo()
mock.mockAccountPresence();
mock.mockTelephoneSession();
rcCallControl = new RingCentralCallControl({ sdk, accountLevel: true });
await rcCallControl.initialize();
});
it('should load active calls successfully', async () => {
expect(rcCallControl.sessions.length).toEqual(1);
});
});
});