@webex/contact-center 3.12.0-task-refactor.11 → 3.12.0-task-refactor.13
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 +297 -83
- package/dist/cc.js.map +1 -1
- package/dist/constants.js +3 -0
- package/dist/constants.js.map +1 -1
- package/dist/metrics/behavioral-events.js +39 -0
- package/dist/metrics/behavioral-events.js.map +1 -1
- package/dist/metrics/constants.js +8 -1
- package/dist/metrics/constants.js.map +1 -1
- package/dist/services/ApiAiAssistant.js +3 -0
- package/dist/services/ApiAiAssistant.js.map +1 -1
- package/dist/services/config/Util.js +1 -1
- package/dist/services/config/Util.js.map +1 -1
- package/dist/services/config/types.js +10 -0
- package/dist/services/config/types.js.map +1 -1
- package/dist/services/constants.js +1 -0
- package/dist/services/constants.js.map +1 -1
- package/dist/services/core/Err.js.map +1 -1
- package/dist/services/core/Utils.js +57 -6
- package/dist/services/core/Utils.js.map +1 -1
- package/dist/services/core/aqm-reqs.js +92 -17
- package/dist/services/core/aqm-reqs.js.map +1 -1
- package/dist/services/core/websocket/WebSocketManager.js +24 -3
- package/dist/services/core/websocket/WebSocketManager.js.map +1 -1
- package/dist/services/index.js +1 -0
- package/dist/services/index.js.map +1 -1
- package/dist/services/task/Task.js +6 -0
- package/dist/services/task/Task.js.map +1 -1
- package/dist/services/task/TaskManager.js +155 -6
- package/dist/services/task/TaskManager.js.map +1 -1
- package/dist/services/task/TaskUtils.js +39 -11
- package/dist/services/task/TaskUtils.js.map +1 -1
- package/dist/services/task/constants.js +7 -1
- package/dist/services/task/constants.js.map +1 -1
- package/dist/services/task/dialer.js +129 -0
- package/dist/services/task/dialer.js.map +1 -1
- package/dist/services/task/state-machine/TaskStateMachine.js +30 -3
- package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -1
- package/dist/services/task/state-machine/actions.js +4 -0
- package/dist/services/task/state-machine/actions.js.map +1 -1
- package/dist/services/task/state-machine/constants.js +3 -0
- package/dist/services/task/state-machine/constants.js.map +1 -1
- package/dist/services/task/state-machine/guards.js +23 -0
- package/dist/services/task/state-machine/guards.js.map +1 -1
- package/dist/services/task/state-machine/types.js.map +1 -1
- package/dist/services/task/types.js +59 -0
- package/dist/services/task/types.js.map +1 -1
- package/dist/types/cc.d.ts +76 -2
- package/dist/types/constants.d.ts +3 -0
- package/dist/types/metrics/constants.d.ts +6 -0
- package/dist/types/services/config/types.d.ts +22 -1
- package/dist/types/services/core/Err.d.ts +6 -0
- package/dist/types/services/core/Utils.d.ts +20 -2
- package/dist/types/services/core/aqm-reqs.d.ts +49 -0
- package/dist/types/services/core/websocket/WebSocketManager.d.ts +1 -0
- package/dist/types/services/core/websocket/connection-service.d.ts +1 -0
- package/dist/types/services/task/Task.d.ts +1 -0
- package/dist/types/services/task/TaskUtils.d.ts +13 -0
- package/dist/types/services/task/constants.d.ts +6 -0
- package/dist/types/services/task/dialer.d.ts +45 -0
- package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +36 -8
- package/dist/types/services/task/state-machine/constants.d.ts +3 -0
- package/dist/types/services/task/state-machine/guards.d.ts +2 -0
- package/dist/types/services/task/state-machine/types.d.ts +11 -0
- package/dist/types/services/task/types.d.ts +75 -1
- package/dist/types.js.map +1 -1
- package/dist/webex.js +1 -1
- package/package.json +2 -2
- package/src/cc.ts +389 -111
- package/src/constants.ts +3 -0
- package/src/metrics/behavioral-events.ts +42 -0
- package/src/metrics/constants.ts +9 -1
- package/src/services/ApiAiAssistant.ts +3 -0
- package/src/services/config/Util.ts +1 -1
- package/src/services/config/types.ts +12 -1
- package/src/services/constants.ts +1 -0
- package/src/services/core/Err.ts +3 -0
- package/src/services/core/Utils.ts +68 -5
- package/src/services/core/aqm-reqs.ts +100 -22
- package/src/services/core/websocket/WebSocketManager.ts +23 -2
- package/src/services/index.ts +1 -0
- package/src/services/task/Task.ts +13 -0
- package/src/services/task/TaskManager.ts +171 -7
- package/src/services/task/TaskUtils.ts +33 -1
- package/src/services/task/constants.ts +6 -0
- package/src/services/task/dialer.ts +136 -1
- package/src/services/task/state-machine/TaskStateMachine.ts +37 -5
- package/src/services/task/state-machine/actions.ts +4 -0
- package/src/services/task/state-machine/constants.ts +3 -0
- package/src/services/task/state-machine/guards.ts +30 -0
- package/src/services/task/state-machine/types.ts +14 -1
- package/src/services/task/types.ts +80 -0
- package/src/types.ts +4 -1
- package/test/unit/spec/cc.ts +294 -32
- package/test/unit/spec/services/config/index.ts +30 -2
- package/test/unit/spec/services/core/Utils.ts +367 -1
- package/test/unit/spec/services/core/websocket/WebSocketManager.ts +91 -1
- package/test/unit/spec/services/core/websocket/connection-service.ts +4 -5
- package/test/unit/spec/services/task/TaskManager.ts +4 -2
- package/test/unit/spec/services/task/dialer.ts +372 -96
- package/umd/contact-center.min.js +2 -2
- package/umd/contact-center.min.js.map +1 -1
|
@@ -746,7 +746,7 @@ describe('AgentConfigService', () => {
|
|
|
746
746
|
agentProfileId: 'profile123',
|
|
747
747
|
siteId: 'site789',
|
|
748
748
|
dbId: 'db123',
|
|
749
|
-
|
|
749
|
+
deafultDialledNumber: '1234567890',
|
|
750
750
|
id: 'user001',
|
|
751
751
|
teamIds: ['team1', 'team2'],
|
|
752
752
|
};
|
|
@@ -775,6 +775,17 @@ describe('AgentConfigService', () => {
|
|
|
775
775
|
|
|
776
776
|
const mockDialPlanData = [];
|
|
777
777
|
|
|
778
|
+
const mockTeamData = [
|
|
779
|
+
{id: 'team1', name: 'Support Team'},
|
|
780
|
+
{id: 'team2', name: 'Sales Team'},
|
|
781
|
+
];
|
|
782
|
+
|
|
783
|
+
const mockOrgInfo = {
|
|
784
|
+
tenantId: 'tenant123',
|
|
785
|
+
timezone: 'GMT',
|
|
786
|
+
environment: 'produs1',
|
|
787
|
+
};
|
|
788
|
+
|
|
778
789
|
const mockOrgSettings = {
|
|
779
790
|
campaignManagerEnabled: true,
|
|
780
791
|
webRtcEnabled: true,
|
|
@@ -798,6 +809,9 @@ describe('AgentConfigService', () => {
|
|
|
798
809
|
{id: 'aux1', type: 'WRAP_UP_CODE', name: 'Wrap Up Code 1', isDefault: true},
|
|
799
810
|
{id: 'aux2', type: 'IDLE_CODE', name: 'Idle Code 1', isDefault: true},
|
|
800
811
|
];
|
|
812
|
+
const mockAIFeatureFlags = {
|
|
813
|
+
data: [{realtimeTranscripts: {enable: true}}],
|
|
814
|
+
};
|
|
801
815
|
|
|
802
816
|
const parseAgentConfigsSpy = jest.spyOn(util, 'parseAgentConfigs');
|
|
803
817
|
agentConfigService.getUserUsingCI = jest.fn().mockResolvedValue(mockUserConfig);
|
|
@@ -861,7 +875,7 @@ describe('AgentConfigService', () => {
|
|
|
861
875
|
skillProfileId: 'skillProfile456',
|
|
862
876
|
siteId: 'site789',
|
|
863
877
|
dbId: 'db123',
|
|
864
|
-
|
|
878
|
+
deafultDialledNumber: '1234567890',
|
|
865
879
|
id: 'user001',
|
|
866
880
|
teamIds: ['team1', 'team2'],
|
|
867
881
|
};
|
|
@@ -895,6 +909,17 @@ describe('AgentConfigService', () => {
|
|
|
895
909
|
{id: 'dialPlan2', name: 'Plan 2'},
|
|
896
910
|
];
|
|
897
911
|
|
|
912
|
+
const mockTeamData = [
|
|
913
|
+
{id: 'team1', name: 'Support Team'},
|
|
914
|
+
{id: 'team2', name: 'Sales Team'},
|
|
915
|
+
];
|
|
916
|
+
|
|
917
|
+
const mockOrgInfo = {
|
|
918
|
+
tenantId: 'tenant123',
|
|
919
|
+
timezone: 'GMT',
|
|
920
|
+
environment: 'produs1',
|
|
921
|
+
};
|
|
922
|
+
|
|
898
923
|
const mockOrgSettings = {
|
|
899
924
|
campaignManagerEnabled: true,
|
|
900
925
|
webRtcEnabled: true,
|
|
@@ -919,6 +944,9 @@ describe('AgentConfigService', () => {
|
|
|
919
944
|
{id: 'aux1', type: 'WRAP_UP_CODE', name: 'Wrap Up Code 1'},
|
|
920
945
|
{id: 'aux2', type: 'IDLE_CODE', name: 'Idle Code 1'},
|
|
921
946
|
];
|
|
947
|
+
const mockAIFeatureFlags = {
|
|
948
|
+
data: [{realtimeTranscripts: {enable: true}}],
|
|
949
|
+
};
|
|
922
950
|
|
|
923
951
|
const parseAgentConfigsSpy = jest.spyOn(util, 'parseAgentConfigs');
|
|
924
952
|
agentConfigService.getUserUsingCI = jest.fn().mockResolvedValue(mockUserConfig);
|
|
@@ -11,6 +11,7 @@ jest.mock('../../../../../src/logger-proxy', () => ({
|
|
|
11
11
|
log: jest.fn(),
|
|
12
12
|
error: jest.fn(),
|
|
13
13
|
info: jest.fn(),
|
|
14
|
+
warn: jest.fn(),
|
|
14
15
|
initialize: jest.fn(),
|
|
15
16
|
},
|
|
16
17
|
}));
|
|
@@ -244,7 +245,7 @@ describe('Utils', () => {
|
|
|
244
245
|
const result = Utils.getStationLoginErrorData(failure, LoginOption.AGENT_DN);
|
|
245
246
|
expect(result).toEqual({
|
|
246
247
|
message:
|
|
247
|
-
'Enter a valid
|
|
248
|
+
'Enter a valid dial number. For help, reach out to your administrator or support team.',
|
|
248
249
|
fieldName: LoginOption.AGENT_DN,
|
|
249
250
|
});
|
|
250
251
|
});
|
|
@@ -670,4 +671,369 @@ describe('Utils', () => {
|
|
|
670
671
|
expect(underscoreResult.data.destinationType).toBe('entryPoint');
|
|
671
672
|
});
|
|
672
673
|
});
|
|
674
|
+
|
|
675
|
+
describe('getDestAgentIdForCBT', () => {
|
|
676
|
+
it('should return destination agent ID for CBT scenario', () => {
|
|
677
|
+
const interaction: any = {
|
|
678
|
+
participants: {
|
|
679
|
+
'agent-uuid-123': {
|
|
680
|
+
type: 'Agent',
|
|
681
|
+
pType: 'dn',
|
|
682
|
+
dn: '5551234567',
|
|
683
|
+
id: 'agent-uuid-123',
|
|
684
|
+
},
|
|
685
|
+
'customer-1': {
|
|
686
|
+
type: 'Customer',
|
|
687
|
+
pType: 'Customer',
|
|
688
|
+
id: 'customer-1',
|
|
689
|
+
},
|
|
690
|
+
},
|
|
691
|
+
};
|
|
692
|
+
const consultingAgent = '5551234567'; // Phone number, not in participants as key
|
|
693
|
+
|
|
694
|
+
const result = Utils.getDestAgentIdForCBT(interaction, consultingAgent);
|
|
695
|
+
expect(result).toBe('agent-uuid-123');
|
|
696
|
+
});
|
|
697
|
+
|
|
698
|
+
it('should return empty string when consultingAgent is in participants (non-CBT)', () => {
|
|
699
|
+
const interaction: any = {
|
|
700
|
+
participants: {
|
|
701
|
+
'agent-123': {
|
|
702
|
+
type: 'Agent',
|
|
703
|
+
pType: 'Agent',
|
|
704
|
+
id: 'agent-123',
|
|
705
|
+
},
|
|
706
|
+
},
|
|
707
|
+
};
|
|
708
|
+
const consultingAgent = 'agent-123'; // Exists as key in participants
|
|
709
|
+
|
|
710
|
+
const result = Utils.getDestAgentIdForCBT(interaction, consultingAgent);
|
|
711
|
+
expect(result).toBe('');
|
|
712
|
+
});
|
|
713
|
+
|
|
714
|
+
it('should return empty string when no matching dial number found', () => {
|
|
715
|
+
const interaction: any = {
|
|
716
|
+
participants: {
|
|
717
|
+
'agent-uuid-123': {
|
|
718
|
+
type: 'Agent',
|
|
719
|
+
pType: 'dn',
|
|
720
|
+
dn: '5559999999',
|
|
721
|
+
id: 'agent-uuid-123',
|
|
722
|
+
},
|
|
723
|
+
},
|
|
724
|
+
};
|
|
725
|
+
const consultingAgent = '5551234567'; // Different number
|
|
726
|
+
|
|
727
|
+
const result = Utils.getDestAgentIdForCBT(interaction, consultingAgent);
|
|
728
|
+
expect(result).toBe('');
|
|
729
|
+
});
|
|
730
|
+
|
|
731
|
+
it('should return empty string when consultingAgent is empty', () => {
|
|
732
|
+
const interaction: any = {
|
|
733
|
+
participants: {
|
|
734
|
+
'agent-uuid-123': {
|
|
735
|
+
type: 'Agent',
|
|
736
|
+
pType: 'dn',
|
|
737
|
+
dn: '5551234567',
|
|
738
|
+
},
|
|
739
|
+
},
|
|
740
|
+
};
|
|
741
|
+
|
|
742
|
+
const result = Utils.getDestAgentIdForCBT(interaction, '');
|
|
743
|
+
expect(result).toBe('');
|
|
744
|
+
});
|
|
745
|
+
|
|
746
|
+
it('should match only when participant type is dial number and type is Agent', () => {
|
|
747
|
+
const interaction: any = {
|
|
748
|
+
participants: {
|
|
749
|
+
'participant-1': {
|
|
750
|
+
type: 'Customer',
|
|
751
|
+
pType: 'dn',
|
|
752
|
+
dn: '5551234567',
|
|
753
|
+
},
|
|
754
|
+
'participant-2': {
|
|
755
|
+
type: 'Agent',
|
|
756
|
+
pType: 'Agent',
|
|
757
|
+
dn: '5551234567',
|
|
758
|
+
},
|
|
759
|
+
'participant-3': {
|
|
760
|
+
type: 'Agent',
|
|
761
|
+
pType: 'dn',
|
|
762
|
+
dn: '5551234567',
|
|
763
|
+
id: 'correct-agent',
|
|
764
|
+
},
|
|
765
|
+
},
|
|
766
|
+
};
|
|
767
|
+
|
|
768
|
+
const result = Utils.getDestAgentIdForCBT(interaction, '5551234567');
|
|
769
|
+
expect(result).toBe('participant-3');
|
|
770
|
+
});
|
|
771
|
+
|
|
772
|
+
it('should handle case-insensitive participant type comparison', () => {
|
|
773
|
+
const interaction: any = {
|
|
774
|
+
participants: {
|
|
775
|
+
'agent-uuid': {
|
|
776
|
+
type: 'Agent',
|
|
777
|
+
pType: 'DN', // Uppercase (dial number)
|
|
778
|
+
dn: '5551234567',
|
|
779
|
+
},
|
|
780
|
+
},
|
|
781
|
+
};
|
|
782
|
+
|
|
783
|
+
const result = Utils.getDestAgentIdForCBT(interaction, '5551234567');
|
|
784
|
+
expect(result).toBe('agent-uuid');
|
|
785
|
+
});
|
|
786
|
+
});
|
|
787
|
+
|
|
788
|
+
describe('calculateDestAgentId', () => {
|
|
789
|
+
const currentAgentId = 'agent-123';
|
|
790
|
+
|
|
791
|
+
it('should return destAgentIdCBT when found', () => {
|
|
792
|
+
const interaction: any = {
|
|
793
|
+
media: {
|
|
794
|
+
consult: {
|
|
795
|
+
mType: 'consult',
|
|
796
|
+
participants: [currentAgentId, '5551234567'],
|
|
797
|
+
},
|
|
798
|
+
},
|
|
799
|
+
participants: {
|
|
800
|
+
'agent-uuid-456': {
|
|
801
|
+
type: 'Agent',
|
|
802
|
+
pType: 'dn',
|
|
803
|
+
dn: '5551234567',
|
|
804
|
+
id: 'agent-uuid-456',
|
|
805
|
+
},
|
|
806
|
+
},
|
|
807
|
+
};
|
|
808
|
+
|
|
809
|
+
const result = Utils.calculateDestAgentId(interaction, currentAgentId);
|
|
810
|
+
expect(result).toBe('agent-uuid-456');
|
|
811
|
+
});
|
|
812
|
+
|
|
813
|
+
it('should return participant id for regular agent when not CBT', () => {
|
|
814
|
+
const consultedAgentId = 'agent-456';
|
|
815
|
+
const interaction: any = {
|
|
816
|
+
media: {
|
|
817
|
+
consult: {
|
|
818
|
+
mType: 'consult',
|
|
819
|
+
participants: [currentAgentId, consultedAgentId],
|
|
820
|
+
},
|
|
821
|
+
},
|
|
822
|
+
participants: {
|
|
823
|
+
[consultedAgentId]: {
|
|
824
|
+
type: 'Agent',
|
|
825
|
+
id: consultedAgentId,
|
|
826
|
+
},
|
|
827
|
+
},
|
|
828
|
+
};
|
|
829
|
+
|
|
830
|
+
const result = Utils.calculateDestAgentId(interaction, currentAgentId);
|
|
831
|
+
expect(result).toBe(consultedAgentId);
|
|
832
|
+
});
|
|
833
|
+
|
|
834
|
+
it('should return epId for EpDn type participants', () => {
|
|
835
|
+
const consultedAgentId = 'epdn-456';
|
|
836
|
+
const interaction: any = {
|
|
837
|
+
media: {
|
|
838
|
+
consult: {
|
|
839
|
+
mType: 'consult',
|
|
840
|
+
participants: [currentAgentId, consultedAgentId],
|
|
841
|
+
},
|
|
842
|
+
},
|
|
843
|
+
participants: {
|
|
844
|
+
[consultedAgentId]: {
|
|
845
|
+
type: 'EpDn',
|
|
846
|
+
id: consultedAgentId,
|
|
847
|
+
epId: 'entry-point-id-789',
|
|
848
|
+
},
|
|
849
|
+
},
|
|
850
|
+
};
|
|
851
|
+
|
|
852
|
+
const result = Utils.calculateDestAgentId(interaction, currentAgentId);
|
|
853
|
+
expect(result).toBe('entry-point-id-789');
|
|
854
|
+
});
|
|
855
|
+
|
|
856
|
+
it('should return undefined when no consulting agent found', () => {
|
|
857
|
+
const interaction: any = {
|
|
858
|
+
media: {
|
|
859
|
+
mainCall: {
|
|
860
|
+
mType: 'mainCall',
|
|
861
|
+
participants: [currentAgentId],
|
|
862
|
+
},
|
|
863
|
+
},
|
|
864
|
+
participants: {},
|
|
865
|
+
};
|
|
866
|
+
|
|
867
|
+
const result = Utils.calculateDestAgentId(interaction, currentAgentId);
|
|
868
|
+
expect(result).toBe('');
|
|
869
|
+
});
|
|
870
|
+
|
|
871
|
+
it('should handle CBT scenario when phone number is not a direct participant key', () => {
|
|
872
|
+
const interaction: any = {
|
|
873
|
+
media: {
|
|
874
|
+
consult: {
|
|
875
|
+
mType: 'consult',
|
|
876
|
+
participants: [currentAgentId, '5551234567'], // Phone number in media
|
|
877
|
+
},
|
|
878
|
+
},
|
|
879
|
+
participants: {
|
|
880
|
+
// Note: '5551234567' is NOT a key - this is CBT
|
|
881
|
+
'agent-uuid-cbt': {
|
|
882
|
+
type: 'Agent',
|
|
883
|
+
pType: 'dn',
|
|
884
|
+
dn: '5551234567', // Found by matching DN
|
|
885
|
+
id: 'agent-uuid-cbt',
|
|
886
|
+
},
|
|
887
|
+
},
|
|
888
|
+
};
|
|
889
|
+
|
|
890
|
+
const result = Utils.calculateDestAgentId(interaction, currentAgentId);
|
|
891
|
+
expect(result).toBe('agent-uuid-cbt'); // Returns the CBT agent
|
|
892
|
+
});
|
|
893
|
+
});
|
|
894
|
+
|
|
895
|
+
describe('isValidDialNumber', () => {
|
|
896
|
+
const anyFormatEntry = {
|
|
897
|
+
name: 'Any Format',
|
|
898
|
+
prefix: '',
|
|
899
|
+
regex: '([0-9a-zA-Z]+[-._])*[0-9a-zA-Z]+',
|
|
900
|
+
strippedChars: '( )-',
|
|
901
|
+
};
|
|
902
|
+
|
|
903
|
+
const usOnlyEntry = {
|
|
904
|
+
name: 'US',
|
|
905
|
+
prefix: '1',
|
|
906
|
+
regex: '1[0-9]{3}[2-9][0-9]{6}([,]{1,10}[0-9]+){0,1}',
|
|
907
|
+
strippedChars: '( )-',
|
|
908
|
+
};
|
|
909
|
+
|
|
910
|
+
describe('with multiple dial plan entries (Any Format + US)', () => {
|
|
911
|
+
const dialPlanEntries = [anyFormatEntry, usOnlyEntry];
|
|
912
|
+
|
|
913
|
+
it('should return true for a valid US phone number', () => {
|
|
914
|
+
const result = Utils.isValidDialNumber('12223334567', dialPlanEntries);
|
|
915
|
+
expect(result).toBe(true);
|
|
916
|
+
});
|
|
917
|
+
|
|
918
|
+
it('should return true for a UK phone number', () => {
|
|
919
|
+
const result = Utils.isValidDialNumber('+442030484377', dialPlanEntries);
|
|
920
|
+
expect(result).toBe(true);
|
|
921
|
+
});
|
|
922
|
+
|
|
923
|
+
it('should return true for a European number', () => {
|
|
924
|
+
const result = Utils.isValidDialNumber('6955577166', dialPlanEntries);
|
|
925
|
+
expect(result).toBe(true);
|
|
926
|
+
});
|
|
927
|
+
});
|
|
928
|
+
|
|
929
|
+
describe('with US-only dial plan entry', () => {
|
|
930
|
+
const dialPlanEntries = [usOnlyEntry];
|
|
931
|
+
|
|
932
|
+
it('should return true for a valid US phone number', () => {
|
|
933
|
+
const result = Utils.isValidDialNumber('12223334567', dialPlanEntries);
|
|
934
|
+
expect(result).toBe(true);
|
|
935
|
+
});
|
|
936
|
+
|
|
937
|
+
it('should return false for a UK phone number', () => {
|
|
938
|
+
const result = Utils.isValidDialNumber('+442030484377', dialPlanEntries);
|
|
939
|
+
expect(result).toBe(false);
|
|
940
|
+
});
|
|
941
|
+
|
|
942
|
+
it('should return false for an invalid US number format', () => {
|
|
943
|
+
const result = Utils.isValidDialNumber('1234567890', dialPlanEntries);
|
|
944
|
+
expect(result).toBe(false);
|
|
945
|
+
});
|
|
946
|
+
});
|
|
947
|
+
|
|
948
|
+
describe('with empty dial plan entries (defers to server)', () => {
|
|
949
|
+
it('should return true for any dial number', () => {
|
|
950
|
+
expect(Utils.isValidDialNumber('12223334567', [])).toBe(true);
|
|
951
|
+
});
|
|
952
|
+
|
|
953
|
+
it('should return true for a UK phone number', () => {
|
|
954
|
+
expect(Utils.isValidDialNumber('+442030484377', [])).toBe(true);
|
|
955
|
+
});
|
|
956
|
+
|
|
957
|
+
it('should return true for a European number', () => {
|
|
958
|
+
expect(Utils.isValidDialNumber('6955577166', [])).toBe(true);
|
|
959
|
+
});
|
|
960
|
+
});
|
|
961
|
+
|
|
962
|
+
describe('strippedChars handling', () => {
|
|
963
|
+
it('should strip characters before regex matching', () => {
|
|
964
|
+
const strictEntry = {
|
|
965
|
+
name: 'Digits Only',
|
|
966
|
+
prefix: '',
|
|
967
|
+
regex: '^[0-9]{10,15}$',
|
|
968
|
+
strippedChars: '( )-+',
|
|
969
|
+
};
|
|
970
|
+
const result = Utils.isValidDialNumber('+44 (203) 048-4377', [strictEntry]);
|
|
971
|
+
expect(result).toBe(true);
|
|
972
|
+
});
|
|
973
|
+
|
|
974
|
+
it('should handle entries with no strippedChars', () => {
|
|
975
|
+
const noStripEntry = {
|
|
976
|
+
name: 'No Strip',
|
|
977
|
+
prefix: '',
|
|
978
|
+
regex: '^[0-9]+$',
|
|
979
|
+
strippedChars: '',
|
|
980
|
+
};
|
|
981
|
+
expect(Utils.isValidDialNumber('12345', [noStripEntry])).toBe(true);
|
|
982
|
+
expect(Utils.isValidDialNumber('+12345', [noStripEntry])).toBe(false);
|
|
983
|
+
});
|
|
984
|
+
});
|
|
985
|
+
|
|
986
|
+
describe('empty or undefined dial number', () => {
|
|
987
|
+
it('should return false and log warning for undefined dial number', () => {
|
|
988
|
+
const result = Utils.isValidDialNumber(undefined as any, [anyFormatEntry]);
|
|
989
|
+
expect(result).toBe(false);
|
|
990
|
+
expect(LoggerProxy.warn).toHaveBeenCalledWith(
|
|
991
|
+
'Dial number is empty or undefined.',
|
|
992
|
+
expect.objectContaining({module: 'Utils', method: 'isValidDialNumber'})
|
|
993
|
+
);
|
|
994
|
+
});
|
|
995
|
+
|
|
996
|
+
it('should return false and log warning for empty string dial number', () => {
|
|
997
|
+
const result = Utils.isValidDialNumber('', [anyFormatEntry]);
|
|
998
|
+
expect(result).toBe(false);
|
|
999
|
+
expect(LoggerProxy.warn).toHaveBeenCalledWith(
|
|
1000
|
+
'Dial number is empty or undefined.',
|
|
1001
|
+
expect.objectContaining({module: 'Utils', method: 'isValidDialNumber'})
|
|
1002
|
+
);
|
|
1003
|
+
});
|
|
1004
|
+
});
|
|
1005
|
+
|
|
1006
|
+
describe('invalid regex handling', () => {
|
|
1007
|
+
it('should return false and log warning for invalid regex pattern', () => {
|
|
1008
|
+
const badEntry = {
|
|
1009
|
+
name: 'Bad Regex',
|
|
1010
|
+
prefix: '',
|
|
1011
|
+
regex: '[invalid(',
|
|
1012
|
+
strippedChars: '',
|
|
1013
|
+
};
|
|
1014
|
+
const result = Utils.isValidDialNumber('12345', [badEntry]);
|
|
1015
|
+
expect(result).toBe(false);
|
|
1016
|
+
expect(LoggerProxy.warn).toHaveBeenCalledWith(
|
|
1017
|
+
expect.stringContaining('Failed to validate dial number against entry "Bad Regex"'),
|
|
1018
|
+
expect.objectContaining({module: 'Utils', method: 'isValidDialNumber'})
|
|
1019
|
+
);
|
|
1020
|
+
});
|
|
1021
|
+
});
|
|
1022
|
+
});
|
|
1023
|
+
|
|
1024
|
+
describe('stripDialPlanChars', () => {
|
|
1025
|
+
it('should remove specified characters from input', () => {
|
|
1026
|
+
expect(Utils.stripDialPlanChars('+44 (203) 048-4377', '( )-+')).toBe('442030484377');
|
|
1027
|
+
});
|
|
1028
|
+
|
|
1029
|
+
it('should return input unchanged when strippedChars is empty', () => {
|
|
1030
|
+
expect(Utils.stripDialPlanChars('+442030484377', '')).toBe('+442030484377');
|
|
1031
|
+
});
|
|
1032
|
+
|
|
1033
|
+
it('should return input unchanged when strippedChars is null/undefined', () => {
|
|
1034
|
+
expect(Utils.stripDialPlanChars('12345', null as any)).toBe('12345');
|
|
1035
|
+
expect(Utils.stripDialPlanChars('12345', undefined as any)).toBe('12345');
|
|
1036
|
+
});
|
|
1037
|
+
});
|
|
1038
|
+
|
|
673
1039
|
});
|
|
@@ -78,6 +78,14 @@ describe('WebSocketManager', () => {
|
|
|
78
78
|
|
|
79
79
|
mockWebex = {
|
|
80
80
|
request: jest.fn(),
|
|
81
|
+
credentials: {
|
|
82
|
+
getOrgId: jest.fn().mockReturnValue('test-org-id'),
|
|
83
|
+
},
|
|
84
|
+
internal: {
|
|
85
|
+
services: {
|
|
86
|
+
isIntegrationEnvironment: jest.fn().mockReturnValue(true), // INT environment by default
|
|
87
|
+
},
|
|
88
|
+
},
|
|
81
89
|
} as unknown as WebexSDK;
|
|
82
90
|
|
|
83
91
|
mockWorker = {
|
|
@@ -111,13 +119,15 @@ describe('WebSocketManager', () => {
|
|
|
111
119
|
expect(webSocketManager).toBeDefined();
|
|
112
120
|
});
|
|
113
121
|
|
|
114
|
-
it('should register and connect to WebSocket', async () => {
|
|
122
|
+
it('should register and connect to WebSocket with X-ORGANIZATION-ID header for INT environment', async () => {
|
|
115
123
|
const subscribeResponse = {
|
|
116
124
|
body: {
|
|
117
125
|
webSocketUrl: 'wss://fake-url',
|
|
118
126
|
},
|
|
119
127
|
};
|
|
120
128
|
|
|
129
|
+
// Mock INT environment (services.isIntegrationEnvironment returns true)
|
|
130
|
+
(mockWebex.internal.services.isIntegrationEnvironment as jest.Mock).mockReturnValue(true);
|
|
121
131
|
(mockWebex.request as jest.Mock).mockResolvedValueOnce(subscribeResponse);
|
|
122
132
|
|
|
123
133
|
await webSocketManager.initWebSocket({body: fakeSubscribeRequest, resource: SUBSCRIBE_API});
|
|
@@ -127,6 +137,7 @@ describe('WebSocketManager', () => {
|
|
|
127
137
|
resource: SUBSCRIBE_API,
|
|
128
138
|
method: 'POST',
|
|
129
139
|
body: fakeSubscribeRequest,
|
|
140
|
+
headers: {'X-ORGANIZATION-ID': 'test-org-id'},
|
|
130
141
|
});
|
|
131
142
|
});
|
|
132
143
|
|
|
@@ -149,9 +160,88 @@ describe('WebSocketManager', () => {
|
|
|
149
160
|
resource: RTD_SUBSCRIBE_API,
|
|
150
161
|
method: 'POST',
|
|
151
162
|
body: fakeSubscribeRequest,
|
|
163
|
+
headers: {'X-ORGANIZATION-ID': 'test-org-id'},
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('should register and connect to WebSocket without X-ORGANIZATION-ID header for production environment', async () => {
|
|
168
|
+
const subscribeResponse = {
|
|
169
|
+
body: {
|
|
170
|
+
webSocketUrl: 'wss://fake-url',
|
|
171
|
+
},
|
|
172
|
+
};
|
|
173
|
+
// Mock production environment (services.isIntegrationEnvironment returns false)
|
|
174
|
+
(mockWebex.internal.services.isIntegrationEnvironment as jest.Mock).mockReturnValue(false);
|
|
175
|
+
(mockWebex.request as jest.Mock).mockResolvedValueOnce(subscribeResponse);
|
|
176
|
+
|
|
177
|
+
// Create new WebSocketManager instance with production mock
|
|
178
|
+
webSocketManager = new WebSocketManager({ webex: mockWebex });
|
|
179
|
+
|
|
180
|
+
setTimeout(() => {
|
|
181
|
+
MockWebSocket.inst.onopen();
|
|
182
|
+
MockWebSocket.inst.onmessage({ data: JSON.stringify({ type: "Welcome" }) });
|
|
183
|
+
}, 1);
|
|
184
|
+
|
|
185
|
+
await webSocketManager.initWebSocket({ body: fakeSubscribeRequest, resource: SUBSCRIBE_API });
|
|
186
|
+
|
|
187
|
+
expect(mockWebex.request).toHaveBeenCalledWith({
|
|
188
|
+
service: WCC_API_GATEWAY,
|
|
189
|
+
resource: SUBSCRIBE_API,
|
|
190
|
+
method: 'POST',
|
|
191
|
+
body: fakeSubscribeRequest,
|
|
192
|
+
headers: undefined,
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('should not send X-ORGANIZATION-ID header when services.isIntegrationEnvironment is not available', async () => {
|
|
197
|
+
const subscribeResponse = {
|
|
198
|
+
body: {
|
|
199
|
+
webSocketUrl: 'wss://fake-url',
|
|
200
|
+
},
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
// Mock services not available (defaults to production behavior)
|
|
204
|
+
(mockWebex as any).internal = undefined;
|
|
205
|
+
(mockWebex.request as jest.Mock).mockResolvedValueOnce(subscribeResponse);
|
|
206
|
+
|
|
207
|
+
webSocketManager = new WebSocketManager({ webex: mockWebex });
|
|
208
|
+
|
|
209
|
+
setTimeout(() => {
|
|
210
|
+
MockWebSocket.inst.onopen();
|
|
211
|
+
MockWebSocket.inst.onmessage({ data: JSON.stringify({ type: "Welcome" }) });
|
|
212
|
+
}, 1);
|
|
213
|
+
|
|
214
|
+
await webSocketManager.initWebSocket({ body: fakeSubscribeRequest, resource: SUBSCRIBE_API });
|
|
215
|
+
|
|
216
|
+
expect(mockWebex.request).toHaveBeenCalledWith({
|
|
217
|
+
service: WCC_API_GATEWAY,
|
|
218
|
+
resource: SUBSCRIBE_API,
|
|
219
|
+
method: 'POST',
|
|
220
|
+
body: fakeSubscribeRequest,
|
|
221
|
+
headers: undefined,
|
|
152
222
|
});
|
|
153
223
|
});
|
|
154
224
|
|
|
225
|
+
it('should log error and throw when register API fails in initWebSocket', async () => {
|
|
226
|
+
const error = new Error('Register API failed');
|
|
227
|
+
|
|
228
|
+
(mockWebex.request as jest.Mock).mockRejectedValueOnce(error);
|
|
229
|
+
|
|
230
|
+
await expect(
|
|
231
|
+
webSocketManager.initWebSocket({ body: fakeSubscribeRequest, resource: SUBSCRIBE_API })
|
|
232
|
+
).rejects.toThrow(error);
|
|
233
|
+
|
|
234
|
+
expect(LoggerProxy.error).toHaveBeenCalledWith(
|
|
235
|
+
`Register API Failed, Request to RoutingNotifs websocket registration API failed ${error}`,
|
|
236
|
+
{ module: WEB_SOCKET_MANAGER_FILE, method: 'register' }
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
expect(LoggerProxy.error).toHaveBeenCalledWith(
|
|
240
|
+
`[WebSocketStatus] | Error in registering Websocket ${error}`,
|
|
241
|
+
{ module: WEB_SOCKET_MANAGER_FILE, method: 'initWebSocket' }
|
|
242
|
+
);
|
|
243
|
+
});
|
|
244
|
+
|
|
155
245
|
it('should close WebSocket connection', async () => {
|
|
156
246
|
const subscribeResponse = {
|
|
157
247
|
body: {
|
|
@@ -3,7 +3,9 @@ import {WebSocketManager} from '../../../../../../src/services/core/websocket/We
|
|
|
3
3
|
import {SubscribeRequest} from '../../../../../../src/types';
|
|
4
4
|
import LoggerProxy from '../../../../../../src/logger-proxy';
|
|
5
5
|
import {CONNECTIVITY_CHECK_INTERVAL} from '../../../../../../src/services/core/constants';
|
|
6
|
-
import {CONNECTION_SERVICE_FILE} from '../../../../../../src/constants';
|
|
6
|
+
import { CONNECTION_SERVICE_FILE } from '../../../../../../src/constants';
|
|
7
|
+
import {SUBSCRIBE_API} from '../../../../../../src/services/constants';
|
|
8
|
+
|
|
7
9
|
|
|
8
10
|
jest.mock('../../../../../../src/services/core/websocket/WebSocketManager');
|
|
9
11
|
jest.mock('../../../../../../src/logger-proxy', () => ({
|
|
@@ -109,10 +111,7 @@ describe('ConnectionService', () => {
|
|
|
109
111
|
'event=socketConnectionRetry | Trying to reconnect to websocket',
|
|
110
112
|
{module: CONNECTION_SERVICE_FILE, method: 'handleSocketClose'}
|
|
111
113
|
);
|
|
112
|
-
expect(mockWebSocketManager.initWebSocket).toHaveBeenCalledWith({
|
|
113
|
-
body: mockSubscribeRequest,
|
|
114
|
-
resource: 'v1/notification/subscribe',
|
|
115
|
-
});
|
|
114
|
+
expect(mockWebSocketManager.initWebSocket).toHaveBeenCalledWith({body: mockSubscribeRequest, resource: SUBSCRIBE_API});
|
|
116
115
|
});
|
|
117
116
|
|
|
118
117
|
describe('ConnectionService onPing', () => {
|
|
@@ -764,6 +764,7 @@ describe('TaskManager', () => {
|
|
|
764
764
|
destAgentId: 'ebeb893b-ba67-4f36-8418-95c7492b28c2',
|
|
765
765
|
owner: '723a8ffb-a26e-496d-b14a-ff44fb83b64f',
|
|
766
766
|
queueMgr: 'aqm',
|
|
767
|
+
agentsPendingWrapUp: ['test-agent-id'],
|
|
767
768
|
},
|
|
768
769
|
};
|
|
769
770
|
|
|
@@ -1380,7 +1381,7 @@ describe('TaskManager', () => {
|
|
|
1380
1381
|
state: 'new',
|
|
1381
1382
|
mediaType: 'telephony',
|
|
1382
1383
|
},
|
|
1383
|
-
agentsPendingWrapUp: ['agent-
|
|
1384
|
+
agentsPendingWrapUp: ['test-agent-id'],
|
|
1384
1385
|
});
|
|
1385
1386
|
task.unregisterWebCallListeners = jest.fn();
|
|
1386
1387
|
const removeTaskSpy = jest.spyOn(taskManager, 'removeTaskFromCollection');
|
|
@@ -1394,7 +1395,7 @@ describe('TaskManager', () => {
|
|
|
1394
1395
|
state: 'new',
|
|
1395
1396
|
mediaType: 'telephony',
|
|
1396
1397
|
},
|
|
1397
|
-
agentsPendingWrapUp: ['agent-
|
|
1398
|
+
agentsPendingWrapUp: ['test-agent-id'],
|
|
1398
1399
|
},
|
|
1399
1400
|
};
|
|
1400
1401
|
|
|
@@ -2174,6 +2175,7 @@ describe('TaskManager', () => {
|
|
|
2174
2175
|
type: CC_EVENTS.CONTACT_ENDED,
|
|
2175
2176
|
interaction: {mediaType: 'email', state: 'connected'}, // Using 'connected' to NOT trigger cleanup
|
|
2176
2177
|
wrapUpRequired: true,
|
|
2178
|
+
agentsPendingWrapUp: ['test-agent-id'],
|
|
2177
2179
|
},
|
|
2178
2180
|
};
|
|
2179
2181
|
|