@webex/plugin-meetings 3.10.0 → 3.11.0-next.10

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 (299) hide show
  1. package/dist/annotation/annotation.types.js.map +1 -1
  2. package/dist/annotation/constants.js.map +1 -1
  3. package/dist/annotation/index.js +19 -22
  4. package/dist/annotation/index.js.map +1 -1
  5. package/dist/breakouts/breakout.js +6 -6
  6. package/dist/breakouts/breakout.js.map +1 -1
  7. package/dist/breakouts/collection.js.map +1 -1
  8. package/dist/breakouts/edit-lock-error.js +9 -11
  9. package/dist/breakouts/edit-lock-error.js.map +1 -1
  10. package/dist/breakouts/events.js.map +1 -1
  11. package/dist/breakouts/index.js +126 -127
  12. package/dist/breakouts/index.js.map +1 -1
  13. package/dist/breakouts/request.js +6 -8
  14. package/dist/breakouts/request.js.map +1 -1
  15. package/dist/breakouts/utils.js.map +1 -1
  16. package/dist/common/browser-detection.js.map +1 -1
  17. package/dist/common/collection.js +1 -2
  18. package/dist/common/collection.js.map +1 -1
  19. package/dist/common/config.js.map +1 -1
  20. package/dist/common/errors/captcha-error.js +9 -11
  21. package/dist/common/errors/captcha-error.js.map +1 -1
  22. package/dist/common/errors/intent-to-join.js +10 -12
  23. package/dist/common/errors/intent-to-join.js.map +1 -1
  24. package/dist/common/errors/join-forbidden-error.js +10 -12
  25. package/dist/common/errors/join-forbidden-error.js.map +1 -1
  26. package/dist/common/errors/join-meeting.js +10 -12
  27. package/dist/common/errors/join-meeting.js.map +1 -1
  28. package/dist/common/errors/join-webinar-error.js +9 -11
  29. package/dist/common/errors/join-webinar-error.js.map +1 -1
  30. package/dist/common/errors/media.js +9 -11
  31. package/dist/common/errors/media.js.map +1 -1
  32. package/dist/common/errors/multistream-not-supported-error.js +9 -11
  33. package/dist/common/errors/multistream-not-supported-error.js.map +1 -1
  34. package/dist/common/errors/no-meeting-info.js +9 -11
  35. package/dist/common/errors/no-meeting-info.js.map +1 -1
  36. package/dist/common/errors/parameter.js +11 -14
  37. package/dist/common/errors/parameter.js.map +1 -1
  38. package/dist/common/errors/password-error.js +9 -11
  39. package/dist/common/errors/password-error.js.map +1 -1
  40. package/dist/common/errors/permission.js +9 -11
  41. package/dist/common/errors/permission.js.map +1 -1
  42. package/dist/common/errors/reclaim-host-role-errors.js +32 -38
  43. package/dist/common/errors/reclaim-host-role-errors.js.map +1 -1
  44. package/dist/common/errors/reconnection-not-started.js +5 -6
  45. package/dist/common/errors/reconnection-not-started.js.map +1 -1
  46. package/dist/common/errors/reconnection.js +9 -11
  47. package/dist/common/errors/reconnection.js.map +1 -1
  48. package/dist/common/errors/stats.js +9 -11
  49. package/dist/common/errors/stats.js.map +1 -1
  50. package/dist/common/errors/webex-errors.js +38 -27
  51. package/dist/common/errors/webex-errors.js.map +1 -1
  52. package/dist/common/errors/webex-meetings-error.js +9 -12
  53. package/dist/common/errors/webex-meetings-error.js.map +1 -1
  54. package/dist/common/events/events-scope.js +9 -10
  55. package/dist/common/events/events-scope.js.map +1 -1
  56. package/dist/common/events/events.js +9 -10
  57. package/dist/common/events/events.js.map +1 -1
  58. package/dist/common/events/trigger-proxy.js.map +1 -1
  59. package/dist/common/events/util.js.map +1 -1
  60. package/dist/common/logs/logger-config.js.map +1 -1
  61. package/dist/common/logs/logger-proxy.js.map +1 -1
  62. package/dist/common/logs/request.js +17 -17
  63. package/dist/common/logs/request.js.map +1 -1
  64. package/dist/common/queue.js +1 -2
  65. package/dist/common/queue.js.map +1 -1
  66. package/dist/config.js +2 -2
  67. package/dist/config.js.map +1 -1
  68. package/dist/constants.js +14 -8
  69. package/dist/constants.js.map +1 -1
  70. package/dist/controls-options-manager/constants.js.map +1 -1
  71. package/dist/controls-options-manager/enums.js.map +1 -1
  72. package/dist/controls-options-manager/index.js +1 -2
  73. package/dist/controls-options-manager/index.js.map +1 -1
  74. package/dist/controls-options-manager/types.js.map +1 -1
  75. package/dist/controls-options-manager/util.js +1 -2
  76. package/dist/controls-options-manager/util.js.map +1 -1
  77. package/dist/hashTree/constants.js +20 -0
  78. package/dist/hashTree/constants.js.map +1 -0
  79. package/dist/hashTree/hashTree.js +515 -0
  80. package/dist/hashTree/hashTree.js.map +1 -0
  81. package/dist/hashTree/hashTreeParser.js +1250 -0
  82. package/dist/hashTree/hashTreeParser.js.map +1 -0
  83. package/dist/hashTree/types.js +23 -0
  84. package/dist/hashTree/types.js.map +1 -0
  85. package/dist/hashTree/utils.js +59 -0
  86. package/dist/hashTree/utils.js.map +1 -0
  87. package/dist/index.js +8 -2
  88. package/dist/index.js.map +1 -1
  89. package/dist/interceptors/index.js.map +1 -1
  90. package/dist/interceptors/locusRetry.js +6 -8
  91. package/dist/interceptors/locusRetry.js.map +1 -1
  92. package/dist/interceptors/locusRouteToken.js +33 -13
  93. package/dist/interceptors/locusRouteToken.js.map +1 -1
  94. package/dist/interpretation/collection.js.map +1 -1
  95. package/dist/interpretation/index.js +1 -2
  96. package/dist/interpretation/index.js.map +1 -1
  97. package/dist/interpretation/siLanguage.js +1 -1
  98. package/dist/interpretation/siLanguage.js.map +1 -1
  99. package/dist/locus-info/controlsUtils.js +5 -3
  100. package/dist/locus-info/controlsUtils.js.map +1 -1
  101. package/dist/locus-info/embeddedAppsUtils.js.map +1 -1
  102. package/dist/locus-info/fullState.js.map +1 -1
  103. package/dist/locus-info/hostUtils.js.map +1 -1
  104. package/dist/locus-info/index.js +619 -177
  105. package/dist/locus-info/index.js.map +1 -1
  106. package/dist/locus-info/infoUtils.js.map +1 -1
  107. package/dist/locus-info/mediaSharesUtils.js.map +1 -1
  108. package/dist/locus-info/parser.js +3 -4
  109. package/dist/locus-info/parser.js.map +1 -1
  110. package/dist/locus-info/selfUtils.js.map +1 -1
  111. package/dist/locus-info/types.js +7 -0
  112. package/dist/locus-info/types.js.map +1 -0
  113. package/dist/media/MediaConnectionAwaiter.js +58 -3
  114. package/dist/media/MediaConnectionAwaiter.js.map +1 -1
  115. package/dist/media/index.js +5 -2
  116. package/dist/media/index.js.map +1 -1
  117. package/dist/media/properties.js +19 -19
  118. package/dist/media/properties.js.map +1 -1
  119. package/dist/media/util.js.map +1 -1
  120. package/dist/meeting/brbState.js +8 -9
  121. package/dist/meeting/brbState.js.map +1 -1
  122. package/dist/meeting/connectionStateHandler.js +10 -13
  123. package/dist/meeting/connectionStateHandler.js.map +1 -1
  124. package/dist/meeting/in-meeting-actions.js.map +1 -1
  125. package/dist/meeting/index.js +1672 -1553
  126. package/dist/meeting/index.js.map +1 -1
  127. package/dist/meeting/locusMediaRequest.js +13 -17
  128. package/dist/meeting/locusMediaRequest.js.map +1 -1
  129. package/dist/meeting/muteState.js +11 -12
  130. package/dist/meeting/muteState.js.map +1 -1
  131. package/dist/meeting/request.js +101 -104
  132. package/dist/meeting/request.js.map +1 -1
  133. package/dist/meeting/request.type.js.map +1 -1
  134. package/dist/meeting/state.js.map +1 -1
  135. package/dist/meeting/type.js.map +1 -1
  136. package/dist/meeting/util.js +118 -25
  137. package/dist/meeting/util.js.map +1 -1
  138. package/dist/meeting/voicea-meeting.js +3 -3
  139. package/dist/meeting/voicea-meeting.js.map +1 -1
  140. package/dist/meeting-info/collection.js +7 -10
  141. package/dist/meeting-info/collection.js.map +1 -1
  142. package/dist/meeting-info/index.js +1 -2
  143. package/dist/meeting-info/index.js.map +1 -1
  144. package/dist/meeting-info/meeting-info-v2.js +135 -146
  145. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  146. package/dist/meeting-info/request.js +1 -2
  147. package/dist/meeting-info/request.js.map +1 -1
  148. package/dist/meeting-info/util.js +36 -37
  149. package/dist/meeting-info/util.js.map +1 -1
  150. package/dist/meeting-info/utilv2.js +30 -31
  151. package/dist/meeting-info/utilv2.js.map +1 -1
  152. package/dist/meetings/collection.js +6 -8
  153. package/dist/meetings/collection.js.map +1 -1
  154. package/dist/meetings/index.js +276 -174
  155. package/dist/meetings/index.js.map +1 -1
  156. package/dist/meetings/meetings.types.js.map +1 -1
  157. package/dist/meetings/request.js +6 -8
  158. package/dist/meetings/request.js.map +1 -1
  159. package/dist/meetings/util.js +36 -30
  160. package/dist/meetings/util.js.map +1 -1
  161. package/dist/member/index.js +1 -2
  162. package/dist/member/index.js.map +1 -1
  163. package/dist/member/types.js +6 -3
  164. package/dist/member/types.js.map +1 -1
  165. package/dist/member/util.js.map +1 -1
  166. package/dist/members/collection.js +1 -2
  167. package/dist/members/collection.js.map +1 -1
  168. package/dist/members/index.js +18 -21
  169. package/dist/members/index.js.map +1 -1
  170. package/dist/members/request.js +8 -11
  171. package/dist/members/request.js.map +1 -1
  172. package/dist/members/types.js.map +1 -1
  173. package/dist/members/util.js.map +1 -1
  174. package/dist/metrics/constants.js +5 -1
  175. package/dist/metrics/constants.js.map +1 -1
  176. package/dist/metrics/index.js +3 -4
  177. package/dist/metrics/index.js.map +1 -1
  178. package/dist/multistream/mediaRequestManager.js +1 -2
  179. package/dist/multistream/mediaRequestManager.js.map +1 -1
  180. package/dist/multistream/receiveSlot.js +34 -45
  181. package/dist/multistream/receiveSlot.js.map +1 -1
  182. package/dist/multistream/receiveSlotManager.js +8 -9
  183. package/dist/multistream/receiveSlotManager.js.map +1 -1
  184. package/dist/multistream/remoteMedia.js +12 -15
  185. package/dist/multistream/remoteMedia.js.map +1 -1
  186. package/dist/multistream/remoteMediaGroup.js +1 -2
  187. package/dist/multistream/remoteMediaGroup.js.map +1 -1
  188. package/dist/multistream/remoteMediaManager.js +122 -123
  189. package/dist/multistream/remoteMediaManager.js.map +1 -1
  190. package/dist/multistream/sendSlotManager.js +29 -30
  191. package/dist/multistream/sendSlotManager.js.map +1 -1
  192. package/dist/personal-meeting-room/index.js +16 -19
  193. package/dist/personal-meeting-room/index.js.map +1 -1
  194. package/dist/personal-meeting-room/request.js +7 -10
  195. package/dist/personal-meeting-room/request.js.map +1 -1
  196. package/dist/personal-meeting-room/util.js.map +1 -1
  197. package/dist/reachability/clusterReachability.js +188 -352
  198. package/dist/reachability/clusterReachability.js.map +1 -1
  199. package/dist/reachability/index.js +212 -204
  200. package/dist/reachability/index.js.map +1 -1
  201. package/dist/reachability/reachability.types.js +14 -1
  202. package/dist/reachability/reachability.types.js.map +1 -1
  203. package/dist/reachability/reachabilityPeerConnection.js +445 -0
  204. package/dist/reachability/reachabilityPeerConnection.js.map +1 -0
  205. package/dist/reachability/request.js.map +1 -1
  206. package/dist/reachability/util.js.map +1 -1
  207. package/dist/reactions/constants.js.map +1 -1
  208. package/dist/reactions/reactions.js.map +1 -1
  209. package/dist/reactions/reactions.type.js.map +1 -1
  210. package/dist/reconnection-manager/index.js +178 -176
  211. package/dist/reconnection-manager/index.js.map +1 -1
  212. package/dist/recording-controller/enums.js.map +1 -1
  213. package/dist/recording-controller/index.js +1 -2
  214. package/dist/recording-controller/index.js.map +1 -1
  215. package/dist/recording-controller/util.js.map +1 -1
  216. package/dist/roap/index.js +12 -15
  217. package/dist/roap/index.js.map +1 -1
  218. package/dist/roap/request.js +24 -26
  219. package/dist/roap/request.js.map +1 -1
  220. package/dist/roap/turnDiscovery.js +75 -76
  221. package/dist/roap/turnDiscovery.js.map +1 -1
  222. package/dist/roap/types.js.map +1 -1
  223. package/dist/transcription/index.js +4 -5
  224. package/dist/transcription/index.js.map +1 -1
  225. package/dist/types/common/errors/webex-errors.d.ts +12 -0
  226. package/dist/types/config.d.ts +1 -0
  227. package/dist/types/constants.d.ts +28 -21
  228. package/dist/types/hashTree/constants.d.ts +8 -0
  229. package/dist/types/hashTree/hashTree.d.ts +129 -0
  230. package/dist/types/hashTree/hashTreeParser.d.ts +250 -0
  231. package/dist/types/hashTree/types.d.ts +33 -0
  232. package/dist/types/hashTree/utils.d.ts +16 -0
  233. package/dist/types/index.d.ts +2 -1
  234. package/dist/types/interceptors/locusRouteToken.d.ts +2 -0
  235. package/dist/types/locus-info/index.d.ts +98 -80
  236. package/dist/types/locus-info/types.d.ts +54 -0
  237. package/dist/types/media/MediaConnectionAwaiter.d.ts +10 -1
  238. package/dist/types/media/properties.d.ts +2 -1
  239. package/dist/types/meeting/index.d.ts +40 -12
  240. package/dist/types/meeting/util.d.ts +25 -0
  241. package/dist/types/meetings/index.d.ts +12 -3
  242. package/dist/types/metrics/constants.d.ts +4 -0
  243. package/dist/types/reachability/clusterReachability.d.ts +33 -84
  244. package/dist/types/reachability/reachability.types.d.ts +12 -1
  245. package/dist/types/reachability/reachabilityPeerConnection.d.ts +111 -0
  246. package/dist/types/reactions/reactions.type.d.ts +1 -0
  247. package/dist/types/webinar/utils.d.ts +6 -0
  248. package/dist/webinar/collection.js +1 -2
  249. package/dist/webinar/collection.js.map +1 -1
  250. package/dist/webinar/index.js +206 -158
  251. package/dist/webinar/index.js.map +1 -1
  252. package/dist/webinar/utils.js +25 -0
  253. package/dist/webinar/utils.js.map +1 -0
  254. package/package.json +24 -23
  255. package/src/common/errors/webex-errors.ts +19 -0
  256. package/src/config.ts +1 -0
  257. package/src/constants.ts +17 -2
  258. package/src/hashTree/constants.ts +9 -0
  259. package/src/hashTree/hashTree.ts +463 -0
  260. package/src/hashTree/hashTreeParser.ts +1143 -0
  261. package/src/hashTree/types.ts +39 -0
  262. package/src/hashTree/utils.ts +53 -0
  263. package/src/index.ts +2 -0
  264. package/src/interceptors/locusRouteToken.ts +22 -5
  265. package/src/locus-info/controlsUtils.ts +6 -0
  266. package/src/locus-info/index.ts +641 -164
  267. package/src/locus-info/types.ts +53 -0
  268. package/src/media/MediaConnectionAwaiter.ts +41 -1
  269. package/src/media/index.ts +6 -0
  270. package/src/media/properties.ts +3 -1
  271. package/src/meeting/index.ts +173 -37
  272. package/src/meeting/util.ts +119 -1
  273. package/src/meetings/index.ts +212 -67
  274. package/src/meetings/util.ts +10 -9
  275. package/src/metrics/constants.ts +4 -0
  276. package/src/reachability/clusterReachability.ts +159 -330
  277. package/src/reachability/index.ts +15 -1
  278. package/src/reachability/reachability.types.ts +15 -1
  279. package/src/reachability/reachabilityPeerConnection.ts +418 -0
  280. package/src/reactions/reactions.type.ts +1 -0
  281. package/src/webinar/index.ts +44 -1
  282. package/src/webinar/utils.ts +16 -0
  283. package/test/unit/spec/hashTree/hashTree.ts +655 -0
  284. package/test/unit/spec/hashTree/hashTreeParser.ts +1524 -0
  285. package/test/unit/spec/hashTree/utils.ts +140 -0
  286. package/test/unit/spec/interceptors/locusRouteToken.ts +44 -0
  287. package/test/unit/spec/locus-info/controlsUtils.js +27 -1
  288. package/test/unit/spec/locus-info/index.js +879 -16
  289. package/test/unit/spec/media/MediaConnectionAwaiter.ts +41 -1
  290. package/test/unit/spec/media/index.ts +140 -9
  291. package/test/unit/spec/media/properties.ts +12 -3
  292. package/test/unit/spec/meeting/index.js +514 -130
  293. package/test/unit/spec/meeting/utils.js +341 -15
  294. package/test/unit/spec/meetings/index.js +822 -32
  295. package/test/unit/spec/meetings/utils.js +51 -1
  296. package/test/unit/spec/reachability/clusterReachability.ts +404 -137
  297. package/test/unit/spec/reachability/index.ts +26 -3
  298. package/test/unit/spec/webinar/index.ts +106 -0
  299. package/test/unit/spec/webinar/utils.ts +39 -0
