@webex/plugin-meetings 3.12.0-next.8 → 3.12.0-task-refactor.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (201) hide show
  1. package/dist/annotation/index.js +5 -14
  2. package/dist/annotation/index.js.map +1 -1
  3. package/dist/breakouts/breakout.js +1 -1
  4. package/dist/breakouts/index.js +1 -1
  5. package/dist/config.js +2 -8
  6. package/dist/config.js.map +1 -1
  7. package/dist/constants.js +6 -29
  8. package/dist/constants.js.map +1 -1
  9. package/dist/hashTree/hashTreeParser.js +29 -1563
  10. package/dist/hashTree/hashTreeParser.js.map +1 -1
  11. package/dist/hashTree/types.js +3 -13
  12. package/dist/hashTree/types.js.map +1 -1
  13. package/dist/index.js +2 -11
  14. package/dist/index.js.map +1 -1
  15. package/dist/interceptors/index.js +0 -7
  16. package/dist/interceptors/index.js.map +1 -1
  17. package/dist/interceptors/locusRouteToken.js +5 -27
  18. package/dist/interceptors/locusRouteToken.js.map +1 -1
  19. package/dist/interpretation/index.js +2 -2
  20. package/dist/interpretation/index.js.map +1 -1
  21. package/dist/interpretation/siLanguage.js +1 -1
  22. package/dist/locus-info/controlsUtils.js +3 -7
  23. package/dist/locus-info/controlsUtils.js.map +1 -1
  24. package/dist/locus-info/index.js +247 -642
  25. package/dist/locus-info/index.js.map +1 -1
  26. package/dist/locus-info/selfUtils.js +0 -1
  27. package/dist/locus-info/selfUtils.js.map +1 -1
  28. package/dist/locus-info/types.js.map +1 -1
  29. package/dist/media/MediaConnectionAwaiter.js +1 -57
  30. package/dist/media/MediaConnectionAwaiter.js.map +1 -1
  31. package/dist/media/properties.js +2 -4
  32. package/dist/media/properties.js.map +1 -1
  33. package/dist/meeting/in-meeting-actions.js +1 -7
  34. package/dist/meeting/in-meeting-actions.js.map +1 -1
  35. package/dist/meeting/index.js +1036 -1481
  36. package/dist/meeting/index.js.map +1 -1
  37. package/dist/meeting/request.js +0 -50
  38. package/dist/meeting/request.js.map +1 -1
  39. package/dist/meeting/request.type.js.map +1 -1
  40. package/dist/meeting/util.js +3 -133
  41. package/dist/meeting/util.js.map +1 -1
  42. package/dist/meetings/index.js +59 -142
  43. package/dist/meetings/index.js.map +1 -1
  44. package/dist/meetings/util.js +7 -11
  45. package/dist/meetings/util.js.map +1 -1
  46. package/dist/member/index.js +0 -10
  47. package/dist/member/index.js.map +1 -1
  48. package/dist/member/util.js +0 -10
  49. package/dist/member/util.js.map +1 -1
  50. package/dist/metrics/constants.js +1 -7
  51. package/dist/metrics/constants.js.map +1 -1
  52. package/dist/multistream/mediaRequestManager.js +60 -9
  53. package/dist/multistream/mediaRequestManager.js.map +1 -1
  54. package/dist/multistream/remoteMediaManager.js +0 -11
  55. package/dist/multistream/remoteMediaManager.js.map +1 -1
  56. package/dist/multistream/sendSlotManager.js +2 -116
  57. package/dist/multistream/sendSlotManager.js.map +1 -1
  58. package/dist/reachability/clusterReachability.js +18 -171
  59. package/dist/reachability/clusterReachability.js.map +1 -1
  60. package/dist/reachability/index.js +11 -21
  61. package/dist/reachability/index.js.map +1 -1
  62. package/dist/reachability/reachabilityPeerConnection.js +1 -1
  63. package/dist/reachability/reachabilityPeerConnection.js.map +1 -1
  64. package/dist/reactions/reactions.type.js.map +1 -1
  65. package/dist/reconnection-manager/index.js +1 -0
  66. package/dist/reconnection-manager/index.js.map +1 -1
  67. package/dist/types/common/browser-detection.d.ts +0 -1
  68. package/dist/types/common/events/events-scope.d.ts +0 -1
  69. package/dist/types/common/events/events.d.ts +0 -1
  70. package/dist/types/config.d.ts +0 -5
  71. package/dist/types/constants.d.ts +1 -24
  72. package/dist/types/hashTree/hashTreeParser.d.ts +11 -260
  73. package/dist/types/hashTree/types.d.ts +0 -20
  74. package/dist/types/index.d.ts +0 -1
  75. package/dist/types/interceptors/index.d.ts +1 -2
  76. package/dist/types/interceptors/locusRouteToken.d.ts +0 -2
  77. package/dist/types/locus-info/index.d.ts +47 -68
  78. package/dist/types/locus-info/types.d.ts +12 -28
  79. package/dist/types/media/MediaConnectionAwaiter.d.ts +1 -10
  80. package/dist/types/media/properties.d.ts +1 -2
  81. package/dist/types/meeting/in-meeting-actions.d.ts +0 -6
  82. package/dist/types/meeting/index.d.ts +7 -86
  83. package/dist/types/meeting/request.d.ts +1 -16
  84. package/dist/types/meeting/request.type.d.ts +0 -5
  85. package/dist/types/meeting/util.d.ts +0 -31
  86. package/dist/types/meeting-info/util.d.ts +0 -1
  87. package/dist/types/meeting-info/utilv2.d.ts +0 -1
  88. package/dist/types/meetings/index.d.ts +2 -4
  89. package/dist/types/member/index.d.ts +0 -1
  90. package/dist/types/member/types.d.ts +4 -4
  91. package/dist/types/member/util.d.ts +0 -5
  92. package/dist/types/metrics/constants.d.ts +0 -6
  93. package/dist/types/multistream/mediaRequestManager.d.ts +23 -0
  94. package/dist/types/multistream/sendSlotManager.d.ts +1 -23
  95. package/dist/types/reachability/clusterReachability.d.ts +3 -30
  96. package/dist/types/reactions/reactions.type.d.ts +0 -1
  97. package/dist/types/recording-controller/util.d.ts +5 -5
  98. package/dist/types/roap/index.d.ts +1 -1
  99. package/dist/webinar/index.js +163 -438
  100. package/dist/webinar/index.js.map +1 -1
  101. package/package.json +24 -26
  102. package/src/annotation/index.ts +7 -27
  103. package/src/config.ts +0 -5
  104. package/src/constants.ts +1 -30
  105. package/src/hashTree/hashTreeParser.ts +25 -1523
  106. package/src/hashTree/types.ts +1 -24
  107. package/src/index.ts +1 -8
  108. package/src/interceptors/index.ts +1 -2
  109. package/src/interceptors/locusRouteToken.ts +5 -22
  110. package/src/interpretation/index.ts +2 -2
  111. package/src/locus-info/controlsUtils.ts +0 -17
  112. package/src/locus-info/index.ts +213 -707
  113. package/src/locus-info/selfUtils.ts +0 -1
  114. package/src/locus-info/types.ts +12 -27
  115. package/src/media/MediaConnectionAwaiter.ts +1 -41
  116. package/src/media/properties.ts +1 -3
  117. package/src/meeting/in-meeting-actions.ts +0 -12
  118. package/src/meeting/index.ts +84 -461
  119. package/src/meeting/request.ts +0 -42
  120. package/src/meeting/request.type.ts +0 -6
  121. package/src/meeting/util.ts +2 -160
  122. package/src/meetings/index.ts +60 -180
  123. package/src/meetings/util.ts +9 -10
  124. package/src/member/index.ts +0 -10
  125. package/src/member/util.ts +0 -12
  126. package/src/metrics/constants.ts +0 -7
  127. package/src/multistream/mediaRequestManager.ts +54 -4
  128. package/src/multistream/remoteMediaManager.ts +0 -13
  129. package/src/multistream/sendSlotManager.ts +3 -97
  130. package/src/reachability/clusterReachability.ts +27 -153
  131. package/src/reachability/index.ts +1 -15
  132. package/src/reachability/reachabilityPeerConnection.ts +1 -3
  133. package/src/reactions/reactions.type.ts +0 -1
  134. package/src/reconnection-manager/index.ts +1 -0
  135. package/src/webinar/index.ts +6 -265
  136. package/test/unit/spec/annotation/index.ts +7 -69
  137. package/test/unit/spec/interceptors/locusRouteToken.ts +0 -44
  138. package/test/unit/spec/locus-info/controlsUtils.js +1 -56
  139. package/test/unit/spec/locus-info/index.js +90 -1457
  140. package/test/unit/spec/media/MediaConnectionAwaiter.ts +1 -41
  141. package/test/unit/spec/media/properties.ts +3 -12
  142. package/test/unit/spec/meeting/in-meeting-actions.ts +2 -8
  143. package/test/unit/spec/meeting/index.js +128 -981
  144. package/test/unit/spec/meeting/request.js +0 -70
  145. package/test/unit/spec/meeting/utils.js +26 -438
  146. package/test/unit/spec/meetings/index.js +33 -845
  147. package/test/unit/spec/meetings/utils.js +1 -51
  148. package/test/unit/spec/member/index.js +4 -28
  149. package/test/unit/spec/member/util.js +27 -65
  150. package/test/unit/spec/multistream/mediaRequestManager.ts +85 -2
  151. package/test/unit/spec/multistream/remoteMediaManager.ts +0 -30
  152. package/test/unit/spec/multistream/sendSlotManager.ts +36 -135
  153. package/test/unit/spec/reachability/clusterReachability.ts +1 -125
  154. package/test/unit/spec/reachability/index.ts +3 -26
  155. package/test/unit/spec/reconnection-manager/index.js +8 -4
  156. package/test/unit/spec/webinar/index.ts +37 -534
  157. package/dist/aiEnableRequest/index.js +0 -184
  158. package/dist/aiEnableRequest/index.js.map +0 -1
  159. package/dist/aiEnableRequest/utils.js +0 -36
  160. package/dist/aiEnableRequest/utils.js.map +0 -1
  161. package/dist/hashTree/constants.js +0 -22
  162. package/dist/hashTree/constants.js.map +0 -1
  163. package/dist/hashTree/hashTree.js +0 -533
  164. package/dist/hashTree/hashTree.js.map +0 -1
  165. package/dist/hashTree/utils.js +0 -69
  166. package/dist/hashTree/utils.js.map +0 -1
  167. package/dist/interceptors/constant.js +0 -12
  168. package/dist/interceptors/constant.js.map +0 -1
  169. package/dist/interceptors/dataChannelAuthToken.js +0 -290
  170. package/dist/interceptors/dataChannelAuthToken.js.map +0 -1
  171. package/dist/interceptors/utils.js +0 -27
  172. package/dist/interceptors/utils.js.map +0 -1
  173. package/dist/types/aiEnableRequest/index.d.ts +0 -5
  174. package/dist/types/aiEnableRequest/utils.d.ts +0 -2
  175. package/dist/types/hashTree/constants.d.ts +0 -9
  176. package/dist/types/hashTree/hashTree.d.ts +0 -136
  177. package/dist/types/hashTree/utils.d.ts +0 -22
  178. package/dist/types/interceptors/constant.d.ts +0 -5
  179. package/dist/types/interceptors/dataChannelAuthToken.d.ts +0 -43
  180. package/dist/types/interceptors/utils.d.ts +0 -1
  181. package/dist/types/webinar/utils.d.ts +0 -6
  182. package/dist/webinar/utils.js +0 -25
  183. package/dist/webinar/utils.js.map +0 -1
  184. package/src/aiEnableRequest/README.md +0 -84
  185. package/src/aiEnableRequest/index.ts +0 -170
  186. package/src/aiEnableRequest/utils.ts +0 -25
  187. package/src/hashTree/constants.ts +0 -10
  188. package/src/hashTree/hashTree.ts +0 -480
  189. package/src/hashTree/utils.ts +0 -62
  190. package/src/interceptors/constant.ts +0 -6
  191. package/src/interceptors/dataChannelAuthToken.ts +0 -170
  192. package/src/interceptors/utils.ts +0 -16
  193. package/src/webinar/utils.ts +0 -16
  194. package/test/unit/spec/aiEnableRequest/index.ts +0 -981
  195. package/test/unit/spec/aiEnableRequest/utils.ts +0 -130
  196. package/test/unit/spec/hashTree/hashTree.ts +0 -721
  197. package/test/unit/spec/hashTree/hashTreeParser.ts +0 -3670
  198. package/test/unit/spec/hashTree/utils.ts +0 -140
  199. package/test/unit/spec/interceptors/dataChannelAuthToken.ts +0 -210
  200. package/test/unit/spec/interceptors/utils.ts +0 -75
  201. package/test/unit/spec/webinar/utils.ts +0 -39
