@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.
Files changed (58) hide show
  1. package/dist/cc.js +3 -4
  2. package/dist/cc.js.map +1 -1
  3. package/dist/constants.js +1 -0
  4. package/dist/constants.js.map +1 -1
  5. package/dist/metrics/constants.js +2 -0
  6. package/dist/metrics/constants.js.map +1 -1
  7. package/dist/services/ApiAiAssistant.js +74 -3
  8. package/dist/services/ApiAiAssistant.js.map +1 -1
  9. package/dist/services/config/types.js +9 -1
  10. package/dist/services/config/types.js.map +1 -1
  11. package/dist/services/task/Task.js +32 -0
  12. package/dist/services/task/Task.js.map +1 -1
  13. package/dist/services/task/TaskManager.js +7 -2
  14. package/dist/services/task/TaskManager.js.map +1 -1
  15. package/dist/services/task/TaskUtils.js +3 -1
  16. package/dist/services/task/TaskUtils.js.map +1 -1
  17. package/dist/services/task/state-machine/TaskStateMachine.js +76 -0
  18. package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -1
  19. package/dist/services/task/state-machine/actions.js +113 -23
  20. package/dist/services/task/state-machine/actions.js.map +1 -1
  21. package/dist/services/task/state-machine/uiControlsComputer.js +99 -21
  22. package/dist/services/task/state-machine/uiControlsComputer.js.map +1 -1
  23. package/dist/types/constants.d.ts +1 -0
  24. package/dist/types/metrics/constants.d.ts +2 -0
  25. package/dist/types/services/ApiAiAssistant.d.ts +10 -2
  26. package/dist/types/services/config/types.d.ts +16 -0
  27. package/dist/types/services/task/Task.d.ts +10 -0
  28. package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +110 -0
  29. package/dist/types/services/task/state-machine/actions.d.ts +2 -0
  30. package/dist/types/types.d.ts +24 -0
  31. package/dist/types.js +15 -0
  32. package/dist/types.js.map +1 -1
  33. package/dist/webex.js +1 -1
  34. package/package.json +1 -1
  35. package/src/cc.ts +6 -4
  36. package/src/constants.ts +1 -0
  37. package/src/metrics/constants.ts +2 -0
  38. package/src/services/ApiAiAssistant.ts +102 -2
  39. package/src/services/config/types.ts +8 -0
  40. package/src/services/task/Task.ts +34 -0
  41. package/src/services/task/TaskManager.ts +7 -2
  42. package/src/services/task/TaskUtils.ts +5 -3
  43. package/src/services/task/ai-docs/AGENTS.md +7 -0
  44. package/src/services/task/ai-docs/ARCHITECTURE.md +12 -0
  45. package/src/services/task/state-machine/TaskStateMachine.ts +104 -0
  46. package/src/services/task/state-machine/actions.ts +151 -25
  47. package/src/services/task/state-machine/uiControlsComputer.ts +173 -30
  48. package/src/types.ts +25 -0
  49. package/test/unit/spec/cc.ts +2 -0
  50. package/test/unit/spec/services/ApiAiAssistant.ts +105 -17
  51. package/test/unit/spec/services/task/Task.ts +61 -0
  52. package/test/unit/spec/services/task/TaskManager.ts +42 -0
  53. package/test/unit/spec/services/task/TaskUtils.ts +65 -0
  54. package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +676 -0
  55. package/test/unit/spec/services/task/state-machine/uiControlsComputer.ts +597 -0
  56. package/test/unit/spec/services/task/voice/WebRTC.ts +99 -106
  57. package/umd/contact-center.min.js +2 -2
  58. package/umd/contact-center.min.js.map +1 -1
@@ -1,5 +1,5 @@
1
1
  import 'jsdom-global/register';
2
- import { LocalMicrophoneStream, CALL_EVENT_KEYS } from '@webex/calling';
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) { this.outputStream = stream; }
12
+ constructor(stream) {
13
+ this.outputStream = stream;
14
+ }
13
15
  },
14
- CALL_EVENT_KEYS: { REMOTE_MEDIA: 'remoteMedia' },
16
+ CALL_EVENT_KEYS: {REMOTE_MEDIA: 'remoteMedia'},
15
17
  }));
16
18
 
