@webex/contact-center 3.11.0 → 3.12.0-llmrefactor.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.
Files changed (267) hide show
  1. package/.sdd/manifest.json +882 -0
  2. package/AGENTS.md +94 -0
  3. package/ai-docs/ARCHITECTURE.md +168 -0
  4. package/ai-docs/CONTRACTS.md +46 -0
  5. package/ai-docs/GETTING_STARTED.md +168 -0
  6. package/ai-docs/GLOSSARY.md +43 -0
  7. package/ai-docs/README.md +138 -0
  8. package/ai-docs/REVIEW_CHECKLIST.md +41 -0
  9. package/ai-docs/RULES.md +444 -0
  10. package/ai-docs/SECURITY.md +52 -0
  11. package/ai-docs/SERVICE_STATE.md +48 -0
  12. package/ai-docs/SPEC_INDEX.md +65 -0
  13. package/ai-docs/adr/0001-spec-source-policy.md +55 -0
  14. package/ai-docs/adr/README.md +8 -0
  15. package/ai-docs/adr/_adr-template.md +31 -0
  16. package/ai-docs/contact-center-spec.md +341 -0
  17. package/ai-docs/features/generated-spec-conformance-fidelity-remediation/spec/feature-spec.md +117 -0
  18. package/ai-docs/features/residual-warning-coverage-completion/spec/feature-spec.md +203 -0
  19. package/ai-docs/features/validator-code-fidelity-drift-fix/spec/feature-spec.md +315 -0
  20. package/ai-docs/patterns/event-driven-patterns.md +485 -0
  21. package/ai-docs/patterns/testing-patterns.md +480 -0
  22. package/ai-docs/patterns/typescript-patterns.md +365 -0
  23. package/ai-docs/templates/README.md +102 -0
  24. package/ai-docs/templates/documentation/create-agents-md.md +240 -0
  25. package/ai-docs/templates/documentation/create-architecture-md.md +295 -0
  26. package/ai-docs/templates/existing-service/bug-fix.md +254 -0
  27. package/ai-docs/templates/existing-service/feature-enhancement.md +450 -0
  28. package/ai-docs/templates/new-method/00-master.md +80 -0
  29. package/ai-docs/templates/new-method/01-requirements.md +232 -0
  30. package/ai-docs/templates/new-method/02-implementation.md +295 -0
  31. package/ai-docs/templates/new-method/03-tests.md +201 -0
  32. package/ai-docs/templates/new-method/04-validation.md +141 -0
  33. package/ai-docs/templates/new-service/00-master.md +109 -0
  34. package/ai-docs/templates/new-service/01-pre-questions.md +159 -0
  35. package/ai-docs/templates/new-service/02-code-generation.md +346 -0
  36. package/ai-docs/templates/new-service/03-integration.md +178 -0
  37. package/ai-docs/templates/new-service/04-test-generation.md +205 -0
  38. package/ai-docs/templates/new-service/05-validation.md +145 -0
  39. package/dist/cc.js +379 -50
  40. package/dist/cc.js.map +1 -1
  41. package/dist/config.js +6 -0
  42. package/dist/config.js.map +1 -1
  43. package/dist/constants.js +22 -2
  44. package/dist/constants.js.map +1 -1
  45. package/dist/index.js +27 -5
  46. package/dist/index.js.map +1 -1
  47. package/dist/metrics/behavioral-events.js +114 -0
  48. package/dist/metrics/behavioral-events.js.map +1 -1
  49. package/dist/metrics/constants.js +32 -3
  50. package/dist/metrics/constants.js.map +1 -1
  51. package/dist/services/ApiAiAssistant.js +318 -0
  52. package/dist/services/ApiAiAssistant.js.map +1 -0
  53. package/dist/services/UserPreference.js +427 -0
  54. package/dist/services/UserPreference.js.map +1 -0
  55. package/dist/services/agent/types.js.map +1 -1
  56. package/dist/services/config/Util.js +8 -4
  57. package/dist/services/config/Util.js.map +1 -1
  58. package/dist/services/config/constants.js +35 -2
  59. package/dist/services/config/constants.js.map +1 -1
  60. package/dist/services/config/index.js +41 -2
  61. package/dist/services/config/index.js.map +1 -1
  62. package/dist/services/config/types.js +66 -8
  63. package/dist/services/config/types.js.map +1 -1
  64. package/dist/services/constants.js +27 -1
  65. package/dist/services/constants.js.map +1 -1
  66. package/dist/services/core/Err.js.map +1 -1
  67. package/dist/services/core/Utils.js +122 -25
  68. package/dist/services/core/Utils.js.map +1 -1
  69. package/dist/services/core/aqm-reqs.js +92 -17
  70. package/dist/services/core/aqm-reqs.js.map +1 -1
  71. package/dist/services/core/websocket/WebSocketManager.js +22 -6
  72. package/dist/services/core/websocket/WebSocketManager.js.map +1 -1
  73. package/dist/services/core/websocket/connection-service.js +3 -1
  74. package/dist/services/core/websocket/connection-service.js.map +1 -1
  75. package/dist/services/core/websocket/types.js.map +1 -1
  76. package/dist/services/index.js +6 -0
  77. package/dist/services/index.js.map +1 -1
  78. package/dist/services/task/Task.js +688 -0
  79. package/dist/services/task/Task.js.map +1 -0
  80. package/dist/services/task/TaskFactory.js +45 -0
  81. package/dist/services/task/TaskFactory.js.map +1 -0
  82. package/dist/services/task/TaskManager.js +751 -457
  83. package/dist/services/task/TaskManager.js.map +1 -1
  84. package/dist/services/task/TaskUtils.js +220 -23
  85. package/dist/services/task/TaskUtils.js.map +1 -1
  86. package/dist/services/task/constants.js +23 -2
  87. package/dist/services/task/constants.js.map +1 -1
  88. package/dist/services/task/dialer.js +129 -0
  89. package/dist/services/task/dialer.js.map +1 -1
  90. package/dist/services/task/digital/Digital.js +77 -0
  91. package/dist/services/task/digital/Digital.js.map +1 -0
  92. package/dist/services/task/state-machine/TaskStateMachine.js +873 -0
  93. package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -0
  94. package/dist/services/task/state-machine/actions.js +567 -0
  95. package/dist/services/task/state-machine/actions.js.map +1 -0
  96. package/dist/services/task/state-machine/constants.js +161 -0
  97. package/dist/services/task/state-machine/constants.js.map +1 -0
  98. package/dist/services/task/state-machine/guards.js +382 -0
  99. package/dist/services/task/state-machine/guards.js.map +1 -0
  100. package/dist/services/task/state-machine/index.js +53 -0
  101. package/dist/services/task/state-machine/index.js.map +1 -0
  102. package/dist/services/task/state-machine/types.js +54 -0
  103. package/dist/services/task/state-machine/types.js.map +1 -0
  104. package/dist/services/task/state-machine/uiControlsComputer.js +603 -0
  105. package/dist/services/task/state-machine/uiControlsComputer.js.map +1 -0
  106. package/dist/services/task/taskDataNormalizer.js +99 -0
  107. package/dist/services/task/taskDataNormalizer.js.map +1 -0
  108. package/dist/services/task/types.js +227 -4
  109. package/dist/services/task/types.js.map +1 -1
  110. package/dist/services/task/voice/Voice.js +1044 -0
  111. package/dist/services/task/voice/Voice.js.map +1 -0
  112. package/dist/services/task/voice/WebRTC.js +149 -0
  113. package/dist/services/task/voice/WebRTC.js.map +1 -0
  114. package/dist/types/cc.d.ts +894 -0
  115. package/dist/types/config.d.ts +72 -0
  116. package/dist/types/constants.d.ts +66 -0
  117. package/dist/types/index.d.ts +199 -0
  118. package/dist/types/logger-proxy.d.ts +71 -0
  119. package/dist/types/metrics/MetricsManager.d.ts +223 -0
  120. package/dist/types/metrics/behavioral-events.d.ts +29 -0
  121. package/dist/types/metrics/constants.d.ts +181 -0
  122. package/dist/types/services/AddressBook.d.ts +74 -0
  123. package/dist/types/services/ApiAiAssistant.d.ts +49 -0
  124. package/dist/types/services/EntryPoint.d.ts +67 -0
  125. package/dist/types/services/Queue.d.ts +76 -0
  126. package/dist/types/services/UserPreference.d.ts +118 -0
  127. package/dist/types/services/WebCallingService.d.ts +1 -0
  128. package/dist/types/services/agent/index.d.ts +46 -0
  129. package/dist/types/services/agent/types.d.ts +413 -0
  130. package/dist/types/services/config/Util.d.ts +20 -0
  131. package/dist/types/services/config/constants.d.ts +270 -0
  132. package/dist/types/services/config/index.d.ts +177 -0
  133. package/dist/types/services/config/types.d.ts +1368 -0
  134. package/dist/types/services/constants.d.ts +110 -0
  135. package/dist/types/services/core/Err.d.ts +125 -0
  136. package/dist/types/services/core/GlobalTypes.d.ts +58 -0
  137. package/dist/types/services/core/Utils.d.ts +121 -0
  138. package/dist/types/services/core/WebexRequest.d.ts +22 -0
  139. package/dist/types/services/core/aqm-reqs.d.ts +65 -0
  140. package/dist/types/services/core/constants.d.ts +99 -0
  141. package/dist/types/services/core/types.d.ts +47 -0
  142. package/dist/types/services/core/websocket/WebSocketManager.d.ts +36 -0
  143. package/dist/types/services/core/websocket/connection-service.d.ts +27 -0
  144. package/dist/types/services/core/websocket/keepalive.worker.d.ts +2 -0
  145. package/dist/types/services/core/websocket/types.d.ts +37 -0
  146. package/dist/types/services/index.d.ts +54 -0
  147. package/dist/types/services/task/AutoWrapup.d.ts +40 -0
  148. package/dist/types/services/task/Task.d.ts +157 -0
  149. package/dist/types/services/task/TaskFactory.d.ts +12 -0
  150. package/dist/types/services/task/TaskManager.d.ts +1 -0
  151. package/dist/types/services/task/TaskUtils.d.ts +138 -0
  152. package/dist/types/services/task/constants.d.ts +91 -0
  153. package/dist/types/services/task/contact.d.ts +69 -0
  154. package/dist/types/services/task/dialer.d.ts +73 -0
  155. package/dist/types/services/task/digital/Digital.d.ts +22 -0
  156. package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +1194 -0
  157. package/dist/types/services/task/state-machine/actions.d.ts +10 -0
  158. package/dist/types/services/task/state-machine/constants.d.ts +107 -0
  159. package/dist/types/services/task/state-machine/guards.d.ts +102 -0
  160. package/dist/types/services/task/state-machine/index.d.ts +13 -0
  161. package/dist/types/services/task/state-machine/types.d.ts +269 -0
  162. package/dist/types/services/task/state-machine/uiControlsComputer.d.ts +9 -0
  163. package/dist/types/services/task/taskDataNormalizer.d.ts +10 -0
  164. package/dist/types/services/task/types.d.ts +1856 -0
  165. package/dist/types/services/task/voice/Voice.d.ts +184 -0
  166. package/dist/types/services/task/voice/WebRTC.d.ts +53 -0
  167. package/dist/types/types.d.ts +778 -0
  168. package/dist/types/utils/PageCache.d.ts +173 -0
  169. package/dist/types/webex-config.d.ts +53 -0
  170. package/dist/types/webex.d.ts +8 -0
  171. package/dist/types.js +130 -1
  172. package/dist/types.js.map +1 -1
  173. package/dist/webex.js +14 -2
  174. package/dist/webex.js.map +1 -1
  175. package/package.json +16 -12
  176. package/src/cc.ts +477 -51
  177. package/src/config.ts +6 -0
  178. package/src/constants.ts +21 -1
  179. package/src/index.ts +24 -5
  180. package/src/metrics/ai-docs/AGENTS.md +350 -0
  181. package/src/metrics/ai-docs/ARCHITECTURE.md +338 -0
  182. package/src/metrics/ai-docs/metrics-spec.md +854 -0
  183. package/src/metrics/behavioral-events.ts +120 -0
  184. package/src/metrics/constants.ts +37 -3
  185. package/src/services/ApiAiAssistant.ts +412 -0
  186. package/src/services/UserPreference.ts +509 -0
  187. package/src/services/agent/ai-docs/AGENTS.md +240 -0
  188. package/src/services/agent/ai-docs/ARCHITECTURE.md +304 -0
  189. package/src/services/agent/ai-docs/agent-spec.md +504 -0
  190. package/src/services/agent/types.ts +1 -1
  191. package/src/services/ai-docs/AGENTS.md +386 -0
  192. package/src/services/ai-docs/services-spec.md +492 -0
  193. package/src/services/config/Util.ts +10 -2
  194. package/src/services/config/ai-docs/AGENTS.md +255 -0
  195. package/src/services/config/ai-docs/ARCHITECTURE.md +426 -0
  196. package/src/services/config/ai-docs/config-spec.md +669 -0
  197. package/src/services/config/constants.ts +37 -1
  198. package/src/services/config/index.ts +45 -1
  199. package/src/services/config/types.ts +241 -11
  200. package/src/services/constants.ts +29 -0
  201. package/src/services/core/Err.ts +3 -0
  202. package/src/services/core/Utils.ts +143 -30
  203. package/src/services/core/ai-docs/AGENTS.md +381 -0
  204. package/src/services/core/ai-docs/ARCHITECTURE.md +698 -0
  205. package/src/services/core/ai-docs/core-spec.md +783 -0
  206. package/src/services/core/aqm-reqs.ts +100 -22
  207. package/src/services/core/websocket/WebSocketManager.ts +23 -6
  208. package/src/services/core/websocket/connection-service.ts +5 -1
  209. package/src/services/core/websocket/types.ts +1 -1
  210. package/src/services/index.ts +4 -0
  211. package/src/services/task/Task.ts +837 -0
  212. package/src/services/task/TaskFactory.ts +55 -0
  213. package/src/services/task/TaskManager.ts +793 -521
  214. package/src/services/task/TaskUtils.ts +314 -24
  215. package/src/services/task/ai-docs/AGENTS.md +457 -0
  216. package/src/services/task/ai-docs/ARCHITECTURE.md +594 -0
  217. package/src/services/task/ai-docs/task-spec.md +1319 -0
  218. package/src/services/task/constants.ts +23 -0
  219. package/src/services/task/dialer.ts +136 -1
  220. package/src/services/task/digital/Digital.ts +95 -0
  221. package/src/services/task/state-machine/TaskStateMachine.ts +1166 -0
  222. package/src/services/task/state-machine/actions.ts +738 -0
  223. package/src/services/task/state-machine/ai-docs/AGENTS.md +458 -0
  224. package/src/services/task/state-machine/ai-docs/ARCHITECTURE.md +1137 -0
  225. package/src/services/task/state-machine/ai-docs/task-state-machine-spec.md +2177 -0
  226. package/src/services/task/state-machine/constants.ts +172 -0
  227. package/src/services/task/state-machine/guards.ts +445 -0
  228. package/src/services/task/state-machine/index.ts +28 -0
  229. package/src/services/task/state-machine/types.ts +243 -0
  230. package/src/services/task/state-machine/uiControlsComputer.ts +961 -0
  231. package/src/services/task/taskDataNormalizer.ts +137 -0
  232. package/src/services/task/types.ts +734 -71
  233. package/src/services/task/voice/Voice.ts +1270 -0
  234. package/src/services/task/voice/WebRTC.ts +187 -0
  235. package/src/types.ts +205 -2
  236. package/src/utils/AGENTS.md +278 -0
  237. package/src/utils/ai-docs/utils-spec.md +381 -0
  238. package/src/webex.js +2 -0
  239. package/test/unit/spec/cc.ts +503 -43
  240. package/test/unit/spec/logger-proxy.ts +70 -0
  241. package/test/unit/spec/services/ApiAiAssistant.ts +273 -0
  242. package/test/unit/spec/services/UserPreference.ts +401 -0
  243. package/test/unit/spec/services/WebCallingService.ts +7 -1
  244. package/test/unit/spec/services/config/index.ts +85 -29
  245. package/test/unit/spec/services/core/Utils.ts +481 -2
  246. package/test/unit/spec/services/core/websocket/WebSocketManager.ts +137 -41
  247. package/test/unit/spec/services/core/websocket/connection-service.ts +3 -1
  248. package/test/unit/spec/services/task/AutoWrapup.ts +63 -0
  249. package/test/unit/spec/services/task/Task.ts +477 -0
  250. package/test/unit/spec/services/task/TaskFactory.ts +62 -0
  251. package/test/unit/spec/services/task/TaskManager.ts +1001 -1003
  252. package/test/unit/spec/services/task/TaskUtils.ts +235 -0
  253. package/test/unit/spec/services/task/dialer.ts +372 -96
  254. package/test/unit/spec/services/task/digital/Digital.ts +105 -0
  255. package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +2651 -0
  256. package/test/unit/spec/services/task/state-machine/guards.ts +637 -0
  257. package/test/unit/spec/services/task/state-machine/types.ts +18 -0
  258. package/test/unit/spec/services/task/state-machine/uiControlsComputer.ts +2663 -0
  259. package/test/unit/spec/services/task/taskTestUtils.ts +87 -0
  260. package/test/unit/spec/services/task/voice/Voice.ts +649 -0
  261. package/test/unit/spec/services/task/voice/WebRTC.ts +235 -0
  262. package/umd/contact-center.min.js +2 -2
  263. package/umd/contact-center.min.js.map +1 -1
  264. package/dist/services/task/index.js +0 -1525
  265. package/dist/services/task/index.js.map +0 -1
  266. package/src/services/task/index.ts +0 -1801
  267. package/test/unit/spec/services/task/index.ts +0 -2184
