@webex/contact-center 3.12.0-next.9 → 3.12.0-next.91
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 +876 -0
- package/AGENTS.md +94 -0
- package/ai-docs/ARCHITECTURE.md +168 -0
- package/ai-docs/CONTRACTS.md +46 -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 +341 -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 +265 -29
- package/dist/cc.js.map +1 -1
- package/dist/config.js +6 -0
- package/dist/config.js.map +1 -1
- package/dist/constants.js +17 -1
- package/dist/constants.js.map +1 -1
- package/dist/index.js +20 -5
- package/dist/index.js.map +1 -1
- package/dist/metrics/behavioral-events.js +101 -0
- package/dist/metrics/behavioral-events.js.map +1 -1
- package/dist/metrics/constants.js +25 -4
- package/dist/metrics/constants.js.map +1 -1
- package/dist/services/ApiAiAssistant.js +153 -8
- package/dist/services/ApiAiAssistant.js.map +1 -1
- package/dist/services/UserPreference.js +427 -0
- package/dist/services/UserPreference.js.map +1 -0
- package/dist/services/config/Util.js +3 -3
- package/dist/services/config/Util.js.map +1 -1
- package/dist/services/config/constants.js +23 -2
- package/dist/services/config/constants.js.map +1 -1
- package/dist/services/config/types.js +49 -9
- package/dist/services/config/types.js.map +1 -1
- package/dist/services/core/Err.js.map +1 -1
- package/dist/services/core/Utils.js +107 -32
- package/dist/services/core/Utils.js.map +1 -1
- package/dist/services/core/websocket/WebSocketManager.js +2 -1
- package/dist/services/core/websocket/WebSocketManager.js.map +1 -1
- package/dist/services/core/websocket/types.js.map +1 -1
- package/dist/services/index.js +1 -1
- package/dist/services/index.js.map +1 -1
- package/dist/services/task/Task.js +688 -0
- package/dist/services/task/Task.js.map +1 -0
- package/dist/services/task/TaskFactory.js +45 -0
- package/dist/services/task/TaskFactory.js.map +1 -0
- package/dist/services/task/TaskManager.js +728 -527
- package/dist/services/task/TaskManager.js.map +1 -1
- package/dist/services/task/TaskUtils.js +162 -26
- package/dist/services/task/TaskUtils.js.map +1 -1
- package/dist/services/task/constants.js +9 -2
- package/dist/services/task/constants.js.map +1 -1
- package/dist/services/task/dialer.js +78 -0
- package/dist/services/task/dialer.js.map +1 -1
- package/dist/services/task/digital/Digital.js +77 -0
- package/dist/services/task/digital/Digital.js.map +1 -0
- package/dist/services/task/state-machine/TaskStateMachine.js +837 -0
- package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -0
- package/dist/services/task/state-machine/actions.js +543 -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 +340 -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 +553 -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 +212 -4
- package/dist/services/task/types.js.map +1 -1
- package/dist/services/task/voice/Voice.js +1042 -0
- package/dist/services/task/voice/Voice.js.map +1 -0
- package/dist/services/task/voice/WebRTC.js +149 -0
- package/dist/services/task/voice/WebRTC.js.map +1 -0
- package/dist/types/cc.d.ts +94 -1
- package/dist/types/config.d.ts +6 -0
- package/dist/types/constants.d.ts +17 -1
- package/dist/types/index.d.ts +21 -6
- package/dist/types/metrics/constants.d.ts +21 -1
- package/dist/types/services/ApiAiAssistant.d.ts +22 -4
- package/dist/types/services/UserPreference.d.ts +118 -0
- package/dist/types/services/config/constants.d.ts +21 -0
- package/dist/types/services/config/types.d.ts +171 -10
- package/dist/types/services/core/Err.d.ts +4 -0
- package/dist/types/services/core/Utils.d.ts +33 -13
- package/dist/types/services/core/websocket/WebSocketManager.d.ts +1 -0
- package/dist/types/services/core/websocket/types.d.ts +1 -1
- package/dist/types/services/index.d.ts +1 -1
- package/dist/types/services/task/Task.d.ts +157 -0
- package/dist/types/services/task/TaskFactory.d.ts +12 -0
- package/dist/types/services/task/TaskUtils.d.ts +46 -2
- package/dist/types/services/task/constants.d.ts +7 -0
- package/dist/types/services/task/dialer.d.ts +30 -0
- package/dist/types/services/task/digital/Digital.d.ts +22 -0
- package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +1144 -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 +90 -0
- package/dist/types/services/task/state-machine/index.d.ts +13 -0
- package/dist/types/services/task/state-machine/types.d.ts +267 -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 +603 -66
- package/dist/types/services/task/voice/Voice.d.ts +184 -0
- package/dist/types/services/task/voice/WebRTC.d.ts +53 -0
- package/dist/types/types.d.ts +135 -0
- package/dist/types/webex.d.ts +1 -0
- package/dist/types.js +118 -2
- package/dist/types.js.map +1 -1
- package/dist/webex.js +14 -2
- package/dist/webex.js.map +1 -1
- package/package.json +15 -12
- package/src/cc.ts +329 -30
- package/src/config.ts +6 -0
- package/src/constants.ts +17 -1
- package/src/index.ts +23 -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 +854 -0
- package/src/metrics/behavioral-events.ts +106 -0
- package/src/metrics/constants.ts +27 -4
- package/src/services/ApiAiAssistant.ts +203 -8
- package/src/services/UserPreference.ts +509 -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/ai-docs/AGENTS.md +386 -0
- package/src/services/ai-docs/services-spec.md +492 -0
- package/src/services/config/Util.ts +3 -3
- package/src/services/config/ai-docs/AGENTS.md +255 -0
- package/src/services/config/ai-docs/ARCHITECTURE.md +426 -0
- package/src/services/config/ai-docs/config-spec.md +669 -0
- package/src/services/config/constants.ts +25 -1
- package/src/services/config/types.ts +174 -11
- package/src/services/core/Err.ts +2 -0
- package/src/services/core/Utils.ts +123 -37
- 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 +783 -0
- package/src/services/core/websocket/WebSocketManager.ts +2 -0
- package/src/services/core/websocket/types.ts +1 -1
- package/src/services/index.ts +1 -1
- package/src/services/task/Task.ts +837 -0
- package/src/services/task/TaskFactory.ts +55 -0
- package/src/services/task/TaskManager.ts +739 -614
- package/src/services/task/TaskUtils.ts +205 -25
- package/src/services/task/ai-docs/AGENTS.md +457 -0
- package/src/services/task/ai-docs/ARCHITECTURE.md +594 -0
- package/src/services/task/ai-docs/task-spec.md +1319 -0
- package/src/services/task/constants.ts +7 -0
- package/src/services/task/dialer.ts +80 -0
- package/src/services/task/digital/Digital.ts +95 -0
- package/src/services/task/state-machine/TaskStateMachine.ts +1077 -0
- package/src/services/task/state-machine/actions.ts +685 -0
- package/src/services/task/state-machine/ai-docs/AGENTS.md +458 -0
- package/src/services/task/state-machine/ai-docs/ARCHITECTURE.md +1137 -0
- package/src/services/task/state-machine/ai-docs/task-state-machine-spec.md +2115 -0
- package/src/services/task/state-machine/constants.ts +172 -0
- package/src/services/task/state-machine/guards.ts +406 -0
- package/src/services/task/state-machine/index.ts +28 -0
- package/src/services/task/state-machine/types.ts +241 -0
- package/src/services/task/state-machine/uiControlsComputer.ts +867 -0
- package/src/services/task/taskDataNormalizer.ts +137 -0
- package/src/services/task/types.ts +710 -71
- package/src/services/task/voice/Voice.ts +1267 -0
- package/src/services/task/voice/WebRTC.ts +187 -0
- package/src/types.ts +166 -2
- package/src/utils/AGENTS.md +278 -0
- package/src/utils/ai-docs/utils-spec.md +381 -0
- package/src/webex.js +2 -0
- package/test/unit/spec/cc.ts +343 -23
- package/test/unit/spec/logger-proxy.ts +70 -0
- package/test/unit/spec/services/ApiAiAssistant.ts +178 -20
- package/test/unit/spec/services/UserPreference.ts +401 -0
- package/test/unit/spec/services/WebCallingService.ts +7 -1
- package/test/unit/spec/services/config/index.ts +30 -30
- package/test/unit/spec/services/core/Utils.ts +425 -8
- package/test/unit/spec/services/core/websocket/WebSocketManager.ts +66 -40
- package/test/unit/spec/services/task/AutoWrapup.ts +63 -0
- package/test/unit/spec/services/task/Task.ts +477 -0
- package/test/unit/spec/services/task/TaskFactory.ts +62 -0
- package/test/unit/spec/services/task/TaskManager.ts +834 -1704
- package/test/unit/spec/services/task/TaskUtils.ts +206 -0
- package/test/unit/spec/services/task/dialer.ts +190 -0
- package/test/unit/spec/services/task/digital/Digital.ts +105 -0
- package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +1825 -0
- package/test/unit/spec/services/task/state-machine/guards.ts +479 -0
- package/test/unit/spec/services/task/state-machine/types.ts +18 -0
- package/test/unit/spec/services/task/state-machine/uiControlsComputer.ts +2020 -0
- package/test/unit/spec/services/task/taskTestUtils.ts +87 -0
- package/test/unit/spec/services/task/voice/Voice.ts +631 -0
- package/test/unit/spec/services/task/voice/WebRTC.ts +235 -0
- package/umd/contact-center.min.js +2 -2
- package/umd/contact-center.min.js.map +1 -1
- package/dist/services/task/index.js +0 -1525
- package/dist/services/task/index.js.map +0 -1
- package/dist/types/services/task/index.d.ts +0 -650
- package/src/services/task/index.ts +0 -1801
- package/test/unit/spec/services/task/index.ts +0 -2184
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import ApiAIAssistant from '../../../../src/services/ApiAiAssistant';
|
|
2
2
|
import MetricsManager from '../../../../src/metrics/MetricsManager';
|
|
3
3
|
import LoggerProxy from '../../../../src/logger-proxy';
|
|
4
|
-
import
|
|
4
|
+
import WebexRequest from '../../../../src/services/core/WebexRequest';
|
|
5
|
+
import {HTTP_METHODS, RealTimeAssistanceUserActionId, WebexSDK} from '../../../../src/types';
|
|
5
6
|
|
|
6
7
|
jest.mock('../../../../src/metrics/MetricsManager');
|
|
7
8
|
jest.mock('../../../../src/logger-proxy');
|
|
@@ -13,6 +14,9 @@ describe('ApiAIAssistant', () => {
|
|
|
13
14
|
|
|
14
15
|
beforeEach(() => {
|
|
15
16
|
jest.clearAllMocks();
|
|
17
|
+
jest.spyOn(WebexRequest, 'getInstance').mockReturnValue({
|
|
18
|
+
uploadLogs: jest.fn(),
|
|
19
|
+
} as any);
|
|
16
20
|
|
|
17
21
|
mockWebex = {
|
|
18
22
|
credentials: {
|
|
@@ -50,26 +54,21 @@ describe('ApiAIAssistant', () => {
|
|
|
50
54
|
'interaction-1',
|
|
51
55
|
'CUSTOM_EVENT',
|
|
52
56
|
'GET_TRANSCRIPTS',
|
|
53
|
-
'START'
|
|
57
|
+
{action: 'START'}
|
|
54
58
|
);
|
|
55
59
|
|
|
56
|
-
expect(mockWebex.request).
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
action: 'START',
|
|
69
|
-
}),
|
|
70
|
-
},
|
|
71
|
-
},
|
|
72
|
-
});
|
|
60
|
+
expect(mockWebex.request).toHaveBeenCalledTimes(1);
|
|
61
|
+
const requestArgs = (mockWebex.request as jest.Mock).mock.calls[0][0];
|
|
62
|
+
|
|
63
|
+
expect(requestArgs.uri).toBe('https://api-ai-assistant.produs1.ciscoccservice.com/event');
|
|
64
|
+
expect(requestArgs.method).toBe(HTTP_METHODS.POST);
|
|
65
|
+
expect(requestArgs.addAuthHeader).toBe(true);
|
|
66
|
+
expect(requestArgs.body.agentId).toBe('test-agent-id');
|
|
67
|
+
expect(requestArgs.body.orgId).toBe('test-org-id');
|
|
68
|
+
expect(requestArgs.body.eventType).toBe('CUSTOM_EVENT');
|
|
69
|
+
expect(requestArgs.body.eventName).toBe('GET_TRANSCRIPTS');
|
|
70
|
+
expect(requestArgs.body.eventDetails.data.interactionId).toBe('interaction-1');
|
|
71
|
+
expect(requestArgs.body.eventDetails.data.action).toBe('START');
|
|
73
72
|
expect(result).toEqual({ok: true});
|
|
74
73
|
});
|
|
75
74
|
|
|
@@ -93,6 +92,118 @@ describe('ApiAIAssistant', () => {
|
|
|
93
92
|
expect(result).toEqual(responseBody as any);
|
|
94
93
|
});
|
|
95
94
|
|
|
95
|
+
it('should request real-time assistance without extra context using sendEvent', async () => {
|
|
96
|
+
const sendEventSpy = jest.spyOn(apiAIAssistant, 'sendEvent').mockResolvedValue({ok: true});
|
|
97
|
+
apiAIAssistant.setAIFeatureFlags({suggestedResponses: {enable: true}} as any);
|
|
98
|
+
|
|
99
|
+
const result = await apiAIAssistant.getRealTimeAssistance({
|
|
100
|
+
agentId: 'test-agent-id',
|
|
101
|
+
interactionId: 'interaction-1',
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
expect(sendEventSpy).toHaveBeenCalledTimes(1);
|
|
105
|
+
const [agentId, interactionId, eventType, eventName, metadata, languageCode, trackingId] =
|
|
106
|
+
sendEventSpy.mock.calls[0];
|
|
107
|
+
|
|
108
|
+
expect(agentId).toBe('test-agent-id');
|
|
109
|
+
expect(interactionId).toBe('interaction-1');
|
|
110
|
+
expect(eventType).toBe('CUSTOM_EVENT');
|
|
111
|
+
expect(eventName).toBe('GET_SUGGESTIONS');
|
|
112
|
+
expect(metadata).toBeUndefined();
|
|
113
|
+
expect(languageCode).toBe('en');
|
|
114
|
+
expect(typeof trackingId).toBe('string');
|
|
115
|
+
expect(trackingId.startsWith('WX_CC_SDK_')).toBe(true);
|
|
116
|
+
expect(result).toEqual({ok: true});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('should request real-time assistance with extra context using sendEvent', async () => {
|
|
120
|
+
const sendEventSpy = jest.spyOn(apiAIAssistant, 'sendEvent').mockResolvedValue({ok: true});
|
|
121
|
+
apiAIAssistant.setAIFeatureFlags({suggestedResponses: {enable: true}} as any);
|
|
122
|
+
|
|
123
|
+
const result = await apiAIAssistant.getRealTimeAssistance({
|
|
124
|
+
agentId: 'test-agent-id',
|
|
125
|
+
interactionId: 'interaction-1',
|
|
126
|
+
context: 'Need assistance with credit card payment due date',
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
expect(sendEventSpy).toHaveBeenCalledTimes(1);
|
|
130
|
+
const [agentId, interactionId, eventType, eventName, metadata, languageCode, trackingId] =
|
|
131
|
+
sendEventSpy.mock.calls[0];
|
|
132
|
+
|
|
133
|
+
expect(agentId).toBe('test-agent-id');
|
|
134
|
+
expect(interactionId).toBe('interaction-1');
|
|
135
|
+
expect(eventType).toBe('CUSTOM_EVENT');
|
|
136
|
+
expect(eventName).toBe('ADD_SUGGESTIONS_EXTRA_CONTEXT');
|
|
137
|
+
expect(metadata).toEqual({context: 'Need assistance with credit card payment due date'});
|
|
138
|
+
expect(languageCode).toBe('en');
|
|
139
|
+
expect(typeof trackingId).toBe('string');
|
|
140
|
+
expect(trackingId.startsWith('WX_CC_SDK_')).toBe(true);
|
|
141
|
+
expect(result).toEqual({ok: true});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('should treat whitespace-only context as GET_SUGGESTIONS', async () => {
|
|
145
|
+
const sendEventSpy = jest.spyOn(apiAIAssistant, 'sendEvent').mockResolvedValue({ok: true});
|
|
146
|
+
apiAIAssistant.setAIFeatureFlags({suggestedResponses: {enable: true}} as any);
|
|
147
|
+
|
|
148
|
+
const result = await apiAIAssistant.getRealTimeAssistance({
|
|
149
|
+
agentId: 'test-agent-id',
|
|
150
|
+
interactionId: 'interaction-1',
|
|
151
|
+
context: ' ',
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
expect(sendEventSpy).toHaveBeenCalledTimes(1);
|
|
155
|
+
const [agentId, interactionId, eventType, eventName, metadata, languageCode, trackingId] =
|
|
156
|
+
sendEventSpy.mock.calls[0];
|
|
157
|
+
|
|
158
|
+
expect(agentId).toBe('test-agent-id');
|
|
159
|
+
expect(interactionId).toBe('interaction-1');
|
|
160
|
+
expect(eventType).toBe('CUSTOM_EVENT');
|
|
161
|
+
expect(eventName).toBe('GET_SUGGESTIONS');
|
|
162
|
+
expect(metadata).toEqual({context: ''});
|
|
163
|
+
expect(languageCode).toBe('en');
|
|
164
|
+
expect(typeof trackingId).toBe('string');
|
|
165
|
+
expect(trackingId.startsWith('WX_CC_SDK_')).toBe(true);
|
|
166
|
+
expect(result).toEqual({ok: true});
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
[
|
|
170
|
+
RealTimeAssistanceUserActionId.LIKE,
|
|
171
|
+
RealTimeAssistanceUserActionId.DISLIKE,
|
|
172
|
+
RealTimeAssistanceUserActionId.COPY,
|
|
173
|
+
].forEach((actionId) => {
|
|
174
|
+
it(`should send real-time assistance user action for ${actionId}`, async () => {
|
|
175
|
+
(mockWebex.request as jest.Mock).mockResolvedValue({body: {ok: true}});
|
|
176
|
+
apiAIAssistant.setAIFeatureFlags({suggestedResponses: {enable: true}} as any);
|
|
177
|
+
|
|
178
|
+
const result = await apiAIAssistant.sendRealTimeAssistanceUserAction({
|
|
179
|
+
agentId: 'test-agent-id',
|
|
180
|
+
interactionId: 'interaction-1',
|
|
181
|
+
adaptiveCardId: 'adaptive-card-1',
|
|
182
|
+
actionId,
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
expect(mockWebex.request).toHaveBeenCalledTimes(1);
|
|
186
|
+
const requestArgs = (mockWebex.request as jest.Mock).mock.calls[0][0];
|
|
187
|
+
const eventData = requestArgs.body.eventDetails.data;
|
|
188
|
+
|
|
189
|
+
expect(requestArgs.uri).toBe('https://api-ai-assistant.produs1.ciscoccservice.com/event');
|
|
190
|
+
expect(requestArgs.method).toBe(HTTP_METHODS.POST);
|
|
191
|
+
expect(requestArgs.addAuthHeader).toBe(true);
|
|
192
|
+
expect(requestArgs.body.agentId).toBe('test-agent-id');
|
|
193
|
+
expect(requestArgs.body.orgId).toBe('test-org-id');
|
|
194
|
+
expect(requestArgs.body.eventType).toBe('CUSTOM_EVENT');
|
|
195
|
+
expect(requestArgs.body.eventName).toBe('SUGGESTED_RESPONSES_USER_ACTION');
|
|
196
|
+
expect(eventData.interactionId).toBe('interaction-1');
|
|
197
|
+
expect(eventData.adaptiveCardId).toBe('adaptive-card-1');
|
|
198
|
+
expect(eventData.userAction.actionType).toBe('Action.Submit');
|
|
199
|
+
expect(eventData.userAction.actionId).toBe(actionId);
|
|
200
|
+
expect(eventData.languageCode).toBe('en');
|
|
201
|
+
expect(typeof eventData.actionTimeStamp).toBe('string');
|
|
202
|
+
expect(eventData.trackingId.startsWith('WX_CC_SDK_')).toBe(true);
|
|
203
|
+
expect(result).toEqual({ok: true});
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
|
|
96
207
|
it('should fail when base URL mapping is not available', async () => {
|
|
97
208
|
(mockWebex.internal.services.get as jest.Mock).mockReturnValue('https://unknown-host.invalid');
|
|
98
209
|
|
|
@@ -103,7 +214,7 @@ describe('ApiAIAssistant', () => {
|
|
|
103
214
|
'interaction-1',
|
|
104
215
|
'CUSTOM_EVENT',
|
|
105
216
|
'GET_TRANSCRIPTS',
|
|
106
|
-
'STOP'
|
|
217
|
+
{action: 'STOP'}
|
|
107
218
|
);
|
|
108
219
|
} catch (_error) {
|
|
109
220
|
failed = true;
|
|
@@ -112,4 +223,51 @@ describe('ApiAIAssistant', () => {
|
|
|
112
223
|
expect(failed).toBe(true);
|
|
113
224
|
expect(LoggerProxy.error).toHaveBeenCalled();
|
|
114
225
|
});
|
|
226
|
+
|
|
227
|
+
it('should fail when realtime transcripts feature is disabled', async () => {
|
|
228
|
+
apiAIAssistant.setAIFeatureFlags({realtimeTranscripts: {enable: false}} as any);
|
|
229
|
+
let errorMessage = '';
|
|
230
|
+
|
|
231
|
+
try {
|
|
232
|
+
await apiAIAssistant.fetchHistoricTranscripts('test-agent-id', 'interaction-1');
|
|
233
|
+
} catch (error) {
|
|
234
|
+
errorMessage = (error as Error)?.message || '';
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
expect(errorMessage).toBe('Error while performing fetchHistoricTranscripts');
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it('should fail when real-time assistance feature is disabled', async () => {
|
|
241
|
+
apiAIAssistant.setAIFeatureFlags({suggestedResponses: {enable: false}} as any);
|
|
242
|
+
let errorMessage = '';
|
|
243
|
+
|
|
244
|
+
try {
|
|
245
|
+
await apiAIAssistant.getRealTimeAssistance({
|
|
246
|
+
agentId: 'test-agent-id',
|
|
247
|
+
interactionId: 'interaction-1',
|
|
248
|
+
});
|
|
249
|
+
} catch (error) {
|
|
250
|
+
errorMessage = (error as Error)?.message || '';
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
expect(errorMessage).toBe('Error while performing getRealTimeAssistance');
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
it('should fail to send real-time assistance user action when feature is disabled', async () => {
|
|
257
|
+
apiAIAssistant.setAIFeatureFlags({suggestedResponses: {enable: false}} as any);
|
|
258
|
+
let errorMessage = '';
|
|
259
|
+
|
|
260
|
+
try {
|
|
261
|
+
await apiAIAssistant.sendRealTimeAssistanceUserAction({
|
|
262
|
+
agentId: 'test-agent-id',
|
|
263
|
+
interactionId: 'interaction-1',
|
|
264
|
+
adaptiveCardId: 'adaptive-card-1',
|
|
265
|
+
actionId: RealTimeAssistanceUserActionId.LIKE,
|
|
266
|
+
});
|
|
267
|
+
} catch (error) {
|
|
268
|
+
errorMessage = (error as Error)?.message || '';
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
expect(errorMessage).toBe('Error while performing sendRealTimeAssistanceUserAction');
|
|
272
|
+
});
|
|
115
273
|
});
|
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
import {expect} from '@jest/globals';
|
|
2
|
+
import UserPreference from '../../../../src/services/UserPreference';
|
|
3
|
+
import {HTTP_METHODS, WebexSDK, IHttpResponse} from '../../../../src/types';
|
|
4
|
+
import {METRIC_EVENT_NAMES} from '../../../../src/metrics/constants';
|
|
5
|
+
import WebexRequest from '../../../../src/services/core/WebexRequest';
|
|
6
|
+
import MetricsManager from '../../../../src/metrics/MetricsManager';
|
|
7
|
+
import LoggerProxy from '../../../../src/logger-proxy';
|
|
8
|
+
|
|
9
|
+
jest.mock('../../../../src/metrics/MetricsManager');
|
|
10
|
+
jest.mock('../../../../src/logger-proxy');
|
|
11
|
+
|
|
12
|
+
describe('UserPreference', () => {
|
|
13
|
+
let userPreferenceAPI: UserPreference;
|
|
14
|
+
let mockWebex: WebexSDK;
|
|
15
|
+
let mockMetricsManager: jest.Mocked<MetricsManager>;
|
|
16
|
+
|
|
17
|
+
const mockGetUserId = jest.fn().mockReturnValue('test-user-id');
|
|
18
|
+
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
jest.clearAllMocks();
|
|
21
|
+
(WebexRequest as any).instance = undefined;
|
|
22
|
+
|
|
23
|
+
mockWebex = {
|
|
24
|
+
credentials: {
|
|
25
|
+
getOrgId: jest.fn().mockReturnValue('test-org-id'),
|
|
26
|
+
},
|
|
27
|
+
request: jest.fn(),
|
|
28
|
+
internal: {
|
|
29
|
+
newMetrics: {
|
|
30
|
+
submitBehavioralEvent: jest.fn(),
|
|
31
|
+
submitOperationalEvent: jest.fn(),
|
|
32
|
+
submitBusinessEvent: jest.fn(),
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
ready: true,
|
|
36
|
+
once: jest.fn(),
|
|
37
|
+
} as unknown as WebexSDK;
|
|
38
|
+
|
|
39
|
+
mockMetricsManager = {
|
|
40
|
+
trackEvent: jest.fn(),
|
|
41
|
+
timeEvent: jest.fn(),
|
|
42
|
+
} as unknown as jest.Mocked<MetricsManager>;
|
|
43
|
+
(MetricsManager.getInstance as jest.Mock).mockReturnValue(mockMetricsManager);
|
|
44
|
+
|
|
45
|
+
userPreferenceAPI = new UserPreference(mockWebex, mockGetUserId);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe('constructor', () => {
|
|
49
|
+
it('should initialize with all required dependencies', () => {
|
|
50
|
+
expect(WebexRequest.getInstance({webex: mockWebex})).toBeDefined();
|
|
51
|
+
expect(MetricsManager.getInstance).toHaveBeenCalledWith({webex: mockWebex});
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
describe('getUserPreference', () => {
|
|
56
|
+
const mockPreference = {
|
|
57
|
+
id: 'pref-123',
|
|
58
|
+
organizationId: 'test-org-id',
|
|
59
|
+
userId: 'test-user-id',
|
|
60
|
+
preferences: {e911Reminder: true},
|
|
61
|
+
createdTime: 1234567890,
|
|
62
|
+
lastUpdatedTime: 1234567890,
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const mockResponse: IHttpResponse = {
|
|
66
|
+
statusCode: 200,
|
|
67
|
+
method: 'GET',
|
|
68
|
+
url: '/organization/test-org-id/user-preference/test-user-id',
|
|
69
|
+
headers: {} as any,
|
|
70
|
+
body: mockPreference,
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
beforeEach(() => {
|
|
74
|
+
jest.spyOn(Date, 'now').mockReturnValue(1000);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
afterEach(() => {
|
|
78
|
+
jest.restoreAllMocks();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('should fetch user preferences successfully with default userId', async () => {
|
|
82
|
+
(mockWebex.request as jest.Mock).mockResolvedValue(mockResponse);
|
|
83
|
+
|
|
84
|
+
const result = await userPreferenceAPI.getUserPreference();
|
|
85
|
+
|
|
86
|
+
expect(mockWebex.request).toHaveBeenCalledWith({
|
|
87
|
+
service: 'wcc-api-gateway',
|
|
88
|
+
resource: 'organization/test-org-id/user-preference/test-user-id',
|
|
89
|
+
method: HTTP_METHODS.GET,
|
|
90
|
+
body: undefined,
|
|
91
|
+
});
|
|
92
|
+
expect(result).toEqual(mockPreference);
|
|
93
|
+
expect(mockMetricsManager.timeEvent).toHaveBeenCalledWith(
|
|
94
|
+
METRIC_EVENT_NAMES.USER_PREFERENCE_GET_SUCCESS
|
|
95
|
+
);
|
|
96
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
|
|
97
|
+
METRIC_EVENT_NAMES.USER_PREFERENCE_GET_SUCCESS,
|
|
98
|
+
expect.objectContaining({
|
|
99
|
+
orgId: 'test-org-id',
|
|
100
|
+
userId: 'test-user-id',
|
|
101
|
+
statusCode: 200,
|
|
102
|
+
}),
|
|
103
|
+
['behavioral', 'operational']
|
|
104
|
+
);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('should fetch user preferences for a specific userId', async () => {
|
|
108
|
+
(mockWebex.request as jest.Mock).mockResolvedValue(mockResponse);
|
|
109
|
+
|
|
110
|
+
const result = await userPreferenceAPI.getUserPreference({userId: 'specific-user-id'});
|
|
111
|
+
|
|
112
|
+
expect(mockWebex.request).toHaveBeenCalledWith({
|
|
113
|
+
service: 'wcc-api-gateway',
|
|
114
|
+
resource: 'organization/test-org-id/user-preference/specific-user-id',
|
|
115
|
+
method: HTTP_METHODS.GET,
|
|
116
|
+
body: undefined,
|
|
117
|
+
});
|
|
118
|
+
expect(result).toEqual(mockPreference);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('should fetch user preferences with pagination parameters', async () => {
|
|
122
|
+
(mockWebex.request as jest.Mock).mockResolvedValue(mockResponse);
|
|
123
|
+
|
|
124
|
+
const result = await userPreferenceAPI.getUserPreference({page: 1, pageSize: 50});
|
|
125
|
+
|
|
126
|
+
expect(mockWebex.request).toHaveBeenCalledWith({
|
|
127
|
+
service: 'wcc-api-gateway',
|
|
128
|
+
resource: 'organization/test-org-id/user-preference/test-user-id?page=1&pageSize=50',
|
|
129
|
+
method: HTTP_METHODS.GET,
|
|
130
|
+
body: undefined,
|
|
131
|
+
});
|
|
132
|
+
expect(result).toEqual(mockPreference);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('should throw error when userId is not available', async () => {
|
|
136
|
+
mockGetUserId.mockReturnValueOnce('');
|
|
137
|
+
|
|
138
|
+
await expect(userPreferenceAPI.getUserPreference()).rejects.toThrow(
|
|
139
|
+
'UserPreference: userId is not available.'
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
expect(mockMetricsManager.trackEvent).not.toHaveBeenCalled();
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('should handle API errors', async () => {
|
|
146
|
+
const mockError = new Error('API call failed');
|
|
147
|
+
(mockWebex.request as jest.Mock).mockRejectedValue(mockError);
|
|
148
|
+
|
|
149
|
+
await expect(userPreferenceAPI.getUserPreference()).rejects.toThrow('API call failed');
|
|
150
|
+
|
|
151
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
|
|
152
|
+
METRIC_EVENT_NAMES.USER_PREFERENCE_GET_FAILED,
|
|
153
|
+
expect.objectContaining({
|
|
154
|
+
error: 'API call failed',
|
|
155
|
+
}),
|
|
156
|
+
['behavioral', 'operational']
|
|
157
|
+
);
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
describe('createUserPreference', () => {
|
|
162
|
+
const mockCreateRequest = {
|
|
163
|
+
userId: 'test-user-id',
|
|
164
|
+
desktopPreference: '{}',
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const mockCreatedPreference = {
|
|
168
|
+
id: 'pref-123',
|
|
169
|
+
organizationId: 'test-org-id',
|
|
170
|
+
userId: 'test-user-id',
|
|
171
|
+
preferences: {e911Reminder: true},
|
|
172
|
+
createdTime: 1234567890,
|
|
173
|
+
lastUpdatedTime: 1234567890,
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
const mockResponse: IHttpResponse = {
|
|
177
|
+
statusCode: 201,
|
|
178
|
+
method: 'POST',
|
|
179
|
+
url: '/organization/test-org-id/user-preference',
|
|
180
|
+
headers: {} as any,
|
|
181
|
+
body: mockCreatedPreference,
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
beforeEach(() => {
|
|
185
|
+
jest.spyOn(Date, 'now').mockReturnValue(1000);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
afterEach(() => {
|
|
189
|
+
jest.restoreAllMocks();
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it('should create user preferences successfully', async () => {
|
|
193
|
+
(mockWebex.request as jest.Mock).mockResolvedValue(mockResponse);
|
|
194
|
+
|
|
195
|
+
const result = await userPreferenceAPI.createUserPreference(mockCreateRequest);
|
|
196
|
+
|
|
197
|
+
expect(mockWebex.request).toHaveBeenCalledWith({
|
|
198
|
+
service: 'wcc-api-gateway',
|
|
199
|
+
resource: 'organization/test-org-id/user-preference',
|
|
200
|
+
method: HTTP_METHODS.POST,
|
|
201
|
+
body: mockCreateRequest,
|
|
202
|
+
});
|
|
203
|
+
expect(result).toEqual(mockCreatedPreference);
|
|
204
|
+
expect(mockMetricsManager.timeEvent).toHaveBeenCalledWith(
|
|
205
|
+
METRIC_EVENT_NAMES.USER_PREFERENCE_CREATE_SUCCESS
|
|
206
|
+
);
|
|
207
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
|
|
208
|
+
METRIC_EVENT_NAMES.USER_PREFERENCE_CREATE_SUCCESS,
|
|
209
|
+
expect.objectContaining({
|
|
210
|
+
orgId: 'test-org-id',
|
|
211
|
+
userId: 'test-user-id',
|
|
212
|
+
statusCode: 201,
|
|
213
|
+
}),
|
|
214
|
+
['behavioral', 'operational']
|
|
215
|
+
);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it('should throw error when userId is missing in request', async () => {
|
|
219
|
+
const invalidRequest = {
|
|
220
|
+
userId: '',
|
|
221
|
+
desktopPreference: '{}',
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
await expect(userPreferenceAPI.createUserPreference(invalidRequest)).rejects.toThrow(
|
|
225
|
+
'UserPreference: userId is required to create user preferences.'
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
expect(mockMetricsManager.trackEvent).not.toHaveBeenCalled();
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it('should handle API errors', async () => {
|
|
232
|
+
const mockError = new Error('API call failed');
|
|
233
|
+
(mockWebex.request as jest.Mock).mockRejectedValue(mockError);
|
|
234
|
+
|
|
235
|
+
await expect(userPreferenceAPI.createUserPreference(mockCreateRequest)).rejects.toThrow(
|
|
236
|
+
'API call failed'
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
|
|
240
|
+
METRIC_EVENT_NAMES.USER_PREFERENCE_CREATE_FAILED,
|
|
241
|
+
expect.objectContaining({
|
|
242
|
+
error: 'API call failed',
|
|
243
|
+
}),
|
|
244
|
+
['behavioral', 'operational']
|
|
245
|
+
);
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
describe('updateUserPreference', () => {
|
|
250
|
+
const mockUpdateRequest = {
|
|
251
|
+
desktopPreference: '{}',
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
const mockUpdatedPreference = {
|
|
255
|
+
id: 'pref-123',
|
|
256
|
+
organizationId: 'test-org-id',
|
|
257
|
+
userId: 'test-user-id',
|
|
258
|
+
preferences: {e911Reminder: false},
|
|
259
|
+
createdTime: 1234567890,
|
|
260
|
+
lastUpdatedTime: 1234567899,
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
const mockResponse: IHttpResponse = {
|
|
264
|
+
statusCode: 200,
|
|
265
|
+
method: 'PUT',
|
|
266
|
+
url: '/organization/test-org-id/user-preference/test-user-id',
|
|
267
|
+
headers: {} as any,
|
|
268
|
+
body: mockUpdatedPreference,
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
beforeEach(() => {
|
|
272
|
+
jest.spyOn(Date, 'now').mockReturnValue(1000);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
afterEach(() => {
|
|
276
|
+
jest.restoreAllMocks();
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it('should update user preferences successfully', async () => {
|
|
280
|
+
(mockWebex.request as jest.Mock).mockResolvedValue(mockResponse);
|
|
281
|
+
|
|
282
|
+
const result = await userPreferenceAPI.updateUserPreference(
|
|
283
|
+
'test-user-id',
|
|
284
|
+
mockUpdateRequest
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
expect(mockWebex.request).toHaveBeenCalledWith({
|
|
288
|
+
service: 'wcc-api-gateway',
|
|
289
|
+
resource: 'organization/test-org-id/user-preference/test-user-id',
|
|
290
|
+
method: HTTP_METHODS.PUT,
|
|
291
|
+
body: mockUpdateRequest,
|
|
292
|
+
});
|
|
293
|
+
expect(result).toEqual(mockUpdatedPreference);
|
|
294
|
+
expect(mockMetricsManager.timeEvent).toHaveBeenCalledWith(
|
|
295
|
+
METRIC_EVENT_NAMES.USER_PREFERENCE_UPDATE_SUCCESS
|
|
296
|
+
);
|
|
297
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
|
|
298
|
+
METRIC_EVENT_NAMES.USER_PREFERENCE_UPDATE_SUCCESS,
|
|
299
|
+
expect.objectContaining({
|
|
300
|
+
orgId: 'test-org-id',
|
|
301
|
+
userId: 'test-user-id',
|
|
302
|
+
statusCode: 200,
|
|
303
|
+
}),
|
|
304
|
+
['behavioral', 'operational']
|
|
305
|
+
);
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
it('should throw error when userId is missing', async () => {
|
|
309
|
+
await expect(userPreferenceAPI.updateUserPreference('', mockUpdateRequest)).rejects.toThrow(
|
|
310
|
+
'UserPreference: userId is required to update user preferences.'
|
|
311
|
+
);
|
|
312
|
+
|
|
313
|
+
expect(mockMetricsManager.trackEvent).not.toHaveBeenCalled();
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
it('should handle API errors', async () => {
|
|
317
|
+
const mockError = new Error('API call failed');
|
|
318
|
+
(mockWebex.request as jest.Mock).mockRejectedValue(mockError);
|
|
319
|
+
|
|
320
|
+
await expect(
|
|
321
|
+
userPreferenceAPI.updateUserPreference('test-user-id', mockUpdateRequest)
|
|
322
|
+
).rejects.toThrow('API call failed');
|
|
323
|
+
|
|
324
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
|
|
325
|
+
METRIC_EVENT_NAMES.USER_PREFERENCE_UPDATE_FAILED,
|
|
326
|
+
expect.objectContaining({
|
|
327
|
+
error: 'API call failed',
|
|
328
|
+
}),
|
|
329
|
+
['behavioral', 'operational']
|
|
330
|
+
);
|
|
331
|
+
});
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
describe('deleteUserPreference', () => {
|
|
335
|
+
const mockResponse: IHttpResponse = {
|
|
336
|
+
statusCode: 204,
|
|
337
|
+
method: 'DELETE',
|
|
338
|
+
url: '/organization/test-org-id/user-preference/test-user-id',
|
|
339
|
+
headers: {} as any,
|
|
340
|
+
body: null,
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
beforeEach(() => {
|
|
344
|
+
jest.spyOn(Date, 'now').mockReturnValue(1000);
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
afterEach(() => {
|
|
348
|
+
jest.restoreAllMocks();
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
it('should delete user preferences successfully', async () => {
|
|
352
|
+
(mockWebex.request as jest.Mock).mockResolvedValue(mockResponse);
|
|
353
|
+
|
|
354
|
+
await userPreferenceAPI.deleteUserPreference('test-user-id');
|
|
355
|
+
|
|
356
|
+
expect(mockWebex.request).toHaveBeenCalledWith({
|
|
357
|
+
service: 'wcc-api-gateway',
|
|
358
|
+
resource: 'organization/test-org-id/user-preference/test-user-id',
|
|
359
|
+
method: HTTP_METHODS.DELETE,
|
|
360
|
+
body: undefined,
|
|
361
|
+
});
|
|
362
|
+
expect(mockMetricsManager.timeEvent).toHaveBeenCalledWith(
|
|
363
|
+
METRIC_EVENT_NAMES.USER_PREFERENCE_DELETE_SUCCESS
|
|
364
|
+
);
|
|
365
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
|
|
366
|
+
METRIC_EVENT_NAMES.USER_PREFERENCE_DELETE_SUCCESS,
|
|
367
|
+
expect.objectContaining({
|
|
368
|
+
orgId: 'test-org-id',
|
|
369
|
+
userId: 'test-user-id',
|
|
370
|
+
statusCode: 204,
|
|
371
|
+
}),
|
|
372
|
+
['behavioral', 'operational']
|
|
373
|
+
);
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
it('should throw error when userId is missing', async () => {
|
|
377
|
+
await expect(userPreferenceAPI.deleteUserPreference('')).rejects.toThrow(
|
|
378
|
+
'UserPreference: userId is required to delete user preferences.'
|
|
379
|
+
);
|
|
380
|
+
|
|
381
|
+
expect(mockMetricsManager.trackEvent).not.toHaveBeenCalled();
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
it('should handle API errors', async () => {
|
|
385
|
+
const mockError = new Error('API call failed');
|
|
386
|
+
(mockWebex.request as jest.Mock).mockRejectedValue(mockError);
|
|
387
|
+
|
|
388
|
+
await expect(userPreferenceAPI.deleteUserPreference('test-user-id')).rejects.toThrow(
|
|
389
|
+
'API call failed'
|
|
390
|
+
);
|
|
391
|
+
|
|
392
|
+
expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
|
|
393
|
+
METRIC_EVENT_NAMES.USER_PREFERENCE_DELETE_FAILED,
|
|
394
|
+
expect.objectContaining({
|
|
395
|
+
error: 'API call failed',
|
|
396
|
+
}),
|
|
397
|
+
['behavioral', 'operational']
|
|
398
|
+
);
|
|
399
|
+
});
|
|
400
|
+
});
|
|
401
|
+
});
|
|
@@ -200,11 +200,17 @@ describe('WebCallingService', () => {
|
|
|
200
200
|
|
|
201
201
|
it('should reject if registration times out', async () => {
|
|
202
202
|
line = callingClient.getLines().line1 as ILine;
|
|
203
|
+
jest.spyOn(global, 'setTimeout').mockImplementation(((handler: TimerHandler) => {
|
|
204
|
+
if (typeof handler === 'function') {
|
|
205
|
+
handler();
|
|
206
|
+
}
|
|
207
|
+
return 0 as unknown as NodeJS.Timeout;
|
|
208
|
+
}) as typeof setTimeout);
|
|
203
209
|
|
|
204
210
|
const promise = webRTCCalling.registerWebCallingLine();
|
|
205
211
|
|
|
206
212
|
await expect(promise).rejects.toThrow('WebCallingService Registration timed out');
|
|
207
|
-
}
|
|
213
|
+
});
|
|
208
214
|
|
|
209
215
|
it('should handle incoming calls', async () => {
|
|
210
216
|
line = callingClient.getLines().line1 as ILine;
|