@webex/contact-center 3.12.0-task-refactor.11 → 3.12.0-task-refactor.12
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 +293 -26
- 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 +90 -1
- package/test/unit/spec/services/core/websocket/connection-service.ts +4 -5
- package/test/unit/spec/services/task/TaskManager.ts +2325 -150
- 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
|
@@ -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).toBeUndefined();
|
|
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
|
|
|
@@ -152,6 +163,84 @@ describe('WebSocketManager', () => {
|
|
|
152
163
|
});
|
|
153
164
|
});
|
|
154
165
|
|
|
166
|
+
it('should register and connect to WebSocket without X-ORGANIZATION-ID header for production environment', async () => {
|
|
167
|
+
const subscribeResponse = {
|
|
168
|
+
body: {
|
|
169
|
+
webSocketUrl: 'wss://fake-url',
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
// Mock production environment (services.isIntegrationEnvironment returns false)
|
|
173
|
+
(mockWebex.internal.services.isIntegrationEnvironment as jest.Mock).mockReturnValue(false);
|
|
174
|
+
(mockWebex.request as jest.Mock).mockResolvedValueOnce(subscribeResponse);
|
|
175
|
+
|
|
176
|
+
// Create new WebSocketManager instance with production mock
|
|
177
|
+
webSocketManager = new WebSocketManager({ webex: mockWebex });
|
|
178
|
+
|
|
179
|
+
setTimeout(() => {
|
|
180
|
+
MockWebSocket.inst.onopen();
|
|
181
|
+
MockWebSocket.inst.onmessage({ data: JSON.stringify({ type: "Welcome" }) });
|
|
182
|
+
}, 1);
|
|
183
|
+
|
|
184
|
+
await webSocketManager.initWebSocket({ body: fakeSubscribeRequest, resource: SUBSCRIBE_API });
|
|
185
|
+
|
|
186
|
+
expect(mockWebex.request).toHaveBeenCalledWith({
|
|
187
|
+
service: WCC_API_GATEWAY,
|
|
188
|
+
resource: SUBSCRIBE_API,
|
|
189
|
+
method: 'POST',
|
|
190
|
+
body: fakeSubscribeRequest,
|
|
191
|
+
headers: undefined,
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it('should not send X-ORGANIZATION-ID header when services.isIntegrationEnvironment is not available', async () => {
|
|
196
|
+
const subscribeResponse = {
|
|
197
|
+
body: {
|
|
198
|
+
webSocketUrl: 'wss://fake-url',
|
|
199
|
+
},
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
// Mock services not available (defaults to production behavior)
|
|
203
|
+
(mockWebex as any).internal = undefined;
|
|
204
|
+
(mockWebex.request as jest.Mock).mockResolvedValueOnce(subscribeResponse);
|
|
205
|
+
|
|
206
|
+
webSocketManager = new WebSocketManager({ webex: mockWebex });
|
|
207
|
+
|
|
208
|
+
setTimeout(() => {
|
|
209
|
+
MockWebSocket.inst.onopen();
|
|
210
|
+
MockWebSocket.inst.onmessage({ data: JSON.stringify({ type: "Welcome" }) });
|
|
211
|
+
}, 1);
|
|
212
|
+
|
|
213
|
+
await webSocketManager.initWebSocket({ body: fakeSubscribeRequest, resource: SUBSCRIBE_API });
|
|
214
|
+
|
|
215
|
+
expect(mockWebex.request).toHaveBeenCalledWith({
|
|
216
|
+
service: WCC_API_GATEWAY,
|
|
217
|
+
resource: SUBSCRIBE_API,
|
|
218
|
+
method: 'POST',
|
|
219
|
+
body: fakeSubscribeRequest,
|
|
220
|
+
headers: undefined,
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it('should log error and throw when register API fails in initWebSocket', async () => {
|
|
225
|
+
const error = new Error('Register API failed');
|
|
226
|
+
|
|
227
|
+
(mockWebex.request as jest.Mock).mockRejectedValueOnce(error);
|
|
228
|
+
|
|
229
|
+
await expect(
|
|
230
|
+
webSocketManager.initWebSocket({ body: fakeSubscribeRequest, resource: SUBSCRIBE_API })
|
|
231
|
+
).rejects.toThrow(error);
|
|
232
|
+
|
|
233
|
+
expect(LoggerProxy.error).toHaveBeenCalledWith(
|
|
234
|
+
`Register API Failed, Request to RoutingNotifs websocket registration API failed ${error}`,
|
|
235
|
+
{ module: WEB_SOCKET_MANAGER_FILE, method: 'register' }
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
expect(LoggerProxy.error).toHaveBeenCalledWith(
|
|
239
|
+
`[WebSocketStatus] | Error in registering Websocket ${error}`,
|
|
240
|
+
{ module: WEB_SOCKET_MANAGER_FILE, method: 'initWebSocket' }
|
|
241
|
+
);
|
|
242
|
+
});
|
|
243
|
+
|
|
155
244
|
it('should close WebSocket connection', async () => {
|
|
156
245
|
const subscribeResponse = {
|
|
157
246
|
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', () => {
|