@@ -1,7 +1,11 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
2
  import {WebSocketManager} from '../../../../../../src/services/core/websocket/WebSocketManager';
3
3
  import {WebexSDK, SubscribeRequest} from '../../../../../../src/types';
4
- import {SUBSCRIBE_API, WCC_API_GATEWAY} from '../../../../../../src/services/constants';
4
+ import {
5
+ RTD_SUBSCRIBE_API,
6
+ SUBSCRIBE_API,
7
+ WCC_API_GATEWAY,
8
+ } from '../../../../../../src/services/constants';
5
9
  import {WEB_SOCKET_MANAGER_FILE} from '../../../../../../src/constants';
6
10
  import LoggerProxy from '../../../../../../src/logger-proxy';
7
11
 
@@ -18,10 +22,10 @@ jest.mock('../../../../../../src/logger-proxy', () => ({
18
22
 
19
23
  class MockWebSocket {
20
24
  static inst: MockWebSocket;
21
- onopen: () => void = () => { };
22
- onerror: (event: any) => void = () => { };
23
- onclose: (event: any) => void = () => { };
24
- onmessage: (msg: any) => void = () => { };
25
+ onopen: () => void = () => {};
26
+ onerror: (event: any) => void = () => {};
27
+ onclose: (event: any) => void = () => {};
28
+ onmessage: (msg: any) => void = () => {};
25
29
  close = jest.fn();
26
30
  send = jest.fn();
27
31
 
@@ -37,7 +41,7 @@ class MockWebSocket {
37
41
  class MockCustomEvent<T> extends Event {
38
42
  detail: T;
39
43
 
40
- constructor(event: string, params: { detail: T }) {
44
+ constructor(event: string, params: {detail: T}) {
41
45
  super(event);
42
46
  this.detail = params.detail;
43
47
  }
@@ -49,7 +53,7 @@ global.CustomEvent = MockCustomEvent as any;
49
53
  class MockMessageEvent extends Event {
50
54
  data: any;
51
55
 
52
- constructor(type: string, eventInitDict: { data: any }) {
56
+ constructor(type: string, eventInitDict: {data: any}) {
53
57
  super(type);
54
58
  this.data = eventInitDict.data;
55
59
  }
@@ -74,6 +78,14 @@ describe('WebSocketManager', () => {
74
78
 
75
79
  mockWebex = {
76
80
  request: jest.fn(),
81
+ credentials: {
82
+ getOrgId: jest.fn().mockReturnValue('test-org-id'),
83
+ },
84
+ internal: {
85
+ services: {
86
+ isIntegrationEnvironment: jest.fn().mockReturnValue(true), // INT environment by default
87
+ },
88
+ },
77
89
  } as unknown as WebexSDK;
78
90
 
79
91
  mockWorker = {
@@ -85,18 +97,18 @@ describe('WebSocketManager', () => {
85
97
  global.WebSocket = MockWebSocket as any;
86
98
 
87
99
  global.Blob = function (content: any[], options: any) {
88
- return { content, options };
100
+ return {content, options};
89
101
  } as any;
90
102
 
91
103
  global.URL.createObjectURL = function (blob: Blob) {
92
104
  return 'blob:http://localhost:3000/12345';
93
105
  };
94
106
 
95
- webSocketManager = new WebSocketManager({ webex: mockWebex });
107
+ webSocketManager = new WebSocketManager({webex: mockWebex});
96
108
 
97
109
  setTimeout(() => {
98
110
  MockWebSocket.inst.onopen();
99
- MockWebSocket.inst.onmessage({ data: JSON.stringify({ type: "Welcome" }) });
111
+ MockWebSocket.inst.onmessage({data: JSON.stringify({type: 'Welcome'})});
100
112
  }, 1);
101
113
 
102
114
  console.log = jest.fn();
@@ -107,22 +119,106 @@ describe('WebSocketManager', () => {
107
119
  expect(webSocketManager).toBeDefined();
108
120
  });
109
121
 
110
- it('should register and connect to WebSocket', async () => {
122
+ it('should register and connect to WebSocket with X-ORGANIZATION-ID header for INT environment', async () => {
123
+ const subscribeResponse = {
124
+ body: {
125
+ webSocketUrl: 'wss://fake-url',
126
+ },
127
+ };
128
+
129
+ // Mock INT environment (services.isIntegrationEnvironment returns true)
130
+ (mockWebex.internal.services.isIntegrationEnvironment as jest.Mock).mockReturnValue(true);
131
+ (mockWebex.request as jest.Mock).mockResolvedValueOnce(subscribeResponse);
132
+
133
+ await webSocketManager.initWebSocket({body: fakeSubscribeRequest, resource: SUBSCRIBE_API});
134
+
135
+ expect(mockWebex.request).toHaveBeenCalledWith({
136
+ service: WCC_API_GATEWAY,
137
+ resource: SUBSCRIBE_API,
138
+ method: 'POST',
139
+ body: fakeSubscribeRequest,
140
+ headers: {'X-ORGANIZATION-ID': 'test-org-id'},
141
+ });
142
+ });
143
+
144
+ it('should connect rtd websocket', async () => {
145
+ const subscribeResponse = {
146
+ body: {
147
+ webSocketUrl: 'wss://fake-url',
148
+ },
149
+ };
150
+
151
+ (mockWebex.request as jest.Mock).mockResolvedValueOnce(subscribeResponse);
152
+
153
+ await webSocketManager.initWebSocket({
154
+ body: fakeSubscribeRequest,
155
+ resource: RTD_SUBSCRIBE_API,
156
+ });
157
+
158
+ expect(mockWebex.request).toHaveBeenCalledWith({
159
+ service: WCC_API_GATEWAY,
160
+ resource: RTD_SUBSCRIBE_API,
161
+ method: 'POST',
162
+ body: fakeSubscribeRequest,
163
+ headers: {'X-ORGANIZATION-ID': 'test-org-id'},
164
+ });
165
+ });
166
+
167
+ it('should register and connect to WebSocket without X-ORGANIZATION-ID header for production environment', async () => {
168
+ const subscribeResponse = {
169
+ body: {
170
+ webSocketUrl: 'wss://fake-url',
171
+ },
172
+ };
173
+ // Mock production environment (services.isIntegrationEnvironment returns false)
174
+ (mockWebex.internal.services.isIntegrationEnvironment as jest.Mock).mockReturnValue(false);
175
+ (mockWebex.request as jest.Mock).mockResolvedValueOnce(subscribeResponse);
176
+
177
+ // Create new WebSocketManager instance with production mock
178
+ webSocketManager = new WebSocketManager({ webex: mockWebex });
179
+
180
+ setTimeout(() => {
181
+ MockWebSocket.inst.onopen();
182
+ MockWebSocket.inst.onmessage({ data: JSON.stringify({ type: "Welcome" }) });
183
+ }, 1);
184
+
185
+ await webSocketManager.initWebSocket({ body: fakeSubscribeRequest, resource: SUBSCRIBE_API });
186
+
187
+ expect(mockWebex.request).toHaveBeenCalledWith({
188
+ service: WCC_API_GATEWAY,
189
+ resource: SUBSCRIBE_API,
190
+ method: 'POST',
191
+ body: fakeSubscribeRequest,
192
+ headers: undefined,
193
+ });
194
+ });
195
+
196
+ it('should not send X-ORGANIZATION-ID header when services.isIntegrationEnvironment is not available', async () => {
111
197
  const subscribeResponse = {
112
198
  body: {
113
199
  webSocketUrl: 'wss://fake-url',
114
200
  },
115
201
  };
116
202
 
203
+ // Mock services not available (defaults to production behavior)
204
+ (mockWebex as any).internal = undefined;
117
205
  (mockWebex.request as jest.Mock).mockResolvedValueOnce(subscribeResponse);
118
206
 
119
- await webSocketManager.initWebSocket({ body: fakeSubscribeRequest });
207
+ webSocketManager = new WebSocketManager({ webex: mockWebex });
208
+
209
+ setTimeout(() => {
210
+ MockWebSocket.inst.onopen();
211
+ MockWebSocket.inst.onmessage({ data: JSON.stringify({ type: "Welcome" }) });
212
+ }, 1);
213
+
214
+ await webSocketManager.initWebSocket({ body: fakeSubscribeRequest, resource: SUBSCRIBE_API });
120
215
 
121
216
  expect(mockWebex.request).toHaveBeenCalledWith({
122
217
  service: WCC_API_GATEWAY,
123
218
  resource: SUBSCRIBE_API,
124
219
  method: 'POST',
125
220
  body: fakeSubscribeRequest,
221
+ headers: undefined,
126
222
  });
127
223
  });
128
224
 
@@ -132,7 +228,7 @@ describe('WebSocketManager', () => {
132
228
  (mockWebex.request as jest.Mock).mockRejectedValueOnce(error);
133
229
 
134
230
  await expect(
135
- webSocketManager.initWebSocket({ body: fakeSubscribeRequest })
231
+ webSocketManager.initWebSocket({ body: fakeSubscribeRequest, resource: SUBSCRIBE_API })
136
232
  ).rejects.toThrow(error);
137
233
 
138
234
  expect(LoggerProxy.error).toHaveBeenCalledWith(
@@ -155,12 +251,12 @@ describe('WebSocketManager', () => {
155
251
 
156
252
  (mockWebex.request as jest.Mock).mockResolvedValueOnce(subscribeResponse);
157
253
 
158
- await webSocketManager.initWebSocket({ body: fakeSubscribeRequest });
254
+ await webSocketManager.initWebSocket({body: fakeSubscribeRequest, resource: SUBSCRIBE_API});
159
255
 
160
256
  webSocketManager.close(true, 'Test reason');
161
257
 
162
258
  expect(MockWebSocket.inst.close).toHaveBeenCalled();
163
- expect(mockWorker.postMessage).toHaveBeenCalledWith({ type: 'terminate' });
259
+ expect(mockWorker.postMessage).toHaveBeenCalledWith({type: 'terminate'});
164
260
  });
165
261
 
166
262
  it('should handle WebSocket keepalive messages', async () => {
@@ -172,19 +268,19 @@ describe('WebSocketManager', () => {
172
268
 
173
269
  (mockWebex.request as jest.Mock).mockResolvedValueOnce(subscribeResponse);
174
270
 
175
- await webSocketManager.initWebSocket({ body: fakeSubscribeRequest });
271
+ await webSocketManager.initWebSocket({body: fakeSubscribeRequest, resource: SUBSCRIBE_API});
176
272
 
177
273
  setTimeout(() => {
178
274
  MockWebSocket.inst.onopen();
179
- MockWebSocket.inst.onmessage({ data: JSON.stringify({ type: 'keepalive' }) });
275
+ MockWebSocket.inst.onmessage({data: JSON.stringify({type: 'keepalive'})});
180
276
  mockWorker.onmessage({
181
277
  data: {
182
- type: 'keepalive'
183
- }
278
+ type: 'keepalive',
279
+ },
184
280
  });
185
281
  }, 1);
186
282
 
187
- expect(MockWebSocket.inst.send).toHaveBeenCalledWith(JSON.stringify({ keepalive: 'true' }));
283
+ expect(MockWebSocket.inst.send).toHaveBeenCalledWith(JSON.stringify({keepalive: 'true'}));
188
284
  });
189
285
 
190
286
  it('should handle WebSocket close due to network issue', async () => {
@@ -196,7 +292,7 @@ describe('WebSocketManager', () => {
196
292
 
197
293
  (mockWebex.request as jest.Mock).mockResolvedValueOnce(subscribeResponse);
198
294
 
199
- await webSocketManager.initWebSocket({ body: fakeSubscribeRequest });
295
+ await webSocketManager.initWebSocket({body: fakeSubscribeRequest, resource: SUBSCRIBE_API});
200
296
 
201
297
  // Mock navigator.onLine to simulate network issue
202
298
  Object.defineProperty(global, 'navigator', {
@@ -219,10 +315,10 @@ describe('WebSocketManager', () => {
219
315
  // Wait for the close event to be handled
220
316
  await new Promise((resolve) => setTimeout(resolve, 10));
221
317
 
222
- expect(mockWorker.postMessage).toHaveBeenCalledWith({ type: 'terminate' });
318
+ expect(mockWorker.postMessage).toHaveBeenCalledWith({type: 'terminate'});
223
319
  expect(LoggerProxy.error).toHaveBeenCalledWith(
224
320
  '[WebSocketStatus] | event=webSocketClose | WebSocket connection closed REASON: network issue',
225
- { module: WEB_SOCKET_MANAGER_FILE, method: 'webSocketOnCloseHandler' }
321
+ {module: WEB_SOCKET_MANAGER_FILE, method: 'webSocketOnCloseHandler'}
226
322
  );
227
323
 
228
324
  // Restore navigator.onLine to true
@@ -243,14 +339,14 @@ describe('WebSocketManager', () => {
243
339
 
244
340
  (mockWebex.request as jest.Mock).mockResolvedValueOnce(subscribeResponse);
245
341
 
246
- await webSocketManager.initWebSocket({ body: fakeSubscribeRequest });
342
+ await webSocketManager.initWebSocket({body: fakeSubscribeRequest, resource: SUBSCRIBE_API});
247
343
 
248
344
  const errorEvent = new Event('error');
249
345
  MockWebSocket.inst.onerror(errorEvent);
250
346
 
251
347
  expect(LoggerProxy.error).toHaveBeenCalledWith(
252
348
  '[WebSocketStatus] | event=socketConnectionFailed | WebSocket connection failed [object Event]',
253
- { module: WEB_SOCKET_MANAGER_FILE, method: 'connect' }
349
+ {module: WEB_SOCKET_MANAGER_FILE, method: 'connect'}
254
350
  );
255
351
  });
256
352
 
@@ -263,17 +359,17 @@ describe('WebSocketManager', () => {
263
359
 
264
360
  (mockWebex.request as jest.Mock).mockResolvedValueOnce(subscribeResponse);
265
361
 
266
- await webSocketManager.initWebSocket({ body: fakeSubscribeRequest });
362
+ await webSocketManager.initWebSocket({body: fakeSubscribeRequest, resource: SUBSCRIBE_API});
267
363
 
268
364
  const messageEvent = new MessageEvent('message', {
269
- data: JSON.stringify({ type: 'AGENT_MULTI_LOGIN' }),
365
+ data: JSON.stringify({type: 'AGENT_MULTI_LOGIN'}),
270
366
  });
271
367
  MockWebSocket.inst.onmessage(messageEvent);
272
368
 
273
369
  expect(MockWebSocket.inst.close).toHaveBeenCalled();
274
370
  expect(LoggerProxy.error).toHaveBeenCalledWith(
275
371
  '[WebSocketStatus] | event=agentMultiLogin | WebSocket connection closed by agent multiLogin',
276
- { module: WEB_SOCKET_MANAGER_FILE, method: 'connect' }
372
+ {module: WEB_SOCKET_MANAGER_FILE, method: 'connect'}
277
373
  );
278
374
  });
279
375
 
@@ -286,10 +382,10 @@ describe('WebSocketManager', () => {
286
382
 
287
383
  (mockWebex.request as jest.Mock).mockResolvedValueOnce(subscribeResponse);
288
384
 
289
- await webSocketManager.initWebSocket({ body: fakeSubscribeRequest });
385
+ await webSocketManager.initWebSocket({body: fakeSubscribeRequest, resource: SUBSCRIBE_API});
290
386
 
291
387
  const messageEvent = new MessageEvent('message', {
292
- data: JSON.stringify({ type: 'Welcome', data: { someData: 'data' } }),
388
+ data: JSON.stringify({type: 'Welcome', data: {someData: 'data'}}),
293
389
  });
294
390
  MockWebSocket.inst.onmessage(messageEvent);
295
391
 
@@ -305,7 +401,7 @@ describe('WebSocketManager', () => {
305
401
 
306
402
  (mockWebex.request as jest.Mock).mockResolvedValueOnce(subscribeResponse);
307
403
 
308
- await webSocketManager.initWebSocket({ body: fakeSubscribeRequest });
404
+ await webSocketManager.initWebSocket({body: fakeSubscribeRequest, resource: SUBSCRIBE_API});
309
405
 
310
406
  webSocketManager['forceCloseWebSocketOnTimeout'] = true;
311
407
 
@@ -324,10 +420,10 @@ describe('WebSocketManager', () => {
324
420
  // Wait for the close event to be handled
325
421
  await new Promise((resolve) => setTimeout(resolve, 10));
326
422
 
327
- expect(mockWorker.postMessage).toHaveBeenCalledWith({ type: 'terminate' });
423
+ expect(mockWorker.postMessage).toHaveBeenCalledWith({type: 'terminate'});
328
424
  expect(LoggerProxy.error).toHaveBeenCalledWith(
329
425
  '[WebSocketStatus] | event=webSocketClose | WebSocket connection closed REASON: WebSocket auto close timed out. Forcefully closed websocket.',
330
- { module: WEB_SOCKET_MANAGER_FILE, method: 'webSocketOnCloseHandler' }
426
+ {module: WEB_SOCKET_MANAGER_FILE, method: 'webSocketOnCloseHandler'}
331
427
  );
332
428
  });
333
429
 
@@ -340,7 +436,7 @@ describe('WebSocketManager', () => {
340
436
 
341
437
  (mockWebex.request as jest.Mock).mockResolvedValueOnce(subscribeResponse);
342
438
 
343
- await webSocketManager.initWebSocket({ body: fakeSubscribeRequest });
439
+ await webSocketManager.initWebSocket({body: fakeSubscribeRequest, resource: SUBSCRIBE_API});
344
440
  webSocketManager.shouldReconnect = false;
345
441
  // Simulate the WebSocket close event
346
442
  setTimeout(() => {
@@ -351,13 +447,13 @@ describe('WebSocketManager', () => {
351
447
  target: MockWebSocket.inst,
352
448
  });
353
449
  }, 1);
354
-
450
+
355
451
  await new Promise((resolve) => setTimeout(resolve, 10));
356
452
 
357
- expect(mockWorker.postMessage).toHaveBeenCalledWith({ type: 'terminate' });
453
+ expect(mockWorker.postMessage).toHaveBeenCalledWith({type: 'terminate'});
358
454
  expect(LoggerProxy.error).not.toHaveBeenCalledWith(
359
455
  '[WebSocketStatus] | event=webSocketClose | WebSocket connection closed REASON: no reconnect',
360
- { module: WEB_SOCKET_MANAGER_FILE, method: 'webSocketOnCloseHandler' }
456
+ {module: WEB_SOCKET_MANAGER_FILE, method: 'webSocketOnCloseHandler'}
361
457
  );
362
458
  });
363
459
 
@@ -370,7 +466,7 @@ describe('WebSocketManager', () => {
370
466
 
371
467
  (mockWebex.request as jest.Mock).mockResolvedValueOnce(subscribeResponse);
372
468
 
373
- await webSocketManager.initWebSocket({ body: fakeSubscribeRequest });
469
+ await webSocketManager.initWebSocket({body: fakeSubscribeRequest, resource: SUBSCRIBE_API});
374
470
 
375
471
  // Simulate the WebSocket close event
376
472
  setTimeout(() => {
@@ -385,10 +481,10 @@ describe('WebSocketManager', () => {
385
481
  // Wait for the close event to be handled
386
482
  await new Promise((resolve) => setTimeout(resolve, 10));
387
483
 
388
- expect(mockWorker.postMessage).toHaveBeenCalledWith({ type: 'terminate' });
484
+ expect(mockWorker.postMessage).toHaveBeenCalledWith({type: 'terminate'});
389
485
  expect(LoggerProxy.error).not.toHaveBeenCalledWith(
390
486
  '[WebSocketStatus] | event=webSocketClose | WebSocket connection closed REASON: clean close',
391
- { module: WEB_SOCKET_MANAGER_FILE, method: 'webSocketOnCloseHandler' }
487
+ {module: WEB_SOCKET_MANAGER_FILE, method: 'webSocketOnCloseHandler'}
392
488
  );
393
489
  });
394
- });
490
+ });
@@ -4,6 +4,8 @@ import {SubscribeRequest} from '../../../../../../src/types';
4
4
  import LoggerProxy from '../../../../../../src/logger-proxy';
5
5
  import {CONNECTIVITY_CHECK_INTERVAL} from '../../../../../../src/services/core/constants';
6
6
  import { CONNECTION_SERVICE_FILE } from '../../../../../../src/constants';
7
+ import {SUBSCRIBE_API} from '../../../../../../src/services/constants';
8
+
7
9
 
8
10
  jest.mock('../../../../../../src/services/core/websocket/WebSocketManager');
9
11
  jest.mock('../../../../../../src/logger-proxy', () => ({
@@ -109,7 +111,7 @@ describe('ConnectionService', () => {
109
111
  'event=socketConnectionRetry | Trying to reconnect to websocket',
110
112
  {module: CONNECTION_SERVICE_FILE, method: 'handleSocketClose'}
111
113
  );
112
- expect(mockWebSocketManager.initWebSocket).toHaveBeenCalledWith({body: mockSubscribeRequest});
114
+ expect(mockWebSocketManager.initWebSocket).toHaveBeenCalledWith({body: mockSubscribeRequest, resource: SUBSCRIBE_API});
113
115
  });
114
116
 
115
117
  describe('ConnectionService onPing', () => {
@@ -0,0 +1,63 @@
1
+ import AutoWrapup from '../../../../../src/services/task/AutoWrapup';
2
+
3
+ describe('AutoWrapup', () => {
4
+ beforeEach(() => {
5
+ jest.useFakeTimers();
6
+ });
7
+
8
+ afterEach(() => {
9
+ jest.useRealTimers();
10
+ });
11
+
12
+ it('starts, reports running, and completes', () => {
13
+ const onComplete = jest.fn();
14
+ const timer = new AutoWrapup(1000, true);
15
+
16
+ expect(timer.allowCancelAutoWrapup).toBe(true);
17
+ expect(timer.isRunning()).toBe(false);
18
+
19
+ timer.start(onComplete);
20
+ expect(timer.isRunning()).toBe(true);
21
+ expect(timer.getTimeLeft()).toBeGreaterThan(0);
22
+ expect(timer.getTimeLeftSeconds()).toBeGreaterThan(0);
23
+
24
+ jest.advanceTimersByTime(1000);
25
+ expect(onComplete).toHaveBeenCalledTimes(1);
26
+ expect(timer.isRunning()).toBe(false);
27
+ });
28
+
29
+ it('clear() is safe when not running and cancels an active timer', () => {
30
+ const onComplete = jest.fn();
31
+ const timer = new AutoWrapup(1000);
32
+
33
+ timer.clear(); // no-op
34
+ expect(timer.isRunning()).toBe(false);
35
+
36
+ timer.start(onComplete);
37
+ expect(timer.isRunning()).toBe(true);
38
+
39
+ timer.clear();
40
+ expect(timer.isRunning()).toBe(false);
41
+
42
+ jest.advanceTimersByTime(1000);
43
+ expect(onComplete).not.toHaveBeenCalled();
44
+ });
45
+
46
+ it('start() replaces an existing timer', () => {
47
+ const onComplete1 = jest.fn();
48
+ const onComplete2 = jest.fn();
49
+ const timer = new AutoWrapup(1000);
50
+
51
+ timer.start(onComplete1);
52
+ jest.advanceTimersByTime(500);
53
+
54
+ timer.start(onComplete2);
55
+ jest.advanceTimersByTime(500);
56
+ expect(onComplete1).not.toHaveBeenCalled();
57
+ expect(onComplete2).not.toHaveBeenCalled();
58
+
59
+ jest.advanceTimersByTime(500);
60
+ expect(onComplete2).toHaveBeenCalledTimes(1);
61
+ });
62
+ });
63
+