@@ -38,7 +38,6 @@ import {
38
38
  import {
39
39
  ConnectionState,
40
40
  MediaConnectionEventNames,
41
- MediaCodecMimeType,
42
41
  StatsAnalyzerEventNames,
43
42
  StatsMonitorEventNames,
44
43
  Errors,
@@ -47,7 +46,6 @@ import {
47
46
  MediaType,
48
47
  } from '@webex/internal-media-core';
49
48
  import {LocalStreamEventNames} from '@webex/media-helpers';
50
- import {CapabilityState, WebCapabilities} from '@webex/web-capabilities';
51
49
  import EventsScope from '@webex/plugin-meetings/src/common/events/events-scope';
52
50
  import Meetings, {CONSTANTS} from '@webex/plugin-meetings';
53
51
  import Meeting from '@webex/plugin-meetings/src/meeting';
@@ -83,7 +81,6 @@ import Mercury from '@webex/internal-plugin-mercury';
83
81
  import Breakouts from '@webex/plugin-meetings/src/breakouts';
84
82
  import SimultaneousInterpretation from '@webex/plugin-meetings/src/interpretation';
85
83
  import Webinar from '@webex/plugin-meetings/src/webinar';
86
- import AIEnableRequest from '@webex/plugin-meetings/src/aiEnableRequest';
87
84
  import {REACTION_RELAY_TYPES} from '../../../../src/reactions/constants';
88
85
  import locus from '../fixture/locus';
89
86
  import {
@@ -125,6 +122,7 @@ import {EVENT_TRIGGERS as VOICEAEVENTS} from '@webex/internal-plugin-voicea';
125
122
  import {createBrbState} from '@webex/plugin-meetings/src/meeting/brbState';
126
123
  import JoinForbiddenError from '../../../../src/common/errors/join-forbidden-error';
127
124
  import {EventEmitter} from 'stream';
125
+
128
126
  describe('plugin-meetings', () => {
129
127
  const logger = {
130
128
  info: () => {},
@@ -135,15 +133,11 @@ describe('plugin-meetings', () => {
135
133
  debug: () => {},
136
134
  };
137
135
 
138
- let fakeClock;
139
-
140
136
  beforeEach(() => {
141
137
  sinon.stub(Metrics, 'sendBehavioralMetric');
142
- fakeClock = sinon.useFakeTimers();
143
138
  });
144
139
  afterEach(() => {
145
140
  sinon.restore();
146
- fakeClock.restore();
147
141
  });
148
142
 
149
143
  before(() => {
@@ -266,9 +260,7 @@ describe('plugin-meetings', () => {
266
260
  stopReachability: sinon.stub(),
267
261
  isSubnetReachable: sinon.stub().returns(true),
268
262
  };
269
- webex.internal.llm.isDataChannelTokenEnabled = sinon.stub().resolves(false);
270
263
  webex.internal.llm.on = sinon.stub();
271
- webex.internal.voicea.announce = sinon.stub();
272
264
  webex.internal.newMetrics.callDiagnosticLatencies = new CallDiagnosticLatencies(
273
265
  {},
274
266
  {parent: webex}
@@ -379,7 +371,6 @@ describe('plugin-meetings', () => {
379
371
  assert.instanceOf(meeting.breakouts, Breakouts);
380
372
  assert.instanceOf(meeting.simultaneousInterpretation, SimultaneousInterpretation);
381
373
  assert.instanceOf(meeting.webinar, Webinar);
382
- assert.instanceOf(meeting.aiEnableRequest, AIEnableRequest);
383
374
  });
384
375
 
385
376
  it('should call the callback with the meeting that has id already set', () => {
@@ -739,13 +730,8 @@ describe('plugin-meetings', () => {
739
730
  let handleTurnDiscoveryHttpResponseStub;
740
731
  let abortTurnDiscoveryStub;
741
732
  let addMediaInternalStub;
742
- let supportsRTCPeerConnectionStub;
743
733
 
744
734
  beforeEach(() => {
745
- supportsRTCPeerConnectionStub = sinon
746
- .stub(WebCapabilities, 'supportsRTCPeerConnection')
747
- .returns(CapabilityState.CAPABLE);
748
-
749
735
  meeting.join = sinon.stub().callsFake((joinOptions) => {
750
736
  meeting.isMultistream = joinOptions.enableMultistream;
751
737
  return Promise.resolve(fakeJoinResult);
@@ -1017,55 +1003,6 @@ describe('plugin-meetings', () => {
1017
1003
  );
1018
1004
  });
1019
1005
 
1020
- it('should call leave() if addMediaInternal() fails with a browser media error (TypeError)', async () => {
1021
- const addMediaError = new Error('fake addMedia error');
1022
- addMediaError.name = 'TypeError'; // This makes it a browser media error
1023
-
1024
- const leaveStub = sinon.stub(meeting, 'leave').resolves();
1025
- meeting.addMediaInternal = sinon.stub().rejects(addMediaError);
1026
-
1027
- // When a browser media error occurs, it gets transformed into a special structure
1028
- const rejectedError = await assert.isRejected(
1029
- meeting.joinWithMedia({
1030
- joinOptions,
1031
- mediaOptions,
1032
- })
1033
- );
1034
-
1035
- // Verify the error was transformed with errorCode 2729
1036
- assert.equal(rejectedError.error.body.errorCode, 2729);
1037
- assert.equal(rejectedError.error.body.message, 'fake addMedia error');
1038
- assert.equal(rejectedError.error.body.name, 'TypeError');
1039
-
1040
- assert.calledOnce(meeting.join);
1041
- assert.calledOnce(meeting.addMediaInternal);
1042
- assert.calledOnce(leaveStub);
1043
- assert.calledOnceWithExactly(leaveStub, {
1044
- resourceId: undefined,
1045
- reason: 'joinWithMedia failure',
1046
- });
1047
-
1048
- // Browser media errors don't retry, so behavioral metric is sent only once
1049
- // NOTE: The error gets transformed, so the metric receives undefined for message/stack/name
1050
- // because they're now nested in error.body instead of at the top level
1051
- assert.calledOnce(Metrics.sendBehavioralMetric);
1052
- assert.calledWith(
1053
- Metrics.sendBehavioralMetric,
1054
- BEHAVIORAL_METRICS.JOIN_WITH_MEDIA_FAILURE,
1055
- {
1056
- correlation_id: meeting.correlationId,
1057
- locus_id: meeting.locusUrl.split('/').pop(),
1058
- reason: undefined, // transformed error doesn't have .message at top level
1059
- stack: undefined, // transformed error doesn't have .stack at top level
1060
- leaveErrorReason: undefined,
1061
- isRetry: false,
1062
- },
1063
- {
1064
- type: undefined, // transformed error doesn't have .name at top level
1065
- }
1066
- );
1067
- });
1068
-
1069
1006
  it('should not call leave() if addMediaInternal() fails the first time and succeeds the second time and should only call join() once', async () => {
1070
1007
  const addMediaError = new Error('fake addMedia error');
1071
1008
  const leaveStub = sinon.stub(meeting, 'leave');
@@ -1274,54 +1211,43 @@ describe('plugin-meetings', () => {
1274
1211
  await assert.isRejected(result);
1275
1212
  });
1276
1213
 
1277
- [
1278
- {
1279
- errorName: 'SdpOfferCreationError',
1280
- description: 'if we fail to create the offer on first attempt',
1281
- },
1282
- {
1283
- errorName: 'WebrtcApiNotAvailableError',
1284
- description: 'if RTCPeerConnection is not available',
1285
- },
1286
- ].forEach(({errorName, description}) => {
1287
- it(`should not attempt a retry ${description}`, async () => {
1288
- const addMediaError = new Error('fake addMedia error');
1289
- addMediaError.name = errorName;
1214
+ it('should not attempt a retry if we fail to create the offer on first atttempt', async () => {
1215
+ const addMediaError = new Error('fake addMedia error');
1216
+ addMediaError.name = 'SdpOfferCreationError';
1290
1217
 
1291
- meeting.addMediaInternal.rejects(addMediaError);
1292
- sinon.stub(meeting, 'leave').resolves();
1218
+ meeting.addMediaInternal.rejects(addMediaError);
1219
+ sinon.stub(meeting, 'leave').resolves();
1293
1220
 
1294
- await assert.isRejected(
1295
- meeting.joinWithMedia({
1296
- joinOptions,
1297
- mediaOptions,
1298
- }),
1299
- addMediaError
1300
- );
1221
+ await assert.isRejected(
1222
+ meeting.joinWithMedia({
1223
+ joinOptions,
1224
+ mediaOptions,
1225
+ }),
1226
+ addMediaError
1227
+ );
1301
1228
 
1302
- // check that only 1 attempt was done
1303
- assert.calledOnce(meeting.join);
1304
- assert.calledOnce(meeting.addMediaInternal);
1305
- assert.calledOnce(Metrics.sendBehavioralMetric);
1306
- assert.calledWith(
1307
- Metrics.sendBehavioralMetric.firstCall,
1308
- BEHAVIORAL_METRICS.JOIN_WITH_MEDIA_FAILURE,
1309
- {
1310
- correlation_id: meeting.correlationId,
1311
- locus_id: meeting.locusUrl.split('/').pop(),
1312
- reason: addMediaError.message,
1313
- stack: addMediaError.stack,
1314
- leaveErrorReason: undefined,
1315
- isRetry: false,
1316
- },
1317
- {
1318
- type: addMediaError.name,
1319
- }
1320
- );
1321
- assert.calledOnceWithExactly(meeting.leave, {
1322
- resourceId: undefined,
1323
- reason: 'joinWithMedia failure',
1324
- });
1229
+ // check that only 1 attempt was done
1230
+ assert.calledOnce(meeting.join);
1231
+ assert.calledOnce(meeting.addMediaInternal);
1232
+ assert.calledOnce(Metrics.sendBehavioralMetric);
1233
+ assert.calledWith(
1234
+ Metrics.sendBehavioralMetric.firstCall,
1235
+ BEHAVIORAL_METRICS.JOIN_WITH_MEDIA_FAILURE,
1236
+ {
1237
+ correlation_id: meeting.correlationId,
1238
+ locus_id: meeting.locusUrl.split('/').pop(),
1239
+ reason: addMediaError.message,
1240
+ stack: addMediaError.stack,
1241
+ leaveErrorReason: undefined,
1242
+ isRetry: false,
1243
+ },
1244
+ {
1245
+ type: addMediaError.name,
1246
+ }
1247
+ );
1248
+ assert.calledOnceWithExactly(meeting.leave, {
1249
+ resourceId: undefined,
1250
+ reason: 'joinWithMedia failure',
1325
1251
  });
1326
1252
  });
1327
1253
 
@@ -1390,21 +1316,6 @@ describe('plugin-meetings', () => {
1390
1316
  })
1391
1317
  );
1392
1318
  });
1393
-
1394
- it('should throw immediately if RTCPeerConnection is not available', async () => {
1395
- supportsRTCPeerConnectionStub.returns(CapabilityState.NOT_CAPABLE);
1396
-
1397
- await assert.isRejected(
1398
- meeting.joinWithMedia({
1399
- joinOptions,
1400
- mediaOptions,
1401
- }),
1402
- Errors.WebrtcApiNotAvailableError
1403
- );
1404
-
1405
- assert.notCalled(meeting.join);
1406
- assert.notCalled(meeting.addMediaInternal);
1407
- });
1408
1319
  });
1409
1320
  describe('#isTranscriptionSupported', () => {
1410
1321
  it('should return false if the feature is not supported for the meeting', () => {
@@ -1419,25 +1330,6 @@ describe('plugin-meetings', () => {
1419
1330
  });
1420
1331
  });
1421
1332
 
1422
- describe('#update hesiod llm id', () => {
1423
- beforeEach(() => {
1424
- webex.internal.voicea.onCaptionServiceIdUpdate = sinon.stub();
1425
- });
1426
- afterEach(() => {
1427
- // Restore the original methods after each test
1428
- sinon.restore();
1429
- });
1430
- it('should call voicea.onCaptionServiceIdUpdate when joined', async () => {
1431
- meeting.joinedWith = {state: 'JOINED'};
1432
- await meeting.locusInfo.emitScoped(
1433
- {function: 'test', file: 'test'},
1434
- LOCUSINFO.EVENTS.CONTROLS_MEETING_HESIOD_LLM_ID_UPDATED,
1435
- {hesiodLlmId: '123a-456b-789c'}
1436
- );
1437
- assert.calledWith(webex.internal.voicea.onCaptionServiceIdUpdate, '123a-456b-789c');
1438
- });
1439
- });
1440
-
1441
1333
  describe('#update spoken language', () => {
1442
1334
  beforeEach(() => {
1443
1335
  webex.internal.voicea.onSpokenLanguageUpdate = sinon.stub();
@@ -1553,22 +1445,6 @@ describe('plugin-meetings', () => {
1553
1445
  EVENT_TRIGGERS.MEETING_STOPPED_RECEIVING_TRANSCRIPTION
1554
1446
  );
1555
1447
  });
1556
-
1557
- it('should stop listening to voicea events even when transcription is undefined', () => {
1558
- meeting.transcription = undefined;
1559
- meeting.stopTranscription();
1560
- assert.equal(webex.internal.voicea.off.callCount, 4);
1561
- assert.equal(meeting.areVoiceaEventsSetup, false);
1562
- assert.calledWith(
1563
- TriggerProxy.trigger,
1564
- sinon.match.instanceOf(Meeting),
1565
- {
1566
- file: 'meeting/index',
1567
- function: 'triggerStopReceivingTranscriptionEvent',
1568
- },
1569
- EVENT_TRIGGERS.MEETING_STOPPED_RECEIVING_TRANSCRIPTION
1570
- );
1571
- });
1572
1448
  });
1573
1449
 
1574
1450
  describe('#setCaptionLanguage', () => {
@@ -1930,64 +1806,16 @@ describe('plugin-meetings', () => {
1930
1806
  fakeProcessedReaction
1931
1807
  );
1932
1808
  });
1933
-
1934
- it('should process if participantId does not exist in membersCollection but has displayName in Webinar', () => {
1935
- LoggerProxy.logger.warn = sinon.stub();
1936
- meeting.isReactionsSupported = sinon.stub().returns(true);
1937
- meeting.config.receiveReactions = true;
1938
- meeting.locusInfo.info = {isWebinar: true};
1939
- const fakeSendersName = 'Fake reactors name';
1940
- const fakeReactionPayload = {
1941
- type: 'fake_type',
1942
- codepoints: 'fake_codepoints',
1943
- shortcodes: 'fake_shortcodes',
1944
- tone: {
1945
- type: 'fake_tone_type',
1946
- codepoints: 'fake_tone_codepoints',
1947
- shortcodes: 'fake_tone_shortcodes',
1948
- },
1949
- };
1950
- const fakeSenderPayload = {
1951
- displayName: 'Fake reactors name',
1952
- participantId: 'fake_participant_id',
1953
- };
1954
- const fakeProcessedReaction = {
1955
- reaction: fakeReactionPayload,
1956
- sender: {
1957
- id: fakeSenderPayload.participantId,
1958
- name: fakeSendersName,
1959
- },
1960
- };
1961
- const fakeRelayEvent = {
1962
- data: {
1963
- relayType: REACTION_RELAY_TYPES.REACTION,
1964
- reaction: fakeReactionPayload,
1965
- sender: fakeSenderPayload,
1966
- },
1967
- };
1968
- meeting.processRelayEvent(fakeRelayEvent);
1969
- assert.calledWith(
1970
- TriggerProxy.trigger,
1971
- sinon.match.instanceOf(Meeting),
1972
- {
1973
- file: 'meeting/index',
1974
- function: 'join',
1975
- },
1976
- EVENT_TRIGGERS.MEETING_RECEIVE_REACTIONS,
1977
- fakeProcessedReaction
1978
- );
1979
- });
1980
1809
  });
1981
1810
 
1982
1811
  describe('#handleLLMOnline', () => {
1983
1812
  beforeEach(() => {
1984
1813
  webex.internal.llm.off = sinon.stub();
1985
- webex.internal.voicea.getIsCaptionBoxOn = sinon.stub().returns(false);
1986
- webex.internal.voicea.updateSubchannelSubscriptions = sinon.stub();
1987
1814
  });
1988
1815
 
1989
- it('emits transcription connected events', () => {
1816
+ it('turns off llm online, emits transcription connected events', () => {
1990
1817
  meeting.handleLLMOnline();
1818
+ assert.calledOnceWithExactly(webex.internal.llm.off, 'online', meeting.handleLLMOnline);
1991
1819
  assert.calledWith(
1992
1820
  TriggerProxy.trigger,
1993
1821
  sinon.match.instanceOf(Meeting),
@@ -1998,24 +1826,6 @@ describe('plugin-meetings', () => {
1998
1826
  EVENT_TRIGGERS.MEETING_TRANSCRIPTION_CONNECTED
1999
1827
  );
2000
1828
  });
2001
-
2002
- it('restores transcription subscription when caption intent is enabled', () => {
2003
- webex.internal.voicea.getIsCaptionBoxOn.returns(true);
2004
-
2005
- meeting.handleLLMOnline();
2006
-
2007
- assert.calledOnceWithExactly(webex.internal.voicea.updateSubchannelSubscriptions, {
2008
- subscribe: ['transcription'],
2009
- });
2010
- });
2011
-
2012
- it('does not restore transcription subscription when caption intent is disabled', () => {
2013
- webex.internal.voicea.getIsCaptionBoxOn.returns(false);
2014
-
2015
- meeting.handleLLMOnline();
2016
-
2017
- assert.notCalled(webex.internal.voicea.updateSubchannelSubscriptions);
2018
- });
2019
1829
  });
2020
1830
 
2021
1831
  describe('#join', () => {
@@ -2035,7 +1845,6 @@ describe('plugin-meetings', () => {
2035
1845
  it('should have #join', () => {
2036
1846
  assert.exists(meeting.join);
2037
1847
  });
2038
-
2039
1848
  beforeEach(() => {
2040
1849
  setCorrelationIdSpy = sinon.spy(meeting, 'setCorrelationId');
2041
1850
  meeting.setLocus = sinon.stub().returns(true);
@@ -2189,6 +1998,7 @@ describe('plugin-meetings', () => {
2189
1998
  await meeting.join().catch(() => {
2190
1999
  assert.calledOnce(MeetingUtil.joinMeeting);
2191
2000
 
2001
+ // Assert that client.locus.join.response error event is not sent from this function, it is now emitted from MeetingUtil.joinMeeting
2192
2002
  assert.calledOnce(webex.internal.newMetrics.submitClientEvent);
2193
2003
  assert.calledWithMatch(webex.internal.newMetrics.submitClientEvent, {
2194
2004
  name: 'client.call.initiated',
@@ -2220,7 +2030,6 @@ describe('plugin-meetings', () => {
2220
2030
  });
2221
2031
  });
2222
2032
  });
2223
-
2224
2033
  describe('lmm, transcription & permissionTokenRefresh decoupling', () => {
2225
2034
  beforeEach(() => {
2226
2035
  sandbox.stub(MeetingUtil, 'joinMeeting').returns(Promise.resolve(joinMeetingResult));
@@ -2291,6 +2100,7 @@ describe('plugin-meetings', () => {
2291
2100
  const locusInfoParseStub = sinon.stub(meeting.locusInfo, 'parse');
2292
2101
  sinon.stub(meeting, 'isJoined').returns(true);
2293
2102
 
2103
+ // Set up llm.on stub to capture the registered listener when updateLLMConnection is called
2294
2104
  let locusLLMEventListener;
2295
2105
  meeting.webex.internal.llm.on = sinon.stub().callsFake((eventName, callback) => {
2296
2106
  if (eventName === 'event:locus.state_message') {
@@ -2299,12 +2109,16 @@ describe('plugin-meetings', () => {
2299
2109
  });
2300
2110
  meeting.webex.internal.llm.off = sinon.stub();
2301
2111
 
2112
+ // we need the real meeting.updateLLMConnection not the mock
2302
2113
  meeting.updateLLMConnection.restore();
2303
2114
 
2115
+ // Call updateLLMConnection to register the listener
2304
2116
  await meeting.updateLLMConnection();
2305
2117
 
2118
+ // Verify the listener was registered and we captured it
2306
2119
  assert.isDefined(locusLLMEventListener, 'LLM event listener should be registered');
2307
2120
 
2121
+ // Now trigger the event
2308
2122
  const eventData = {
2309
2123
  eventType: 'locus.state_message',
2310
2124
  stateElementsMessage: {
@@ -2317,66 +2131,6 @@ describe('plugin-meetings', () => {
2317
2131
 
2318
2132
  assert.calledOnceWithExactly(locusInfoParseStub, meeting, eventData);
2319
2133
  });
2320
-
2321
- it('UpdateLLMConnection sends a metric if not connected after timeout', async () => {
2322
- sinon.stub(meeting, 'isJoined').returns(true);
2323
- sinon.stub(meeting.webex.internal.llm, 'isConnected').returns(false);
2324
- sinon.stub(meeting.webex.internal.llm, 'hasEverConnected').value(true);
2325
- sinon.stub(meeting.webex.internal.llm, 'registerAndConnect').resolves({});
2326
-
2327
- meeting.updateLLMConnection.restore();
2328
-
2329
- await meeting.updateLLMConnection();
2330
-
2331
- fakeClock.tick(3 * 60 * 1000);
2332
-
2333
- assert.calledWith(
2334
- Metrics.sendBehavioralMetric,
2335
- BEHAVIORAL_METRICS.LLM_HEALTHCHECK_FAILURE,
2336
- {
2337
- correlation_id: meeting.correlationId,
2338
- hasEverConnected: true,
2339
- }
2340
- );
2341
- });
2342
-
2343
- it('clears the LLM health check timer when disconnecting LLM', async () => {
2344
- const isJoinedStub = sinon.stub(meeting, 'isJoined');
2345
- sinon.stub(meeting.webex.internal.llm, 'isConnected');
2346
- sinon.stub(meeting.webex.internal.llm, 'disconnectLLM').resolves();
2347
- sinon.stub(meeting.webex.internal.llm, 'registerAndConnect').resolves({});
2348
- sinon
2349
- .stub(meeting.webex.internal.llm, 'getLocusUrl')
2350
- .returns('https://locus1.example.com');
2351
- sinon
2352
- .stub(meeting.webex.internal.llm, 'getDatachannelUrl')
2353
- .returns('https://datachannel1.example.com');
2354
-
2355
- meeting.updateLLMConnection.restore();
2356
-
2357
- isJoinedStub.returns(true);
2358
- meeting.webex.internal.llm.isConnected.returns(false);
2359
- await meeting.updateLLMConnection();
2360
-
2361
- assert.exists(meeting.llmHealthCheckTimer);
2362
-
2363
- isJoinedStub.returns(false);
2364
- meeting.webex.internal.llm.isConnected.returns(true);
2365
-
2366
- await meeting.updateLLMConnection();
2367
-
2368
- assert.calledOnce(meeting.webex.internal.llm.disconnectLLM);
2369
-
2370
- assert.isUndefined(meeting.llmHealthCheckTimer);
2371
-
2372
- Metrics.sendBehavioralMetric.resetHistory();
2373
- fakeClock.tick(3 * 60 * 1000);
2374
-
2375
- assert.neverCalledWith(
2376
- Metrics.sendBehavioralMetric,
2377
- BEHAVIORAL_METRICS.LLM_HEALTHCHECK_FAILURE
2378
- );
2379
- });
2380
2134
  });
2381
2135
 
2382
2136
  describe('refreshPermissionToken', () => {
@@ -2403,6 +2157,7 @@ describe('plugin-meetings', () => {
2403
2157
  .stub()
2404
2158
  .rejects(new CaptchaError('bad captcha'));
2405
2159
  const stateMachineFailSpy = sinon.spy(meeting.meetingFiniteStateMachine, 'fail');
2160
+ const joinMeetingOptionsSpy = sinon.spy(MeetingUtil, 'joinMeetingOptions');
2406
2161
 
2407
2162
  try {
2408
2163
  await meeting.join();
@@ -2416,7 +2171,8 @@ describe('plugin-meetings', () => {
2416
2171
  );
2417
2172
  assert.instanceOf(error, CaptchaError);
2418
2173
  assert.equal(error.message, 'bad captcha');
2419
- assert.notCalled(MeetingUtil.joinMeeting);
2174
+ // should not get to the end promise chain, which does do the join
2175
+ assert.notCalled(joinMeetingOptionsSpy);
2420
2176
  }
2421
2177
  });
2422
2178
 
@@ -2425,6 +2181,7 @@ describe('plugin-meetings', () => {
2425
2181
  .stub()
2426
2182
  .rejects(new PasswordError('bad password'));
2427
2183
  const stateMachineFailSpy = sinon.spy(meeting.meetingFiniteStateMachine, 'fail');
2184
+ const joinMeetingOptionsSpy = sinon.spy(MeetingUtil.joinMeetingOptions);
2428
2185
 
2429
2186
  try {
2430
2187
  await meeting.join();
@@ -2438,7 +2195,8 @@ describe('plugin-meetings', () => {
2438
2195
  );
2439
2196
  assert.instanceOf(error, PasswordError);
2440
2197
  assert.equal(error.message, 'bad password');
2441
- assert.notCalled(MeetingUtil.joinMeeting);
2198
+ // should not get to the end promise chain, which does do the join
2199
+ assert.notCalled(joinMeetingOptionsSpy);
2442
2200
  }
2443
2201
  });
2444
2202
 
@@ -2447,6 +2205,7 @@ describe('plugin-meetings', () => {
2447
2205
  .stub()
2448
2206
  .rejects(new PermissionError('bad permission'));
2449
2207
  const stateMachineFailSpy = sinon.spy(meeting.meetingFiniteStateMachine, 'fail');
2208
+ const joinMeetingOptionsSpy = sinon.spy(MeetingUtil.joinMeetingOptions);
2450
2209
 
2451
2210
  try {
2452
2211
  await meeting.join();
@@ -2460,14 +2219,14 @@ describe('plugin-meetings', () => {
2460
2219
  );
2461
2220
  assert.instanceOf(error, PermissionError);
2462
2221
  assert.equal(error.message, 'bad permission');
2463
- assert.notCalled(MeetingUtil.joinMeeting);
2222
+ // should not get to the end promise chain, which does do the join
2223
+ assert.notCalled(joinMeetingOptionsSpy);
2464
2224
  }
2465
2225
  });
2466
2226
  });
2467
2227
  });
2468
2228
  });
2469
2229
 
2470
-
2471
2230
  describe('#addMedia', () => {
2472
2231
  const muteStateStub = {
2473
2232
  handleClientRequest: sinon.stub().returns(Promise.resolve(true)),
@@ -3124,111 +2883,6 @@ describe('plugin-meetings', () => {
3124
2883
  checkWorking({allowMediaInLobby: true});
3125
2884
  });
3126
2885
 
3127
- const setupLobbyTest = () => {
3128
- meeting.roap.doTurnDiscovery = sinon
3129
- .stub()
3130
- .resolves({turnServerInfo: undefined, turnDiscoverySkippedReason: undefined});
3131
-
3132
- meeting.meetingState = 'ACTIVE';
3133
- meeting.locusInfo.parsedLocus = {self: {state: 'IDLE'}};
3134
- meeting.isUserUnadmitted = true;
3135
-
3136
- // Mock locusMediaRequest
3137
- meeting.locusMediaRequest = {
3138
- send: sinon.stub().resolves(),
3139
- isConfluenceCreated: sinon.stub().returns(false),
3140
- };
3141
-
3142
- sinon.stub(RemoteMediaManagerModule, 'RemoteMediaManager').returns({
3143
- start: sinon.stub().resolves(),
3144
- on: sinon.stub(),
3145
- logAllReceiveSlots: sinon.stub(),
3146
- });
3147
-
3148
- meeting.isMultistream = true;
3149
-
3150
- const createFakeStream = (id) => ({
3151
- on: sinon.stub(),
3152
- off: sinon.stub(),
3153
- userMuted: false,
3154
- systemMuted: false,
3155
- get muted() {
3156
- return this.userMuted || this.systemMuted;
3157
- },
3158
- setUnmuteAllowed: sinon.stub(),
3159
- setUserMuted: sinon.stub(),
3160
- outputStream: {
3161
- getTracks: () => [{id}],
3162
- },
3163
- getSettings: sinon.stub().returns({}),
3164
- });
3165
-
3166
- return {
3167
- fakeMicrophoneStream: createFakeStream('fake mic'),
3168
- fakeCameraStream: createFakeStream('fake camera'),
3169
- };
3170
- };
3171
-
3172
- it('should not publish any local streams when in the lobby and allowPublishMediaInLobby is false', async () => {
3173
- const {fakeMicrophoneStream, fakeCameraStream} = setupLobbyTest();
3174
-
3175
- const publishStreamStub = sinon.stub();
3176
- fakeMediaConnection.createSendSlot = sinon.stub().returns({
3177
- publishStream: publishStreamStub,
3178
- unpublishStream: sinon.stub(),
3179
- setNamedMediaGroups: sinon.stub(),
3180
- });
3181
-
3182
- await meeting.addMedia({
3183
- allowMediaInLobby: true,
3184
- allowPublishMediaInLobby: false,
3185
- audioEnabled: true,
3186
- videoEnabled: true,
3187
- localStreams: {
3188
- microphone: fakeMicrophoneStream,
3189
- camera: fakeCameraStream,
3190
- },
3191
- });
3192
-
3193
- assert.notCalled(publishStreamStub);
3194
- });
3195
-
3196
- it('should publish local streams when in the lobby and allowPublishMediaInLobby is true', async () => {
3197
- const {fakeMicrophoneStream, fakeCameraStream} = setupLobbyTest();
3198
-
3199
- const audioSlot = {
3200
- publishStream: sinon.stub(),
3201
- unpublishStream: sinon.stub(),
3202
- setNamedMediaGroups: sinon.stub(),
3203
- };
3204
- const videoSlot = {
3205
- publishStream: sinon.stub(),
3206
- unpublishStream: sinon.stub(),
3207
- setNamedMediaGroups: sinon.stub(),
3208
- };
3209
-
3210
- fakeMediaConnection.createSendSlot = sinon.stub().callsFake((mediaType) => {
3211
- if (mediaType === 'AUDIO-MAIN') {
3212
- return audioSlot;
3213
- }
3214
- return videoSlot;
3215
- });
3216
-
3217
- await meeting.addMedia({
3218
- allowMediaInLobby: true,
3219
- allowPublishMediaInLobby: true,
3220
- audioEnabled: true,
3221
- videoEnabled: true,
3222
- localStreams: {
3223
- microphone: fakeMicrophoneStream,
3224
- camera: fakeCameraStream,
3225
- },
3226
- });
3227
-
3228
- assert.calledOnceWithExactly(audioSlot.publishStream, fakeMicrophoneStream);
3229
- assert.calledOnceWithExactly(videoSlot.publishStream, fakeCameraStream);
3230
- });
3231
-
3232
2886
  it('should create rtcMetrics and pass them to Media.createMediaConnection()', async () => {
3233
2887
  const setIntervalOriginal = window.setInterval;
3234
2888
  window.setInterval = sinon.stub().returns(1);
@@ -6419,10 +6073,7 @@ describe('plugin-meetings', () => {
6419
6073
  meeting.statsAnalyzer = {stopAnalyzer: sinon.stub().resolves()};
6420
6074
  meeting.unsetPeerConnections = sinon.stub().returns(true);
6421
6075
  meeting.logger.error = sinon.stub().returns(true);
6422
- meeting.clearMeetingData = sinon.stub().callsFake(async () => {
6423
- meeting.audio = null;
6424
- meeting.video = null;
6425
- });
6076
+ meeting.updateLLMConnection = sinon.stub().returns(Promise.resolve());
6426
6077
  webex.internal.voicea.off = sinon.stub().returns(true);
6427
6078
  meeting.stopTranscription = sinon.stub();
6428
6079
  meeting.transcription = {};
@@ -6449,7 +6100,9 @@ describe('plugin-meetings', () => {
6449
6100
  assert.calledOnce(meeting.closePeerConnections);
6450
6101
  assert.calledOnce(meeting.unsetRemoteStreams);
6451
6102
  assert.calledOnce(meeting.unsetPeerConnections);
6452
- assert.calledOnce(meeting.clearMeetingData);
6103
+ assert.calledOnce(meeting.stopTranscription);
6104
+ assert.calledOnce(meeting.annotation.deregisterEvents);
6105
+ assert.calledWith(webex.internal.llm.off, 'event:relay.event', meeting.processRelayEvent);
6453
6106
  });
6454
6107
 
6455
6108
  it('should reset call diagnostic latencies correctly', async () => {
@@ -8450,10 +8103,7 @@ describe('plugin-meetings', () => {
8450
8103
  meeting.statsAnalyzer = {stopAnalyzer: sinon.stub().resolves()};
8451
8104
  meeting.unsetPeerConnections = sinon.stub().returns(true);
8452
8105
  meeting.logger.error = sinon.stub().returns(true);
8453
- meeting.clearMeetingData = sinon.stub().callsFake(async () => {
8454
- meeting.audio = null;
8455
- meeting.video = null;
8456
- });
8106
+ meeting.updateLLMConnection = sinon.stub().returns(Promise.resolve());
8457
8107
  meeting.transcription = {};
8458
8108
  meeting.stopTranscription = sinon.stub();
8459
8109
 
@@ -8479,7 +8129,10 @@ describe('plugin-meetings', () => {
8479
8129
  assert.calledOnce(meeting?.closePeerConnections);
8480
8130
  assert.calledOnce(meeting?.unsetRemoteStreams);
8481
8131
  assert.calledOnce(meeting?.unsetPeerConnections);
8482
- assert.calledOnce(meeting?.clearMeetingData);
8132
+ assert.calledOnce(meeting?.stopTranscription);
8133
+
8134
+ assert.called(meeting.annotation.deregisterEvents);
8135
+ assert.calledWith(webex.internal.llm.off, 'event:relay.event', meeting.processRelayEvent);
8483
8136
  });
8484
8137
  });
8485
8138
 
@@ -9216,8 +8869,8 @@ describe('plugin-meetings', () => {
9216
8869
  const fakeMultistreamRoapMediaConnection = {
9217
8870
  createSendSlot: () => {
9218
8871
  return {
9219
- setCustomCodecParameters: sinon.stub().resolves(),
9220
- markCustomCodecParametersForDeletion: sinon.stub().resolves(),
8872
+ setCodecParameters: sinon.stub().resolves(),
8873
+ deleteCodecParameters: sinon.stub().resolves(),
9221
8874
  };
9222
8875
  },
9223
8876
  };
@@ -9240,29 +8893,27 @@ describe('plugin-meetings', () => {
9240
8893
  }
9241
8894
  );
9242
8895
 
9243
- it('should set custom codec parameters when shouldEnableMusicMode is true', async () => {
8896
+ it('should set the codec parameters when shouldEnableMusicMode is true', async () => {
9244
8897
  await meeting.enableMusicMode(true);
9245
8898
  assert.calledOnceWithExactly(
9246
- meeting.sendSlotManager.getSlot(MediaType.AudioMain).setCustomCodecParameters,
9247
- MediaCodecMimeType.OPUS,
8899
+ meeting.sendSlotManager.getSlot(MediaType.AudioMain).setCodecParameters,
9248
8900
  {
9249
8901
  maxaveragebitrate: '64000',
9250
8902
  maxplaybackrate: '48000',
9251
8903
  }
9252
8904
  );
9253
8905
  assert.notCalled(
9254
- meeting.sendSlotManager.getSlot(MediaType.AudioMain).markCustomCodecParametersForDeletion
8906
+ meeting.sendSlotManager.getSlot(MediaType.AudioMain).deleteCodecParameters
9255
8907
  );
9256
8908
  });
9257
8909
 
9258
- it('should mark custom codec parameters for deletion when shouldEnableMusicMode is false', async () => {
8910
+ it('should set the codec parameters when shouldEnableMusicMode is false', async () => {
9259
8911
  await meeting.enableMusicMode(false);
9260
8912
  assert.calledOnceWithExactly(
9261
- meeting.sendSlotManager.getSlot(MediaType.AudioMain).markCustomCodecParametersForDeletion,
9262
- MediaCodecMimeType.OPUS,
8913
+ meeting.sendSlotManager.getSlot(MediaType.AudioMain).deleteCodecParameters,
9263
8914
  ['maxaveragebitrate', 'maxplaybackrate']
9264
8915
  );
9265
- assert.notCalled(meeting.sendSlotManager.getSlot(MediaType.AudioMain).setCustomCodecParameters);
8916
+ assert.notCalled(meeting.sendSlotManager.getSlot(MediaType.AudioMain).setCodecParameters);
9266
8917
  });
9267
8918
  });
9268
8919
 
@@ -9353,10 +9004,7 @@ describe('plugin-meetings', () => {
9353
9004
 
9354
9005
  // check that the right things were called by the callback
9355
9006
  assert.calledOnceWithExactly(meeting.waitForRemoteSDPAnswer);
9356
- assert.calledOnceWithExactly(
9357
- meeting.mediaProperties.waitForMediaConnectionConnected,
9358
- meeting.correlationId
9359
- );
9007
+ assert.calledOnceWithExactly(meeting.mediaProperties.waitForMediaConnectionConnected);
9360
9008
  });
9361
9009
  });
9362
9010
 
@@ -10417,24 +10065,14 @@ describe('plugin-meetings', () => {
10417
10065
  );
10418
10066
  done();
10419
10067
  });
10420
- it('listens to the self admitted guest event without blocking on token prefetch', async () => {
10068
+ it('listens to the self admitted guest event', (done) => {
10421
10069
  meeting.stopKeepAlive = sinon.stub();
10422
10070
  meeting.updateLLMConnection = sinon.stub();
10423
- let resolvePrefetch;
10424
-
10425
- meeting.ensureDefaultDatachannelTokenAfterAdmit = sinon
10426
- .stub()
10427
- .returns(new Promise((resolve) => {
10428
- resolvePrefetch = resolve;
10429
- }));
10430
10071
  meeting.rtcMetrics = {
10431
10072
  sendNextMetrics: sinon.stub(),
10432
10073
  };
10433
-
10434
10074
  meeting.locusInfo.emit({function: 'test', file: 'test'}, 'SELF_ADMITTED_GUEST', test1);
10435
-
10436
10075
  assert.calledOnceWithExactly(meeting.stopKeepAlive);
10437
- assert.calledOnceWithExactly(meeting.ensureDefaultDatachannelTokenAfterAdmit);
10438
10076
  assert.calledThrice(TriggerProxy.trigger);
10439
10077
  assert.calledWith(
10440
10078
  TriggerProxy.trigger,
@@ -10453,11 +10091,7 @@ describe('plugin-meetings', () => {
10453
10091
  correlation_id: meeting.correlationId,
10454
10092
  }
10455
10093
  );
10456
-
10457
- resolvePrefetch(false);
10458
- await Promise.resolve();
10459
-
10460
- assert.calledOnce(meeting.updateLLMConnection);
10094
+ done();
10461
10095
  });
10462
10096
 
10463
10097
  it('listens to the breakouts changed event', () => {
@@ -10544,21 +10178,6 @@ describe('plugin-meetings', () => {
10544
10178
  EVENT_TRIGGERS.MEETING_INTERPRETATION_UPDATE
10545
10179
  );
10546
10180
  });
10547
-
10548
- it('listens to the self id changed event and updates aiEnableRequest', () => {
10549
- meeting.aiEnableRequest = {
10550
- selfParticipantIdUpdate: sinon.stub(),
10551
- };
10552
-
10553
- const payload = {selfId: 'participant-test-123'};
10554
-
10555
- meeting.locusInfo.emit({function: 'test', file: 'test'}, 'SELF_ID_CHANGED', payload);
10556
-
10557
- assert.calledOnceWithExactly(
10558
- meeting.aiEnableRequest.selfParticipantIdUpdate,
10559
- payload.selfId
10560
- );
10561
- });
10562
10181
  });
10563
10182
 
10564
10183
  describe('#setUpBreakoutsListener', () => {
@@ -10806,24 +10425,6 @@ describe('plugin-meetings', () => {
10806
10425
  );
10807
10426
  });
10808
10427
 
10809
- it('listens to MEETING_CONTROLS_AI_SUMMARY_NOTIFICATION_UPDATED', async () => {
10810
- const aiSummaryNotification = {example: 'value'};
10811
-
10812
- await meeting.locusInfo.emitScoped(
10813
- {function: 'test', file: 'test'},
10814
- LOCUSINFO.EVENTS.CONTROLS_AI_SUMMARY_NOTIFICATION_UPDATED,
10815
- {aiSummaryNotification}
10816
- );
10817
-
10818
- assert.calledWith(
10819
- TriggerProxy.trigger,
10820
- meeting,
10821
- {file: 'meeting/index', function: 'setupLocusControlsListener'},
10822
- EVENT_TRIGGERS.MEETING_CONTROLS_AI_SUMMARY_NOTIFICATION_UPDATED,
10823
- {aiSummaryNotification}
10824
- );
10825
- });
10826
-
10827
10428
  it('listens to MEETING_CONTROLS_MEETING_FULL_UPDATED', async () => {
10828
10429
  const state = {example: 'value'};
10829
10430
 
@@ -11096,9 +10697,6 @@ describe('plugin-meetings', () => {
11096
10697
  meeting.simultaneousInterpretation = {
11097
10698
  approvalUrlUpdate: sinon.stub().returns(undefined),
11098
10699
  };
11099
- meeting.aiEnableRequest = {
11100
- approvalUrlUpdate: sinon.stub().returns(undefined),
11101
- };
11102
10700
 
11103
10701
  meeting.locusInfo.emit(
11104
10702
  {function: 'test', file: 'test'},
@@ -11118,10 +10716,6 @@ describe('plugin-meetings', () => {
11118
10716
  meeting.simultaneousInterpretation.approvalUrlUpdate,
11119
10717
  newLocusServices.services.approval.url
11120
10718
  );
11121
- assert.calledWith(
11122
- meeting.aiEnableRequest.approvalUrlUpdate,
11123
- newLocusServices.services.approval.url
11124
- );
11125
10719
  assert.calledOnce(meeting.recordingController.setSessionId);
11126
10720
  done();
11127
10721
  });
@@ -11522,43 +11116,8 @@ describe('plugin-meetings', () => {
11522
11116
  meeting.unsetPeerConnections();
11523
11117
  assert.calledOnce(meeting.mediaProperties.unsetPeerConnection);
11524
11118
  assert.calledTwice(meeting.webex.internal.mercury.off);
11525
- assert.calledWith(meeting.webex.internal.mercury.off.firstCall, ONLINE);
11526
- assert.calledWith(meeting.webex.internal.mercury.off.secondCall, OFFLINE);
11527
- });
11528
- });
11529
-
11530
- describe('localConstraintsChangeHandler', () => {
11531
- it('calls updatePreferredBitrateKbps when not multistream', () => {
11532
- meeting.isMultistream = false;
11533
- meeting.mediaProperties.webrtcMediaConnection = {
11534
- updatePreferredBitrateKbps: sinon.stub(),
11535
- };
11536
-
11537
- meeting.localConstraintsChangeHandler();
11538
-
11539
- assert.calledOnce(
11540
- meeting.mediaProperties.webrtcMediaConnection.updatePreferredBitrateKbps
11541
- );
11542
- });
11543
-
11544
- it('does not call updatePreferredBitrateKbps when multistream', () => {
11545
- meeting.isMultistream = true;
11546
- meeting.mediaProperties.webrtcMediaConnection = {
11547
- updatePreferredBitrateKbps: sinon.stub(),
11548
- };
11549
-
11550
- meeting.localConstraintsChangeHandler();
11551
-
11552
- assert.notCalled(
11553
- meeting.mediaProperties.webrtcMediaConnection.updatePreferredBitrateKbps
11554
- );
11555
- });
11556
-
11557
- it('does not throw when webrtcMediaConnection is undefined', () => {
11558
- meeting.isMultistream = false;
11559
- meeting.mediaProperties.webrtcMediaConnection = undefined;
11560
-
11561
- assert.doesNotThrow(() => meeting.localConstraintsChangeHandler());
11119
+ assert.calledWith(meeting.webex.internal.mercury.off.firstCall, ONLINE);
11120
+ assert.calledWith(meeting.webex.internal.mercury.off.secondCall, OFFLINE);
11562
11121
  });
11563
11122
  });
11564
11123
 
@@ -11941,7 +11500,6 @@ describe('plugin-meetings', () => {
11941
11500
  let canUnsetDisallowUnmuteSpy;
11942
11501
  let canUserRaiseHandSpy;
11943
11502
  let bothLeaveAndEndMeetingAvailableSpy;
11944
- let requireHostEndMeetingBeforeLeaveSpy;
11945
11503
  let canUserLowerAllHandsSpy;
11946
11504
  let canUserLowerSomeoneElsesHandSpy;
11947
11505
  let waitingForOthersToJoinSpy;
@@ -11953,8 +11511,6 @@ describe('plugin-meetings', () => {
11953
11511
  let canMoveToLobbySpy;
11954
11512
  let isSpokenLanguageAutoDetectionEnabledSpy;
11955
11513
  let showAutoEndMeetingWarningSpy;
11956
- let canAttendeeRequestAiAssistantEnabledSpy;
11957
- let attendeeRequestAiAssistantDeclinedAllSpy;
11958
11514
  // Due to import tree issues, hasHints must be stubed within the scope of the `it`.
11959
11515
 
11960
11516
  beforeEach(() => {
@@ -11975,10 +11531,6 @@ describe('plugin-meetings', () => {
11975
11531
  MeetingUtil,
11976
11532
  'bothLeaveAndEndMeetingAvailable'
11977
11533
  );
11978
- requireHostEndMeetingBeforeLeaveSpy = sinon.spy(
11979
- MeetingUtil,
11980
- 'requireHostEndMeetingBeforeLeave'
11981
- );
11982
11534
  canUserLowerSomeoneElsesHandSpy = sinon.spy(MeetingUtil, 'canUserLowerSomeoneElsesHand');
11983
11535
  waitingForOthersToJoinSpy = sinon.spy(MeetingUtil, 'waitingForOthersToJoin');
11984
11536
  canSendReactionsSpy = sinon.spy(MeetingUtil, 'canSendReactions');
@@ -11995,22 +11547,12 @@ describe('plugin-meetings', () => {
11995
11547
  MeetingUtil,
11996
11548
  'isSpokenLanguageAutoDetectionEnabled'
11997
11549
  );
11998
- canAttendeeRequestAiAssistantEnabledSpy = sinon.spy(
11999
- MeetingUtil,
12000
- 'canAttendeeRequestAiAssistantEnabled'
12001
- );
12002
- attendeeRequestAiAssistantDeclinedAllSpy = sinon.spy(
12003
- MeetingUtil,
12004
- 'attendeeRequestAiAssistantDeclinedAll'
12005
- );
12006
11550
  });
12007
11551
 
12008
11552
  afterEach(() => {
12009
11553
  inMeetingActionsSetSpy.restore();
12010
11554
  waitingForOthersToJoinSpy.restore();
12011
11555
  showAutoEndMeetingWarningSpy.restore();
12012
- canAttendeeRequestAiAssistantEnabledSpy.restore();
12013
- attendeeRequestAiAssistantDeclinedAllSpy.restore();
12014
11556
  });
12015
11557
 
12016
11558
  forEach(
@@ -12534,7 +12076,6 @@ describe('plugin-meetings', () => {
12534
12076
  const userDisplayHints = ['LOCK_CONTROL_UNLOCK'];
12535
12077
  meeting.userDisplayHints = ['LOCK_CONTROL_UNLOCK'];
12536
12078
  meeting.meetingInfo.supportVoIP = true;
12537
- meeting.roles = [];
12538
12079
 
12539
12080
  meeting.updateMeetingActions();
12540
12081
 
@@ -12550,7 +12091,6 @@ describe('plugin-meetings', () => {
12550
12091
  assert.calledWith(canUnsetDisallowUnmuteSpy, userDisplayHints);
12551
12092
  assert.calledWith(canUserRaiseHandSpy, userDisplayHints);
12552
12093
  assert.calledWith(bothLeaveAndEndMeetingAvailableSpy, userDisplayHints);
12553
- assert.calledWith(requireHostEndMeetingBeforeLeaveSpy, userDisplayHints);
12554
12094
  assert.calledWith(canUserLowerAllHandsSpy, userDisplayHints);
12555
12095
  assert.calledWith(canUserLowerSomeoneElsesHandSpy, userDisplayHints);
12556
12096
  assert.calledWith(waitingForOthersToJoinSpy, userDisplayHints);
@@ -12562,12 +12102,6 @@ describe('plugin-meetings', () => {
12562
12102
  assert.calledWith(canMoveToLobbySpy, userDisplayHints);
12563
12103
  assert.calledWith(showAutoEndMeetingWarningSpy, userDisplayHints);
12564
12104
  assert.calledWith(isSpokenLanguageAutoDetectionEnabledSpy, userDisplayHints);
12565
- assert.calledWith(
12566
- canAttendeeRequestAiAssistantEnabledSpy,
12567
- userDisplayHints,
12568
- meeting.roles
12569
- );
12570
- assert.calledWith(attendeeRequestAiAssistantDeclinedAllSpy, userDisplayHints);
12571
12105
 
12572
12106
  assert.calledWith(ControlsOptionsUtil.hasHints, {
12573
12107
  requiredHints: [DISPLAY_HINTS.MUTE_ALL],
@@ -12710,159 +12244,33 @@ describe('plugin-meetings', () => {
12710
12244
 
12711
12245
  describe('#handleDataChannelUrlChange', () => {
12712
12246
  let updateLLMConnectionSpy;
12713
- let updatePSDataChannelSpy;
12714
12247
 
12715
12248
  beforeEach(() => {
12716
12249
  updateLLMConnectionSpy = sinon.spy(meeting, 'updateLLMConnection');
12717
- updatePSDataChannelSpy = sinon.stub(meeting.webinar, 'updatePSDataChannel').resolves();
12718
- meeting.webinar.isJoinPracticeSessionDataChannel = sinon.stub().returns(false);
12719
12250
  });
12720
12251
 
12721
- const check = (
12722
- url,
12723
- practiceSessionDatachannelUrl,
12724
- {expectedMainCalled, expectedPracticeCalled}
12725
- ) => {
12726
- meeting.handleDataChannelUrlChange(url, practiceSessionDatachannelUrl);
12252
+ const check = (url, expectedCalled) => {
12253
+ meeting.handleDataChannelUrlChange(url);
12727
12254
 
12728
- if (expectedMainCalled) {
12255
+ if (expectedCalled) {
12729
12256
  assert.calledWith(updateLLMConnectionSpy);
12730
12257
  } else {
12731
12258
  assert.notCalled(updateLLMConnectionSpy);
12732
12259
  }
12733
-
12734
- if (expectedPracticeCalled) {
12735
- assert.calledWith(updatePSDataChannelSpy);
12736
- } else {
12737
- assert.notCalled(updatePSDataChannelSpy);
12738
- }
12739
12260
  };
12740
12261
 
12741
12262
  it('calls deferred updateLLMConnection if datachannelURL is set and the enableAutomaticLLM is true', () => {
12742
12263
  meeting.config.enableAutomaticLLM = true;
12743
- check('some url', undefined, {expectedMainCalled: true, expectedPracticeCalled: false});
12264
+ check('some url', true);
12744
12265
  });
12745
12266
 
12746
12267
  it('does not call updateLLMConnection if datachannelURL is undefined', () => {
12747
12268
  meeting.config.enableAutomaticLLM = true;
12748
- check(undefined, undefined, {
12749
- expectedMainCalled: false,
12750
- expectedPracticeCalled: false,
12751
- });
12269
+ check(undefined, false);
12752
12270
  });
12753
12271
 
12754
12272
  it('does not call updateLLMConnection if enableAutomaticLLM is false', () => {
12755
- check('some url', 'some practice url', {
12756
- expectedMainCalled: false,
12757
- expectedPracticeCalled: false,
12758
- });
12759
- });
12760
-
12761
- it('calls updatePSDataChannel when practice-session routing is active', () => {
12762
- meeting.config.enableAutomaticLLM = true;
12763
- meeting.webinar.isJoinPracticeSessionDataChannel.returns(true);
12764
-
12765
- check('some url', 'some practice url', {
12766
- expectedMainCalled: true,
12767
- expectedPracticeCalled: true,
12768
- });
12769
- });
12770
-
12771
- it('does not call updatePSDataChannel when the main datachannelURL is undefined', () => {
12772
- meeting.config.enableAutomaticLLM = true;
12773
- meeting.webinar.isJoinPracticeSessionDataChannel.returns(true);
12774
-
12775
- check(undefined, 'some practice url', {
12776
- expectedMainCalled: false,
12777
- expectedPracticeCalled: false,
12778
- });
12779
- });
12780
- });
12781
-
12782
- describe('#saveDataChannelToken', () => {
12783
- beforeEach(() => {
12784
- webex.internal.llm.setDatachannelToken = sinon.stub();
12785
- });
12786
-
12787
- it('saves datachannelToken into LLM as Default', () => {
12788
- meeting.saveDataChannelToken({
12789
- locus: {
12790
- self: {datachannelToken: 'default-token'},
12791
- },
12792
- });
12793
-
12794
- assert.calledWithExactly(
12795
- webex.internal.llm.setDatachannelToken,
12796
- 'default-token',
12797
- 'llm-default-session'
12798
- );
12799
- });
12800
-
12801
- it('saves practiceSessionDatachannelToken into LLM as PracticeSession', () => {
12802
- meeting.saveDataChannelToken({
12803
- locus: {
12804
- self: {practiceSessionDatachannelToken: 'ps-token'},
12805
- },
12806
- });
12807
-
12808
- assert.calledWithExactly(
12809
- webex.internal.llm.setDatachannelToken,
12810
- 'ps-token',
12811
- 'llm-practice-session'
12812
- );
12813
- });
12814
-
12815
- it('saves both tokens when both are present', () => {
12816
- meeting.saveDataChannelToken({
12817
- locus: {
12818
- self: {
12819
- datachannelToken: 'default-token',
12820
- practiceSessionDatachannelToken: 'ps-token',
12821
- },
12822
- },
12823
- });
12824
-
12825
- assert.calledTwice(webex.internal.llm.setDatachannelToken);
12826
- assert.calledWithExactly(
12827
- webex.internal.llm.setDatachannelToken,
12828
- 'default-token',
12829
- 'llm-default-session'
12830
- );
12831
- assert.calledWithExactly(
12832
- webex.internal.llm.setDatachannelToken,
12833
- 'ps-token',
12834
- 'llm-practice-session'
12835
- );
12836
- });
12837
-
12838
- it('does not call setDatachannelToken when no tokens are present', () => {
12839
- meeting.saveDataChannelToken({locus: {self: {}}});
12840
-
12841
- assert.notCalled(webex.internal.llm.setDatachannelToken);
12842
- });
12843
-
12844
- it('handles undefined join gracefully', () => {
12845
- meeting.saveDataChannelToken(undefined);
12846
-
12847
- assert.notCalled(webex.internal.llm.setDatachannelToken);
12848
- });
12849
-
12850
- it('handles missing locus.self gracefully', () => {
12851
- meeting.saveDataChannelToken({locus: {}});
12852
-
12853
- assert.notCalled(webex.internal.llm.setDatachannelToken);
12854
- });
12855
- });
12856
-
12857
- describe('#clearDataChannelToken', () => {
12858
- beforeEach(() => {
12859
- webex.internal.llm.resetDatachannelTokens = sinon.stub();
12860
- });
12861
-
12862
- it('calls resetDatachannelTokens on LLM', () => {
12863
- meeting.clearDataChannelToken();
12864
-
12865
- assert.calledOnce(webex.internal.llm.resetDatachannelTokens);
12273
+ check('some url', false);
12866
12274
  });
12867
12275
  });
12868
12276
 
@@ -12871,20 +12279,16 @@ describe('plugin-meetings', () => {
12871
12279
  webex.internal.llm.isConnected = sinon.stub().returns(false);
12872
12280
  webex.internal.llm.getLocusUrl = sinon.stub();
12873
12281
  webex.internal.llm.getDatachannelUrl = sinon.stub();
12874
- webex.internal.llm.registerAndConnect = sinon.stub().resolves('something');
12875
- webex.internal.llm.disconnectLLM = sinon.stub().resolves();
12876
- webex.internal.llm.on = sinon.stub();
12877
- webex.internal.llm.off = sinon.stub();
12878
- webex.internal.llm.getDatachannelToken = sinon.stub().returns(undefined);
12879
- webex.internal.llm.setDatachannelToken = sinon.stub();
12880
-
12282
+ webex.internal.llm.registerAndConnect = sinon
12283
+ .stub()
12284
+ .returns(Promise.resolve('something'));
12285
+ webex.internal.llm.disconnectLLM = sinon.stub().returns(Promise.resolve());
12286
+ meeting.webex.internal.llm.on = sinon.stub();
12287
+ meeting.webex.internal.llm.off = sinon.stub();
12881
12288
  meeting.processRelayEvent = sinon.stub();
12882
- meeting.processLocusLLMEvent = sinon.stub();
12883
- meeting.clearLLMHealthCheckTimer = sinon.stub();
12884
- meeting.startLLMHealthCheckTimer = sinon.stub();
12885
-
12886
12289
  meeting.webinar.isJoinPracticeSessionDataChannel = sinon.stub().returns(false);
12887
12290
  });
12291
+
12888
12292
  it('does not connect if the call is not joined yet', async () => {
12889
12293
  meeting.joinedWith = {state: 'any other state'};
12890
12294
  webex.internal.llm.getLocusUrl.returns('a url');
@@ -12898,21 +12302,31 @@ describe('plugin-meetings', () => {
12898
12302
  assert.equal(result, undefined);
12899
12303
  assert.notCalled(meeting.webex.internal.llm.on);
12900
12304
  });
12305
+
12901
12306
  it('returns undefined if llm is already connected and the locus url is unchanged', async () => {
12902
12307
  meeting.joinedWith = {state: 'JOINED'};
12903
- meeting.locusInfo = {
12904
- url: 'a url',
12905
- info: {datachannelUrl: 'a datachannel url'},
12906
- };
12308
+ webex.internal.llm.isConnected.returns(true);
12309
+ webex.internal.llm.getLocusUrl.returns('a url');
12310
+ webex.internal.llm.getDatachannelUrl.returns('a datachannel url');
12311
+
12312
+ meeting.locusInfo = {url: 'a url', info: {datachannelUrl: 'a datachannel url'}};
12907
12313
 
12908
12314
  const result = await meeting.updateLLMConnection();
12315
+
12316
+ assert.notCalled(webex.internal.llm.registerAndConnect);
12909
12317
  assert.notCalled(webex.internal.llm.disconnectLLM);
12910
- assert.calledWithExactly(
12911
- webex.internal.llm.registerAndConnect,
12912
- 'a url',
12913
- 'a datachannel url',
12914
- undefined
12915
- );
12318
+ assert.equal(result, undefined);
12319
+ assert.notCalled(meeting.webex.internal.llm.on);
12320
+ });
12321
+
12322
+ it('connects if not already connected', async () => {
12323
+ meeting.joinedWith = {state: 'JOINED'};
12324
+ meeting.locusInfo = {url: 'a url', info: {datachannelUrl: 'a datachannel url'}};
12325
+
12326
+ const result = await meeting.updateLLMConnection();
12327
+
12328
+ assert.notCalled(webex.internal.llm.disconnectLLM);
12329
+ assert.calledWith(webex.internal.llm.registerAndConnect, 'a url', 'a datachannel url');
12916
12330
  assert.equal(result, 'something');
12917
12331
  assert.calledWithExactly(
12918
12332
  meeting.webex.internal.llm.off,
@@ -12935,49 +12349,27 @@ describe('plugin-meetings', () => {
12935
12349
  meeting.processLocusLLMEvent
12936
12350
  );
12937
12351
  });
12938
- it('connects if not already connected', async () => {
12939
- meeting.joinedWith = {state: 'JOINED'};
12940
- meeting.locusInfo = {url: 'a url', info: {datachannelUrl: 'a datachannel url'}};
12941
-
12942
- const result = await meeting.updateLLMConnection();
12943
12352
 
12944
- assert.notCalled(webex.internal.llm.disconnectLLM);
12945
- assert.calledWithExactly(
12946
- webex.internal.llm.registerAndConnect,
12947
- 'a url',
12948
- 'a datachannel url',
12949
- undefined
12950
- );
12951
- assert.equal(result, 'something');
12952
- });
12953
- it('disconnects if the locus url has changed', async () => {
12353
+ it('disconnects if first if the locus url has changed', async () => {
12954
12354
  meeting.joinedWith = {state: 'JOINED'};
12955
-
12956
12355
  webex.internal.llm.isConnected.returns(true);
12957
12356
  webex.internal.llm.getLocusUrl.returns('a url');
12357
+ webex.internal.llm.getDatachannelUrl.returns('a datachannel url');
12958
12358
 
12959
- meeting.locusInfo = {
12960
- url: 'a different url',
12961
- info: {datachannelUrl: 'a datachannel url'},
12962
- self: {},
12963
- };
12359
+ meeting.locusInfo = {url: 'a different url', info: {datachannelUrl: 'a datachannel url'}};
12964
12360
 
12965
12361
  const result = await meeting.updateLLMConnection();
12966
12362
 
12967
- assert.calledWithExactly(webex.internal.llm.disconnectLLM, {
12363
+ assert.calledWith(webex.internal.llm.disconnectLLM, {
12968
12364
  code: 3050,
12969
12365
  reason: 'done (permanent)',
12970
12366
  });
12971
-
12972
- assert.calledWithExactly(
12367
+ assert.calledWith(
12973
12368
  webex.internal.llm.registerAndConnect,
12974
12369
  'a different url',
12975
- 'a datachannel url',
12976
- undefined
12370
+ 'a datachannel url'
12977
12371
  );
12978
-
12979
12372
  assert.equal(result, 'something');
12980
-
12981
12373
  assert.calledWithExactly(
12982
12374
  meeting.webex.internal.llm.off,
12983
12375
  'event:relay.event',
@@ -12989,7 +12381,6 @@ describe('plugin-meetings', () => {
12989
12381
  meeting.processLocusLLMEvent
12990
12382
  );
12991
12383
  assert.callCount(meeting.webex.internal.llm.off, 4);
12992
-
12993
12384
  assert.calledWithExactly(
12994
12385
  meeting.webex.internal.llm.on,
12995
12386
  'event:relay.event',
@@ -13000,37 +12391,28 @@ describe('plugin-meetings', () => {
13000
12391
  'event:locus.state_message',
13001
12392
  meeting.processLocusLLMEvent
13002
12393
  );
13003
- assert.isFalse(
13004
- meeting.webex.internal.llm.off.calledWithExactly('online', meeting.handleLLMOnline)
13005
- );
13006
12394
  });
13007
- it('disconnects if the data channel url has changed', async () => {
12395
+
12396
+ it('disconnects it first if the data channel url has changed', async () => {
13008
12397
  meeting.joinedWith = {state: 'JOINED'};
13009
12398
  webex.internal.llm.isConnected.returns(true);
13010
12399
  webex.internal.llm.getLocusUrl.returns('a url');
12400
+ webex.internal.llm.getDatachannelUrl.returns('a datachannel url');
13011
12401
 
13012
- meeting.locusInfo = {
13013
- url: 'a url',
13014
- info: {datachannelUrl: 'a different datachannel url'},
13015
- self: {},
13016
- };
12402
+ meeting.locusInfo = {url: 'a url', info: {datachannelUrl: 'a different datachannel url'}};
13017
12403
 
13018
12404
  const result = await meeting.updateLLMConnection();
13019
12405
 
13020
- assert.calledWithExactly(webex.internal.llm.disconnectLLM, {
12406
+ assert.calledWith(webex.internal.llm.disconnectLLM, {
13021
12407
  code: 3050,
13022
12408
  reason: 'done (permanent)',
13023
12409
  });
13024
-
13025
- assert.calledWithExactly(
12410
+ assert.calledWith(
13026
12411
  webex.internal.llm.registerAndConnect,
13027
12412
  'a url',
13028
- 'a different datachannel url',
13029
- undefined
12413
+ 'a different datachannel url'
13030
12414
  );
13031
-
13032
12415
  assert.equal(result, 'something');
13033
-
13034
12416
  assert.calledWithExactly(
13035
12417
  meeting.webex.internal.llm.off,
13036
12418
  'event:relay.event',
@@ -13041,7 +12423,6 @@ describe('plugin-meetings', () => {
13041
12423
  'event:locus.state_message',
13042
12424
  meeting.processLocusLLMEvent
13043
12425
  );
13044
-
13045
12426
  assert.calledWithExactly(
13046
12427
  meeting.webex.internal.llm.on,
13047
12428
  'event:relay.event',
@@ -13052,10 +12433,8 @@ describe('plugin-meetings', () => {
13052
12433
  'event:locus.state_message',
13053
12434
  meeting.processLocusLLMEvent
13054
12435
  );
13055
- assert.isFalse(
13056
- meeting.webex.internal.llm.off.calledWithExactly('online', meeting.handleLLMOnline)
13057
- );
13058
12436
  });
12437
+
13059
12438
  it('disconnects when the state is not JOINED', async () => {
13060
12439
  meeting.joinedWith = {state: 'any other state'};
13061
12440
  webex.internal.llm.isConnected.returns(true);
@@ -13065,38 +12444,9 @@ describe('plugin-meetings', () => {
13065
12444
 
13066
12445
  const result = await meeting.updateLLMConnection();
13067
12446
 
13068
- assert.calledWith(webex.internal.llm.disconnectLLM, {
13069
- code: 3050,
13070
- reason: 'done (permanent)',
13071
- });
12447
+ assert.calledWith(webex.internal.llm.disconnectLLM, undefined);
13072
12448
  assert.notCalled(webex.internal.llm.registerAndConnect);
13073
12449
  assert.equal(result, undefined);
13074
- assert.isFalse(
13075
- meeting.webex.internal.llm.off.calledWithExactly('online', meeting.handleLLMOnline)
13076
- );
13077
- });
13078
- it('rethrows disconnect errors during reconnect cleanup after removing relay listeners and timer', async () => {
13079
- const disconnectError = new Error('disconnect failed');
13080
-
13081
- meeting.joinedWith = {state: 'JOINED'};
13082
- webex.internal.llm.isConnected.returns(true);
13083
- webex.internal.llm.getLocusUrl.returns('a url');
13084
- webex.internal.llm.disconnectLLM.rejects(disconnectError);
13085
-
13086
- meeting.locusInfo = {
13087
- url: 'a different url',
13088
- info: {datachannelUrl: 'a datachannel url'},
13089
- self: {},
13090
- };
13091
-
13092
- try {
13093
- await meeting.updateLLMConnection();
13094
- assert.fail('Expected updateLLMConnection to reject when disconnectLLM fails');
13095
- } catch (error) {
13096
- assert.equal(error, disconnectError);
13097
- }
13098
-
13099
- assert.notCalled(webex.internal.llm.registerAndConnect);
13100
12450
  assert.calledWithExactly(
13101
12451
  meeting.webex.internal.llm.off,
13102
12452
  'event:relay.event',
@@ -13107,159 +12457,22 @@ describe('plugin-meetings', () => {
13107
12457
  'event:locus.state_message',
13108
12458
  meeting.processLocusLLMEvent
13109
12459
  );
13110
- assert.isFalse(
13111
- meeting.webex.internal.llm.off.calledWithExactly('online', meeting.handleLLMOnline)
13112
- );
13113
- assert.calledOnce(meeting.clearLLMHealthCheckTimer);
13114
12460
  });
13115
- it('still need connect main session data channel when PS started', async () => {
12461
+
12462
+ it('connect ps data channel if ps started in webinar', async () => {
13116
12463
  meeting.joinedWith = {state: 'JOINED'};
13117
12464
  meeting.locusInfo = {
13118
12465
  url: 'a url',
13119
12466
  info: {
13120
12467
  datachannelUrl: 'a datachannel url',
13121
- practiceSessionDatachannelUrl: 'ps-url',
12468
+ practiceSessionDatachannelUrl: 'a ps datachannel url',
13122
12469
  },
13123
12470
  };
13124
- meeting.webinar.isJoinPracticeSessionDataChannel.returns(true);
13125
-
13126
- await meeting.updateLLMConnection();
13127
-
13128
- assert.calledWithExactly(
13129
- webex.internal.llm.registerAndConnect,
13130
- 'a url',
13131
- 'a datachannel url',
13132
- undefined
13133
- );
13134
- });
13135
- it('passes dataChannelToken from LLM to registerAndConnect', async () => {
13136
- meeting.joinedWith = {state: 'JOINED'};
13137
- meeting.locusInfo = {
13138
- url: 'a url',
13139
- info: {datachannelUrl: 'a datachannel url'},
13140
- };
13141
-
13142
- webex.internal.llm.getDatachannelToken.withArgs('llm-default-session').returns('token-123');
13143
-
13144
- await meeting.updateLLMConnection();
13145
-
13146
- assert.calledWithExactly(
13147
- webex.internal.llm.registerAndConnect,
13148
- 'a url',
13149
- 'a datachannel url',
13150
- 'token-123'
13151
- );
13152
- assert.notCalled(webex.internal.llm.setDatachannelToken);
13153
- });
13154
- it('passes undefined token when LLM has no token stored', async () => {
13155
- meeting.joinedWith = {state: 'JOINED'};
13156
- meeting.locusInfo = {
13157
- url: 'a url',
13158
- info: {datachannelUrl: 'a datachannel url'},
13159
- };
13160
-
13161
- webex.internal.llm.getDatachannelToken.returns(undefined);
13162
-
13163
- await meeting.updateLLMConnection();
13164
-
13165
- assert.calledWithExactly(
13166
- webex.internal.llm.registerAndConnect,
13167
- 'a url',
13168
- 'a datachannel url',
13169
- undefined
13170
- );
13171
-
13172
- assert.notCalled(webex.internal.llm.setDatachannelToken);
13173
- });
13174
-
13175
- it('does not pass token when data channel with jwt token is disabled', async () => {
13176
- meeting.joinedWith = {state: 'JOINED'};
13177
- meeting.locusInfo = {
13178
- url: 'a url',
13179
- info: {datachannelUrl: 'a datachannel url'},
13180
- };
13181
-
13182
- webex.internal.llm.getDatachannelToken.returns(undefined);
13183
- webex.internal.llm.isDataChannelTokenEnabled = sinon.stub().resolves(false);
13184
-
12471
+ meeting.webinar.isJoinPracticeSessionDataChannel = sinon.stub().returns(true);
13185
12472
  await meeting.updateLLMConnection();
13186
12473
 
13187
- assert.calledWithExactly(
13188
- webex.internal.llm.registerAndConnect,
13189
- 'a url',
13190
- 'a datachannel url',
13191
- undefined
13192
- );
13193
- assert.notCalled(webex.internal.llm.setDatachannelToken);
13194
- });
13195
-
13196
- describe('#clearMeetingData', () => {
13197
- beforeEach(() => {
13198
- webex.internal.llm.isConnected = sinon.stub().returns(true);
13199
- webex.internal.llm.disconnectLLM = sinon.stub().resolves();
13200
- webex.internal.llm.off = sinon.stub();
13201
- meeting.annotation.deregisterEvents = sinon.stub();
13202
- meeting.clearLLMHealthCheckTimer = sinon.stub();
13203
- meeting.stopTranscription = sinon.stub();
13204
- meeting.clearDataChannelToken = sinon.stub();
13205
- meeting.shareStatus = 'no-share';
13206
- });
13207
-
13208
- it('disconnects llm and removes online and relay listeners during meeting data cleanup', async () => {
13209
- await meeting.clearMeetingData();
13210
-
13211
- assert.calledOnceWithExactly(webex.internal.llm.disconnectLLM, {
13212
- code: 3050,
13213
- reason: 'done (permanent)',
13214
- });
13215
- assert.calledWithExactly(webex.internal.llm.off, 'online', meeting.handleLLMOnline);
13216
- assert.calledWithExactly(
13217
- webex.internal.llm.off,
13218
- 'event:relay.event',
13219
- meeting.processRelayEvent
13220
- );
13221
- assert.calledWithExactly(
13222
- webex.internal.llm.off,
13223
- 'event:locus.state_message',
13224
- meeting.processLocusLLMEvent
13225
- );
13226
- assert.calledOnce(meeting.clearLLMHealthCheckTimer);
13227
- assert.calledOnce(meeting.stopTranscription);
13228
- assert.isUndefined(meeting.transcription);
13229
- assert.calledOnce(meeting.clearDataChannelToken);
13230
- assert.calledOnce(meeting.annotation.deregisterEvents);
13231
- });
13232
- it('continues cleanup when disconnectLLM fails during meeting data cleanup', async () => {
13233
- webex.internal.llm.disconnectLLM.rejects(new Error('disconnect failed'));
13234
-
13235
- await meeting.clearMeetingData();
13236
-
13237
- assert.calledWithExactly(webex.internal.llm.off, 'online', meeting.handleLLMOnline);
13238
- assert.calledWithExactly(
13239
- webex.internal.llm.off,
13240
- 'event:relay.event',
13241
- meeting.processRelayEvent
13242
- );
13243
- assert.calledWithExactly(
13244
- webex.internal.llm.off,
13245
- 'event:locus.state_message',
13246
- meeting.processLocusLLMEvent
13247
- );
13248
- assert.calledOnce(meeting.clearLLMHealthCheckTimer);
13249
- assert.calledOnce(meeting.stopTranscription);
13250
- assert.isUndefined(meeting.transcription);
13251
- assert.calledOnce(meeting.clearDataChannelToken);
13252
- assert.calledOnce(meeting.annotation.deregisterEvents);
13253
- });
13254
- it('always calls stopTranscription even when transcription is undefined', async () => {
13255
- meeting.transcription = undefined;
13256
-
13257
- await meeting.clearMeetingData();
13258
-
13259
- assert.calledOnce(meeting.stopTranscription);
13260
- assert.isUndefined(meeting.transcription);
13261
- assert.calledOnce(meeting.clearDataChannelToken);
13262
- });
12474
+ assert.notCalled(webex.internal.llm.disconnectLLM);
12475
+ assert.calledWith(webex.internal.llm.registerAndConnect, 'a url', 'a ps datachannel url');
13263
12476
  });
13264
12477
  });
13265
12478
 
@@ -13270,7 +12483,6 @@ describe('plugin-meetings', () => {
13270
12483
 
13271
12484
  it('should read the locus object, set on the meeting and return null', () => {
13272
12485
  const dataSets = {someFakeStuff: 'dataSet'};
13273
- const metadata = {some: 'metadata'};
13274
12486
 
13275
12487
  meeting.setLocus({
13276
12488
  mediaConnections: [test1],
@@ -13280,14 +12492,12 @@ describe('plugin-meetings', () => {
13280
12492
  mediaId: uuid3,
13281
12493
  locus: {host: {id: uuid4}},
13282
12494
  dataSets,
13283
- metadata,
13284
12495
  });
13285
12496
  assert.calledOnce(meeting.locusInfo.initialSetup);
13286
12497
  assert.calledWith(meeting.locusInfo.initialSetup, {
13287
12498
  trigger: 'join-response',
13288
12499
  locus: {host: {id: uuid4}},
13289
12500
  dataSets,
13290
- metadata,
13291
12501
  });
13292
12502
  assert.equal(meeting.mediaConnections, test1);
13293
12503
  assert.equal(meeting.locusUrl, url1);
@@ -14829,69 +14039,6 @@ describe('plugin-meetings', () => {
14829
14039
  assert.calledOnce(meeting.meetingRequest.keepAlive);
14830
14040
  });
14831
14041
  });
14832
- describe('#refreshDataChannelToken()', () => {
14833
- let meeting;
14834
-
14835
- beforeEach(() => {
14836
- meeting = Object.create(Meeting.prototype);
14837
- meeting.locusUrl = 'https://locus.example.com';
14838
- meeting.meetingRequest = {
14839
- fetchDatachannelToken: sinon.stub().resolves({
14840
- body: {datachannelToken: 'mock-token'},
14841
- }),
14842
- };
14843
- meeting.members = {
14844
- selfId: 'self-123',
14845
- };
14846
- meeting.webinar = {
14847
- isJoinPracticeSessionDataChannel: sinon.stub().returns(true),
14848
- };
14849
- });
14850
-
14851
- it('calls fetchDatachannelToken with correct parameters', async () => {
14852
- await meeting.refreshDataChannelToken();
14853
-
14854
- sinon.assert.calledOnce(meeting.meetingRequest.fetchDatachannelToken);
14855
-
14856
- sinon.assert.calledWith(meeting.meetingRequest.fetchDatachannelToken, {
14857
- locusUrl: 'https://locus.example.com',
14858
- requestingParticipantId: 'self-123',
14859
- isPracticeSession: true,
14860
- });
14861
- });
14862
-
14863
- it('returns the correct structured result', async () => {
14864
- const result = await meeting.refreshDataChannelToken();
14865
-
14866
- expect(result).to.deep.equal({
14867
- body: {
14868
- datachannelToken: 'mock-token',
14869
- dataChannelTokenType: 'llm-practice-session',
14870
- },
14871
- });
14872
- });
14873
- });
14874
- describe('#getDataChannelTokenType', () => {
14875
- it('returns PracticeSession when webinar is in practice session mode', () => {
14876
- meeting.webinar = {
14877
- isJoinPracticeSessionDataChannel: sinon.stub().returns(true),
14878
- };
14879
-
14880
- const result = meeting.getDataChannelTokenType();
14881
-
14882
- expect(result).to.equal('llm-practice-session');
14883
- });
14884
-
14885
- it('returns Default when not in practice session mode', () => {
14886
- meeting.webinar = {
14887
- isJoinPracticeSessionDataChannel: sinon.stub().returns(false),
14888
- };
14889
-
14890
- const result = meeting.getDataChannelTokenType();
14891
-
14892
- expect(result).to.equal('llm-default-session');
14893
- });
14894
- });
14895
14042
  describe('#stopKeepAlive', () => {
14896
14043
  let clock;
14897
14044
  const defaultKeepAliveUrl = 'keep.alive.url';