@webex/contact-center 3.11.0 → 3.12.0-auth-prejoin-fetch.1
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 +883 -0
- package/AGENTS.md +94 -0
- package/ai-docs/ARCHITECTURE.md +168 -0
- package/ai-docs/CONTRACTS.md +50 -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 +818 -59
- package/dist/cc.js.map +1 -1
- package/dist/config.js +13 -0
- package/dist/config.js.map +1 -1
- package/dist/constants.js +31 -3
- package/dist/constants.js.map +1 -1
- package/dist/index.js +27 -5
- package/dist/index.js.map +1 -1
- package/dist/metrics/behavioral-events.js +127 -0
- package/dist/metrics/behavioral-events.js.map +1 -1
- package/dist/metrics/constants.js +34 -3
- 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 +174 -0
- package/dist/services/AnswerCallOnWebexService.js.map +1 -0
- package/dist/services/ApiAiAssistant.js +318 -0
- package/dist/services/ApiAiAssistant.js.map +1 -0
- 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 +171 -0
- package/dist/services/WebexCrossClientService.js.map +1 -0
- package/dist/services/WxAppTelephonyMercurySync.js +93 -0
- package/dist/services/WxAppTelephonyMercurySync.js.map +1 -0
- package/dist/services/agent/types.js.map +1 -1
- package/dist/services/config/Util.js +11 -4
- package/dist/services/config/Util.js.map +1 -1
- package/dist/services/config/constants.js +45 -8
- package/dist/services/config/constants.js.map +1 -1
- package/dist/services/config/index.js +41 -2
- package/dist/services/config/index.js.map +1 -1
- package/dist/services/config/types.js +70 -8
- package/dist/services/config/types.js.map +1 -1
- package/dist/services/constants.js +27 -1
- package/dist/services/constants.js.map +1 -1
- package/dist/services/core/Err.js.map +1 -1
- package/dist/services/core/Utils.js +122 -25
- 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 +119 -30
- 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 +22 -6
- package/dist/services/core/websocket/WebSocketManager.js.map +1 -1
- package/dist/services/core/websocket/connection-service.js +3 -1
- package/dist/services/core/websocket/connection-service.js.map +1 -1
- package/dist/services/core/websocket/types.js.map +1 -1
- package/dist/services/index.js +6 -0
- 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 +1073 -447
- package/dist/services/task/TaskManager.js.map +1 -1
- package/dist/services/task/TaskUtils.js +220 -23
- 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 +26 -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 +129 -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 +971 -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 +409 -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 +243 -5
- package/dist/services/task/types.js.map +1 -1
- package/dist/services/task/voice/Voice.js +1380 -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 +201 -0
- package/dist/services/task/voice/wxAppVoiceMethods.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 +967 -0
- package/dist/types/config.d.ts +79 -0
- package/dist/types/constants.d.ts +74 -0
- package/dist/types/index.d.ts +201 -0
- package/dist/types/logger-proxy.d.ts +71 -0
- package/dist/types/metrics/MetricsManager.d.ts +223 -0
- package/dist/types/metrics/behavioral-events.d.ts +29 -0
- package/dist/types/metrics/constants.d.ts +183 -0
- package/dist/types/services/AddressBook.d.ts +75 -0
- package/dist/types/services/AnswerCallOnWebexService.d.ts +37 -0
- package/dist/types/services/ApiAiAssistant.d.ts +49 -0
- package/dist/types/services/EntryPoint.d.ts +69 -0
- package/dist/types/services/Queue.d.ts +78 -0
- package/dist/types/services/UserPreference.d.ts +118 -0
- package/dist/types/services/WebCallingService.d.ts +1 -0
- package/dist/types/services/WebexCrossClientService.d.ts +28 -0
- package/dist/types/services/WxAppTelephonyMercurySync.d.ts +28 -0
- package/dist/types/services/agent/index.d.ts +46 -0
- package/dist/types/services/agent/types.d.ts +413 -0
- package/dist/types/services/config/Util.d.ts +20 -0
- package/dist/types/services/config/constants.d.ts +273 -0
- package/dist/types/services/config/index.d.ts +177 -0
- package/dist/types/services/config/types.d.ts +1381 -0
- package/dist/types/services/constants.d.ts +110 -0
- package/dist/types/services/core/Err.d.ts +127 -0
- package/dist/types/services/core/GlobalTypes.d.ts +58 -0
- package/dist/types/services/core/Utils.d.ts +121 -0
- package/dist/types/services/core/WebexRequest.d.ts +23 -0
- package/dist/types/services/core/aqm-reqs.d.ts +65 -0
- package/dist/types/services/core/constants.d.ts +99 -0
- package/dist/types/services/core/types.d.ts +49 -0
- package/dist/types/services/core/websocket/WebSocketManager.d.ts +36 -0
- package/dist/types/services/core/websocket/connection-service.d.ts +27 -0
- package/dist/types/services/core/websocket/keepalive.worker.d.ts +2 -0
- package/dist/types/services/core/websocket/types.d.ts +37 -0
- package/dist/types/services/index.d.ts +54 -0
- package/dist/types/services/task/AutoWrapup.d.ts +40 -0
- 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/TaskManager.d.ts +1 -0
- package/dist/types/services/task/TaskUtils.d.ts +138 -0
- package/dist/types/services/task/WebexCallingUtils.d.ts +11 -0
- package/dist/types/services/task/constants.d.ts +94 -0
- package/dist/types/services/task/contact.d.ts +73 -0
- package/dist/types/services/task/dialer.d.ts +73 -0
- package/dist/types/services/task/digital/Digital.d.ts +22 -0
- package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +1398 -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 +1933 -0
- package/dist/types/services/task/voice/Voice.d.ts +223 -0
- package/dist/types/services/task/voice/WebRTC.d.ts +53 -0
- package/dist/types/services/task/voice/wxAppVoiceMethods.d.ts +52 -0
- package/dist/types/services/wxAppTelephonyUtils.d.ts +5 -0
- package/dist/types/types.d.ts +784 -0
- package/dist/types/utils/PageCache.d.ts +190 -0
- package/dist/types/webex-config.d.ts +53 -0
- package/dist/types/webex.d.ts +8 -0
- package/dist/types.js +137 -3
- 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 +983 -60
- package/src/config.ts +13 -0
- package/src/constants.ts +29 -1
- package/src/index.ts +26 -5
- 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 +860 -0
- package/src/metrics/behavioral-events.ts +134 -0
- package/src/metrics/constants.ts +39 -3
- package/src/services/AddressBook.ts +17 -6
- package/src/services/AnswerCallOnWebexService.ts +206 -0
- package/src/services/ApiAiAssistant.ts +412 -0
- 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 +212 -0
- package/src/services/WxAppTelephonyMercurySync.ts +115 -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 +504 -0
- package/src/services/agent/types.ts +1 -1
- 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 +13 -2
- 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 +47 -7
- package/src/services/config/index.ts +45 -1
- package/src/services/config/types.ts +253 -11
- package/src/services/constants.ts +29 -0
- package/src/services/core/Err.ts +4 -0
- package/src/services/core/Utils.ts +143 -30
- 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 +125 -32
- package/src/services/core/types.ts +2 -0
- package/src/services/core/websocket/WebSocketManager.ts +23 -6
- package/src/services/core/websocket/connection-service.ts +5 -1
- package/src/services/core/websocket/types.ts +1 -1
- package/src/services/index.ts +4 -0
- package/src/services/task/Task.ts +908 -0
- package/src/services/task/TaskFactory.ts +60 -0
- package/src/services/task/TaskManager.ts +1291 -513
- package/src/services/task/TaskUtils.ts +314 -24
- 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 +1469 -0
- package/src/services/task/constants.ts +26 -0
- package/src/services/task/contact.ts +30 -0
- package/src/services/task/dialer.ts +136 -1
- package/src/services/task/digital/Digital.ts +97 -0
- package/src/services/task/state-machine/TaskStateMachine.ts +1313 -0
- package/src/services/task/state-machine/actions.ts +741 -0
- package/src/services/task/state-machine/ai-docs/AGENTS.md +462 -0
- package/src/services/task/state-machine/ai-docs/ARCHITECTURE.md +1146 -0
- package/src/services/task/state-machine/ai-docs/task-state-machine-spec.md +2209 -0
- package/src/services/task/state-machine/constants.ts +172 -0
- package/src/services/task/state-machine/guards.ts +498 -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 +843 -70
- package/src/services/task/voice/Voice.ts +1720 -0
- package/src/services/task/voice/WebRTC.ts +191 -0
- package/src/services/task/voice/wxAppVoiceMethods.ts +307 -0
- package/src/services/wxAppTelephonyUtils.ts +14 -0
- package/src/types.ts +238 -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 +1856 -122
- package/test/unit/spec/logger-proxy.ts +70 -0
- package/test/unit/spec/metrics/behavioral-events.ts +18 -0
- package/test/unit/spec/services/AddressBook.ts +37 -6
- package/test/unit/spec/services/AnswerCallOnWebexService.ts +223 -0
- package/test/unit/spec/services/ApiAiAssistant.ts +273 -0
- 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 +261 -0
- package/test/unit/spec/services/WxAppTelephonyMercurySync.ts +113 -0
- package/test/unit/spec/services/config/Util.ts +85 -0
- package/test/unit/spec/services/config/index.ts +85 -29
- package/test/unit/spec/services/core/Utils.ts +481 -2
- 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 +137 -41
- package/test/unit/spec/services/core/websocket/connection-service.ts +3 -1
- 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 +2209 -918
- 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 +372 -96
- package/test/unit/spec/services/task/digital/Digital.ts +105 -0
- package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +3433 -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 +1523 -0
- package/test/unit/spec/services/task/voice/WebRTC.ts +235 -0
- package/test/unit/spec/services/task/voice/wxAppVoiceMethods.ts +459 -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/src/services/task/index.ts +0 -1801
- package/test/unit/spec/services/task/index.ts +0 -2184
package/test/unit/spec/cc.ts
CHANGED
|
@@ -15,7 +15,7 @@ import type {ContactServiceQueuesResponse} from '../../../src/types';
|
|
|
15
15
|
import MockWebex from '@webex/test-helper-mock-webex';
|
|
16
16
|
import {StationLoginSuccess, AGENT_EVENTS} from '../../../src/services/agent/types';
|
|
17
17
|
import {SetStateResponse} from '../../../src/types';
|
|
18
|
-
import {AGENT, WEB_RTC_PREFIX} from '../../../src/services/constants';
|
|
18
|
+
import {AGENT, SUBSCRIBE_API, WEB_RTC_PREFIX} from '../../../src/services/constants';
|
|
19
19
|
import Services from '../../../src/services';
|
|
20
20
|
import config from '../../../src/config';
|
|
21
21
|
import {CC_EVENTS} from '../../../src/services/config/types';
|
|
@@ -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;
|
|
@@ -88,6 +89,8 @@ describe('webex.cc', () => {
|
|
|
88
89
|
initWebSocket: jest.fn(),
|
|
89
90
|
on: jest.fn(),
|
|
90
91
|
off: jest.fn(),
|
|
92
|
+
close: jest.fn(),
|
|
93
|
+
isSocketClosed: false,
|
|
91
94
|
};
|
|
92
95
|
|
|
93
96
|
mockContact = {
|
|
@@ -107,6 +110,14 @@ describe('webex.cc', () => {
|
|
|
107
110
|
cancelCtq: jest.fn(),
|
|
108
111
|
};
|
|
109
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
|
+
|
|
110
121
|
// Mock Services instance
|
|
111
122
|
const mockServicesInstance = {
|
|
112
123
|
agent: {
|
|
@@ -121,6 +132,13 @@ describe('webex.cc', () => {
|
|
|
121
132
|
getOutdialAniEntries: jest.fn(),
|
|
122
133
|
},
|
|
123
134
|
webSocketManager: mockWebSocketManager,
|
|
135
|
+
rtdWebSocketManager: {
|
|
136
|
+
initWebSocket: jest.fn().mockResolvedValue({}),
|
|
137
|
+
on: jest.fn(),
|
|
138
|
+
off: jest.fn(),
|
|
139
|
+
close: jest.fn(),
|
|
140
|
+
isSocketClosed: false,
|
|
141
|
+
},
|
|
124
142
|
connectionService: {
|
|
125
143
|
on: jest.fn(),
|
|
126
144
|
off: jest.fn(),
|
|
@@ -129,23 +147,35 @@ describe('webex.cc', () => {
|
|
|
129
147
|
|
|
130
148
|
dialer: {
|
|
131
149
|
startOutdial: jest.fn(),
|
|
150
|
+
acceptPreviewContact: jest.fn(),
|
|
151
|
+
skipPreviewContact: jest.fn(),
|
|
152
|
+
removePreviewContact: jest.fn(),
|
|
153
|
+
},
|
|
154
|
+
apiAIAssistant: {
|
|
155
|
+
sendEvent: jest.fn(),
|
|
156
|
+
fetchHistoricTranscripts: jest.fn(),
|
|
132
157
|
},
|
|
133
158
|
};
|
|
134
159
|
|
|
135
160
|
mockTaskManager = {
|
|
161
|
+
apiAIAssistant: mockApiAIAssistant,
|
|
136
162
|
contact: mockContact,
|
|
137
163
|
call: undefined,
|
|
138
164
|
taskCollection: {},
|
|
139
165
|
webCallingService: undefined,
|
|
140
166
|
webSocketManager: mockWebSocketManager,
|
|
141
167
|
task: undefined,
|
|
168
|
+
setConfigFlags: jest.fn(),
|
|
142
169
|
setWrapupData: jest.fn(),
|
|
143
170
|
setAgentId: jest.fn(),
|
|
144
171
|
setWebRtcEnabled: jest.fn(),
|
|
172
|
+
setAnswerCallOnWebexService: jest.fn(),
|
|
145
173
|
registerIncomingCallEvent: jest.fn(),
|
|
146
174
|
registerTaskListeners: jest.fn(),
|
|
147
175
|
getTask: jest.fn(),
|
|
148
176
|
getActiveTasks: jest.fn(),
|
|
177
|
+
applyEnableWxBetterTogether: jest.fn(),
|
|
178
|
+
syncWxAppMuteFromCallDetailsForAllTasks: jest.fn(),
|
|
149
179
|
on: jest.fn(),
|
|
150
180
|
off: jest.fn(),
|
|
151
181
|
emit: jest.fn(),
|
|
@@ -184,6 +214,35 @@ describe('webex.cc', () => {
|
|
|
184
214
|
webex.emit('ready');
|
|
185
215
|
});
|
|
186
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
|
+
|
|
187
246
|
describe('cc.getDeviceId', () => {
|
|
188
247
|
it('should return dialNumber when loginOption is EXTENSION', () => {
|
|
189
248
|
const loginOption = LoginOption.EXTENSION;
|
|
@@ -251,10 +310,13 @@ describe('webex.cc', () => {
|
|
|
251
310
|
isAgentAvailableAfterOutdial: false,
|
|
252
311
|
isCampaignManagementEnabled: false,
|
|
253
312
|
outDialEp: '',
|
|
254
|
-
|
|
313
|
+
isEndTaskEnabled: false,
|
|
255
314
|
isEndConsultEnabled: false,
|
|
256
315
|
agentDbId: '',
|
|
257
316
|
allowConsultToQueue: false,
|
|
317
|
+
accessQueue: 'SPECIFIC',
|
|
318
|
+
accessEntryPoint: 'ALL',
|
|
319
|
+
accessBuddyTeam: 'ALL',
|
|
258
320
|
agentPersonalStatsEnabled: false,
|
|
259
321
|
isTimeoutDesktopInactivityEnabled: false,
|
|
260
322
|
webRtcEnabled: true,
|
|
@@ -262,6 +324,7 @@ describe('webex.cc', () => {
|
|
|
262
324
|
};
|
|
263
325
|
|
|
264
326
|
it('should register successfully and return agent profile', async () => {
|
|
327
|
+
mockAgentProfile.aiFeature = {realtimeTranscripts: {enable: true}} as any;
|
|
265
328
|
const mercuryConnect = jest.spyOn(webex.internal.mercury, 'connect').mockResolvedValue(true);
|
|
266
329
|
const connectWebsocketSpy = jest.spyOn(webex.cc, 'connectWebsocket');
|
|
267
330
|
const setupEventListenersSpy = jest.spyOn(webex.cc, 'setupEventListeners');
|
|
@@ -283,7 +346,7 @@ describe('webex.cc', () => {
|
|
|
283
346
|
const result = await webex.cc.register();
|
|
284
347
|
|
|
285
348
|
// Verify logging calls
|
|
286
|
-
expect(LoggerProxy.
|
|
349
|
+
expect(LoggerProxy.log).toHaveBeenCalledWith('Starting CC SDK registration', {
|
|
287
350
|
module: CC_FILE,
|
|
288
351
|
method: 'register',
|
|
289
352
|
});
|
|
@@ -305,6 +368,7 @@ describe('webex.cc', () => {
|
|
|
305
368
|
clientType: 'WebexCCSDK',
|
|
306
369
|
allowMultiLogin: false,
|
|
307
370
|
},
|
|
371
|
+
resource: SUBSCRIBE_API,
|
|
308
372
|
});
|
|
309
373
|
|
|
310
374
|
// TODO: https://jira-eng-gpk2.cisco.com/jira/browse/SPARK-626777 Implement the de-register method and close the listener there
|
|
@@ -316,13 +380,44 @@ describe('webex.cc', () => {
|
|
|
316
380
|
TASK_EVENTS.TASK_HYDRATE,
|
|
317
381
|
expect.any(Function)
|
|
318
382
|
);
|
|
383
|
+
expect(mockTaskManager.on).toHaveBeenCalledWith(
|
|
384
|
+
TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE,
|
|
385
|
+
expect.any(Function)
|
|
386
|
+
);
|
|
319
387
|
expect(mockWebSocketManager.on).toHaveBeenCalledWith('message', expect.any(Function));
|
|
388
|
+
expect(webex.cc.services.rtdWebSocketManager.initWebSocket).toHaveBeenCalledWith({
|
|
389
|
+
body: {
|
|
390
|
+
force: true,
|
|
391
|
+
isKeepAliveEnabled: false,
|
|
392
|
+
clientType: 'WebexCCSDK',
|
|
393
|
+
allowMultiLogin: false,
|
|
394
|
+
},
|
|
395
|
+
resource: 'v1/realtime/subscribe',
|
|
396
|
+
});
|
|
397
|
+
expect(webex.cc.services.rtdWebSocketManager.on).toHaveBeenCalledWith(
|
|
398
|
+
'message',
|
|
399
|
+
expect.any(Function)
|
|
400
|
+
);
|
|
320
401
|
|
|
321
402
|
expect(configSpy).toHaveBeenCalled();
|
|
322
403
|
expect(LoggerProxy.log).toHaveBeenCalledWith('Agent config is fetched successfully', {
|
|
323
404
|
module: CC_FILE,
|
|
324
405
|
method: 'connectWebsocket',
|
|
325
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
|
+
});
|
|
326
421
|
expect(reloadSpy).toHaveBeenCalled();
|
|
327
422
|
expect(result).toEqual(mockAgentProfile);
|
|
328
423
|
expect(mockMetricsManager.timeEvent).toHaveBeenCalledWith([
|
|
@@ -360,6 +455,7 @@ describe('webex.cc', () => {
|
|
|
360
455
|
clientType: 'WebexCCSDK',
|
|
361
456
|
allowMultiLogin: true,
|
|
362
457
|
},
|
|
458
|
+
resource: SUBSCRIBE_API,
|
|
363
459
|
});
|
|
364
460
|
expect(configSpy).toHaveBeenCalled();
|
|
365
461
|
expect(LoggerProxy.log).toHaveBeenCalledWith('Agent config is fetched successfully', {
|
|
@@ -381,7 +477,7 @@ describe('webex.cc', () => {
|
|
|
381
477
|
|
|
382
478
|
await expect(webex.cc.register()).rejects.toThrow('Error while performing register');
|
|
383
479
|
|
|
384
|
-
expect(LoggerProxy.
|
|
480
|
+
expect(LoggerProxy.log).toHaveBeenCalledWith('Starting CC SDK registration', {
|
|
385
481
|
module: CC_FILE,
|
|
386
482
|
method: 'register',
|
|
387
483
|
});
|
|
@@ -439,6 +535,7 @@ describe('webex.cc', () => {
|
|
|
439
535
|
clientType: 'WebexCCSDK',
|
|
440
536
|
allowMultiLogin: false,
|
|
441
537
|
},
|
|
538
|
+
resource: SUBSCRIBE_API,
|
|
442
539
|
});
|
|
443
540
|
|
|
444
541
|
expect(mockTaskManager.on).toHaveBeenCalledWith(
|
|
@@ -449,6 +546,10 @@ describe('webex.cc', () => {
|
|
|
449
546
|
TASK_EVENTS.TASK_HYDRATE,
|
|
450
547
|
expect.any(Function)
|
|
451
548
|
);
|
|
549
|
+
expect(mockTaskManager.on).toHaveBeenCalledWith(
|
|
550
|
+
TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE,
|
|
551
|
+
expect.any(Function)
|
|
552
|
+
);
|
|
452
553
|
expect(mockWebSocketManager.on).toHaveBeenCalledWith('message', expect.any(Function));
|
|
453
554
|
|
|
454
555
|
expect(configSpy).toHaveBeenCalled();
|
|
@@ -460,8 +561,39 @@ describe('webex.cc', () => {
|
|
|
460
561
|
expect(result).toEqual(mockAgentProfile);
|
|
461
562
|
});
|
|
462
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
|
+
|
|
463
594
|
it('should not attempt for mercury connection when webrtc is disabled', async () => {
|
|
464
595
|
mockAgentProfile.webRtcEnabled = false;
|
|
596
|
+
mockAgentProfile.aiFeature = {realtimeTranscripts: {enable: false}} as any;
|
|
465
597
|
const mercurySpy = jest.spyOn(webex.internal.mercury, 'connect');
|
|
466
598
|
const connectWebsocketSpy = jest.spyOn(webex.cc, 'connectWebsocket');
|
|
467
599
|
const setupEventListenersSpy = jest.spyOn(webex.cc, 'setupEventListeners');
|
|
@@ -491,12 +623,39 @@ describe('webex.cc', () => {
|
|
|
491
623
|
clientType: 'WebexCCSDK',
|
|
492
624
|
allowMultiLogin: false,
|
|
493
625
|
},
|
|
626
|
+
resource: SUBSCRIBE_API,
|
|
494
627
|
});
|
|
495
628
|
|
|
496
629
|
expect(configSpy).toHaveBeenCalled();
|
|
497
630
|
expect(mercurySpy).not.toHaveBeenCalled();
|
|
631
|
+
expect(webex.cc.services.rtdWebSocketManager.initWebSocket).not.toHaveBeenCalled();
|
|
498
632
|
expect(result).toEqual(mockAgentProfile);
|
|
499
633
|
});
|
|
634
|
+
|
|
635
|
+
it('should not connect RTD websocket when realtime transcripts feature is disabled', async () => {
|
|
636
|
+
mockAgentProfile.aiFeature = {realtimeTranscripts: {enable: false}} as any;
|
|
637
|
+
jest.spyOn(webex.internal.mercury, 'connect').mockResolvedValue(true);
|
|
638
|
+
jest.spyOn(webex.cc.services.agent, 'reload').mockResolvedValue({
|
|
639
|
+
data: {
|
|
640
|
+
auxCodeId: 'auxCodeId',
|
|
641
|
+
agentId: 'agentId',
|
|
642
|
+
deviceType: LoginOption.EXTENSION,
|
|
643
|
+
dn: '12345',
|
|
644
|
+
},
|
|
645
|
+
});
|
|
646
|
+
jest.spyOn(webex.cc.services.config, 'getAgentConfig').mockResolvedValue(mockAgentProfile);
|
|
647
|
+
mockWebSocketManager.initWebSocket.mockResolvedValue({
|
|
648
|
+
agentId: 'agent123',
|
|
649
|
+
});
|
|
650
|
+
|
|
651
|
+
await webex.cc.register();
|
|
652
|
+
|
|
653
|
+
expect(webex.cc.services.rtdWebSocketManager.initWebSocket).not.toHaveBeenCalled();
|
|
654
|
+
expect(webex.cc.services.rtdWebSocketManager.on).not.toHaveBeenCalledWith(
|
|
655
|
+
'message',
|
|
656
|
+
expect.any(Function)
|
|
657
|
+
);
|
|
658
|
+
});
|
|
500
659
|
});
|
|
501
660
|
|
|
502
661
|
describe('stationLogin', () => {
|
|
@@ -588,6 +747,7 @@ describe('webex.cc', () => {
|
|
|
588
747
|
METRIC_EVENT_NAMES.STATION_LOGIN_FAILED,
|
|
589
748
|
]);
|
|
590
749
|
expect(result).toEqual(responseMock);
|
|
750
|
+
expect(webex.cc.agentConfig.deviceType).toBe(LoginOption.BROWSER);
|
|
591
751
|
|
|
592
752
|
const onSpy = jest.spyOn(mockTaskManager, 'on');
|
|
593
753
|
const emitSpy = jest.spyOn(webex.cc, 'trigger');
|
|
@@ -600,10 +760,8 @@ describe('webex.cc', () => {
|
|
|
600
760
|
|
|
601
761
|
expect(emitSpy).toHaveBeenCalledTimes(1);
|
|
602
762
|
expect(emitSpy).toHaveBeenCalledWith(TASK_EVENTS.TASK_INCOMING, mockTask);
|
|
603
|
-
// Verify message
|
|
604
|
-
const messageCallback =
|
|
605
|
-
(call) => call[0] === 'message'
|
|
606
|
-
)[1];
|
|
763
|
+
// Verify websocket message handling
|
|
764
|
+
const messageCallback = webex.cc['handleWebsocketMessage'];
|
|
607
765
|
const agentStateChangeEventData = {
|
|
608
766
|
type: CC_EVENTS.AGENT_STATE_CHANGE,
|
|
609
767
|
data: {some: 'data'},
|
|
@@ -631,6 +789,59 @@ describe('webex.cc', () => {
|
|
|
631
789
|
);
|
|
632
790
|
});
|
|
633
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
|
+
|
|
634
845
|
it('should not attempt mobius registration for LoginOption.BROWSER if webrtc is disabled', async () => {
|
|
635
846
|
const options = {
|
|
636
847
|
teamId: 'teamId',
|
|
@@ -753,10 +964,13 @@ describe('webex.cc', () => {
|
|
|
753
964
|
const result = await webex.cc.stationLogin(options);
|
|
754
965
|
|
|
755
966
|
// Verify logging calls
|
|
756
|
-
expect(LoggerProxy.
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
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
|
+
);
|
|
760
974
|
expect(LoggerProxy.log).toHaveBeenCalledWith(
|
|
761
975
|
`Agent station login completed successfully agentId: ${mockData.data.agentId} loginOption: ${mockData.data.loginOption} teamId: ${mockData.data.teamId}`,
|
|
762
976
|
{
|
|
@@ -807,102 +1021,726 @@ describe('webex.cc', () => {
|
|
|
807
1021
|
|
|
808
1022
|
await expect(webex.cc.stationLogin(options)).rejects.toThrow(error.details.data.reason);
|
|
809
1023
|
|
|
810
|
-
expect(LoggerProxy.
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
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
|
+
);
|
|
814
1031
|
expect(LoggerProxy.error).toHaveBeenCalledWith(
|
|
815
1032
|
`stationLogin failed with reason: ${error.details.data.reason}`,
|
|
816
1033
|
{module: CC_FILE, method: 'stationLogin', trackingId: error.details.trackingId}
|
|
817
1034
|
);
|
|
818
1035
|
});
|
|
819
|
-
});
|
|
820
1036
|
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
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;
|
|
825
1053
|
|
|
826
|
-
const
|
|
827
|
-
.spyOn(webex.cc
|
|
828
|
-
.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(() => {});
|
|
829
1060
|
|
|
830
|
-
|
|
1061
|
+
jest.spyOn(webex.cc.services.agent, 'stationLogin').mockResolvedValue({
|
|
1062
|
+
data: {
|
|
1063
|
+
agentId: 'agentId',
|
|
1064
|
+
teamId: 'teamId',
|
|
1065
|
+
channelsMap: {chat: [], email: [], social: [], telephony: []},
|
|
1066
|
+
},
|
|
1067
|
+
trackingId: 'track-1',
|
|
1068
|
+
} as StationLoginSuccess);
|
|
831
1069
|
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
});
|
|
837
|
-
expect(LoggerProxy.log).toHaveBeenCalledWith('Agent station logout completed successfully', {
|
|
838
|
-
module: CC_FILE,
|
|
839
|
-
method: 'stationLogout',
|
|
1070
|
+
await webex.cc.stationLogin({
|
|
1071
|
+
teamId: 'teamId',
|
|
1072
|
+
loginOption: LoginOption.EXTENSION,
|
|
1073
|
+
dialNumber: '1001',
|
|
840
1074
|
});
|
|
841
1075
|
|
|
842
|
-
expect(
|
|
843
|
-
|
|
844
|
-
// expect(mockTaskManager.unregisterIncomingCallEvent).toHaveBeenCalledWith();
|
|
845
|
-
// expect(mockTaskManager.off).toHaveBeenCalledWith(
|
|
846
|
-
// TASK_EVENTS.TASK_INCOMING,
|
|
847
|
-
// expect.any(Function)
|
|
848
|
-
// );
|
|
849
|
-
// expect(mockTaskManager.off).toHaveBeenCalledWith(
|
|
850
|
-
// TASK_EVENTS.TASK_HYDRATE,
|
|
851
|
-
// expect.any(Function)
|
|
852
|
-
// );
|
|
853
|
-
// expect(mockWebSocketManager.off).toHaveBeenCalledWith('message', expect.any(Function));
|
|
854
|
-
expect(result).toEqual(response);
|
|
855
|
-
expect(mockMetricsManager.timeEvent).toHaveBeenCalledWith([
|
|
856
|
-
METRIC_EVENT_NAMES.STATION_LOGOUT_SUCCESS,
|
|
857
|
-
METRIC_EVENT_NAMES.STATION_LOGOUT_FAILED,
|
|
858
|
-
]);
|
|
1076
|
+
expect(publishSpy).toHaveBeenCalledWith(true, {userId: 'user-123'});
|
|
1077
|
+
expect(subscribeSpy).toHaveBeenCalledWith('agentId', expect.any(Function));
|
|
859
1078
|
});
|
|
860
1079
|
|
|
861
|
-
it('should
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
},
|
|
869
|
-
},
|
|
1080
|
+
it('should rollback wxApp config when post-station-login init fails', async () => {
|
|
1081
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: true};
|
|
1082
|
+
webex.internal.device = {userId: 'user-123', url: 'https://wdm.example.com/devices/dev-1'};
|
|
1083
|
+
webex.cc.agentConfig = {
|
|
1084
|
+
agentId: 'agentId',
|
|
1085
|
+
webRtcEnabled: false,
|
|
1086
|
+
loginVoiceOptions: ['EXTENSION'],
|
|
870
1087
|
};
|
|
1088
|
+
webex.cc.webCallingService.loginOption = LoginOption.EXTENSION;
|
|
1089
|
+
mockTaskManager.applyEnableWxBetterTogether(true);
|
|
871
1090
|
|
|
872
|
-
jest
|
|
1091
|
+
jest
|
|
1092
|
+
.spyOn(webex.cc as never, 'publishAnswerOnWebexCrossClientState' as never)
|
|
1093
|
+
.mockRejectedValue(new Error('usersub publish failed'));
|
|
1094
|
+
const unsubscribeSpy = jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'unsubscribe');
|
|
873
1095
|
|
|
874
|
-
|
|
1096
|
+
jest.spyOn(webex.cc.services.agent, 'stationLogin').mockResolvedValue({
|
|
1097
|
+
data: {
|
|
1098
|
+
agentId: 'agentId',
|
|
1099
|
+
teamId: 'teamId',
|
|
1100
|
+
channelsMap: {chat: [], email: [], social: [], telephony: []},
|
|
1101
|
+
},
|
|
1102
|
+
trackingId: 'track-1',
|
|
1103
|
+
} as StationLoginSuccess);
|
|
875
1104
|
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
1105
|
+
await webex.cc.stationLogin({
|
|
1106
|
+
teamId: 'teamId',
|
|
1107
|
+
loginOption: LoginOption.EXTENSION,
|
|
1108
|
+
dialNumber: '1001',
|
|
879
1109
|
});
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
);
|
|
1110
|
+
|
|
1111
|
+
expect(unsubscribeSpy).toHaveBeenCalled();
|
|
1112
|
+
expect(webex.cc.isWxBetterTogetherEnabled()).toBe(false);
|
|
1113
|
+
expect(mockTaskManager.applyEnableWxBetterTogether).toHaveBeenCalledWith(false);
|
|
884
1114
|
});
|
|
885
|
-
});
|
|
886
1115
|
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
1116
|
+
it('should reset wxApp config when Mercury fails before usersub publish on station login', async () => {
|
|
1117
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: true};
|
|
1118
|
+
webex.internal.device = {userId: 'user-123', url: 'https://wdm.example.com/devices/dev-1'};
|
|
1119
|
+
webex.cc.agentConfig = {
|
|
1120
|
+
agentId: 'agentId',
|
|
1121
|
+
webRtcEnabled: false,
|
|
1122
|
+
loginVoiceOptions: ['EXTENSION'],
|
|
894
1123
|
};
|
|
1124
|
+
webex.cc.webCallingService.loginOption = LoginOption.EXTENSION;
|
|
1125
|
+
mockTaskManager.applyEnableWxBetterTogether(true);
|
|
1126
|
+
|
|
1127
|
+
const publishSpy = jest
|
|
1128
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
1129
|
+
.mockResolvedValue(undefined);
|
|
1130
|
+
jest
|
|
1131
|
+
.spyOn(webex.cc as any, 'ensureWxAppMercuryConnected')
|
|
1132
|
+
.mockRejectedValue(new Error('mercury connect failed'));
|
|
1133
|
+
const teardownSpy = jest.spyOn(webex.cc['webexCrossClientService'], 'teardown');
|
|
1134
|
+
const unsubscribeSpy = jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'unsubscribe');
|
|
1135
|
+
jest
|
|
1136
|
+
.spyOn(webex.cc as any, 'releaseWxAppMercuryResources')
|
|
1137
|
+
.mockResolvedValue(undefined);
|
|
1138
|
+
|
|
1139
|
+
jest.spyOn(webex.cc.services.agent, 'stationLogin').mockResolvedValue({
|
|
1140
|
+
data: {
|
|
1141
|
+
agentId: 'agentId',
|
|
1142
|
+
teamId: 'teamId',
|
|
1143
|
+
channelsMap: {chat: [], email: [], social: [], telephony: []},
|
|
1144
|
+
},
|
|
1145
|
+
trackingId: 'track-1',
|
|
1146
|
+
} as StationLoginSuccess);
|
|
895
1147
|
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
1148
|
+
await webex.cc.stationLogin({
|
|
1149
|
+
teamId: 'teamId',
|
|
1150
|
+
loginOption: LoginOption.EXTENSION,
|
|
1151
|
+
dialNumber: '1001',
|
|
1152
|
+
});
|
|
899
1153
|
|
|
900
|
-
|
|
1154
|
+
expect(publishSpy).not.toHaveBeenCalled();
|
|
1155
|
+
expect(teardownSpy).not.toHaveBeenCalled();
|
|
1156
|
+
expect(unsubscribeSpy).toHaveBeenCalled();
|
|
1157
|
+
expect(webex.cc.isWxBetterTogetherEnabled()).toBe(false);
|
|
1158
|
+
expect(mockTaskManager.applyEnableWxBetterTogether).toHaveBeenCalledWith(false);
|
|
1159
|
+
});
|
|
901
1160
|
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
1161
|
+
it('should register device before publishing usersub true on extension station login', async () => {
|
|
1162
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: true};
|
|
1163
|
+
webex.internal.device = {
|
|
1164
|
+
userId: 'user-123',
|
|
1165
|
+
registered: false,
|
|
1166
|
+
register: jest.fn().mockImplementation(async function (this: {
|
|
1167
|
+
url?: string;
|
|
1168
|
+
registered?: boolean;
|
|
1169
|
+
}) {
|
|
1170
|
+
this.url = 'https://wdm.example.com/devices/dev-1';
|
|
1171
|
+
this.registered = true;
|
|
1172
|
+
}),
|
|
1173
|
+
};
|
|
1174
|
+
webex.internal.mercury = {
|
|
1175
|
+
connected: false,
|
|
1176
|
+
connect: jest.fn().mockResolvedValue(undefined),
|
|
1177
|
+
};
|
|
1178
|
+
webex.cc.agentConfig = {
|
|
1179
|
+
agentId: 'agentId',
|
|
1180
|
+
webRtcEnabled: false,
|
|
1181
|
+
loginVoiceOptions: ['EXTENSION'],
|
|
1182
|
+
};
|
|
1183
|
+
webex.cc.webCallingService.loginOption = LoginOption.EXTENSION;
|
|
1184
|
+
jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'subscribe').mockImplementation(() => {});
|
|
1185
|
+
|
|
1186
|
+
const callOrder: string[] = [];
|
|
1187
|
+
jest.spyOn(webex.internal.device, 'register').mockImplementation(async function (this: {
|
|
1188
|
+
url?: string;
|
|
1189
|
+
registered?: boolean;
|
|
1190
|
+
}) {
|
|
1191
|
+
callOrder.push('device.register');
|
|
1192
|
+
this.url = 'https://wdm.example.com/devices/dev-1';
|
|
1193
|
+
this.registered = true;
|
|
1194
|
+
});
|
|
1195
|
+
jest
|
|
1196
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
1197
|
+
.mockImplementation(async (enable: boolean) => {
|
|
1198
|
+
if (enable === true && !webex.internal.device.url) {
|
|
1199
|
+
throw new Error('Device URL is unavailable for cross-client publish');
|
|
1200
|
+
}
|
|
1201
|
+
callOrder.push(enable ? 'publish:true' : 'publish:false');
|
|
1202
|
+
});
|
|
1203
|
+
|
|
1204
|
+
jest.spyOn(webex.cc.services.agent, 'stationLogin').mockResolvedValue({
|
|
1205
|
+
data: {
|
|
1206
|
+
agentId: 'agentId',
|
|
1207
|
+
teamId: 'teamId',
|
|
1208
|
+
channelsMap: {chat: [], email: [], social: [], telephony: []},
|
|
1209
|
+
},
|
|
1210
|
+
trackingId: 'track-1',
|
|
1211
|
+
} as StationLoginSuccess);
|
|
1212
|
+
|
|
1213
|
+
await webex.cc.stationLogin({
|
|
1214
|
+
teamId: 'teamId',
|
|
1215
|
+
loginOption: LoginOption.EXTENSION,
|
|
1216
|
+
dialNumber: '1001',
|
|
1217
|
+
});
|
|
1218
|
+
|
|
1219
|
+
expect(callOrder).toEqual(['device.register', 'publish:true']);
|
|
1220
|
+
});
|
|
1221
|
+
|
|
1222
|
+
it('should register device before publishing forced usersub false when wxApp is disabled at station login', async () => {
|
|
1223
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: false};
|
|
1224
|
+
webex.internal.device = {
|
|
1225
|
+
userId: 'user-123',
|
|
1226
|
+
registered: false,
|
|
1227
|
+
register: jest.fn().mockImplementation(async function (this: {
|
|
1228
|
+
url?: string;
|
|
1229
|
+
registered?: boolean;
|
|
1230
|
+
}) {
|
|
1231
|
+
this.url = 'https://wdm.example.com/devices/dev-1';
|
|
1232
|
+
this.registered = true;
|
|
1233
|
+
}),
|
|
1234
|
+
};
|
|
1235
|
+
webex.internal.mercury = {
|
|
1236
|
+
connected: false,
|
|
1237
|
+
connect: jest.fn().mockResolvedValue(undefined),
|
|
1238
|
+
};
|
|
1239
|
+
webex.cc.agentConfig = {
|
|
1240
|
+
agentId: 'agentId',
|
|
1241
|
+
webRtcEnabled: false,
|
|
1242
|
+
loginVoiceOptions: ['EXTENSION'],
|
|
1243
|
+
};
|
|
1244
|
+
webex.cc.webCallingService.loginOption = LoginOption.EXTENSION;
|
|
1245
|
+
jest
|
|
1246
|
+
.spyOn(webex.cc as any, 'releaseWxAppMercuryResources')
|
|
1247
|
+
.mockResolvedValue(undefined);
|
|
1248
|
+
|
|
1249
|
+
const callOrder: string[] = [];
|
|
1250
|
+
jest.spyOn(webex.internal.device, 'register').mockImplementation(async function (this: {
|
|
1251
|
+
url?: string;
|
|
1252
|
+
registered?: boolean;
|
|
1253
|
+
}) {
|
|
1254
|
+
callOrder.push('device.register');
|
|
1255
|
+
this.url = 'https://wdm.example.com/devices/dev-1';
|
|
1256
|
+
this.registered = true;
|
|
1257
|
+
});
|
|
1258
|
+
jest
|
|
1259
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
1260
|
+
.mockImplementation(async (enable: boolean) => {
|
|
1261
|
+
if (!webex.internal.device.url) {
|
|
1262
|
+
throw new Error('Device URL is unavailable for cross-client publish');
|
|
1263
|
+
}
|
|
1264
|
+
callOrder.push(enable ? 'publish:true' : 'publish:false');
|
|
1265
|
+
});
|
|
1266
|
+
|
|
1267
|
+
jest.spyOn(webex.cc.services.agent, 'stationLogin').mockResolvedValue({
|
|
1268
|
+
data: {
|
|
1269
|
+
agentId: 'agentId',
|
|
1270
|
+
teamId: 'teamId',
|
|
1271
|
+
channelsMap: {chat: [], email: [], social: [], telephony: []},
|
|
1272
|
+
},
|
|
1273
|
+
trackingId: 'track-1',
|
|
1274
|
+
} as StationLoginSuccess);
|
|
1275
|
+
|
|
1276
|
+
await webex.cc.stationLogin({
|
|
1277
|
+
teamId: 'teamId',
|
|
1278
|
+
loginOption: LoginOption.EXTENSION,
|
|
1279
|
+
dialNumber: '1001',
|
|
1280
|
+
});
|
|
1281
|
+
|
|
1282
|
+
expect(callOrder).toEqual(['device.register', 'publish:false']);
|
|
1283
|
+
});
|
|
1284
|
+
|
|
1285
|
+
it('should retry forced false publish when wxApp is disabled at station login', async () => {
|
|
1286
|
+
jest.useFakeTimers();
|
|
1287
|
+
try {
|
|
1288
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: false};
|
|
1289
|
+
webex.internal.device = {userId: 'user-123', url: 'https://wdm.example.com/devices/dev-1'};
|
|
1290
|
+
webex.cc.agentConfig = {
|
|
1291
|
+
agentId: 'agentId',
|
|
1292
|
+
webRtcEnabled: false,
|
|
1293
|
+
loginVoiceOptions: ['EXTENSION'],
|
|
1294
|
+
};
|
|
1295
|
+
webex.cc.webCallingService.loginOption = LoginOption.EXTENSION;
|
|
1296
|
+
|
|
1297
|
+
const publishSpy = jest
|
|
1298
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
1299
|
+
.mockRejectedValueOnce(new Error('usersub publish failed'))
|
|
1300
|
+
.mockResolvedValue(undefined);
|
|
1301
|
+
const scheduleRetrySpy = jest.spyOn(
|
|
1302
|
+
webex.cc as any,
|
|
1303
|
+
'scheduleForcedFalsePublishRetry'
|
|
1304
|
+
);
|
|
1305
|
+
const teardownSpy = jest.spyOn(webex.cc['webexCrossClientService'], 'teardown');
|
|
1306
|
+
jest
|
|
1307
|
+
.spyOn(webex.cc as any, 'releaseWxAppMercuryResources')
|
|
1308
|
+
.mockResolvedValue(undefined);
|
|
1309
|
+
|
|
1310
|
+
jest.spyOn(webex.cc.services.agent, 'stationLogin').mockResolvedValue({
|
|
1311
|
+
data: {
|
|
1312
|
+
agentId: 'agentId',
|
|
1313
|
+
teamId: 'teamId',
|
|
1314
|
+
channelsMap: {chat: [], email: [], social: [], telephony: []},
|
|
1315
|
+
},
|
|
1316
|
+
trackingId: 'track-1',
|
|
1317
|
+
} as StationLoginSuccess);
|
|
1318
|
+
|
|
1319
|
+
await webex.cc.stationLogin({
|
|
1320
|
+
teamId: 'teamId',
|
|
1321
|
+
loginOption: LoginOption.EXTENSION,
|
|
1322
|
+
dialNumber: '1001',
|
|
1323
|
+
});
|
|
1324
|
+
|
|
1325
|
+
expect(publishSpy).toHaveBeenCalledTimes(1);
|
|
1326
|
+
expect(scheduleRetrySpy).toHaveBeenCalled();
|
|
1327
|
+
expect(teardownSpy).not.toHaveBeenCalled();
|
|
1328
|
+
|
|
1329
|
+
jest.advanceTimersByTime(30_000);
|
|
1330
|
+
await publishSpy.mock.results[1]?.value;
|
|
1331
|
+
|
|
1332
|
+
expect(publishSpy).toHaveBeenCalledTimes(2);
|
|
1333
|
+
expect(publishSpy).toHaveBeenLastCalledWith(false, {userId: 'user-123'});
|
|
1334
|
+
} finally {
|
|
1335
|
+
jest.useRealTimers();
|
|
1336
|
+
}
|
|
1337
|
+
});
|
|
1338
|
+
|
|
1339
|
+
it('should not publish usersub on BROWSER station login even when enableWxBetterTogether is true at init', async () => {
|
|
1340
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: true};
|
|
1341
|
+
webex.internal.device = {userId: 'user-123', url: 'https://wdm.example.com/devices/dev-1'};
|
|
1342
|
+
webex.cc.agentConfig = {
|
|
1343
|
+
agentId: 'agentId',
|
|
1344
|
+
webRtcEnabled: true,
|
|
1345
|
+
loginVoiceOptions: ['BROWSER'],
|
|
1346
|
+
};
|
|
1347
|
+
webex.cc.webCallingService.loginOption = LoginOption.BROWSER;
|
|
1348
|
+
|
|
1349
|
+
const publishSpy = jest.spyOn(
|
|
1350
|
+
webex.cc['webexCrossClientService'],
|
|
1351
|
+
'setManageWebexCallingInWxcc'
|
|
1352
|
+
);
|
|
1353
|
+
|
|
1354
|
+
jest.spyOn(webex.cc.services.agent, 'stationLogin').mockResolvedValue({
|
|
1355
|
+
data: {
|
|
1356
|
+
agentId: 'agentId',
|
|
1357
|
+
teamId: 'teamId',
|
|
1358
|
+
channelsMap: {chat: [], email: [], social: [], telephony: []},
|
|
1359
|
+
},
|
|
1360
|
+
trackingId: 'track-1',
|
|
1361
|
+
} as StationLoginSuccess);
|
|
1362
|
+
|
|
1363
|
+
await webex.cc.stationLogin({
|
|
1364
|
+
teamId: 'teamId',
|
|
1365
|
+
loginOption: LoginOption.BROWSER,
|
|
1366
|
+
});
|
|
1367
|
+
|
|
1368
|
+
expect(publishSpy).not.toHaveBeenCalled();
|
|
1369
|
+
});
|
|
1370
|
+
|
|
1371
|
+
it('should force-publish usersub false on station login when enableWxBetterTogether is false at init', async () => {
|
|
1372
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: false};
|
|
1373
|
+
webex.internal.device = {userId: 'user-123', url: 'https://wdm.example.com/devices/dev-1'};
|
|
1374
|
+
webex.cc.agentConfig = {
|
|
1375
|
+
agentId: 'agentId',
|
|
1376
|
+
webRtcEnabled: false,
|
|
1377
|
+
loginVoiceOptions: ['EXTENSION'],
|
|
1378
|
+
};
|
|
1379
|
+
webex.cc.webCallingService.loginOption = LoginOption.EXTENSION;
|
|
1380
|
+
|
|
1381
|
+
const publishSpy = jest
|
|
1382
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
1383
|
+
.mockResolvedValue(undefined);
|
|
1384
|
+
const teardownSpy = jest.spyOn(webex.cc['webexCrossClientService'], 'teardown');
|
|
1385
|
+
const unsubscribeSpy = jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'unsubscribe');
|
|
1386
|
+
|
|
1387
|
+
jest.spyOn(webex.cc.services.agent, 'stationLogin').mockResolvedValue({
|
|
1388
|
+
data: {
|
|
1389
|
+
agentId: 'agentId',
|
|
1390
|
+
teamId: 'teamId',
|
|
1391
|
+
channelsMap: {chat: [], email: [], social: [], telephony: []},
|
|
1392
|
+
},
|
|
1393
|
+
trackingId: 'track-1',
|
|
1394
|
+
} as StationLoginSuccess);
|
|
1395
|
+
|
|
1396
|
+
await webex.cc.stationLogin({
|
|
1397
|
+
teamId: 'teamId',
|
|
1398
|
+
loginOption: LoginOption.EXTENSION,
|
|
1399
|
+
dialNumber: '1001',
|
|
1400
|
+
});
|
|
1401
|
+
|
|
1402
|
+
expect(publishSpy).toHaveBeenCalledWith(false, {userId: 'user-123'});
|
|
1403
|
+
expect(teardownSpy).toHaveBeenCalled();
|
|
1404
|
+
expect(unsubscribeSpy).toHaveBeenCalled();
|
|
1405
|
+
});
|
|
1406
|
+
|
|
1407
|
+
it('should clear stale usersub on login after prior session enabled wxApp then hard refresh', async () => {
|
|
1408
|
+
webex.internal.device = {userId: 'user-123', url: 'https://wdm.example.com/devices/dev-1'};
|
|
1409
|
+
webex.cc.agentConfig = {
|
|
1410
|
+
agentId: 'agentId',
|
|
1411
|
+
webRtcEnabled: false,
|
|
1412
|
+
loginVoiceOptions: ['EXTENSION'],
|
|
1413
|
+
};
|
|
1414
|
+
webex.cc.webCallingService.loginOption = LoginOption.EXTENSION;
|
|
1415
|
+
webex.request = jest.fn().mockResolvedValue({body: {}});
|
|
1416
|
+
|
|
1417
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: true};
|
|
1418
|
+
jest.spyOn(webex.cc.services.agent, 'stationLogin').mockResolvedValue({
|
|
1419
|
+
data: {
|
|
1420
|
+
agentId: 'agentId',
|
|
1421
|
+
teamId: 'teamId',
|
|
1422
|
+
channelsMap: {chat: [], email: [], social: [], telephony: []},
|
|
1423
|
+
},
|
|
1424
|
+
trackingId: 'track-1',
|
|
1425
|
+
} as StationLoginSuccess);
|
|
1426
|
+
|
|
1427
|
+
await webex.cc.stationLogin({
|
|
1428
|
+
teamId: 'teamId',
|
|
1429
|
+
loginOption: LoginOption.EXTENSION,
|
|
1430
|
+
dialNumber: '1001',
|
|
1431
|
+
});
|
|
1432
|
+
|
|
1433
|
+
webex.cc['webexCrossClientService'].teardown();
|
|
1434
|
+
webex.cc['wxAppTelephonyMercurySync'].unsubscribe();
|
|
1435
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: false};
|
|
1436
|
+
|
|
1437
|
+
const publishSpy = jest
|
|
1438
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
1439
|
+
.mockResolvedValue(undefined);
|
|
1440
|
+
|
|
1441
|
+
await webex.cc.stationLogin({
|
|
1442
|
+
teamId: 'teamId',
|
|
1443
|
+
loginOption: LoginOption.EXTENSION,
|
|
1444
|
+
dialNumber: '1001',
|
|
1445
|
+
});
|
|
1446
|
+
|
|
1447
|
+
expect(publishSpy).toHaveBeenCalledWith(false, {userId: 'user-123'});
|
|
1448
|
+
});
|
|
1449
|
+
});
|
|
1450
|
+
|
|
1451
|
+
describe('stationLogout', () => {
|
|
1452
|
+
it('should logout successfully', async () => {
|
|
1453
|
+
const data = {logoutReason: 'Logout reason'};
|
|
1454
|
+
const response = {};
|
|
1455
|
+
|
|
1456
|
+
const stationLogoutMock = jest
|
|
1457
|
+
.spyOn(webex.cc.services.agent, 'logout')
|
|
1458
|
+
.mockResolvedValue({} as StationLogoutResponse);
|
|
1459
|
+
|
|
1460
|
+
const result = await webex.cc.stationLogout(data);
|
|
1461
|
+
|
|
1462
|
+
// Verify logging calls
|
|
1463
|
+
expect(LoggerProxy.info).toHaveBeenCalledWith('Starting agent station logout', {
|
|
1464
|
+
module: CC_FILE,
|
|
1465
|
+
method: 'stationLogout',
|
|
1466
|
+
});
|
|
1467
|
+
expect(LoggerProxy.log).toHaveBeenCalledWith('Agent station logout completed successfully', {
|
|
1468
|
+
module: CC_FILE,
|
|
1469
|
+
method: 'stationLogout',
|
|
1470
|
+
});
|
|
1471
|
+
|
|
1472
|
+
expect(stationLogoutMock).toHaveBeenCalledWith({data: data});
|
|
1473
|
+
// TODO: https://jira-eng-gpk2.cisco.com/jira/browse/SPARK-626777 Implement the de-register method and close the listener there
|
|
1474
|
+
// expect(mockTaskManager.unregisterIncomingCallEvent).toHaveBeenCalledWith();
|
|
1475
|
+
// expect(mockTaskManager.off).toHaveBeenCalledWith(
|
|
1476
|
+
// TASK_EVENTS.TASK_INCOMING,
|
|
1477
|
+
// expect.any(Function)
|
|
1478
|
+
// );
|
|
1479
|
+
// expect(mockTaskManager.off).toHaveBeenCalledWith(
|
|
1480
|
+
// TASK_EVENTS.TASK_HYDRATE,
|
|
1481
|
+
// expect.any(Function)
|
|
1482
|
+
// );
|
|
1483
|
+
// expect(mockWebSocketManager.off).toHaveBeenCalledWith('message', expect.any(Function));
|
|
1484
|
+
expect(result).toEqual(response);
|
|
1485
|
+
expect(mockMetricsManager.timeEvent).toHaveBeenCalledWith([
|
|
1486
|
+
METRIC_EVENT_NAMES.STATION_LOGOUT_SUCCESS,
|
|
1487
|
+
METRIC_EVENT_NAMES.STATION_LOGOUT_FAILED,
|
|
1488
|
+
]);
|
|
1489
|
+
});
|
|
1490
|
+
|
|
1491
|
+
it('should not publish usersub false on logout when wxApp was never enabled', async () => {
|
|
1492
|
+
webex.internal.device = {userId: 'user-123'};
|
|
1493
|
+
const publishSpy = jest
|
|
1494
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
1495
|
+
.mockResolvedValue(undefined);
|
|
1496
|
+
const teardownSpy = jest.spyOn(webex.cc['webexCrossClientService'], 'teardown');
|
|
1497
|
+
const unsubscribeSpy = jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'unsubscribe');
|
|
1498
|
+
|
|
1499
|
+
jest.spyOn(webex.cc.services.agent, 'logout').mockResolvedValue({
|
|
1500
|
+
trackingId: 'track-1',
|
|
1501
|
+
} as StationLogoutResponse);
|
|
1502
|
+
|
|
1503
|
+
await webex.cc.stationLogout({logoutReason: 'Logout reason'});
|
|
1504
|
+
|
|
1505
|
+
expect(publishSpy).not.toHaveBeenCalled();
|
|
1506
|
+
expect(teardownSpy).toHaveBeenCalled();
|
|
1507
|
+
expect(unsubscribeSpy).toHaveBeenCalled();
|
|
1508
|
+
});
|
|
1509
|
+
|
|
1510
|
+
it('should publish usersub false and teardown wxApp sync on logout when wxApp was enabled', async () => {
|
|
1511
|
+
webex.internal.device = {
|
|
1512
|
+
userId: 'user-123',
|
|
1513
|
+
url: 'https://wdm.example.com/devices/dev-1',
|
|
1514
|
+
registered: false,
|
|
1515
|
+
register: jest.fn().mockResolvedValue(undefined),
|
|
1516
|
+
unregister: jest.fn().mockResolvedValue(undefined),
|
|
1517
|
+
};
|
|
1518
|
+
webex.internal.mercury = {
|
|
1519
|
+
connected: false,
|
|
1520
|
+
connect: jest.fn().mockResolvedValue(undefined),
|
|
1521
|
+
disconnect: jest.fn().mockResolvedValue(undefined),
|
|
1522
|
+
};
|
|
1523
|
+
jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'subscribe').mockImplementation(() => {});
|
|
1524
|
+
webex.request = jest.fn().mockResolvedValue({body: {}});
|
|
1525
|
+
webex.cc['agentConfig'] = {
|
|
1526
|
+
agentId: 'agent-123',
|
|
1527
|
+
deviceType: LoginOption.EXTENSION,
|
|
1528
|
+
} as Profile;
|
|
1529
|
+
webex.cc.webCallingService.loginOption = LoginOption.EXTENSION;
|
|
1530
|
+
|
|
1531
|
+
await (webex.cc as any).setManageWebexCallingInWxcc(true);
|
|
1532
|
+
|
|
1533
|
+
const publishSpy = jest
|
|
1534
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
1535
|
+
.mockResolvedValue(undefined);
|
|
1536
|
+
const teardownSpy = jest.spyOn(webex.cc['webexCrossClientService'], 'teardown');
|
|
1537
|
+
const unsubscribeSpy = jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'unsubscribe');
|
|
1538
|
+
|
|
1539
|
+
jest.spyOn(webex.cc.services.agent, 'logout').mockResolvedValue({
|
|
1540
|
+
trackingId: 'track-1',
|
|
1541
|
+
} as StationLogoutResponse);
|
|
1542
|
+
|
|
1543
|
+
await webex.cc.stationLogout({logoutReason: 'Logout reason'});
|
|
1544
|
+
|
|
1545
|
+
expect(publishSpy).toHaveBeenCalledWith(false, {userId: 'user-123'});
|
|
1546
|
+
expect(teardownSpy).toHaveBeenCalled();
|
|
1547
|
+
expect(unsubscribeSpy).toHaveBeenCalled();
|
|
1548
|
+
});
|
|
1549
|
+
|
|
1550
|
+
it('should teardown wxApp state on logout even when usersub publish fails', async () => {
|
|
1551
|
+
webex.internal.device = {userId: 'user-123'};
|
|
1552
|
+
webex.cc['agentConfig'] = {
|
|
1553
|
+
agentId: 'agent-123',
|
|
1554
|
+
deviceType: LoginOption.EXTENSION,
|
|
1555
|
+
} as Profile;
|
|
1556
|
+
webex.cc.webCallingService.loginOption = LoginOption.EXTENSION;
|
|
1557
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: true};
|
|
1558
|
+
mockTaskManager.applyEnableWxBetterTogether(true);
|
|
1559
|
+
|
|
1560
|
+
jest.spyOn(webex.cc.services.agent, 'logout').mockResolvedValue({
|
|
1561
|
+
trackingId: 'track-1',
|
|
1562
|
+
} as StationLogoutResponse);
|
|
1563
|
+
jest
|
|
1564
|
+
.spyOn(webex.cc as never, 'publishAnswerOnWebexCrossClientState' as never)
|
|
1565
|
+
.mockRejectedValue(new Error('usersub publish failed'));
|
|
1566
|
+
const scheduleRetrySpy = jest.spyOn(
|
|
1567
|
+
webex.cc as any,
|
|
1568
|
+
'scheduleCompensatingFalsePublishRetry'
|
|
1569
|
+
);
|
|
1570
|
+
const teardownSpy = jest.spyOn(webex.cc['webexCrossClientService'], 'teardown');
|
|
1571
|
+
const unsubscribeSpy = jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'unsubscribe');
|
|
1572
|
+
|
|
1573
|
+
const result = await webex.cc.stationLogout({logoutReason: 'Logout reason'});
|
|
1574
|
+
|
|
1575
|
+
expect(result).toEqual({trackingId: 'track-1'});
|
|
1576
|
+
expect(teardownSpy).toHaveBeenCalled();
|
|
1577
|
+
expect(unsubscribeSpy).toHaveBeenCalled();
|
|
1578
|
+
expect(scheduleRetrySpy).toHaveBeenCalled();
|
|
1579
|
+
expect(webex.cc.isWxBetterTogetherEnabled()).toBe(true);
|
|
1580
|
+
});
|
|
1581
|
+
|
|
1582
|
+
it('should retry compensating false publish when teardown usersub publish fails on logout', async () => {
|
|
1583
|
+
jest.useFakeTimers();
|
|
1584
|
+
try {
|
|
1585
|
+
webex.internal.device = {
|
|
1586
|
+
userId: 'user-123',
|
|
1587
|
+
url: 'https://wdm.example.com/devices/dev-1',
|
|
1588
|
+
};
|
|
1589
|
+
webex.cc['agentConfig'] = {
|
|
1590
|
+
agentId: 'agent-123',
|
|
1591
|
+
deviceType: LoginOption.EXTENSION,
|
|
1592
|
+
} as Profile;
|
|
1593
|
+
webex.cc.webCallingService.loginOption = LoginOption.EXTENSION;
|
|
1594
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: true};
|
|
1595
|
+
mockTaskManager.applyEnableWxBetterTogether(true);
|
|
1596
|
+
webex.cc['webexCrossClientService'].answerCallsState = true;
|
|
1597
|
+
|
|
1598
|
+
jest.spyOn(webex.cc.services.agent, 'logout').mockResolvedValue({
|
|
1599
|
+
trackingId: 'track-1',
|
|
1600
|
+
} as StationLogoutResponse);
|
|
1601
|
+
|
|
1602
|
+
const publishSpy = jest
|
|
1603
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
1604
|
+
.mockRejectedValueOnce(new Error('usersub publish failed'))
|
|
1605
|
+
.mockResolvedValue(undefined);
|
|
1606
|
+
const teardownSpy = jest.spyOn(webex.cc['webexCrossClientService'], 'teardown');
|
|
1607
|
+
const unsubscribeSpy = jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'unsubscribe');
|
|
1608
|
+
jest
|
|
1609
|
+
.spyOn(webex.cc as any, 'releaseWxAppMercuryResources')
|
|
1610
|
+
.mockResolvedValue(undefined);
|
|
1611
|
+
|
|
1612
|
+
const result = await webex.cc.stationLogout({logoutReason: 'Logout reason'});
|
|
1613
|
+
|
|
1614
|
+
expect(result).toEqual({trackingId: 'track-1'});
|
|
1615
|
+
expect(publishSpy).toHaveBeenCalledTimes(1);
|
|
1616
|
+
expect(publishSpy).toHaveBeenCalledWith(false, {userId: 'user-123'});
|
|
1617
|
+
expect(teardownSpy).toHaveBeenCalled();
|
|
1618
|
+
expect(unsubscribeSpy).toHaveBeenCalled();
|
|
1619
|
+
|
|
1620
|
+
jest.advanceTimersByTime(30_000);
|
|
1621
|
+
await publishSpy.mock.results[1]?.value;
|
|
1622
|
+
|
|
1623
|
+
expect(publishSpy).toHaveBeenCalledTimes(2);
|
|
1624
|
+
expect(publishSpy).toHaveBeenLastCalledWith(false, {userId: 'user-123'});
|
|
1625
|
+
} finally {
|
|
1626
|
+
jest.useRealTimers();
|
|
1627
|
+
}
|
|
1628
|
+
});
|
|
1629
|
+
|
|
1630
|
+
it('should cancel compensating retry when station login succeeds before retry fires', async () => {
|
|
1631
|
+
jest.useFakeTimers();
|
|
1632
|
+
try {
|
|
1633
|
+
webex.internal.device = {
|
|
1634
|
+
userId: 'user-123',
|
|
1635
|
+
url: 'https://wdm.example.com/devices/dev-1',
|
|
1636
|
+
registered: false,
|
|
1637
|
+
register: jest.fn().mockResolvedValue(undefined),
|
|
1638
|
+
unregister: jest.fn().mockResolvedValue(undefined),
|
|
1639
|
+
};
|
|
1640
|
+
webex.internal.mercury = {
|
|
1641
|
+
connected: false,
|
|
1642
|
+
connect: jest.fn().mockResolvedValue(undefined),
|
|
1643
|
+
disconnect: jest.fn().mockResolvedValue(undefined),
|
|
1644
|
+
};
|
|
1645
|
+
jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'subscribe').mockImplementation(() => {});
|
|
1646
|
+
webex.cc['agentConfig'] = {
|
|
1647
|
+
agentId: 'agent-123',
|
|
1648
|
+
deviceType: LoginOption.EXTENSION,
|
|
1649
|
+
webRtcEnabled: false,
|
|
1650
|
+
loginVoiceOptions: ['EXTENSION'],
|
|
1651
|
+
} as Profile;
|
|
1652
|
+
webex.cc.webCallingService.loginOption = LoginOption.EXTENSION;
|
|
1653
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: true};
|
|
1654
|
+
mockTaskManager.applyEnableWxBetterTogether(true);
|
|
1655
|
+
webex.cc['webexCrossClientService'].answerCallsState = true;
|
|
1656
|
+
|
|
1657
|
+
jest.spyOn(webex.cc.services.agent, 'logout').mockResolvedValue({
|
|
1658
|
+
trackingId: 'track-1',
|
|
1659
|
+
} as StationLogoutResponse);
|
|
1660
|
+
jest.spyOn(webex.cc.services.agent, 'stationLogin').mockResolvedValue({
|
|
1661
|
+
data: {
|
|
1662
|
+
agentId: 'agent-123',
|
|
1663
|
+
teamId: 'team-123',
|
|
1664
|
+
channelsMap: {chat: [], email: [], social: [], telephony: []},
|
|
1665
|
+
},
|
|
1666
|
+
trackingId: 'track-2',
|
|
1667
|
+
} as StationLoginSuccess);
|
|
1668
|
+
|
|
1669
|
+
const publishSpy = jest
|
|
1670
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
1671
|
+
.mockRejectedValueOnce(new Error('usersub publish failed'))
|
|
1672
|
+
.mockResolvedValue(undefined);
|
|
1673
|
+
const teardownSpy = jest.spyOn(webex.cc['webexCrossClientService'], 'teardown');
|
|
1674
|
+
jest
|
|
1675
|
+
.spyOn(webex.cc as any, 'releaseWxAppMercuryResources')
|
|
1676
|
+
.mockResolvedValue(undefined);
|
|
1677
|
+
|
|
1678
|
+
await webex.cc.stationLogout({logoutReason: 'Logout reason'});
|
|
1679
|
+
await webex.cc.stationLogin({
|
|
1680
|
+
teamId: 'team-123',
|
|
1681
|
+
loginOption: LoginOption.EXTENSION,
|
|
1682
|
+
dialNumber: '1001',
|
|
1683
|
+
});
|
|
1684
|
+
|
|
1685
|
+
expect(publishSpy).toHaveBeenCalledTimes(2);
|
|
1686
|
+
expect(publishSpy).toHaveBeenNthCalledWith(1, false, {userId: 'user-123'});
|
|
1687
|
+
expect(publishSpy).toHaveBeenNthCalledWith(2, true, {userId: 'user-123'});
|
|
1688
|
+
expect(teardownSpy).toHaveBeenCalledTimes(1);
|
|
1689
|
+
|
|
1690
|
+
jest.advanceTimersByTime(30_000);
|
|
1691
|
+
|
|
1692
|
+
expect(publishSpy).toHaveBeenCalledTimes(2);
|
|
1693
|
+
expect(teardownSpy).toHaveBeenCalledTimes(1);
|
|
1694
|
+
} finally {
|
|
1695
|
+
jest.useRealTimers();
|
|
1696
|
+
}
|
|
1697
|
+
});
|
|
1698
|
+
|
|
1699
|
+
it('should handle error during stationLogout', async () => {
|
|
1700
|
+
const data = {logoutReason: 'Logout reason'};
|
|
1701
|
+
const error = {
|
|
1702
|
+
details: {
|
|
1703
|
+
trackingId: '1234',
|
|
1704
|
+
data: {
|
|
1705
|
+
reason: 'Error while performing station logout',
|
|
1706
|
+
},
|
|
1707
|
+
},
|
|
1708
|
+
};
|
|
1709
|
+
|
|
1710
|
+
jest.spyOn(webex.cc.services.agent, 'logout').mockRejectedValue(error);
|
|
1711
|
+
|
|
1712
|
+
await expect(webex.cc.stationLogout(data)).rejects.toThrow(error.details.data.reason);
|
|
1713
|
+
|
|
1714
|
+
expect(LoggerProxy.info).toHaveBeenCalledWith('Starting agent station logout', {
|
|
1715
|
+
module: CC_FILE,
|
|
1716
|
+
method: 'stationLogout',
|
|
1717
|
+
});
|
|
1718
|
+
expect(LoggerProxy.error).toHaveBeenCalledWith(
|
|
1719
|
+
`stationLogout failed with reason: ${error.details.data.reason}`,
|
|
1720
|
+
{module: CC_FILE, method: 'stationLogout', trackingId: error.details.trackingId}
|
|
1721
|
+
);
|
|
1722
|
+
});
|
|
1723
|
+
});
|
|
1724
|
+
|
|
1725
|
+
describe('setAgentStatus', () => {
|
|
1726
|
+
it('should set agent status successfully when status is Available', async () => {
|
|
1727
|
+
const expectedPayload = {
|
|
1728
|
+
state: 'Available',
|
|
1729
|
+
auxCodeId: '0',
|
|
1730
|
+
agentId: '123',
|
|
1731
|
+
lastStateChangeReason: 'Agent is available',
|
|
1732
|
+
};
|
|
1733
|
+
|
|
1734
|
+
const setAgentStatusMock = jest
|
|
1735
|
+
.spyOn(webex.cc.services.agent, 'stateChange')
|
|
1736
|
+
.mockResolvedValue({data: expectedPayload});
|
|
1737
|
+
|
|
1738
|
+
const result = await webex.cc.setAgentState(expectedPayload);
|
|
1739
|
+
|
|
1740
|
+
// Verify logging calls
|
|
1741
|
+
expect(LoggerProxy.info).toHaveBeenCalledWith('Setting agent state', {
|
|
1742
|
+
module: CC_FILE,
|
|
1743
|
+
method: 'setAgentState',
|
|
906
1744
|
});
|
|
907
1745
|
expect(LoggerProxy.log).toHaveBeenCalledWith(
|
|
908
1746
|
`Agent state changed successfully to auxCodeId: ${expectedPayload.auxCodeId}`,
|
|
@@ -1073,6 +1911,41 @@ describe('webex.cc', () => {
|
|
|
1073
1911
|
]);
|
|
1074
1912
|
});
|
|
1075
1913
|
|
|
1914
|
+
it('should map Transfer action to an Available backend state filter', async () => {
|
|
1915
|
+
webex.cc.agentConfig = {agentProfileID: 'test-agent-profile-id'};
|
|
1916
|
+
const buddyAgentsResponse = {data: {agentList: []}};
|
|
1917
|
+
const buddyAgentsSpy = jest
|
|
1918
|
+
.spyOn(webex.cc.services.agent, 'buddyAgents')
|
|
1919
|
+
.mockResolvedValue(buddyAgentsResponse);
|
|
1920
|
+
|
|
1921
|
+
await webex.cc.getBuddyAgents({action: 'Transfer', mediaType: 'chat'});
|
|
1922
|
+
|
|
1923
|
+
expect(buddyAgentsSpy).toHaveBeenCalledWith({
|
|
1924
|
+
data: {
|
|
1925
|
+
agentProfileId: 'test-agent-profile-id',
|
|
1926
|
+
mediaType: 'chat',
|
|
1927
|
+
state: 'Available',
|
|
1928
|
+
},
|
|
1929
|
+
});
|
|
1930
|
+
});
|
|
1931
|
+
|
|
1932
|
+
it('should omit the backend state filter for Consult and default media type to telephony', async () => {
|
|
1933
|
+
webex.cc.agentConfig = {agentProfileID: 'test-agent-profile-id'};
|
|
1934
|
+
const buddyAgentsResponse = {data: {agentList: []}};
|
|
1935
|
+
const buddyAgentsSpy = jest
|
|
1936
|
+
.spyOn(webex.cc.services.agent, 'buddyAgents')
|
|
1937
|
+
.mockResolvedValue(buddyAgentsResponse);
|
|
1938
|
+
|
|
1939
|
+
await webex.cc.getBuddyAgents({action: 'Consult'});
|
|
1940
|
+
|
|
1941
|
+
expect(buddyAgentsSpy).toHaveBeenCalledWith({
|
|
1942
|
+
data: {
|
|
1943
|
+
agentProfileId: 'test-agent-profile-id',
|
|
1944
|
+
mediaType: 'telephony',
|
|
1945
|
+
},
|
|
1946
|
+
});
|
|
1947
|
+
});
|
|
1948
|
+
|
|
1076
1949
|
it('should handle error', async () => {
|
|
1077
1950
|
const data: BuddyAgents = {state: 'Available', mediaType: 'telephony'};
|
|
1078
1951
|
webex.cc.agentConfig = {
|
|
@@ -1149,11 +2022,11 @@ describe('webex.cc', () => {
|
|
|
1149
2022
|
const webSocketManagerOnSpy = jest.spyOn(webex.cc.services.webSocketManager, 'on');
|
|
1150
2023
|
await webex.cc['silentRelogin']();
|
|
1151
2024
|
|
|
1152
|
-
expect(LoggerProxy.
|
|
2025
|
+
expect(LoggerProxy.log).toHaveBeenCalledWith('Starting silent relogin process', {
|
|
1153
2026
|
module: CC_FILE,
|
|
1154
2027
|
method: 'silentRelogin',
|
|
1155
2028
|
});
|
|
1156
|
-
expect(LoggerProxy.
|
|
2029
|
+
expect(LoggerProxy.log).toHaveBeenCalledWith(
|
|
1157
2030
|
'event=requestAutoStateChange | Requesting state change to available on socket reconnect',
|
|
1158
2031
|
{module: CC_FILE, method: 'silentRelogin'}
|
|
1159
2032
|
);
|
|
@@ -1198,7 +2071,7 @@ describe('webex.cc', () => {
|
|
|
1198
2071
|
|
|
1199
2072
|
jest.spyOn(webex.cc.services.agent, 'reload').mockRejectedValue(error);
|
|
1200
2073
|
await webex.cc['silentRelogin']();
|
|
1201
|
-
expect(LoggerProxy.
|
|
2074
|
+
expect(LoggerProxy.log).toHaveBeenCalledWith('Starting silent relogin process', {
|
|
1202
2075
|
module: CC_FILE,
|
|
1203
2076
|
method: 'silentRelogin',
|
|
1204
2077
|
});
|
|
@@ -1213,7 +2086,7 @@ describe('webex.cc', () => {
|
|
|
1213
2086
|
jest.spyOn(webex.cc.services.agent, 'reload').mockRejectedValue(error);
|
|
1214
2087
|
|
|
1215
2088
|
await expect(webex.cc['silentRelogin']()).rejects.toThrow(error);
|
|
1216
|
-
expect(LoggerProxy.
|
|
2089
|
+
expect(LoggerProxy.log).toHaveBeenCalledWith('Starting silent relogin process', {
|
|
1217
2090
|
module: CC_FILE,
|
|
1218
2091
|
method: 'silentRelogin',
|
|
1219
2092
|
});
|
|
@@ -1255,7 +2128,7 @@ describe('webex.cc', () => {
|
|
|
1255
2128
|
|
|
1256
2129
|
await webex.cc['silentRelogin']();
|
|
1257
2130
|
|
|
1258
|
-
expect(LoggerProxy.
|
|
2131
|
+
expect(LoggerProxy.log).toHaveBeenCalledWith('Starting silent relogin process', {
|
|
1259
2132
|
module: CC_FILE,
|
|
1260
2133
|
method: 'silentRelogin',
|
|
1261
2134
|
});
|
|
@@ -1300,6 +2173,81 @@ describe('webex.cc', () => {
|
|
|
1300
2173
|
expect(webex.cc.agentConfig.deviceType).toBe(LoginOption.AGENT_DN);
|
|
1301
2174
|
expect(webex.cc.agentConfig.dn).toBe('67890');
|
|
1302
2175
|
});
|
|
2176
|
+
|
|
2177
|
+
it('should publish usersub true on silent relogin when enableWxBetterTogether is true at init', async () => {
|
|
2178
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: true};
|
|
2179
|
+
webex.internal.device = {
|
|
2180
|
+
userId: 'user-123',
|
|
2181
|
+
url: 'https://wdm.example.com/devices/dev-1',
|
|
2182
|
+
registered: true,
|
|
2183
|
+
register: jest.fn().mockResolvedValue(undefined),
|
|
2184
|
+
};
|
|
2185
|
+
webex.internal.mercury.connected = true;
|
|
2186
|
+
webex.internal.mercury.connect = jest.fn().mockResolvedValue(undefined);
|
|
2187
|
+
webex.cc.agentConfig = {
|
|
2188
|
+
agentId: 'agentId',
|
|
2189
|
+
agentProfileID: 'test-agent-profile-id',
|
|
2190
|
+
isAgentLoggedIn: false,
|
|
2191
|
+
} as Profile;
|
|
2192
|
+
|
|
2193
|
+
jest.spyOn(webex.cc.services.agent, 'reload').mockResolvedValue({
|
|
2194
|
+
data: {
|
|
2195
|
+
auxCodeId: 'auxCodeId',
|
|
2196
|
+
agentId: 'agentId',
|
|
2197
|
+
deviceType: LoginOption.EXTENSION,
|
|
2198
|
+
dn: '1001',
|
|
2199
|
+
teamId: 'teamId',
|
|
2200
|
+
lastStateChangeTimestamp: 1738575135188,
|
|
2201
|
+
lastIdleCodeChangeTimestamp: 1738575135189,
|
|
2202
|
+
},
|
|
2203
|
+
});
|
|
2204
|
+
|
|
2205
|
+
const publishSpy = jest
|
|
2206
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
2207
|
+
.mockResolvedValue(undefined);
|
|
2208
|
+
const subscribeSpy = jest
|
|
2209
|
+
.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'subscribe')
|
|
2210
|
+
.mockImplementation(() => {});
|
|
2211
|
+
|
|
2212
|
+
await webex.cc['silentRelogin']();
|
|
2213
|
+
|
|
2214
|
+
expect(publishSpy).toHaveBeenCalledWith(true, {userId: 'user-123'});
|
|
2215
|
+
expect(subscribeSpy).toHaveBeenCalledWith('agentId', expect.any(Function));
|
|
2216
|
+
});
|
|
2217
|
+
|
|
2218
|
+
it('should force-publish usersub false on silent relogin when enableWxBetterTogether is false at init', async () => {
|
|
2219
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: false};
|
|
2220
|
+
webex.internal.device = {userId: 'user-123', url: 'https://wdm.example.com/devices/dev-1'};
|
|
2221
|
+
webex.cc.agentConfig = {
|
|
2222
|
+
agentId: 'agentId',
|
|
2223
|
+
agentProfileID: 'test-agent-profile-id',
|
|
2224
|
+
isAgentLoggedIn: false,
|
|
2225
|
+
} as Profile;
|
|
2226
|
+
|
|
2227
|
+
jest.spyOn(webex.cc.services.agent, 'reload').mockResolvedValue({
|
|
2228
|
+
data: {
|
|
2229
|
+
auxCodeId: 'auxCodeId',
|
|
2230
|
+
agentId: 'agentId',
|
|
2231
|
+
deviceType: LoginOption.EXTENSION,
|
|
2232
|
+
dn: '1001',
|
|
2233
|
+
teamId: 'teamId',
|
|
2234
|
+
lastStateChangeTimestamp: 1738575135188,
|
|
2235
|
+
lastIdleCodeChangeTimestamp: 1738575135189,
|
|
2236
|
+
},
|
|
2237
|
+
});
|
|
2238
|
+
|
|
2239
|
+
const publishSpy = jest
|
|
2240
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
2241
|
+
.mockResolvedValue(undefined);
|
|
2242
|
+
const teardownSpy = jest.spyOn(webex.cc['webexCrossClientService'], 'teardown');
|
|
2243
|
+
const unsubscribeSpy = jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'unsubscribe');
|
|
2244
|
+
|
|
2245
|
+
await webex.cc['silentRelogin']();
|
|
2246
|
+
|
|
2247
|
+
expect(publishSpy).toHaveBeenCalledWith(false, {userId: 'user-123'});
|
|
2248
|
+
expect(teardownSpy).toHaveBeenCalled();
|
|
2249
|
+
expect(unsubscribeSpy).toHaveBeenCalled();
|
|
2250
|
+
});
|
|
1303
2251
|
});
|
|
1304
2252
|
|
|
1305
2253
|
describe('setupEventListeners()', () => {
|
|
@@ -1439,6 +2387,27 @@ describe('webex.cc', () => {
|
|
|
1439
2387
|
});
|
|
1440
2388
|
});
|
|
1441
2389
|
|
|
2390
|
+
describe('getQueues', () => {
|
|
2391
|
+
it('delegates to the queue service when successful', async () => {
|
|
2392
|
+
const mockQueuesResponse = [{queueId: 'queue1', queueName: 'Queue 1'}];
|
|
2393
|
+
const queueSpy = jest
|
|
2394
|
+
.spyOn(webex.cc.queue, 'getQueues')
|
|
2395
|
+
.mockResolvedValue(mockQueuesResponse as any);
|
|
2396
|
+
|
|
2397
|
+
const result = await webex.cc.getQueues({page: 1});
|
|
2398
|
+
|
|
2399
|
+
expect(queueSpy).toHaveBeenCalledWith({page: 1});
|
|
2400
|
+
expect(result).toBe(mockQueuesResponse);
|
|
2401
|
+
});
|
|
2402
|
+
|
|
2403
|
+
it('propagates queue service errors', async () => {
|
|
2404
|
+
const error = new Error('Test error.');
|
|
2405
|
+
jest.spyOn(webex.cc.queue, 'getQueues').mockRejectedValue(error);
|
|
2406
|
+
|
|
2407
|
+
await expect(webex.cc.getQueues()).rejects.toThrow('Test error.');
|
|
2408
|
+
});
|
|
2409
|
+
});
|
|
2410
|
+
|
|
1442
2411
|
describe('uploadLogs', () => {
|
|
1443
2412
|
it('should upload logs successfully', async () => {
|
|
1444
2413
|
const uploadLogsMock = jest.spyOn(webex.cc.webexRequest, 'uploadLogs').mockResolvedValue({
|
|
@@ -1468,6 +2437,7 @@ describe('webex.cc', () => {
|
|
|
1468
2437
|
|
|
1469
2438
|
describe('unregister', () => {
|
|
1470
2439
|
let mockWebSocketManager;
|
|
2440
|
+
let mockRTDWebSocketManager;
|
|
1471
2441
|
let mercuryDisconnectSpy;
|
|
1472
2442
|
let deviceUnregisterSpy;
|
|
1473
2443
|
|
|
@@ -1484,8 +2454,15 @@ describe('webex.cc', () => {
|
|
|
1484
2454
|
off: jest.fn(),
|
|
1485
2455
|
on: jest.fn(),
|
|
1486
2456
|
};
|
|
2457
|
+
mockRTDWebSocketManager = {
|
|
2458
|
+
isSocketClosed: false,
|
|
2459
|
+
close: jest.fn(),
|
|
2460
|
+
off: jest.fn(),
|
|
2461
|
+
on: jest.fn(),
|
|
2462
|
+
};
|
|
1487
2463
|
|
|
1488
2464
|
webex.cc.services.webSocketManager = mockWebSocketManager;
|
|
2465
|
+
webex.cc.services.rtdWebSocketManager = mockRTDWebSocketManager;
|
|
1489
2466
|
|
|
1490
2467
|
webex.internal = webex.internal || {};
|
|
1491
2468
|
webex.internal.mercury = {
|
|
@@ -1502,6 +2479,25 @@ describe('webex.cc', () => {
|
|
|
1502
2479
|
});
|
|
1503
2480
|
|
|
1504
2481
|
it('should unregister successfully and clean up all resources when webrtc is enabled', async () => {
|
|
2482
|
+
webex.cc.services.rtdWebSocketManager = {
|
|
2483
|
+
isSocketClosed: false,
|
|
2484
|
+
close: jest.fn(),
|
|
2485
|
+
off: jest.fn(),
|
|
2486
|
+
on: jest.fn(),
|
|
2487
|
+
} as any;
|
|
2488
|
+
|
|
2489
|
+
webex.internal.device = {
|
|
2490
|
+
...webex.internal.device,
|
|
2491
|
+
userId: 'user-123',
|
|
2492
|
+
unregister: jest.fn().mockResolvedValue(undefined),
|
|
2493
|
+
};
|
|
2494
|
+
deviceUnregisterSpy = jest.spyOn(webex.internal.device, 'unregister');
|
|
2495
|
+
const publishSpy = jest
|
|
2496
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
2497
|
+
.mockResolvedValue(undefined);
|
|
2498
|
+
const teardownSpy = jest.spyOn(webex.cc['webexCrossClientService'], 'teardown');
|
|
2499
|
+
const unsubscribeSpy = jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'unsubscribe');
|
|
2500
|
+
|
|
1505
2501
|
await webex.cc.deregister();
|
|
1506
2502
|
|
|
1507
2503
|
expect(mockTaskManager.off).toHaveBeenCalledWith(
|
|
@@ -1512,13 +2508,25 @@ describe('webex.cc', () => {
|
|
|
1512
2508
|
TASK_EVENTS.TASK_HYDRATE,
|
|
1513
2509
|
expect.any(Function)
|
|
1514
2510
|
);
|
|
2511
|
+
expect(mockTaskManager.off).toHaveBeenCalledWith(
|
|
2512
|
+
TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE,
|
|
2513
|
+
expect.any(Function)
|
|
2514
|
+
);
|
|
1515
2515
|
expect(mockWebSocketManager.off).toHaveBeenCalledWith('message', expect.any(Function));
|
|
2516
|
+
expect(webex.cc.services.rtdWebSocketManager.off).toHaveBeenCalledWith(
|
|
2517
|
+
'message',
|
|
2518
|
+
expect.any(Function)
|
|
2519
|
+
);
|
|
1516
2520
|
expect(webex.cc.services.connectionService.off).toHaveBeenCalledWith(
|
|
1517
2521
|
'connectionLost',
|
|
1518
2522
|
expect.any(Function)
|
|
1519
2523
|
);
|
|
1520
2524
|
|
|
1521
2525
|
expect(mockWebSocketManager.close).toHaveBeenCalledWith(false, 'Unregistering the SDK');
|
|
2526
|
+
expect(webex.cc.services.rtdWebSocketManager.close).toHaveBeenCalledWith(
|
|
2527
|
+
false,
|
|
2528
|
+
'Unregistering the RTD websocket'
|
|
2529
|
+
);
|
|
1522
2530
|
expect(webex.cc.agentConfig).toBeNull();
|
|
1523
2531
|
|
|
1524
2532
|
expect(webex.internal.mercury.off).toHaveBeenCalledWith('online');
|
|
@@ -1560,6 +2568,13 @@ describe('webex.cc', () => {
|
|
|
1560
2568
|
const [, hydrateCallback] = hydrateCalls[0];
|
|
1561
2569
|
expect(hydrateCallback).toBe(webex.cc['handleTaskHydrate']);
|
|
1562
2570
|
|
|
2571
|
+
const multiLoginHydrateCalls = mockTaskManager.off.mock.calls.filter(
|
|
2572
|
+
([evt]) => evt === TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE
|
|
2573
|
+
);
|
|
2574
|
+
expect(multiLoginHydrateCalls).toHaveLength(1);
|
|
2575
|
+
const [, multiLoginHydrateCallback] = multiLoginHydrateCalls[0];
|
|
2576
|
+
expect(multiLoginHydrateCallback).toBe(webex.cc['handleTaskMultiLoginHydrate']);
|
|
2577
|
+
|
|
1563
2578
|
const messageCalls = mockWebSocketManager.off.mock.calls.filter(([evt]) => evt === 'message');
|
|
1564
2579
|
expect(messageCalls).toHaveLength(1);
|
|
1565
2580
|
const [, messageCallback] = messageCalls[0];
|
|
@@ -1571,6 +2586,39 @@ describe('webex.cc', () => {
|
|
|
1571
2586
|
expect(connectionCalls).toHaveLength(1);
|
|
1572
2587
|
const [, connectionCallback] = connectionCalls[0];
|
|
1573
2588
|
expect(connectionCallback).toBe(webex.cc['handleConnectionLost']);
|
|
2589
|
+
|
|
2590
|
+
expect(publishSpy).not.toHaveBeenCalled();
|
|
2591
|
+
expect(teardownSpy).toHaveBeenCalled();
|
|
2592
|
+
expect(unsubscribeSpy).toHaveBeenCalled();
|
|
2593
|
+
});
|
|
2594
|
+
|
|
2595
|
+
it('should publish usersub false before device unregister when wxApp is active on deregister', async () => {
|
|
2596
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: true};
|
|
2597
|
+
webex.cc['webexCrossClientService'].answerCallsState = true;
|
|
2598
|
+
webex.internal.device = {
|
|
2599
|
+
userId: 'user-123',
|
|
2600
|
+
url: 'https://wdm.example.com/devices/dev-1',
|
|
2601
|
+
unregister: jest.fn().mockResolvedValue(undefined),
|
|
2602
|
+
};
|
|
2603
|
+
deviceUnregisterSpy = jest.spyOn(webex.internal.device, 'unregister');
|
|
2604
|
+
|
|
2605
|
+
const callOrder: string[] = [];
|
|
2606
|
+
jest
|
|
2607
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
2608
|
+
.mockImplementation(async (enable: boolean) => {
|
|
2609
|
+
if (enable === false && !webex.internal.device.url) {
|
|
2610
|
+
throw new Error('Device URL is unavailable for cross-client publish');
|
|
2611
|
+
}
|
|
2612
|
+
callOrder.push(enable ? 'publish:true' : 'publish:false');
|
|
2613
|
+
});
|
|
2614
|
+
deviceUnregisterSpy.mockImplementation(async () => {
|
|
2615
|
+
callOrder.push('device.unregister');
|
|
2616
|
+
webex.internal.device.url = undefined;
|
|
2617
|
+
});
|
|
2618
|
+
|
|
2619
|
+
await webex.cc.deregister();
|
|
2620
|
+
|
|
2621
|
+
expect(callOrder).toEqual(['publish:false', 'device.unregister']);
|
|
1574
2622
|
});
|
|
1575
2623
|
|
|
1576
2624
|
it('should skip webCallingService and internal cleanup when webrtc is disabled', async () => {
|
|
@@ -1585,6 +2633,10 @@ describe('webex.cc', () => {
|
|
|
1585
2633
|
TASK_EVENTS.TASK_HYDRATE,
|
|
1586
2634
|
expect.any(Function)
|
|
1587
2635
|
);
|
|
2636
|
+
expect(mockTaskManager.off).toHaveBeenCalledWith(
|
|
2637
|
+
TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE,
|
|
2638
|
+
expect.any(Function)
|
|
2639
|
+
);
|
|
1588
2640
|
expect(mockWebSocketManager.off).toHaveBeenCalledWith('message', expect.any(Function));
|
|
1589
2641
|
expect(webex.cc.services.connectionService.off).toHaveBeenCalledWith(
|
|
1590
2642
|
'connectionLost',
|
|
@@ -1594,24 +2646,155 @@ describe('webex.cc', () => {
|
|
|
1594
2646
|
expect(webex.internal.mercury.off).not.toHaveBeenCalled();
|
|
1595
2647
|
expect(mercuryDisconnectSpy).not.toHaveBeenCalled();
|
|
1596
2648
|
expect(deviceUnregisterSpy).not.toHaveBeenCalled();
|
|
2649
|
+
expect(mockRTDWebSocketManager.close).toHaveBeenCalledWith(false, 'Unregistering the SDK');
|
|
2650
|
+
});
|
|
2651
|
+
|
|
2652
|
+
it('should skip internal mercury cleanup when loginVoiceOptions does not include BROWSER', async () => {
|
|
2653
|
+
webex.cc.agentConfig = {
|
|
2654
|
+
agentId: 'agentId',
|
|
2655
|
+
webRtcEnabled: true,
|
|
2656
|
+
loginVoiceOptions: ['EXTENSION'],
|
|
2657
|
+
};
|
|
2658
|
+
|
|
2659
|
+
await webex.cc.deregister();
|
|
2660
|
+
|
|
2661
|
+
// mercury listeners & disconnect should not run
|
|
2662
|
+
expect(webex.internal.mercury.off).not.toHaveBeenCalled();
|
|
2663
|
+
expect(mercuryDisconnectSpy).not.toHaveBeenCalled();
|
|
2664
|
+
expect(deviceUnregisterSpy).not.toHaveBeenCalled();
|
|
2665
|
+
|
|
2666
|
+
expect(mockWebSocketManager.close).toHaveBeenCalledWith(false, 'Unregistering the SDK');
|
|
2667
|
+
expect(mockRTDWebSocketManager.close).toHaveBeenCalledWith(false, 'Unregistering the SDK');
|
|
2668
|
+
expect(webex.cc.agentConfig).toBeNull();
|
|
2669
|
+
});
|
|
2670
|
+
|
|
2671
|
+
it('should teardown wxApp state on deregister even when usersub publish fails', async () => {
|
|
2672
|
+
webex.cc.agentConfig = {
|
|
2673
|
+
agentId: 'agentId',
|
|
2674
|
+
webRtcEnabled: false,
|
|
2675
|
+
loginVoiceOptions: [LoginOption.EXTENSION],
|
|
2676
|
+
};
|
|
2677
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: true};
|
|
2678
|
+
webex.cc['webexCrossClientService'].answerCallsState = true;
|
|
2679
|
+
webex.internal.device = {
|
|
2680
|
+
userId: 'user-123',
|
|
2681
|
+
unregister: jest.fn().mockResolvedValue(undefined),
|
|
2682
|
+
};
|
|
2683
|
+
|
|
2684
|
+
jest
|
|
2685
|
+
.spyOn(webex.cc as never, 'publishAnswerOnWebexCrossClientState' as never)
|
|
2686
|
+
.mockRejectedValue(new Error('usersub publish failed'));
|
|
2687
|
+
const teardownSpy = jest.spyOn(webex.cc['webexCrossClientService'], 'teardown');
|
|
2688
|
+
const unsubscribeSpy = jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'unsubscribe');
|
|
2689
|
+
|
|
2690
|
+
await expect(webex.cc.deregister()).rejects.toThrow('usersub publish failed');
|
|
2691
|
+
|
|
2692
|
+
expect(teardownSpy).toHaveBeenCalled();
|
|
2693
|
+
expect(unsubscribeSpy).toHaveBeenCalled();
|
|
2694
|
+
expect(webex.cc.isWxBetterTogetherEnabled()).toBe(false);
|
|
2695
|
+
expect(mockWebSocketManager.close).toHaveBeenCalledWith(false, 'Unregistering the SDK');
|
|
2696
|
+
expect(webex.cc.agentConfig).toBeNull();
|
|
2697
|
+
});
|
|
2698
|
+
|
|
2699
|
+
it('should publish forced false during teardown when enable publish is still in flight', async () => {
|
|
2700
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: true};
|
|
2701
|
+
webex.internal.device = {
|
|
2702
|
+
userId: 'user-123',
|
|
2703
|
+
url: 'https://wdm.example.com/devices/dev-1',
|
|
2704
|
+
};
|
|
2705
|
+
|
|
2706
|
+
let resolveEnable: () => void = () => {};
|
|
2707
|
+
const enableGate = new Promise<void>((resolve) => {
|
|
2708
|
+
resolveEnable = resolve;
|
|
2709
|
+
});
|
|
2710
|
+
|
|
2711
|
+
const publishSpy = jest
|
|
2712
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
2713
|
+
.mockImplementation(async (enable: boolean) => {
|
|
2714
|
+
if (enable) {
|
|
2715
|
+
await enableGate;
|
|
2716
|
+
}
|
|
2717
|
+
});
|
|
2718
|
+
jest
|
|
2719
|
+
.spyOn(webex.cc as any, 'releaseWxAppMercuryResources')
|
|
2720
|
+
.mockResolvedValue(undefined);
|
|
2721
|
+
|
|
2722
|
+
expect(webex.cc['webexCrossClientService'].answerCallsState).toBe(false);
|
|
2723
|
+
|
|
2724
|
+
const enableInFlight = (webex.cc as any).publishAnswerOnWebexCrossClientState(true);
|
|
2725
|
+
|
|
2726
|
+
await (webex.cc as any).teardownWxAppLocalState();
|
|
2727
|
+
|
|
2728
|
+
expect(publishSpy).toHaveBeenCalledWith(false, {userId: 'user-123'});
|
|
2729
|
+
|
|
2730
|
+
resolveEnable();
|
|
2731
|
+
await enableInFlight;
|
|
1597
2732
|
});
|
|
1598
2733
|
|
|
1599
|
-
it('should
|
|
2734
|
+
it('should release wxApp-owned Mercury and device on deregister', async () => {
|
|
1600
2735
|
webex.cc.agentConfig = {
|
|
1601
2736
|
agentId: 'agentId',
|
|
1602
|
-
webRtcEnabled:
|
|
1603
|
-
loginVoiceOptions: [
|
|
2737
|
+
webRtcEnabled: false,
|
|
2738
|
+
loginVoiceOptions: [LoginOption.EXTENSION],
|
|
1604
2739
|
};
|
|
2740
|
+
webex.cc['wxAppMercuryConnectedByCc'] = true;
|
|
2741
|
+
webex.cc['wxAppDeviceRegisteredByCc'] = true;
|
|
2742
|
+
webex.internal.mercury.connected = true;
|
|
2743
|
+
webex.internal.device.registered = true;
|
|
2744
|
+
|
|
2745
|
+
const mercuryDisconnectSpy = jest
|
|
2746
|
+
.spyOn(webex.internal.mercury, 'disconnect')
|
|
2747
|
+
.mockResolvedValue(undefined);
|
|
2748
|
+
const deviceUnregisterSpy = jest
|
|
2749
|
+
.spyOn(webex.internal.device, 'unregister')
|
|
2750
|
+
.mockResolvedValue(undefined);
|
|
1605
2751
|
|
|
1606
2752
|
await webex.cc.deregister();
|
|
1607
2753
|
|
|
1608
|
-
|
|
1609
|
-
expect(
|
|
1610
|
-
expect(
|
|
1611
|
-
expect(
|
|
2754
|
+
expect(mercuryDisconnectSpy).toHaveBeenCalled();
|
|
2755
|
+
expect(deviceUnregisterSpy).toHaveBeenCalled();
|
|
2756
|
+
expect(webex.cc['wxAppMercuryConnectedByCc']).toBe(false);
|
|
2757
|
+
expect(webex.cc['wxAppDeviceRegisteredByCc']).toBe(false);
|
|
2758
|
+
});
|
|
1612
2759
|
|
|
1613
|
-
|
|
1614
|
-
|
|
2760
|
+
it('should retain wxApp Mercury ownership when disconnect fails and retry cleanup', async () => {
|
|
2761
|
+
webex.cc['wxAppMercuryConnectedByCc'] = true;
|
|
2762
|
+
webex.internal.mercury.connected = true;
|
|
2763
|
+
|
|
2764
|
+
const mercuryDisconnectSpy = jest
|
|
2765
|
+
.spyOn(webex.internal.mercury, 'disconnect')
|
|
2766
|
+
.mockRejectedValueOnce(new Error('disconnect failed'))
|
|
2767
|
+
.mockResolvedValueOnce(undefined);
|
|
2768
|
+
|
|
2769
|
+
await webex.cc['releaseWxAppMercuryResources']();
|
|
2770
|
+
|
|
2771
|
+
expect(mercuryDisconnectSpy).toHaveBeenCalledTimes(1);
|
|
2772
|
+
expect(webex.cc['wxAppMercuryConnectedByCc']).toBe(true);
|
|
2773
|
+
|
|
2774
|
+
await webex.cc['releaseWxAppMercuryResources']();
|
|
2775
|
+
|
|
2776
|
+
expect(mercuryDisconnectSpy).toHaveBeenCalledTimes(2);
|
|
2777
|
+
expect(webex.cc['wxAppMercuryConnectedByCc']).toBe(false);
|
|
2778
|
+
});
|
|
2779
|
+
|
|
2780
|
+
it('should retain wxApp device ownership when unregister fails and retry cleanup', async () => {
|
|
2781
|
+
webex.cc['wxAppDeviceRegisteredByCc'] = true;
|
|
2782
|
+
webex.internal.device.registered = true;
|
|
2783
|
+
|
|
2784
|
+
const deviceUnregisterSpy = jest
|
|
2785
|
+
.spyOn(webex.internal.device, 'unregister')
|
|
2786
|
+
.mockRejectedValueOnce(new Error('unregister failed'))
|
|
2787
|
+
.mockResolvedValueOnce(undefined);
|
|
2788
|
+
|
|
2789
|
+
await webex.cc['releaseWxAppMercuryResources']();
|
|
2790
|
+
|
|
2791
|
+
expect(deviceUnregisterSpy).toHaveBeenCalledTimes(1);
|
|
2792
|
+
expect(webex.cc['wxAppDeviceRegisteredByCc']).toBe(true);
|
|
2793
|
+
|
|
2794
|
+
await webex.cc['releaseWxAppMercuryResources']();
|
|
2795
|
+
|
|
2796
|
+
expect(deviceUnregisterSpy).toHaveBeenCalledTimes(2);
|
|
2797
|
+
expect(webex.cc['wxAppDeviceRegisteredByCc']).toBe(false);
|
|
1615
2798
|
});
|
|
1616
2799
|
|
|
1617
2800
|
it('should handle errors during unregister and track metrics', async () => {
|
|
@@ -1628,6 +2811,10 @@ describe('webex.cc', () => {
|
|
|
1628
2811
|
TASK_EVENTS.TASK_HYDRATE,
|
|
1629
2812
|
expect.any(Function)
|
|
1630
2813
|
);
|
|
2814
|
+
expect(mockTaskManager.off).toHaveBeenCalledWith(
|
|
2815
|
+
TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE,
|
|
2816
|
+
expect.any(Function)
|
|
2817
|
+
);
|
|
1631
2818
|
|
|
1632
2819
|
expect(LoggerProxy.error).toHaveBeenCalledWith(`Error during deregister: ${mockError}`, {
|
|
1633
2820
|
module: CC_FILE,
|
|
@@ -1650,7 +2837,7 @@ describe('webex.cc', () => {
|
|
|
1650
2837
|
|
|
1651
2838
|
beforeEach(() => {
|
|
1652
2839
|
emitSpy = jest.spyOn(webex.cc, 'emit');
|
|
1653
|
-
messageCallback =
|
|
2840
|
+
messageCallback = webex.cc['handleWebsocketMessage'];
|
|
1654
2841
|
});
|
|
1655
2842
|
|
|
1656
2843
|
it('should emit AGENT_STATION_LOGIN_SUCCESS on CC_EVENTS.AGENT_STATION_LOGIN_SUCCESS with mapped payload', () => {
|
|
@@ -1850,7 +3037,7 @@ describe('webex.cc', () => {
|
|
|
1850
3037
|
);
|
|
1851
3038
|
|
|
1852
3039
|
expect(webex.cc.stationLogout).toHaveBeenCalledWith({
|
|
1853
|
-
logoutReason: 'User requested agent
|
|
3040
|
+
logoutReason: 'User requested agent profile update',
|
|
1854
3041
|
});
|
|
1855
3042
|
expect(webex.cc.stationLogin).toHaveBeenCalledWith({
|
|
1856
3043
|
teamId: 'teamId',
|
|
@@ -1860,6 +3047,54 @@ describe('webex.cc', () => {
|
|
|
1860
3047
|
expect(result).toEqual(mockResp);
|
|
1861
3048
|
});
|
|
1862
3049
|
|
|
3050
|
+
it('should preserve enableWxBetterTogether init flag across profile update logout/login', async () => {
|
|
3051
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: true};
|
|
3052
|
+
webex.internal.device = {userId: 'user-123', url: 'https://wdm.example.com/devices/dev-1'};
|
|
3053
|
+
webex.cc.agentConfig = {
|
|
3054
|
+
...webex.cc.agentConfig,
|
|
3055
|
+
agentId: 'agentId',
|
|
3056
|
+
webRtcEnabled: false,
|
|
3057
|
+
loginVoiceOptions: ['BROWSER', 'EXTENSION'],
|
|
3058
|
+
} as Profile;
|
|
3059
|
+
webex.cc.webCallingService.loginOption = LoginOption.BROWSER;
|
|
3060
|
+
webex.cc.webCallingService.setLoginOption = jest.fn((loginOption: LoginOption) => {
|
|
3061
|
+
webex.cc.webCallingService.loginOption = loginOption;
|
|
3062
|
+
});
|
|
3063
|
+
webex.cc['webexCrossClientService'].answerCallsState = true;
|
|
3064
|
+
|
|
3065
|
+
const publishSpy = jest
|
|
3066
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
3067
|
+
.mockResolvedValue(undefined);
|
|
3068
|
+
jest
|
|
3069
|
+
.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'subscribe')
|
|
3070
|
+
.mockImplementation(() => {});
|
|
3071
|
+
jest.spyOn(webex.cc as any, 'releaseWxAppMercuryResources').mockResolvedValue(undefined);
|
|
3072
|
+
|
|
3073
|
+
jest.spyOn(webex.cc.services.agent, 'logout').mockResolvedValue({
|
|
3074
|
+
trackingId: 'track-logout',
|
|
3075
|
+
} as StationLogoutResponse);
|
|
3076
|
+
jest.spyOn(webex.cc.services.agent, 'stationLogin').mockResolvedValue({
|
|
3077
|
+
data: {
|
|
3078
|
+
agentId: 'agentId',
|
|
3079
|
+
teamId: 'teamId',
|
|
3080
|
+
channelsMap: {chat: [], email: [], social: [], telephony: []},
|
|
3081
|
+
},
|
|
3082
|
+
trackingId: 'track-login',
|
|
3083
|
+
} as StationLoginSuccess);
|
|
3084
|
+
|
|
3085
|
+
webex.internal.mercury.connected = true;
|
|
3086
|
+
|
|
3087
|
+
await webex.cc.updateAgentProfile({
|
|
3088
|
+
teamId: 'teamId',
|
|
3089
|
+
loginOption: LoginOption.EXTENSION,
|
|
3090
|
+
dialNumber: '1001',
|
|
3091
|
+
});
|
|
3092
|
+
|
|
3093
|
+
expect(webex.cc.isWxBetterTogetherEnabled()).toBe(true);
|
|
3094
|
+
expect(publishSpy).toHaveBeenCalledWith(false, {userId: 'user-123'});
|
|
3095
|
+
expect(publishSpy).toHaveBeenCalledWith(true, {userId: 'user-123'});
|
|
3096
|
+
});
|
|
3097
|
+
|
|
1863
3098
|
it('should use provided teamId if passed in payload', async () => {
|
|
1864
3099
|
const dataWithTeam = {
|
|
1865
3100
|
teamId: 'newTeam',
|
|
@@ -1934,24 +3169,26 @@ describe('webex.cc', () => {
|
|
|
1934
3169
|
});
|
|
1935
3170
|
});
|
|
1936
3171
|
|
|
1937
|
-
it('should
|
|
1938
|
-
webex.cc.webCallingService.loginOption = LoginOption.
|
|
3172
|
+
it('should allow update when loginOption and teamId are unchanged (e.g. dialNumber or profile refresh)', async () => {
|
|
3173
|
+
webex.cc.webCallingService.loginOption = LoginOption.AGENT_DN;
|
|
1939
3174
|
const data = {
|
|
1940
3175
|
teamId: 'teamId',
|
|
1941
|
-
loginOption: LoginOption.
|
|
1942
|
-
dialNumber: '',
|
|
1943
|
-
};
|
|
1944
|
-
const
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
3176
|
+
loginOption: LoginOption.AGENT_DN,
|
|
3177
|
+
dialNumber: '1234',
|
|
3178
|
+
};
|
|
3179
|
+
const logoutSpy = jest.spyOn(webex.cc, 'stationLogout').mockResolvedValue({});
|
|
3180
|
+
const loginSpy = jest.spyOn(webex.cc, 'stationLogin').mockResolvedValue({
|
|
3181
|
+
type: 'AgentDeviceTypeUpdateSuccess',
|
|
3182
|
+
} as any);
|
|
3183
|
+
|
|
3184
|
+
await expect(webex.cc.updateAgentProfile(data)).resolves.toBeDefined();
|
|
3185
|
+
expect(logoutSpy).toHaveBeenCalledWith({
|
|
3186
|
+
logoutReason: 'User requested agent profile update',
|
|
3187
|
+
});
|
|
3188
|
+
expect(loginSpy).toHaveBeenCalledWith({
|
|
3189
|
+
teamId: data.teamId,
|
|
3190
|
+
loginOption: data.loginOption,
|
|
3191
|
+
dialNumber: data.dialNumber,
|
|
1955
3192
|
});
|
|
1956
3193
|
});
|
|
1957
3194
|
|
|
@@ -2104,7 +3341,9 @@ describe('webex.cc', () => {
|
|
|
2104
3341
|
const detailedError = new Error('Detailed service error');
|
|
2105
3342
|
getErrorDetailsSpy.mockReturnValue({error: detailedError});
|
|
2106
3343
|
|
|
2107
|
-
await expect(webex.cc.getOutdialAniEntries(mockParams)).rejects.toThrow(
|
|
3344
|
+
await expect(webex.cc.getOutdialAniEntries(mockParams)).rejects.toThrow(
|
|
3345
|
+
'Detailed service error'
|
|
3346
|
+
);
|
|
2108
3347
|
|
|
2109
3348
|
// Verify failure metrics are tracked
|
|
2110
3349
|
expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
|
|
@@ -2128,11 +3367,7 @@ describe('webex.cc', () => {
|
|
|
2128
3367
|
);
|
|
2129
3368
|
|
|
2130
3369
|
// Verify getErrorDetails was called
|
|
2131
|
-
expect(getErrorDetailsSpy).toHaveBeenCalledWith(
|
|
2132
|
-
mockError,
|
|
2133
|
-
'getOutdialAniEntries',
|
|
2134
|
-
CC_FILE
|
|
2135
|
-
);
|
|
3370
|
+
expect(getErrorDetailsSpy).toHaveBeenCalledWith(mockError, 'getOutdialAniEntries', CC_FILE);
|
|
2136
3371
|
});
|
|
2137
3372
|
|
|
2138
3373
|
it('should throw error when orgId is not found', async () => {
|
|
@@ -2180,4 +3415,503 @@ describe('webex.cc', () => {
|
|
|
2180
3415
|
);
|
|
2181
3416
|
});
|
|
2182
3417
|
});
|
|
3418
|
+
|
|
3419
|
+
describe('acceptPreviewContact', () => {
|
|
3420
|
+
const previewPayload = {
|
|
3421
|
+
interactionId: 'interaction-123',
|
|
3422
|
+
campaignId: 'campaign-456',
|
|
3423
|
+
};
|
|
3424
|
+
|
|
3425
|
+
it('should accept preview contact successfully', async () => {
|
|
3426
|
+
const mockResponse = {trackingId: 'track-123'} as AgentContact;
|
|
3427
|
+
|
|
3428
|
+
const acceptPreviewContactMock = jest
|
|
3429
|
+
.spyOn(webex.cc.services.dialer, 'acceptPreviewContact')
|
|
3430
|
+
.mockResolvedValue(mockResponse);
|
|
3431
|
+
|
|
3432
|
+
const result = await webex.cc.acceptPreviewContact(previewPayload);
|
|
3433
|
+
|
|
3434
|
+
expect(LoggerProxy.info).toHaveBeenCalledWith('Accepting campaign preview contact', {
|
|
3435
|
+
module: CC_FILE,
|
|
3436
|
+
method: 'acceptPreviewContact',
|
|
3437
|
+
});
|
|
3438
|
+
expect(LoggerProxy.log).toHaveBeenCalledWith(
|
|
3439
|
+
'Campaign preview contact accepted successfully',
|
|
3440
|
+
{
|
|
3441
|
+
module: CC_FILE,
|
|
3442
|
+
method: 'acceptPreviewContact',
|
|
3443
|
+
trackingId: 'track-123',
|
|
3444
|
+
interactionId: previewPayload.interactionId,
|
|
3445
|
+
}
|
|
3446
|
+
);
|
|
3447
|
+
|
|
3448
|
+
expect(acceptPreviewContactMock).toHaveBeenCalledWith({data: previewPayload});
|
|
3449
|
+
expect(result).toEqual(mockResponse);
|
|
3450
|
+
});
|
|
3451
|
+
|
|
3452
|
+
it('should handle error during acceptPreviewContact', async () => {
|
|
3453
|
+
getErrorDetailsSpy.mockRestore();
|
|
3454
|
+
getErrorDetailsSpy = jest.spyOn(Utils, 'getErrorDetails');
|
|
3455
|
+
|
|
3456
|
+
const error = {
|
|
3457
|
+
details: {
|
|
3458
|
+
trackingId: '1234',
|
|
3459
|
+
data: {
|
|
3460
|
+
reason: 'Error while performing acceptPreviewContact',
|
|
3461
|
+
},
|
|
3462
|
+
},
|
|
3463
|
+
};
|
|
3464
|
+
|
|
3465
|
+
jest.spyOn(webex.cc.services.dialer, 'acceptPreviewContact').mockRejectedValue(error);
|
|
3466
|
+
|
|
3467
|
+
await expect(webex.cc.acceptPreviewContact(previewPayload)).rejects.toThrow(
|
|
3468
|
+
error.details.data.reason
|
|
3469
|
+
);
|
|
3470
|
+
|
|
3471
|
+
expect(LoggerProxy.info).toHaveBeenCalledWith('Accepting campaign preview contact', {
|
|
3472
|
+
module: CC_FILE,
|
|
3473
|
+
method: 'acceptPreviewContact',
|
|
3474
|
+
});
|
|
3475
|
+
expect(LoggerProxy.error).toHaveBeenCalledWith(
|
|
3476
|
+
`acceptPreviewContact failed with reason: ${error.details.data.reason}`,
|
|
3477
|
+
{module: CC_FILE, method: 'acceptPreviewContact', trackingId: error.details.trackingId}
|
|
3478
|
+
);
|
|
3479
|
+
expect(getErrorDetailsSpy).toHaveBeenCalledWith(error, 'acceptPreviewContact', CC_FILE);
|
|
3480
|
+
});
|
|
3481
|
+
});
|
|
3482
|
+
|
|
3483
|
+
describe('skipPreviewContact', () => {
|
|
3484
|
+
const previewPayload = {
|
|
3485
|
+
interactionId: 'interaction-123',
|
|
3486
|
+
campaignId: 'campaign-456',
|
|
3487
|
+
};
|
|
3488
|
+
|
|
3489
|
+
it('should skip preview contact successfully', async () => {
|
|
3490
|
+
const mockResponse = {trackingId: 'track-123'} as AgentContact;
|
|
3491
|
+
|
|
3492
|
+
const skipPreviewContactMock = jest
|
|
3493
|
+
.spyOn(webex.cc.services.dialer, 'skipPreviewContact')
|
|
3494
|
+
.mockResolvedValue(mockResponse);
|
|
3495
|
+
|
|
3496
|
+
const result = await webex.cc.skipPreviewContact(previewPayload);
|
|
3497
|
+
|
|
3498
|
+
expect(LoggerProxy.info).toHaveBeenCalledWith('Skipping campaign preview contact', {
|
|
3499
|
+
module: CC_FILE,
|
|
3500
|
+
method: 'skipPreviewContact',
|
|
3501
|
+
});
|
|
3502
|
+
expect(LoggerProxy.log).toHaveBeenCalledWith(
|
|
3503
|
+
'Campaign preview contact skipped successfully',
|
|
3504
|
+
{
|
|
3505
|
+
module: CC_FILE,
|
|
3506
|
+
method: 'skipPreviewContact',
|
|
3507
|
+
trackingId: 'track-123',
|
|
3508
|
+
interactionId: previewPayload.interactionId,
|
|
3509
|
+
}
|
|
3510
|
+
);
|
|
3511
|
+
|
|
3512
|
+
expect(skipPreviewContactMock).toHaveBeenCalledWith({data: previewPayload});
|
|
3513
|
+
expect(result).toEqual(mockResponse);
|
|
3514
|
+
});
|
|
3515
|
+
|
|
3516
|
+
it('should handle error during skipPreviewContact', async () => {
|
|
3517
|
+
getErrorDetailsSpy.mockRestore();
|
|
3518
|
+
getErrorDetailsSpy = jest.spyOn(Utils, 'getErrorDetails');
|
|
3519
|
+
|
|
3520
|
+
const error = {
|
|
3521
|
+
details: {
|
|
3522
|
+
trackingId: '1234',
|
|
3523
|
+
data: {
|
|
3524
|
+
reason: 'Error while performing skipPreviewContact',
|
|
3525
|
+
},
|
|
3526
|
+
},
|
|
3527
|
+
};
|
|
3528
|
+
|
|
3529
|
+
jest.spyOn(webex.cc.services.dialer, 'skipPreviewContact').mockRejectedValue(error);
|
|
3530
|
+
|
|
3531
|
+
await expect(webex.cc.skipPreviewContact(previewPayload)).rejects.toThrow(
|
|
3532
|
+
error.details.data.reason
|
|
3533
|
+
);
|
|
3534
|
+
|
|
3535
|
+
expect(LoggerProxy.info).toHaveBeenCalledWith('Skipping campaign preview contact', {
|
|
3536
|
+
module: CC_FILE,
|
|
3537
|
+
method: 'skipPreviewContact',
|
|
3538
|
+
});
|
|
3539
|
+
expect(LoggerProxy.error).toHaveBeenCalledWith(
|
|
3540
|
+
`skipPreviewContact failed with reason: ${error.details.data.reason}`,
|
|
3541
|
+
{module: CC_FILE, method: 'skipPreviewContact', trackingId: error.details.trackingId}
|
|
3542
|
+
);
|
|
3543
|
+
expect(getErrorDetailsSpy).toHaveBeenCalledWith(error, 'skipPreviewContact', CC_FILE);
|
|
3544
|
+
});
|
|
3545
|
+
});
|
|
3546
|
+
|
|
3547
|
+
describe('removePreviewContact', () => {
|
|
3548
|
+
const previewPayload = {
|
|
3549
|
+
interactionId: 'interaction-123',
|
|
3550
|
+
campaignId: 'campaign-456',
|
|
3551
|
+
};
|
|
3552
|
+
|
|
3553
|
+
it('should remove preview contact successfully', async () => {
|
|
3554
|
+
const mockResponse = {trackingId: 'track-123'} as AgentContact;
|
|
3555
|
+
|
|
3556
|
+
const removePreviewContactMock = jest
|
|
3557
|
+
.spyOn(webex.cc.services.dialer, 'removePreviewContact')
|
|
3558
|
+
.mockResolvedValue(mockResponse);
|
|
3559
|
+
|
|
3560
|
+
const result = await webex.cc.removePreviewContact(previewPayload);
|
|
3561
|
+
|
|
3562
|
+
expect(LoggerProxy.info).toHaveBeenCalledWith('Removing campaign preview contact', {
|
|
3563
|
+
module: CC_FILE,
|
|
3564
|
+
method: 'removePreviewContact',
|
|
3565
|
+
});
|
|
3566
|
+
expect(LoggerProxy.log).toHaveBeenCalledWith(
|
|
3567
|
+
'Campaign preview contact removed successfully',
|
|
3568
|
+
{
|
|
3569
|
+
module: CC_FILE,
|
|
3570
|
+
method: 'removePreviewContact',
|
|
3571
|
+
trackingId: 'track-123',
|
|
3572
|
+
interactionId: previewPayload.interactionId,
|
|
3573
|
+
}
|
|
3574
|
+
);
|
|
3575
|
+
|
|
3576
|
+
expect(removePreviewContactMock).toHaveBeenCalledWith({data: previewPayload});
|
|
3577
|
+
expect(result).toEqual(mockResponse);
|
|
3578
|
+
});
|
|
3579
|
+
|
|
3580
|
+
it('should handle error during removePreviewContact', async () => {
|
|
3581
|
+
getErrorDetailsSpy.mockRestore();
|
|
3582
|
+
getErrorDetailsSpy = jest.spyOn(Utils, 'getErrorDetails');
|
|
3583
|
+
|
|
3584
|
+
const error = {
|
|
3585
|
+
details: {
|
|
3586
|
+
trackingId: '1234',
|
|
3587
|
+
data: {
|
|
3588
|
+
reason: 'Error while performing removePreviewContact',
|
|
3589
|
+
},
|
|
3590
|
+
},
|
|
3591
|
+
};
|
|
3592
|
+
|
|
3593
|
+
jest.spyOn(webex.cc.services.dialer, 'removePreviewContact').mockRejectedValue(error);
|
|
3594
|
+
|
|
3595
|
+
await expect(webex.cc.removePreviewContact(previewPayload)).rejects.toThrow(
|
|
3596
|
+
error.details.data.reason
|
|
3597
|
+
);
|
|
3598
|
+
|
|
3599
|
+
expect(LoggerProxy.info).toHaveBeenCalledWith('Removing campaign preview contact', {
|
|
3600
|
+
module: CC_FILE,
|
|
3601
|
+
method: 'removePreviewContact',
|
|
3602
|
+
});
|
|
3603
|
+
expect(LoggerProxy.error).toHaveBeenCalledWith(
|
|
3604
|
+
`removePreviewContact failed with reason: ${error.details.data.reason}`,
|
|
3605
|
+
{module: CC_FILE, method: 'removePreviewContact', trackingId: error.details.trackingId}
|
|
3606
|
+
);
|
|
3607
|
+
expect(getErrorDetailsSpy).toHaveBeenCalledWith(error, 'removePreviewContact', CC_FILE);
|
|
3608
|
+
});
|
|
3609
|
+
});
|
|
3610
|
+
|
|
3611
|
+
describe('private setManageWebexCallingInWxcc (runtime toggle impl)', () => {
|
|
3612
|
+
beforeEach(() => {
|
|
3613
|
+
webex.internal.device = {
|
|
3614
|
+
userId: 'user-123',
|
|
3615
|
+
registered: false,
|
|
3616
|
+
register: jest.fn().mockResolvedValue(undefined),
|
|
3617
|
+
unregister: jest.fn().mockResolvedValue(undefined),
|
|
3618
|
+
};
|
|
3619
|
+
webex.internal.mercury = {
|
|
3620
|
+
connected: false,
|
|
3621
|
+
connect: jest.fn().mockResolvedValue(undefined),
|
|
3622
|
+
disconnect: jest.fn().mockResolvedValue(undefined),
|
|
3623
|
+
};
|
|
3624
|
+
jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'subscribe').mockImplementation(() => {});
|
|
3625
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: false};
|
|
3626
|
+
webex.cc['agentConfig'] = {
|
|
3627
|
+
agentId: 'agent-123',
|
|
3628
|
+
deviceType: LoginOption.EXTENSION,
|
|
3629
|
+
} as Profile;
|
|
3630
|
+
webex.cc.webCallingService.loginOption = LoginOption.EXTENSION;
|
|
3631
|
+
webex.cc.webCallingService.setLoginOption = jest.fn((loginOption: LoginOption) => {
|
|
3632
|
+
webex.cc.webCallingService.loginOption = loginOption;
|
|
3633
|
+
});
|
|
3634
|
+
});
|
|
3635
|
+
|
|
3636
|
+
afterEach(() => {
|
|
3637
|
+
jest.restoreAllMocks();
|
|
3638
|
+
});
|
|
3639
|
+
|
|
3640
|
+
it('should update config, task manager, and publish usersub when enabled', async () => {
|
|
3641
|
+
const publishSpy = jest
|
|
3642
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
3643
|
+
.mockResolvedValue(undefined);
|
|
3644
|
+
|
|
3645
|
+
await (webex.cc as any).setManageWebexCallingInWxcc(true);
|
|
3646
|
+
|
|
3647
|
+
expect(webex.cc.isWxBetterTogetherEnabled()).toBe(true);
|
|
3648
|
+
expect(mockTaskManager.applyEnableWxBetterTogether).toHaveBeenCalledWith(true);
|
|
3649
|
+
expect(publishSpy).toHaveBeenCalledWith(true, {userId: 'user-123'});
|
|
3650
|
+
});
|
|
3651
|
+
|
|
3652
|
+
it('should publish false and update config when disabled after wxApp was enabled', async () => {
|
|
3653
|
+
webex.internal.device = {
|
|
3654
|
+
userId: 'user-123',
|
|
3655
|
+
url: 'https://wdm.example.com/devices/dev-1',
|
|
3656
|
+
};
|
|
3657
|
+
webex.request = jest.fn().mockResolvedValue({body: {}});
|
|
3658
|
+
|
|
3659
|
+
await (webex.cc as any).setManageWebexCallingInWxcc(true);
|
|
3660
|
+
|
|
3661
|
+
const publishSpy = jest
|
|
3662
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
3663
|
+
.mockResolvedValue(undefined);
|
|
3664
|
+
|
|
3665
|
+
await (webex.cc as any).setManageWebexCallingInWxcc(false);
|
|
3666
|
+
|
|
3667
|
+
expect(webex.cc.isWxBetterTogetherEnabled()).toBe(false);
|
|
3668
|
+
expect(mockTaskManager.applyEnableWxBetterTogether).toHaveBeenCalledWith(false);
|
|
3669
|
+
expect(publishSpy).toHaveBeenCalledWith(false, {userId: 'user-123'});
|
|
3670
|
+
});
|
|
3671
|
+
|
|
3672
|
+
it('should skip usersub publish when disabling without prior wxApp publish', async () => {
|
|
3673
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: true};
|
|
3674
|
+
const publishSpy = jest
|
|
3675
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
3676
|
+
.mockResolvedValue(undefined);
|
|
3677
|
+
|
|
3678
|
+
await (webex.cc as any).setManageWebexCallingInWxcc(false);
|
|
3679
|
+
|
|
3680
|
+
expect(webex.cc.isWxBetterTogetherEnabled()).toBe(false);
|
|
3681
|
+
expect(mockTaskManager.applyEnableWxBetterTogether).toHaveBeenCalledWith(false);
|
|
3682
|
+
expect(publishSpy).not.toHaveBeenCalled();
|
|
3683
|
+
});
|
|
3684
|
+
|
|
3685
|
+
it('should throw when enabling before login', async () => {
|
|
3686
|
+
webex.internal.device = undefined;
|
|
3687
|
+
|
|
3688
|
+
await expect((webex.cc as any).setManageWebexCallingInWxcc(true)).rejects.toThrow(
|
|
3689
|
+
'Cannot publish answer-calls-on-wxcc: user is not logged in'
|
|
3690
|
+
);
|
|
3691
|
+
expect(webex.cc.isWxBetterTogetherEnabled()).toBe(false);
|
|
3692
|
+
expect(mockTaskManager.applyEnableWxBetterTogether).not.toHaveBeenCalled();
|
|
3693
|
+
});
|
|
3694
|
+
|
|
3695
|
+
it('should throw when enabling on BROWSER (Desktop) login', async () => {
|
|
3696
|
+
webex.cc['agentConfig'] = {deviceType: LoginOption.BROWSER} as Profile;
|
|
3697
|
+
webex.cc.webCallingService.loginOption = LoginOption.BROWSER;
|
|
3698
|
+
const publishSpy = jest
|
|
3699
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
3700
|
+
.mockResolvedValue(undefined);
|
|
3701
|
+
|
|
3702
|
+
await expect((webex.cc as any).setManageWebexCallingInWxcc(true)).rejects.toThrow(
|
|
3703
|
+
'setManageWebexCallingInWxcc requires EXTENSION or AGENT_DN station login'
|
|
3704
|
+
);
|
|
3705
|
+
expect(webex.cc.isWxBetterTogetherEnabled()).toBe(false);
|
|
3706
|
+
expect(mockTaskManager.applyEnableWxBetterTogether).not.toHaveBeenCalled();
|
|
3707
|
+
expect(publishSpy).not.toHaveBeenCalled();
|
|
3708
|
+
});
|
|
3709
|
+
|
|
3710
|
+
it('should throw when enabling before station login', async () => {
|
|
3711
|
+
webex.cc['agentConfig'] = undefined as unknown as Profile;
|
|
3712
|
+
webex.cc.webCallingService.loginOption = undefined as unknown as LoginOption;
|
|
3713
|
+
const publishSpy = jest
|
|
3714
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
3715
|
+
.mockResolvedValue(undefined);
|
|
3716
|
+
|
|
3717
|
+
await expect((webex.cc as any).setManageWebexCallingInWxcc(true)).rejects.toThrow(
|
|
3718
|
+
'setManageWebexCallingInWxcc requires EXTENSION or AGENT_DN station login'
|
|
3719
|
+
);
|
|
3720
|
+
expect(webex.cc.isWxBetterTogetherEnabled()).toBe(false);
|
|
3721
|
+
expect(mockTaskManager.applyEnableWxBetterTogether).not.toHaveBeenCalled();
|
|
3722
|
+
expect(publishSpy).not.toHaveBeenCalled();
|
|
3723
|
+
});
|
|
3724
|
+
|
|
3725
|
+
it('should throw when webCallingService is BROWSER even if agentConfig deviceType is stale', async () => {
|
|
3726
|
+
webex.cc['agentConfig'] = {deviceType: LoginOption.EXTENSION} as Profile;
|
|
3727
|
+
webex.cc.webCallingService.loginOption = LoginOption.BROWSER;
|
|
3728
|
+
const publishSpy = jest
|
|
3729
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
3730
|
+
.mockResolvedValue(undefined);
|
|
3731
|
+
|
|
3732
|
+
await expect((webex.cc as any).setManageWebexCallingInWxcc(true)).rejects.toThrow(
|
|
3733
|
+
'setManageWebexCallingInWxcc requires EXTENSION or AGENT_DN station login'
|
|
3734
|
+
);
|
|
3735
|
+
expect(publishSpy).not.toHaveBeenCalled();
|
|
3736
|
+
});
|
|
3737
|
+
|
|
3738
|
+
it('should publish usersub when enabling on AGENT_DN login', async () => {
|
|
3739
|
+
webex.cc['agentConfig'] = {
|
|
3740
|
+
agentId: 'agent-123',
|
|
3741
|
+
deviceType: LoginOption.AGENT_DN,
|
|
3742
|
+
} as Profile;
|
|
3743
|
+
const publishSpy = jest
|
|
3744
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
3745
|
+
.mockResolvedValue(undefined);
|
|
3746
|
+
|
|
3747
|
+
await (webex.cc as any).setManageWebexCallingInWxcc(true);
|
|
3748
|
+
|
|
3749
|
+
expect(webex.cc.isWxBetterTogetherEnabled()).toBe(true);
|
|
3750
|
+
expect(publishSpy).toHaveBeenCalledWith(true, {userId: 'user-123'});
|
|
3751
|
+
});
|
|
3752
|
+
|
|
3753
|
+
it('should no-op usersub publish when disabling before login', async () => {
|
|
3754
|
+
webex.internal.device = undefined;
|
|
3755
|
+
const publishSpy = jest
|
|
3756
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
3757
|
+
.mockResolvedValue(undefined);
|
|
3758
|
+
|
|
3759
|
+
await (webex.cc as any).setManageWebexCallingInWxcc(false);
|
|
3760
|
+
|
|
3761
|
+
expect(webex.cc.isWxBetterTogetherEnabled()).toBe(false);
|
|
3762
|
+
expect(mockTaskManager.applyEnableWxBetterTogether).toHaveBeenCalledWith(false);
|
|
3763
|
+
expect(publishSpy).not.toHaveBeenCalled();
|
|
3764
|
+
});
|
|
3765
|
+
|
|
3766
|
+
it('should subscribe to wxApp Mercury mute sync when enabled', async () => {
|
|
3767
|
+
webex.cc['agentConfig'] = {agentId: 'agent-123'} as Profile;
|
|
3768
|
+
webex.internal.device = {
|
|
3769
|
+
...webex.internal.device,
|
|
3770
|
+
registered: true,
|
|
3771
|
+
register: jest.fn().mockResolvedValue(undefined),
|
|
3772
|
+
};
|
|
3773
|
+
webex.internal.mercury.connected = true;
|
|
3774
|
+
webex.internal.mercury.connect = jest.fn().mockResolvedValue(undefined);
|
|
3775
|
+
jest
|
|
3776
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
3777
|
+
.mockResolvedValue(undefined);
|
|
3778
|
+
const subscribeSpy = jest
|
|
3779
|
+
.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'subscribe')
|
|
3780
|
+
.mockImplementation(() => {});
|
|
3781
|
+
|
|
3782
|
+
await (webex.cc as any).setManageWebexCallingInWxcc(true);
|
|
3783
|
+
|
|
3784
|
+
expect(subscribeSpy).toHaveBeenCalledWith('agent-123', expect.any(Function));
|
|
3785
|
+
});
|
|
3786
|
+
|
|
3787
|
+
it('should unsubscribe from wxApp Mercury mute sync when disabled', async () => {
|
|
3788
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: true};
|
|
3789
|
+
jest
|
|
3790
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
3791
|
+
.mockResolvedValue(undefined);
|
|
3792
|
+
const unsubscribeSpy = jest
|
|
3793
|
+
.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'unsubscribe')
|
|
3794
|
+
.mockImplementation(() => {});
|
|
3795
|
+
|
|
3796
|
+
await (webex.cc as any).setManageWebexCallingInWxcc(false);
|
|
3797
|
+
|
|
3798
|
+
expect(unsubscribeSpy).toHaveBeenCalled();
|
|
3799
|
+
});
|
|
3800
|
+
|
|
3801
|
+
it('should register device and connect mercury when enabling wxApp sync', async () => {
|
|
3802
|
+
webex.cc['agentConfig'] = {agentId: 'agent-123'} as Profile;
|
|
3803
|
+
webex.internal.device = {
|
|
3804
|
+
userId: 'user-123',
|
|
3805
|
+
registered: false,
|
|
3806
|
+
register: jest.fn().mockResolvedValue(undefined),
|
|
3807
|
+
};
|
|
3808
|
+
webex.internal.mercury = {
|
|
3809
|
+
connected: false,
|
|
3810
|
+
connect: jest.fn().mockResolvedValue(undefined),
|
|
3811
|
+
};
|
|
3812
|
+
jest
|
|
3813
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
3814
|
+
.mockResolvedValue(undefined);
|
|
3815
|
+
const subscribeSpy = jest
|
|
3816
|
+
.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'subscribe')
|
|
3817
|
+
.mockImplementation(() => {});
|
|
3818
|
+
|
|
3819
|
+
await (webex.cc as any).setManageWebexCallingInWxcc(true);
|
|
3820
|
+
|
|
3821
|
+
expect(webex.internal.device.register).toHaveBeenCalled();
|
|
3822
|
+
expect(webex.internal.mercury.connect).toHaveBeenCalled();
|
|
3823
|
+
expect(subscribeSpy).toHaveBeenCalledWith('agent-123', expect.any(Function));
|
|
3824
|
+
});
|
|
3825
|
+
|
|
3826
|
+
it('should rollback config and task manager when usersub publish fails', async () => {
|
|
3827
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: false};
|
|
3828
|
+
jest
|
|
3829
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
3830
|
+
.mockRejectedValue(new Error('usersub failed'));
|
|
3831
|
+
|
|
3832
|
+
await expect((webex.cc as any).setManageWebexCallingInWxcc(true)).rejects.toThrow('usersub failed');
|
|
3833
|
+
|
|
3834
|
+
expect(webex.cc.isWxBetterTogetherEnabled()).toBe(false);
|
|
3835
|
+
expect(mockTaskManager.applyEnableWxBetterTogether).toHaveBeenCalledWith(true);
|
|
3836
|
+
expect(mockTaskManager.applyEnableWxBetterTogether).toHaveBeenLastCalledWith(false);
|
|
3837
|
+
});
|
|
3838
|
+
|
|
3839
|
+
it('should rollback config and release Mercury resources when Mercury connect fails', async () => {
|
|
3840
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: false};
|
|
3841
|
+
const publishSpy = jest
|
|
3842
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
3843
|
+
.mockResolvedValue(undefined);
|
|
3844
|
+
jest
|
|
3845
|
+
.spyOn(webex.cc as any, 'ensureWxAppMercuryConnected')
|
|
3846
|
+
.mockRejectedValue(new Error('mercury connect failed'));
|
|
3847
|
+
const releaseSpy = jest
|
|
3848
|
+
.spyOn(webex.cc as any, 'releaseWxAppMercuryResources')
|
|
3849
|
+
.mockResolvedValue(undefined);
|
|
3850
|
+
const unsubscribeSpy = jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'unsubscribe');
|
|
3851
|
+
const teardownSpy = jest.spyOn(webex.cc['webexCrossClientService'], 'teardown');
|
|
3852
|
+
|
|
3853
|
+
await expect((webex.cc as any).setManageWebexCallingInWxcc(true)).rejects.toThrow(
|
|
3854
|
+
'mercury connect failed'
|
|
3855
|
+
);
|
|
3856
|
+
|
|
3857
|
+
expect(webex.cc.isWxBetterTogetherEnabled()).toBe(false);
|
|
3858
|
+
expect(mockTaskManager.applyEnableWxBetterTogether).toHaveBeenLastCalledWith(false);
|
|
3859
|
+
expect(publishSpy).not.toHaveBeenCalled();
|
|
3860
|
+
expect(teardownSpy).not.toHaveBeenCalled();
|
|
3861
|
+
expect(unsubscribeSpy).toHaveBeenCalled();
|
|
3862
|
+
expect(releaseSpy).toHaveBeenCalled();
|
|
3863
|
+
});
|
|
3864
|
+
|
|
3865
|
+
it('should schedule compensating false retry when rollback publish fails after enable error', async () => {
|
|
3866
|
+
jest.useFakeTimers();
|
|
3867
|
+
try {
|
|
3868
|
+
webex.cc.$config = {...webex.cc.$config, enableWxBetterTogether: true};
|
|
3869
|
+
webex.internal.device = {userId: 'user-123', url: 'https://wdm.example.com/devices/dev-1'};
|
|
3870
|
+
webex.cc.agentConfig = {
|
|
3871
|
+
agentId: 'agent-123',
|
|
3872
|
+
deviceType: LoginOption.EXTENSION,
|
|
3873
|
+
};
|
|
3874
|
+
webex.cc.webCallingService.loginOption = LoginOption.EXTENSION;
|
|
3875
|
+
|
|
3876
|
+
const publishSpy = jest
|
|
3877
|
+
.spyOn(webex.cc['webexCrossClientService'], 'setManageWebexCallingInWxcc')
|
|
3878
|
+
.mockResolvedValueOnce(undefined)
|
|
3879
|
+
.mockRejectedValueOnce(new Error('compensating false failed'))
|
|
3880
|
+
.mockResolvedValue(undefined);
|
|
3881
|
+
jest.spyOn(webex.cc['wxAppTelephonyMercurySync'], 'subscribe').mockImplementation(() => {});
|
|
3882
|
+
jest
|
|
3883
|
+
.spyOn(webex.cc as any, 'ensureWxAppMercuryConnected')
|
|
3884
|
+
.mockResolvedValue(undefined);
|
|
3885
|
+
jest
|
|
3886
|
+
.spyOn(webex.cc as any, 'releaseWxAppMercuryResources')
|
|
3887
|
+
.mockResolvedValue(undefined);
|
|
3888
|
+
jest
|
|
3889
|
+
.spyOn(mockTaskManager, 'syncWxAppMuteFromCallDetailsForAllTasks')
|
|
3890
|
+
.mockImplementation(() => {
|
|
3891
|
+
throw new Error('sync failed');
|
|
3892
|
+
});
|
|
3893
|
+
const scheduleRetrySpy = jest.spyOn(
|
|
3894
|
+
webex.cc as any,
|
|
3895
|
+
'scheduleCompensatingFalsePublishRetry'
|
|
3896
|
+
);
|
|
3897
|
+
|
|
3898
|
+
await expect((webex.cc as any).setManageWebexCallingInWxcc(true)).rejects.toThrow(
|
|
3899
|
+
'sync failed'
|
|
3900
|
+
);
|
|
3901
|
+
|
|
3902
|
+
expect(scheduleRetrySpy).toHaveBeenCalled();
|
|
3903
|
+
expect(publishSpy).toHaveBeenCalledTimes(2);
|
|
3904
|
+
expect(publishSpy).toHaveBeenNthCalledWith(1, true, {userId: 'user-123'});
|
|
3905
|
+
expect(publishSpy).toHaveBeenNthCalledWith(2, false, {userId: 'user-123'});
|
|
3906
|
+
|
|
3907
|
+
jest.advanceTimersByTime(30_000);
|
|
3908
|
+
await publishSpy.mock.results[2]?.value;
|
|
3909
|
+
|
|
3910
|
+
expect(publishSpy).toHaveBeenCalledTimes(3);
|
|
3911
|
+
expect(publishSpy).toHaveBeenLastCalledWith(false, {userId: 'user-123'});
|
|
3912
|
+
} finally {
|
|
3913
|
+
jest.useRealTimers();
|
|
3914
|
+
}
|
|
3915
|
+
});
|
|
3916
|
+
});
|
|
2183
3917
|
});
|