@webex/plugin-meetings 3.10.0-next.2 → 3.10.0-next.20

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 (269) 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 +0 -1
  67. package/dist/config.js.map +1 -1
  68. package/dist/constants.js +11 -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 +1266 -0
  82. package/dist/hashTree/hashTreeParser.js.map +1 -0
  83. package/dist/hashTree/types.js +22 -0
  84. package/dist/hashTree/types.js.map +1 -0
  85. package/dist/hashTree/utils.js +48 -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 +6 -8
  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.map +1 -1
  100. package/dist/locus-info/embeddedAppsUtils.js.map +1 -1
  101. package/dist/locus-info/fullState.js.map +1 -1
  102. package/dist/locus-info/hostUtils.js.map +1 -1
  103. package/dist/locus-info/index.js +584 -170
  104. package/dist/locus-info/index.js.map +1 -1
  105. package/dist/locus-info/infoUtils.js.map +1 -1
  106. package/dist/locus-info/mediaSharesUtils.js.map +1 -1
  107. package/dist/locus-info/parser.js +3 -4
  108. package/dist/locus-info/parser.js.map +1 -1
  109. package/dist/locus-info/selfUtils.js.map +1 -1
  110. package/dist/locus-info/types.js +7 -0
  111. package/dist/locus-info/types.js.map +1 -0
  112. package/dist/media/MediaConnectionAwaiter.js +1 -2
  113. package/dist/media/MediaConnectionAwaiter.js.map +1 -1
  114. package/dist/media/index.js +0 -2
  115. package/dist/media/index.js.map +1 -1
  116. package/dist/media/properties.js +15 -17
  117. package/dist/media/properties.js.map +1 -1
  118. package/dist/media/util.js.map +1 -1
  119. package/dist/meeting/brbState.js +8 -9
  120. package/dist/meeting/brbState.js.map +1 -1
  121. package/dist/meeting/connectionStateHandler.js +10 -13
  122. package/dist/meeting/connectionStateHandler.js.map +1 -1
  123. package/dist/meeting/in-meeting-actions.js.map +1 -1
  124. package/dist/meeting/index.js +1577 -1534
  125. package/dist/meeting/index.js.map +1 -1
  126. package/dist/meeting/locusMediaRequest.js +13 -17
  127. package/dist/meeting/locusMediaRequest.js.map +1 -1
  128. package/dist/meeting/muteState.js +11 -12
  129. package/dist/meeting/muteState.js.map +1 -1
  130. package/dist/meeting/request.js +101 -104
  131. package/dist/meeting/request.js.map +1 -1
  132. package/dist/meeting/request.type.js.map +1 -1
  133. package/dist/meeting/state.js.map +1 -1
  134. package/dist/meeting/type.js.map +1 -1
  135. package/dist/meeting/util.js +24 -23
  136. package/dist/meeting/util.js.map +1 -1
  137. package/dist/meeting/voicea-meeting.js +3 -3
  138. package/dist/meeting/voicea-meeting.js.map +1 -1
  139. package/dist/meeting-info/collection.js +7 -10
  140. package/dist/meeting-info/collection.js.map +1 -1
  141. package/dist/meeting-info/index.js +1 -2
  142. package/dist/meeting-info/index.js.map +1 -1
  143. package/dist/meeting-info/meeting-info-v2.js +135 -146
  144. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  145. package/dist/meeting-info/request.js +1 -2
  146. package/dist/meeting-info/request.js.map +1 -1
  147. package/dist/meeting-info/util.js +36 -37
  148. package/dist/meeting-info/util.js.map +1 -1
  149. package/dist/meeting-info/utilv2.js +30 -31
  150. package/dist/meeting-info/utilv2.js.map +1 -1
  151. package/dist/meetings/collection.js +6 -8
  152. package/dist/meetings/collection.js.map +1 -1
  153. package/dist/meetings/index.js +179 -141
  154. package/dist/meetings/index.js.map +1 -1
  155. package/dist/meetings/meetings.types.js.map +1 -1
  156. package/dist/meetings/request.js +6 -8
  157. package/dist/meetings/request.js.map +1 -1
  158. package/dist/meetings/util.js +25 -23
  159. package/dist/meetings/util.js.map +1 -1
  160. package/dist/member/index.js +1 -2
  161. package/dist/member/index.js.map +1 -1
  162. package/dist/member/types.js +6 -3
  163. package/dist/member/types.js.map +1 -1
  164. package/dist/member/util.js.map +1 -1
  165. package/dist/members/collection.js +1 -2
  166. package/dist/members/collection.js.map +1 -1
  167. package/dist/members/index.js +18 -21
  168. package/dist/members/index.js.map +1 -1
  169. package/dist/members/request.js +8 -11
  170. package/dist/members/request.js.map +1 -1
  171. package/dist/members/types.js.map +1 -1
  172. package/dist/members/util.js.map +1 -1
  173. package/dist/metrics/constants.js +3 -1
  174. package/dist/metrics/constants.js.map +1 -1
  175. package/dist/metrics/index.js +3 -4
  176. package/dist/metrics/index.js.map +1 -1
  177. package/dist/multistream/mediaRequestManager.js +1 -2
  178. package/dist/multistream/mediaRequestManager.js.map +1 -1
  179. package/dist/multistream/receiveSlot.js +34 -45
  180. package/dist/multistream/receiveSlot.js.map +1 -1
  181. package/dist/multistream/receiveSlotManager.js +8 -9
  182. package/dist/multistream/receiveSlotManager.js.map +1 -1
  183. package/dist/multistream/remoteMedia.js +12 -15
  184. package/dist/multistream/remoteMedia.js.map +1 -1
  185. package/dist/multistream/remoteMediaGroup.js +1 -2
  186. package/dist/multistream/remoteMediaGroup.js.map +1 -1
  187. package/dist/multistream/remoteMediaManager.js +122 -123
  188. package/dist/multistream/remoteMediaManager.js.map +1 -1
  189. package/dist/multistream/sendSlotManager.js +29 -30
  190. package/dist/multistream/sendSlotManager.js.map +1 -1
  191. package/dist/personal-meeting-room/index.js +16 -19
  192. package/dist/personal-meeting-room/index.js.map +1 -1
  193. package/dist/personal-meeting-room/request.js +7 -10
  194. package/dist/personal-meeting-room/request.js.map +1 -1
  195. package/dist/personal-meeting-room/util.js.map +1 -1
  196. package/dist/reachability/clusterReachability.js +56 -373
  197. package/dist/reachability/clusterReachability.js.map +1 -1
  198. package/dist/reachability/index.js +203 -205
  199. package/dist/reachability/index.js.map +1 -1
  200. package/dist/reachability/reachability.types.js +14 -1
  201. package/dist/reachability/reachability.types.js.map +1 -1
  202. package/dist/reachability/reachabilityPeerConnection.js +445 -0
  203. package/dist/reachability/reachabilityPeerConnection.js.map +1 -0
  204. package/dist/reachability/request.js.map +1 -1
  205. package/dist/reachability/util.js.map +1 -1
  206. package/dist/reactions/constants.js.map +1 -1
  207. package/dist/reactions/reactions.js.map +1 -1
  208. package/dist/reactions/reactions.type.js.map +1 -1
  209. package/dist/reconnection-manager/index.js +178 -176
  210. package/dist/reconnection-manager/index.js.map +1 -1
  211. package/dist/recording-controller/enums.js.map +1 -1
  212. package/dist/recording-controller/index.js +1 -2
  213. package/dist/recording-controller/index.js.map +1 -1
  214. package/dist/recording-controller/util.js.map +1 -1
  215. package/dist/roap/index.js +12 -15
  216. package/dist/roap/index.js.map +1 -1
  217. package/dist/roap/request.js +24 -26
  218. package/dist/roap/request.js.map +1 -1
  219. package/dist/roap/turnDiscovery.js +75 -76
  220. package/dist/roap/turnDiscovery.js.map +1 -1
  221. package/dist/roap/types.js.map +1 -1
  222. package/dist/transcription/index.js +4 -5
  223. package/dist/transcription/index.js.map +1 -1
  224. package/dist/types/common/errors/webex-errors.d.ts +12 -0
  225. package/dist/types/constants.d.ts +26 -21
  226. package/dist/types/hashTree/constants.d.ts +8 -0
  227. package/dist/types/hashTree/hashTree.d.ts +129 -0
  228. package/dist/types/hashTree/hashTreeParser.d.ts +260 -0
  229. package/dist/types/hashTree/types.d.ts +27 -0
  230. package/dist/types/hashTree/utils.d.ts +9 -0
  231. package/dist/types/index.d.ts +2 -1
  232. package/dist/types/locus-info/index.d.ts +97 -80
  233. package/dist/types/locus-info/types.d.ts +54 -0
  234. package/dist/types/meeting/index.d.ts +22 -9
  235. package/dist/types/meetings/index.d.ts +9 -2
  236. package/dist/types/metrics/constants.d.ts +2 -0
  237. package/dist/types/reachability/clusterReachability.d.ts +10 -88
  238. package/dist/types/reachability/reachability.types.d.ts +12 -1
  239. package/dist/types/reachability/reachabilityPeerConnection.d.ts +111 -0
  240. package/dist/webinar/collection.js +1 -2
  241. package/dist/webinar/collection.js.map +1 -1
  242. package/dist/webinar/index.js +148 -158
  243. package/dist/webinar/index.js.map +1 -1
  244. package/package.json +23 -22
  245. package/src/common/errors/webex-errors.ts +19 -0
  246. package/src/constants.ts +13 -1
  247. package/src/hashTree/constants.ts +9 -0
  248. package/src/hashTree/hashTree.ts +463 -0
  249. package/src/hashTree/hashTreeParser.ts +1161 -0
  250. package/src/hashTree/types.ts +32 -0
  251. package/src/hashTree/utils.ts +42 -0
  252. package/src/index.ts +2 -0
  253. package/src/locus-info/index.ts +597 -154
  254. package/src/locus-info/types.ts +53 -0
  255. package/src/meeting/index.ts +80 -28
  256. package/src/meeting/util.ts +1 -0
  257. package/src/meetings/index.ts +104 -51
  258. package/src/metrics/constants.ts +2 -0
  259. package/src/reachability/clusterReachability.ts +50 -347
  260. package/src/reachability/reachability.types.ts +15 -1
  261. package/src/reachability/reachabilityPeerConnection.ts +416 -0
  262. package/test/unit/spec/hashTree/hashTree.ts +655 -0
  263. package/test/unit/spec/hashTree/hashTreeParser.ts +1532 -0
  264. package/test/unit/spec/hashTree/utils.ts +103 -0
  265. package/test/unit/spec/locus-info/index.js +795 -16
  266. package/test/unit/spec/meeting/index.js +207 -94
  267. package/test/unit/spec/meeting/utils.js +77 -0
  268. package/test/unit/spec/meetings/index.js +71 -26
  269. package/test/unit/spec/reachability/clusterReachability.ts +281 -138