@@ -46,6 +46,7 @@ import {
46
46
  MediaType,
47
47
  } from '@webex/internal-media-core';
48
48
  import {LocalStreamEventNames} from '@webex/media-helpers';
49
+ import {CapabilityState, WebCapabilities} from '@webex/web-capabilities';
49
50
  import EventsScope from '@webex/plugin-meetings/src/common/events/events-scope';
50
51
  import Meetings, {CONSTANTS} from '@webex/plugin-meetings';
51
52
  import Meeting from '@webex/plugin-meetings/src/meeting';
@@ -97,6 +98,7 @@ import PermissionError from '../../../../src/common/errors/permission';
97
98
  import JoinWebinarError from '../../../../src/common/errors/join-webinar-error';
98
99
  import IntentToJoinError from '../../../../src/common/errors/intent-to-join';
99
100
  import MultistreamNotSupportedError from '../../../../src/common/errors/multistream-not-supported-error';
101
+ import {SdpResponseTimeoutError} from '@webex/plugin-meetings/src/common/errors/webex-errors';
100
102
  import testUtils from '../../../utils/testUtils';
101
103
  import {
102
104
  MeetingInfoV2CaptchaError,
@@ -132,11 +134,15 @@ describe('plugin-meetings', () => {
132
134
  debug: () => {},
133
135
  };
134
136
 
137
+ let fakeClock;
138
+
135
139
  beforeEach(() => {
136
140
  sinon.stub(Metrics, 'sendBehavioralMetric');
141
+ fakeClock = sinon.useFakeTimers();
137
142
  });
138
143
  afterEach(() => {
139
144
  sinon.restore();
145
+ fakeClock.restore();
140
146
  });
141
147
 
142
148
  before(() => {
@@ -729,8 +735,11 @@ describe('plugin-meetings', () => {
729
735
  let handleTurnDiscoveryHttpResponseStub;
730
736
  let abortTurnDiscoveryStub;
731
737
  let addMediaInternalStub;
738
+ let supportsRTCPeerConnectionStub;
732
739
 
733
740
  beforeEach(() => {
741
+ supportsRTCPeerConnectionStub = sinon.stub(WebCapabilities, 'supportsRTCPeerConnection').returns(CapabilityState.CAPABLE);
742
+
734
743
  meeting.join = sinon.stub().callsFake((joinOptions) => {
735
744
  meeting.isMultistream = joinOptions.enableMultistream;
736
745
  return Promise.resolve(fakeJoinResult);
@@ -1002,6 +1011,35 @@ describe('plugin-meetings', () => {
1002
1011
  );
1003
1012
  });
1004
1013
 
1014
+ it('should call leave() if addMediaInternal() fails ', async () => {
1015
+ const addMediaError = new Error('fake addMedia error');
1016
+ addMediaError.name = 'TypeError';
1017
+
1018
+ const rejectError = {
1019
+ error: {
1020
+ body: {
1021
+ errorCode: 2729,
1022
+ message: 'fake addMedia error',
1023
+ name: 'TypeError'
1024
+ }
1025
+ }
1026
+ };
1027
+ meeting.addMediaInternal.rejects(addMediaError);
1028
+ sinon.stub(meeting, 'leave').resolves();
1029
+
1030
+ await assert.isRejected(
1031
+ meeting.joinWithMedia({
1032
+ joinOptions,
1033
+ mediaOptions,
1034
+ }),
1035
+ rejectError
1036
+ );
1037
+
1038
+ assert.calledOnce(meeting.join);
1039
+ assert.calledOnce(meeting.addMediaInternal);
1040
+ assert.calledOnce(Metrics.sendBehavioralMetric);
1041
+ });
1042
+
1005
1043
  it('should not call leave() if addMediaInternal() fails the first time and succeeds the second time and should only call join() once', async () => {
1006
1044
  const addMediaError = new Error('fake addMedia error');
1007
1045
  const leaveStub = sinon.stub(meeting, 'leave');
@@ -1210,44 +1248,49 @@ describe('plugin-meetings', () => {
1210
1248
  await assert.isRejected(result);
1211
1249
  });
1212
1250
 
1213
- it('should not attempt a retry if we fail to create the offer on first atttempt', async () => {
1214
- const addMediaError = new Error('fake addMedia error');
1215
- addMediaError.name = 'SdpOfferCreationError';
1251
+ [
1252
+ {errorName: 'SdpOfferCreationError', description: 'if we fail to create the offer on first attempt'},
1253
+ {errorName: 'WebrtcApiNotAvailableError', description: 'if RTCPeerConnection is not available'},
1254
+ ].forEach(({errorName, description}) => {
1255
+ it(`should not attempt a retry ${description}`, async () => {
1256
+ const addMediaError = new Error('fake addMedia error');
1257
+ addMediaError.name = errorName;
1216
1258
 
1217
- meeting.addMediaInternal.rejects(addMediaError);
1218
- sinon.stub(meeting, 'leave').resolves();
1259
+ meeting.addMediaInternal.rejects(addMediaError);
1260
+ sinon.stub(meeting, 'leave').resolves();
1219
1261
 
1220
- await assert.isRejected(
1221
- meeting.joinWithMedia({
1222
- joinOptions,
1223
- mediaOptions,
1224
- }),
1225
- addMediaError
1226
- );
1262
+ await assert.isRejected(
1263
+ meeting.joinWithMedia({
1264
+ joinOptions,
1265
+ mediaOptions,
1266
+ }),
1267
+ addMediaError
1268
+ );
1227
1269
 
1228
- // check that only 1 attempt was done
1229
- assert.calledOnce(meeting.join);
1230
- assert.calledOnce(meeting.addMediaInternal);
1231
- assert.calledOnce(Metrics.sendBehavioralMetric);
1232
- assert.calledWith(
1233
- Metrics.sendBehavioralMetric.firstCall,
1234
- BEHAVIORAL_METRICS.JOIN_WITH_MEDIA_FAILURE,
1235
- {
1236
- correlation_id: meeting.correlationId,
1237
- locus_id: meeting.locusUrl.split('/').pop(),
1238
- reason: addMediaError.message,
1239
- stack: addMediaError.stack,
1240
- leaveErrorReason: undefined,
1241
- isRetry: false,
1242
- },
1243
- {
1244
- type: addMediaError.name,
1245
- }
1246
- );
1247
- assert.calledOnceWithExactly(meeting.leave, {
1248
- resourceId: undefined,
1249
- reason: 'joinWithMedia failure',
1250
- });
1270
+ // check that only 1 attempt was done
1271
+ assert.calledOnce(meeting.join);
1272
+ assert.calledOnce(meeting.addMediaInternal);
1273
+ assert.calledOnce(Metrics.sendBehavioralMetric);
1274
+ assert.calledWith(
1275
+ Metrics.sendBehavioralMetric.firstCall,
1276
+ BEHAVIORAL_METRICS.JOIN_WITH_MEDIA_FAILURE,
1277
+ {
1278
+ correlation_id: meeting.correlationId,
1279
+ locus_id: meeting.locusUrl.split('/').pop(),
1280
+ reason: addMediaError.message,
1281
+ stack: addMediaError.stack,
1282
+ leaveErrorReason: undefined,
1283
+ isRetry: false,
1284
+ },
1285
+ {
1286
+ type: addMediaError.name,
1287
+ }
1288
+ );
1289
+ assert.calledOnceWithExactly(meeting.leave, {
1290
+ resourceId: undefined,
1291
+ reason: 'joinWithMedia failure',
1292
+ });
1293
+ })
1251
1294
  });
1252
1295
 
1253
1296
  it('should ignore sendVideo/receiveVideo when videoEnabled is false', async () => {
@@ -1315,6 +1358,21 @@ describe('plugin-meetings', () => {
1315
1358
  })
1316
1359
  );
1317
1360
  });
1361
+
1362
+ it('should throw immediately if RTCPeerConnection is not available', async () => {
1363
+ supportsRTCPeerConnectionStub.returns(CapabilityState.NOT_CAPABLE);
1364
+
1365
+ await assert.isRejected(
1366
+ meeting.joinWithMedia({
1367
+ joinOptions,
1368
+ mediaOptions,
1369
+ }),
1370
+ Errors.WebrtcApiNotAvailableError
1371
+ );
1372
+
1373
+ assert.notCalled(meeting.join);
1374
+ assert.notCalled(meeting.addMediaInternal);
1375
+ });
1318
1376
  });
1319
1377
  describe('#isTranscriptionSupported', () => {
1320
1378
  it('should return false if the feature is not supported for the meeting', () => {
@@ -1329,6 +1387,25 @@ describe('plugin-meetings', () => {
1329
1387
  });
1330
1388
  });
1331
1389
 
1390
+ describe('#update hesiod llm id', () => {
1391
+ beforeEach(() => {
1392
+ webex.internal.voicea.onCaptionServiceIdUpdate = sinon.stub();
1393
+ });
1394
+ afterEach(() => {
1395
+ // Restore the original methods after each test
1396
+ sinon.restore();
1397
+ });
1398
+ it('should call voicea.onCaptionServiceIdUpdate when joined', async () => {
1399
+ meeting.joinedWith = {state: 'JOINED'};
1400
+ await meeting.locusInfo.emitScoped(
1401
+ {function: 'test', file: 'test'},
1402
+ LOCUSINFO.EVENTS.CONTROLS_MEETING_HESIOD_LLM_ID_UPDATED,
1403
+ {hesiodLlmId: '123a-456b-789c'}
1404
+ );
1405
+ assert.calledWith(webex.internal.voicea.onCaptionServiceIdUpdate, '123a-456b-789c');
1406
+ });
1407
+ });
1408
+
1332
1409
  describe('#update spoken language', () => {
1333
1410
  beforeEach(() => {
1334
1411
  webex.internal.voicea.onSpokenLanguageUpdate = sinon.stub();
@@ -1805,6 +1882,53 @@ describe('plugin-meetings', () => {
1805
1882
  fakeProcessedReaction
1806
1883
  );
1807
1884
  });
1885
+
1886
+ it('should process if participantId does not exist in membersCollection but has displayName in Webinar', () => {
1887
+ LoggerProxy.logger.warn = sinon.stub();
1888
+ meeting.isReactionsSupported = sinon.stub().returns(true);
1889
+ meeting.config.receiveReactions = true;
1890
+ meeting.locusInfo.info = {isWebinar: true};
1891
+ const fakeSendersName = 'Fake reactors name';
1892
+ const fakeReactionPayload = {
1893
+ type: 'fake_type',
1894
+ codepoints: 'fake_codepoints',
1895
+ shortcodes: 'fake_shortcodes',
1896
+ tone: {
1897
+ type: 'fake_tone_type',
1898
+ codepoints: 'fake_tone_codepoints',
1899
+ shortcodes: 'fake_tone_shortcodes',
1900
+ },
1901
+ };
1902
+ const fakeSenderPayload = {
1903
+ displayName: 'Fake reactors name',
1904
+ participantId: 'fake_participant_id',
1905
+ };
1906
+ const fakeProcessedReaction = {
1907
+ reaction: fakeReactionPayload,
1908
+ sender: {
1909
+ id: fakeSenderPayload.participantId,
1910
+ name: fakeSendersName,
1911
+ },
1912
+ };
1913
+ const fakeRelayEvent = {
1914
+ data: {
1915
+ relayType: REACTION_RELAY_TYPES.REACTION,
1916
+ reaction: fakeReactionPayload,
1917
+ sender: fakeSenderPayload,
1918
+ },
1919
+ };
1920
+ meeting.processRelayEvent(fakeRelayEvent);
1921
+ assert.calledWith(
1922
+ TriggerProxy.trigger,
1923
+ sinon.match.instanceOf(Meeting),
1924
+ {
1925
+ file: 'meeting/index',
1926
+ function: 'join',
1927
+ },
1928
+ EVENT_TRIGGERS.MEETING_RECEIVE_REACTIONS,
1929
+ fakeProcessedReaction
1930
+ );
1931
+ });
1808
1932
  });
1809
1933
 
1810
1934
  describe('#handleLLMOnline', () => {
@@ -1848,7 +1972,7 @@ describe('plugin-meetings', () => {
1848
1972
  setCorrelationIdSpy = sinon.spy(meeting, 'setCorrelationId');
1849
1973
  meeting.setLocus = sinon.stub().returns(true);
1850
1974
  webex.meetings.registered = true;
1851
- meeting.updateLLMConnection = sinon.stub().returns(Promise.resolve());
1975
+ sinon.stub(meeting, 'updateLLMConnection').returns(Promise.resolve());
1852
1976
  });
1853
1977
 
1854
1978
  describe('successful', () => {
@@ -1999,18 +2123,15 @@ describe('plugin-meetings', () => {
1999
2123
 
2000
2124
  // Assert that client.locus.join.response error event is not sent from this function, it is now emitted from MeetingUtil.joinMeeting
2001
2125
  assert.calledOnce(webex.internal.newMetrics.submitClientEvent);
2002
- assert.calledWithMatch(
2003
- webex.internal.newMetrics.submitClientEvent,
2004
- {
2005
- name: 'client.call.initiated',
2006
- payload: {
2007
- trigger: 'user-interaction',
2008
- isRoapCallEnabled: true,
2009
- pstnAudioType: undefined
2010
- },
2011
- options: {meetingId: meeting.id},
2012
- }
2013
- );
2126
+ assert.calledWithMatch(webex.internal.newMetrics.submitClientEvent, {
2127
+ name: 'client.call.initiated',
2128
+ payload: {
2129
+ trigger: 'user-interaction',
2130
+ isRoapCallEnabled: true,
2131
+ pstnAudioType: undefined,
2132
+ },
2133
+ options: {meetingId: meeting.id},
2134
+ });
2014
2135
  });
2015
2136
  });
2016
2137
  it('should fail if password is required', async () => {
@@ -2042,7 +2163,7 @@ describe('plugin-meetings', () => {
2042
2163
  const defer = new Defer();
2043
2164
 
2044
2165
  meeting.config.enableAutomaticLLM = true;
2045
- meeting.updateLLMConnection = sinon.stub().returns(defer.promise);
2166
+ meeting.updateLLMConnection.returns(defer.promise);
2046
2167
 
2047
2168
  const result = await meeting.join();
2048
2169
 
@@ -2053,7 +2174,7 @@ describe('plugin-meetings', () => {
2053
2174
 
2054
2175
  it('should call updateLLMConnection as part of joining if config value is set', async () => {
2055
2176
  meeting.config.enableAutomaticLLM = true;
2056
- meeting.updateLLMConnection = sinon.stub().resolves();
2177
+ meeting.updateLLMConnection.resolves();
2057
2178
 
2058
2179
  await meeting.join();
2059
2180
 
@@ -2061,7 +2182,7 @@ describe('plugin-meetings', () => {
2061
2182
  });
2062
2183
 
2063
2184
  it('should not call updateLLMConnection as part of joining if config value is not set', async () => {
2064
- meeting.updateLLMConnection = sinon.stub().resolves();
2185
+ meeting.updateLLMConnection.resolves();
2065
2186
  await meeting.join();
2066
2187
 
2067
2188
  assert.notCalled(meeting.updateLLMConnection);
@@ -2071,7 +2192,7 @@ describe('plugin-meetings', () => {
2071
2192
  const defer = new Defer();
2072
2193
 
2073
2194
  meeting.config.enableAutomaticLLM = true;
2074
- meeting.updateLLMConnection = sinon.stub().returns(defer.promise);
2195
+ meeting.updateLLMConnection.returns(defer.promise);
2075
2196
 
2076
2197
  const result = await meeting.join();
2077
2198
 
@@ -2097,6 +2218,111 @@ describe('plugin-meetings', () => {
2097
2218
  ]);
2098
2219
  }
2099
2220
  });
2221
+
2222
+ it('handles Locus LLM events', async () => {
2223
+ const locusInfoParseStub = sinon.stub(meeting.locusInfo, 'parse');
2224
+ sinon.stub(meeting, 'isJoined').returns(true);
2225
+
2226
+ // Set up llm.on stub to capture the registered listener when updateLLMConnection is called
2227
+ let locusLLMEventListener;
2228
+ meeting.webex.internal.llm.on = sinon.stub().callsFake((eventName, callback) => {
2229
+ if (eventName === 'event:locus.state_message') {
2230
+ locusLLMEventListener = callback;
2231
+ }
2232
+ });
2233
+ meeting.webex.internal.llm.off = sinon.stub();
2234
+
2235
+ // we need the real meeting.updateLLMConnection not the mock
2236
+ meeting.updateLLMConnection.restore();
2237
+
2238
+ // Call updateLLMConnection to register the listener
2239
+ await meeting.updateLLMConnection();
2240
+
2241
+ // Verify the listener was registered and we captured it
2242
+ assert.isDefined(locusLLMEventListener, 'LLM event listener should be registered');
2243
+
2244
+ // Now trigger the event
2245
+ const eventData = {
2246
+ eventType: 'locus.state_message',
2247
+ stateElementsMessage: {
2248
+ header: {messageId: 'msg-1'},
2249
+ elements: [],
2250
+ },
2251
+ };
2252
+
2253
+ locusLLMEventListener({data: eventData});
2254
+
2255
+ assert.calledOnceWithExactly(locusInfoParseStub, meeting, eventData);
2256
+ });
2257
+
2258
+ it('UpdateLLMConnection sends a metric if not connected after timeout', async () => {
2259
+ sinon.stub(meeting, 'isJoined').returns(true);
2260
+ sinon.stub(meeting.webex.internal.llm, 'isConnected').returns(false);
2261
+ sinon.stub(meeting.webex.internal.llm, 'hasEverConnected').value(true);
2262
+ sinon.stub(meeting.webex.internal.llm, 'registerAndConnect').resolves({});
2263
+
2264
+ // Restore the real updateLLMConnection
2265
+ meeting.updateLLMConnection.restore();
2266
+
2267
+ // Call updateLLMConnection to start the timer
2268
+ await meeting.updateLLMConnection();
2269
+
2270
+ // Fast forward time by 3 minutes
2271
+ fakeClock.tick(3 * 60 * 1000);
2272
+
2273
+ assert.calledWith(
2274
+ Metrics.sendBehavioralMetric,
2275
+ BEHAVIORAL_METRICS.LLM_HEALTHCHECK_FAILURE,
2276
+ {
2277
+ correlation_id: meeting.correlationId,
2278
+ hasEverConnected: true,
2279
+ }
2280
+ );
2281
+ });
2282
+
2283
+ it('clears the LLM health check timer when disconnecting LLM', async () => {
2284
+ const isJoinedStub = sinon.stub(meeting, 'isJoined');
2285
+ sinon.stub(meeting.webex.internal.llm, 'isConnected');
2286
+ sinon.stub(meeting.webex.internal.llm, 'disconnectLLM').resolves();
2287
+ sinon.stub(meeting.webex.internal.llm, 'registerAndConnect').resolves({});
2288
+ sinon
2289
+ .stub(meeting.webex.internal.llm, 'getLocusUrl')
2290
+ .returns('https://locus1.example.com');
2291
+ sinon
2292
+ .stub(meeting.webex.internal.llm, 'getDatachannelUrl')
2293
+ .returns('https://datachannel1.example.com');
2294
+
2295
+ // Restore the real updateLLMConnection
2296
+ meeting.updateLLMConnection.restore();
2297
+
2298
+ // First, connect LLM and start the timer
2299
+ isJoinedStub.returns(true);
2300
+ meeting.webex.internal.llm.isConnected.returns(false);
2301
+ await meeting.updateLLMConnection();
2302
+
2303
+ // Verify timer was started
2304
+ assert.exists(meeting.llmHealthCheckTimer);
2305
+
2306
+ // Now simulate that we're no longer joined
2307
+ isJoinedStub.returns(false);
2308
+ meeting.webex.internal.llm.isConnected.returns(true);
2309
+
2310
+ await meeting.updateLLMConnection();
2311
+
2312
+ assert.calledOnce(meeting.webex.internal.llm.disconnectLLM);
2313
+
2314
+ // Verify the timer was cleared (should be undefined)
2315
+ assert.isUndefined(meeting.llmHealthCheckTimer);
2316
+
2317
+ // Fast forward time to ensure no metric is sent
2318
+ Metrics.sendBehavioralMetric.resetHistory();
2319
+ fakeClock.tick(3 * 60 * 1000);
2320
+
2321
+ assert.neverCalledWith(
2322
+ Metrics.sendBehavioralMetric,
2323
+ BEHAVIORAL_METRICS.LLM_HEALTHCHECK_FAILURE
2324
+ );
2325
+ });
2100
2326
  });
2101
2327
 
2102
2328
  describe('refreshPermissionToken', () => {
@@ -2679,7 +2905,11 @@ describe('plugin-meetings', () => {
2679
2905
  // simulate timeout waiting for the SDP answer that never comes
2680
2906
  await clock.tickAsync(ROAP_OFFER_ANSWER_EXCHANGE_TIMEOUT);
2681
2907
 
2682
- await assert.isRejected(result);
2908
+ await assert.isRejected(
2909
+ result,
2910
+ SdpResponseTimeoutError,
2911
+ 'Timed out waiting for REMOTE SDP ANSWER'
2912
+ );
2683
2913
 
2684
2914
  assert.calledOnceWithExactly(getErrorPayloadForClientErrorCodeStub, {
2685
2915
  clientErrorCode: 2007,
@@ -2845,6 +3075,111 @@ describe('plugin-meetings', () => {
2845
3075
  checkWorking({allowMediaInLobby: true});
2846
3076
  });
2847
3077
 
3078
+ const setupLobbyTest = () => {
3079
+ meeting.roap.doTurnDiscovery = sinon
3080
+ .stub()
3081
+ .resolves({turnServerInfo: undefined, turnDiscoverySkippedReason: undefined});
3082
+
3083
+ meeting.meetingState = 'ACTIVE';
3084
+ meeting.locusInfo.parsedLocus = {self: {state: 'IDLE'}};
3085
+ meeting.isUserUnadmitted = true;
3086
+
3087
+ // Mock locusMediaRequest
3088
+ meeting.locusMediaRequest = {
3089
+ send: sinon.stub().resolves(),
3090
+ isConfluenceCreated: sinon.stub().returns(false),
3091
+ };
3092
+
3093
+ sinon.stub(RemoteMediaManagerModule, 'RemoteMediaManager').returns({
3094
+ start: sinon.stub().resolves(),
3095
+ on: sinon.stub(),
3096
+ logAllReceiveSlots: sinon.stub(),
3097
+ });
3098
+
3099
+ meeting.isMultistream = true;
3100
+
3101
+ const createFakeStream = (id) => ({
3102
+ on: sinon.stub(),
3103
+ off: sinon.stub(),
3104
+ userMuted: false,
3105
+ systemMuted: false,
3106
+ get muted() {
3107
+ return this.userMuted || this.systemMuted;
3108
+ },
3109
+ setUnmuteAllowed: sinon.stub(),
3110
+ setUserMuted: sinon.stub(),
3111
+ outputStream: {
3112
+ getTracks: () => [{id}],
3113
+ },
3114
+ getSettings: sinon.stub().returns({}),
3115
+ });
3116
+
3117
+ return {
3118
+ fakeMicrophoneStream: createFakeStream('fake mic'),
3119
+ fakeCameraStream: createFakeStream('fake camera'),
3120
+ };
3121
+ };
3122
+
3123
+ it('should not publish any local streams when in the lobby and allowPublishMediaInLobby is false', async () => {
3124
+ const {fakeMicrophoneStream, fakeCameraStream} = setupLobbyTest();
3125
+
3126
+ const publishStreamStub = sinon.stub();
3127
+ fakeMediaConnection.createSendSlot = sinon.stub().returns({
3128
+ publishStream: publishStreamStub,
3129
+ unpublishStream: sinon.stub(),
3130
+ setNamedMediaGroups: sinon.stub(),
3131
+ });
3132
+
3133
+ await meeting.addMedia({
3134
+ allowMediaInLobby: true,
3135
+ allowPublishMediaInLobby: false,
3136
+ audioEnabled: true,
3137
+ videoEnabled: true,
3138
+ localStreams: {
3139
+ microphone: fakeMicrophoneStream,
3140
+ camera: fakeCameraStream,
3141
+ },
3142
+ });
3143
+
3144
+ assert.notCalled(publishStreamStub);
3145
+ });
3146
+
3147
+ it('should publish local streams when in the lobby and allowPublishMediaInLobby is true', async () => {
3148
+ const {fakeMicrophoneStream, fakeCameraStream} = setupLobbyTest();
3149
+
3150
+ const audioSlot = {
3151
+ publishStream: sinon.stub(),
3152
+ unpublishStream: sinon.stub(),
3153
+ setNamedMediaGroups: sinon.stub(),
3154
+ };
3155
+ const videoSlot = {
3156
+ publishStream: sinon.stub(),
3157
+ unpublishStream: sinon.stub(),
3158
+ setNamedMediaGroups: sinon.stub(),
3159
+ };
3160
+
3161
+ fakeMediaConnection.createSendSlot = sinon.stub().callsFake((mediaType) => {
3162
+ if (mediaType === 'AUDIO-MAIN') {
3163
+ return audioSlot;
3164
+ }
3165
+ return videoSlot;
3166
+ });
3167
+
3168
+ await meeting.addMedia({
3169
+ allowMediaInLobby: true,
3170
+ allowPublishMediaInLobby: true,
3171
+ audioEnabled: true,
3172
+ videoEnabled: true,
3173
+ localStreams: {
3174
+ microphone: fakeMicrophoneStream,
3175
+ camera: fakeCameraStream,
3176
+ },
3177
+ });
3178
+
3179
+ assert.calledOnceWithExactly(audioSlot.publishStream, fakeMicrophoneStream);
3180
+ assert.calledOnceWithExactly(videoSlot.publishStream, fakeCameraStream);
3181
+ });
3182
+
2848
3183
  it('should create rtcMetrics and pass them to Media.createMediaConnection()', async () => {
2849
3184
  const setIntervalOriginal = window.setInterval;
2850
3185
  window.setInterval = sinon.stub().returns(1);
@@ -4087,7 +4422,7 @@ describe('plugin-meetings', () => {
4087
4422
  member2: {isInMeeting: false, isInLobby: true},
4088
4423
  member3: {isInMeeting: false, isInLobby: false},
4089
4424
  member4: {isInMeeting: true, isInLobby: false},
4090
- }
4425
+ },
4091
4426
  };
4092
4427
  sinon.stub(meeting, 'getMembers').returns({membersCollection: fakeMembersCollection});
4093
4428
  const fakeData = {intervalMetadata: {}};
@@ -4747,6 +5082,7 @@ describe('plugin-meetings', () => {
4747
5082
  id: 'fake locus from mocked join request',
4748
5083
  locusUrl: 'fake locus url',
4749
5084
  mediaId: 'fake media id',
5085
+ locus: {fullState: {}},
4750
5086
  });
4751
5087
  sinon.stub(meeting.meetingRequest, 'joinMeeting').resolves({
4752
5088
  headers: {
@@ -6739,7 +7075,7 @@ describe('plugin-meetings', () => {
6739
7075
  // Verify pstnCorrelationId was set
6740
7076
  assert.exists(meeting.pstnCorrelationId);
6741
7077
  assert.notEqual(meeting.pstnCorrelationId, meeting.correlationId);
6742
- const firstPstnCorrelationId = meeting.pstnCorrelationId
7078
+ const firstPstnCorrelationId = meeting.pstnCorrelationId;
6743
7079
 
6744
7080
  meeting.meetingRequest.dialIn.resetHistory();
6745
7081
 
@@ -6814,15 +7150,19 @@ describe('plugin-meetings', () => {
6814
7150
  assert.equal(e, error);
6815
7151
 
6816
7152
  // Verify behavioral metric was sent with dial_in_correlation_id
6817
- assert.calledWith(Metrics.sendBehavioralMetric, BEHAVIORAL_METRICS.ADD_DIAL_IN_FAILURE, {
6818
- correlation_id: meeting.correlationId,
6819
- dial_in_url: meeting.dialInUrl,
6820
- dial_in_correlation_id: sinon.match.string,
6821
- locus_id: meeting.locusUrl.split('/').pop(),
6822
- client_url: meeting.deviceUrl,
6823
- reason: error.error.message,
6824
- stack: error.stack,
6825
- });
7153
+ assert.calledWith(
7154
+ Metrics.sendBehavioralMetric,
7155
+ BEHAVIORAL_METRICS.ADD_DIAL_IN_FAILURE,
7156
+ {
7157
+ correlation_id: meeting.correlationId,
7158
+ dial_in_url: meeting.dialInUrl,
7159
+ dial_in_correlation_id: sinon.match.string,
7160
+ locus_id: meeting.locusUrl.split('/').pop(),
7161
+ client_url: meeting.deviceUrl,
7162
+ reason: error.error.message,
7163
+ stack: error.stack,
7164
+ }
7165
+ );
6826
7166
 
6827
7167
  // Verify pstnCorrelationId was cleared after error
6828
7168
  assert.equal(meeting.pstnCorrelationId, undefined);
@@ -6841,15 +7181,19 @@ describe('plugin-meetings', () => {
6841
7181
  assert.equal(e, error);
6842
7182
 
6843
7183
  // Verify behavioral metric was sent with dial_out_correlation_id
6844
- assert.calledWith(Metrics.sendBehavioralMetric, BEHAVIORAL_METRICS.ADD_DIAL_OUT_FAILURE, {
6845
- correlation_id: meeting.correlationId,
6846
- dial_out_url: meeting.dialOutUrl,
6847
- dial_out_correlation_id: sinon.match.string,
6848
- locus_id: meeting.locusUrl.split('/').pop(),
6849
- client_url: meeting.deviceUrl,
6850
- reason: error.error.message,
6851
- stack: error.stack,
6852
- });
7184
+ assert.calledWith(
7185
+ Metrics.sendBehavioralMetric,
7186
+ BEHAVIORAL_METRICS.ADD_DIAL_OUT_FAILURE,
7187
+ {
7188
+ correlation_id: meeting.correlationId,
7189
+ dial_out_url: meeting.dialOutUrl,
7190
+ dial_out_correlation_id: sinon.match.string,
7191
+ locus_id: meeting.locusUrl.split('/').pop(),
7192
+ client_url: meeting.deviceUrl,
7193
+ reason: error.error.message,
7194
+ stack: error.stack,
7195
+ }
7196
+ );
6853
7197
 
6854
7198
  // Verify pstnCorrelationId was cleared after error
6855
7199
  assert.equal(meeting.pstnCorrelationId, undefined);
@@ -6894,7 +7238,7 @@ describe('plugin-meetings', () => {
6894
7238
 
6895
7239
  // Verify that pstnCorrelationId is still cleared even when no phone connection is active
6896
7240
  assert.equal(meeting.pstnCorrelationId, undefined);
6897
- // And verify no disconnect was attempted
7241
+ // And verify no disconnect was attempted
6898
7242
  assert.notCalled(MeetingUtil.disconnectPhoneAudio);
6899
7243
  });
6900
7244
  });
@@ -8957,7 +9301,10 @@ describe('plugin-meetings', () => {
8957
9301
 
8958
9302
  // check that the right things were called by the callback
8959
9303
  assert.calledOnceWithExactly(meeting.waitForRemoteSDPAnswer);
8960
- assert.calledOnceWithExactly(meeting.mediaProperties.waitForMediaConnectionConnected);
9304
+ assert.calledOnceWithExactly(
9305
+ meeting.mediaProperties.waitForMediaConnectionConnected,
9306
+ meeting.correlationId
9307
+ );
8961
9308
  });
8962
9309
  });
8963
9310
 
@@ -10568,7 +10915,7 @@ describe('plugin-meetings', () => {
10568
10915
  describe('#setUpLocusUrlListener', () => {
10569
10916
  it('listens to the locus url update event', (done) => {
10570
10917
  const newLocusUrl = 'newLocusUrl/12345';
10571
- const payload = {url: newLocusUrl}
10918
+ const payload = {url: newLocusUrl};
10572
10919
 
10573
10920
  meeting.members = {locusUrlUpdate: sinon.stub().returns(Promise.resolve(test1))};
10574
10921
  meeting.recordingController = {setLocusUrl: sinon.stub().returns(undefined)};
@@ -10611,7 +10958,7 @@ describe('plugin-meetings', () => {
10611
10958
  });
10612
10959
  it('update mainLocusUrl for controlsOptionManager if payload.isMainLocus as true', (done) => {
10613
10960
  const newLocusUrl = 'newLocusUrl/12345';
10614
- const payload = {url: newLocusUrl, isMainLocus: true}
10961
+ const payload = {url: newLocusUrl, isMainLocus: true};
10615
10962
 
10616
10963
  meeting.controlsOptionsManager = {setLocusUrl: sinon.stub().returns(undefined)};
10617
10964
 
@@ -10843,7 +11190,9 @@ describe('plugin-meetings', () => {
10843
11190
  meeting.meetingRequest.changeMeetingFloor = sinon.stub().returns(Promise.resolve());
10844
11191
  (meeting.deviceUrl = 'deviceUrl.com'), (meeting.localShareInstanceId = '1234-5678');
10845
11192
  webex.internal.newMetrics.callDiagnosticLatencies.saveTimestamp = sinon.stub();
10846
- webex.internal.newMetrics.callDiagnosticLatencies.getShareDuration = sinon.stub().returns(1000);
11193
+ webex.internal.newMetrics.callDiagnosticLatencies.getShareDuration = sinon
11194
+ .stub()
11195
+ .returns(1000);
10847
11196
  });
10848
11197
  it('should call changeMeetingFloor()', async () => {
10849
11198
  meeting.screenShareFloorState = 'GRANTED';
@@ -11494,8 +11843,10 @@ describe('plugin-meetings', () => {
11494
11843
  canShareWhiteBoardSpy = sinon.spy(MeetingUtil, 'canShareWhiteBoard');
11495
11844
  canMoveToLobbySpy = sinon.spy(MeetingUtil, 'canMoveToLobby');
11496
11845
  showAutoEndMeetingWarningSpy = sinon.spy(MeetingUtil, 'showAutoEndMeetingWarning');
11497
- isSpokenLanguageAutoDetectionEnabledSpy = sinon.spy(MeetingUtil, 'isSpokenLanguageAutoDetectionEnabled');
11498
-
11846
+ isSpokenLanguageAutoDetectionEnabledSpy = sinon.spy(
11847
+ MeetingUtil,
11848
+ 'isSpokenLanguageAutoDetectionEnabled'
11849
+ );
11499
11850
  });
11500
11851
 
11501
11852
  afterEach(() => {
@@ -12277,16 +12628,26 @@ describe('plugin-meetings', () => {
12277
12628
  assert.notCalled(webex.internal.llm.disconnectLLM);
12278
12629
  assert.calledWith(webex.internal.llm.registerAndConnect, 'a url', 'a datachannel url');
12279
12630
  assert.equal(result, 'something');
12280
- assert.calledOnceWithExactly(
12631
+ assert.calledWithExactly(
12281
12632
  meeting.webex.internal.llm.off,
12282
12633
  'event:relay.event',
12283
12634
  meeting.processRelayEvent
12284
12635
  );
12285
- assert.calledOnceWithExactly(
12636
+ assert.calledWithExactly(
12637
+ meeting.webex.internal.llm.off,
12638
+ 'event:locus.state_message',
12639
+ meeting.processLocusLLMEvent
12640
+ );
12641
+ assert.calledWithExactly(
12286
12642
  meeting.webex.internal.llm.on,
12287
12643
  'event:relay.event',
12288
12644
  meeting.processRelayEvent
12289
12645
  );
12646
+ assert.calledWithExactly(
12647
+ meeting.webex.internal.llm.on,
12648
+ 'event:locus.state_message',
12649
+ meeting.processLocusLLMEvent
12650
+ );
12290
12651
  });
12291
12652
 
12292
12653
  it('disconnects if first if the locus url has changed', async () => {
@@ -12314,15 +12675,25 @@ describe('plugin-meetings', () => {
12314
12675
  'event:relay.event',
12315
12676
  meeting.processRelayEvent
12316
12677
  );
12317
- assert.calledTwice(meeting.webex.internal.llm.off);
12318
- assert.calledOnceWithExactly(
12678
+ assert.calledWithExactly(
12679
+ meeting.webex.internal.llm.off,
12680
+ 'event:locus.state_message',
12681
+ meeting.processLocusLLMEvent
12682
+ );
12683
+ assert.callCount(meeting.webex.internal.llm.off, 4);
12684
+ assert.calledWithExactly(
12319
12685
  meeting.webex.internal.llm.on,
12320
12686
  'event:relay.event',
12321
12687
  meeting.processRelayEvent
12322
12688
  );
12689
+ assert.calledWithExactly(
12690
+ meeting.webex.internal.llm.on,
12691
+ 'event:locus.state_message',
12692
+ meeting.processLocusLLMEvent
12693
+ );
12323
12694
  });
12324
12695
 
12325
- it('disconnects if first if the data channel url has changed', async () => {
12696
+ it('disconnects it first if the data channel url has changed', async () => {
12326
12697
  meeting.joinedWith = {state: 'JOINED'};
12327
12698
  webex.internal.llm.isConnected.returns(true);
12328
12699
  webex.internal.llm.getLocusUrl.returns('a url');
@@ -12347,12 +12718,21 @@ describe('plugin-meetings', () => {
12347
12718
  'event:relay.event',
12348
12719
  meeting.processRelayEvent
12349
12720
  );
12350
- assert.calledTwice(meeting.webex.internal.llm.off);
12351
- assert.calledOnceWithExactly(
12721
+ assert.calledWithExactly(
12722
+ meeting.webex.internal.llm.off,
12723
+ 'event:locus.state_message',
12724
+ meeting.processLocusLLMEvent
12725
+ );
12726
+ assert.calledWithExactly(
12352
12727
  meeting.webex.internal.llm.on,
12353
12728
  'event:relay.event',
12354
12729
  meeting.processRelayEvent
12355
12730
  );
12731
+ assert.calledWithExactly(
12732
+ meeting.webex.internal.llm.on,
12733
+ 'event:locus.state_message',
12734
+ meeting.processLocusLLMEvent
12735
+ );
12356
12736
  });
12357
12737
 
12358
12738
  it('disconnects when the state is not JOINED', async () => {
@@ -12367,11 +12747,16 @@ describe('plugin-meetings', () => {
12367
12747
  assert.calledWith(webex.internal.llm.disconnectLLM, undefined);
12368
12748
  assert.notCalled(webex.internal.llm.registerAndConnect);
12369
12749
  assert.equal(result, undefined);
12370
- assert.calledOnceWithExactly(
12750
+ assert.calledWithExactly(
12371
12751
  meeting.webex.internal.llm.off,
12372
12752
  'event:relay.event',
12373
12753
  meeting.processRelayEvent
12374
12754
  );
12755
+ assert.calledWithExactly(
12756
+ meeting.webex.internal.llm.off,
12757
+ 'event:locus.state_message',
12758
+ meeting.processLocusLLMEvent
12759
+ );
12375
12760
  });
12376
12761
 
12377
12762
  it('connect ps data channel if ps started in webinar', async () => {
@@ -12397,22 +12782,22 @@ describe('plugin-meetings', () => {
12397
12782
  });
12398
12783
 
12399
12784
  it('should read the locus object, set on the meeting and return null', () => {
12785
+ const dataSets = {someFakeStuff: 'dataSet'};
12786
+
12400
12787
  meeting.setLocus({
12401
12788
  mediaConnections: [test1],
12402
12789
  locusUrl: url1,
12403
12790
  locusId: uuid1,
12404
12791
  selfId: uuid2,
12405
12792
  mediaId: uuid3,
12406
- host: {id: uuid4},
12793
+ locus: {host: {id: uuid4}},
12794
+ dataSets,
12407
12795
  });
12408
12796
  assert.calledOnce(meeting.locusInfo.initialSetup);
12409
12797
  assert.calledWith(meeting.locusInfo.initialSetup, {
12410
- mediaConnections: [test1],
12411
- locusUrl: url1,
12412
- locusId: uuid1,
12413
- selfId: uuid2,
12414
- mediaId: uuid3,
12415
- host: {id: uuid4},
12798
+ trigger: 'join-response',
12799
+ locus: {host: {id: uuid4}},
12800
+ dataSets,
12416
12801
  });
12417
12802
  assert.equal(meeting.mediaConnections, test1);
12418
12803
  assert.equal(meeting.locusUrl, url1);
@@ -12502,7 +12887,9 @@ describe('plugin-meetings', () => {
12502
12887
  meeting.meetingRequest.changeMeetingFloor = sinon.stub().returns(Promise.resolve());
12503
12888
  meeting.deviceUrl = 'deviceUrl.com';
12504
12889
  webex.internal.newMetrics.callDiagnosticLatencies.saveTimestamp = sinon.stub();
12505
- webex.internal.newMetrics.callDiagnosticLatencies.getShareDuration = sinon.stub().returns(1000);
12890
+ webex.internal.newMetrics.callDiagnosticLatencies.getShareDuration = sinon
12891
+ .stub()
12892
+ .returns(1000);
12506
12893
  webex.internal.newMetrics.submitClientEvent = sinon.stub();
12507
12894
  });
12508
12895
  it('should stop the whiteboard share', async () => {
@@ -12606,7 +12993,9 @@ describe('plugin-meetings', () => {
12606
12993
  meeting.deviceUrl = 'my-web-url';
12607
12994
  meeting.locusInfo.info = {isWebinar: false};
12608
12995
  webex.internal.newMetrics.callDiagnosticLatencies.saveTimestamp = sinon.stub();
12609
- webex.internal.newMetrics.callDiagnosticLatencies.getShareDuration = sinon.stub().returns(1500);
12996
+ webex.internal.newMetrics.callDiagnosticLatencies.getShareDuration = sinon
12997
+ .stub()
12998
+ .returns(1500);
12610
12999
  webex.internal.newMetrics.submitClientEvent = sinon.stub();
12611
13000
  });
12612
13001
 
@@ -12855,8 +13244,8 @@ describe('plugin-meetings', () => {
12855
13244
 
12856
13245
  shareStatus =
12857
13246
  meeting.webinar.selfIsAttendee || meeting.guest
12858
- ? SHARE_STATUS.REMOTE_SHARE_ACTIVE
12859
- : SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE;
13247
+ ? SHARE_STATUS.REMOTE_SHARE_ACTIVE
13248
+ : SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE;
12860
13249
  }
12861
13250
 
12862
13251
  if (eventTrigger.member) {
@@ -13802,32 +14191,32 @@ describe('plugin-meetings', () => {
13802
14191
  });
13803
14192
  });
13804
14193
 
13805
- describe('handleShareVideoStreamMuteStateChange', () => {
13806
- it('should emit MEETING_SHARE_VIDEO_MUTE_STATE_CHANGE event with correct fields', () => {
13807
- meeting.isMultistream = true;
13808
- meeting.statsAnalyzer = {shareVideoEncoderImplementation: 'OpenH264'};
13809
- meeting.mediaProperties.shareVideoStream = {
13810
- getSettings: sinon.stub().returns({displaySurface: 'monitor', frameRate: 30}),
13811
- };
14194
+ describe('handleShareVideoStreamMuteStateChange', () => {
14195
+ it('should emit MEETING_SHARE_VIDEO_MUTE_STATE_CHANGE event with correct fields', () => {
14196
+ meeting.isMultistream = true;
14197
+ meeting.statsAnalyzer = {shareVideoEncoderImplementation: 'OpenH264'};
14198
+ meeting.mediaProperties.shareVideoStream = {
14199
+ getSettings: sinon.stub().returns({displaySurface: 'monitor', frameRate: 30}),
14200
+ };
13812
14201
 
13813
- meeting.handleShareVideoStreamMuteStateChange(true);
14202
+ meeting.handleShareVideoStreamMuteStateChange(true);
13814
14203
 
13815
- assert.calledOnceWithExactly(
13816
- Metrics.sendBehavioralMetric,
13817
- BEHAVIORAL_METRICS.MEETING_SHARE_VIDEO_MUTE_STATE_CHANGE,
13818
- {
13819
- correlationId: meeting.correlationId,
13820
- muted: true,
13821
- encoderImplementation: 'OpenH264',
13822
- displaySurface: 'monitor',
13823
- isMultistream: true,
13824
- frameRate: 30,
13825
- }
13826
- );
14204
+ assert.calledOnceWithExactly(
14205
+ Metrics.sendBehavioralMetric,
14206
+ BEHAVIORAL_METRICS.MEETING_SHARE_VIDEO_MUTE_STATE_CHANGE,
14207
+ {
14208
+ correlationId: meeting.correlationId,
14209
+ muted: true,
14210
+ encoderImplementation: 'OpenH264',
14211
+ displaySurface: 'monitor',
14212
+ isMultistream: true,
14213
+ frameRate: 30,
14214
+ }
14215
+ );
14216
+ });
13827
14217
  });
13828
14218
  });
13829
14219
  });
13830
- });
13831
14220
 
13832
14221
  describe('#startKeepAlive', () => {
13833
14222
  let clock;
@@ -15026,11 +15415,9 @@ describe('plugin-meetings', () => {
15026
15415
  assert.exists(unsetStagePromise.then);
15027
15416
  await unsetStagePromise;
15028
15417
 
15029
- assert.calledOnceWithExactly(
15030
- meeting.meetingRequest.synchronizeStage,
15031
- locusUrl,
15032
- {overrideDefault: false}
15033
- );
15418
+ assert.calledOnceWithExactly(meeting.meetingRequest.synchronizeStage, locusUrl, {
15419
+ overrideDefault: false,
15420
+ });
15034
15421
  });
15035
15422
  });
15036
15423
 
@@ -15055,7 +15442,7 @@ describe('plugin-meetings', () => {
15055
15442
  meeting.meetingInfo.siteFullUrl,
15056
15443
  meeting.locusId,
15057
15444
  meetingUuid,
15058
- displayName,
15445
+ displayName
15059
15446
  );
15060
15447
  });
15061
15448
  });
@@ -15102,10 +15489,7 @@ describe('plugin-meetings', () => {
15102
15489
  assert.exists(cancelSipCallOutPromise.then);
15103
15490
  await cancelSipCallOutPromise;
15104
15491
 
15105
- assert.calledOnceWithExactly(
15106
- meeting.meetingRequest.cancelSipCallOut,
15107
- participantId
15108
- );
15492
+ assert.calledOnceWithExactly(meeting.meetingRequest.cancelSipCallOut, participantId);
15109
15493
  });
15110
15494
  });
15111
15495
  });