@webex/plugin-meetings 3.10.0-next.1 → 3.10.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 (246) 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 +12 -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/hashTreeParser.js +165 -0
  78. package/dist/hashTree/hashTreeParser.js.map +1 -0
  79. package/dist/hashTree/types.js +15 -0
  80. package/dist/hashTree/types.js.map +1 -0
  81. package/dist/index.js +8 -2
  82. package/dist/index.js.map +1 -1
  83. package/dist/interceptors/index.js.map +1 -1
  84. package/dist/interceptors/locusRetry.js +6 -8
  85. package/dist/interceptors/locusRetry.js.map +1 -1
  86. package/dist/interceptors/locusRouteToken.js +6 -8
  87. package/dist/interceptors/locusRouteToken.js.map +1 -1
  88. package/dist/interpretation/collection.js.map +1 -1
  89. package/dist/interpretation/index.js +1 -2
  90. package/dist/interpretation/index.js.map +1 -1
  91. package/dist/interpretation/siLanguage.js +1 -1
  92. package/dist/interpretation/siLanguage.js.map +1 -1
  93. package/dist/locus-info/controlsUtils.js.map +1 -1
  94. package/dist/locus-info/embeddedAppsUtils.js.map +1 -1
  95. package/dist/locus-info/fullState.js.map +1 -1
  96. package/dist/locus-info/hostUtils.js.map +1 -1
  97. package/dist/locus-info/index.js +532 -94
  98. package/dist/locus-info/index.js.map +1 -1
  99. package/dist/locus-info/infoUtils.js.map +1 -1
  100. package/dist/locus-info/mediaSharesUtils.js.map +1 -1
  101. package/dist/locus-info/parser.js +3 -4
  102. package/dist/locus-info/parser.js.map +1 -1
  103. package/dist/locus-info/selfUtils.js.map +1 -1
  104. package/dist/locus-info/types.js +7 -0
  105. package/dist/locus-info/types.js.map +1 -0
  106. package/dist/media/MediaConnectionAwaiter.js +1 -2
  107. package/dist/media/MediaConnectionAwaiter.js.map +1 -1
  108. package/dist/media/index.js +5 -2
  109. package/dist/media/index.js.map +1 -1
  110. package/dist/media/properties.js +15 -17
  111. package/dist/media/properties.js.map +1 -1
  112. package/dist/media/util.js.map +1 -1
  113. package/dist/meeting/brbState.js +8 -9
  114. package/dist/meeting/brbState.js.map +1 -1
  115. package/dist/meeting/connectionStateHandler.js +10 -13
  116. package/dist/meeting/connectionStateHandler.js.map +1 -1
  117. package/dist/meeting/in-meeting-actions.js.map +1 -1
  118. package/dist/meeting/index.js +1556 -1528
  119. package/dist/meeting/index.js.map +1 -1
  120. package/dist/meeting/locusMediaRequest.js +13 -17
  121. package/dist/meeting/locusMediaRequest.js.map +1 -1
  122. package/dist/meeting/muteState.js +11 -12
  123. package/dist/meeting/muteState.js.map +1 -1
  124. package/dist/meeting/request.js +101 -104
  125. package/dist/meeting/request.js.map +1 -1
  126. package/dist/meeting/request.type.js.map +1 -1
  127. package/dist/meeting/state.js.map +1 -1
  128. package/dist/meeting/type.js.map +1 -1
  129. package/dist/meeting/util.js +24 -23
  130. package/dist/meeting/util.js.map +1 -1
  131. package/dist/meeting/voicea-meeting.js +3 -3
  132. package/dist/meeting/voicea-meeting.js.map +1 -1
  133. package/dist/meeting-info/collection.js +7 -10
  134. package/dist/meeting-info/collection.js.map +1 -1
  135. package/dist/meeting-info/index.js +1 -2
  136. package/dist/meeting-info/index.js.map +1 -1
  137. package/dist/meeting-info/meeting-info-v2.js +135 -146
  138. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  139. package/dist/meeting-info/request.js +1 -2
  140. package/dist/meeting-info/request.js.map +1 -1
  141. package/dist/meeting-info/util.js +36 -37
  142. package/dist/meeting-info/util.js.map +1 -1
  143. package/dist/meeting-info/utilv2.js +30 -31
  144. package/dist/meeting-info/utilv2.js.map +1 -1
  145. package/dist/meetings/collection.js +6 -8
  146. package/dist/meetings/collection.js.map +1 -1
  147. package/dist/meetings/index.js +179 -141
  148. package/dist/meetings/index.js.map +1 -1
  149. package/dist/meetings/meetings.types.js.map +1 -1
  150. package/dist/meetings/request.js +6 -8
  151. package/dist/meetings/request.js.map +1 -1
  152. package/dist/meetings/util.js +25 -23
  153. package/dist/meetings/util.js.map +1 -1
  154. package/dist/member/index.js +1 -2
  155. package/dist/member/index.js.map +1 -1
  156. package/dist/member/types.js +6 -3
  157. package/dist/member/types.js.map +1 -1
  158. package/dist/member/util.js.map +1 -1
  159. package/dist/members/collection.js +1 -2
  160. package/dist/members/collection.js.map +1 -1
  161. package/dist/members/index.js +18 -21
  162. package/dist/members/index.js.map +1 -1
  163. package/dist/members/request.js +8 -11
  164. package/dist/members/request.js.map +1 -1
  165. package/dist/members/types.js.map +1 -1
  166. package/dist/members/util.js.map +1 -1
  167. package/dist/metrics/constants.js +3 -1
  168. package/dist/metrics/constants.js.map +1 -1
  169. package/dist/metrics/index.js +3 -4
  170. package/dist/metrics/index.js.map +1 -1
  171. package/dist/multistream/mediaRequestManager.js +1 -2
  172. package/dist/multistream/mediaRequestManager.js.map +1 -1
  173. package/dist/multistream/receiveSlot.js +34 -45
  174. package/dist/multistream/receiveSlot.js.map +1 -1
  175. package/dist/multistream/receiveSlotManager.js +8 -9
  176. package/dist/multistream/receiveSlotManager.js.map +1 -1
  177. package/dist/multistream/remoteMedia.js +12 -15
  178. package/dist/multistream/remoteMedia.js.map +1 -1
  179. package/dist/multistream/remoteMediaGroup.js +1 -2
  180. package/dist/multistream/remoteMediaGroup.js.map +1 -1
  181. package/dist/multistream/remoteMediaManager.js +122 -123
  182. package/dist/multistream/remoteMediaManager.js.map +1 -1
  183. package/dist/multistream/sendSlotManager.js +29 -30
  184. package/dist/multistream/sendSlotManager.js.map +1 -1
  185. package/dist/personal-meeting-room/index.js +16 -19
  186. package/dist/personal-meeting-room/index.js.map +1 -1
  187. package/dist/personal-meeting-room/request.js +7 -10
  188. package/dist/personal-meeting-room/request.js.map +1 -1
  189. package/dist/personal-meeting-room/util.js.map +1 -1
  190. package/dist/reachability/clusterReachability.js +36 -39
  191. package/dist/reachability/clusterReachability.js.map +1 -1
  192. package/dist/reachability/index.js +203 -205
  193. package/dist/reachability/index.js.map +1 -1
  194. package/dist/reachability/reachability.types.js.map +1 -1
  195. package/dist/reachability/request.js.map +1 -1
  196. package/dist/reachability/util.js.map +1 -1
  197. package/dist/reactions/constants.js.map +1 -1
  198. package/dist/reactions/reactions.js.map +1 -1
  199. package/dist/reactions/reactions.type.js.map +1 -1
  200. package/dist/reconnection-manager/index.js +178 -176
  201. package/dist/reconnection-manager/index.js.map +1 -1
  202. package/dist/recording-controller/enums.js.map +1 -1
  203. package/dist/recording-controller/index.js +1 -2
  204. package/dist/recording-controller/index.js.map +1 -1
  205. package/dist/recording-controller/util.js.map +1 -1
  206. package/dist/roap/index.js +12 -15
  207. package/dist/roap/index.js.map +1 -1
  208. package/dist/roap/request.js +24 -26
  209. package/dist/roap/request.js.map +1 -1
  210. package/dist/roap/turnDiscovery.js +75 -76
  211. package/dist/roap/turnDiscovery.js.map +1 -1
  212. package/dist/roap/types.js.map +1 -1
  213. package/dist/transcription/index.js +4 -5
  214. package/dist/transcription/index.js.map +1 -1
  215. package/dist/types/common/errors/webex-errors.d.ts +12 -0
  216. package/dist/types/constants.d.ts +26 -21
  217. package/dist/types/hashTree/hashTreeParser.d.ts +109 -0
  218. package/dist/types/hashTree/types.d.ts +16 -0
  219. package/dist/types/index.d.ts +2 -1
  220. package/dist/types/locus-info/index.d.ts +91 -42
  221. package/dist/types/locus-info/types.d.ts +45 -0
  222. package/dist/types/meeting/index.d.ts +22 -9
  223. package/dist/types/meetings/index.d.ts +9 -2
  224. package/dist/types/metrics/constants.d.ts +2 -0
  225. package/dist/webinar/collection.js +1 -2
  226. package/dist/webinar/collection.js.map +1 -1
  227. package/dist/webinar/index.js +148 -158
  228. package/dist/webinar/index.js.map +1 -1
  229. package/package.json +16 -16
  230. package/src/common/errors/webex-errors.ts +19 -0
  231. package/src/constants.ts +14 -2
  232. package/src/hashTree/hashTreeParser.ts +146 -0
  233. package/src/hashTree/types.ts +20 -0
  234. package/src/index.ts +2 -0
  235. package/src/locus-info/index.ts +534 -85
  236. package/src/locus-info/types.ts +46 -0
  237. package/src/media/index.ts +6 -0
  238. package/src/meeting/index.ts +61 -27
  239. package/src/meeting/util.ts +1 -0
  240. package/src/meetings/index.ts +104 -51
  241. package/src/metrics/constants.ts +2 -0
  242. package/test/unit/spec/locus-info/index.js +576 -1
  243. package/test/unit/spec/media/index.ts +140 -9
  244. package/test/unit/spec/meeting/index.js +178 -94
  245. package/test/unit/spec/meeting/utils.js +77 -0
  246. package/test/unit/spec/meetings/index.js +71 -28
