@webex/contact-center 3.10.0 → 3.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cc.js +42 -31
- package/dist/cc.js.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/constants.js.map +1 -1
- package/dist/index.js +17 -1
- package/dist/index.js.map +1 -1
- package/dist/logger-proxy.js.map +1 -1
- package/dist/metrics/MetricsManager.js +2 -1
- package/dist/metrics/MetricsManager.js.map +1 -1
- package/dist/metrics/behavioral-events.js +12 -0
- package/dist/metrics/behavioral-events.js.map +1 -1
- package/dist/metrics/constants.js +4 -0
- package/dist/metrics/constants.js.map +1 -1
- package/dist/services/AddressBook.js +2 -3
- package/dist/services/AddressBook.js.map +1 -1
- package/dist/services/EntryPoint.js +2 -3
- package/dist/services/EntryPoint.js.map +1 -1
- package/dist/services/Queue.js +2 -3
- package/dist/services/Queue.js.map +1 -1
- package/dist/services/WebCallingService.js +1 -1
- package/dist/services/WebCallingService.js.map +1 -1
- package/dist/services/agent/index.js +1 -2
- package/dist/services/agent/index.js.map +1 -1
- package/dist/services/agent/types.js +10 -0
- package/dist/services/agent/types.js.map +1 -1
- package/dist/services/config/Util.js.map +1 -1
- package/dist/services/config/constants.js.map +1 -1
- package/dist/services/config/index.js +1 -1
- package/dist/services/config/index.js.map +1 -1
- package/dist/services/config/types.js +2 -2
- package/dist/services/config/types.js.map +1 -1
- package/dist/services/constants.js.map +1 -1
- package/dist/services/core/Err.js.map +1 -1
- package/dist/services/core/GlobalTypes.js.map +1 -1
- package/dist/services/core/Utils.js +92 -74
- package/dist/services/core/Utils.js.map +1 -1
- package/dist/services/core/WebexRequest.js +1 -2
- package/dist/services/core/WebexRequest.js.map +1 -1
- package/dist/services/core/aqm-reqs.js +2 -3
- package/dist/services/core/aqm-reqs.js.map +1 -1
- package/dist/services/core/constants.js +17 -1
- package/dist/services/core/constants.js.map +1 -1
- package/dist/services/core/types.js.map +1 -1
- package/dist/services/core/websocket/WebSocketManager.js +11 -3
- package/dist/services/core/websocket/WebSocketManager.js.map +1 -1
- package/dist/services/core/websocket/connection-service.js +1 -1
- package/dist/services/core/websocket/connection-service.js.map +1 -1
- package/dist/services/core/websocket/keepalive.worker.js.map +1 -1
- package/dist/services/core/websocket/types.js.map +1 -1
- package/dist/services/index.js +1 -1
- package/dist/services/index.js.map +1 -1
- package/dist/services/task/AutoWrapup.js +1 -1
- package/dist/services/task/AutoWrapup.js.map +1 -1
- package/dist/services/task/TaskManager.js +177 -56
- package/dist/services/task/TaskManager.js.map +1 -1
- package/dist/services/task/TaskUtils.js +122 -5
- package/dist/services/task/TaskUtils.js.map +1 -1
- package/dist/services/task/constants.js +3 -1
- package/dist/services/task/constants.js.map +1 -1
- package/dist/services/task/contact.js +0 -2
- package/dist/services/task/contact.js.map +1 -1
- package/dist/services/task/dialer.js.map +1 -1
- package/dist/services/task/index.js +78 -51
- package/dist/services/task/index.js.map +1 -1
- package/dist/services/task/types.js +377 -4
- package/dist/services/task/types.js.map +1 -1
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -1
- package/dist/utils/PageCache.js +1 -1
- package/dist/utils/PageCache.js.map +1 -1
- package/dist/webex-config.js.map +1 -1
- package/dist/webex.js +2 -2
- package/dist/webex.js.map +1 -1
- package/package.json +9 -9
- package/src/cc.ts +50 -41
- package/src/index.ts +1 -0
- package/src/metrics/behavioral-events.ts +12 -0
- package/src/metrics/constants.ts +4 -0
- package/src/services/config/types.ts +2 -2
- package/src/services/core/Utils.ts +101 -85
- package/src/services/core/constants.ts +16 -0
- package/src/services/core/websocket/WebSocketManager.ts +10 -1
- package/src/services/task/TaskManager.ts +204 -36
- package/src/services/task/TaskUtils.ts +145 -5
- package/src/services/task/constants.ts +2 -0
- package/src/services/task/index.ts +88 -74
- package/src/services/task/types.ts +72 -15
- package/test/unit/spec/cc.ts +1 -0
- package/test/unit/spec/metrics/behavioral-events.ts +14 -0
- package/test/unit/spec/services/core/Utils.ts +262 -31
- package/test/unit/spec/services/core/websocket/WebSocketManager.ts +20 -0
- package/test/unit/spec/services/task/TaskManager.ts +748 -5
- package/test/unit/spec/services/task/TaskUtils.ts +311 -9
- package/test/unit/spec/services/task/index.ts +440 -68
- package/umd/contact-center.min.js +2 -2
- package/umd/contact-center.min.js.map +1 -1
- package/dist/types/cc.d.ts +0 -756
- package/dist/types/config.d.ts +0 -66
- package/dist/types/constants.d.ts +0 -46
- package/dist/types/index.d.ts +0 -183
- package/dist/types/logger-proxy.d.ts +0 -71
- package/dist/types/metrics/MetricsManager.d.ts +0 -223
- package/dist/types/metrics/behavioral-events.d.ts +0 -29
- package/dist/types/metrics/constants.d.ts +0 -151
- package/dist/types/services/AddressBook.d.ts +0 -74
- package/dist/types/services/EntryPoint.d.ts +0 -67
- package/dist/types/services/Queue.d.ts +0 -76
- package/dist/types/services/WebCallingService.d.ts +0 -1
- package/dist/types/services/agent/index.d.ts +0 -46
- package/dist/types/services/agent/types.d.ts +0 -413
- package/dist/types/services/config/Util.d.ts +0 -19
- package/dist/types/services/config/constants.d.ts +0 -237
- package/dist/types/services/config/index.d.ts +0 -168
- package/dist/types/services/config/types.d.ts +0 -1134
- package/dist/types/services/constants.d.ts +0 -97
- package/dist/types/services/core/Err.d.ts +0 -119
- package/dist/types/services/core/GlobalTypes.d.ts +0 -58
- package/dist/types/services/core/Utils.d.ts +0 -75
- package/dist/types/services/core/WebexRequest.d.ts +0 -22
- package/dist/types/services/core/aqm-reqs.d.ts +0 -16
- package/dist/types/services/core/constants.d.ts +0 -85
- package/dist/types/services/core/types.d.ts +0 -47
- package/dist/types/services/core/websocket/WebSocketManager.d.ts +0 -34
- package/dist/types/services/core/websocket/connection-service.d.ts +0 -27
- package/dist/types/services/core/websocket/keepalive.worker.d.ts +0 -2
- package/dist/types/services/core/websocket/types.d.ts +0 -37
- package/dist/types/services/index.d.ts +0 -52
- package/dist/types/services/task/AutoWrapup.d.ts +0 -40
- package/dist/types/services/task/TaskManager.d.ts +0 -1
- package/dist/types/services/task/TaskUtils.d.ts +0 -28
- package/dist/types/services/task/constants.d.ts +0 -69
- package/dist/types/services/task/contact.d.ts +0 -69
- package/dist/types/services/task/dialer.d.ts +0 -28
- package/dist/types/services/task/index.d.ts +0 -632
- package/dist/types/services/task/types.d.ts +0 -1243
- package/dist/types/types.d.ts +0 -614
- package/dist/types/utils/PageCache.d.ts +0 -173
- package/dist/types/webex-config.d.ts +0 -53
- package/dist/types/webex.d.ts +0 -7
|
@@ -39,7 +39,8 @@ describe('Task', () => {
|
|
|
39
39
|
let loggerInfoSpy;
|
|
40
40
|
let loggerLogSpy;
|
|
41
41
|
let loggerErrorSpy;
|
|
42
|
-
let
|
|
42
|
+
let calculateDestAgentIdSpy;
|
|
43
|
+
let calculateDestTypeSpy;
|
|
43
44
|
|
|
44
45
|
const taskId = '0ae913a4-c857-4705-8d49-76dd3dde75e4';
|
|
45
46
|
const mockTrack = {} as MediaStreamTrack;
|
|
@@ -119,6 +120,32 @@ describe('Task', () => {
|
|
|
119
120
|
interaction: {
|
|
120
121
|
mediaType: 'telephony',
|
|
121
122
|
mainInteractionId: taskId,
|
|
123
|
+
participants: {
|
|
124
|
+
'723a8ffb-a26e-496d-b14a-ff44fb83b64f': {
|
|
125
|
+
pType: 'Agent',
|
|
126
|
+
type: 'AGENT',
|
|
127
|
+
id: '723a8ffb-a26e-496d-b14a-ff44fb83b64f',
|
|
128
|
+
hasLeft: false,
|
|
129
|
+
hasJoined: true,
|
|
130
|
+
isWrapUp: false,
|
|
131
|
+
},
|
|
132
|
+
'f520d6b5-28ad-4f2f-b83e-781bb64af617': {
|
|
133
|
+
pType: 'Agent',
|
|
134
|
+
type: 'AGENT',
|
|
135
|
+
id: 'f520d6b5-28ad-4f2f-b83e-781bb64af617',
|
|
136
|
+
hasLeft: false,
|
|
137
|
+
hasJoined: true,
|
|
138
|
+
isWrapUp: false,
|
|
139
|
+
},
|
|
140
|
+
'ebeb893b-ba67-4f36-8418-95c7492b28c2': {
|
|
141
|
+
pType: 'Agent',
|
|
142
|
+
type: 'AGENT',
|
|
143
|
+
id: 'ebeb893b-ba67-4f36-8418-95c7492b28c2',
|
|
144
|
+
hasLeft: false,
|
|
145
|
+
hasJoined: true,
|
|
146
|
+
isWrapUp: false,
|
|
147
|
+
},
|
|
148
|
+
},
|
|
122
149
|
media: {
|
|
123
150
|
'58a45567-4e61-4f4b-a580-5bc86357bef0': {
|
|
124
151
|
holdTimestamp: null,
|
|
@@ -145,13 +172,18 @@ describe('Task', () => {
|
|
|
145
172
|
},
|
|
146
173
|
};
|
|
147
174
|
|
|
148
|
-
// Mock
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
175
|
+
// Mock calculateDestAgentId to return the expected destination agent
|
|
176
|
+
calculateDestAgentIdSpy = jest.spyOn(Utils, 'calculateDestAgentId').mockReturnValue(taskDataMock.destAgentId);
|
|
177
|
+
|
|
178
|
+
// Mock calculateDestType to return 'agent' by default
|
|
179
|
+
calculateDestTypeSpy = jest.spyOn(Utils, 'calculateDestType').mockReturnValue('agent');
|
|
152
180
|
|
|
153
|
-
// Create an instance of Task
|
|
154
|
-
task = new Task(contactMock, webCallingService, taskDataMock
|
|
181
|
+
// Create an instance of Task with wrapupData and agentId
|
|
182
|
+
task = new Task(contactMock, webCallingService, taskDataMock, {
|
|
183
|
+
wrapUpProps: { wrapUpReasonList: [] },
|
|
184
|
+
autoWrapEnabled: false,
|
|
185
|
+
autoWrapAfterSeconds: 0
|
|
186
|
+
}, taskDataMock.agentId);
|
|
155
187
|
|
|
156
188
|
// Mock navigator.mediaDevices
|
|
157
189
|
global.navigator.mediaDevices = {
|
|
@@ -625,6 +657,40 @@ describe('Task', () => {
|
|
|
625
657
|
);
|
|
626
658
|
});
|
|
627
659
|
|
|
660
|
+
it('should hold the task with custom mediaResourceId and return the expected response', async () => {
|
|
661
|
+
const customMediaResourceId = 'custom-media-resource-id-123';
|
|
662
|
+
const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
|
|
663
|
+
contactMock.hold.mockResolvedValue(expectedResponse);
|
|
664
|
+
|
|
665
|
+
const response = await task.hold(customMediaResourceId);
|
|
666
|
+
|
|
667
|
+
expect(contactMock.hold).toHaveBeenCalledWith({
|
|
668
|
+
interactionId: taskId,
|
|
669
|
+
data: {mediaResourceId: customMediaResourceId},
|
|
670
|
+
});
|
|
671
|
+
expect(response).toEqual(expectedResponse);
|
|
672
|
+
expect(loggerInfoSpy).toHaveBeenCalledWith(`Holding task`, {
|
|
673
|
+
module: TASK_FILE,
|
|
674
|
+
method: 'hold',
|
|
675
|
+
interactionId: task.data.interactionId,
|
|
676
|
+
});
|
|
677
|
+
expect(loggerLogSpy).toHaveBeenCalledWith(`Task placed on hold successfully`, {
|
|
678
|
+
module: TASK_FILE,
|
|
679
|
+
method: 'hold',
|
|
680
|
+
interactionId: task.data.interactionId,
|
|
681
|
+
});
|
|
682
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
683
|
+
1,
|
|
684
|
+
METRIC_EVENT_NAMES.TASK_HOLD_SUCCESS,
|
|
685
|
+
{
|
|
686
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponse(expectedResponse),
|
|
687
|
+
taskId: taskDataMock.interactionId,
|
|
688
|
+
mediaResourceId: customMediaResourceId,
|
|
689
|
+
},
|
|
690
|
+
['operational', 'behavioral']
|
|
691
|
+
);
|
|
692
|
+
});
|
|
693
|
+
|
|
628
694
|
it('should handle errors in hold method', async () => {
|
|
629
695
|
const error = {details: (global as any).makeFailure('Hold Failed')};
|
|
630
696
|
contactMock.hold.mockImplementation(() => {
|
|
@@ -654,6 +720,36 @@ describe('Task', () => {
|
|
|
654
720
|
);
|
|
655
721
|
});
|
|
656
722
|
|
|
723
|
+
it('should handle errors in hold method with custom mediaResourceId', async () => {
|
|
724
|
+
const customMediaResourceId = 'custom-media-resource-id-456';
|
|
725
|
+
const error = {details: (global as any).makeFailure('Hold Failed with custom mediaResourceId')};
|
|
726
|
+
contactMock.hold.mockImplementation(() => {
|
|
727
|
+
throw error;
|
|
728
|
+
});
|
|
729
|
+
|
|
730
|
+
await expect(task.hold(customMediaResourceId)).rejects.toThrow(error.details.data.reason);
|
|
731
|
+
expect(generateTaskErrorObjectSpy).toHaveBeenCalledWith(error, 'hold', TASK_FILE);
|
|
732
|
+
const expectedTaskErrorFieldsHold = {
|
|
733
|
+
trackingId: error.details.trackingId,
|
|
734
|
+
errorMessage: error.details.data.reason,
|
|
735
|
+
errorType: '',
|
|
736
|
+
errorData: '',
|
|
737
|
+
reasonCode: 0,
|
|
738
|
+
};
|
|
739
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
740
|
+
1,
|
|
741
|
+
METRIC_EVENT_NAMES.TASK_HOLD_FAILED,
|
|
742
|
+
{
|
|
743
|
+
taskId: taskDataMock.interactionId,
|
|
744
|
+
mediaResourceId: customMediaResourceId,
|
|
745
|
+
error: error.toString(),
|
|
746
|
+
...expectedTaskErrorFieldsHold,
|
|
747
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details),
|
|
748
|
+
},
|
|
749
|
+
['operational', 'behavioral']
|
|
750
|
+
);
|
|
751
|
+
});
|
|
752
|
+
|
|
657
753
|
it('should resume the task and return the expected response', async () => {
|
|
658
754
|
const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
|
|
659
755
|
contactMock.unHold.mockResolvedValue(expectedResponse);
|
|
@@ -678,6 +774,29 @@ describe('Task', () => {
|
|
|
678
774
|
);
|
|
679
775
|
});
|
|
680
776
|
|
|
777
|
+
it('should resume the task with custom mediaResourceId and return the expected response', async () => {
|
|
778
|
+
const customMediaResourceId = 'custom-media-resource-id-789';
|
|
779
|
+
const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
|
|
780
|
+
contactMock.unHold.mockResolvedValue(expectedResponse);
|
|
781
|
+
const response = await task.resume(customMediaResourceId);
|
|
782
|
+
expect(contactMock.unHold).toHaveBeenCalledWith({
|
|
783
|
+
interactionId: taskId,
|
|
784
|
+
data: {mediaResourceId: customMediaResourceId},
|
|
785
|
+
});
|
|
786
|
+
expect(response).toEqual(expectedResponse);
|
|
787
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
788
|
+
1,
|
|
789
|
+
METRIC_EVENT_NAMES.TASK_RESUME_SUCCESS,
|
|
790
|
+
{
|
|
791
|
+
taskId: taskDataMock.interactionId,
|
|
792
|
+
mainInteractionId: taskDataMock.interaction.mainInteractionId,
|
|
793
|
+
mediaResourceId: customMediaResourceId,
|
|
794
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponse(expectedResponse),
|
|
795
|
+
},
|
|
796
|
+
['operational', 'behavioral']
|
|
797
|
+
);
|
|
798
|
+
});
|
|
799
|
+
|
|
681
800
|
it('should handle errors in resume method', async () => {
|
|
682
801
|
const error = {details: (global as any).makeFailure('Resume Failed')};
|
|
683
802
|
contactMock.unHold.mockImplementation(() => {
|
|
@@ -709,6 +828,36 @@ describe('Task', () => {
|
|
|
709
828
|
);
|
|
710
829
|
});
|
|
711
830
|
|
|
831
|
+
it('should handle errors in resume method with custom mediaResourceId', async () => {
|
|
832
|
+
const customMediaResourceId = 'custom-media-resource-id-999';
|
|
833
|
+
const error = {details: (global as any).makeFailure('Resume Failed with custom mediaResourceId')};
|
|
834
|
+
contactMock.unHold.mockImplementation(() => {
|
|
835
|
+
throw error;
|
|
836
|
+
});
|
|
837
|
+
|
|
838
|
+
await expect(task.resume(customMediaResourceId)).rejects.toThrow(error.details.data.reason);
|
|
839
|
+
expect(generateTaskErrorObjectSpy).toHaveBeenCalledWith(error, 'resume', TASK_FILE);
|
|
840
|
+
const expectedTaskErrorFieldsResume = {
|
|
841
|
+
trackingId: error.details.trackingId,
|
|
842
|
+
errorMessage: error.details.data.reason,
|
|
843
|
+
errorType: '',
|
|
844
|
+
errorData: '',
|
|
845
|
+
reasonCode: 0,
|
|
846
|
+
};
|
|
847
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenNthCalledWith(
|
|
848
|
+
1,
|
|
849
|
+
METRIC_EVENT_NAMES.TASK_RESUME_FAILED,
|
|
850
|
+
{
|
|
851
|
+
taskId: taskDataMock.interactionId,
|
|
852
|
+
mainInteractionId: taskDataMock.interaction.mainInteractionId,
|
|
853
|
+
mediaResourceId: customMediaResourceId,
|
|
854
|
+
...expectedTaskErrorFieldsResume,
|
|
855
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(error.details),
|
|
856
|
+
},
|
|
857
|
+
['operational', 'behavioral']
|
|
858
|
+
);
|
|
859
|
+
});
|
|
860
|
+
|
|
712
861
|
it('should initiate a consult call and return the expected response', async () => {
|
|
713
862
|
const consultPayload = {
|
|
714
863
|
to: '1234',
|
|
@@ -876,15 +1025,16 @@ describe('Task', () => {
|
|
|
876
1025
|
);
|
|
877
1026
|
});
|
|
878
1027
|
|
|
879
|
-
it('should send DIALNUMBER when
|
|
1028
|
+
it('should send DIALNUMBER when calculateDestType returns dialNumber during consultTransfer', async () => {
|
|
880
1029
|
const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
|
|
881
1030
|
contactMock.consultTransfer.mockResolvedValue(expectedResponse);
|
|
882
1031
|
|
|
883
|
-
//
|
|
884
|
-
|
|
1032
|
+
// Mock calculateDestType to return dialNumber
|
|
1033
|
+
calculateDestTypeSpy.mockReturnValue(CONSULT_TRANSFER_DESTINATION_TYPE.DIALNUMBER);
|
|
885
1034
|
|
|
886
1035
|
await task.consultTransfer();
|
|
887
1036
|
|
|
1037
|
+
expect(calculateDestTypeSpy).toHaveBeenCalledWith(taskDataMock.interaction, taskDataMock.agentId);
|
|
888
1038
|
expect(contactMock.consultTransfer).toHaveBeenCalledWith({
|
|
889
1039
|
interactionId: taskId,
|
|
890
1040
|
data: {
|
|
@@ -894,15 +1044,16 @@ describe('Task', () => {
|
|
|
894
1044
|
});
|
|
895
1045
|
});
|
|
896
1046
|
|
|
897
|
-
it('should send ENTRYPOINT when
|
|
1047
|
+
it('should send ENTRYPOINT when calculateDestType returns entryPoint during consultTransfer', async () => {
|
|
898
1048
|
const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
|
|
899
1049
|
contactMock.consultTransfer.mockResolvedValue(expectedResponse);
|
|
900
1050
|
|
|
901
|
-
//
|
|
902
|
-
|
|
1051
|
+
// Mock calculateDestType to return entryPoint
|
|
1052
|
+
calculateDestTypeSpy.mockReturnValue(CONSULT_TRANSFER_DESTINATION_TYPE.ENTRYPOINT);
|
|
903
1053
|
|
|
904
1054
|
await task.consultTransfer();
|
|
905
1055
|
|
|
1056
|
+
expect(calculateDestTypeSpy).toHaveBeenCalledWith(taskDataMock.interaction, taskDataMock.agentId);
|
|
906
1057
|
expect(contactMock.consultTransfer).toHaveBeenCalledWith({
|
|
907
1058
|
interactionId: taskId,
|
|
908
1059
|
data: {
|
|
@@ -912,15 +1063,16 @@ describe('Task', () => {
|
|
|
912
1063
|
});
|
|
913
1064
|
});
|
|
914
1065
|
|
|
915
|
-
it('should
|
|
1066
|
+
it('should use AGENT when calculateDestType returns agent during consultTransfer', async () => {
|
|
916
1067
|
const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
|
|
917
1068
|
contactMock.consultTransfer.mockResolvedValue(expectedResponse);
|
|
918
1069
|
|
|
919
|
-
//
|
|
920
|
-
|
|
1070
|
+
// Mock calculateDestType to return agent (default behavior)
|
|
1071
|
+
calculateDestTypeSpy.mockReturnValue(CONSULT_TRANSFER_DESTINATION_TYPE.AGENT);
|
|
921
1072
|
|
|
922
1073
|
await task.consultTransfer();
|
|
923
1074
|
|
|
1075
|
+
expect(calculateDestTypeSpy).toHaveBeenCalledWith(taskDataMock.interaction, taskDataMock.agentId);
|
|
924
1076
|
expect(contactMock.consultTransfer).toHaveBeenCalledWith({
|
|
925
1077
|
interactionId: taskId,
|
|
926
1078
|
data: {
|
|
@@ -957,7 +1109,11 @@ describe('Task', () => {
|
|
|
957
1109
|
const taskWithoutDestAgentId = new Task(contactMock, webCallingService, {
|
|
958
1110
|
...taskDataMock,
|
|
959
1111
|
destAgentId: undefined,
|
|
960
|
-
}
|
|
1112
|
+
}, {
|
|
1113
|
+
wrapUpProps: { wrapUpReasonList: [] },
|
|
1114
|
+
autoWrapEnabled: false,
|
|
1115
|
+
autoWrapAfterSeconds: 0
|
|
1116
|
+
}, taskDataMock.agentId);
|
|
961
1117
|
|
|
962
1118
|
const queueConsultTransferPayload: ConsultTransferPayLoad = {
|
|
963
1119
|
to: 'some-queue-id',
|
|
@@ -965,61 +1121,123 @@ describe('Task', () => {
|
|
|
965
1121
|
};
|
|
966
1122
|
|
|
967
1123
|
// For this negative case, ensure computed destination is empty
|
|
968
|
-
|
|
1124
|
+
calculateDestAgentIdSpy.mockReturnValueOnce('');
|
|
969
1125
|
|
|
970
1126
|
await expect(
|
|
971
1127
|
taskWithoutDestAgentId.consultTransfer(queueConsultTransferPayload)
|
|
972
|
-
).rejects.toThrow('
|
|
1128
|
+
).rejects.toThrow('No agent has accepted this queue consult yet');
|
|
973
1129
|
});
|
|
974
1130
|
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
1131
|
+
describe('consultTransfer', () => {
|
|
1132
|
+
it('should successfully perform consult transfer with agent destination', async () => {
|
|
1133
|
+
const expectedResponse: TaskResponse = {
|
|
1134
|
+
data: {interactionId: taskId},
|
|
1135
|
+
trackingId: 'test-tracking-id'
|
|
1136
|
+
} as AgentContact;
|
|
1137
|
+
contactMock.consultTransfer.mockResolvedValue(expectedResponse);
|
|
1138
|
+
|
|
1139
|
+
calculateDestTypeSpy.mockReturnValue(CONSULT_TRANSFER_DESTINATION_TYPE.AGENT);
|
|
982
1140
|
|
|
983
|
-
|
|
1141
|
+
const result = await task.consultTransfer();
|
|
984
1142
|
|
|
985
|
-
|
|
986
|
-
|
|
1143
|
+
expect(calculateDestAgentIdSpy).toHaveBeenCalledWith(taskDataMock.interaction, taskDataMock.agentId);
|
|
1144
|
+
expect(calculateDestTypeSpy).toHaveBeenCalledWith(taskDataMock.interaction, taskDataMock.agentId);
|
|
1145
|
+
expect(contactMock.consultTransfer).toHaveBeenCalledWith({
|
|
1146
|
+
interactionId: taskId,
|
|
1147
|
+
data: {
|
|
1148
|
+
to: taskDataMock.destAgentId,
|
|
1149
|
+
destinationType: CONSULT_TRANSFER_DESTINATION_TYPE.AGENT,
|
|
1150
|
+
},
|
|
1151
|
+
});
|
|
1152
|
+
expect(result).toEqual(expectedResponse);
|
|
1153
|
+
expect(loggerInfoSpy).toHaveBeenCalledWith(
|
|
1154
|
+
`Initiating consult transfer to ${taskDataMock.destAgentId}`,
|
|
1155
|
+
{
|
|
1156
|
+
module: TASK_FILE,
|
|
1157
|
+
method: 'consultTransfer',
|
|
1158
|
+
interactionId: taskId,
|
|
1159
|
+
}
|
|
1160
|
+
);
|
|
1161
|
+
expect(loggerLogSpy).toHaveBeenCalledWith(
|
|
1162
|
+
`Consult transfer completed successfully to ${taskDataMock.destAgentId}`,
|
|
1163
|
+
{
|
|
1164
|
+
module: TASK_FILE,
|
|
1165
|
+
method: 'consultTransfer',
|
|
1166
|
+
trackingId: expectedResponse.trackingId,
|
|
1167
|
+
interactionId: taskId,
|
|
1168
|
+
}
|
|
1169
|
+
);
|
|
1170
|
+
});
|
|
1171
|
+
|
|
1172
|
+
it('should track metrics on successful consult transfer', async () => {
|
|
1173
|
+
const expectedResponse: TaskResponse = {
|
|
1174
|
+
data: {interactionId: taskId},
|
|
1175
|
+
trackingId: 'test-tracking-id'
|
|
1176
|
+
} as AgentContact;
|
|
1177
|
+
contactMock.consultTransfer.mockResolvedValue(expectedResponse);
|
|
1178
|
+
|
|
1179
|
+
await task.consultTransfer();
|
|
1180
|
+
|
|
1181
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
|
|
1182
|
+
METRIC_EVENT_NAMES.TASK_TRANSFER_SUCCESS,
|
|
1183
|
+
{
|
|
1184
|
+
taskId: taskDataMock.interactionId,
|
|
1185
|
+
destination: taskDataMock.destAgentId,
|
|
1186
|
+
destinationType: 'agent',
|
|
1187
|
+
isConsultTransfer: true,
|
|
1188
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponse(expectedResponse),
|
|
1189
|
+
},
|
|
1190
|
+
['operational', 'behavioral', 'business']
|
|
1191
|
+
);
|
|
1192
|
+
});
|
|
987
1193
|
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
1194
|
+
it('should throw error when no destination agent is found', async () => {
|
|
1195
|
+
calculateDestAgentIdSpy.mockReturnValue('');
|
|
1196
|
+
|
|
1197
|
+
await expect(task.consultTransfer()).rejects.toThrow('No agent has accepted this queue consult yet');
|
|
1198
|
+
|
|
1199
|
+
expect(contactMock.consultTransfer).not.toHaveBeenCalled();
|
|
991
1200
|
});
|
|
992
1201
|
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
1202
|
+
it('should handle and rethrow contact method errors', async () => {
|
|
1203
|
+
const mockError = new Error('Consult Transfer Failed');
|
|
1204
|
+
contactMock.consultTransfer.mockRejectedValue(mockError);
|
|
1205
|
+
generateTaskErrorObjectSpy.mockReturnValue(mockError);
|
|
997
1206
|
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1207
|
+
await expect(task.consultTransfer()).rejects.toThrow('Consult Transfer Failed');
|
|
1208
|
+
|
|
1209
|
+
expect(generateTaskErrorObjectSpy).toHaveBeenCalledWith(mockError, 'consultTransfer', TASK_FILE);
|
|
1210
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
|
|
1211
|
+
METRIC_EVENT_NAMES.TASK_TRANSFER_FAILED,
|
|
1212
|
+
expect.objectContaining({
|
|
1213
|
+
taskId: taskDataMock.interactionId,
|
|
1214
|
+
destination: taskDataMock.destAgentId,
|
|
1215
|
+
destinationType: 'agent',
|
|
1216
|
+
isConsultTransfer: true,
|
|
1217
|
+
error: mockError.toString(),
|
|
1218
|
+
}),
|
|
1219
|
+
['operational', 'behavioral', 'business']
|
|
1220
|
+
);
|
|
1221
|
+
});
|
|
1222
|
+
|
|
1223
|
+
it('should dynamically calculate destAgentId when not available', async () => {
|
|
1224
|
+
const consultedAgentId = 'dynamic-agent-123';
|
|
1225
|
+
calculateDestAgentIdSpy.mockReturnValue(consultedAgentId);
|
|
1226
|
+
|
|
1227
|
+
const expectedResponse: TaskResponse = {data: {interactionId: taskId}} as AgentContact;
|
|
1228
|
+
contactMock.consultTransfer.mockResolvedValue(expectedResponse);
|
|
1229
|
+
|
|
1230
|
+
await task.consultTransfer();
|
|
1231
|
+
|
|
1232
|
+
expect(calculateDestAgentIdSpy).toHaveBeenCalledWith(taskDataMock.interaction, taskDataMock.agentId);
|
|
1233
|
+
expect(contactMock.consultTransfer).toHaveBeenCalledWith({
|
|
1234
|
+
interactionId: taskId,
|
|
1235
|
+
data: {
|
|
1236
|
+
to: consultedAgentId,
|
|
1237
|
+
destinationType: 'agent',
|
|
1238
|
+
},
|
|
1239
|
+
});
|
|
1240
|
+
});
|
|
1023
1241
|
});
|
|
1024
1242
|
|
|
1025
1243
|
it('should do vteamTransfer if destinationType is queue and return the expected response', async () => {
|
|
@@ -1642,12 +1860,6 @@ describe('Task', () => {
|
|
|
1642
1860
|
conferenceTransfer: jest.fn(),
|
|
1643
1861
|
};
|
|
1644
1862
|
|
|
1645
|
-
// Re-setup the getDestinationAgentId spy for conference methods
|
|
1646
|
-
getDestinationAgentIdSpy = jest
|
|
1647
|
-
.spyOn(Utils, 'getDestinationAgentId')
|
|
1648
|
-
.mockReturnValue(taskDataMock.destAgentId);
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
1863
|
task = new Task(contactMock, webCallingService, taskDataMock, {
|
|
1652
1864
|
wrapUpProps: { wrapUpReasonList: [] },
|
|
1653
1865
|
autoWrapEnabled: false,
|
|
@@ -1671,7 +1883,7 @@ describe('Task', () => {
|
|
|
1671
1883
|
interactionId: taskId,
|
|
1672
1884
|
data: {
|
|
1673
1885
|
agentId: taskDataMock.agentId, // From task data agent ID
|
|
1674
|
-
to: taskDataMock.destAgentId, // From
|
|
1886
|
+
to: taskDataMock.destAgentId, // From calculateDestAgentId() using task participants
|
|
1675
1887
|
destinationType: 'agent', // From consultation data
|
|
1676
1888
|
},
|
|
1677
1889
|
});
|
|
@@ -1713,6 +1925,166 @@ describe('Task', () => {
|
|
|
1713
1925
|
interactionId: taskId,
|
|
1714
1926
|
});
|
|
1715
1927
|
});
|
|
1928
|
+
|
|
1929
|
+
it('should dynamically calculate destAgentId from participants when this.data.destAgentId is null', async () => {
|
|
1930
|
+
// Simulate scenario where destAgentId is not preserved (e.g., after hold/unhold)
|
|
1931
|
+
task.data.destAgentId = null;
|
|
1932
|
+
|
|
1933
|
+
const consultedAgentId = 'consulted-agent-123';
|
|
1934
|
+
calculateDestAgentIdSpy.mockReturnValue(consultedAgentId);
|
|
1935
|
+
|
|
1936
|
+
const mockResponse = {
|
|
1937
|
+
trackingId: 'test-tracking-dynamic',
|
|
1938
|
+
interactionId: taskId,
|
|
1939
|
+
};
|
|
1940
|
+
contactMock.consultConference.mockResolvedValue(mockResponse);
|
|
1941
|
+
|
|
1942
|
+
const result = await task.consultConference();
|
|
1943
|
+
|
|
1944
|
+
// Verify calculateDestAgentId was called to dynamically calculate the destination
|
|
1945
|
+
expect(calculateDestAgentIdSpy).toHaveBeenCalledWith(
|
|
1946
|
+
taskDataMock.interaction,
|
|
1947
|
+
taskDataMock.agentId
|
|
1948
|
+
);
|
|
1949
|
+
|
|
1950
|
+
// Verify the conference was called with the dynamically calculated destAgentId
|
|
1951
|
+
expect(contactMock.consultConference).toHaveBeenCalledWith({
|
|
1952
|
+
interactionId: taskId,
|
|
1953
|
+
data: {
|
|
1954
|
+
agentId: taskDataMock.agentId,
|
|
1955
|
+
to: consultedAgentId, // Dynamically calculated value
|
|
1956
|
+
destinationType: 'agent',
|
|
1957
|
+
},
|
|
1958
|
+
});
|
|
1959
|
+
expect(result).toEqual(mockResponse);
|
|
1960
|
+
});
|
|
1961
|
+
|
|
1962
|
+
it('should throw error when no destination agent is found (queue consult not accepted)', async () => {
|
|
1963
|
+
// Simulate queue consult scenario where no agent has accepted yet
|
|
1964
|
+
calculateDestAgentIdSpy.mockReturnValue(''); // No agent found
|
|
1965
|
+
|
|
1966
|
+
await expect(task.consultConference()).rejects.toThrow('No agent has accepted this queue consult yet');
|
|
1967
|
+
|
|
1968
|
+
// Verify the conference was NOT called
|
|
1969
|
+
expect(contactMock.consultConference).not.toHaveBeenCalled();
|
|
1970
|
+
});
|
|
1971
|
+
|
|
1972
|
+
it('should calculate destination type from participant type for regular agents', async () => {
|
|
1973
|
+
const destAgentId = 'consulted-agent-456';
|
|
1974
|
+
|
|
1975
|
+
calculateDestAgentIdSpy = jest.spyOn(Utils, 'calculateDestAgentId').mockReturnValue(destAgentId);
|
|
1976
|
+
calculateDestTypeSpy = jest.spyOn(Utils, 'calculateDestType').mockReturnValue('agent');
|
|
1977
|
+
|
|
1978
|
+
const mockResponse = {trackingId: 'test-tracking-id', interactionId: taskId};
|
|
1979
|
+
contactMock.consultConference.mockResolvedValue(mockResponse);
|
|
1980
|
+
|
|
1981
|
+
await task.consultConference();
|
|
1982
|
+
|
|
1983
|
+
expect(calculateDestTypeSpy).toHaveBeenCalledWith(
|
|
1984
|
+
task.data.interaction,
|
|
1985
|
+
taskDataMock.agentId
|
|
1986
|
+
);
|
|
1987
|
+
|
|
1988
|
+
expect(contactMock.consultConference).toHaveBeenCalledWith({
|
|
1989
|
+
interactionId: taskId,
|
|
1990
|
+
data: {
|
|
1991
|
+
agentId: taskDataMock.agentId,
|
|
1992
|
+
to: destAgentId,
|
|
1993
|
+
destinationType: 'agent',
|
|
1994
|
+
},
|
|
1995
|
+
});
|
|
1996
|
+
});
|
|
1997
|
+
|
|
1998
|
+
it('should use DN destination type for dial number participants', async () => {
|
|
1999
|
+
const destAgentId = 'dn-uuid-123';
|
|
2000
|
+
|
|
2001
|
+
calculateDestAgentIdSpy = jest.spyOn(Utils, 'calculateDestAgentId').mockReturnValue(destAgentId);
|
|
2002
|
+
calculateDestTypeSpy = jest.spyOn(Utils, 'calculateDestType').mockReturnValue('dialNumber');
|
|
2003
|
+
|
|
2004
|
+
const mockResponse = {trackingId: 'test-tracking-id-dn', interactionId: taskId};
|
|
2005
|
+
contactMock.consultConference.mockResolvedValue(mockResponse);
|
|
2006
|
+
|
|
2007
|
+
await task.consultConference();
|
|
2008
|
+
|
|
2009
|
+
expect(contactMock.consultConference).toHaveBeenCalledWith({
|
|
2010
|
+
interactionId: taskId,
|
|
2011
|
+
data: {
|
|
2012
|
+
agentId: taskDataMock.agentId,
|
|
2013
|
+
to: destAgentId,
|
|
2014
|
+
destinationType: 'dialNumber',
|
|
2015
|
+
},
|
|
2016
|
+
});
|
|
2017
|
+
});
|
|
2018
|
+
|
|
2019
|
+
it('should use EpDn destination type for entry point dial number participants', async () => {
|
|
2020
|
+
const destAgentId = 'epdn-uuid-456';
|
|
2021
|
+
|
|
2022
|
+
calculateDestAgentIdSpy = jest.spyOn(Utils, 'calculateDestAgentId').mockReturnValue(destAgentId);
|
|
2023
|
+
calculateDestTypeSpy = jest.spyOn(Utils, 'calculateDestType').mockReturnValue('entryPoint');
|
|
2024
|
+
|
|
2025
|
+
const mockResponse = {trackingId: 'test-tracking-id-epdn', interactionId: taskId};
|
|
2026
|
+
contactMock.consultConference.mockResolvedValue(mockResponse);
|
|
2027
|
+
|
|
2028
|
+
await task.consultConference();
|
|
2029
|
+
|
|
2030
|
+
expect(contactMock.consultConference).toHaveBeenCalledWith({
|
|
2031
|
+
interactionId: taskId,
|
|
2032
|
+
data: {
|
|
2033
|
+
agentId: taskDataMock.agentId,
|
|
2034
|
+
to: destAgentId,
|
|
2035
|
+
destinationType: 'entryPoint',
|
|
2036
|
+
},
|
|
2037
|
+
});
|
|
2038
|
+
});
|
|
2039
|
+
|
|
2040
|
+
it('should fall back to task.data.destinationType when calculateDestType returns empty', async () => {
|
|
2041
|
+
const destAgentId = 'consulted-agent-789';
|
|
2042
|
+
|
|
2043
|
+
calculateDestAgentIdSpy = jest.spyOn(Utils, 'calculateDestAgentId').mockReturnValue(destAgentId);
|
|
2044
|
+
calculateDestTypeSpy = jest.spyOn(Utils, 'calculateDestType').mockReturnValue(''); // No type found
|
|
2045
|
+
|
|
2046
|
+
task.data.destinationType = 'EPDN';
|
|
2047
|
+
|
|
2048
|
+
const mockResponse = {trackingId: 'test-tracking-id-fallback', interactionId: taskId};
|
|
2049
|
+
contactMock.consultConference.mockResolvedValue(mockResponse);
|
|
2050
|
+
|
|
2051
|
+
await task.consultConference();
|
|
2052
|
+
|
|
2053
|
+
expect(contactMock.consultConference).toHaveBeenCalledWith({
|
|
2054
|
+
interactionId: taskId,
|
|
2055
|
+
data: {
|
|
2056
|
+
agentId: taskDataMock.agentId,
|
|
2057
|
+
to: destAgentId,
|
|
2058
|
+
destinationType: 'EPDN', // Falls back to task.data.destinationType
|
|
2059
|
+
},
|
|
2060
|
+
});
|
|
2061
|
+
});
|
|
2062
|
+
|
|
2063
|
+
it('should handle CBT scenarios with correct destination type', async () => {
|
|
2064
|
+
const destAgentId = 'agent-cbt-uuid';
|
|
2065
|
+
|
|
2066
|
+
calculateDestAgentIdSpy = jest.spyOn(Utils, 'calculateDestAgentId').mockReturnValue(destAgentId);
|
|
2067
|
+
calculateDestTypeSpy = jest.spyOn(Utils, 'calculateDestType').mockReturnValue('dialNumber');
|
|
2068
|
+
|
|
2069
|
+
const mockResponse = {trackingId: 'test-tracking-id-cbt', interactionId: taskId};
|
|
2070
|
+
contactMock.consultConference.mockResolvedValue(mockResponse);
|
|
2071
|
+
|
|
2072
|
+
await task.consultConference();
|
|
2073
|
+
|
|
2074
|
+
expect(calculateDestTypeSpy).toHaveBeenCalledWith(
|
|
2075
|
+
task.data.interaction,
|
|
2076
|
+
taskDataMock.agentId
|
|
2077
|
+
);
|
|
2078
|
+
|
|
2079
|
+
expect(contactMock.consultConference).toHaveBeenCalledWith({
|
|
2080
|
+
interactionId: taskId,
|
|
2081
|
+
data: {
|
|
2082
|
+
agentId: taskDataMock.agentId,
|
|
2083
|
+
to: destAgentId,
|
|
2084
|
+
destinationType: 'dialNumber', // dialNumber for CBT scenarios
|
|
2085
|
+
},
|
|
2086
|
+
});
|
|
2087
|
+
});
|
|
1716
2088
|
});
|
|
1717
2089
|
|
|
1718
2090
|
describe('exitConference', () => {
|