Skip to content

Commit

Permalink
fix(call): handle connect after setup and fix send digit (#3375)
Browse files Browse the repository at this point in the history
  • Loading branch information
sreenara authored Feb 14, 2024
1 parent 623e510 commit ec581dc
Show file tree
Hide file tree
Showing 2 changed files with 138 additions and 18 deletions.
129 changes: 122 additions & 7 deletions packages/calling/src/CallingClient/calling/call.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,47 @@ describe('Call Tests', () => {
expect(call).toBeTruthy();
});

it('should log a warning when sending a digit fails', async () => {
const tone = '1';
const errorMessage = 'Failed to send digit';

// Mock the mediaConnection object
const mockMediaConnection = {
insertDTMF: jest.fn(() => {
throw new Error(errorMessage);
}),
};

const callManager = getCallManager(webex, defaultServiceIndicator);

const call = callManager.createCall(dest, CallDirection.OUTBOUND, deviceId, mockLineId);

const realMediaConnection = call.mediaConnection;
// Set the mock mediaConnection object
call.mediaConnection = mockMediaConnection;

// Spy on the log.warn method
const logWarnSpy = jest.spyOn(log, 'warn');

// Call the sendDigit method
call.sendDigit(tone);

// Expect the log.warn method to be called with the error message
expect(logWarnSpy).toHaveBeenLastCalledWith(`Unable to send digit on call: ${errorMessage}`, {
file: 'call',
method: 'sendDigit',
});

// Restore the real mediaConnection object
call.mediaConnection = realMediaConnection;

call.end();
await waitForMsecs(50); // Need to add a small delay for Promise and callback to finish.

/* After call ends, call manager should have 0 record */
expect(Object.keys(callManager.getActiveCalls()).length).toBe(0);
});

it('delete call object when ending the call', async () => {
webex.request.mockReturnValue({
statusCode: 200,
Expand Down Expand Up @@ -755,13 +796,6 @@ describe('State Machine handler tests', () => {
expect(call['callStateMachine'].state.value).toBe('S_UNKNOWN');
expect(stateMachineSpy).toBeCalledTimes(3);
expect(warnSpy).toBeCalledTimes(4);
warnSpy.mockClear();
/* Try sending a dtmf which shouldn't work as call is not connected. */
call.sendDigit('1');
expect(warnSpy).toBeCalledOnceWith(`Can't send DTMF as call is not yet connected`, {
file: 'call',
method: 'sendDigit',
});
});

it('state changes during successful outgoing call', async () => {
Expand Down Expand Up @@ -854,6 +888,87 @@ describe('State Machine handler tests', () => {
expect(call['callStateMachine'].state.value).toBe('S_SEND_CALL_DISCONNECT');
});

it('outgoing call where we receive connect directly after setup. Media established before connect. test call and media state changes', async () => {
const statusPayload = <WebexRequestPayload>(<unknown>{
statusCode: 200,
body: mockStatusBody,
});

const dummySetupEvent = {
type: 'E_SEND_CALL_SETUP',
data: undefined as any,
};

const dummyConnectEvent = {
type: 'E_RECV_CALL_CONNECT',
data: undefined as any,
};

const dummyOfferEvent = {
type: 'E_SEND_ROAP_OFFER',
data: undefined as any,
};

const dummyAnswerEvent = {
type: 'E_RECV_ROAP_ANSWER',
data: {
seq: 1,
messageType: 'ANSWER',
sdp: 'sdp',
},
};

const dummyOkEvent = {
type: 'E_ROAP_OK',
data: {
received: false,
message: {
seq: 1,
messageType: 'OK',
},
},
};

const postMediaSpy = jest.spyOn(call as any, 'postMedia');

webex.request.mockReturnValue(statusPayload);

call.sendCallStateMachineEvt(dummySetupEvent as CallEvent);
expect(call['callStateMachine'].state.value).toBe('S_SEND_CALL_SETUP');

call.sendMediaStateMachineEvt(dummyOfferEvent as RoapEvent);

/**
* Since the event doesn't have any data above, we should request media sdk for an offer here.
* The below event is expected to be called again my mediaSdk.
*/
dummyOfferEvent.data = {
seq: 1,
messageType: 'OFFER',
sdp: 'sdp',
};
call.sendMediaStateMachineEvt(dummyOfferEvent as RoapEvent);
expect(mediaConnection.initiateOffer).toHaveBeenCalledTimes(1);
expect(postMediaSpy).toHaveBeenLastCalledWith(dummyOfferEvent.data as RoapMessage);

call.sendMediaStateMachineEvt(dummyAnswerEvent as RoapEvent);
expect(mediaConnection.roapMessageReceived).toHaveBeenLastCalledWith(
dummyAnswerEvent.data as RoapMessage
);

call.sendMediaStateMachineEvt(dummyOkEvent as RoapEvent);
expect(postMediaSpy).toHaveBeenLastCalledWith(dummyOkEvent.data.message as RoapMessage);

expect(call['mediaStateMachine'].state.value).toBe('S_ROAP_OK');

call.sendCallStateMachineEvt(dummyConnectEvent as CallEvent);
expect(call['callStateMachine'].state.value).toBe('S_CALL_ESTABLISHED');
expect(call.isConnected()).toBe(true);

call.sendCallStateMachineEvt({type: 'E_SEND_CALL_DISCONNECT'});
expect(call['callStateMachine'].state.value).toBe('S_SEND_CALL_DISCONNECT');
});

it('state changes during successful outgoing call with early media', async () => {
const statusPayload = <WebexRequestPayload>(<unknown>{
statusCode: 200,
Expand Down
27 changes: 16 additions & 11 deletions packages/calling/src/CallingClient/calling/call.ts
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,10 @@ export class Call extends Eventing<CallEventTypes> implements ICall {
target: 'S_RECV_CALL_PROGRESS',
actions: ['incomingCallProgress'],
},
E_RECV_CALL_CONNECT: {
target: 'S_RECV_CALL_CONNECT',
actions: ['incomingCallConnect'],
},
E_RECV_CALL_DISCONNECT: {
target: 'S_RECV_CALL_DISCONNECT',
actions: ['incomingCallDisconnect'],
Expand Down Expand Up @@ -1561,7 +1565,10 @@ export class Call extends Eventing<CallEventTypes> implements ICall {
});

try {
if (this.callStateMachine.state.value === 'S_RECV_CALL_PROGRESS') {
if (
this.callStateMachine.state.value === 'S_RECV_CALL_PROGRESS' ||
this.callStateMachine.state.value === 'S_SEND_CALL_SETUP'
) {
log.info(
'Media negotiation completed before call connect. Setting media negotiation completed flag.',
{
Expand Down Expand Up @@ -2622,21 +2629,19 @@ export class Call extends Eventing<CallEventTypes> implements ICall {
*/
public sendDigit(tone: string) {
/* istanbul ignore else */
if (!this.connected) {
log.warn(`Can't send DTMF as call is not yet connected`, {
try {
log.info(`Sending digit : ${tone}`, {
file: CALL_FILE,
method: 'sendDigit',
});

return;
this.mediaConnection.insertDTMF(tone);
} catch (e: any) {
log.warn(`Unable to send digit on call: ${e.message}`, {
file: CALL_FILE,
method: 'sendDigit',
});
}

log.info(`Sending digit : ${tone}`, {
file: CALL_FILE,
method: 'sendDigit',
});

this.mediaConnection.insertDTMF(tone);
}

/**
Expand Down

0 comments on commit ec581dc

Please sign in to comment.