@@ -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,
@@ -1848,7 +1849,7 @@ describe('plugin-meetings', () => {
1848
1849
  setCorrelationIdSpy = sinon.spy(meeting, 'setCorrelationId');
1849
1850
  meeting.setLocus = sinon.stub().returns(true);
1850
1851
  webex.meetings.registered = true;
1851
- meeting.updateLLMConnection = sinon.stub().returns(Promise.resolve());
1852
+ sinon.stub(meeting, 'updateLLMConnection').returns(Promise.resolve());
1852
1853
  });
1853
1854
 
1854
1855
  describe('successful', () => {
@@ -1999,18 +2000,15 @@ describe('plugin-meetings', () => {
1999
2000
 
2000
2001
  // Assert that client.locus.join.response error event is not sent from this function, it is now emitted from MeetingUtil.joinMeeting
2001
2002
  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
- );
2003
+ assert.calledWithMatch(webex.internal.newMetrics.submitClientEvent, {
2004
+ name: 'client.call.initiated',
2005
+ payload: {
2006
+ trigger: 'user-interaction',
2007
+ isRoapCallEnabled: true,
2008
+ pstnAudioType: undefined,
2009
+ },
2010
+ options: {meetingId: meeting.id},
2011
+ });
2014
2012
  });
2015
2013
  });