@@ -97,6 +97,7 @@ import PermissionError from '../../../../src/common/errors/permission';
97
97
  import JoinWebinarError from '../../../../src/common/errors/join-webinar-error';
98
98
  import IntentToJoinError from '../../../../src/common/errors/intent-to-join';
99
99
  import MultistreamNotSupportedError from '../../../../src/common/errors/multistream-not-supported-error';
100
+ import {SdpResponseTimeoutError} from '@webex/plugin-meetings/src/common/errors/webex-errors';
100
101
  import testUtils from '../../../utils/testUtils';
101
102
  import {
102
103
  MeetingInfoV2CaptchaError,
@@ -1002,6 +1003,35 @@ describe('plugin-meetings', () => {
1002
1003
  );
1003
1004
  });
1004
1005
 
1006
+ it('should call leave() if addMediaInternal() fails ', async () => {
1007
+ const addMediaError = new Error('fake addMedia error');
1008
+ addMediaError.name = 'TypeError';
1009
+
1010
+ const rejectError = {
1011
+ error: {
1012
+ body: {
1013
+ errorCode: 2729,
1014
+ message: 'fake addMedia error',
1015
+ name: 'TypeError'
1016
+ }
1017
+ }
1018
+ };
1019
+ meeting.addMediaInternal.rejects(addMediaError);
1020
+ sinon.stub(meeting, 'leave').resolves();
1021
+
1022
+ await assert.isRejected(
1023
+ meeting.joinWithMedia({
1024
+ joinOptions,
1025
+ mediaOptions,
1026
+ }),
1027
+ rejectError
1028
+ );
1029
+
1030
+ assert.calledOnce(meeting.join);
1031
+ assert.calledOnce(meeting.addMediaInternal);
1032
+ assert.calledOnce(Metrics.sendBehavioralMetric);
1033
+ });
1034
+
1005
1035
  it('should not call leave() if addMediaInternal() fails the first time and succeeds the second time and should only call join() once', async () => {
1006
1036
  const addMediaError = new Error('fake addMedia error');
1007
1037
  const leaveStub = sinon.stub(meeting, 'leave');
@@ -1848,7 +1878,7 @@ describe('plugin-meetings', () => {
1848
1878
  setCorrelationIdSpy = sinon.spy(meeting, 'setCorrelationId');
1849
1879
  meeting.setLocus = sinon.stub().returns(true);
1850
1880
  webex.meetings.registered = true;
1851
- meeting.updateLLMConnection = sinon.stub().returns(Promise.resolve());
1881
+ sinon.stub(meeting, 'updateLLMConnection').returns(Promise.resolve());
1852
1882
  });
1853
1883
 
1854
1884
  describe('successful', () => {
@@ -1999,18 +2029,15 @@ describe('plugin-meetings', () => {
1999
2029
 
2000
2030
  // Assert that client.locus.join.response error event is not sent from this function, it is now emitted from MeetingUtil.joinMeeting
2001
2031
  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
- );
2032
+ assert.calledWithMatch(webex.internal.newMetrics.submitClientEvent, {
2033
+ name: 'client.call.initiated',
2034
+ payload: {
2035
+ trigger: 'user-interaction',
2036
+ isRoapCallEnabled: true,
2037
+ pstnAudioType: undefined,
2038
+ },
2039
+ options: {meetingId: meeting.id},
2040
+ });
2014
2041
  });
2015
2042
  });
2016
2043
  it('should fail if password is required', async () => {
@@ -2042,7 +2069,7 @@ describe('plugin-meetings', () => {
2042
2069
  const defer = new Defer();
2043
2070
 
2044
2071
  meeting.config.enableAutomaticLLM = true;
2045
- meeting.updateLLMConnection = sinon.stub().returns(defer.promise);
2072
+ meeting.updateLLMConnection.returns(defer.promise);
2046
2073
 
2047
2074
  const result = await meeting.join();
2048
2075
 
@@ -2053,7 +2080,7 @@ describe('plugin-meetings', () => {
2053
2080
 
2054
2081
  it('should call updateLLMConnection as part of joining if config value is set', async () => {
2055
2082
  meeting.config.enableAutomaticLLM = true;
2056
- meeting.updateLLMConnection = sinon.stub().resolves();
2083
+ meeting.updateLLMConnection.resolves();
2057
2084
 
2058
2085
  await meeting.join();
2059
2086
 
@@ -2061,7 +2088,7 @@ describe('plugin-meetings', () => {
2061
2088
  });
2062
2089
 
2063
2090
  it('should not call updateLLMConnection as part of joining if config value is not set', async () => {
2064
- meeting.updateLLMConnection = sinon.stub().resolves();
2091
+ meeting.updateLLMConnection.resolves();
2065
2092
  await meeting.join();
2066
2093
 
2067
2094
  assert.notCalled(meeting.updateLLMConnection);
@@ -2071,7 +2098,7 @@ describe('plugin-meetings', () => {
2071
2098
  const defer = new Defer();
2072
2099
 
2073
2100
  meeting.config.enableAutomaticLLM = true;
2074
- meeting.updateLLMConnection = sinon.stub().returns(defer.promise);
2101
+ meeting.updateLLMConnection.returns(defer.promise);
2075
2102
 
2076
2103
  const result = await meeting.join();
2077
2104
 
@@ -2097,6 +2124,42 @@ describe('plugin-meetings', () => {
2097
2124
  ]);
2098
2125
  }
2099
2126
  });
2127
+
2128
+ it('handles Locus LLM events', async () => {
2129
+ const locusInfoParseStub = sinon.stub(meeting.locusInfo, 'parse');
2130
+ sinon.stub(meeting, 'isJoined').returns(true);
2131
+
2132
+ // Set up llm.on stub to capture the registered listener when updateLLMConnection is called
2133
+ let locusLLMEventListener;
2134
+ meeting.webex.internal.llm.on = sinon.stub().callsFake((eventName, callback) => {
2135
+ if (eventName === 'event:locus.state_message') {
2136
+ locusLLMEventListener = callback;
2137
+ }
2138
+ });
2139
+ meeting.webex.internal.llm.off = sinon.stub();
2140
+
2141
+ // we need the real meeting.updateLLMConnection not the mock
2142
+ meeting.updateLLMConnection.restore();
2143
+
2144
+ // Call updateLLMConnection to register the listener
2145
+ await meeting.updateLLMConnection();
2146
+
2147
+ // Verify the listener was registered and we captured it
2148
+ assert.isDefined(locusLLMEventListener, 'LLM event listener should be registered');
2149
+
2150
+ // Now trigger the event
2151
+ const eventData = {
2152
+ eventType: 'locus.state_message',
2153
+ stateElementsMessage: {
2154
+ header: {messageId: 'msg-1'},
2155
+ elements: [],
2156
+ },
2157
+ };
2158
+
2159
+ locusLLMEventListener({data: eventData});
2160
+
2161
+ assert.calledOnceWithExactly(locusInfoParseStub, meeting, eventData);
2162
+ });
2100
2163
  });
2101
2164
 
2102
2165
  describe('refreshPermissionToken', () => {
@@ -2679,7 +2742,11 @@ describe('plugin-meetings', () => {
2679
2742
  // simulate timeout waiting for the SDP answer that never comes
2680
2743
  await clock.tickAsync(ROAP_OFFER_ANSWER_EXCHANGE_TIMEOUT);
2681
2744
 
2682
- await assert.isRejected(result);
2745
+ await assert.isRejected(
2746
+ result,
2747
+ SdpResponseTimeoutError,
2748
+ 'Timed out waiting for REMOTE SDP ANSWER'
2749
+ );
2683
2750
 
2684
2751
  assert.calledOnceWithExactly(getErrorPayloadForClientErrorCodeStub, {
2685
2752
  clientErrorCode: 2007,
@@ -4087,7 +4154,7 @@ describe('plugin-meetings', () => {
4087
4154
  member2: {isInMeeting: false, isInLobby: true},
4088
4155
  member3: {isInMeeting: false, isInLobby: false},
4089
4156
  member4: {isInMeeting: true, isInLobby: false},
4090
- }
4157
+ },
4091
4158
  };
4092
4159
  sinon.stub(meeting, 'getMembers').returns({membersCollection: fakeMembersCollection});
4093
4160
  const fakeData = {intervalMetadata: {}};
@@ -4747,6 +4814,7 @@ describe('plugin-meetings', () => {
4747
4814
  id: 'fake locus from mocked join request',
4748
4815
  locusUrl: 'fake locus url',
4749
4816
  mediaId: 'fake media id',
4817
+ locus: {fullState: {}},
4750
4818
  });
4751
4819
  sinon.stub(meeting.meetingRequest, 'joinMeeting').resolves({
4752
4820
  headers: {
@@ -6739,7 +6807,7 @@ describe('plugin-meetings', () => {
6739
6807
  // Verify pstnCorrelationId was set
6740
6808
  assert.exists(meeting.pstnCorrelationId);
6741
6809
  assert.notEqual(meeting.pstnCorrelationId, meeting.correlationId);
6742
- const firstPstnCorrelationId = meeting.pstnCorrelationId
6810
+ const firstPstnCorrelationId = meeting.pstnCorrelationId;
6743
6811
 
6744
6812
  meeting.meetingRequest.dialIn.resetHistory();
6745
6813
 
@@ -6814,15 +6882,19 @@ describe('plugin-meetings', () => {
6814
6882
  assert.equal(e, error);
6815
6883
 
6816
6884
  // 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
- });
6885
+ assert.calledWith(
6886
+ Metrics.sendBehavioralMetric,
6887
+ BEHAVIORAL_METRICS.ADD_DIAL_IN_FAILURE,
6888
+ {
6889
+ correlation_id: meeting.correlationId,
6890
+ dial_in_url: meeting.dialInUrl,
6891
+ dial_in_correlation_id: sinon.match.string,
6892
+ locus_id: meeting.locusUrl.split('/').pop(),
6893
+ client_url: meeting.deviceUrl,
6894
+ reason: error.error.message,
6895
+ stack: error.stack,
6896
+ }
6897
+ );
6826
6898
 
6827
6899
  // Verify pstnCorrelationId was cleared after error
6828
6900
  assert.equal(meeting.pstnCorrelationId, undefined);
@@ -6841,15 +6913,19 @@ describe('plugin-meetings', () => {
6841
6913
  assert.equal(e, error);
6842
6914
 
6843
6915
  // 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
- });
6916
+ assert.calledWith(
6917
+ Metrics.sendBehavioralMetric,
6918
+ BEHAVIORAL_METRICS.ADD_DIAL_OUT_FAILURE,
6919
+ {
6920
+ correlation_id: meeting.correlationId,
6921
+ dial_out_url: meeting.dialOutUrl,
6922
+ dial_out_correlation_id: sinon.match.string,
6923
+ locus_id: meeting.locusUrl.split('/').pop(),
6924
+ client_url: meeting.deviceUrl,
6925
+ reason: error.error.message,
6926
+ stack: error.stack,
6927
+ }
6928
+ );
6853
6929
 
6854
6930
  // Verify pstnCorrelationId was cleared after error
6855
6931
  assert.equal(meeting.pstnCorrelationId, undefined);
@@ -6894,7 +6970,7 @@ describe('plugin-meetings', () => {
6894
6970
 
6895
6971
  // Verify that pstnCorrelationId is still cleared even when no phone connection is active
6896
6972
  assert.equal(meeting.pstnCorrelationId, undefined);
6897
- // And verify no disconnect was attempted
6973
+ // And verify no disconnect was attempted
6898
6974
  assert.notCalled(MeetingUtil.disconnectPhoneAudio);
6899
6975
  });
6900
6976
  });
@@ -10568,7 +10644,7 @@ describe('plugin-meetings', () => {
10568
10644
  describe('#setUpLocusUrlListener', () => {
10569
10645
  it('listens to the locus url update event', (done) => {
10570
10646
  const newLocusUrl = 'newLocusUrl/12345';
10571
- const payload = {url: newLocusUrl}
10647
+ const payload = {url: newLocusUrl};
10572
10648
 
10573
10649
  meeting.members = {locusUrlUpdate: sinon.stub().returns(Promise.resolve(test1))};
10574
10650
  meeting.recordingController = {setLocusUrl: sinon.stub().returns(undefined)};
@@ -10611,7 +10687,7 @@ describe('plugin-meetings', () => {
10611
10687
  });
10612
10688
  it('update mainLocusUrl for controlsOptionManager if payload.isMainLocus as true', (done) => {
10613
10689
  const newLocusUrl = 'newLocusUrl/12345';
10614
- const payload = {url: newLocusUrl, isMainLocus: true}
10690
+ const payload = {url: newLocusUrl, isMainLocus: true};
10615
10691
 
10616
10692
  meeting.controlsOptionsManager = {setLocusUrl: sinon.stub().returns(undefined)};
10617
10693
 
@@ -10843,7 +10919,9 @@ describe('plugin-meetings', () => {
10843
10919
  meeting.meetingRequest.changeMeetingFloor = sinon.stub().returns(Promise.resolve());
10844
10920
  (meeting.deviceUrl = 'deviceUrl.com'), (meeting.localShareInstanceId = '1234-5678');
10845
10921
  webex.internal.newMetrics.callDiagnosticLatencies.saveTimestamp = sinon.stub();
10846
- webex.internal.newMetrics.callDiagnosticLatencies.getShareDuration = sinon.stub().returns(1000);
10922
+ webex.internal.newMetrics.callDiagnosticLatencies.getShareDuration = sinon
10923
+ .stub()
10924
+ .returns(1000);
10847
10925
  });
10848
10926
  it('should call changeMeetingFloor()', async () => {
10849
10927
  meeting.screenShareFloorState = 'GRANTED';
@@ -11494,8 +11572,10 @@ describe('plugin-meetings', () => {
11494
11572
  canShareWhiteBoardSpy = sinon.spy(MeetingUtil, 'canShareWhiteBoard');
11495
11573
  canMoveToLobbySpy = sinon.spy(MeetingUtil, 'canMoveToLobby');
11496
11574
  showAutoEndMeetingWarningSpy = sinon.spy(MeetingUtil, 'showAutoEndMeetingWarning');
11497
- isSpokenLanguageAutoDetectionEnabledSpy = sinon.spy(MeetingUtil, 'isSpokenLanguageAutoDetectionEnabled');
11498
-
11575
+ isSpokenLanguageAutoDetectionEnabledSpy = sinon.spy(
11576
+ MeetingUtil,
11577
+ 'isSpokenLanguageAutoDetectionEnabled'
11578
+ );
11499
11579
  });
11500
11580
 
11501
11581
  afterEach(() => {
@@ -12277,16 +12357,26 @@ describe('plugin-meetings', () => {
12277
12357
  assert.notCalled(webex.internal.llm.disconnectLLM);
12278
12358
  assert.calledWith(webex.internal.llm.registerAndConnect, 'a url', 'a datachannel url');
12279
12359
  assert.equal(result, 'something');
12280
- assert.calledOnceWithExactly(
12360
+ assert.calledWithExactly(
12281
12361
  meeting.webex.internal.llm.off,
12282
12362
  'event:relay.event',
12283
12363
  meeting.processRelayEvent
12284
12364
  );
12285
- assert.calledOnceWithExactly(
12365
+ assert.calledWithExactly(
12366
+ meeting.webex.internal.llm.off,
12367
+ 'event:locus.state_message',
12368
+ meeting.processLocusLLMEvent
12369
+ );
12370
+ assert.calledWithExactly(
12286
12371
  meeting.webex.internal.llm.on,
12287
12372
  'event:relay.event',
12288
12373
  meeting.processRelayEvent
12289
12374
  );
12375
+ assert.calledWithExactly(
12376
+ meeting.webex.internal.llm.on,
12377
+ 'event:locus.state_message',
12378
+ meeting.processLocusLLMEvent
12379
+ );
12290
12380
  });
12291
12381
 
12292
12382
  it('disconnects if first if the locus url has changed', async () => {
@@ -12314,15 +12404,25 @@ describe('plugin-meetings', () => {
12314
12404
  'event:relay.event',
12315
12405
  meeting.processRelayEvent
12316
12406
  );
12317
- assert.calledTwice(meeting.webex.internal.llm.off);
12318
- assert.calledOnceWithExactly(
12407
+ assert.calledWithExactly(
12408
+ meeting.webex.internal.llm.off,
12409
+ 'event:locus.state_message',
12410
+ meeting.processLocusLLMEvent
12411
+ );
12412
+ assert.callCount(meeting.webex.internal.llm.off, 4);
12413
+ assert.calledWithExactly(
12319
12414
  meeting.webex.internal.llm.on,
12320
12415
  'event:relay.event',
12321
12416
  meeting.processRelayEvent
12322
12417
  );
12418
+ assert.calledWithExactly(
12419
+ meeting.webex.internal.llm.on,
12420
+ 'event:locus.state_message',
12421
+ meeting.processLocusLLMEvent
12422
+ );
12323
12423
  });
12324
12424
 
12325
- it('disconnects if first if the data channel url has changed', async () => {
12425
+ it('disconnects it first if the data channel url has changed', async () => {
12326
12426
  meeting.joinedWith = {state: 'JOINED'};
12327
12427
  webex.internal.llm.isConnected.returns(true);
12328
12428
  webex.internal.llm.getLocusUrl.returns('a url');
@@ -12347,12 +12447,21 @@ describe('plugin-meetings', () => {
12347
12447
  'event:relay.event',
12348
12448
  meeting.processRelayEvent
12349
12449
  );
12350
- assert.calledTwice(meeting.webex.internal.llm.off);
12351
- assert.calledOnceWithExactly(
12450
+ assert.calledWithExactly(
12451
+ meeting.webex.internal.llm.off,
12452
+ 'event:locus.state_message',
12453
+ meeting.processLocusLLMEvent
12454
+ );
12455
+ assert.calledWithExactly(
12352
12456
  meeting.webex.internal.llm.on,
12353
12457
  'event:relay.event',
12354
12458
  meeting.processRelayEvent
12355
12459
  );
12460
+ assert.calledWithExactly(
12461
+ meeting.webex.internal.llm.on,
12462
+ 'event:locus.state_message',
12463
+ meeting.processLocusLLMEvent
12464
+ );
12356
12465
  });
12357
12466
 
12358
12467
  it('disconnects when the state is not JOINED', async () => {
@@ -12367,11 +12476,16 @@ describe('plugin-meetings', () => {
12367
12476
  assert.calledWith(webex.internal.llm.disconnectLLM, undefined);
12368
12477
  assert.notCalled(webex.internal.llm.registerAndConnect);
12369
12478
  assert.equal(result, undefined);
12370
- assert.calledOnceWithExactly(
12479
+ assert.calledWithExactly(
12371
12480
  meeting.webex.internal.llm.off,
12372
12481
  'event:relay.event',
12373
12482
  meeting.processRelayEvent
12374
12483
  );
12484
+ assert.calledWithExactly(
12485
+ meeting.webex.internal.llm.off,
12486
+ 'event:locus.state_message',
12487
+ meeting.processLocusLLMEvent
12488
+ );
12375
12489
  });
12376
12490
 
12377
12491
  it('connect ps data channel if ps started in webinar', async () => {
@@ -12397,22 +12511,22 @@ describe('plugin-meetings', () => {
12397
12511
  });
12398
12512
 
12399
12513
  it('should read the locus object, set on the meeting and return null', () => {
12514
+ const dataSets = {someFakeStuff: 'dataSet'};
12515
+
12400
12516
  meeting.setLocus({
12401
12517
  mediaConnections: [test1],
12402
12518
  locusUrl: url1,
12403
12519
  locusId: uuid1,
12404
12520
  selfId: uuid2,
12405
12521
  mediaId: uuid3,
12406
- host: {id: uuid4},
12522
+ locus: {host: {id: uuid4}},
12523
+ dataSets,
12407
12524
  });
12408
12525
  assert.calledOnce(meeting.locusInfo.initialSetup);
12409
12526
  assert.calledWith(meeting.locusInfo.initialSetup, {
12410
- mediaConnections: [test1],
12411
- locusUrl: url1,
12412
- locusId: uuid1,
12413
- selfId: uuid2,
12414
- mediaId: uuid3,
12415
- host: {id: uuid4},
12527
+ trigger: 'join-response',
12528
+ locus: {host: {id: uuid4}},
12529
+ dataSets,
12416
12530
  });
12417
12531
  assert.equal(meeting.mediaConnections, test1);
12418
12532
  assert.equal(meeting.locusUrl, url1);
@@ -12502,7 +12616,9 @@ describe('plugin-meetings', () => {
12502
12616
  meeting.meetingRequest.changeMeetingFloor = sinon.stub().returns(Promise.resolve());
12503
12617
  meeting.deviceUrl = 'deviceUrl.com';
12504
12618
  webex.internal.newMetrics.callDiagnosticLatencies.saveTimestamp = sinon.stub();
12505
- webex.internal.newMetrics.callDiagnosticLatencies.getShareDuration = sinon.stub().returns(1000);
12619
+ webex.internal.newMetrics.callDiagnosticLatencies.getShareDuration = sinon
12620
+ .stub()
12621
+ .returns(1000);
12506
12622
  webex.internal.newMetrics.submitClientEvent = sinon.stub();
12507
12623
  });
12508
12624
  it('should stop the whiteboard share', async () => {
@@ -12606,7 +12722,9 @@ describe('plugin-meetings', () => {
12606
12722
  meeting.deviceUrl = 'my-web-url';
12607
12723
  meeting.locusInfo.info = {isWebinar: false};
12608
12724
  webex.internal.newMetrics.callDiagnosticLatencies.saveTimestamp = sinon.stub();
12609
- webex.internal.newMetrics.callDiagnosticLatencies.getShareDuration = sinon.stub().returns(1500);
12725
+ webex.internal.newMetrics.callDiagnosticLatencies.getShareDuration = sinon
12726
+ .stub()
12727
+ .returns(1500);
12610
12728
  webex.internal.newMetrics.submitClientEvent = sinon.stub();
12611
12729
  });
12612
12730
 
@@ -12855,8 +12973,8 @@ describe('plugin-meetings', () => {
12855
12973
 
12856
12974
  shareStatus =
12857
12975
  meeting.webinar.selfIsAttendee || meeting.guest
12858
- ? SHARE_STATUS.REMOTE_SHARE_ACTIVE
12859
- : SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE;
12976
+ ? SHARE_STATUS.REMOTE_SHARE_ACTIVE
12977
+ : SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE;
12860
12978
  }
12861
12979
 
12862
12980
  if (eventTrigger.member) {
@@ -13802,32 +13920,32 @@ describe('plugin-meetings', () => {
13802
13920
  });
13803
13921
  });
13804
13922
 
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
- };
13923
+ describe('handleShareVideoStreamMuteStateChange', () => {
13924
+ it('should emit MEETING_SHARE_VIDEO_MUTE_STATE_CHANGE event with correct fields', () => {
13925
+ meeting.isMultistream = true;
13926
+ meeting.statsAnalyzer = {shareVideoEncoderImplementation: 'OpenH264'};
13927
+ meeting.mediaProperties.shareVideoStream = {
13928
+ getSettings: sinon.stub().returns({displaySurface: 'monitor', frameRate: 30}),
13929
+ };
13812
13930
 
13813
- meeting.handleShareVideoStreamMuteStateChange(true);
13931
+ meeting.handleShareVideoStreamMuteStateChange(true);
13814
13932
 
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
- );
13933
+ assert.calledOnceWithExactly(
13934
+ Metrics.sendBehavioralMetric,
13935
+ BEHAVIORAL_METRICS.MEETING_SHARE_VIDEO_MUTE_STATE_CHANGE,
13936
+ {
13937
+ correlationId: meeting.correlationId,
13938
+ muted: true,
13939
+ encoderImplementation: 'OpenH264',
13940
+ displaySurface: 'monitor',
13941
+ isMultistream: true,
13942
+ frameRate: 30,
13943
+ }
13944
+ );
13945
+ });
13827
13946
  });
13828
13947
  });
13829
13948
  });
13830
- });
13831
13949
 
13832
13950
  describe('#startKeepAlive', () => {
13833
13951
  let clock;
@@ -15026,11 +15144,9 @@ describe('plugin-meetings', () => {
15026
15144
  assert.exists(unsetStagePromise.then);
15027
15145
  await unsetStagePromise;
15028
15146
 
15029
- assert.calledOnceWithExactly(
15030
- meeting.meetingRequest.synchronizeStage,
15031
- locusUrl,
15032
- {overrideDefault: false}
15033
- );
15147
+ assert.calledOnceWithExactly(meeting.meetingRequest.synchronizeStage, locusUrl, {
15148
+ overrideDefault: false,
15149
+ });
15034
15150
  });
15035
15151
  });
15036
15152
 
@@ -15055,7 +15171,7 @@ describe('plugin-meetings', () => {
15055
15171
  meeting.meetingInfo.siteFullUrl,
15056
15172
  meeting.locusId,
15057
15173
  meetingUuid,
15058
- displayName,
15174
+ displayName
15059
15175
  );
15060
15176
  });
15061
15177
  });
@@ -15102,10 +15218,7 @@ describe('plugin-meetings', () => {
15102
15218
  assert.exists(cancelSipCallOutPromise.then);
15103
15219
  await cancelSipCallOutPromise;
15104
15220
 
15105
- assert.calledOnceWithExactly(
15106
- meeting.meetingRequest.cancelSipCallOut,
15107
- participantId
15108
- );
15221
+ assert.calledOnceWithExactly(meeting.meetingRequest.cancelSipCallOut, participantId);
15109
15222
  });
15110
15223
  });
15111
15224
  });
@@ -1433,5 +1433,82 @@ describe('plugin-meetings', () => {
1433
1433
  });
1434
1434
  });
1435
1435
  });
1436
+
1437
+ describe('#parseLocusJoin', () => {
1438
+ let response;
1439
+
1440
+ beforeEach(() => {
1441
+ response = {
1442
+ body: {
1443
+ locus: {
1444
+ url: 'https://locus-a.wbx2.com/locus/api/v1/loci/12345',
1445
+ self: {
1446
+ id: 'selfId123',
1447
+ },
1448
+ },
1449
+ dataSets: [{name: 'dataset1', url: 'http://dataset.com'}],
1450
+ mediaConnections: [
1451
+ {mediaId: 'mediaId456'},
1452
+ {someOtherField: 'value'},
1453
+ ],
1454
+ },
1455
+ };
1456
+ });
1457
+
1458
+ it('works as expected', () => {
1459
+ const result = MeetingUtil.parseLocusJoin(response);
1460
+
1461
+ assert.deepEqual(result, {
1462
+ locus: response.body.locus,
1463
+ dataSets: response.body.dataSets,
1464
+ mediaConnections: response.body.mediaConnections,
1465
+ locusUrl: 'https://locus-a.wbx2.com/locus/api/v1/loci/12345',
1466
+ locusId: '12345',
1467
+ selfId: 'selfId123',
1468
+ mediaId: 'mediaId456',
1469
+ });
1470
+ });
1471
+
1472
+ it('extracts mediaId from the last mediaConnection that has it', () => {
1473
+ response.body.mediaConnections = [
1474
+ {someField: 'noMediaId'},
1475
+ {mediaId: 'firstMediaId'},
1476
+ {mediaId: 'secondMediaId'},
1477
+ ];
1478
+
1479
+ const result = MeetingUtil.parseLocusJoin(response);
1480
+
1481
+ // Note: the implementation uses forEach which doesn't break,
1482
+ // so it will use the last mediaId found, not the first
1483
+ assert.equal(result.mediaId, 'secondMediaId');
1484
+ });
1485
+
1486
+ it('handles empty mediaConnections array', () => {
1487
+ response.body.mediaConnections = [];
1488
+
1489
+ const result = MeetingUtil.parseLocusJoin(response);
1490
+
1491
+ assert.deepEqual(result, {
1492
+ locus: response.body.locus,
1493
+ dataSets: response.body.dataSets,
1494
+ mediaConnections: [],
1495
+ locusUrl: 'https://locus-a.wbx2.com/locus/api/v1/loci/12345',
1496
+ locusId: '12345',
1497
+ selfId: 'selfId123',
1498
+ });
1499
+ assert.isUndefined(result.mediaId);
1500
+ });
1501
+
1502
+ it('handles mediaConnections without mediaId', () => {
1503
+ response.body.mediaConnections = [
1504
+ {someField: 'value1'},
1505
+ {anotherField: 'value2'},
1506
+ ];
1507
+
1508
+ const result = MeetingUtil.parseLocusJoin(response);
1509
+
1510
+ assert.isUndefined(result.mediaId);
1511
+ });
1512
+ });
1436
1513
  });
1437
1514
  });