17
19
  beforeAll(() => {
18
- navigator.mediaDevices = { getUserMedia: jest.fn() };
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({ uploadLogs: jest.fn() }),
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 = { getAudioTracks: () => [fakeTrack] } as any;
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((webRtc).unregisterWebCallListeners).toHaveBeenCalled();
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
- (webRtc).handleRemoteMedia(fakeTrack);
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
- sendStateEvents(webRtc, [
144
- {type: TaskEvent.TASK_INCOMING, taskData},
145
- {type: TaskEvent.ASSIGN, taskData},
146
- ]);
147
- expect(webRtc.uiControls.main.mute.isVisible).toBe(true);
148
- expect(webRtc.uiControls.main.mute.isEnabled).toBe(true);
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
- it('default setUIControls hides mute when wrapup visible', () => {
152
- sendStateEvents(webRtc, [
153
- {type: TaskEvent.TASK_INCOMING, taskData},
154
- {type: TaskEvent.ASSIGN, taskData},
155
- {type: TaskEvent.TASK_WRAPUP},
156
- ]);
157
- expect(webRtc.uiControls.main.wrapup.isVisible).toBe(true);
158
- expect(webRtc.uiControls.main.mute.isVisible).toBe(false);
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
- it('setUIControls for AGENT_CONTACT_HELD disables mute', () => {
162
- sendStateEvents(webRtc, [
163
- {type: TaskEvent.TASK_INCOMING, taskData},
164
- {type: TaskEvent.ASSIGN, taskData},
165
- {type: TaskEvent.HOLD_INITIATED, mediaResourceId: taskData.mediaResourceId},
166
- {type: TaskEvent.HOLD_SUCCESS, mediaResourceId: taskData.mediaResourceId},
167
- ]);
168
- expect(webRtc.uiControls.main.mute.isVisible).toBe(true);
169
- expect(webRtc.uiControls.main.mute.isEnabled).toBe(false);
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
- it('setUIControls for AGENT_CONTACT_UNHELD re-enables mute', () => {
173
- sendStateEvents(webRtc, [
174
- {type: TaskEvent.TASK_INCOMING, taskData},
175
- {type: TaskEvent.ASSIGN, taskData},
176
- {type: TaskEvent.HOLD_INITIATED, mediaResourceId: taskData.mediaResourceId},
177
- {type: TaskEvent.HOLD_SUCCESS, mediaResourceId: taskData.mediaResourceId},
178
- {type: TaskEvent.UNHOLD_INITIATED, mediaResourceId: taskData.mediaResourceId},
179
- {type: TaskEvent.UNHOLD_SUCCESS, mediaResourceId: taskData.mediaResourceId},
180
- ]);
181
- expect(webRtc.uiControls.main.mute.isVisible).toBe(true);
182
- expect(webRtc.uiControls.main.mute.isEnabled).toBe(true);
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
- it('setUIControls for AGENT_OFFER_CONTACT shows accept and decline', () => {
186
- sendStateEvents(webRtc, [{type: TaskEvent.TASK_INCOMING, taskData}]);
187
- expect(webRtc.uiControls.main.accept.isVisible).toBe(true);
188
- expect(webRtc.uiControls.main.decline.isVisible).toBe(true);
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
- it('setUIControls for AGENT_OFFER_CONSULT shows accept and decline', () => {
192
- const consultedTaskData = {...taskData, isConsulted: true};
193
- sendStateEvents(webRtc, [
194
- {type: TaskEvent.TASK_INCOMING, taskData},
195
- {type: TaskEvent.OFFER_CONSULT, taskData: consultedTaskData},
196
- ]);
197
- expect(webRtc.uiControls.main.accept.isVisible).toBe(true);
198
- expect(webRtc.uiControls.main.decline.isVisible).toBe(true);
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
- it('setUIControls for AGENT_CONSULTING hides accept/decline and shows mute when consulted', () => {
202
- const consultedTaskData = {...taskData, isConsulted: true};
203
- sendStateEvents(webRtc, [
204
- {type: TaskEvent.TASK_INCOMING, taskData},
205
- {type: TaskEvent.OFFER_CONSULT, taskData: consultedTaskData},
206
- {
207
- type: TaskEvent.CONSULTING_ACTIVE,
208
- consultDestinationAgentJoined: true,
209
- taskData: consultedTaskData,
210
- },
211
- ]);
212
- expect(webRtc.uiControls.main.accept.isVisible).toBe(false);
213
- expect(webRtc.uiControls.main.decline.isVisible).toBe(false);
214
- expect(webRtc.uiControls.main.mute.isVisible).toBe(true);
215
- expect(webRtc.uiControls.main.mute.isEnabled).toBe(true);
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
- it('setUIControls for AGENT_CONSULT_ENDED returns mute to connected state behavior', () => {
219
- const consultedTaskData = {...taskData, isConsulted: true};
220
- sendStateEvents(webRtc, [
221
- {type: TaskEvent.TASK_INCOMING, taskData},
222
- {type: TaskEvent.OFFER_CONSULT, taskData: consultedTaskData},
223
- {
224
- type: TaskEvent.CONSULTING_ACTIVE,
225
- consultDestinationAgentJoined: true,
226
- taskData: consultedTaskData,
227
- },
228
- {type: TaskEvent.CONSULT_END},
229
- ]);
230
- expect(webRtc.uiControls.main.mute.isVisible).toBe(false);
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
- it('setUIControls for AGENT_CONTACT_OFFER_RONA hides accept and decline', () => {
234
- sendStateEvents(webRtc, [
235
- {type: TaskEvent.TASK_INCOMING, taskData},
236
- {type: TaskEvent.RONA},
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
  });