2016
2014
  it('should fail if password is required', async () => {
@@ -2042,7 +2040,7 @@ describe('plugin-meetings', () => {
2042
2040
  const defer = new Defer();
2043
2041
 
2044
2042
  meeting.config.enableAutomaticLLM = true;
2045
- meeting.updateLLMConnection = sinon.stub().returns(defer.promise);
2043
+ meeting.updateLLMConnection.returns(defer.promise);
2046
2044
 
2047
2045
  const result = await meeting.join();
2048
2046
 
@@ -2053,7 +2051,7 @@ describe('plugin-meetings', () => {
2053
2051
 
2054
2052
  it('should call updateLLMConnection as part of joining if config value is set', async () => {
2055
2053
  meeting.config.enableAutomaticLLM = true;
2056
- meeting.updateLLMConnection = sinon.stub().resolves();
2054
+ meeting.updateLLMConnection.resolves();
2057
2055
 
2058
2056
  await meeting.join();
2059
2057
 
@@ -2061,7 +2059,7 @@ describe('plugin-meetings', () => {
2061
2059
  });
2062
2060
 
2063
2061
  it('should not call updateLLMConnection as part of joining if config value is not set', async () => {
2064
- meeting.updateLLMConnection = sinon.stub().resolves();
2062
+ meeting.updateLLMConnection.resolves();
2065
2063
  await meeting.join();
2066
2064
 
2067
2065
  assert.notCalled(meeting.updateLLMConnection);
@@ -2071,7 +2069,7 @@ describe('plugin-meetings', () => {
2071
2069
  const defer = new Defer();
2072
2070
 
2073
2071
  meeting.config.enableAutomaticLLM = true;
2074
- meeting.updateLLMConnection = sinon.stub().returns(defer.promise);
2072
+ meeting.updateLLMConnection.returns(defer.promise);
2075
2073
 
2076
2074
  const result = await meeting.join();
2077
2075
 
@@ -2097,6 +2095,42 @@ describe('plugin-meetings', () => {
2097
2095
  ]);
2098
2096
  }
2099
2097
  });
2098
+
2099
+ it('handles Locus LLM events', async () => {
2100
+ const locusInfoParseStub = sinon.stub(meeting.locusInfo, 'parse');
2101
+ sinon.stub(meeting, 'isJoined').returns(true);
2102
+
2103
+ // Set up llm.on stub to capture the registered listener when updateLLMConnection is called
2104
+ let locusLLMEventListener;
2105
+ meeting.webex.internal.llm.on = sinon.stub().callsFake((eventName, callback) => {
2106
+ if (eventName === 'event:locus.state_message') {
2107
+ locusLLMEventListener = callback;
2108
+ }
2109
+ });
2110
+ meeting.webex.internal.llm.off = sinon.stub();
2111
+
2112
+ // we need the real meeting.updateLLMConnection not the mock
2113
+ meeting.updateLLMConnection.restore();
2114
+
2115
+ // Call updateLLMConnection to register the listener
2116
+ await meeting.updateLLMConnection();
2117
+
2118
+ // Verify the listener was registered and we captured it
2119
+ assert.isDefined(locusLLMEventListener, 'LLM event listener should be registered');
2120
+
2121
+ // Now trigger the event
2122
+ const eventData = {
2123
+ eventType: 'locus.state_message',
2124
+ stateElementsMessage: {
2125
+ header: {messageId: 'msg-1'},
2126
+ elements: [],
2127
+ },
2128
+ };
2129
+
2130
+ locusLLMEventListener({data: eventData});
2131
+
2132
+ assert.calledOnceWithExactly(locusInfoParseStub, meeting, eventData);
2133
+ });
2100
2134
  });
2101
2135
 
2102
2136
  describe('refreshPermissionToken', () => {
@@ -2679,7 +2713,11 @@ describe('plugin-meetings', () => {
2679
2713
  // simulate timeout waiting for the SDP answer that never comes
2680
2714
  await clock.tickAsync(ROAP_OFFER_ANSWER_EXCHANGE_TIMEOUT);
2681
2715
 
2682
- await assert.isRejected(result);
2716
+ await assert.isRejected(
2717
+ result,
2718
+ SdpResponseTimeoutError,
2719
+ 'Timed out waiting for REMOTE SDP ANSWER'
2720
+ );
2683
2721
 
2684
2722
  assert.calledOnceWithExactly(getErrorPayloadForClientErrorCodeStub, {
2685
2723
  clientErrorCode: 2007,
@@ -4087,7 +4125,7 @@ describe('plugin-meetings', () => {
4087
4125
  member2: {isInMeeting: false, isInLobby: true},
4088
4126
  member3: {isInMeeting: false, isInLobby: false},
4089
4127
  member4: {isInMeeting: true, isInLobby: false},
4090
- }
4128
+ },
4091
4129
  };
4092
4130
  sinon.stub(meeting, 'getMembers').returns({membersCollection: fakeMembersCollection});
4093
4131
  const fakeData = {intervalMetadata: {}};
@@ -4747,6 +4785,7 @@ describe('plugin-meetings', () => {
4747
4785
  id: 'fake locus from mocked join request',
4748
4786
  locusUrl: 'fake locus url',
4749
4787
  mediaId: 'fake media id',
4788
+ locus: {fullState: {}},
4750
4789
  });
4751
4790
  sinon.stub(meeting.meetingRequest, 'joinMeeting').resolves({
4752
4791
  headers: {
@@ -6739,7 +6778,7 @@ describe('plugin-meetings', () => {
6739
6778
  // Verify pstnCorrelationId was set
6740
6779
  assert.exists(meeting.pstnCorrelationId);
6741
6780
  assert.notEqual(meeting.pstnCorrelationId, meeting.correlationId);
6742
- const firstPstnCorrelationId = meeting.pstnCorrelationId
6781
+ const firstPstnCorrelationId = meeting.pstnCorrelationId;
6743
6782
 
6744
6783
  meeting.meetingRequest.dialIn.resetHistory();
6745
6784
 
@@ -6814,15 +6853,19 @@ describe('plugin-meetings', () => {
6814
6853
  assert.equal(e, error);
6815
6854
 
6816
6855
  // 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
- });
6856
+ assert.calledWith(
6857
+ Metrics.sendBehavioralMetric,
6858
+ BEHAVIORAL_METRICS.ADD_DIAL_IN_FAILURE,
6859
+ {
6860
+ correlation_id: meeting.correlationId,
6861
+ dial_in_url: meeting.dialInUrl,
6862
+ dial_in_correlation_id: sinon.match.string,
6863
+ locus_id: meeting.locusUrl.split('/').pop(),
6864
+ client_url: meeting.deviceUrl,
6865
+ reason: error.error.message,
6866
+ stack: error.stack,
6867
+ }
6868
+ );
6826
6869
 
6827
6870
  // Verify pstnCorrelationId was cleared after error
6828
6871
  assert.equal(meeting.pstnCorrelationId, undefined);
@@ -6841,15 +6884,19 @@ describe('plugin-meetings', () => {
6841
6884
  assert.equal(e, error);
6842
6885
 
6843
6886
  // 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
- });
6887
+ assert.calledWith(
6888
+ Metrics.sendBehavioralMetric,
6889
+ BEHAVIORAL_METRICS.ADD_DIAL_OUT_FAILURE,
6890
+ {
6891
+ correlation_id: meeting.correlationId,
6892
+ dial_out_url: meeting.dialOutUrl,
6893
+ dial_out_correlation_id: sinon.match.string,
6894
+ locus_id: meeting.locusUrl.split('/').pop(),
6895
+ client_url: meeting.deviceUrl,
6896
+ reason: error.error.message,
6897
+ stack: error.stack,
6898
+ }
6899
+ );
6853
6900
 
6854
6901
  // Verify pstnCorrelationId was cleared after error
6855
6902
  assert.equal(meeting.pstnCorrelationId, undefined);
@@ -6894,7 +6941,7 @@ describe('plugin-meetings', () => {
6894
6941
 
6895
6942
  // Verify that pstnCorrelationId is still cleared even when no phone connection is active
6896
6943
  assert.equal(meeting.pstnCorrelationId, undefined);
6897
- // And verify no disconnect was attempted
6944
+ // And verify no disconnect was attempted
6898
6945
  assert.notCalled(MeetingUtil.disconnectPhoneAudio);
6899
6946
  });
6900
6947
  });
@@ -10568,7 +10615,7 @@ describe('plugin-meetings', () => {
10568
10615
  describe('#setUpLocusUrlListener', () => {
10569
10616
  it('listens to the locus url update event', (done) => {
10570
10617
  const newLocusUrl = 'newLocusUrl/12345';
10571
- const payload = {url: newLocusUrl}
10618
+ const payload = {url: newLocusUrl};
10572
10619
 
10573
10620
  meeting.members = {locusUrlUpdate: sinon.stub().returns(Promise.resolve(test1))};
10574
10621
  meeting.recordingController = {setLocusUrl: sinon.stub().returns(undefined)};
@@ -10611,7 +10658,7 @@ describe('plugin-meetings', () => {
10611
10658
  });
10612
10659
  it('update mainLocusUrl for controlsOptionManager if payload.isMainLocus as true', (done) => {
10613
10660
  const newLocusUrl = 'newLocusUrl/12345';
10614
- const payload = {url: newLocusUrl, isMainLocus: true}
10661
+ const payload = {url: newLocusUrl, isMainLocus: true};
10615
10662
 
10616
10663
  meeting.controlsOptionsManager = {setLocusUrl: sinon.stub().returns(undefined)};
10617
10664
 
@@ -10843,7 +10890,9 @@ describe('plugin-meetings', () => {
10843
10890
  meeting.meetingRequest.changeMeetingFloor = sinon.stub().returns(Promise.resolve());
10844
10891
  (meeting.deviceUrl = 'deviceUrl.com'), (meeting.localShareInstanceId = '1234-5678');
10845
10892
  webex.internal.newMetrics.callDiagnosticLatencies.saveTimestamp = sinon.stub();
10846
- webex.internal.newMetrics.callDiagnosticLatencies.getShareDuration = sinon.stub().returns(1000);
10893
+ webex.internal.newMetrics.callDiagnosticLatencies.getShareDuration = sinon
10894
+ .stub()
10895
+ .returns(1000);
10847
10896
  });
10848
10897
  it('should call changeMeetingFloor()', async () => {
10849
10898
  meeting.screenShareFloorState = 'GRANTED';
@@ -11494,8 +11543,10 @@ describe('plugin-meetings', () => {
11494
11543
  canShareWhiteBoardSpy = sinon.spy(MeetingUtil, 'canShareWhiteBoard');
11495
11544
  canMoveToLobbySpy = sinon.spy(MeetingUtil, 'canMoveToLobby');
11496
11545
  showAutoEndMeetingWarningSpy = sinon.spy(MeetingUtil, 'showAutoEndMeetingWarning');
11497
- isSpokenLanguageAutoDetectionEnabledSpy = sinon.spy(MeetingUtil, 'isSpokenLanguageAutoDetectionEnabled');
11498
-
11546
+ isSpokenLanguageAutoDetectionEnabledSpy = sinon.spy(
11547
+ MeetingUtil,
11548
+ 'isSpokenLanguageAutoDetectionEnabled'
11549
+ );
11499
11550
  });
11500
11551
 
11501
11552
  afterEach(() => {
@@ -12277,16 +12328,26 @@ describe('plugin-meetings', () => {
12277
12328
  assert.notCalled(webex.internal.llm.disconnectLLM);
12278
12329
  assert.calledWith(webex.internal.llm.registerAndConnect, 'a url', 'a datachannel url');
12279
12330
  assert.equal(result, 'something');
12280
- assert.calledOnceWithExactly(
12331
+ assert.calledWithExactly(
12281
12332
  meeting.webex.internal.llm.off,
12282
12333
  'event:relay.event',
12283
12334
  meeting.processRelayEvent
12284
12335
  );
12285
- assert.calledOnceWithExactly(
12336
+ assert.calledWithExactly(
12337
+ meeting.webex.internal.llm.off,
12338
+ 'event:locus.state_message',
12339
+ meeting.processLocusLLMEvent
12340
+ );
12341
+ assert.calledWithExactly(
12286
12342
  meeting.webex.internal.llm.on,
12287
12343
  'event:relay.event',
12288
12344
  meeting.processRelayEvent
12289
12345
  );
12346
+ assert.calledWithExactly(
12347
+ meeting.webex.internal.llm.on,
12348
+ 'event:locus.state_message',
12349
+ meeting.processLocusLLMEvent
12350
+ );
12290
12351
  });
12291
12352
 
12292
12353
  it('disconnects if first if the locus url has changed', async () => {
@@ -12314,15 +12375,25 @@ describe('plugin-meetings', () => {
12314
12375
  'event:relay.event',
12315
12376
  meeting.processRelayEvent
12316
12377
  );
12317
- assert.calledTwice(meeting.webex.internal.llm.off);
12318
- assert.calledOnceWithExactly(
12378
+ assert.calledWithExactly(
12379
+ meeting.webex.internal.llm.off,
12380
+ 'event:locus.state_message',
12381
+ meeting.processLocusLLMEvent
12382
+ );
12383
+ assert.callCount(meeting.webex.internal.llm.off, 4);
12384
+ assert.calledWithExactly(
12319
12385
  meeting.webex.internal.llm.on,
12320
12386
  'event:relay.event',
12321
12387
  meeting.processRelayEvent
12322
12388
  );
12389
+ assert.calledWithExactly(
12390
+ meeting.webex.internal.llm.on,
12391
+ 'event:locus.state_message',
12392
+ meeting.processLocusLLMEvent
12393
+ );
12323
12394
  });
12324
12395
 
12325
- it('disconnects if first if the data channel url has changed', async () => {
12396
+ it('disconnects it first if the data channel url has changed', async () => {
12326
12397
  meeting.joinedWith = {state: 'JOINED'};
12327
12398
  webex.internal.llm.isConnected.returns(true);
12328
12399
  webex.internal.llm.getLocusUrl.returns('a url');
@@ -12347,12 +12418,21 @@ describe('plugin-meetings', () => {
12347
12418
  'event:relay.event',
12348
12419
  meeting.processRelayEvent
12349
12420
  );
12350
- assert.calledTwice(meeting.webex.internal.llm.off);
12351
- assert.calledOnceWithExactly(
12421
+ assert.calledWithExactly(
12422
+ meeting.webex.internal.llm.off,
12423
+ 'event:locus.state_message',
12424
+ meeting.processLocusLLMEvent
12425
+ );
12426
+ assert.calledWithExactly(
12352
12427
  meeting.webex.internal.llm.on,
12353
12428
  'event:relay.event',
12354
12429
  meeting.processRelayEvent
12355
12430
  );
12431
+ assert.calledWithExactly(
12432
+ meeting.webex.internal.llm.on,
12433
+ 'event:locus.state_message',
12434
+ meeting.processLocusLLMEvent
12435
+ );
12356
12436
  });
12357
12437
 
12358
12438
  it('disconnects when the state is not JOINED', async () => {
@@ -12367,11 +12447,16 @@ describe('plugin-meetings', () => {
12367
12447
  assert.calledWith(webex.internal.llm.disconnectLLM, undefined);
12368
12448
  assert.notCalled(webex.internal.llm.registerAndConnect);
12369
12449
  assert.equal(result, undefined);
12370
- assert.calledOnceWithExactly(
12450
+ assert.calledWithExactly(
12371
12451
  meeting.webex.internal.llm.off,
12372
12452
  'event:relay.event',
12373
12453
  meeting.processRelayEvent
12374
12454
  );
12455
+ assert.calledWithExactly(
12456
+ meeting.webex.internal.llm.off,
12457
+ 'event:locus.state_message',
12458
+ meeting.processLocusLLMEvent
12459
+ );
12375
12460
  });
12376
12461
 
12377
12462
  it('connect ps data channel if ps started in webinar', async () => {
@@ -12397,22 +12482,22 @@ describe('plugin-meetings', () => {
12397
12482
  });
12398
12483
 
12399
12484
  it('should read the locus object, set on the meeting and return null', () => {
12485
+ const dataSets = {someFakeStuff: 'dataSet'};
12486
+
12400
12487
  meeting.setLocus({
12401
12488
  mediaConnections: [test1],
12402
12489
  locusUrl: url1,
12403
12490
  locusId: uuid1,
12404
12491
  selfId: uuid2,
12405
12492
  mediaId: uuid3,
12406
- host: {id: uuid4},
12493
+ locus: {host: {id: uuid4}},
12494
+ dataSets,
12407
12495
  });
12408
12496
  assert.calledOnce(meeting.locusInfo.initialSetup);
12409
12497
  assert.calledWith(meeting.locusInfo.initialSetup, {
12410
- mediaConnections: [test1],
12411
- locusUrl: url1,
12412
- locusId: uuid1,
12413
- selfId: uuid2,
12414
- mediaId: uuid3,
12415
- host: {id: uuid4},
12498
+ trigger: 'join-response',
12499
+ locus: {host: {id: uuid4}},
12500
+ dataSets,
12416
12501
  });
12417
12502
  assert.equal(meeting.mediaConnections, test1);
12418
12503
  assert.equal(meeting.locusUrl, url1);
@@ -12502,7 +12587,9 @@ describe('plugin-meetings', () => {
12502
12587
  meeting.meetingRequest.changeMeetingFloor = sinon.stub().returns(Promise.resolve());
12503
12588
  meeting.deviceUrl = 'deviceUrl.com';
12504
12589
  webex.internal.newMetrics.callDiagnosticLatencies.saveTimestamp = sinon.stub();
12505
- webex.internal.newMetrics.callDiagnosticLatencies.getShareDuration = sinon.stub().returns(1000);
12590
+ webex.internal.newMetrics.callDiagnosticLatencies.getShareDuration = sinon
12591
+ .stub()
12592
+ .returns(1000);
12506
12593
  webex.internal.newMetrics.submitClientEvent = sinon.stub();
12507
12594
  });
12508
12595
  it('should stop the whiteboard share', async () => {
@@ -12606,7 +12693,9 @@ describe('plugin-meetings', () => {
12606
12693
  meeting.deviceUrl = 'my-web-url';
12607
12694
  meeting.locusInfo.info = {isWebinar: false};
12608
12695
  webex.internal.newMetrics.callDiagnosticLatencies.saveTimestamp = sinon.stub();
12609
- webex.internal.newMetrics.callDiagnosticLatencies.getShareDuration = sinon.stub().returns(1500);
12696
+ webex.internal.newMetrics.callDiagnosticLatencies.getShareDuration = sinon
12697
+ .stub()
12698
+ .returns(1500);
12610
12699
  webex.internal.newMetrics.submitClientEvent = sinon.stub();
12611
12700
  });
12612
12701
 
@@ -12855,8 +12944,8 @@ describe('plugin-meetings', () => {
12855
12944
 
12856
12945
  shareStatus =
12857
12946
  meeting.webinar.selfIsAttendee || meeting.guest
12858
- ? SHARE_STATUS.REMOTE_SHARE_ACTIVE
12859
- : SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE;
12947
+ ? SHARE_STATUS.REMOTE_SHARE_ACTIVE
12948
+ : SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE;
12860
12949
  }
12861
12950
 
12862
12951
  if (eventTrigger.member) {
@@ -13802,32 +13891,32 @@ describe('plugin-meetings', () => {
13802
13891
  });
13803
13892
  });
13804
13893
 
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
- };
13894
+ describe('handleShareVideoStreamMuteStateChange', () => {
13895
+ it('should emit MEETING_SHARE_VIDEO_MUTE_STATE_CHANGE event with correct fields', () => {
13896
+ meeting.isMultistream = true;
13897
+ meeting.statsAnalyzer = {shareVideoEncoderImplementation: 'OpenH264'};
13898
+ meeting.mediaProperties.shareVideoStream = {
13899
+ getSettings: sinon.stub().returns({displaySurface: 'monitor', frameRate: 30}),
13900
+ };
13812
13901
 
13813
- meeting.handleShareVideoStreamMuteStateChange(true);
13902
+ meeting.handleShareVideoStreamMuteStateChange(true);
13814
13903
 
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
- );
13904
+ assert.calledOnceWithExactly(
13905
+ Metrics.sendBehavioralMetric,
13906
+ BEHAVIORAL_METRICS.MEETING_SHARE_VIDEO_MUTE_STATE_CHANGE,
13907
+ {
13908
+ correlationId: meeting.correlationId,
13909
+ muted: true,
13910
+ encoderImplementation: 'OpenH264',
13911
+ displaySurface: 'monitor',
13912
+ isMultistream: true,
13913
+ frameRate: 30,
13914
+ }
13915
+ );
13916
+ });
13827
13917
  });
13828
13918
  });
13829
13919
  });
13830
- });
13831
13920
 
13832
13921
  describe('#startKeepAlive', () => {
13833
13922
  let clock;
@@ -15026,11 +15115,9 @@ describe('plugin-meetings', () => {
15026
15115
  assert.exists(unsetStagePromise.then);
15027
15116
  await unsetStagePromise;
15028
15117
 
15029
- assert.calledOnceWithExactly(
15030
- meeting.meetingRequest.synchronizeStage,
15031
- locusUrl,
15032
- {overrideDefault: false}
15033
- );
15118
+ assert.calledOnceWithExactly(meeting.meetingRequest.synchronizeStage, locusUrl, {
15119
+ overrideDefault: false,
15120
+ });
15034
15121
  });
15035
15122
  });
15036
15123
 
@@ -15055,7 +15142,7 @@ describe('plugin-meetings', () => {
15055
15142
  meeting.meetingInfo.siteFullUrl,
15056
15143
  meeting.locusId,
15057
15144
  meetingUuid,
15058
- displayName,
15145
+ displayName
15059
15146
  );
15060
15147
  });
15061
15148
  });
@@ -15102,10 +15189,7 @@ describe('plugin-meetings', () => {
15102
15189
  assert.exists(cancelSipCallOutPromise.then);
15103
15190
  await cancelSipCallOutPromise;
15104
15191
 
15105
- assert.calledOnceWithExactly(
15106
- meeting.meetingRequest.cancelSipCallOut,
15107
- participantId
15108
- );
15192
+ assert.calledOnceWithExactly(meeting.meetingRequest.cancelSipCallOut, participantId);
15109
15193
  });
15110
15194
  });
15111
15195
  });
@@ -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
  });