@webex/contact-center 3.12.0-task-refactor.7 → 3.12.0-task-refactor.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cc.js +3 -4
- package/dist/cc.js.map +1 -1
- package/dist/constants.js +1 -0
- package/dist/constants.js.map +1 -1
- package/dist/metrics/constants.js +2 -0
- package/dist/metrics/constants.js.map +1 -1
- package/dist/services/ApiAiAssistant.js +74 -3
- package/dist/services/ApiAiAssistant.js.map +1 -1
- package/dist/services/config/types.js +9 -1
- package/dist/services/config/types.js.map +1 -1
- package/dist/services/task/Task.js +32 -0
- package/dist/services/task/Task.js.map +1 -1
- package/dist/services/task/TaskManager.js +7 -2
- package/dist/services/task/TaskManager.js.map +1 -1
- package/dist/services/task/TaskUtils.js +3 -1
- package/dist/services/task/TaskUtils.js.map +1 -1
- package/dist/services/task/state-machine/TaskStateMachine.js +76 -0
- package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -1
- package/dist/services/task/state-machine/actions.js +113 -23
- package/dist/services/task/state-machine/actions.js.map +1 -1
- package/dist/services/task/state-machine/uiControlsComputer.js +99 -21
- package/dist/services/task/state-machine/uiControlsComputer.js.map +1 -1
- package/dist/types/constants.d.ts +1 -0
- package/dist/types/metrics/constants.d.ts +2 -0
- package/dist/types/services/ApiAiAssistant.d.ts +10 -2
- package/dist/types/services/config/types.d.ts +16 -0
- package/dist/types/services/task/Task.d.ts +10 -0
- package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +110 -0
- package/dist/types/services/task/state-machine/actions.d.ts +2 -0
- package/dist/types/types.d.ts +24 -0
- package/dist/types.js +15 -0
- package/dist/types.js.map +1 -1
- package/dist/webex.js +1 -1
- package/package.json +1 -1
- package/src/cc.ts +6 -4
- package/src/constants.ts +1 -0
- package/src/metrics/constants.ts +2 -0
- package/src/services/ApiAiAssistant.ts +102 -2
- package/src/services/config/types.ts +8 -0
- package/src/services/task/Task.ts +34 -0
- package/src/services/task/TaskManager.ts +7 -2
- package/src/services/task/TaskUtils.ts +5 -3
- package/src/services/task/ai-docs/AGENTS.md +7 -0
- package/src/services/task/ai-docs/ARCHITECTURE.md +12 -0
- package/src/services/task/state-machine/TaskStateMachine.ts +104 -0
- package/src/services/task/state-machine/actions.ts +151 -25
- package/src/services/task/state-machine/uiControlsComputer.ts +173 -30
- package/src/types.ts +25 -0
- package/test/unit/spec/cc.ts +2 -0
- package/test/unit/spec/services/ApiAiAssistant.ts +105 -17
- package/test/unit/spec/services/task/Task.ts +61 -0
- package/test/unit/spec/services/task/TaskManager.ts +42 -0
- package/test/unit/spec/services/task/TaskUtils.ts +65 -0
- package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +676 -0
- package/test/unit/spec/services/task/state-machine/uiControlsComputer.ts +597 -0
- package/test/unit/spec/services/task/voice/WebRTC.ts +99 -106
- package/umd/contact-center.min.js +2 -2
- package/umd/contact-center.min.js.map +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import 'jsdom-global/register';
|
|
2
|
-
import {
|
|
2
|
+
import {LocalMicrophoneStream, CALL_EVENT_KEYS} from '@webex/calling';
|
|
3
3
|
import WebRTC from '../../../../../../src/services/task/voice/WebRTC';
|
|
4
4
|
import WebCallingService from '../../../../../../src/services/WebCallingService';
|
|
5
5
|
import {TaskData, TASK_EVENTS} from '../../../../../../src/services/task/types';
|
|
@@ -9,13 +9,15 @@ import {createTaskData} from '../taskTestUtils';
|
|
|
9
9
|
|
|
10
10
|
jest.mock('@webex/calling', () => ({
|
|
11
11
|
LocalMicrophoneStream: class {
|
|
12
|
-
constructor(stream) {
|
|
12
|
+
constructor(stream) {
|
|
13
|
+
this.outputStream = stream;
|
|
14
|
+
}
|
|
13
15
|
},
|
|
14
|
-
CALL_EVENT_KEYS: {
|
|
16
|
+
CALL_EVENT_KEYS: {REMOTE_MEDIA: 'remoteMedia'},
|
|
15
17
|
}));
|
|
16
18
|
|
|
17
19
|
beforeAll(() => {
|
|
18
|
-
navigator.mediaDevices = {
|
|
20
|
+
navigator.mediaDevices = {getUserMedia: jest.fn()};
|
|
19
21
|
// @ts-ignore
|
|
20
22
|
global.MediaStream = class {
|
|
21
23
|
constructor(private tracks: MediaStreamTrack[]) {}
|
|
@@ -28,7 +30,7 @@ beforeAll(() => {
|
|
|
28
30
|
jest.mock('../../../../../../src/services/core/WebexRequest', () => ({
|
|
29
31
|
__esModule: true,
|
|
30
32
|
default: {
|
|
31
|
-
getInstance: jest.fn().mockReturnValue({
|
|
33
|
+
getInstance: jest.fn().mockReturnValue({uploadLogs: jest.fn()}),
|
|
32
34
|
},
|
|
33
35
|
}));
|
|
34
36
|
|
|
@@ -80,7 +82,7 @@ describe('WebRTC Task', () => {
|
|
|
80
82
|
|
|
81
83
|
it('accept() obtains media and answers call', async () => {
|
|
82
84
|
const fakeTrack = {} as any;
|
|
83
|
-
const fakeStream = {
|
|
85
|
+
const fakeStream = {getAudioTracks: () => [fakeTrack]} as any;
|
|
84
86
|
jest.spyOn(navigator.mediaDevices, 'getUserMedia').mockResolvedValue(fakeStream);
|
|
85
87
|
await webRtc.accept();
|
|
86
88
|
expect(answerSpy).toHaveBeenCalled();
|
|
@@ -93,7 +95,7 @@ describe('WebRTC Task', () => {
|
|
|
93
95
|
jest.spyOn(webRtc, 'unregisterWebCallListeners');
|
|
94
96
|
const res = await webRtc.decline();
|
|
95
97
|
expect(declineSpy).toHaveBeenCalledWith(taskData.interactionId);
|
|
96
|
-
expect(
|
|
98
|
+
expect(webRtc.unregisterWebCallListeners).toHaveBeenCalled();
|
|
97
99
|
expect(res).toBeUndefined();
|
|
98
100
|
});
|
|
99
101
|
|
|
@@ -107,20 +109,14 @@ describe('WebRTC Task', () => {
|
|
|
107
109
|
describe('WebRTC internal methods', () => {
|
|
108
110
|
it('registerWebCallListeners binds remote media event', () => {
|
|
109
111
|
webRtc.registerWebCallListeners();
|
|
110
|
-
expect(onSpy).toHaveBeenCalledWith(
|
|
111
|
-
CALL_EVENT_KEYS.REMOTE_MEDIA,
|
|
112
|
-
webRtc.handleRemoteMedia
|
|
113
|
-
);
|
|
112
|
+
expect(onSpy).toHaveBeenCalledWith(CALL_EVENT_KEYS.REMOTE_MEDIA, webRtc.handleRemoteMedia);
|
|
114
113
|
});
|
|
115
114
|
|
|
116
115
|
it('handleRemoteMedia emits TASK_MEDIA event', () => {
|
|
117
116
|
const fakeTrack = {} as any;
|
|
118
117
|
jest.spyOn(webRtc, 'emit');
|
|
119
|
-
|
|
120
|
-
expect(webRtc.emit).toHaveBeenCalledWith(
|
|
121
|
-
TASK_EVENTS.TASK_MEDIA,
|
|
122
|
-
fakeTrack
|
|
123
|
-
);
|
|
118
|
+
webRtc.handleRemoteMedia(fakeTrack);
|
|
119
|
+
expect(webRtc.emit).toHaveBeenCalledWith(TASK_EVENTS.TASK_MEDIA, fakeTrack);
|
|
124
120
|
});
|
|
125
121
|
});
|
|
126
122
|
|
|
@@ -140,103 +136,100 @@ describe('WebRTC Task', () => {
|
|
|
140
136
|
});
|
|
141
137
|
|
|
142
138
|
it('setUIControls for AGENT_CONTACT_ASSIGNED shows mute enabled', () => {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
139
|
+
sendStateEvents(webRtc, [
|
|
140
|
+
{type: TaskEvent.TASK_INCOMING, taskData},
|
|
141
|
+
{type: TaskEvent.ASSIGN, taskData},
|
|
142
|
+
]);
|
|
143
|
+
expect(webRtc.uiControls.main.mute.isVisible).toBe(true);
|
|
144
|
+
expect(webRtc.uiControls.main.mute.isEnabled).toBe(true);
|
|
145
|
+
});
|
|
150
146
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
147
|
+
it('default setUIControls hides mute when wrapup visible', () => {
|
|
148
|
+
sendStateEvents(webRtc, [
|
|
149
|
+
{type: TaskEvent.TASK_INCOMING, taskData},
|
|
150
|
+
{type: TaskEvent.ASSIGN, taskData},
|
|
151
|
+
{type: TaskEvent.TASK_WRAPUP},
|
|
152
|
+
]);
|
|
153
|
+
expect(webRtc.uiControls.main.wrapup.isVisible).toBe(true);
|
|
154
|
+
expect(webRtc.uiControls.main.mute.isVisible).toBe(false);
|
|
155
|
+
});
|
|
160
156
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
157
|
+
it('setUIControls for AGENT_CONTACT_HELD disables mute', () => {
|
|
158
|
+
sendStateEvents(webRtc, [
|
|
159
|
+
{type: TaskEvent.TASK_INCOMING, taskData},
|
|
160
|
+
{type: TaskEvent.ASSIGN, taskData},
|
|
161
|
+
{type: TaskEvent.HOLD_INITIATED, mediaResourceId: taskData.mediaResourceId},
|
|
162
|
+
{type: TaskEvent.HOLD_SUCCESS, mediaResourceId: taskData.mediaResourceId},
|
|
163
|
+
]);
|
|
164
|
+
expect(webRtc.uiControls.main.mute.isVisible).toBe(true);
|
|
165
|
+
expect(webRtc.uiControls.main.mute.isEnabled).toBe(false);
|
|
166
|
+
});
|
|
171
167
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
168
|
+
it('setUIControls for AGENT_CONTACT_UNHELD re-enables mute', () => {
|
|
169
|
+
sendStateEvents(webRtc, [
|
|
170
|
+
{type: TaskEvent.TASK_INCOMING, taskData},
|
|
171
|
+
{type: TaskEvent.ASSIGN, taskData},
|
|
172
|
+
{type: TaskEvent.HOLD_INITIATED, mediaResourceId: taskData.mediaResourceId},
|
|
173
|
+
{type: TaskEvent.HOLD_SUCCESS, mediaResourceId: taskData.mediaResourceId},
|
|
174
|
+
{type: TaskEvent.UNHOLD_INITIATED, mediaResourceId: taskData.mediaResourceId},
|
|
175
|
+
{type: TaskEvent.UNHOLD_SUCCESS, mediaResourceId: taskData.mediaResourceId},
|
|
176
|
+
]);
|
|
177
|
+
expect(webRtc.uiControls.main.mute.isVisible).toBe(true);
|
|
178
|
+
expect(webRtc.uiControls.main.mute.isEnabled).toBe(true);
|
|
179
|
+
});
|
|
184
180
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
181
|
+
it('setUIControls for AGENT_OFFER_CONTACT shows accept and decline', () => {
|
|
182
|
+
sendStateEvents(webRtc, [{type: TaskEvent.TASK_INCOMING, taskData}]);
|
|
183
|
+
expect(webRtc.uiControls.main.accept.isVisible).toBe(true);
|
|
184
|
+
expect(webRtc.uiControls.main.decline.isVisible).toBe(true);
|
|
185
|
+
});
|
|
190
186
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
187
|
+
it('setUIControls for AGENT_OFFER_CONSULT shows accept and decline', () => {
|
|
188
|
+
const consultedTaskData = {...taskData, isConsulted: true};
|
|
189
|
+
sendStateEvents(webRtc, [
|
|
190
|
+
{type: TaskEvent.TASK_INCOMING, taskData},
|
|
191
|
+
{type: TaskEvent.OFFER_CONSULT, taskData: consultedTaskData},
|
|
192
|
+
]);
|
|
193
|
+
expect(webRtc.uiControls.main.accept.isVisible).toBe(true);
|
|
194
|
+
expect(webRtc.uiControls.main.decline.isVisible).toBe(true);
|
|
195
|
+
});
|
|
200
196
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
197
|
+
it('setUIControls for AGENT_CONSULTING hides accept/decline and shows mute when consulted', () => {
|
|
198
|
+
const consultedTaskData = {...taskData, isConsulted: true};
|
|
199
|
+
sendStateEvents(webRtc, [
|
|
200
|
+
{type: TaskEvent.TASK_INCOMING, taskData},
|
|
201
|
+
{type: TaskEvent.OFFER_CONSULT, taskData: consultedTaskData},
|
|
202
|
+
{
|
|
203
|
+
type: TaskEvent.CONSULTING_ACTIVE,
|
|
204
|
+
consultDestinationAgentJoined: true,
|
|
205
|
+
taskData: consultedTaskData,
|
|
206
|
+
},
|
|
207
|
+
]);
|
|
208
|
+
expect(webRtc.uiControls.main.accept.isVisible).toBe(false);
|
|
209
|
+
expect(webRtc.uiControls.main.decline.isVisible).toBe(false);
|
|
210
|
+
expect(webRtc.uiControls.main.mute.isVisible).toBe(true);
|
|
211
|
+
expect(webRtc.uiControls.main.mute.isEnabled).toBe(true);
|
|
212
|
+
});
|
|
217
213
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
214
|
+
it('setUIControls for AGENT_CONSULT_ENDED returns mute to connected state behavior', () => {
|
|
215
|
+
const consultedTaskData = {...taskData, isConsulted: true};
|
|
216
|
+
sendStateEvents(webRtc, [
|
|
217
|
+
{type: TaskEvent.TASK_INCOMING, taskData},
|
|
218
|
+
{type: TaskEvent.OFFER_CONSULT, taskData: consultedTaskData},
|
|
219
|
+
{
|
|
220
|
+
type: TaskEvent.CONSULTING_ACTIVE,
|
|
221
|
+
consultDestinationAgentJoined: true,
|
|
222
|
+
taskData: consultedTaskData,
|
|
223
|
+
},
|
|
224
|
+
{type: TaskEvent.CONSULT_END},
|
|
225
|
+
]);
|
|
226
|
+
expect(webRtc.uiControls.main.mute.isVisible).toBe(false);
|
|
227
|
+
});
|
|
232
228
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
expect(webRtc.uiControls.main.accept.isVisible).toBe(false);
|
|
239
|
-
expect(webRtc.uiControls.main.decline.isVisible).toBe(false);
|
|
240
|
-
});
|
|
229
|
+
it('setUIControls for AGENT_CONTACT_OFFER_RONA hides accept and decline', () => {
|
|
230
|
+
sendStateEvents(webRtc, [{type: TaskEvent.TASK_INCOMING, taskData}, {type: TaskEvent.RONA}]);
|
|
231
|
+
expect(webRtc.uiControls.main.accept.isVisible).toBe(false);
|
|
232
|
+
expect(webRtc.uiControls.main.decline.isVisible).toBe(false);
|
|
233
|
+
});
|
|
241
234
|
});
|
|
242
235
|
});
|