@webex/contact-center 3.12.0-next.12 → 3.12.0-next.121
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/.sdd/manifest.json +884 -0
- package/AGENTS.md +94 -0
- package/ai-docs/ARCHITECTURE.md +168 -0
- package/ai-docs/CONTRACTS.md +51 -0
- package/ai-docs/GETTING_STARTED.md +168 -0
- package/ai-docs/GLOSSARY.md +43 -0
- package/ai-docs/README.md +138 -0
- package/ai-docs/REVIEW_CHECKLIST.md +41 -0
- package/ai-docs/RULES.md +444 -0
- package/ai-docs/SECURITY.md +52 -0
- package/ai-docs/SERVICE_STATE.md +48 -0
- package/ai-docs/SPEC_INDEX.md +65 -0
- package/ai-docs/adr/0001-spec-source-policy.md +55 -0
- package/ai-docs/adr/README.md +8 -0
- package/ai-docs/adr/_adr-template.md +31 -0
- package/ai-docs/contact-center-spec.md +359 -0
- package/ai-docs/features/consult-transfer-list-policy/spec/feature-spec.md +362 -0
- package/ai-docs/features/generated-spec-conformance-fidelity-remediation/spec/feature-spec.md +117 -0
- package/ai-docs/features/residual-warning-coverage-completion/spec/feature-spec.md +203 -0
- package/ai-docs/features/validator-code-fidelity-drift-fix/spec/feature-spec.md +315 -0
- package/ai-docs/patterns/event-driven-patterns.md +485 -0
- package/ai-docs/patterns/testing-patterns.md +480 -0
- package/ai-docs/patterns/typescript-patterns.md +365 -0
- package/ai-docs/templates/README.md +102 -0
- package/ai-docs/templates/documentation/create-agents-md.md +240 -0
- package/ai-docs/templates/documentation/create-architecture-md.md +295 -0
- package/ai-docs/templates/existing-service/bug-fix.md +254 -0
- package/ai-docs/templates/existing-service/feature-enhancement.md +450 -0
- package/ai-docs/templates/new-method/00-master.md +80 -0
- package/ai-docs/templates/new-method/01-requirements.md +232 -0
- package/ai-docs/templates/new-method/02-implementation.md +295 -0
- package/ai-docs/templates/new-method/03-tests.md +201 -0
- package/ai-docs/templates/new-method/04-validation.md +141 -0
- package/ai-docs/templates/new-service/00-master.md +109 -0
- package/ai-docs/templates/new-service/01-pre-questions.md +159 -0
- package/ai-docs/templates/new-service/02-code-generation.md +346 -0
- package/ai-docs/templates/new-service/03-integration.md +178 -0
- package/ai-docs/templates/new-service/04-test-generation.md +205 -0
- package/ai-docs/templates/new-service/05-validation.md +145 -0
- package/dist/cc.js +808 -40
- package/dist/cc.js.map +1 -1
- package/dist/config.js +13 -0
- package/dist/config.js.map +1 -1
- package/dist/constants.js +27 -3
- package/dist/constants.js.map +1 -1
- package/dist/index.js +20 -5
- package/dist/index.js.map +1 -1
- package/dist/metrics/MetricsManager.js +16 -0
- package/dist/metrics/MetricsManager.js.map +1 -1
- package/dist/metrics/behavioral-events.js +205 -0
- package/dist/metrics/behavioral-events.js.map +1 -1
- package/dist/metrics/constants.js +43 -4
- package/dist/metrics/constants.js.map +1 -1
- package/dist/services/AddressBook.js +18 -15
- package/dist/services/AddressBook.js.map +1 -1
- package/dist/services/AnswerCallOnWebexService.js +202 -0
- package/dist/services/AnswerCallOnWebexService.js.map +1 -0
- package/dist/services/ApiAiAssistant.js +153 -8
- package/dist/services/ApiAiAssistant.js.map +1 -1
- package/dist/services/EntryPoint.js +46 -68
- package/dist/services/EntryPoint.js.map +1 -1
- package/dist/services/Queue.js +27 -22
- package/dist/services/Queue.js.map +1 -1
- package/dist/services/UserPreference.js +427 -0
- package/dist/services/UserPreference.js.map +1 -0
- package/dist/services/WebexCrossClientService.js +222 -0
- package/dist/services/WebexCrossClientService.js.map +1 -0
- package/dist/services/WxAppTelephonyMercurySync.js +108 -0
- package/dist/services/WxAppTelephonyMercurySync.js.map +1 -0
- package/dist/services/config/Util.js +6 -3
- package/dist/services/config/Util.js.map +1 -1
- package/dist/services/config/constants.js +33 -8
- package/dist/services/config/constants.js.map +1 -1
- package/dist/services/config/types.js +53 -9
- package/dist/services/config/types.js.map +1 -1
- package/dist/services/core/Err.js.map +1 -1
- package/dist/services/core/Utils.js +107 -32
- package/dist/services/core/Utils.js.map +1 -1
- package/dist/services/core/WebexRequest.js +6 -2
- package/dist/services/core/WebexRequest.js.map +1 -1
- package/dist/services/core/aqm-reqs.js +28 -14
- package/dist/services/core/aqm-reqs.js.map +1 -1
- package/dist/services/core/types.js.map +1 -1
- package/dist/services/core/websocket/WebSocketManager.js +2 -1
- package/dist/services/core/websocket/WebSocketManager.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/Task.js +754 -0
- package/dist/services/task/Task.js.map +1 -0
- package/dist/services/task/TaskFactory.js +49 -0
- package/dist/services/task/TaskFactory.js.map +1 -0
- package/dist/services/task/TaskManager.js +1053 -520
- package/dist/services/task/TaskManager.js.map +1 -1
- package/dist/services/task/TaskUtils.js +211 -27
- package/dist/services/task/TaskUtils.js.map +1 -1
- package/dist/services/task/WebexCallingUtils.js +70 -0
- package/dist/services/task/WebexCallingUtils.js.map +1 -0
- package/dist/services/task/constants.js +12 -2
- package/dist/services/task/constants.js.map +1 -1
- package/dist/services/task/contact.js +29 -0
- package/dist/services/task/contact.js.map +1 -1
- package/dist/services/task/dialer.js +78 -0
- package/dist/services/task/dialer.js.map +1 -1
- package/dist/services/task/digital/Digital.js +78 -0
- package/dist/services/task/digital/Digital.js.map +1 -0
- package/dist/services/task/state-machine/TaskStateMachine.js +954 -0
- package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -0
- package/dist/services/task/state-machine/actions.js +572 -0
- package/dist/services/task/state-machine/actions.js.map +1 -0
- package/dist/services/task/state-machine/constants.js +161 -0
- package/dist/services/task/state-machine/constants.js.map +1 -0
- package/dist/services/task/state-machine/guards.js +379 -0
- package/dist/services/task/state-machine/guards.js.map +1 -0
- package/dist/services/task/state-machine/index.js +53 -0
- package/dist/services/task/state-machine/index.js.map +1 -0
- package/dist/services/task/state-machine/types.js +54 -0
- package/dist/services/task/state-machine/types.js.map +1 -0
- package/dist/services/task/state-machine/uiControlsComputer.js +703 -0
- package/dist/services/task/state-machine/uiControlsComputer.js.map +1 -0
- package/dist/services/task/taskDataNormalizer.js +99 -0
- package/dist/services/task/taskDataNormalizer.js.map +1 -0
- package/dist/services/task/types.js +228 -5
- package/dist/services/task/types.js.map +1 -1
- package/dist/services/task/voice/Voice.js +1447 -0
- package/dist/services/task/voice/Voice.js.map +1 -0
- package/dist/services/task/voice/WebRTC.js +152 -0
- package/dist/services/task/voice/WebRTC.js.map +1 -0
- package/dist/services/task/voice/wxAppVoiceMethods.js +354 -0
- package/dist/services/task/voice/wxAppVoiceMethods.js.map +1 -0
- package/dist/services/wxAppDiagnosticLogging.js +122 -0
- package/dist/services/wxAppDiagnosticLogging.js.map +1 -0
- package/dist/services/wxAppTelephonyUtils.js +19 -0
- package/dist/services/wxAppTelephonyUtils.js.map +1 -0
- package/dist/types/cc.d.ts +172 -5
- package/dist/types/config.d.ts +13 -0
- package/dist/types/constants.d.ts +25 -1
- package/dist/types/index.d.ts +24 -7
- package/dist/types/metrics/MetricsManager.d.ts +6 -0
- package/dist/types/metrics/constants.d.ts +38 -1
- package/dist/types/services/AddressBook.d.ts +2 -1
- package/dist/types/services/AnswerCallOnWebexService.d.ts +38 -0
- package/dist/types/services/ApiAiAssistant.d.ts +22 -4
- package/dist/types/services/EntryPoint.d.ts +7 -5
- package/dist/types/services/Queue.d.ts +5 -3
- package/dist/types/services/UserPreference.d.ts +118 -0
- package/dist/types/services/WebexCrossClientService.d.ts +32 -0
- package/dist/types/services/WxAppTelephonyMercurySync.d.ts +28 -0
- package/dist/types/services/config/constants.d.ts +29 -5
- package/dist/types/services/config/types.d.ts +184 -10
- package/dist/types/services/core/Err.d.ts +6 -0
- package/dist/types/services/core/Utils.d.ts +33 -13
- package/dist/types/services/core/WebexRequest.d.ts +1 -0
- package/dist/types/services/core/types.d.ts +2 -0
- package/dist/types/services/core/websocket/WebSocketManager.d.ts +1 -0
- package/dist/types/services/core/websocket/types.d.ts +1 -1
- package/dist/types/services/index.d.ts +1 -1
- package/dist/types/services/task/Task.d.ts +175 -0
- package/dist/types/services/task/TaskFactory.d.ts +13 -0
- package/dist/types/services/task/TaskUtils.d.ts +48 -2
- package/dist/types/services/task/WebexCallingUtils.d.ts +11 -0
- package/dist/types/services/task/constants.d.ts +10 -0
- package/dist/types/services/task/contact.d.ts +4 -0
- package/dist/types/services/task/dialer.d.ts +30 -0
- package/dist/types/services/task/digital/Digital.d.ts +22 -0
- package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +1356 -0
- package/dist/types/services/task/state-machine/actions.d.ts +10 -0
- package/dist/types/services/task/state-machine/constants.d.ts +107 -0
- package/dist/types/services/task/state-machine/guards.d.ts +103 -0
- package/dist/types/services/task/state-machine/index.d.ts +13 -0
- package/dist/types/services/task/state-machine/types.d.ts +277 -0
- package/dist/types/services/task/state-machine/uiControlsComputer.d.ts +9 -0
- package/dist/types/services/task/taskDataNormalizer.d.ts +10 -0
- package/dist/types/services/task/types.d.ts +680 -66
- package/dist/types/services/task/voice/Voice.d.ts +227 -0
- package/dist/types/services/task/voice/WebRTC.d.ts +53 -0
- package/dist/types/services/task/voice/wxAppVoiceMethods.d.ts +55 -0
- package/dist/types/services/wxAppDiagnosticLogging.d.ts +51 -0
- package/dist/types/services/wxAppTelephonyUtils.d.ts +5 -0
- package/dist/types/types.d.ts +149 -8
- package/dist/types/utils/PageCache.d.ts +20 -3
- package/dist/types/webex.d.ts +1 -0
- package/dist/types.js +125 -4
- package/dist/types.js.map +1 -1
- package/dist/utils/PageCache.js +19 -5
- package/dist/utils/PageCache.js.map +1 -1
- package/dist/webex.js +14 -2
- package/dist/webex.js.map +1 -1
- package/package.json +16 -12
- package/src/cc.ts +986 -42
- package/src/config.ts +13 -0
- package/src/constants.ts +25 -1
- package/src/index.ts +25 -5
- package/src/metrics/MetricsManager.ts +16 -0
- package/src/metrics/ai-docs/AGENTS.md +350 -0
- package/src/metrics/ai-docs/ARCHITECTURE.md +338 -0
- package/src/metrics/ai-docs/metrics-spec.md +922 -0
- package/src/metrics/behavioral-events.ts +212 -0
- package/src/metrics/constants.ts +47 -4
- package/src/services/AddressBook.ts +17 -6
- package/src/services/AnswerCallOnWebexService.ts +239 -0
- package/src/services/ApiAiAssistant.ts +203 -8
- package/src/services/EntryPoint.ts +59 -60
- package/src/services/Queue.ts +29 -12
- package/src/services/UserPreference.ts +509 -0
- package/src/services/WebexCrossClientService.ts +293 -0
- package/src/services/WxAppTelephonyMercurySync.ts +142 -0
- package/src/services/agent/ai-docs/AGENTS.md +240 -0
- package/src/services/agent/ai-docs/ARCHITECTURE.md +304 -0
- package/src/services/agent/ai-docs/agent-spec.md +505 -0
- package/src/services/ai-docs/AGENTS.md +386 -0
- package/src/services/ai-docs/services-spec.md +497 -0
- package/src/services/config/Util.ts +6 -3
- package/src/services/config/ai-docs/AGENTS.md +255 -0
- package/src/services/config/ai-docs/ARCHITECTURE.md +426 -0
- package/src/services/config/ai-docs/config-spec.md +675 -0
- package/src/services/config/constants.ts +35 -7
- package/src/services/config/types.ts +186 -11
- package/src/services/core/Err.ts +3 -0
- package/src/services/core/Utils.ts +123 -37
- package/src/services/core/WebexRequest.ts +3 -1
- package/src/services/core/ai-docs/AGENTS.md +381 -0
- package/src/services/core/ai-docs/ARCHITECTURE.md +698 -0
- package/src/services/core/ai-docs/core-spec.md +787 -0
- package/src/services/core/aqm-reqs.ts +30 -15
- package/src/services/core/types.ts +2 -0
- package/src/services/core/websocket/WebSocketManager.ts +2 -0
- package/src/services/core/websocket/types.ts +1 -1
- package/src/services/index.ts +1 -1
- package/src/services/task/Task.ts +908 -0
- package/src/services/task/TaskFactory.ts +60 -0
- package/src/services/task/TaskManager.ts +1239 -608
- package/src/services/task/TaskUtils.ts +303 -25
- package/src/services/task/WebexCallingUtils.ts +136 -0
- package/src/services/task/ai-docs/AGENTS.md +457 -0
- package/src/services/task/ai-docs/ARCHITECTURE.md +595 -0
- package/src/services/task/ai-docs/task-spec.md +1470 -0
- package/src/services/task/constants.ts +10 -0
- package/src/services/task/contact.ts +30 -0
- package/src/services/task/dialer.ts +80 -0
- package/src/services/task/digital/Digital.ts +97 -0
- package/src/services/task/state-machine/TaskStateMachine.ts +1293 -0
- package/src/services/task/state-machine/actions.ts +741 -0
- package/src/services/task/state-machine/ai-docs/AGENTS.md +460 -0
- package/src/services/task/state-machine/ai-docs/ARCHITECTURE.md +1145 -0
- package/src/services/task/state-machine/ai-docs/task-state-machine-spec.md +2207 -0
- package/src/services/task/state-machine/constants.ts +172 -0
- package/src/services/task/state-machine/guards.ts +442 -0
- package/src/services/task/state-machine/index.ts +28 -0
- package/src/services/task/state-machine/types.ts +258 -0
- package/src/services/task/state-machine/uiControlsComputer.ts +1135 -0
- package/src/services/task/taskDataNormalizer.ts +137 -0
- package/src/services/task/types.ts +819 -70
- package/src/services/task/voice/Voice.ts +1809 -0
- package/src/services/task/voice/WebRTC.ts +191 -0
- package/src/services/task/voice/wxAppVoiceMethods.ts +516 -0
- package/src/services/wxAppDiagnosticLogging.ts +174 -0
- package/src/services/wxAppTelephonyUtils.ts +14 -0
- package/src/types.ts +199 -11
- package/src/utils/AGENTS.md +289 -0
- package/src/utils/PageCache.ts +38 -5
- package/src/utils/ai-docs/utils-spec.md +391 -0
- package/src/webex.js +2 -0
- package/test/unit/spec/cc.ts +1947 -124
- package/test/unit/spec/logger-proxy.ts +70 -0
- package/test/unit/spec/metrics/MetricsManager.ts +25 -0
- package/test/unit/spec/metrics/behavioral-events.ts +60 -0
- package/test/unit/spec/services/AddressBook.ts +37 -6
- package/test/unit/spec/services/AnswerCallOnWebexService.ts +264 -0
- package/test/unit/spec/services/ApiAiAssistant.ts +178 -20
- package/test/unit/spec/services/EntryPoint.ts +87 -40
- package/test/unit/spec/services/Queue.ts +123 -12
- package/test/unit/spec/services/UserPreference.ts +401 -0
- package/test/unit/spec/services/WebCallingService.ts +7 -1
- package/test/unit/spec/services/WebexCrossClientService.ts +467 -0
- package/test/unit/spec/services/WxAppTelephonyMercurySync.ts +136 -0
- package/test/unit/spec/services/config/Util.ts +85 -0
- package/test/unit/spec/services/config/index.ts +30 -30
- package/test/unit/spec/services/core/Utils.ts +425 -8
- package/test/unit/spec/services/core/WebexRequest.ts +3 -1
- package/test/unit/spec/services/core/aqm-reqs.ts +113 -1
- package/test/unit/spec/services/core/websocket/WebSocketManager.ts +66 -40
- package/test/unit/spec/services/task/AutoWrapup.ts +63 -0
- package/test/unit/spec/services/task/Task.ts +677 -0
- package/test/unit/spec/services/task/TaskFactory.ts +99 -0
- package/test/unit/spec/services/task/TaskManager.ts +1972 -1549
- package/test/unit/spec/services/task/TaskUtils.ts +235 -0
- package/test/unit/spec/services/task/WebexCallingUtils.ts +153 -0
- package/test/unit/spec/services/task/contact.ts +33 -0
- package/test/unit/spec/services/task/dialer.ts +190 -0
- package/test/unit/spec/services/task/digital/Digital.ts +105 -0
- package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +3459 -0
- package/test/unit/spec/services/task/state-machine/guards.ts +839 -0
- package/test/unit/spec/services/task/state-machine/types.ts +18 -0
- package/test/unit/spec/services/task/state-machine/uiControlsComputer.ts +3101 -0
- package/test/unit/spec/services/task/taskTestUtils.ts +87 -0
- package/test/unit/spec/services/task/voice/Voice.ts +1903 -0
- package/test/unit/spec/services/task/voice/WebRTC.ts +235 -0
- package/test/unit/spec/services/task/voice/wxAppVoiceMethods.ts +603 -0
- package/test/unit/spec/services/wxAppDiagnosticLogging.ts +143 -0
- package/umd/contact-center.min.js +2 -2
- package/umd/contact-center.min.js.map +1 -1
- package/dist/services/task/index.js +0 -1525
- package/dist/services/task/index.js.map +0 -1
- package/dist/types/services/task/index.d.ts +0 -650
- package/src/services/task/index.ts +0 -1801
- package/test/unit/spec/services/task/index.ts +0 -2184
package/test/unit/spec/cc.ts
CHANGED
|
@@ -59,6 +59,7 @@ global.URL.createObjectURL = jest.fn(() => 'blob:http://localhost:3000/12345');
|
|
|
59
59
|
|
|
60
60
|
describe('webex.cc', () => {
|
|
61
61
|
let webex;
|
|
62
|
+
let mockApiAIAssistant;
|
|
62
63
|
let mockContact;
|
|
63
64
|
let mockTaskManager;
|
|
64
65
|
let mockMetricsManager;
|
|
@@ -109,6 +110,14 @@ describe('webex.cc', () => {
|
|
|
109
110
|
cancelCtq: jest.fn(),
|
|
110
111
|
};
|
|
111
112
|
|
|
113
|
+
mockApiAIAssistant = {
|
|
114
|
+
sendEvent: jest.fn(),
|
|
115
|
+
getRealTimeAssistance: jest.fn(),
|
|
116
|
+
fetchHistoricTranscripts: jest.fn(),
|
|
117
|
+
setAIFeatureFlags: jest.fn(),
|
|
118
|
+
setAgentId: jest.fn(),
|
|
119
|
+
};
|
|
120
|
+
|
|
112
121
|
// Mock Services instance
|
|
113
122
|
const mockServicesInstance = {
|
|
114
123
|
agent: {
|
|
@@ -139,6 +148,8 @@ describe('webex.cc', () => {
|
|
|
139
148
|
dialer: {
|
|
140
149
|
startOutdial: jest.fn(),
|
|
141
150
|
acceptPreviewContact: jest.fn(),
|
|
151
|
+
skipPreviewContact: jest.fn(),
|
|
152
|
+
removePreviewContact: jest.fn(),
|
|
142
153
|
},
|
|
143
154
|
apiAIAssistant: {
|
|
144
155
|
sendEvent: jest.fn(),
|
|
@@ -147,21 +158,24 @@ describe('webex.cc', () => {
|
|
|
147
158
|
};
|
|
148
159
|
|
|
149
160
|
mockTaskManager = {
|
|
161
|
+
apiAIAssistant: mockApiAIAssistant,
|
|
150
162
|
contact: mockContact,
|
|
151
163
|
call: undefined,
|
|
152
164
|
taskCollection: {},
|
|
153
165
|
webCallingService: undefined,
|
|
154
166
|
webSocketManager: mockWebSocketManager,
|
|
155
167
|
task: undefined,
|
|
168
|
+
setConfigFlags: jest.fn(),
|
|
156
169
|
setWrapupData: jest.fn(),
|
|
157
170
|
setAgentId: jest.fn(),
|
|
158
171
|
setWebRtcEnabled: jest.fn(),
|
|
159
|
-
|
|
160
|
-
setApiAIAssistant: jest.fn(),
|
|
172
|
+
setAnswerCallOnWebexService: jest.fn(),
|
|
161
173
|
registerIncomingCallEvent: jest.fn(),
|
|
162
174
|
registerTaskListeners: jest.fn(),
|
|
163
175
|
getTask: jest.fn(),
|
|
164
176
|
getActiveTasks: jest.fn(),
|
|
177
|
+
applyEnableWxBetterTogether: jest.fn(),
|
|
178
|
+
syncWxAppMuteFromCallDetailsForAllTasks: jest.fn(),
|
|
165
179
|
on: jest.fn(),
|
|
166
180
|
off: jest.fn(),
|
|
167
181
|
emit: jest.fn(),
|
|
@@ -200,6 +214,35 @@ describe('webex.cc', () => {
|
|
|
200
214
|
webex.emit('ready');
|
|
201
215
|
});
|
|
202
216
|
|
|
217
|
+
it('should throw when WebRTC registration is disabled without multi-login', () => {
|
|
218
|
+
const invalidWebex = MockWebex({
|
|
219
|
+
children: {
|
|
220
|
+
mercury: Mercury,
|
|
221
|
+
},
|
|
222
|
+
logger: {
|
|
223
|
+
log: jest.fn(),
|
|
224
|
+
error: jest.fn(),
|
|
225
|
+
info: jest.fn(),
|
|
226
|
+
},
|
|
227
|
+
credentials: {
|
|
228
|
+
getOrgId: jest.fn(() => 'mockOrgId'),
|
|
229
|
+
},
|
|
230
|
+
config: {
|
|
231
|
+
...config,
|
|
232
|
+
cc: {
|
|
233
|
+
...config.cc,
|
|
234
|
+
allowMultiLogin: false,
|
|
235
|
+
disableWebRTCRegistration: true,
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
once: jest.fn((event, callback) => callback()),
|
|
239
|
+
}) as unknown as WebexSDK;
|
|
240
|
+
|
|
241
|
+
expect(() => new ContactCenter({parent: invalidWebex})).toThrow(
|
|
242
|
+
'Invalid Contact Center configuration: disableWebRTCRegistration cannot be true when allowMultiLogin is false. Enable allowMultiLogin or allow WebRTC registration so an SDK instance can receive Mobius/WebRTC task events.'
|
|
243
|
+
);
|
|
244
|
+
});
|
|
245
|
+
|
|
203
246
|
describe('cc.getDeviceId', () => {
|
|
204
247
|
it('should return dialNumber when loginOption is EXTENSION', () => {
|
|
205
248
|
const loginOption = LoginOption.EXTENSION;
|
|
@@ -267,10 +310,13 @@ describe('webex.cc', () => {
|
|
|
267
310
|
isAgentAvailableAfterOutdial: false,
|
|
268
311
|
isCampaignManagementEnabled: false,
|
|
269
312
|
outDialEp: '',
|
|
270
|
-
|
|
313
|
+
isEndTaskEnabled: false,
|
|
271
314
|
isEndConsultEnabled: false,
|
|
272
315
|
agentDbId: '',
|
|
273
316
|
allowConsultToQueue: false,
|
|
317
|
+
accessQueue: 'SPECIFIC',
|
|
318
|
+
accessEntryPoint: 'ALL',
|
|
319
|
+
accessBuddyTeam: 'ALL',
|
|
274
320
|
agentPersonalStatsEnabled: false,
|
|
275
321
|
isTimeoutDesktopInactivityEnabled: false,
|
|
276
322
|
webRtcEnabled: true,
|
|
@@ -300,7 +346,7 @@ describe('webex.cc', () => {
|
|
|
300
346
|
const result = await webex.cc.register();
|
|
301
347
|
|
|
302
348
|
// Verify logging calls
|
|
303
|
-
expect(LoggerProxy.
|
|
349
|
+
expect(LoggerProxy.log).toHaveBeenCalledWith('Starting CC SDK registration', {
|
|
304
350
|
module: CC_FILE,
|
|
305
351
|
method: 'register',
|
|
306
352
|
});
|
|
@@ -334,6 +380,10 @@ describe('webex.cc', () => {
|
|
|
334
380
|
TASK_EVENTS.TASK_HYDRATE,
|
|
335
381
|
expect.any(Function)
|
|
336
382
|
);
|
|
383
|
+
expect(mockTaskManager.on).toHaveBeenCalledWith(
|
|
384
|
+
TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE,
|
|
385
|
+
expect.any(Function)
|
|
386
|
+
);
|
|
337
387
|
expect(mockWebSocketManager.on).toHaveBeenCalledWith('message', expect.any(Function));
|
|
338
388
|
expect(webex.cc.services.rtdWebSocketManager.initWebSocket).toHaveBeenCalledWith({
|
|
339
389
|
body: {
|
|
@@ -354,6 +404,20 @@ describe('webex.cc', () => {
|
|
|
354
404
|
module: CC_FILE,
|
|
355
405
|
method: 'connectWebsocket',
|
|
356
406
|
});
|
|
407
|
+
expect(mockTaskManager.setConfigFlags).toHaveBeenCalledWith({
|
|
408
|
+
isEndTaskEnabled: mockAgentProfile.isEndTaskEnabled,
|
|
409
|
+
isEndConsultEnabled: mockAgentProfile.isEndConsultEnabled,
|
|
410
|
+
webRtcEnabled: mockAgentProfile.webRtcEnabled,
|
|
411
|
+
autoWrapup: mockAgentProfile.wrapUpData.wrapUpProps.autoWrapup ?? false,
|
|
412
|
+
aiFeature: mockAgentProfile.aiFeature,
|
|
413
|
+
consultTransfer: {
|
|
414
|
+
allowConsultToQueue: mockAgentProfile.allowConsultToQueue,
|
|
415
|
+
accessQueue: mockAgentProfile.accessQueue,
|
|
416
|
+
accessEntryPoint: mockAgentProfile.accessEntryPoint,
|
|
417
|
+
accessBuddyTeam: mockAgentProfile.accessBuddyTeam,
|
|
418
|
+
},
|
|
419
|
+
enableWxBetterTogether: false,
|
|
420
|
+
});
|
|
357
421
|
expect(reloadSpy).toHaveBeenCalled();
|
|
358
422
|
expect(result).toEqual(mockAgentProfile);
|
|
359
423
|
expect(mockMetricsManager.timeEvent).toHaveBeenCalledWith([
|
|
@@ -413,7 +477,7 @@ describe('webex.cc', () => {
|
|
|
413
477
|
|
|
414
478
|
await expect(webex.cc.register()).rejects.toThrow('Error while performing register');
|
|
415
479
|
|
|
416
|
-
expect(LoggerProxy.
|
|
480
|
+
expect(LoggerProxy.log).toHaveBeenCalledWith('Starting CC SDK registration', {
|
|
417
481
|
module: CC_FILE,
|
|
418
482
|
method: 'register',
|
|
419
483
|
});
|
|
@@ -482,6 +546,10 @@ describe('webex.cc', () => {
|
|
|
482
546
|
TASK_EVENTS.TASK_HYDRATE,
|
|
483
547
|
expect.any(Function)
|
|
484
548
|
);
|
|
549
|
+
expect(mockTaskManager.on).toHaveBeenCalledWith(
|
|
550
|
+
TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE,
|
|
551
|
+
expect.any(Function)
|
|
552
|
+
);
|
|
485
553
|
expect(mockWebSocketManager.on).toHaveBeenCalledWith('message', expect.any(Function));
|
|
486
554
|
|
|
487
555
|
expect(configSpy).toHaveBeenCalled();
|
|
@@ -493,6 +561,36 @@ describe('webex.cc', () => {
|
|
|
493
561
|
expect(result).toEqual(mockAgentProfile);
|
|
494
562
|
});
|
|
495
563
|
|
|
564
|
+
it('should skip mercury connection when disableWebRTCRegistration is enabled', async () => {
|
|
565
|
+
webex.cc.$config = {
|
|
566
|
+
...webex.cc.$config,
|
|
567
|
+
allowAutomatedRelogin: false,
|
|
568
|
+
disableWebRTCRegistration: true,
|
|
569
|
+
};
|
|
570
|
+
mockAgentProfile.webRtcEnabled = true;
|
|
571
|
+
const mercurySpy = jest.spyOn(webex.internal.mercury, 'connect');
|
|
572
|
+
const connectWebsocketSpy = jest.spyOn(webex.cc, 'connectWebsocket');
|
|
573
|
+
const setupEventListenersSpy = jest.spyOn(webex.cc, 'setupEventListeners');
|
|
574
|
+
jest.spyOn(webex.cc.services.config, 'getAgentConfig').mockResolvedValue(mockAgentProfile);
|
|
575
|
+
mockWebSocketManager.initWebSocket.mockResolvedValue({
|
|
576
|
+
agentId: 'agent123',
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
const result = await webex.cc.register();
|
|
580
|
+
|
|
581
|
+
expect(connectWebsocketSpy).toHaveBeenCalled();
|
|
582
|
+
expect(setupEventListenersSpy).toHaveBeenCalled();
|
|
583
|
+
expect(mercurySpy).not.toHaveBeenCalled();
|
|
584
|
+
expect(LoggerProxy.info).toHaveBeenCalledWith(
|
|
585
|
+
'Skipping Mobius registration because disableWebRTCRegistration is enabled',
|
|
586
|
+
{
|
|
587
|
+
module: CC_FILE,
|
|
588
|
+
method: 'connectWebsocket',
|
|
589
|
+
}
|
|
590
|
+
);
|
|
591
|
+
expect(result).toEqual(mockAgentProfile);
|
|
592
|
+
});
|
|
593
|
+
|
|
496
594
|
it('should not attempt for mercury connection when webrtc is disabled', async () => {
|
|
497
595
|
mockAgentProfile.webRtcEnabled = false;
|
|
498
596
|
mockAgentProfile.aiFeature = {realtimeTranscripts: {enable: false}} as any;
|
|
@@ -631,7 +729,7 @@ describe('webex.cc', () => {
|
|
|
631
729
|
expect(registerWebCallingLineSpy).toHaveBeenCalled();
|
|
632
730
|
expect(stationLoginMock).toHaveBeenCalledWith({
|
|
633
731
|
data: {
|
|
634
|
-
dialNumber:
|
|
732
|
+
dialNumber: `${WEB_RTC_PREFIX}agentId`,
|
|
635
733
|
teamId: 'teamId',
|
|
636
734
|
deviceType: LoginOption.BROWSER,
|
|
637
735
|
isExtension: false,
|
|
@@ -649,6 +747,7 @@ describe('webex.cc', () => {
|
|
|
649
747
|
METRIC_EVENT_NAMES.STATION_LOGIN_FAILED,
|
|
650
748
|
]);
|
|
651
749
|
expect(result).toEqual(responseMock);
|
|
750
|
+
expect(webex.cc.agentConfig.deviceType).toBe(LoginOption.BROWSER);
|
|
652
751
|
|
|
653
752
|
const onSpy = jest.spyOn(mockTaskManager, 'on');
|
|
654
753
|
const emitSpy = jest.spyOn(webex.cc, 'trigger');
|
|
@@ -661,10 +760,8 @@ describe('webex.cc', () => {
|
|
|
661
760
|
|
|
662
761
|
expect(emitSpy).toHaveBeenCalledTimes(1);
|
|
663
762
|
expect(emitSpy).toHaveBeenCalledWith(TASK_EVENTS.TASK_INCOMING, mockTask);
|
|
664
|
-
// Verify message
|
|
665
|
-
const messageCallback =
|
|
666
|
-
(call) => call[0] === 'message'
|
|
667
|
-
)[1];
|
|
763
|
+
// Verify websocket message handling
|
|
764
|
+
const messageCallback = webex.cc['handleWebsocketMessage'];
|
|
668
765
|
const agentStateChangeEventData = {
|
|
669
766
|
type: CC_EVENTS.AGENT_STATE_CHANGE,
|
|
670
767
|
data: {some: 'data'},
|
|
@@ -692,6 +789,59 @@ describe('webex.cc', () => {
|
|
|
692
789
|
);
|
|
693
790
|
});
|
|
694
791
|
|
|
792
|
+
it('should skip web calling line registration when disableWebRTCRegistration is enabled', async () => {
|
|
793
|
+
const options = {
|
|
794
|
+
teamId: 'teamId',
|
|
795
|
+
loginOption: LoginOption.BROWSER,
|
|
796
|
+
};
|
|
797
|
+
|
|
798
|
+
webex.cc.$config = {
|
|
799
|
+
...webex.cc.$config,
|
|
800
|
+
disableWebRTCRegistration: true,
|
|
801
|
+
};
|
|
802
|
+
webex.cc.agentConfig = {
|
|
803
|
+
agentId: 'agentId',
|
|
804
|
+
webRtcEnabled: true,
|
|
805
|
+
loginVoiceOptions: ['BROWSER', 'EXTENSION', 'AGENT_DN'],
|
|
806
|
+
};
|
|
807
|
+
|
|
808
|
+
const registerWebCallingLineSpy = jest.spyOn(
|
|
809
|
+
webex.cc.webCallingService,
|
|
810
|
+
'registerWebCallingLine'
|
|
811
|
+
);
|
|
812
|
+
|
|
813
|
+
jest.spyOn(webex.cc.services.agent, 'stationLogin').mockResolvedValue({
|
|
814
|
+
data: {
|
|
815
|
+
loginOption: LoginOption.BROWSER,
|
|
816
|
+
agentId: 'agentId',
|
|
817
|
+
teamId: 'teamId',
|
|
818
|
+
siteId: 'siteId',
|
|
819
|
+
roles: [AGENT],
|
|
820
|
+
channelsMap: {
|
|
821
|
+
chat: [],
|
|
822
|
+
email: [],
|
|
823
|
+
social: [],
|
|
824
|
+
telephony: [],
|
|
825
|
+
},
|
|
826
|
+
},
|
|
827
|
+
trackingId: 'notifs_52628',
|
|
828
|
+
orgId: 'orgId',
|
|
829
|
+
type: 'StationLoginSuccess',
|
|
830
|
+
eventType: 'STATION_LOGIN',
|
|
831
|
+
} as unknown as StationLoginSuccess);
|
|
832
|
+
|
|
833
|
+
await webex.cc.stationLogin(options);
|
|
834
|
+
|
|
835
|
+
expect(registerWebCallingLineSpy).not.toHaveBeenCalled();
|
|
836
|
+
expect(LoggerProxy.info).toHaveBeenCalledWith(
|
|
837
|
+
'Skipping web calling line registration because disableWebRTCRegistration is enabled',
|
|
838
|
+
{
|
|
839
|
+
module: CC_FILE,
|
|
840
|
+
method: 'stationLogin',
|
|
841
|
+
}
|
|
842
|
+
);
|
|
843
|
+
});
|
|
844
|
+
|
|
695
845
|
it('should not attempt mobius registration for LoginOption.BROWSER if webrtc is disabled', async () => {
|
|
696
846
|
const options = {
|
|
697
847
|
teamId: 'teamId',
|
|
@@ -739,7 +889,7 @@ describe('webex.cc', () => {
|
|
|
739
889
|
expect(registerWebCallingLineSpy).not.toHaveBeenCalled();
|
|
740
890
|
expect(stationLoginSpy).toHaveBeenCalledWith({
|
|
741
891
|
data: {
|
|
742
|
-
dialNumber:
|
|
892
|
+
dialNumber: `${WEB_RTC_PREFIX}agentId`,
|
|
743
893
|
teamId: 'teamId',
|
|
744
894
|
deviceType: LoginOption.BROWSER,
|
|
745
895
|
isExtension: false,
|
|
@@ -814,10 +964,13 @@ describe('webex.cc', () => {
|
|
|
814
964
|
const result = await webex.cc.stationLogin(options);
|
|
815
965
|
|
|
816
966
|
// Verify logging calls
|
|
817
|
-
expect(LoggerProxy.
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
967
|
+
expect(LoggerProxy.log).toHaveBeenCalledWith(
|
|
968
|
+
`Starting agent station login | loginOption: ${options.loginOption} teamId: ${options.teamId}`,
|
|
969
|
+
{
|
|
970
|
+
module: CC_FILE,
|
|
971
|
+
method: 'stationLogin',
|
|
972
|
+
}
|
|
973
|
+
);
|
|
821
974
|
expect(LoggerProxy.log).toHaveBeenCalledWith(
|
|
822
975
|
`Agent station login completed successfully agentId: ${mockData.data.agentId} loginOption: ${mockData.data.loginOption} teamId: ${mockData.data.teamId}`,
|
|
823
976
|
{
|
|
@@ -868,132 +1021,978 @@ describe('webex.cc', () => {
|
|
|
868
1021
|
|
|
869
1022
|
await expect(webex.cc.stationLogin(options)).rejects.toThrow(error.details.data.reason);
|
|
870
1023
|
|
|
871
|
-
expect(LoggerProxy.
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
1024
|
+
expect(LoggerProxy.log).toHaveBeenCalledWith(
|
|
1025
|
+
`Starting agent station login | loginOption: ${options.loginOption} teamId: ${options.teamId}`,
|
|
1026
|
+
{
|
|
1027
|
+
module: CC_FILE,
|
|
1028
|
+
method: 'stationLogin',
|
|
1029
|
+
}
|
|
1030
|
+
);
|
|
875
1031
|
expect(LoggerProxy.error).toHaveBeenCalledWith(
|
|
876
1032
|
`stationLogin failed with reason: ${error.details.data.reason}`,
|
|
877
1033
|
{module: CC_FILE, method: 'stationLogin', trackingId: error.details.trackingId}
|
|
878
1034
|
);
|
|
879
1035
|
});
|
|
880
|
-
});
|
|
881
1036
|
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
1037
|
+
it('should publish usersub and subscribe Mercury on station login when enableWxBetterTogether is true at init', async () => {
|
|
1038
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: true};
|
|
1039
|
+
webex.internal.device = {
|
|
1040
|
+
userId: 'user-123',
|
|
1041
|
+
url: 'https://wdm.example.com/devices/dev-1',
|
|
1042
|
+
registered: true,
|
|
1043
|
+
register: jest.fn().mockResolvedValue(undefined),
|
|
1044
|
+
};
|
|
1045
|
+
webex.internal.mercury.connected = true;
|
|
1046
|
+
webex.internal.mercury.connect = jest.fn().mockResolvedValue(undefined);
|
|
1047
|
+
webex.cc.agentConfig = {
|
|
1048
|
+
agentId: 'agentId',
|
|
1049
|
+
webRtcEnabled: false,
|
|
1050
|
+
loginVoiceOptions: ['EXTENSION'],
|
|
1051
|
+
};
|
|
1052
|
+
webex.cc.webCallingService.loginOption = LoginOption.EXTENSION;
|
|
886
1053
|
|
|
887
|
-
const
|
|
888
|
-
.spyOn(webex.cc
|
|
889
|
-
.mockResolvedValue(
|
|
1054
|
+
const publishSpy = jest
|
|
1055
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
1056
|
+
.mockResolvedValue(undefined);
|
|
1057
|
+
const subscribeSpy = jest
|
|
1058
|
+
.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'subscribe')
|
|
1059
|
+
.mockImplementation(() => {});
|
|
1060
|
+
jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'isSubscribed').mockReturnValue(true);
|
|
890
1061
|
|
|
891
|
-
|
|
1062
|
+
jest.spyOn(webex.cc.services.agent, 'stationLogin').mockResolvedValue({
|
|
1063
|
+
data: {
|
|
1064
|
+
agentId: 'agentId',
|
|
1065
|
+
teamId: 'teamId',
|
|
1066
|
+
channelsMap: {chat: [], email: [], social: [], telephony: []},
|
|
1067
|
+
},
|
|
1068
|
+
trackingId: 'track-1',
|
|
1069
|
+
} as StationLoginSuccess);
|
|
892
1070
|
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
});
|
|
898
|
-
expect(LoggerProxy.log).toHaveBeenCalledWith('Agent station logout completed successfully', {
|
|
899
|
-
module: CC_FILE,
|
|
900
|
-
method: 'stationLogout',
|
|
1071
|
+
await webex.cc.stationLogin({
|
|
1072
|
+
teamId: 'teamId',
|
|
1073
|
+
loginOption: LoginOption.EXTENSION,
|
|
1074
|
+
dialNumber: '1001',
|
|
901
1075
|
});
|
|
902
1076
|
|
|
903
|
-
expect(
|
|
904
|
-
|
|
905
|
-
// expect(mockTaskManager.unregisterIncomingCallEvent).toHaveBeenCalledWith();
|
|
906
|
-
// expect(mockTaskManager.off).toHaveBeenCalledWith(
|
|
907
|
-
// TASK_EVENTS.TASK_INCOMING,
|
|
908
|
-
// expect.any(Function)
|
|
909
|
-
// );
|
|
910
|
-
// expect(mockTaskManager.off).toHaveBeenCalledWith(
|
|
911
|
-
// TASK_EVENTS.TASK_HYDRATE,
|
|
912
|
-
// expect.any(Function)
|
|
913
|
-
// );
|
|
914
|
-
// expect(mockWebSocketManager.off).toHaveBeenCalledWith('message', expect.any(Function));
|
|
915
|
-
expect(result).toEqual(response);
|
|
916
|
-
expect(mockMetricsManager.timeEvent).toHaveBeenCalledWith([
|
|
917
|
-
METRIC_EVENT_NAMES.STATION_LOGOUT_SUCCESS,
|
|
918
|
-
METRIC_EVENT_NAMES.STATION_LOGOUT_FAILED,
|
|
919
|
-
]);
|
|
1077
|
+
expect(publishSpy).toHaveBeenCalledWith(true, {userId: 'user-123', trackPublishMetrics: true});
|
|
1078
|
+
expect(subscribeSpy).toHaveBeenCalledWith('agentId', expect.any(Function));
|
|
920
1079
|
});
|
|
921
1080
|
|
|
922
|
-
it('should
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
},
|
|
930
|
-
},
|
|
1081
|
+
it('should rollback wxApp config when post-station-login init fails', async () => {
|
|
1082
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: true};
|
|
1083
|
+
webex.internal.device = {userId: 'user-123', url: 'https://wdm.example.com/devices/dev-1'};
|
|
1084
|
+
webex.cc.agentConfig = {
|
|
1085
|
+
agentId: 'agentId',
|
|
1086
|
+
webRtcEnabled: false,
|
|
1087
|
+
loginVoiceOptions: ['EXTENSION'],
|
|
931
1088
|
};
|
|
1089
|
+
webex.cc.webCallingService.loginOption = LoginOption.EXTENSION;
|
|
1090
|
+
mockTaskManager.applyEnableWxBetterTogether(true);
|
|
932
1091
|
|
|
933
|
-
jest
|
|
1092
|
+
jest
|
|
1093
|
+
.spyOn(webex.cc as never, 'publishAnswerOnWebexCrossClientState' as never)
|
|
1094
|
+
.mockRejectedValue(new Error('usersub publish failed'));
|
|
1095
|
+
const unsubscribeSpy = jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'unsubscribe');
|
|
934
1096
|
|
|
935
|
-
|
|
1097
|
+
jest.spyOn(webex.cc.services.agent, 'stationLogin').mockResolvedValue({
|
|
1098
|
+
data: {
|
|
1099
|
+
agentId: 'agentId',
|
|
1100
|
+
teamId: 'teamId',
|
|
1101
|
+
channelsMap: {chat: [], email: [], social: [], telephony: []},
|
|
1102
|
+
},
|
|
1103
|
+
trackingId: 'track-1',
|
|
1104
|
+
} as StationLoginSuccess);
|
|
936
1105
|
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
1106
|
+
await webex.cc.stationLogin({
|
|
1107
|
+
teamId: 'teamId',
|
|
1108
|
+
loginOption: LoginOption.EXTENSION,
|
|
1109
|
+
dialNumber: '1001',
|
|
940
1110
|
});
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
);
|
|
1111
|
+
|
|
1112
|
+
expect(unsubscribeSpy).toHaveBeenCalled();
|
|
1113
|
+
expect(webex.cc.isWxBetterTogetherEnabled()).toBe(false);
|
|
1114
|
+
expect(mockTaskManager.applyEnableWxBetterTogether).toHaveBeenCalledWith(false);
|
|
945
1115
|
});
|
|
946
|
-
});
|
|
947
1116
|
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
1117
|
+
it('should reset wxApp config when Mercury fails before usersub publish on station login', async () => {
|
|
1118
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: true};
|
|
1119
|
+
webex.internal.device = {userId: 'user-123', url: 'https://wdm.example.com/devices/dev-1'};
|
|
1120
|
+
webex.cc.agentConfig = {
|
|
1121
|
+
agentId: 'agentId',
|
|
1122
|
+
webRtcEnabled: false,
|
|
1123
|
+
loginVoiceOptions: ['EXTENSION'],
|
|
955
1124
|
};
|
|
1125
|
+
webex.cc.webCallingService.loginOption = LoginOption.EXTENSION;
|
|
1126
|
+
mockTaskManager.applyEnableWxBetterTogether(true);
|
|
1127
|
+
|
|
1128
|
+
const publishSpy = jest
|
|
1129
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
1130
|
+
.mockResolvedValue(undefined);
|
|
1131
|
+
jest
|
|
1132
|
+
.spyOn(webex.cc as any, 'ensureWxAppMercuryConnected')
|
|
1133
|
+
.mockRejectedValue(new Error('mercury connect failed'));
|
|
1134
|
+
const teardownSpy = jest.spyOn(webex.cc['webexCrossClientService'], 'teardown');
|
|
1135
|
+
const unsubscribeSpy = jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'unsubscribe');
|
|
1136
|
+
jest
|
|
1137
|
+
.spyOn(webex.cc as any, 'releaseWxAppMercuryResources')
|
|
1138
|
+
.mockResolvedValue(undefined);
|
|
1139
|
+
const metricSpy = jest.spyOn(mockMetricsManager, 'trackEvent');
|
|
956
1140
|
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
1141
|
+
jest.spyOn(webex.cc.services.agent, 'stationLogin').mockResolvedValue({
|
|
1142
|
+
data: {
|
|
1143
|
+
agentId: 'agentId',
|
|
1144
|
+
teamId: 'teamId',
|
|
1145
|
+
channelsMap: {chat: [], email: [], social: [], telephony: []},
|
|
1146
|
+
},
|
|
1147
|
+
trackingId: 'track-1',
|
|
1148
|
+
} as StationLoginSuccess);
|
|
962
1149
|
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
1150
|
+
await webex.cc.stationLogin({
|
|
1151
|
+
teamId: 'teamId',
|
|
1152
|
+
loginOption: LoginOption.EXTENSION,
|
|
1153
|
+
dialNumber: '1001',
|
|
967
1154
|
});
|
|
968
|
-
expect(LoggerProxy.log).toHaveBeenCalledWith(
|
|
969
|
-
`Agent state changed successfully to auxCodeId: ${expectedPayload.auxCodeId}`,
|
|
970
|
-
{
|
|
971
|
-
module: CC_FILE,
|
|
972
|
-
method: 'setAgentState',
|
|
973
|
-
}
|
|
974
|
-
);
|
|
975
1155
|
|
|
976
|
-
expect(
|
|
977
|
-
expect(
|
|
978
|
-
expect(
|
|
979
|
-
|
|
980
|
-
METRIC_EVENT_NAMES.
|
|
981
|
-
|
|
1156
|
+
expect(publishSpy).not.toHaveBeenCalled();
|
|
1157
|
+
expect(teardownSpy).not.toHaveBeenCalled();
|
|
1158
|
+
expect(unsubscribeSpy).toHaveBeenCalled();
|
|
1159
|
+
expect(metricSpy).toHaveBeenCalledWith(
|
|
1160
|
+
METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_FAILED,
|
|
1161
|
+
expect.objectContaining({skipReason: 'mercury_subscribe_failed'}),
|
|
1162
|
+
['operational', 'behavioral']
|
|
1163
|
+
);
|
|
1164
|
+
expect(webex.cc.isWxBetterTogetherEnabled()).toBe(false);
|
|
1165
|
+
expect(mockTaskManager.applyEnableWxBetterTogether).toHaveBeenCalledWith(false);
|
|
982
1166
|
});
|
|
983
1167
|
|
|
984
|
-
it('should
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
agentId: '
|
|
989
|
-
|
|
1168
|
+
it('should record publish_failed when usersub publish fails after Mercury init', async () => {
|
|
1169
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: true};
|
|
1170
|
+
webex.internal.device = {userId: 'user-123', url: 'https://wdm.example.com/devices/dev-1'};
|
|
1171
|
+
webex.cc.agentConfig = {
|
|
1172
|
+
agentId: 'agentId',
|
|
1173
|
+
webRtcEnabled: false,
|
|
1174
|
+
loginVoiceOptions: ['EXTENSION'],
|
|
990
1175
|
};
|
|
1176
|
+
webex.cc.webCallingService.loginOption = LoginOption.EXTENSION;
|
|
1177
|
+
mockTaskManager.applyEnableWxBetterTogether(true);
|
|
1178
|
+
|
|
1179
|
+
jest.spyOn(webex.cc as any, 'ensureWxAppMercuryConnected').mockResolvedValue(undefined);
|
|
1180
|
+
jest
|
|
1181
|
+
.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'subscribe')
|
|
1182
|
+
.mockImplementation(() => {});
|
|
1183
|
+
jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'isSubscribed').mockReturnValue(true);
|
|
1184
|
+
jest
|
|
1185
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
1186
|
+
.mockRejectedValue(new Error('publish failed'));
|
|
1187
|
+
jest
|
|
1188
|
+
.spyOn(webex.cc as any, 'releaseWxAppMercuryResources')
|
|
1189
|
+
.mockResolvedValue(undefined);
|
|
1190
|
+
const metricSpy = jest.spyOn(mockMetricsManager, 'trackEvent');
|
|
991
1191
|
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
1192
|
+
jest.spyOn(webex.cc.services.agent, 'stationLogin').mockResolvedValue({
|
|
1193
|
+
data: {
|
|
1194
|
+
agentId: 'agentId',
|
|
1195
|
+
teamId: 'teamId',
|
|
1196
|
+
channelsMap: {chat: [], email: [], social: [], telephony: []},
|
|
1197
|
+
},
|
|
1198
|
+
trackingId: 'track-1',
|
|
1199
|
+
} as StationLoginSuccess);
|
|
1200
|
+
|
|
1201
|
+
await webex.cc.stationLogin({
|
|
1202
|
+
teamId: 'teamId',
|
|
1203
|
+
loginOption: LoginOption.EXTENSION,
|
|
1204
|
+
dialNumber: '1001',
|
|
1205
|
+
});
|
|
1206
|
+
|
|
1207
|
+
expect(metricSpy).toHaveBeenCalledWith(
|
|
1208
|
+
METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_FAILED,
|
|
1209
|
+
expect.objectContaining({skipReason: 'publish_failed'}),
|
|
1210
|
+
['operational', 'behavioral']
|
|
1211
|
+
);
|
|
1212
|
+
expect(webex.cc.isWxBetterTogetherEnabled()).toBe(false);
|
|
1213
|
+
});
|
|
1214
|
+
|
|
1215
|
+
it('should register device before publishing usersub true on extension station login', async () => {
|
|
1216
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: true};
|
|
1217
|
+
webex.internal.device = {
|
|
1218
|
+
userId: 'user-123',
|
|
1219
|
+
registered: false,
|
|
1220
|
+
register: jest.fn().mockImplementation(async function (this: {
|
|
1221
|
+
url?: string;
|
|
1222
|
+
registered?: boolean;
|
|
1223
|
+
}) {
|
|
1224
|
+
this.url = 'https://wdm.example.com/devices/dev-1';
|
|
1225
|
+
this.registered = true;
|
|
1226
|
+
}),
|
|
1227
|
+
};
|
|
1228
|
+
webex.internal.mercury = {
|
|
1229
|
+
connected: false,
|
|
1230
|
+
connect: jest.fn().mockResolvedValue(undefined),
|
|
1231
|
+
};
|
|
1232
|
+
webex.cc.agentConfig = {
|
|
1233
|
+
agentId: 'agentId',
|
|
1234
|
+
webRtcEnabled: false,
|
|
1235
|
+
loginVoiceOptions: ['EXTENSION'],
|
|
1236
|
+
};
|
|
1237
|
+
webex.cc.webCallingService.loginOption = LoginOption.EXTENSION;
|
|
1238
|
+
jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'subscribe').mockImplementation(() => {});
|
|
1239
|
+
jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'isSubscribed').mockReturnValue(true);
|
|
1240
|
+
|
|
1241
|
+
const callOrder: string[] = [];
|
|
1242
|
+
jest.spyOn(webex.internal.device, 'register').mockImplementation(async function (this: {
|
|
1243
|
+
url?: string;
|
|
1244
|
+
registered?: boolean;
|
|
1245
|
+
}) {
|
|
1246
|
+
callOrder.push('device.register');
|
|
1247
|
+
this.url = 'https://wdm.example.com/devices/dev-1';
|
|
1248
|
+
this.registered = true;
|
|
1249
|
+
});
|
|
1250
|
+
jest
|
|
1251
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
1252
|
+
.mockImplementation(async (enable: boolean) => {
|
|
1253
|
+
if (enable === true && !webex.internal.device.url) {
|
|
1254
|
+
throw new Error('Device URL is unavailable for cross-client publish');
|
|
1255
|
+
}
|
|
1256
|
+
callOrder.push(enable ? 'publish:true' : 'publish:false');
|
|
1257
|
+
});
|
|
1258
|
+
|
|
1259
|
+
jest.spyOn(webex.cc.services.agent, 'stationLogin').mockResolvedValue({
|
|
1260
|
+
data: {
|
|
1261
|
+
agentId: 'agentId',
|
|
1262
|
+
teamId: 'teamId',
|
|
1263
|
+
channelsMap: {chat: [], email: [], social: [], telephony: []},
|
|
1264
|
+
},
|
|
1265
|
+
trackingId: 'track-1',
|
|
1266
|
+
} as StationLoginSuccess);
|
|
1267
|
+
|
|
1268
|
+
await webex.cc.stationLogin({
|
|
1269
|
+
teamId: 'teamId',
|
|
1270
|
+
loginOption: LoginOption.EXTENSION,
|
|
1271
|
+
dialNumber: '1001',
|
|
1272
|
+
});
|
|
1273
|
+
|
|
1274
|
+
expect(callOrder).toEqual(['device.register', 'publish:true']);
|
|
1275
|
+
});
|
|
1276
|
+
|
|
1277
|
+
it('should register device before publishing forced usersub false when wxApp is disabled at station login', async () => {
|
|
1278
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: false};
|
|
1279
|
+
webex.internal.device = {
|
|
1280
|
+
userId: 'user-123',
|
|
1281
|
+
registered: false,
|
|
1282
|
+
register: jest.fn().mockImplementation(async function (this: {
|
|
1283
|
+
url?: string;
|
|
1284
|
+
registered?: boolean;
|
|
1285
|
+
}) {
|
|
1286
|
+
this.url = 'https://wdm.example.com/devices/dev-1';
|
|
1287
|
+
this.registered = true;
|
|
1288
|
+
}),
|
|
1289
|
+
};
|
|
1290
|
+
webex.internal.mercury = {
|
|
1291
|
+
connected: false,
|
|
1292
|
+
connect: jest.fn().mockResolvedValue(undefined),
|
|
1293
|
+
};
|
|
1294
|
+
webex.cc.agentConfig = {
|
|
1295
|
+
agentId: 'agentId',
|
|
1296
|
+
webRtcEnabled: false,
|
|
1297
|
+
loginVoiceOptions: ['EXTENSION'],
|
|
1298
|
+
};
|
|
1299
|
+
webex.cc.webCallingService.loginOption = LoginOption.EXTENSION;
|
|
1300
|
+
jest
|
|
1301
|
+
.spyOn(webex.cc as any, 'releaseWxAppMercuryResources')
|
|
1302
|
+
.mockResolvedValue(undefined);
|
|
1303
|
+
|
|
1304
|
+
const callOrder: string[] = [];
|
|
1305
|
+
jest.spyOn(webex.internal.device, 'register').mockImplementation(async function (this: {
|
|
1306
|
+
url?: string;
|
|
1307
|
+
registered?: boolean;
|
|
1308
|
+
}) {
|
|
1309
|
+
callOrder.push('device.register');
|
|
1310
|
+
this.url = 'https://wdm.example.com/devices/dev-1';
|
|
1311
|
+
this.registered = true;
|
|
1312
|
+
});
|
|
1313
|
+
jest
|
|
1314
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
1315
|
+
.mockImplementation(async (enable: boolean) => {
|
|
1316
|
+
if (!webex.internal.device.url) {
|
|
1317
|
+
throw new Error('Device URL is unavailable for cross-client publish');
|
|
1318
|
+
}
|
|
1319
|
+
callOrder.push(enable ? 'publish:true' : 'publish:false');
|
|
1320
|
+
});
|
|
1321
|
+
|
|
1322
|
+
jest.spyOn(webex.cc.services.agent, 'stationLogin').mockResolvedValue({
|
|
1323
|
+
data: {
|
|
1324
|
+
agentId: 'agentId',
|
|
1325
|
+
teamId: 'teamId',
|
|
1326
|
+
channelsMap: {chat: [], email: [], social: [], telephony: []},
|
|
1327
|
+
},
|
|
1328
|
+
trackingId: 'track-1',
|
|
1329
|
+
} as StationLoginSuccess);
|
|
1330
|
+
|
|
1331
|
+
await webex.cc.stationLogin({
|
|
1332
|
+
teamId: 'teamId',
|
|
1333
|
+
loginOption: LoginOption.EXTENSION,
|
|
1334
|
+
dialNumber: '1001',
|
|
1335
|
+
});
|
|
1336
|
+
|
|
1337
|
+
expect(callOrder).toEqual(['device.register', 'publish:false']);
|
|
1338
|
+
});
|
|
1339
|
+
|
|
1340
|
+
it('should retry forced false publish when wxApp is disabled at station login', async () => {
|
|
1341
|
+
jest.useFakeTimers();
|
|
1342
|
+
try {
|
|
1343
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: false};
|
|
1344
|
+
webex.internal.device = {userId: 'user-123', url: 'https://wdm.example.com/devices/dev-1'};
|
|
1345
|
+
webex.cc.agentConfig = {
|
|
1346
|
+
agentId: 'agentId',
|
|
1347
|
+
webRtcEnabled: false,
|
|
1348
|
+
loginVoiceOptions: ['EXTENSION'],
|
|
1349
|
+
};
|
|
1350
|
+
webex.cc.webCallingService.loginOption = LoginOption.EXTENSION;
|
|
1351
|
+
|
|
1352
|
+
const publishSpy = jest
|
|
1353
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
1354
|
+
.mockRejectedValueOnce(new Error('usersub publish failed'))
|
|
1355
|
+
.mockResolvedValue(undefined);
|
|
1356
|
+
const scheduleRetrySpy = jest.spyOn(
|
|
1357
|
+
webex.cc as any,
|
|
1358
|
+
'scheduleForcedFalsePublishRetry'
|
|
1359
|
+
);
|
|
1360
|
+
const teardownSpy = jest.spyOn(webex.cc['webexCrossClientService'], 'teardown');
|
|
1361
|
+
jest
|
|
1362
|
+
.spyOn(webex.cc as any, 'releaseWxAppMercuryResources')
|
|
1363
|
+
.mockResolvedValue(undefined);
|
|
1364
|
+
|
|
1365
|
+
jest.spyOn(webex.cc.services.agent, 'stationLogin').mockResolvedValue({
|
|
1366
|
+
data: {
|
|
1367
|
+
agentId: 'agentId',
|
|
1368
|
+
teamId: 'teamId',
|
|
1369
|
+
channelsMap: {chat: [], email: [], social: [], telephony: []},
|
|
1370
|
+
},
|
|
1371
|
+
trackingId: 'track-1',
|
|
1372
|
+
} as StationLoginSuccess);
|
|
1373
|
+
|
|
1374
|
+
await webex.cc.stationLogin({
|
|
1375
|
+
teamId: 'teamId',
|
|
1376
|
+
loginOption: LoginOption.EXTENSION,
|
|
1377
|
+
dialNumber: '1001',
|
|
1378
|
+
});
|
|
1379
|
+
|
|
1380
|
+
expect(publishSpy).toHaveBeenCalledTimes(1);
|
|
1381
|
+
expect(scheduleRetrySpy).toHaveBeenCalled();
|
|
1382
|
+
expect(teardownSpy).not.toHaveBeenCalled();
|
|
1383
|
+
|
|
1384
|
+
jest.advanceTimersByTime(30_000);
|
|
1385
|
+
await publishSpy.mock.results[1]?.value;
|
|
1386
|
+
|
|
1387
|
+
expect(publishSpy).toHaveBeenCalledTimes(2);
|
|
1388
|
+
expect(publishSpy).toHaveBeenLastCalledWith(false, {userId: 'user-123', trackPublishMetrics: true});
|
|
1389
|
+
} finally {
|
|
1390
|
+
jest.useRealTimers();
|
|
1391
|
+
}
|
|
1392
|
+
});
|
|
1393
|
+
|
|
1394
|
+
it('should not publish usersub on BROWSER station login even when enableWxBetterTogether is true at init', async () => {
|
|
1395
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: true};
|
|
1396
|
+
webex.internal.device = {userId: 'user-123', url: 'https://wdm.example.com/devices/dev-1'};
|
|
1397
|
+
webex.cc.agentConfig = {
|
|
1398
|
+
agentId: 'agentId',
|
|
1399
|
+
webRtcEnabled: true,
|
|
1400
|
+
loginVoiceOptions: ['BROWSER'],
|
|
1401
|
+
};
|
|
1402
|
+
webex.cc.webCallingService.loginOption = LoginOption.BROWSER;
|
|
1403
|
+
|
|
1404
|
+
const publishSpy = jest.spyOn(
|
|
1405
|
+
webex.cc['webexCrossClientService'],
|
|
1406
|
+
'setManageWebexCallingInWxcc'
|
|
1407
|
+
);
|
|
1408
|
+
|
|
1409
|
+
jest.spyOn(webex.cc.services.agent, 'stationLogin').mockResolvedValue({
|
|
1410
|
+
data: {
|
|
1411
|
+
agentId: 'agentId',
|
|
1412
|
+
teamId: 'teamId',
|
|
1413
|
+
channelsMap: {chat: [], email: [], social: [], telephony: []},
|
|
1414
|
+
},
|
|
1415
|
+
trackingId: 'track-1',
|
|
1416
|
+
} as StationLoginSuccess);
|
|
1417
|
+
|
|
1418
|
+
await webex.cc.stationLogin({
|
|
1419
|
+
teamId: 'teamId',
|
|
1420
|
+
loginOption: LoginOption.BROWSER,
|
|
1421
|
+
});
|
|
1422
|
+
|
|
1423
|
+
expect(publishSpy).not.toHaveBeenCalled();
|
|
1424
|
+
});
|
|
1425
|
+
|
|
1426
|
+
it('should force-publish usersub false on station login when enableWxBetterTogether is false at init', async () => {
|
|
1427
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: false};
|
|
1428
|
+
webex.internal.device = {userId: 'user-123', url: 'https://wdm.example.com/devices/dev-1'};
|
|
1429
|
+
webex.cc.agentConfig = {
|
|
1430
|
+
agentId: 'agentId',
|
|
1431
|
+
webRtcEnabled: false,
|
|
1432
|
+
loginVoiceOptions: ['EXTENSION'],
|
|
1433
|
+
};
|
|
1434
|
+
webex.cc.webCallingService.loginOption = LoginOption.EXTENSION;
|
|
1435
|
+
|
|
1436
|
+
const publishSpy = jest
|
|
1437
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
1438
|
+
.mockResolvedValue(undefined);
|
|
1439
|
+
const teardownSpy = jest.spyOn(webex.cc['webexCrossClientService'], 'teardown');
|
|
1440
|
+
const unsubscribeSpy = jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'unsubscribe');
|
|
1441
|
+
|
|
1442
|
+
jest.spyOn(webex.cc.services.agent, 'stationLogin').mockResolvedValue({
|
|
1443
|
+
data: {
|
|
1444
|
+
agentId: 'agentId',
|
|
1445
|
+
teamId: 'teamId',
|
|
1446
|
+
channelsMap: {chat: [], email: [], social: [], telephony: []},
|
|
1447
|
+
},
|
|
1448
|
+
trackingId: 'track-1',
|
|
1449
|
+
} as StationLoginSuccess);
|
|
1450
|
+
|
|
1451
|
+
await webex.cc.stationLogin({
|
|
1452
|
+
teamId: 'teamId',
|
|
1453
|
+
loginOption: LoginOption.EXTENSION,
|
|
1454
|
+
dialNumber: '1001',
|
|
1455
|
+
});
|
|
1456
|
+
|
|
1457
|
+
expect(publishSpy).toHaveBeenCalledWith(false, {userId: 'user-123', trackPublishMetrics: true});
|
|
1458
|
+
expect(teardownSpy).toHaveBeenCalled();
|
|
1459
|
+
expect(unsubscribeSpy).toHaveBeenCalled();
|
|
1460
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
|
|
1461
|
+
METRIC_EVENT_NAMES.WXAPP_SESSION_SKIPPED,
|
|
1462
|
+
expect.objectContaining({
|
|
1463
|
+
enableWxBetterTogether: false,
|
|
1464
|
+
skipReason: 'flag_disabled',
|
|
1465
|
+
}),
|
|
1466
|
+
['operational', 'behavioral']
|
|
1467
|
+
);
|
|
1468
|
+
});
|
|
1469
|
+
|
|
1470
|
+
it('should not emit WXAPP_SESSION_INIT_FAILED when flag-disabled cleanup fails', async () => {
|
|
1471
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: false};
|
|
1472
|
+
webex.internal.device = {userId: 'user-123', url: 'https://wdm.example.com/devices/dev-1'};
|
|
1473
|
+
webex.cc.agentConfig = {
|
|
1474
|
+
agentId: 'agentId',
|
|
1475
|
+
webRtcEnabled: false,
|
|
1476
|
+
loginVoiceOptions: ['EXTENSION'],
|
|
1477
|
+
};
|
|
1478
|
+
webex.cc.webCallingService.loginOption = LoginOption.EXTENSION;
|
|
1479
|
+
|
|
1480
|
+
jest
|
|
1481
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
1482
|
+
.mockRejectedValue(new Error('forced false publish failed'));
|
|
1483
|
+
const metricSpy = jest.spyOn(mockMetricsManager, 'trackEvent');
|
|
1484
|
+
|
|
1485
|
+
jest.spyOn(webex.cc.services.agent, 'stationLogin').mockResolvedValue({
|
|
1486
|
+
data: {
|
|
1487
|
+
agentId: 'agentId',
|
|
1488
|
+
teamId: 'teamId',
|
|
1489
|
+
channelsMap: {chat: [], email: [], social: [], telephony: []},
|
|
1490
|
+
},
|
|
1491
|
+
trackingId: 'track-1',
|
|
1492
|
+
} as StationLoginSuccess);
|
|
1493
|
+
|
|
1494
|
+
await webex.cc.stationLogin({
|
|
1495
|
+
teamId: 'teamId',
|
|
1496
|
+
loginOption: LoginOption.EXTENSION,
|
|
1497
|
+
dialNumber: '1001',
|
|
1498
|
+
});
|
|
1499
|
+
|
|
1500
|
+
expect(metricSpy).not.toHaveBeenCalledWith(
|
|
1501
|
+
METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_FAILED,
|
|
1502
|
+
expect.anything(),
|
|
1503
|
+
expect.anything()
|
|
1504
|
+
);
|
|
1505
|
+
});
|
|
1506
|
+
|
|
1507
|
+
it('should still publish usersub but record session init failed when Mercury is unavailable', async () => {
|
|
1508
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: true};
|
|
1509
|
+
webex.internal.device = {
|
|
1510
|
+
userId: 'user-123',
|
|
1511
|
+
url: 'https://wdm.example.com/devices/dev-1',
|
|
1512
|
+
registered: true,
|
|
1513
|
+
register: jest.fn().mockResolvedValue(undefined),
|
|
1514
|
+
};
|
|
1515
|
+
delete webex.internal.mercury;
|
|
1516
|
+
webex.cc.agentConfig = {
|
|
1517
|
+
agentId: 'agentId',
|
|
1518
|
+
webRtcEnabled: false,
|
|
1519
|
+
loginVoiceOptions: ['EXTENSION'],
|
|
1520
|
+
};
|
|
1521
|
+
webex.cc.webCallingService.loginOption = LoginOption.EXTENSION;
|
|
1522
|
+
mockTaskManager.applyEnableWxBetterTogether(true);
|
|
1523
|
+
|
|
1524
|
+
const publishSpy = jest
|
|
1525
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
1526
|
+
.mockResolvedValue(undefined);
|
|
1527
|
+
jest
|
|
1528
|
+
.spyOn(webex.cc['webexCrossClientService'], 'isAnswerCallsStateActive')
|
|
1529
|
+
.mockReturnValue(true);
|
|
1530
|
+
const metricSpy = jest.spyOn(mockMetricsManager, 'trackEvent');
|
|
1531
|
+
|
|
1532
|
+
jest.spyOn(webex.cc.services.agent, 'stationLogin').mockResolvedValue({
|
|
1533
|
+
data: {
|
|
1534
|
+
agentId: 'agentId',
|
|
1535
|
+
teamId: 'teamId',
|
|
1536
|
+
channelsMap: {chat: [], email: [], social: [], telephony: []},
|
|
1537
|
+
},
|
|
1538
|
+
trackingId: 'track-1',
|
|
1539
|
+
} as StationLoginSuccess);
|
|
1540
|
+
|
|
1541
|
+
await webex.cc.stationLogin({
|
|
1542
|
+
teamId: 'teamId',
|
|
1543
|
+
loginOption: LoginOption.EXTENSION,
|
|
1544
|
+
dialNumber: '1001',
|
|
1545
|
+
});
|
|
1546
|
+
|
|
1547
|
+
expect(publishSpy).toHaveBeenCalledWith(true, {userId: 'user-123', trackPublishMetrics: true});
|
|
1548
|
+
expect(metricSpy).toHaveBeenCalledWith(
|
|
1549
|
+
METRIC_EVENT_NAMES.WXAPP_MERCURY_SUBSCRIBE_FAILED,
|
|
1550
|
+
expect.objectContaining({error: 'Mercury is unavailable for wxApp mute sync'}),
|
|
1551
|
+
['operational', 'behavioral']
|
|
1552
|
+
);
|
|
1553
|
+
expect(metricSpy).not.toHaveBeenCalledWith(
|
|
1554
|
+
METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_SUCCESS,
|
|
1555
|
+
expect.anything(),
|
|
1556
|
+
expect.anything()
|
|
1557
|
+
);
|
|
1558
|
+
expect(metricSpy).toHaveBeenCalledWith(
|
|
1559
|
+
METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_FAILED,
|
|
1560
|
+
expect.objectContaining({skipReason: 'mercury_not_subscribed'}),
|
|
1561
|
+
['operational', 'behavioral']
|
|
1562
|
+
);
|
|
1563
|
+
expect(webex.cc.isWxBetterTogetherEnabled()).toBe(true);
|
|
1564
|
+
});
|
|
1565
|
+
|
|
1566
|
+
it('should record session init failed when userId is missing but not emit success', async () => {
|
|
1567
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: true};
|
|
1568
|
+
webex.internal.device = {
|
|
1569
|
+
url: 'https://wdm.example.com/devices/dev-1',
|
|
1570
|
+
registered: true,
|
|
1571
|
+
register: jest.fn().mockResolvedValue(undefined),
|
|
1572
|
+
};
|
|
1573
|
+
webex.internal.mercury = {
|
|
1574
|
+
connected: true,
|
|
1575
|
+
connect: jest.fn().mockResolvedValue(undefined),
|
|
1576
|
+
on: jest.fn(),
|
|
1577
|
+
off: jest.fn(),
|
|
1578
|
+
};
|
|
1579
|
+
webex.cc.agentConfig = {
|
|
1580
|
+
agentId: 'agentId',
|
|
1581
|
+
webRtcEnabled: false,
|
|
1582
|
+
loginVoiceOptions: ['EXTENSION'],
|
|
1583
|
+
};
|
|
1584
|
+
webex.cc.webCallingService.loginOption = LoginOption.EXTENSION;
|
|
1585
|
+
mockTaskManager.applyEnableWxBetterTogether(true);
|
|
1586
|
+
|
|
1587
|
+
const publishSpy = jest
|
|
1588
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
1589
|
+
.mockResolvedValue(undefined);
|
|
1590
|
+
jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'isSubscribed').mockReturnValue(true);
|
|
1591
|
+
const metricSpy = jest.spyOn(mockMetricsManager, 'trackEvent');
|
|
1592
|
+
|
|
1593
|
+
jest.spyOn(webex.cc.services.agent, 'stationLogin').mockResolvedValue({
|
|
1594
|
+
data: {
|
|
1595
|
+
agentId: 'agentId',
|
|
1596
|
+
teamId: 'teamId',
|
|
1597
|
+
channelsMap: {chat: [], email: [], social: [], telephony: []},
|
|
1598
|
+
},
|
|
1599
|
+
trackingId: 'track-1',
|
|
1600
|
+
} as StationLoginSuccess);
|
|
1601
|
+
|
|
1602
|
+
await webex.cc.stationLogin({
|
|
1603
|
+
teamId: 'teamId',
|
|
1604
|
+
loginOption: LoginOption.EXTENSION,
|
|
1605
|
+
dialNumber: '1001',
|
|
1606
|
+
});
|
|
1607
|
+
|
|
1608
|
+
expect(publishSpy).not.toHaveBeenCalledWith(true, expect.anything());
|
|
1609
|
+
expect(metricSpy).not.toHaveBeenCalledWith(
|
|
1610
|
+
METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_SUCCESS,
|
|
1611
|
+
expect.anything(),
|
|
1612
|
+
expect.anything()
|
|
1613
|
+
);
|
|
1614
|
+
expect(metricSpy).toHaveBeenCalledWith(
|
|
1615
|
+
METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_FAILED,
|
|
1616
|
+
expect.objectContaining({skipReason: 'usersub_not_published'}),
|
|
1617
|
+
['operational', 'behavioral']
|
|
1618
|
+
);
|
|
1619
|
+
expect(metricSpy).toHaveBeenCalledWith(
|
|
1620
|
+
METRIC_EVENT_NAMES.WXAPP_USERSUB_PUBLISH_FAILED,
|
|
1621
|
+
expect.objectContaining({skipReason: 'user_id_unavailable'}),
|
|
1622
|
+
['operational', 'behavioral']
|
|
1623
|
+
);
|
|
1624
|
+
expect(webex.cc.isWxBetterTogetherEnabled()).toBe(true);
|
|
1625
|
+
});
|
|
1626
|
+
|
|
1627
|
+
it('should clear stale usersub on login after prior session enabled wxApp then hard refresh', async () => {
|
|
1628
|
+
webex.internal.device = {userId: 'user-123', url: 'https://wdm.example.com/devices/dev-1'};
|
|
1629
|
+
webex.cc.agentConfig = {
|
|
1630
|
+
agentId: 'agentId',
|
|
1631
|
+
webRtcEnabled: false,
|
|
1632
|
+
loginVoiceOptions: ['EXTENSION'],
|
|
1633
|
+
};
|
|
1634
|
+
webex.cc.webCallingService.loginOption = LoginOption.EXTENSION;
|
|
1635
|
+
webex.request = jest.fn().mockResolvedValue({body: {}});
|
|
1636
|
+
|
|
1637
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: true};
|
|
1638
|
+
jest.spyOn(webex.cc.services.agent, 'stationLogin').mockResolvedValue({
|
|
1639
|
+
data: {
|
|
1640
|
+
agentId: 'agentId',
|
|
1641
|
+
teamId: 'teamId',
|
|
1642
|
+
channelsMap: {chat: [], email: [], social: [], telephony: []},
|
|
1643
|
+
},
|
|
1644
|
+
trackingId: 'track-1',
|
|
1645
|
+
} as StationLoginSuccess);
|
|
1646
|
+
|
|
1647
|
+
await webex.cc.stationLogin({
|
|
1648
|
+
teamId: 'teamId',
|
|
1649
|
+
loginOption: LoginOption.EXTENSION,
|
|
1650
|
+
dialNumber: '1001',
|
|
1651
|
+
});
|
|
1652
|
+
|
|
1653
|
+
webex.cc['webexCrossClientService'].teardown();
|
|
1654
|
+
webex.cc['wxAppTelephonyMercurySync'].unsubscribe();
|
|
1655
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: false};
|
|
1656
|
+
|
|
1657
|
+
const publishSpy = jest
|
|
1658
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
1659
|
+
.mockResolvedValue(undefined);
|
|
1660
|
+
|
|
1661
|
+
await webex.cc.stationLogin({
|
|
1662
|
+
teamId: 'teamId',
|
|
1663
|
+
loginOption: LoginOption.EXTENSION,
|
|
1664
|
+
dialNumber: '1001',
|
|
1665
|
+
});
|
|
1666
|
+
|
|
1667
|
+
expect(publishSpy).toHaveBeenCalledWith(false, {userId: 'user-123', trackPublishMetrics: true});
|
|
1668
|
+
});
|
|
1669
|
+
});
|
|
1670
|
+
|
|
1671
|
+
describe('stationLogout', () => {
|
|
1672
|
+
it('should logout successfully', async () => {
|
|
1673
|
+
const data = {logoutReason: 'Logout reason'};
|
|
1674
|
+
const response = {};
|
|
1675
|
+
|
|
1676
|
+
const stationLogoutMock = jest
|
|
1677
|
+
.spyOn(webex.cc.services.agent, 'logout')
|
|
1678
|
+
.mockResolvedValue({} as StationLogoutResponse);
|
|
1679
|
+
|
|
1680
|
+
const result = await webex.cc.stationLogout(data);
|
|
1681
|
+
|
|
1682
|
+
// Verify logging calls
|
|
1683
|
+
expect(LoggerProxy.info).toHaveBeenCalledWith('Starting agent station logout', {
|
|
1684
|
+
module: CC_FILE,
|
|
1685
|
+
method: 'stationLogout',
|
|
1686
|
+
});
|
|
1687
|
+
expect(LoggerProxy.log).toHaveBeenCalledWith('Agent station logout completed successfully', {
|
|
1688
|
+
module: CC_FILE,
|
|
1689
|
+
method: 'stationLogout',
|
|
1690
|
+
});
|
|
1691
|
+
|
|
1692
|
+
expect(stationLogoutMock).toHaveBeenCalledWith({data: data});
|
|
1693
|
+
// TODO: https://jira-eng-gpk2.cisco.com/jira/browse/SPARK-626777 Implement the de-register method and close the listener there
|
|
1694
|
+
// expect(mockTaskManager.unregisterIncomingCallEvent).toHaveBeenCalledWith();
|
|
1695
|
+
// expect(mockTaskManager.off).toHaveBeenCalledWith(
|
|
1696
|
+
// TASK_EVENTS.TASK_INCOMING,
|
|
1697
|
+
// expect.any(Function)
|
|
1698
|
+
// );
|
|
1699
|
+
// expect(mockTaskManager.off).toHaveBeenCalledWith(
|
|
1700
|
+
// TASK_EVENTS.TASK_HYDRATE,
|
|
1701
|
+
// expect.any(Function)
|
|
1702
|
+
// );
|
|
1703
|
+
// expect(mockWebSocketManager.off).toHaveBeenCalledWith('message', expect.any(Function));
|
|
1704
|
+
expect(result).toEqual(response);
|
|
1705
|
+
expect(mockMetricsManager.timeEvent).toHaveBeenCalledWith([
|
|
1706
|
+
METRIC_EVENT_NAMES.STATION_LOGOUT_SUCCESS,
|
|
1707
|
+
METRIC_EVENT_NAMES.STATION_LOGOUT_FAILED,
|
|
1708
|
+
]);
|
|
1709
|
+
});
|
|
1710
|
+
|
|
1711
|
+
it('should not publish usersub false on logout when wxApp was never enabled', async () => {
|
|
1712
|
+
webex.internal.device = {userId: 'user-123'};
|
|
1713
|
+
const publishSpy = jest
|
|
1714
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
1715
|
+
.mockResolvedValue(undefined);
|
|
1716
|
+
const teardownSpy = jest.spyOn(webex.cc['webexCrossClientService'], 'teardown');
|
|
1717
|
+
const unsubscribeSpy = jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'unsubscribe');
|
|
1718
|
+
|
|
1719
|
+
jest.spyOn(webex.cc.services.agent, 'logout').mockResolvedValue({
|
|
1720
|
+
trackingId: 'track-1',
|
|
1721
|
+
} as StationLogoutResponse);
|
|
1722
|
+
|
|
1723
|
+
await webex.cc.stationLogout({logoutReason: 'Logout reason'});
|
|
1724
|
+
|
|
1725
|
+
expect(publishSpy).not.toHaveBeenCalled();
|
|
1726
|
+
expect(teardownSpy).toHaveBeenCalled();
|
|
1727
|
+
expect(unsubscribeSpy).toHaveBeenCalled();
|
|
1728
|
+
});
|
|
1729
|
+
|
|
1730
|
+
it('should publish usersub false and teardown wxApp sync on logout when wxApp was enabled', async () => {
|
|
1731
|
+
webex.internal.device = {
|
|
1732
|
+
userId: 'user-123',
|
|
1733
|
+
url: 'https://wdm.example.com/devices/dev-1',
|
|
1734
|
+
registered: false,
|
|
1735
|
+
register: jest.fn().mockResolvedValue(undefined),
|
|
1736
|
+
unregister: jest.fn().mockResolvedValue(undefined),
|
|
1737
|
+
};
|
|
1738
|
+
webex.internal.mercury = {
|
|
1739
|
+
connected: false,
|
|
1740
|
+
connect: jest.fn().mockResolvedValue(undefined),
|
|
1741
|
+
disconnect: jest.fn().mockResolvedValue(undefined),
|
|
1742
|
+
};
|
|
1743
|
+
jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'subscribe').mockImplementation(() => {});
|
|
1744
|
+
jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'isSubscribed').mockReturnValue(true);
|
|
1745
|
+
webex.request = jest.fn().mockResolvedValue({body: {}});
|
|
1746
|
+
webex.cc['agentConfig'] = {
|
|
1747
|
+
agentId: 'agent-123',
|
|
1748
|
+
deviceType: LoginOption.EXTENSION,
|
|
1749
|
+
} as Profile;
|
|
1750
|
+
webex.cc.webCallingService.loginOption = LoginOption.EXTENSION;
|
|
1751
|
+
|
|
1752
|
+
await (webex.cc as any).setManageWebexCallingInWxcc(true);
|
|
1753
|
+
|
|
1754
|
+
const publishSpy = jest
|
|
1755
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
1756
|
+
.mockResolvedValue(undefined);
|
|
1757
|
+
const teardownSpy = jest.spyOn(webex.cc['webexCrossClientService'], 'teardown');
|
|
1758
|
+
const unsubscribeSpy = jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'unsubscribe');
|
|
1759
|
+
|
|
1760
|
+
jest.spyOn(webex.cc.services.agent, 'logout').mockResolvedValue({
|
|
1761
|
+
trackingId: 'track-1',
|
|
1762
|
+
} as StationLogoutResponse);
|
|
1763
|
+
|
|
1764
|
+
await webex.cc.stationLogout({logoutReason: 'Logout reason'});
|
|
1765
|
+
|
|
1766
|
+
expect(publishSpy).toHaveBeenCalledWith(false, {userId: 'user-123', trackPublishMetrics: true});
|
|
1767
|
+
expect(teardownSpy).toHaveBeenCalled();
|
|
1768
|
+
expect(unsubscribeSpy).toHaveBeenCalled();
|
|
1769
|
+
});
|
|
1770
|
+
|
|
1771
|
+
it('should teardown wxApp state on logout even when usersub publish fails', async () => {
|
|
1772
|
+
webex.internal.device = {userId: 'user-123'};
|
|
1773
|
+
webex.cc['agentConfig'] = {
|
|
1774
|
+
agentId: 'agent-123',
|
|
1775
|
+
deviceType: LoginOption.EXTENSION,
|
|
1776
|
+
} as Profile;
|
|
1777
|
+
webex.cc.webCallingService.loginOption = LoginOption.EXTENSION;
|
|
1778
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: true};
|
|
1779
|
+
mockTaskManager.applyEnableWxBetterTogether(true);
|
|
1780
|
+
|
|
1781
|
+
jest.spyOn(webex.cc.services.agent, 'logout').mockResolvedValue({
|
|
1782
|
+
trackingId: 'track-1',
|
|
1783
|
+
} as StationLogoutResponse);
|
|
1784
|
+
jest
|
|
1785
|
+
.spyOn(webex.cc as never, 'publishAnswerOnWebexCrossClientState' as never)
|
|
1786
|
+
.mockRejectedValue(new Error('usersub publish failed'));
|
|
1787
|
+
const scheduleRetrySpy = jest.spyOn(
|
|
1788
|
+
webex.cc as any,
|
|
1789
|
+
'scheduleCompensatingFalsePublishRetry'
|
|
1790
|
+
);
|
|
1791
|
+
const teardownSpy = jest.spyOn(webex.cc['webexCrossClientService'], 'teardown');
|
|
1792
|
+
const unsubscribeSpy = jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'unsubscribe');
|
|
1793
|
+
|
|
1794
|
+
const result = await webex.cc.stationLogout({logoutReason: 'Logout reason'});
|
|
1795
|
+
|
|
1796
|
+
expect(result).toEqual({trackingId: 'track-1'});
|
|
1797
|
+
expect(teardownSpy).toHaveBeenCalled();
|
|
1798
|
+
expect(unsubscribeSpy).toHaveBeenCalled();
|
|
1799
|
+
expect(scheduleRetrySpy).toHaveBeenCalled();
|
|
1800
|
+
expect(webex.cc.isWxBetterTogetherEnabled()).toBe(true);
|
|
1801
|
+
});
|
|
1802
|
+
|
|
1803
|
+
it('should retry compensating false publish when teardown usersub publish fails on logout', async () => {
|
|
1804
|
+
jest.useFakeTimers();
|
|
1805
|
+
try {
|
|
1806
|
+
webex.internal.device = {
|
|
1807
|
+
userId: 'user-123',
|
|
1808
|
+
url: 'https://wdm.example.com/devices/dev-1',
|
|
1809
|
+
};
|
|
1810
|
+
webex.cc['agentConfig'] = {
|
|
1811
|
+
agentId: 'agent-123',
|
|
1812
|
+
deviceType: LoginOption.EXTENSION,
|
|
1813
|
+
} as Profile;
|
|
1814
|
+
webex.cc.webCallingService.loginOption = LoginOption.EXTENSION;
|
|
1815
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: true};
|
|
1816
|
+
mockTaskManager.applyEnableWxBetterTogether(true);
|
|
1817
|
+
webex.cc['webexCrossClientService'].answerCallsState = true;
|
|
1818
|
+
|
|
1819
|
+
jest.spyOn(webex.cc.services.agent, 'logout').mockResolvedValue({
|
|
1820
|
+
trackingId: 'track-1',
|
|
1821
|
+
} as StationLogoutResponse);
|
|
1822
|
+
|
|
1823
|
+
const publishSpy = jest
|
|
1824
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
1825
|
+
.mockRejectedValueOnce(new Error('usersub publish failed'))
|
|
1826
|
+
.mockResolvedValue(undefined);
|
|
1827
|
+
const teardownSpy = jest.spyOn(webex.cc['webexCrossClientService'], 'teardown');
|
|
1828
|
+
const unsubscribeSpy = jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'unsubscribe');
|
|
1829
|
+
jest
|
|
1830
|
+
.spyOn(webex.cc as any, 'releaseWxAppMercuryResources')
|
|
1831
|
+
.mockResolvedValue(undefined);
|
|
1832
|
+
|
|
1833
|
+
const result = await webex.cc.stationLogout({logoutReason: 'Logout reason'});
|
|
1834
|
+
|
|
1835
|
+
expect(result).toEqual({trackingId: 'track-1'});
|
|
1836
|
+
expect(publishSpy).toHaveBeenCalledTimes(1);
|
|
1837
|
+
expect(publishSpy).toHaveBeenCalledWith(false, {userId: 'user-123', trackPublishMetrics: true});
|
|
1838
|
+
expect(teardownSpy).toHaveBeenCalled();
|
|
1839
|
+
expect(unsubscribeSpy).toHaveBeenCalled();
|
|
1840
|
+
|
|
1841
|
+
jest.advanceTimersByTime(30_000);
|
|
1842
|
+
await publishSpy.mock.results[1]?.value;
|
|
1843
|
+
|
|
1844
|
+
expect(publishSpy).toHaveBeenCalledTimes(2);
|
|
1845
|
+
expect(publishSpy).toHaveBeenLastCalledWith(false, {userId: 'user-123', trackPublishMetrics: true});
|
|
1846
|
+
} finally {
|
|
1847
|
+
jest.useRealTimers();
|
|
1848
|
+
}
|
|
1849
|
+
});
|
|
1850
|
+
|
|
1851
|
+
it('should cancel compensating retry when station login succeeds before retry fires', async () => {
|
|
1852
|
+
jest.useFakeTimers();
|
|
1853
|
+
try {
|
|
1854
|
+
webex.internal.device = {
|
|
1855
|
+
userId: 'user-123',
|
|
1856
|
+
url: 'https://wdm.example.com/devices/dev-1',
|
|
1857
|
+
registered: false,
|
|
1858
|
+
register: jest.fn().mockResolvedValue(undefined),
|
|
1859
|
+
unregister: jest.fn().mockResolvedValue(undefined),
|
|
1860
|
+
};
|
|
1861
|
+
webex.internal.mercury = {
|
|
1862
|
+
connected: false,
|
|
1863
|
+
connect: jest.fn().mockResolvedValue(undefined),
|
|
1864
|
+
disconnect: jest.fn().mockResolvedValue(undefined),
|
|
1865
|
+
};
|
|
1866
|
+
jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'subscribe').mockImplementation(() => {});
|
|
1867
|
+
jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'isSubscribed').mockReturnValue(true);
|
|
1868
|
+
webex.cc['agentConfig'] = {
|
|
1869
|
+
agentId: 'agent-123',
|
|
1870
|
+
deviceType: LoginOption.EXTENSION,
|
|
1871
|
+
webRtcEnabled: false,
|
|
1872
|
+
loginVoiceOptions: ['EXTENSION'],
|
|
1873
|
+
} as Profile;
|
|
1874
|
+
webex.cc.webCallingService.loginOption = LoginOption.EXTENSION;
|
|
1875
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: true};
|
|
1876
|
+
mockTaskManager.applyEnableWxBetterTogether(true);
|
|
1877
|
+
webex.cc['webexCrossClientService'].answerCallsState = true;
|
|
1878
|
+
|
|
1879
|
+
jest.spyOn(webex.cc.services.agent, 'logout').mockResolvedValue({
|
|
1880
|
+
trackingId: 'track-1',
|
|
1881
|
+
} as StationLogoutResponse);
|
|
1882
|
+
jest.spyOn(webex.cc.services.agent, 'stationLogin').mockResolvedValue({
|
|
1883
|
+
data: {
|
|
1884
|
+
agentId: 'agent-123',
|
|
1885
|
+
teamId: 'team-123',
|
|
1886
|
+
channelsMap: {chat: [], email: [], social: [], telephony: []},
|
|
1887
|
+
},
|
|
1888
|
+
trackingId: 'track-2',
|
|
1889
|
+
} as StationLoginSuccess);
|
|
1890
|
+
|
|
1891
|
+
const publishSpy = jest
|
|
1892
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
1893
|
+
.mockRejectedValueOnce(new Error('usersub publish failed'))
|
|
1894
|
+
.mockResolvedValue(undefined);
|
|
1895
|
+
const teardownSpy = jest.spyOn(webex.cc['webexCrossClientService'], 'teardown');
|
|
1896
|
+
jest
|
|
1897
|
+
.spyOn(webex.cc as any, 'releaseWxAppMercuryResources')
|
|
1898
|
+
.mockResolvedValue(undefined);
|
|
1899
|
+
|
|
1900
|
+
await webex.cc.stationLogout({logoutReason: 'Logout reason'});
|
|
1901
|
+
await webex.cc.stationLogin({
|
|
1902
|
+
teamId: 'team-123',
|
|
1903
|
+
loginOption: LoginOption.EXTENSION,
|
|
1904
|
+
dialNumber: '1001',
|
|
1905
|
+
});
|
|
1906
|
+
|
|
1907
|
+
expect(publishSpy).toHaveBeenCalledTimes(2);
|
|
1908
|
+
expect(publishSpy).toHaveBeenNthCalledWith(1, false, {userId: 'user-123', trackPublishMetrics: true});
|
|
1909
|
+
expect(publishSpy).toHaveBeenNthCalledWith(2, true, {userId: 'user-123', trackPublishMetrics: true});
|
|
1910
|
+
expect(teardownSpy).toHaveBeenCalledTimes(1);
|
|
1911
|
+
|
|
1912
|
+
jest.advanceTimersByTime(30_000);
|
|
1913
|
+
|
|
1914
|
+
expect(publishSpy).toHaveBeenCalledTimes(2);
|
|
1915
|
+
expect(teardownSpy).toHaveBeenCalledTimes(1);
|
|
1916
|
+
} finally {
|
|
1917
|
+
jest.useRealTimers();
|
|
1918
|
+
}
|
|
1919
|
+
});
|
|
1920
|
+
|
|
1921
|
+
it('should handle error during stationLogout', async () => {
|
|
1922
|
+
const data = {logoutReason: 'Logout reason'};
|
|
1923
|
+
const error = {
|
|
1924
|
+
details: {
|
|
1925
|
+
trackingId: '1234',
|
|
1926
|
+
data: {
|
|
1927
|
+
reason: 'Error while performing station logout',
|
|
1928
|
+
},
|
|
1929
|
+
},
|
|
1930
|
+
};
|
|
1931
|
+
|
|
1932
|
+
jest.spyOn(webex.cc.services.agent, 'logout').mockRejectedValue(error);
|
|
1933
|
+
|
|
1934
|
+
await expect(webex.cc.stationLogout(data)).rejects.toThrow(error.details.data.reason);
|
|
1935
|
+
|
|
1936
|
+
expect(LoggerProxy.info).toHaveBeenCalledWith('Starting agent station logout', {
|
|
1937
|
+
module: CC_FILE,
|
|
1938
|
+
method: 'stationLogout',
|
|
1939
|
+
});
|
|
1940
|
+
expect(LoggerProxy.error).toHaveBeenCalledWith(
|
|
1941
|
+
`stationLogout failed with reason: ${error.details.data.reason}`,
|
|
1942
|
+
{module: CC_FILE, method: 'stationLogout', trackingId: error.details.trackingId}
|
|
1943
|
+
);
|
|
1944
|
+
});
|
|
1945
|
+
});
|
|
1946
|
+
|
|
1947
|
+
describe('setAgentStatus', () => {
|
|
1948
|
+
it('should set agent status successfully when status is Available', async () => {
|
|
1949
|
+
const expectedPayload = {
|
|
1950
|
+
state: 'Available',
|
|
1951
|
+
auxCodeId: '0',
|
|
1952
|
+
agentId: '123',
|
|
1953
|
+
lastStateChangeReason: 'Agent is available',
|
|
1954
|
+
};
|
|
1955
|
+
|
|
1956
|
+
const setAgentStatusMock = jest
|
|
1957
|
+
.spyOn(webex.cc.services.agent, 'stateChange')
|
|
1958
|
+
.mockResolvedValue({data: expectedPayload});
|
|
1959
|
+
|
|
1960
|
+
const result = await webex.cc.setAgentState(expectedPayload);
|
|
1961
|
+
|
|
1962
|
+
// Verify logging calls
|
|
1963
|
+
expect(LoggerProxy.info).toHaveBeenCalledWith('Setting agent state', {
|
|
1964
|
+
module: CC_FILE,
|
|
1965
|
+
method: 'setAgentState',
|
|
1966
|
+
});
|
|
1967
|
+
expect(LoggerProxy.log).toHaveBeenCalledWith(
|
|
1968
|
+
`Agent state changed successfully to auxCodeId: ${expectedPayload.auxCodeId}`,
|
|
1969
|
+
{
|
|
1970
|
+
module: CC_FILE,
|
|
1971
|
+
method: 'setAgentState',
|
|
1972
|
+
}
|
|
1973
|
+
);
|
|
1974
|
+
|
|
1975
|
+
expect(setAgentStatusMock).toHaveBeenCalledWith({data: expectedPayload});
|
|
1976
|
+
expect(result).toEqual({data: expectedPayload});
|
|
1977
|
+
expect(mockMetricsManager.timeEvent).toHaveBeenCalledWith([
|
|
1978
|
+
METRIC_EVENT_NAMES.AGENT_STATE_CHANGE_SUCCESS,
|
|
1979
|
+
METRIC_EVENT_NAMES.AGENT_STATE_CHANGE_FAILED,
|
|
1980
|
+
]);
|
|
1981
|
+
});
|
|
1982
|
+
|
|
1983
|
+
it('should set agent status successfully when status is Meeting', async () => {
|
|
1984
|
+
const expectedPayload = {
|
|
1985
|
+
state: 'Meeting',
|
|
1986
|
+
auxCodeId: '12345',
|
|
1987
|
+
agentId: '123',
|
|
1988
|
+
lastStateChangeReason: 'Agent is in meeting',
|
|
1989
|
+
};
|
|
1990
|
+
|
|
1991
|
+
const setAgentStatusMock = jest
|
|
1992
|
+
.spyOn(webex.cc.services.agent, 'stateChange')
|
|
1993
|
+
.mockResolvedValue({data: expectedPayload});
|
|
1994
|
+
|
|
1995
|
+
const result = await webex.cc.setAgentState(expectedPayload);
|
|
997
1996
|
|
|
998
1997
|
expect(setAgentStatusMock).toHaveBeenCalledWith({data: expectedPayload});
|
|
999
1998
|
expect(result).toEqual({data: expectedPayload});
|
|
@@ -1134,6 +2133,41 @@ describe('webex.cc', () => {
|
|
|
1134
2133
|
]);
|
|
1135
2134
|
});
|
|
1136
2135
|
|
|
2136
|
+
it('should map Transfer action to an Available backend state filter', async () => {
|
|
2137
|
+
webex.cc.agentConfig = {agentProfileID: 'test-agent-profile-id'};
|
|
2138
|
+
const buddyAgentsResponse = {data: {agentList: []}};
|
|
2139
|
+
const buddyAgentsSpy = jest
|
|
2140
|
+
.spyOn(webex.cc.services.agent, 'buddyAgents')
|
|
2141
|
+
.mockResolvedValue(buddyAgentsResponse);
|
|
2142
|
+
|
|
2143
|
+
await webex.cc.getBuddyAgents({action: 'Transfer', mediaType: 'chat'});
|
|
2144
|
+
|
|
2145
|
+
expect(buddyAgentsSpy).toHaveBeenCalledWith({
|
|
2146
|
+
data: {
|
|
2147
|
+
agentProfileId: 'test-agent-profile-id',
|
|
2148
|
+
mediaType: 'chat',
|
|
2149
|
+
state: 'Available',
|
|
2150
|
+
},
|
|
2151
|
+
});
|
|
2152
|
+
});
|
|
2153
|
+
|
|
2154
|
+
it('should omit the backend state filter for Consult and default media type to telephony', async () => {
|
|
2155
|
+
webex.cc.agentConfig = {agentProfileID: 'test-agent-profile-id'};
|
|
2156
|
+
const buddyAgentsResponse = {data: {agentList: []}};
|
|
2157
|
+
const buddyAgentsSpy = jest
|
|
2158
|
+
.spyOn(webex.cc.services.agent, 'buddyAgents')
|
|
2159
|
+
.mockResolvedValue(buddyAgentsResponse);
|
|
2160
|
+
|
|
2161
|
+
await webex.cc.getBuddyAgents({action: 'Consult'});
|
|
2162
|
+
|
|
2163
|
+
expect(buddyAgentsSpy).toHaveBeenCalledWith({
|
|
2164
|
+
data: {
|
|
2165
|
+
agentProfileId: 'test-agent-profile-id',
|
|
2166
|
+
mediaType: 'telephony',
|
|
2167
|
+
},
|
|
2168
|
+
});
|
|
2169
|
+
});
|
|
2170
|
+
|
|
1137
2171
|
it('should handle error', async () => {
|
|
1138
2172
|
const data: BuddyAgents = {state: 'Available', mediaType: 'telephony'};
|
|
1139
2173
|
webex.cc.agentConfig = {
|
|
@@ -1210,11 +2244,11 @@ describe('webex.cc', () => {
|
|
|
1210
2244
|
const webSocketManagerOnSpy = jest.spyOn(webex.cc.services.webSocketManager, 'on');
|
|
1211
2245
|
await webex.cc['silentRelogin']();
|
|
1212
2246
|
|
|
1213
|
-
expect(LoggerProxy.
|
|
2247
|
+
expect(LoggerProxy.log).toHaveBeenCalledWith('Starting silent relogin process', {
|
|
1214
2248
|
module: CC_FILE,
|
|
1215
2249
|
method: 'silentRelogin',
|
|
1216
2250
|
});
|
|
1217
|
-
expect(LoggerProxy.
|
|
2251
|
+
expect(LoggerProxy.log).toHaveBeenCalledWith(
|
|
1218
2252
|
'event=requestAutoStateChange | Requesting state change to available on socket reconnect',
|
|
1219
2253
|
{module: CC_FILE, method: 'silentRelogin'}
|
|
1220
2254
|
);
|
|
@@ -1259,7 +2293,7 @@ describe('webex.cc', () => {
|
|
|
1259
2293
|
|
|
1260
2294
|
jest.spyOn(webex.cc.services.agent, 'reload').mockRejectedValue(error);
|
|
1261
2295
|
await webex.cc['silentRelogin']();
|
|
1262
|
-
expect(LoggerProxy.
|
|
2296
|
+
expect(LoggerProxy.log).toHaveBeenCalledWith('Starting silent relogin process', {
|
|
1263
2297
|
module: CC_FILE,
|
|
1264
2298
|
method: 'silentRelogin',
|
|
1265
2299
|
});
|
|
@@ -1274,7 +2308,7 @@ describe('webex.cc', () => {
|
|
|
1274
2308
|
jest.spyOn(webex.cc.services.agent, 'reload').mockRejectedValue(error);
|
|
1275
2309
|
|
|
1276
2310
|
await expect(webex.cc['silentRelogin']()).rejects.toThrow(error);
|
|
1277
|
-
expect(LoggerProxy.
|
|
2311
|
+
expect(LoggerProxy.log).toHaveBeenCalledWith('Starting silent relogin process', {
|
|
1278
2312
|
module: CC_FILE,
|
|
1279
2313
|
method: 'silentRelogin',
|
|
1280
2314
|
});
|
|
@@ -1316,7 +2350,7 @@ describe('webex.cc', () => {
|
|
|
1316
2350
|
|
|
1317
2351
|
await webex.cc['silentRelogin']();
|
|
1318
2352
|
|
|
1319
|
-
expect(LoggerProxy.
|
|
2353
|
+
expect(LoggerProxy.log).toHaveBeenCalledWith('Starting silent relogin process', {
|
|
1320
2354
|
module: CC_FILE,
|
|
1321
2355
|
method: 'silentRelogin',
|
|
1322
2356
|
});
|
|
@@ -1361,6 +2395,82 @@ describe('webex.cc', () => {
|
|
|
1361
2395
|
expect(webex.cc.agentConfig.deviceType).toBe(LoginOption.AGENT_DN);
|
|
1362
2396
|
expect(webex.cc.agentConfig.dn).toBe('67890');
|
|
1363
2397
|
});
|
|
2398
|
+
|
|
2399
|
+
it('should publish usersub true on silent relogin when enableWxBetterTogether is true at init', async () => {
|
|
2400
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: true};
|
|
2401
|
+
webex.internal.device = {
|
|
2402
|
+
userId: 'user-123',
|
|
2403
|
+
url: 'https://wdm.example.com/devices/dev-1',
|
|
2404
|
+
registered: true,
|
|
2405
|
+
register: jest.fn().mockResolvedValue(undefined),
|
|
2406
|
+
};
|
|
2407
|
+
webex.internal.mercury.connected = true;
|
|
2408
|
+
webex.internal.mercury.connect = jest.fn().mockResolvedValue(undefined);
|
|
2409
|
+
webex.cc.agentConfig = {
|
|
2410
|
+
agentId: 'agentId',
|
|
2411
|
+
agentProfileID: 'test-agent-profile-id',
|
|
2412
|
+
isAgentLoggedIn: false,
|
|
2413
|
+
} as Profile;
|
|
2414
|
+
|
|
2415
|
+
jest.spyOn(webex.cc.services.agent, 'reload').mockResolvedValue({
|
|
2416
|
+
data: {
|
|
2417
|
+
auxCodeId: 'auxCodeId',
|
|
2418
|
+
agentId: 'agentId',
|
|
2419
|
+
deviceType: LoginOption.EXTENSION,
|
|
2420
|
+
dn: '1001',
|
|
2421
|
+
teamId: 'teamId',
|
|
2422
|
+
lastStateChangeTimestamp: 1738575135188,
|
|
2423
|
+
lastIdleCodeChangeTimestamp: 1738575135189,
|
|
2424
|
+
},
|
|
2425
|
+
});
|
|
2426
|
+
|
|
2427
|
+
const publishSpy = jest
|
|
2428
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
2429
|
+
.mockResolvedValue(undefined);
|
|
2430
|
+
const subscribeSpy = jest
|
|
2431
|
+
.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'subscribe')
|
|
2432
|
+
.mockImplementation(() => {});
|
|
2433
|
+
jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'isSubscribed').mockReturnValue(true);
|
|
2434
|
+
|
|
2435
|
+
await webex.cc['silentRelogin']();
|
|
2436
|
+
|
|
2437
|
+
expect(publishSpy).toHaveBeenCalledWith(true, {userId: 'user-123', trackPublishMetrics: true});
|
|
2438
|
+
expect(subscribeSpy).toHaveBeenCalledWith('agentId', expect.any(Function));
|
|
2439
|
+
});
|
|
2440
|
+
|
|
2441
|
+
it('should force-publish usersub false on silent relogin when enableWxBetterTogether is false at init', async () => {
|
|
2442
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: false};
|
|
2443
|
+
webex.internal.device = {userId: 'user-123', url: 'https://wdm.example.com/devices/dev-1'};
|
|
2444
|
+
webex.cc.agentConfig = {
|
|
2445
|
+
agentId: 'agentId',
|
|
2446
|
+
agentProfileID: 'test-agent-profile-id',
|
|
2447
|
+
isAgentLoggedIn: false,
|
|
2448
|
+
} as Profile;
|
|
2449
|
+
|
|
2450
|
+
jest.spyOn(webex.cc.services.agent, 'reload').mockResolvedValue({
|
|
2451
|
+
data: {
|
|
2452
|
+
auxCodeId: 'auxCodeId',
|
|
2453
|
+
agentId: 'agentId',
|
|
2454
|
+
deviceType: LoginOption.EXTENSION,
|
|
2455
|
+
dn: '1001',
|
|
2456
|
+
teamId: 'teamId',
|
|
2457
|
+
lastStateChangeTimestamp: 1738575135188,
|
|
2458
|
+
lastIdleCodeChangeTimestamp: 1738575135189,
|
|
2459
|
+
},
|
|
2460
|
+
});
|
|
2461
|
+
|
|
2462
|
+
const publishSpy = jest
|
|
2463
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
2464
|
+
.mockResolvedValue(undefined);
|
|
2465
|
+
const teardownSpy = jest.spyOn(webex.cc['webexCrossClientService'], 'teardown');
|
|
2466
|
+
const unsubscribeSpy = jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'unsubscribe');
|
|
2467
|
+
|
|
2468
|
+
await webex.cc['silentRelogin']();
|
|
2469
|
+
|
|
2470
|
+
expect(publishSpy).toHaveBeenCalledWith(false, {userId: 'user-123', trackPublishMetrics: true});
|
|
2471
|
+
expect(teardownSpy).toHaveBeenCalled();
|
|
2472
|
+
expect(unsubscribeSpy).toHaveBeenCalled();
|
|
2473
|
+
});
|
|
1364
2474
|
});
|
|
1365
2475
|
|
|
1366
2476
|
describe('setupEventListeners()', () => {
|
|
@@ -1500,6 +2610,27 @@ describe('webex.cc', () => {
|
|
|
1500
2610
|
});
|
|
1501
2611
|
});
|
|
1502
2612
|
|
|
2613
|
+
describe('getQueues', () => {
|
|
2614
|
+
it('delegates to the queue service when successful', async () => {
|
|
2615
|
+
const mockQueuesResponse = [{queueId: 'queue1', queueName: 'Queue 1'}];
|
|
2616
|
+
const queueSpy = jest
|
|
2617
|
+
.spyOn(webex.cc.queue, 'getQueues')
|
|
2618
|
+
.mockResolvedValue(mockQueuesResponse as any);
|
|
2619
|
+
|
|
2620
|
+
const result = await webex.cc.getQueues({page: 1});
|
|
2621
|
+
|
|
2622
|
+
expect(queueSpy).toHaveBeenCalledWith({page: 1});
|
|
2623
|
+
expect(result).toBe(mockQueuesResponse);
|
|
2624
|
+
});
|
|
2625
|
+
|
|
2626
|
+
it('propagates queue service errors', async () => {
|
|
2627
|
+
const error = new Error('Test error.');
|
|
2628
|
+
jest.spyOn(webex.cc.queue, 'getQueues').mockRejectedValue(error);
|
|
2629
|
+
|
|
2630
|
+
await expect(webex.cc.getQueues()).rejects.toThrow('Test error.');
|
|
2631
|
+
});
|
|
2632
|
+
});
|
|
2633
|
+
|
|
1503
2634
|
describe('uploadLogs', () => {
|
|
1504
2635
|
it('should upload logs successfully', async () => {
|
|
1505
2636
|
const uploadLogsMock = jest.spyOn(webex.cc.webexRequest, 'uploadLogs').mockResolvedValue({
|
|
@@ -1529,6 +2660,7 @@ describe('webex.cc', () => {
|
|
|
1529
2660
|
|
|
1530
2661
|
describe('unregister', () => {
|
|
1531
2662
|
let mockWebSocketManager;
|
|
2663
|
+
let mockRTDWebSocketManager;
|
|
1532
2664
|
let mercuryDisconnectSpy;
|
|
1533
2665
|
let deviceUnregisterSpy;
|
|
1534
2666
|
|
|
@@ -1545,8 +2677,15 @@ describe('webex.cc', () => {
|
|
|
1545
2677
|
off: jest.fn(),
|
|
1546
2678
|
on: jest.fn(),
|
|
1547
2679
|
};
|
|
2680
|
+
mockRTDWebSocketManager = {
|
|
2681
|
+
isSocketClosed: false,
|
|
2682
|
+
close: jest.fn(),
|
|
2683
|
+
off: jest.fn(),
|
|
2684
|
+
on: jest.fn(),
|
|
2685
|
+
};
|
|
1548
2686
|
|
|
1549
2687
|
webex.cc.services.webSocketManager = mockWebSocketManager;
|
|
2688
|
+
webex.cc.services.rtdWebSocketManager = mockRTDWebSocketManager;
|
|
1550
2689
|
|
|
1551
2690
|
webex.internal = webex.internal || {};
|
|
1552
2691
|
webex.internal.mercury = {
|
|
@@ -1570,6 +2709,18 @@ describe('webex.cc', () => {
|
|
|
1570
2709
|
on: jest.fn(),
|
|
1571
2710
|
} as any;
|
|
1572
2711
|
|
|
2712
|
+
webex.internal.device = {
|
|
2713
|
+
...webex.internal.device,
|
|
2714
|
+
userId: 'user-123',
|
|
2715
|
+
unregister: jest.fn().mockResolvedValue(undefined),
|
|
2716
|
+
};
|
|
2717
|
+
deviceUnregisterSpy = jest.spyOn(webex.internal.device, 'unregister');
|
|
2718
|
+
const publishSpy = jest
|
|
2719
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
2720
|
+
.mockResolvedValue(undefined);
|
|
2721
|
+
const teardownSpy = jest.spyOn(webex.cc['webexCrossClientService'], 'teardown');
|
|
2722
|
+
const unsubscribeSpy = jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'unsubscribe');
|
|
2723
|
+
|
|
1573
2724
|
await webex.cc.deregister();
|
|
1574
2725
|
|
|
1575
2726
|
expect(mockTaskManager.off).toHaveBeenCalledWith(
|
|
@@ -1580,6 +2731,10 @@ describe('webex.cc', () => {
|
|
|
1580
2731
|
TASK_EVENTS.TASK_HYDRATE,
|
|
1581
2732
|
expect.any(Function)
|
|
1582
2733
|
);
|
|
2734
|
+
expect(mockTaskManager.off).toHaveBeenCalledWith(
|
|
2735
|
+
TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE,
|
|
2736
|
+
expect.any(Function)
|
|
2737
|
+
);
|
|
1583
2738
|
expect(mockWebSocketManager.off).toHaveBeenCalledWith('message', expect.any(Function));
|
|
1584
2739
|
expect(webex.cc.services.rtdWebSocketManager.off).toHaveBeenCalledWith(
|
|
1585
2740
|
'message',
|
|
@@ -1636,17 +2791,57 @@ describe('webex.cc', () => {
|
|
|
1636
2791
|
const [, hydrateCallback] = hydrateCalls[0];
|
|
1637
2792
|
expect(hydrateCallback).toBe(webex.cc['handleTaskHydrate']);
|
|
1638
2793
|
|
|
2794
|
+
const multiLoginHydrateCalls = mockTaskManager.off.mock.calls.filter(
|
|
2795
|
+
([evt]) => evt === TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE
|
|
2796
|
+
);
|
|
2797
|
+
expect(multiLoginHydrateCalls).toHaveLength(1);
|
|
2798
|
+
const [, multiLoginHydrateCallback] = multiLoginHydrateCalls[0];
|
|
2799
|
+
expect(multiLoginHydrateCallback).toBe(webex.cc['handleTaskMultiLoginHydrate']);
|
|
2800
|
+
|
|
1639
2801
|
const messageCalls = mockWebSocketManager.off.mock.calls.filter(([evt]) => evt === 'message');
|
|
1640
2802
|
expect(messageCalls).toHaveLength(1);
|
|
1641
2803
|
const [, messageCallback] = messageCalls[0];
|
|
1642
2804
|
expect(messageCallback).toBe(webex.cc['handleWebsocketMessage']);
|
|
1643
2805
|
|
|
1644
|
-
const connectionCalls = webex.cc.services.connectionService.off.mock.calls.filter(
|
|
1645
|
-
([evt]) => evt === 'connectionLost'
|
|
1646
|
-
);
|
|
1647
|
-
expect(connectionCalls).toHaveLength(1);
|
|
1648
|
-
const [, connectionCallback] = connectionCalls[0];
|
|
1649
|
-
expect(connectionCallback).toBe(webex.cc['handleConnectionLost']);
|
|
2806
|
+
const connectionCalls = webex.cc.services.connectionService.off.mock.calls.filter(
|
|
2807
|
+
([evt]) => evt === 'connectionLost'
|
|
2808
|
+
);
|
|
2809
|
+
expect(connectionCalls).toHaveLength(1);
|
|
2810
|
+
const [, connectionCallback] = connectionCalls[0];
|
|
2811
|
+
expect(connectionCallback).toBe(webex.cc['handleConnectionLost']);
|
|
2812
|
+
|
|
2813
|
+
expect(publishSpy).not.toHaveBeenCalled();
|
|
2814
|
+
expect(teardownSpy).toHaveBeenCalled();
|
|
2815
|
+
expect(unsubscribeSpy).toHaveBeenCalled();
|
|
2816
|
+
});
|
|
2817
|
+
|
|
2818
|
+
it('should publish usersub false before device unregister when wxApp is active on deregister', async () => {
|
|
2819
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: true};
|
|
2820
|
+
webex.cc['webexCrossClientService'].answerCallsState = true;
|
|
2821
|
+
webex.internal.device = {
|
|
2822
|
+
userId: 'user-123',
|
|
2823
|
+
url: 'https://wdm.example.com/devices/dev-1',
|
|
2824
|
+
unregister: jest.fn().mockResolvedValue(undefined),
|
|
2825
|
+
};
|
|
2826
|
+
deviceUnregisterSpy = jest.spyOn(webex.internal.device, 'unregister');
|
|
2827
|
+
|
|
2828
|
+
const callOrder: string[] = [];
|
|
2829
|
+
jest
|
|
2830
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
2831
|
+
.mockImplementation(async (enable: boolean) => {
|
|
2832
|
+
if (enable === false && !webex.internal.device.url) {
|
|
2833
|
+
throw new Error('Device URL is unavailable for cross-client publish');
|
|
2834
|
+
}
|
|
2835
|
+
callOrder.push(enable ? 'publish:true' : 'publish:false');
|
|
2836
|
+
});
|
|
2837
|
+
deviceUnregisterSpy.mockImplementation(async () => {
|
|
2838
|
+
callOrder.push('device.unregister');
|
|
2839
|
+
webex.internal.device.url = undefined;
|
|
2840
|
+
});
|
|
2841
|
+
|
|
2842
|
+
await webex.cc.deregister();
|
|
2843
|
+
|
|
2844
|
+
expect(callOrder).toEqual(['publish:false', 'device.unregister']);
|
|
1650
2845
|
});
|
|
1651
2846
|
|
|
1652
2847
|
it('should skip webCallingService and internal cleanup when webrtc is disabled', async () => {
|
|
@@ -1661,6 +2856,10 @@ describe('webex.cc', () => {
|
|
|
1661
2856
|
TASK_EVENTS.TASK_HYDRATE,
|
|
1662
2857
|
expect.any(Function)
|
|
1663
2858
|
);
|
|
2859
|
+
expect(mockTaskManager.off).toHaveBeenCalledWith(
|
|
2860
|
+
TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE,
|
|
2861
|
+
expect.any(Function)
|
|
2862
|
+
);
|
|
1664
2863
|
expect(mockWebSocketManager.off).toHaveBeenCalledWith('message', expect.any(Function));
|
|
1665
2864
|
expect(webex.cc.services.connectionService.off).toHaveBeenCalledWith(
|
|
1666
2865
|
'connectionLost',
|
|
@@ -1670,6 +2869,7 @@ describe('webex.cc', () => {
|
|
|
1670
2869
|
expect(webex.internal.mercury.off).not.toHaveBeenCalled();
|
|
1671
2870
|
expect(mercuryDisconnectSpy).not.toHaveBeenCalled();
|
|
1672
2871
|
expect(deviceUnregisterSpy).not.toHaveBeenCalled();
|
|
2872
|
+
expect(mockRTDWebSocketManager.close).toHaveBeenCalledWith(false, 'Unregistering the SDK');
|
|
1673
2873
|
});
|
|
1674
2874
|
|
|
1675
2875
|
it('should skip internal mercury cleanup when loginVoiceOptions does not include BROWSER', async () => {
|
|
@@ -1686,10 +2886,140 @@ describe('webex.cc', () => {
|
|
|
1686
2886
|
expect(mercuryDisconnectSpy).not.toHaveBeenCalled();
|
|
1687
2887
|
expect(deviceUnregisterSpy).not.toHaveBeenCalled();
|
|
1688
2888
|
|
|
2889
|
+
expect(mockWebSocketManager.close).toHaveBeenCalledWith(false, 'Unregistering the SDK');
|
|
2890
|
+
expect(mockRTDWebSocketManager.close).toHaveBeenCalledWith(false, 'Unregistering the SDK');
|
|
2891
|
+
expect(webex.cc.agentConfig).toBeNull();
|
|
2892
|
+
});
|
|
2893
|
+
|
|
2894
|
+
it('should teardown wxApp state on deregister even when usersub publish fails', async () => {
|
|
2895
|
+
webex.cc.agentConfig = {
|
|
2896
|
+
agentId: 'agentId',
|
|
2897
|
+
webRtcEnabled: false,
|
|
2898
|
+
loginVoiceOptions: [LoginOption.EXTENSION],
|
|
2899
|
+
};
|
|
2900
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: true};
|
|
2901
|
+
webex.cc['webexCrossClientService'].answerCallsState = true;
|
|
2902
|
+
webex.internal.device = {
|
|
2903
|
+
userId: 'user-123',
|
|
2904
|
+
unregister: jest.fn().mockResolvedValue(undefined),
|
|
2905
|
+
};
|
|
2906
|
+
|
|
2907
|
+
jest
|
|
2908
|
+
.spyOn(webex.cc as never, 'publishAnswerOnWebexCrossClientState' as never)
|
|
2909
|
+
.mockRejectedValue(new Error('usersub publish failed'));
|
|
2910
|
+
const teardownSpy = jest.spyOn(webex.cc['webexCrossClientService'], 'teardown');
|
|
2911
|
+
const unsubscribeSpy = jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'unsubscribe');
|
|
2912
|
+
|
|
2913
|
+
await expect(webex.cc.deregister()).rejects.toThrow('usersub publish failed');
|
|
2914
|
+
|
|
2915
|
+
expect(teardownSpy).toHaveBeenCalled();
|
|
2916
|
+
expect(unsubscribeSpy).toHaveBeenCalled();
|
|
2917
|
+
expect(webex.cc.isWxBetterTogetherEnabled()).toBe(false);
|
|
1689
2918
|
expect(mockWebSocketManager.close).toHaveBeenCalledWith(false, 'Unregistering the SDK');
|
|
1690
2919
|
expect(webex.cc.agentConfig).toBeNull();
|
|
1691
2920
|
});
|
|
1692
2921
|
|
|
2922
|
+
it('should publish forced false during teardown when enable publish is still in flight', async () => {
|
|
2923
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: true};
|
|
2924
|
+
webex.internal.device = {
|
|
2925
|
+
userId: 'user-123',
|
|
2926
|
+
url: 'https://wdm.example.com/devices/dev-1',
|
|
2927
|
+
};
|
|
2928
|
+
|
|
2929
|
+
let resolveEnable: () => void = () => {};
|
|
2930
|
+
const enableGate = new Promise<void>((resolve) => {
|
|
2931
|
+
resolveEnable = resolve;
|
|
2932
|
+
});
|
|
2933
|
+
|
|
2934
|
+
const publishSpy = jest
|
|
2935
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
2936
|
+
.mockImplementation(async (enable: boolean) => {
|
|
2937
|
+
if (enable) {
|
|
2938
|
+
await enableGate;
|
|
2939
|
+
}
|
|
2940
|
+
});
|
|
2941
|
+
jest
|
|
2942
|
+
.spyOn(webex.cc as any, 'releaseWxAppMercuryResources')
|
|
2943
|
+
.mockResolvedValue(undefined);
|
|
2944
|
+
|
|
2945
|
+
expect(webex.cc['webexCrossClientService'].answerCallsState).toBe(false);
|
|
2946
|
+
|
|
2947
|
+
const enableInFlight = (webex.cc as any).publishAnswerOnWebexCrossClientState(true);
|
|
2948
|
+
|
|
2949
|
+
await (webex.cc as any).teardownWxAppLocalState();
|
|
2950
|
+
|
|
2951
|
+
expect(publishSpy).toHaveBeenCalledWith(false, {userId: 'user-123', trackPublishMetrics: true});
|
|
2952
|
+
|
|
2953
|
+
resolveEnable();
|
|
2954
|
+
await enableInFlight;
|
|
2955
|
+
});
|
|
2956
|
+
|
|
2957
|
+
it('should release wxApp-owned Mercury and device on deregister', async () => {
|
|
2958
|
+
webex.cc.agentConfig = {
|
|
2959
|
+
agentId: 'agentId',
|
|
2960
|
+
webRtcEnabled: false,
|
|
2961
|
+
loginVoiceOptions: [LoginOption.EXTENSION],
|
|
2962
|
+
};
|
|
2963
|
+
webex.cc['wxAppMercuryConnectedByCc'] = true;
|
|
2964
|
+
webex.cc['wxAppDeviceRegisteredByCc'] = true;
|
|
2965
|
+
webex.internal.mercury.connected = true;
|
|
2966
|
+
webex.internal.device.registered = true;
|
|
2967
|
+
|
|
2968
|
+
const mercuryDisconnectSpy = jest
|
|
2969
|
+
.spyOn(webex.internal.mercury, 'disconnect')
|
|
2970
|
+
.mockResolvedValue(undefined);
|
|
2971
|
+
const deviceUnregisterSpy = jest
|
|
2972
|
+
.spyOn(webex.internal.device, 'unregister')
|
|
2973
|
+
.mockResolvedValue(undefined);
|
|
2974
|
+
|
|
2975
|
+
await webex.cc.deregister();
|
|
2976
|
+
|
|
2977
|
+
expect(mercuryDisconnectSpy).toHaveBeenCalled();
|
|
2978
|
+
expect(deviceUnregisterSpy).toHaveBeenCalled();
|
|
2979
|
+
expect(webex.cc['wxAppMercuryConnectedByCc']).toBe(false);
|
|
2980
|
+
expect(webex.cc['wxAppDeviceRegisteredByCc']).toBe(false);
|
|
2981
|
+
});
|
|
2982
|
+
|
|
2983
|
+
it('should retain wxApp Mercury ownership when disconnect fails and retry cleanup', async () => {
|
|
2984
|
+
webex.cc['wxAppMercuryConnectedByCc'] = true;
|
|
2985
|
+
webex.internal.mercury.connected = true;
|
|
2986
|
+
|
|
2987
|
+
const mercuryDisconnectSpy = jest
|
|
2988
|
+
.spyOn(webex.internal.mercury, 'disconnect')
|
|
2989
|
+
.mockRejectedValueOnce(new Error('disconnect failed'))
|
|
2990
|
+
.mockResolvedValueOnce(undefined);
|
|
2991
|
+
|
|
2992
|
+
await webex.cc['releaseWxAppMercuryResources']();
|
|
2993
|
+
|
|
2994
|
+
expect(mercuryDisconnectSpy).toHaveBeenCalledTimes(1);
|
|
2995
|
+
expect(webex.cc['wxAppMercuryConnectedByCc']).toBe(true);
|
|
2996
|
+
|
|
2997
|
+
await webex.cc['releaseWxAppMercuryResources']();
|
|
2998
|
+
|
|
2999
|
+
expect(mercuryDisconnectSpy).toHaveBeenCalledTimes(2);
|
|
3000
|
+
expect(webex.cc['wxAppMercuryConnectedByCc']).toBe(false);
|
|
3001
|
+
});
|
|
3002
|
+
|
|
3003
|
+
it('should retain wxApp device ownership when unregister fails and retry cleanup', async () => {
|
|
3004
|
+
webex.cc['wxAppDeviceRegisteredByCc'] = true;
|
|
3005
|
+
webex.internal.device.registered = true;
|
|
3006
|
+
|
|
3007
|
+
const deviceUnregisterSpy = jest
|
|
3008
|
+
.spyOn(webex.internal.device, 'unregister')
|
|
3009
|
+
.mockRejectedValueOnce(new Error('unregister failed'))
|
|
3010
|
+
.mockResolvedValueOnce(undefined);
|
|
3011
|
+
|
|
3012
|
+
await webex.cc['releaseWxAppMercuryResources']();
|
|
3013
|
+
|
|
3014
|
+
expect(deviceUnregisterSpy).toHaveBeenCalledTimes(1);
|
|
3015
|
+
expect(webex.cc['wxAppDeviceRegisteredByCc']).toBe(true);
|
|
3016
|
+
|
|
3017
|
+
await webex.cc['releaseWxAppMercuryResources']();
|
|
3018
|
+
|
|
3019
|
+
expect(deviceUnregisterSpy).toHaveBeenCalledTimes(2);
|
|
3020
|
+
expect(webex.cc['wxAppDeviceRegisteredByCc']).toBe(false);
|
|
3021
|
+
});
|
|
3022
|
+
|
|
1693
3023
|
it('should handle errors during unregister and track metrics', async () => {
|
|
1694
3024
|
const mockError = new Error('Failed to deregister device');
|
|
1695
3025
|
webex.internal.device.unregister.mockRejectedValue(mockError);
|
|
@@ -1704,6 +3034,10 @@ describe('webex.cc', () => {
|
|
|
1704
3034
|
TASK_EVENTS.TASK_HYDRATE,
|
|
1705
3035
|
expect.any(Function)
|
|
1706
3036
|
);
|
|
3037
|
+
expect(mockTaskManager.off).toHaveBeenCalledWith(
|
|
3038
|
+
TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE,
|
|
3039
|
+
expect.any(Function)
|
|
3040
|
+
);
|
|
1707
3041
|
|
|
1708
3042
|
expect(LoggerProxy.error).toHaveBeenCalledWith(`Error during deregister: ${mockError}`, {
|
|
1709
3043
|
module: CC_FILE,
|
|
@@ -1726,7 +3060,7 @@ describe('webex.cc', () => {
|
|
|
1726
3060
|
|
|
1727
3061
|
beforeEach(() => {
|
|
1728
3062
|
emitSpy = jest.spyOn(webex.cc, 'emit');
|
|
1729
|
-
messageCallback =
|
|
3063
|
+
messageCallback = webex.cc['handleWebsocketMessage'];
|
|
1730
3064
|
});
|
|
1731
3065
|
|
|
1732
3066
|
it('should emit AGENT_STATION_LOGIN_SUCCESS on CC_EVENTS.AGENT_STATION_LOGIN_SUCCESS with mapped payload', () => {
|
|
@@ -1936,6 +3270,55 @@ describe('webex.cc', () => {
|
|
|
1936
3270
|
expect(result).toEqual(mockResp);
|
|
1937
3271
|
});
|
|
1938
3272
|
|
|
3273
|
+
it('should preserve enableWxBetterTogether init flag across profile update logout/login', async () => {
|
|
3274
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: true};
|
|
3275
|
+
webex.internal.device = {userId: 'user-123', url: 'https://wdm.example.com/devices/dev-1'};
|
|
3276
|
+
webex.cc.agentConfig = {
|
|
3277
|
+
...webex.cc.agentConfig,
|
|
3278
|
+
agentId: 'agentId',
|
|
3279
|
+
webRtcEnabled: false,
|
|
3280
|
+
loginVoiceOptions: ['BROWSER', 'EXTENSION'],
|
|
3281
|
+
} as Profile;
|
|
3282
|
+
webex.cc.webCallingService.loginOption = LoginOption.BROWSER;
|
|
3283
|
+
webex.cc.webCallingService.setLoginOption = jest.fn((loginOption: LoginOption) => {
|
|
3284
|
+
webex.cc.webCallingService.loginOption = loginOption;
|
|
3285
|
+
});
|
|
3286
|
+
webex.cc['webexCrossClientService'].answerCallsState = true;
|
|
3287
|
+
|
|
3288
|
+
const publishSpy = jest
|
|
3289
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
3290
|
+
.mockResolvedValue(undefined);
|
|
3291
|
+
jest
|
|
3292
|
+
.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'subscribe')
|
|
3293
|
+
.mockImplementation(() => {});
|
|
3294
|
+
jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'isSubscribed').mockReturnValue(true);
|
|
3295
|
+
jest.spyOn(webex.cc as any, 'releaseWxAppMercuryResources').mockResolvedValue(undefined);
|
|
3296
|
+
|
|
3297
|
+
jest.spyOn(webex.cc.services.agent, 'logout').mockResolvedValue({
|
|
3298
|
+
trackingId: 'track-logout',
|
|
3299
|
+
} as StationLogoutResponse);
|
|
3300
|
+
jest.spyOn(webex.cc.services.agent, 'stationLogin').mockResolvedValue({
|
|
3301
|
+
data: {
|
|
3302
|
+
agentId: 'agentId',
|
|
3303
|
+
teamId: 'teamId',
|
|
3304
|
+
channelsMap: {chat: [], email: [], social: [], telephony: []},
|
|
3305
|
+
},
|
|
3306
|
+
trackingId: 'track-login',
|
|
3307
|
+
} as StationLoginSuccess);
|
|
3308
|
+
|
|
3309
|
+
webex.internal.mercury.connected = true;
|
|
3310
|
+
|
|
3311
|
+
await webex.cc.updateAgentProfile({
|
|
3312
|
+
teamId: 'teamId',
|
|
3313
|
+
loginOption: LoginOption.EXTENSION,
|
|
3314
|
+
dialNumber: '1001',
|
|
3315
|
+
});
|
|
3316
|
+
|
|
3317
|
+
expect(webex.cc.isWxBetterTogetherEnabled()).toBe(true);
|
|
3318
|
+
expect(publishSpy).toHaveBeenCalledWith(false, {userId: 'user-123', trackPublishMetrics: true});
|
|
3319
|
+
expect(publishSpy).toHaveBeenCalledWith(true, {userId: 'user-123', trackPublishMetrics: true});
|
|
3320
|
+
});
|
|
3321
|
+
|
|
1939
3322
|
it('should use provided teamId if passed in payload', async () => {
|
|
1940
3323
|
const dataWithTeam = {
|
|
1941
3324
|
teamId: 'newTeam',
|
|
@@ -2320,4 +3703,444 @@ describe('webex.cc', () => {
|
|
|
2320
3703
|
expect(getErrorDetailsSpy).toHaveBeenCalledWith(error, 'acceptPreviewContact', CC_FILE);
|
|
2321
3704
|
});
|
|
2322
3705
|
});
|
|
3706
|
+
|
|
3707
|
+
describe('skipPreviewContact', () => {
|
|
3708
|
+
const previewPayload = {
|
|
3709
|
+
interactionId: 'interaction-123',
|
|
3710
|
+
campaignId: 'campaign-456',
|
|
3711
|
+
};
|
|
3712
|
+
|
|
3713
|
+
it('should skip preview contact successfully', async () => {
|
|
3714
|
+
const mockResponse = {trackingId: 'track-123'} as AgentContact;
|
|
3715
|
+
|
|
3716
|
+
const skipPreviewContactMock = jest
|
|
3717
|
+
.spyOn(webex.cc.services.dialer, 'skipPreviewContact')
|
|
3718
|
+
.mockResolvedValue(mockResponse);
|
|
3719
|
+
|
|
3720
|
+
const result = await webex.cc.skipPreviewContact(previewPayload);
|
|
3721
|
+
|
|
3722
|
+
expect(LoggerProxy.info).toHaveBeenCalledWith('Skipping campaign preview contact', {
|
|
3723
|
+
module: CC_FILE,
|
|
3724
|
+
method: 'skipPreviewContact',
|
|
3725
|
+
});
|
|
3726
|
+
expect(LoggerProxy.log).toHaveBeenCalledWith(
|
|
3727
|
+
'Campaign preview contact skipped successfully',
|
|
3728
|
+
{
|
|
3729
|
+
module: CC_FILE,
|
|
3730
|
+
method: 'skipPreviewContact',
|
|
3731
|
+
trackingId: 'track-123',
|
|
3732
|
+
interactionId: previewPayload.interactionId,
|
|
3733
|
+
}
|
|
3734
|
+
);
|
|
3735
|
+
|
|
3736
|
+
expect(skipPreviewContactMock).toHaveBeenCalledWith({data: previewPayload});
|
|
3737
|
+
expect(result).toEqual(mockResponse);
|
|
3738
|
+
});
|
|
3739
|
+
|
|
3740
|
+
it('should handle error during skipPreviewContact', async () => {
|
|
3741
|
+
getErrorDetailsSpy.mockRestore();
|
|
3742
|
+
getErrorDetailsSpy = jest.spyOn(Utils, 'getErrorDetails');
|
|
3743
|
+
|
|
3744
|
+
const error = {
|
|
3745
|
+
details: {
|
|
3746
|
+
trackingId: '1234',
|
|
3747
|
+
data: {
|
|
3748
|
+
reason: 'Error while performing skipPreviewContact',
|
|
3749
|
+
},
|
|
3750
|
+
},
|
|
3751
|
+
};
|
|
3752
|
+
|
|
3753
|
+
jest.spyOn(webex.cc.services.dialer, 'skipPreviewContact').mockRejectedValue(error);
|
|
3754
|
+
|
|
3755
|
+
await expect(webex.cc.skipPreviewContact(previewPayload)).rejects.toThrow(
|
|
3756
|
+
error.details.data.reason
|
|
3757
|
+
);
|
|
3758
|
+
|
|
3759
|
+
expect(LoggerProxy.info).toHaveBeenCalledWith('Skipping campaign preview contact', {
|
|
3760
|
+
module: CC_FILE,
|
|
3761
|
+
method: 'skipPreviewContact',
|
|
3762
|
+
});
|
|
3763
|
+
expect(LoggerProxy.error).toHaveBeenCalledWith(
|
|
3764
|
+
`skipPreviewContact failed with reason: ${error.details.data.reason}`,
|
|
3765
|
+
{module: CC_FILE, method: 'skipPreviewContact', trackingId: error.details.trackingId}
|
|
3766
|
+
);
|
|
3767
|
+
expect(getErrorDetailsSpy).toHaveBeenCalledWith(error, 'skipPreviewContact', CC_FILE);
|
|
3768
|
+
});
|
|
3769
|
+
});
|
|
3770
|
+
|
|
3771
|
+
describe('removePreviewContact', () => {
|
|
3772
|
+
const previewPayload = {
|
|
3773
|
+
interactionId: 'interaction-123',
|
|
3774
|
+
campaignId: 'campaign-456',
|
|
3775
|
+
};
|
|
3776
|
+
|
|
3777
|
+
it('should remove preview contact successfully', async () => {
|
|
3778
|
+
const mockResponse = {trackingId: 'track-123'} as AgentContact;
|
|
3779
|
+
|
|
3780
|
+
const removePreviewContactMock = jest
|
|
3781
|
+
.spyOn(webex.cc.services.dialer, 'removePreviewContact')
|
|
3782
|
+
.mockResolvedValue(mockResponse);
|
|
3783
|
+
|
|
3784
|
+
const result = await webex.cc.removePreviewContact(previewPayload);
|
|
3785
|
+
|
|
3786
|
+
expect(LoggerProxy.info).toHaveBeenCalledWith('Removing campaign preview contact', {
|
|
3787
|
+
module: CC_FILE,
|
|
3788
|
+
method: 'removePreviewContact',
|
|
3789
|
+
});
|
|
3790
|
+
expect(LoggerProxy.log).toHaveBeenCalledWith(
|
|
3791
|
+
'Campaign preview contact removed successfully',
|
|
3792
|
+
{
|
|
3793
|
+
module: CC_FILE,
|
|
3794
|
+
method: 'removePreviewContact',
|
|
3795
|
+
trackingId: 'track-123',
|
|
3796
|
+
interactionId: previewPayload.interactionId,
|
|
3797
|
+
}
|
|
3798
|
+
);
|
|
3799
|
+
|
|
3800
|
+
expect(removePreviewContactMock).toHaveBeenCalledWith({data: previewPayload});
|
|
3801
|
+
expect(result).toEqual(mockResponse);
|
|
3802
|
+
});
|
|
3803
|
+
|
|
3804
|
+
it('should handle error during removePreviewContact', async () => {
|
|
3805
|
+
getErrorDetailsSpy.mockRestore();
|
|
3806
|
+
getErrorDetailsSpy = jest.spyOn(Utils, 'getErrorDetails');
|
|
3807
|
+
|
|
3808
|
+
const error = {
|
|
3809
|
+
details: {
|
|
3810
|
+
trackingId: '1234',
|
|
3811
|
+
data: {
|
|
3812
|
+
reason: 'Error while performing removePreviewContact',
|
|
3813
|
+
},
|
|
3814
|
+
},
|
|
3815
|
+
};
|
|
3816
|
+
|
|
3817
|
+
jest.spyOn(webex.cc.services.dialer, 'removePreviewContact').mockRejectedValue(error);
|
|
3818
|
+
|
|
3819
|
+
await expect(webex.cc.removePreviewContact(previewPayload)).rejects.toThrow(
|
|
3820
|
+
error.details.data.reason
|
|
3821
|
+
);
|
|
3822
|
+
|
|
3823
|
+
expect(LoggerProxy.info).toHaveBeenCalledWith('Removing campaign preview contact', {
|
|
3824
|
+
module: CC_FILE,
|
|
3825
|
+
method: 'removePreviewContact',
|
|
3826
|
+
});
|
|
3827
|
+
expect(LoggerProxy.error).toHaveBeenCalledWith(
|
|
3828
|
+
`removePreviewContact failed with reason: ${error.details.data.reason}`,
|
|
3829
|
+
{module: CC_FILE, method: 'removePreviewContact', trackingId: error.details.trackingId}
|
|
3830
|
+
);
|
|
3831
|
+
expect(getErrorDetailsSpy).toHaveBeenCalledWith(error, 'removePreviewContact', CC_FILE);
|
|
3832
|
+
});
|
|
3833
|
+
});
|
|
3834
|
+
|
|
3835
|
+
describe('private setManageWebexCallingInWxcc (runtime toggle impl)', () => {
|
|
3836
|
+
beforeEach(() => {
|
|
3837
|
+
webex.internal.device = {
|
|
3838
|
+
userId: 'user-123',
|
|
3839
|
+
registered: false,
|
|
3840
|
+
register: jest.fn().mockResolvedValue(undefined),
|
|
3841
|
+
unregister: jest.fn().mockResolvedValue(undefined),
|
|
3842
|
+
};
|
|
3843
|
+
webex.internal.mercury = {
|
|
3844
|
+
connected: false,
|
|
3845
|
+
connect: jest.fn().mockResolvedValue(undefined),
|
|
3846
|
+
disconnect: jest.fn().mockResolvedValue(undefined),
|
|
3847
|
+
};
|
|
3848
|
+
jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'subscribe').mockImplementation(() => {});
|
|
3849
|
+
jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'isSubscribed').mockReturnValue(true);
|
|
3850
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: false};
|
|
3851
|
+
webex.cc['agentConfig'] = {
|
|
3852
|
+
agentId: 'agent-123',
|
|
3853
|
+
deviceType: LoginOption.EXTENSION,
|
|
3854
|
+
} as Profile;
|
|
3855
|
+
webex.cc.webCallingService.loginOption = LoginOption.EXTENSION;
|
|
3856
|
+
webex.cc.webCallingService.setLoginOption = jest.fn((loginOption: LoginOption) => {
|
|
3857
|
+
webex.cc.webCallingService.loginOption = loginOption;
|
|
3858
|
+
});
|
|
3859
|
+
});
|
|
3860
|
+
|
|
3861
|
+
afterEach(() => {
|
|
3862
|
+
jest.restoreAllMocks();
|
|
3863
|
+
});
|
|
3864
|
+
|
|
3865
|
+
it('should update config, task manager, and publish usersub when enabled', async () => {
|
|
3866
|
+
const publishSpy = jest
|
|
3867
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
3868
|
+
.mockResolvedValue(undefined);
|
|
3869
|
+
|
|
3870
|
+
await (webex.cc as any).setManageWebexCallingInWxcc(true);
|
|
3871
|
+
|
|
3872
|
+
expect(webex.cc.isWxBetterTogetherEnabled()).toBe(true);
|
|
3873
|
+
expect(mockTaskManager.applyEnableWxBetterTogether).toHaveBeenCalledWith(true);
|
|
3874
|
+
expect(publishSpy).toHaveBeenCalledWith(true, {userId: 'user-123', trackPublishMetrics: true});
|
|
3875
|
+
});
|
|
3876
|
+
|
|
3877
|
+
it('should publish false and update config when disabled after wxApp was enabled', async () => {
|
|
3878
|
+
webex.internal.device = {
|
|
3879
|
+
userId: 'user-123',
|
|
3880
|
+
url: 'https://wdm.example.com/devices/dev-1',
|
|
3881
|
+
};
|
|
3882
|
+
webex.request = jest.fn().mockResolvedValue({body: {}});
|
|
3883
|
+
|
|
3884
|
+
await (webex.cc as any).setManageWebexCallingInWxcc(true);
|
|
3885
|
+
|
|
3886
|
+
const publishSpy = jest
|
|
3887
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
3888
|
+
.mockResolvedValue(undefined);
|
|
3889
|
+
|
|
3890
|
+
await (webex.cc as any).setManageWebexCallingInWxcc(false);
|
|
3891
|
+
|
|
3892
|
+
expect(webex.cc.isWxBetterTogetherEnabled()).toBe(false);
|
|
3893
|
+
expect(mockTaskManager.applyEnableWxBetterTogether).toHaveBeenCalledWith(false);
|
|
3894
|
+
expect(publishSpy).toHaveBeenCalledWith(false, {userId: 'user-123', trackPublishMetrics: true});
|
|
3895
|
+
});
|
|
3896
|
+
|
|
3897
|
+
it('should skip usersub publish when disabling without prior wxApp publish', async () => {
|
|
3898
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: true};
|
|
3899
|
+
const publishSpy = jest
|
|
3900
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
3901
|
+
.mockResolvedValue(undefined);
|
|
3902
|
+
|
|
3903
|
+
await (webex.cc as any).setManageWebexCallingInWxcc(false);
|
|
3904
|
+
|
|
3905
|
+
expect(webex.cc.isWxBetterTogetherEnabled()).toBe(false);
|
|
3906
|
+
expect(mockTaskManager.applyEnableWxBetterTogether).toHaveBeenCalledWith(false);
|
|
3907
|
+
expect(publishSpy).not.toHaveBeenCalled();
|
|
3908
|
+
});
|
|
3909
|
+
|
|
3910
|
+
it('should throw when enabling before login', async () => {
|
|
3911
|
+
webex.internal.device = undefined;
|
|
3912
|
+
|
|
3913
|
+
await expect((webex.cc as any).setManageWebexCallingInWxcc(true)).rejects.toThrow(
|
|
3914
|
+
'Cannot publish answer-calls-on-wxcc: user is not logged in'
|
|
3915
|
+
);
|
|
3916
|
+
expect(webex.cc.isWxBetterTogetherEnabled()).toBe(false);
|
|
3917
|
+
expect(mockTaskManager.applyEnableWxBetterTogether).not.toHaveBeenCalled();
|
|
3918
|
+
});
|
|
3919
|
+
|
|
3920
|
+
it('should throw when enabling on BROWSER (Desktop) login', async () => {
|
|
3921
|
+
webex.cc['agentConfig'] = {deviceType: LoginOption.BROWSER} as Profile;
|
|
3922
|
+
webex.cc.webCallingService.loginOption = LoginOption.BROWSER;
|
|
3923
|
+
const publishSpy = jest
|
|
3924
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
3925
|
+
.mockResolvedValue(undefined);
|
|
3926
|
+
|
|
3927
|
+
await expect((webex.cc as any).setManageWebexCallingInWxcc(true)).rejects.toThrow(
|
|
3928
|
+
'setManageWebexCallingInWxcc requires EXTENSION or AGENT_DN station login'
|
|
3929
|
+
);
|
|
3930
|
+
expect(webex.cc.isWxBetterTogetherEnabled()).toBe(false);
|
|
3931
|
+
expect(mockTaskManager.applyEnableWxBetterTogether).not.toHaveBeenCalled();
|
|
3932
|
+
expect(publishSpy).not.toHaveBeenCalled();
|
|
3933
|
+
});
|
|
3934
|
+
|
|
3935
|
+
it('should throw when enabling before station login', async () => {
|
|
3936
|
+
webex.cc['agentConfig'] = undefined as unknown as Profile;
|
|
3937
|
+
webex.cc.webCallingService.loginOption = undefined as unknown as LoginOption;
|
|
3938
|
+
const publishSpy = jest
|
|
3939
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
3940
|
+
.mockResolvedValue(undefined);
|
|
3941
|
+
|
|
3942
|
+
await expect((webex.cc as any).setManageWebexCallingInWxcc(true)).rejects.toThrow(
|
|
3943
|
+
'setManageWebexCallingInWxcc requires EXTENSION or AGENT_DN station login'
|
|
3944
|
+
);
|
|
3945
|
+
expect(webex.cc.isWxBetterTogetherEnabled()).toBe(false);
|
|
3946
|
+
expect(mockTaskManager.applyEnableWxBetterTogether).not.toHaveBeenCalled();
|
|
3947
|
+
expect(publishSpy).not.toHaveBeenCalled();
|
|
3948
|
+
});
|
|
3949
|
+
|
|
3950
|
+
it('should throw when webCallingService is BROWSER even if agentConfig deviceType is stale', async () => {
|
|
3951
|
+
webex.cc['agentConfig'] = {deviceType: LoginOption.EXTENSION} as Profile;
|
|
3952
|
+
webex.cc.webCallingService.loginOption = LoginOption.BROWSER;
|
|
3953
|
+
const publishSpy = jest
|
|
3954
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
3955
|
+
.mockResolvedValue(undefined);
|
|
3956
|
+
|
|
3957
|
+
await expect((webex.cc as any).setManageWebexCallingInWxcc(true)).rejects.toThrow(
|
|
3958
|
+
'setManageWebexCallingInWxcc requires EXTENSION or AGENT_DN station login'
|
|
3959
|
+
);
|
|
3960
|
+
expect(publishSpy).not.toHaveBeenCalled();
|
|
3961
|
+
});
|
|
3962
|
+
|
|
3963
|
+
it('should publish usersub when enabling on AGENT_DN login', async () => {
|
|
3964
|
+
webex.cc['agentConfig'] = {
|
|
3965
|
+
agentId: 'agent-123',
|
|
3966
|
+
deviceType: LoginOption.AGENT_DN,
|
|
3967
|
+
} as Profile;
|
|
3968
|
+
const publishSpy = jest
|
|
3969
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
3970
|
+
.mockResolvedValue(undefined);
|
|
3971
|
+
|
|
3972
|
+
await (webex.cc as any).setManageWebexCallingInWxcc(true);
|
|
3973
|
+
|
|
3974
|
+
expect(webex.cc.isWxBetterTogetherEnabled()).toBe(true);
|
|
3975
|
+
expect(publishSpy).toHaveBeenCalledWith(true, {userId: 'user-123', trackPublishMetrics: true});
|
|
3976
|
+
});
|
|
3977
|
+
|
|
3978
|
+
it('should no-op usersub publish when disabling before login', async () => {
|
|
3979
|
+
webex.internal.device = undefined;
|
|
3980
|
+
const publishSpy = jest
|
|
3981
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
3982
|
+
.mockResolvedValue(undefined);
|
|
3983
|
+
|
|
3984
|
+
await (webex.cc as any).setManageWebexCallingInWxcc(false);
|
|
3985
|
+
|
|
3986
|
+
expect(webex.cc.isWxBetterTogetherEnabled()).toBe(false);
|
|
3987
|
+
expect(mockTaskManager.applyEnableWxBetterTogether).toHaveBeenCalledWith(false);
|
|
3988
|
+
expect(publishSpy).not.toHaveBeenCalled();
|
|
3989
|
+
});
|
|
3990
|
+
|
|
3991
|
+
it('should subscribe to wxApp Mercury mute sync when enabled', async () => {
|
|
3992
|
+
webex.cc['agentConfig'] = {agentId: 'agent-123'} as Profile;
|
|
3993
|
+
webex.internal.device = {
|
|
3994
|
+
...webex.internal.device,
|
|
3995
|
+
registered: true,
|
|
3996
|
+
register: jest.fn().mockResolvedValue(undefined),
|
|
3997
|
+
};
|
|
3998
|
+
webex.internal.mercury.connected = true;
|
|
3999
|
+
webex.internal.mercury.connect = jest.fn().mockResolvedValue(undefined);
|
|
4000
|
+
jest
|
|
4001
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
4002
|
+
.mockResolvedValue(undefined);
|
|
4003
|
+
const subscribeSpy = jest
|
|
4004
|
+
.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'subscribe')
|
|
4005
|
+
.mockImplementation(() => {});
|
|
4006
|
+
jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'isSubscribed').mockReturnValue(true);
|
|
4007
|
+
|
|
4008
|
+
await (webex.cc as any).setManageWebexCallingInWxcc(true);
|
|
4009
|
+
|
|
4010
|
+
expect(subscribeSpy).toHaveBeenCalledWith('agent-123', expect.any(Function));
|
|
4011
|
+
});
|
|
4012
|
+
|
|
4013
|
+
it('should unsubscribe from wxApp Mercury mute sync when disabled', async () => {
|
|
4014
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: true};
|
|
4015
|
+
jest
|
|
4016
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
4017
|
+
.mockResolvedValue(undefined);
|
|
4018
|
+
const unsubscribeSpy = jest
|
|
4019
|
+
.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'unsubscribe')
|
|
4020
|
+
.mockImplementation(() => {});
|
|
4021
|
+
jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'isSubscribed').mockReturnValue(true);
|
|
4022
|
+
|
|
4023
|
+
await (webex.cc as any).setManageWebexCallingInWxcc(false);
|
|
4024
|
+
|
|
4025
|
+
expect(unsubscribeSpy).toHaveBeenCalled();
|
|
4026
|
+
});
|
|
4027
|
+
|
|
4028
|
+
it('should register device and connect mercury when enabling wxApp sync', async () => {
|
|
4029
|
+
webex.cc['agentConfig'] = {agentId: 'agent-123'} as Profile;
|
|
4030
|
+
webex.internal.device = {
|
|
4031
|
+
userId: 'user-123',
|
|
4032
|
+
registered: false,
|
|
4033
|
+
register: jest.fn().mockResolvedValue(undefined),
|
|
4034
|
+
};
|
|
4035
|
+
webex.internal.mercury = {
|
|
4036
|
+
connected: false,
|
|
4037
|
+
connect: jest.fn().mockResolvedValue(undefined),
|
|
4038
|
+
};
|
|
4039
|
+
jest
|
|
4040
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
4041
|
+
.mockResolvedValue(undefined);
|
|
4042
|
+
const subscribeSpy = jest
|
|
4043
|
+
.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'subscribe')
|
|
4044
|
+
.mockImplementation(() => {});
|
|
4045
|
+
jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'isSubscribed').mockReturnValue(true);
|
|
4046
|
+
|
|
4047
|
+
await (webex.cc as any).setManageWebexCallingInWxcc(true);
|
|
4048
|
+
|
|
4049
|
+
expect(webex.internal.device.register).toHaveBeenCalled();
|
|
4050
|
+
expect(webex.internal.mercury.connect).toHaveBeenCalled();
|
|
4051
|
+
expect(subscribeSpy).toHaveBeenCalledWith('agent-123', expect.any(Function));
|
|
4052
|
+
});
|
|
4053
|
+
|
|
4054
|
+
it('should rollback config and task manager when usersub publish fails', async () => {
|
|
4055
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: false};
|
|
4056
|
+
jest
|
|
4057
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
4058
|
+
.mockRejectedValue(new Error('usersub failed'));
|
|
4059
|
+
|
|
4060
|
+
await expect((webex.cc as any).setManageWebexCallingInWxcc(true)).rejects.toThrow('usersub failed');
|
|
4061
|
+
|
|
4062
|
+
expect(webex.cc.isWxBetterTogetherEnabled()).toBe(false);
|
|
4063
|
+
expect(mockTaskManager.applyEnableWxBetterTogether).toHaveBeenCalledWith(true);
|
|
4064
|
+
expect(mockTaskManager.applyEnableWxBetterTogether).toHaveBeenLastCalledWith(false);
|
|
4065
|
+
});
|
|
4066
|
+
|
|
4067
|
+
it('should rollback config and release Mercury resources when Mercury connect fails', async () => {
|
|
4068
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: false};
|
|
4069
|
+
const publishSpy = jest
|
|
4070
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
4071
|
+
.mockResolvedValue(undefined);
|
|
4072
|
+
jest
|
|
4073
|
+
.spyOn(webex.cc as any, 'ensureWxAppMercuryConnected')
|
|
4074
|
+
.mockRejectedValue(new Error('mercury connect failed'));
|
|
4075
|
+
const releaseSpy = jest
|
|
4076
|
+
.spyOn(webex.cc as any, 'releaseWxAppMercuryResources')
|
|
4077
|
+
.mockResolvedValue(undefined);
|
|
4078
|
+
const unsubscribeSpy = jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'unsubscribe');
|
|
4079
|
+
const teardownSpy = jest.spyOn(webex.cc['webexCrossClientService'], 'teardown');
|
|
4080
|
+
|
|
4081
|
+
await expect((webex.cc as any).setManageWebexCallingInWxcc(true)).rejects.toThrow(
|
|
4082
|
+
'mercury connect failed'
|
|
4083
|
+
);
|
|
4084
|
+
|
|
4085
|
+
expect(webex.cc.isWxBetterTogetherEnabled()).toBe(false);
|
|
4086
|
+
expect(mockTaskManager.applyEnableWxBetterTogether).toHaveBeenLastCalledWith(false);
|
|
4087
|
+
expect(publishSpy).not.toHaveBeenCalled();
|
|
4088
|
+
expect(teardownSpy).not.toHaveBeenCalled();
|
|
4089
|
+
expect(unsubscribeSpy).toHaveBeenCalled();
|
|
4090
|
+
expect(releaseSpy).toHaveBeenCalled();
|
|
4091
|
+
});
|
|
4092
|
+
|
|
4093
|
+
it('should schedule compensating false retry when rollback publish fails after enable error', async () => {
|
|
4094
|
+
jest.useFakeTimers();
|
|
4095
|
+
try {
|
|
4096
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: true};
|
|
4097
|
+
webex.internal.device = {userId: 'user-123', url: 'https://wdm.example.com/devices/dev-1'};
|
|
4098
|
+
webex.cc.agentConfig = {
|
|
4099
|
+
agentId: 'agent-123',
|
|
4100
|
+
deviceType: LoginOption.EXTENSION,
|
|
4101
|
+
};
|
|
4102
|
+
webex.cc.webCallingService.loginOption = LoginOption.EXTENSION;
|
|
4103
|
+
|
|
4104
|
+
const publishSpy = jest
|
|
4105
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
4106
|
+
.mockResolvedValueOnce(undefined)
|
|
4107
|
+
.mockRejectedValueOnce(new Error('compensating false failed'))
|
|
4108
|
+
.mockResolvedValue(undefined);
|
|
4109
|
+
jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'subscribe').mockImplementation(() => {});
|
|
4110
|
+
jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'isSubscribed').mockReturnValue(true);
|
|
4111
|
+
jest
|
|
4112
|
+
.spyOn(webex.cc as any, 'ensureWxAppMercuryConnected')
|
|
4113
|
+
.mockResolvedValue(undefined);
|
|
4114
|
+
jest
|
|
4115
|
+
.spyOn(webex.cc as any, 'releaseWxAppMercuryResources')
|
|
4116
|
+
.mockResolvedValue(undefined);
|
|
4117
|
+
jest
|
|
4118
|
+
.spyOn(mockTaskManager, 'syncWxAppMuteFromCallDetailsForAllTasks')
|
|
4119
|
+
.mockImplementation(() => {
|
|
4120
|
+
throw new Error('sync failed');
|
|
4121
|
+
});
|
|
4122
|
+
const scheduleRetrySpy = jest.spyOn(
|
|
4123
|
+
webex.cc as any,
|
|
4124
|
+
'scheduleCompensatingFalsePublishRetry'
|
|
4125
|
+
);
|
|
4126
|
+
|
|
4127
|
+
await expect((webex.cc as any).setManageWebexCallingInWxcc(true)).rejects.toThrow(
|
|
4128
|
+
'sync failed'
|
|
4129
|
+
);
|
|
4130
|
+
|
|
4131
|
+
expect(scheduleRetrySpy).toHaveBeenCalled();
|
|
4132
|
+
expect(publishSpy).toHaveBeenCalledTimes(2);
|
|
4133
|
+
expect(publishSpy).toHaveBeenNthCalledWith(1, true, {userId: 'user-123', trackPublishMetrics: true});
|
|
4134
|
+
expect(publishSpy).toHaveBeenNthCalledWith(2, false, {userId: 'user-123', trackPublishMetrics: true});
|
|
4135
|
+
|
|
4136
|
+
jest.advanceTimersByTime(30_000);
|
|
4137
|
+
await publishSpy.mock.results[2]?.value;
|
|
4138
|
+
|
|
4139
|
+
expect(publishSpy).toHaveBeenCalledTimes(3);
|
|
4140
|
+
expect(publishSpy).toHaveBeenLastCalledWith(false, {userId: 'user-123', trackPublishMetrics: true});
|
|
4141
|
+
} finally {
|
|
4142
|
+
jest.useRealTimers();
|
|
4143
|
+
}
|
|
4144
|
+
});
|
|
4145
|
+
});
|
|
2323
4146
|
});
|