@webex/plugin-meetings 3.6.0-next.9 → 3.7.0-ipv6-multi-turn-urls.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 (159) hide show
  1. package/README.md +2 -1
  2. package/dist/breakouts/breakout.js +1 -1
  3. package/dist/breakouts/index.js +1 -1
  4. package/dist/common/errors/{webinar-registration-error.js → join-webinar-error.js} +12 -12
  5. package/dist/common/errors/join-webinar-error.js.map +1 -0
  6. package/dist/config.js +3 -1
  7. package/dist/config.js.map +1 -1
  8. package/dist/constants.js +50 -7
  9. package/dist/constants.js.map +1 -1
  10. package/dist/controls-options-manager/enums.js +1 -0
  11. package/dist/controls-options-manager/enums.js.map +1 -1
  12. package/dist/controls-options-manager/index.js +10 -3
  13. package/dist/controls-options-manager/index.js.map +1 -1
  14. package/dist/controls-options-manager/types.js.map +1 -1
  15. package/dist/controls-options-manager/util.js +12 -0
  16. package/dist/controls-options-manager/util.js.map +1 -1
  17. package/dist/index.js +7 -7
  18. package/dist/index.js.map +1 -1
  19. package/dist/interpretation/index.js +1 -1
  20. package/dist/interpretation/siLanguage.js +1 -1
  21. package/dist/locus-info/controlsUtils.js +28 -4
  22. package/dist/locus-info/controlsUtils.js.map +1 -1
  23. package/dist/locus-info/fullState.js +2 -1
  24. package/dist/locus-info/fullState.js.map +1 -1
  25. package/dist/locus-info/index.js +61 -3
  26. package/dist/locus-info/index.js.map +1 -1
  27. package/dist/media/index.js +29 -1
  28. package/dist/media/index.js.map +1 -1
  29. package/dist/meeting/in-meeting-actions.js +29 -1
  30. package/dist/meeting/in-meeting-actions.js.map +1 -1
  31. package/dist/meeting/index.js +692 -472
  32. package/dist/meeting/index.js.map +1 -1
  33. package/dist/meeting/locusMediaRequest.js +2 -6
  34. package/dist/meeting/locusMediaRequest.js.map +1 -1
  35. package/dist/meeting/request.js +21 -29
  36. package/dist/meeting/request.js.map +1 -1
  37. package/dist/meeting/util.js +95 -59
  38. package/dist/meeting/util.js.map +1 -1
  39. package/dist/meeting-info/meeting-info-v2.js +29 -17
  40. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  41. package/dist/meetings/index.js +8 -3
  42. package/dist/meetings/index.js.map +1 -1
  43. package/dist/members/index.js +3 -2
  44. package/dist/members/index.js.map +1 -1
  45. package/dist/members/util.js +13 -7
  46. package/dist/members/util.js.map +1 -1
  47. package/dist/metrics/constants.js +3 -1
  48. package/dist/metrics/constants.js.map +1 -1
  49. package/dist/multistream/remoteMedia.js +30 -15
  50. package/dist/multistream/remoteMedia.js.map +1 -1
  51. package/dist/reachability/clusterReachability.js +12 -15
  52. package/dist/reachability/clusterReachability.js.map +1 -1
  53. package/dist/reachability/index.js +433 -136
  54. package/dist/reachability/index.js.map +1 -1
  55. package/dist/reachability/reachability.types.js +7 -0
  56. package/dist/reachability/reachability.types.js.map +1 -0
  57. package/dist/reachability/request.js +23 -9
  58. package/dist/reachability/request.js.map +1 -1
  59. package/dist/recording-controller/enums.js +8 -4
  60. package/dist/recording-controller/enums.js.map +1 -1
  61. package/dist/recording-controller/index.js +18 -9
  62. package/dist/recording-controller/index.js.map +1 -1
  63. package/dist/recording-controller/util.js +13 -9
  64. package/dist/recording-controller/util.js.map +1 -1
  65. package/dist/roap/index.js +5 -7
  66. package/dist/roap/index.js.map +1 -1
  67. package/dist/roap/request.js +45 -79
  68. package/dist/roap/request.js.map +1 -1
  69. package/dist/roap/turnDiscovery.js +3 -6
  70. package/dist/roap/turnDiscovery.js.map +1 -1
  71. package/dist/types/common/errors/{webinar-registration-error.d.ts → join-webinar-error.d.ts} +2 -2
  72. package/dist/types/config.d.ts +2 -0
  73. package/dist/types/constants.d.ts +38 -1
  74. package/dist/types/controls-options-manager/enums.d.ts +2 -1
  75. package/dist/types/controls-options-manager/index.d.ts +2 -1
  76. package/dist/types/controls-options-manager/types.d.ts +2 -0
  77. package/dist/types/index.d.ts +2 -2
  78. package/dist/types/locus-info/index.d.ts +9 -0
  79. package/dist/types/meeting/in-meeting-actions.d.ts +28 -0
  80. package/dist/types/meeting/index.d.ts +34 -3
  81. package/dist/types/meeting/locusMediaRequest.d.ts +2 -3
  82. package/dist/types/meeting/request.d.ts +2 -2
  83. package/dist/types/meeting/util.d.ts +2 -2
  84. package/dist/types/meeting-info/meeting-info-v2.d.ts +4 -4
  85. package/dist/types/meetings/index.d.ts +4 -1
  86. package/dist/types/members/index.d.ts +2 -1
  87. package/dist/types/members/util.d.ts +5 -1
  88. package/dist/types/metrics/constants.d.ts +3 -1
  89. package/dist/types/reachability/clusterReachability.d.ts +1 -10
  90. package/dist/types/reachability/index.d.ts +74 -35
  91. package/dist/types/reachability/reachability.types.d.ts +64 -0
  92. package/dist/types/reachability/request.d.ts +5 -1
  93. package/dist/types/recording-controller/enums.d.ts +5 -2
  94. package/dist/types/recording-controller/index.d.ts +1 -0
  95. package/dist/types/recording-controller/util.d.ts +2 -1
  96. package/dist/types/roap/request.d.ts +1 -13
  97. package/dist/webinar/index.js +382 -19
  98. package/dist/webinar/index.js.map +1 -1
  99. package/package.json +22 -22
  100. package/src/common/errors/join-webinar-error.ts +24 -0
  101. package/src/config.ts +2 -0
  102. package/src/constants.ts +49 -3
  103. package/src/controls-options-manager/enums.ts +1 -0
  104. package/src/controls-options-manager/index.ts +19 -2
  105. package/src/controls-options-manager/types.ts +2 -0
  106. package/src/controls-options-manager/util.ts +12 -0
  107. package/src/index.ts +2 -2
  108. package/src/locus-info/controlsUtils.ts +46 -2
  109. package/src/locus-info/fullState.ts +1 -0
  110. package/src/locus-info/index.ts +60 -0
  111. package/src/media/index.ts +15 -0
  112. package/src/meeting/in-meeting-actions.ts +58 -0
  113. package/src/meeting/index.ts +232 -25
  114. package/src/meeting/locusMediaRequest.ts +4 -8
  115. package/src/meeting/request.ts +4 -11
  116. package/src/meeting/util.ts +25 -4
  117. package/src/meeting-info/meeting-info-v2.ts +23 -11
  118. package/src/meetings/index.ts +54 -41
  119. package/src/members/index.ts +4 -2
  120. package/src/members/util.ts +4 -1
  121. package/src/metrics/constants.ts +3 -1
  122. package/src/multistream/remoteMedia.ts +28 -15
  123. package/src/reachability/clusterReachability.ts +5 -15
  124. package/src/reachability/index.ts +285 -77
  125. package/src/reachability/reachability.types.ts +85 -0
  126. package/src/reachability/request.ts +55 -30
  127. package/src/recording-controller/enums.ts +5 -2
  128. package/src/recording-controller/index.ts +17 -4
  129. package/src/recording-controller/util.ts +20 -5
  130. package/src/roap/index.ts +4 -5
  131. package/src/roap/request.ts +30 -44
  132. package/src/roap/turnDiscovery.ts +2 -4
  133. package/src/webinar/index.ts +223 -17
  134. package/test/unit/spec/controls-options-manager/index.js +56 -32
  135. package/test/unit/spec/controls-options-manager/util.js +44 -0
  136. package/test/unit/spec/locus-info/controlsUtils.js +80 -4
  137. package/test/unit/spec/locus-info/index.js +59 -2
  138. package/test/unit/spec/meeting/in-meeting-actions.ts +31 -1
  139. package/test/unit/spec/meeting/index.js +369 -103
  140. package/test/unit/spec/meeting/locusMediaRequest.ts +18 -11
  141. package/test/unit/spec/meeting/request.js +3 -26
  142. package/test/unit/spec/meeting/utils.js +55 -13
  143. package/test/unit/spec/meeting-info/meetinginfov2.js +9 -4
  144. package/test/unit/spec/meetings/index.js +25 -6
  145. package/test/unit/spec/members/index.js +25 -2
  146. package/test/unit/spec/members/request.js +37 -3
  147. package/test/unit/spec/members/utils.js +110 -1
  148. package/test/unit/spec/multistream/remoteMedia.ts +11 -7
  149. package/test/unit/spec/reachability/clusterReachability.ts +7 -0
  150. package/test/unit/spec/reachability/index.ts +265 -1
  151. package/test/unit/spec/reachability/request.js +56 -15
  152. package/test/unit/spec/recording-controller/index.js +61 -5
  153. package/test/unit/spec/recording-controller/util.js +39 -3
  154. package/test/unit/spec/roap/index.ts +1 -1
  155. package/test/unit/spec/roap/request.ts +51 -109
  156. package/test/unit/spec/roap/turnDiscovery.ts +202 -147
  157. package/test/unit/spec/webinar/index.ts +443 -14
  158. package/dist/common/errors/webinar-registration-error.js.map +0 -1
  159. package/src/common/errors/webinar-registration-error.ts +0 -27
@@ -90,15 +90,15 @@ import WebExMeetingsErrors from '../../../../src/common/errors/webex-meetings-er
90
90
  import ParameterError from '../../../../src/common/errors/parameter';
91
91
  import PasswordError from '../../../../src/common/errors/password-error';
92
92
  import CaptchaError from '../../../../src/common/errors/captcha-error';
93
- import PermissionError from '../../../../src/common/errors/permission';
94
- import WebinarRegistrationError from '../../../../src/common/errors/webinar-registration-error';
93
+ import PermissionError from '../../../../src/common/errors/permission';
94
+ import JoinWebinarError from '../../../../src/common/errors/join-webinar-error';
95
95
  import IntentToJoinError from '../../../../src/common/errors/intent-to-join';
96
96
  import testUtils from '../../../utils/testUtils';
97
97
  import {
98
98
  MeetingInfoV2CaptchaError,
99
99
  MeetingInfoV2PasswordError,
100
100
  MeetingInfoV2PolicyError,
101
- MeetingInfoV2WebinarRegistrationError,
101
+ MeetingInfoV2JoinWebinarError,
102
102
  } from '../../../../src/meeting-info/meeting-info-v2';
103
103
  import {
104
104
  DTLS_HANDSHAKE_FAILED_CLIENT_CODE,
@@ -377,7 +377,10 @@ describe('plugin-meetings', () => {
377
377
  }
378
378
  );
379
379
  assert.equal(newMeeting.correlationId, newMeeting.id);
380
- assert.deepEqual(newMeeting.callStateForMetrics, {correlationId: newMeeting.id, sessionCorrelationId: ''});
380
+ assert.deepEqual(newMeeting.callStateForMetrics, {
381
+ correlationId: newMeeting.id,
382
+ sessionCorrelationId: '',
383
+ });
381
384
  });
382
385
 
383
386
  it('correlationId can be provided in callStateForMetrics', () => {
@@ -646,7 +649,6 @@ describe('plugin-meetings', () => {
646
649
  });
647
650
 
648
651
  const fakeRoapMessage = {id: 'fake TURN discovery message'};
649
- const fakeReachabilityResults = {id: 'fake reachability'};
650
652
  const fakeTurnServerInfo = {id: 'fake turn info'};
651
653
  const fakeJoinResult = {id: 'join result'};
652
654
 
@@ -664,8 +666,6 @@ describe('plugin-meetings', () => {
664
666
  .stub(meeting, 'addMediaInternal')
665
667
  .returns(Promise.resolve(test4));
666
668
 
667
- webex.meetings.reachability.getReachabilityResults.resolves(fakeReachabilityResults);
668
-
669
669
  generateTurnDiscoveryRequestMessageStub = sinon
670
670
  .stub(meeting.roap, 'generateTurnDiscoveryRequestMessage')
671
671
  .resolves({roapMessage: fakeRoapMessage});
@@ -685,7 +685,6 @@ describe('plugin-meetings', () => {
685
685
  assert.calledOnceWithExactly(meeting.join, {
686
686
  ...joinOptions,
687
687
  roapMessage: fakeRoapMessage,
688
- reachability: fakeReachabilityResults,
689
688
  });
690
689
  assert.calledOnceWithExactly(generateTurnDiscoveryRequestMessageStub, meeting, true);
691
690
  assert.calledOnceWithExactly(
@@ -722,7 +721,6 @@ describe('plugin-meetings', () => {
722
721
  assert.calledOnceWithExactly(meeting.join, {
723
722
  ...joinOptions,
724
723
  roapMessage: undefined,
725
- reachability: fakeReachabilityResults,
726
724
  });
727
725
  assert.calledOnceWithExactly(generateTurnDiscoveryRequestMessageStub, meeting, true);
728
726
  assert.notCalled(handleTurnDiscoveryHttpResponseStub);
@@ -754,7 +752,6 @@ describe('plugin-meetings', () => {
754
752
  assert.calledOnceWithExactly(meeting.join, {
755
753
  ...joinOptions,
756
754
  roapMessage: fakeRoapMessage,
757
- reachability: fakeReachabilityResults,
758
755
  });
759
756
  assert.calledOnceWithExactly(generateTurnDiscoveryRequestMessageStub, meeting, true);
760
757
  assert.calledOnceWithExactly(
@@ -1708,6 +1705,12 @@ describe('plugin-meetings', () => {
1708
1705
  sinon.assert.called(setCorrelationIdSpy);
1709
1706
  assert.equal(meeting.correlationId, '123');
1710
1707
  });
1708
+
1709
+ it('should not send client.call.initiated if told not to', async () => {
1710
+ await meeting.join({sendCallInitiated: false});
1711
+
1712
+ sinon.assert.notCalled(webex.internal.newMetrics.submitClientEvent);
1713
+ });
1711
1714
  });
1712
1715
 
1713
1716
  describe('failure', () => {
@@ -2468,6 +2471,61 @@ describe('plugin-meetings', () => {
2468
2471
  checkWorking();
2469
2472
  });
2470
2473
 
2474
+ it('should upload logs periodically', async () => {
2475
+ const clock = sinon.useFakeTimers();
2476
+
2477
+ meeting.roap.doTurnDiscovery = sinon
2478
+ .stub()
2479
+ .resolves({turnServerInfo: undefined, turnDiscoverySkippedReason: undefined});
2480
+
2481
+ let logUploadCounter = 0;
2482
+
2483
+ TriggerProxy.trigger.callsFake((meetingObject, options, event) => {
2484
+ if (
2485
+ meetingObject === meeting &&
2486
+ options.file === 'meeting/index' &&
2487
+ options.function === 'uploadLogs' &&
2488
+ event === 'REQUEST_UPLOAD_LOGS'
2489
+ ) {
2490
+ logUploadCounter += 1;
2491
+ }
2492
+ });
2493
+
2494
+ meeting.config.logUploadIntervalMultiplicationFactor = 1;
2495
+ meeting.meetingState = 'ACTIVE';
2496
+
2497
+ await meeting.addMedia({
2498
+ mediaSettings: {},
2499
+ });
2500
+
2501
+ const checkLogCounter = (delayInMinutes, expectedCounter) => {
2502
+ const delayInMilliseconds = delayInMinutes * 60 * 1000;
2503
+
2504
+ // first check that the counter is not increased just before the delay
2505
+ clock.tick(delayInMilliseconds - 50);
2506
+ assert.equal(logUploadCounter, expectedCounter - 1);
2507
+
2508
+ // and now check that it has reached expected value after the delay
2509
+ clock.tick(50);
2510
+ assert.equal(logUploadCounter, expectedCounter);
2511
+ };
2512
+
2513
+ checkLogCounter(0.1, 1);
2514
+ checkLogCounter(15, 2);
2515
+ checkLogCounter(30, 3);
2516
+ checkLogCounter(60, 4);
2517
+ checkLogCounter(60, 5);
2518
+
2519
+ // simulate media connection being removed -> 1 more upload should happen, but nothing more afterwards
2520
+ meeting.mediaProperties.webrtcMediaConnection = undefined;
2521
+ checkLogCounter(60, 6);
2522
+
2523
+ clock.tick(120 * 1000 * 60);
2524
+ assert.equal(logUploadCounter, 6);
2525
+
2526
+ clock.restore();
2527
+ });
2528
+
2471
2529
  it('should attach the media and return promise when in the lobby if allowMediaInLobby is set', async () => {
2472
2530
  meeting.roap.doTurnDiscovery = sinon
2473
2531
  .stub()
@@ -3442,47 +3500,60 @@ describe('plugin-meetings', () => {
3442
3500
  });
3443
3501
  });
3444
3502
 
3445
- it('should pass bundlePolicy to createMediaConnection', async () => {
3503
+ describe('bundlePolicy', () => {
3446
3504
  const FAKE_TURN_URL = 'turns:webex.com:3478';
3447
3505
  const FAKE_TURN_USER = 'some-turn-username';
3448
3506
  const FAKE_TURN_PASSWORD = 'some-password';
3449
3507
 
3450
- meeting.meetingState = 'ACTIVE';
3451
- Media.createMediaConnection.resetHistory();
3452
-
3453
- meeting.roap.doTurnDiscovery = sinon.stub().resolves({
3454
- turnServerInfo: {
3455
- url: FAKE_TURN_URL,
3456
- username: FAKE_TURN_USER,
3457
- password: FAKE_TURN_PASSWORD,
3458
- },
3459
- turnDiscoverySkippedReason: undefined,
3460
- });
3461
- const media = meeting.addMedia({
3462
- mediaSettings: {},
3463
- bundlePolicy: 'bundlePolicy-value',
3464
- });
3508
+ beforeEach(() => {
3509
+ meeting.meetingState = 'ACTIVE';
3510
+ Media.createMediaConnection.resetHistory();
3465
3511
 
3466
- assert.exists(media);
3467
- await media;
3468
- assert.calledOnce(meeting.roap.doTurnDiscovery);
3469
- assert.calledWith(meeting.roap.doTurnDiscovery, meeting, false);
3470
- assert.calledOnce(Media.createMediaConnection);
3471
- assert.calledWith(
3472
- Media.createMediaConnection,
3473
- false,
3474
- meeting.getMediaConnectionDebugId(),
3475
- meeting.id,
3476
- sinon.match({
3512
+ meeting.roap.doTurnDiscovery = sinon.stub().resolves({
3477
3513
  turnServerInfo: {
3478
3514
  url: FAKE_TURN_URL,
3479
3515
  username: FAKE_TURN_USER,
3480
3516
  password: FAKE_TURN_PASSWORD,
3481
3517
  },
3482
- bundlePolicy: 'bundlePolicy-value',
3483
- })
3484
- );
3485
- assert.calledOnce(fakeMediaConnection.initiateOffer);
3518
+ turnDiscoverySkippedReason: undefined,
3519
+ });
3520
+ });
3521
+
3522
+ const runCheck = async (bundlePolicy, expectedValue) => {
3523
+ const media = meeting.addMedia({
3524
+ mediaSettings: {},
3525
+ bundlePolicy,
3526
+ });
3527
+
3528
+ assert.exists(media);
3529
+ await media;
3530
+ assert.calledOnce(meeting.roap.doTurnDiscovery);
3531
+ assert.calledWith(meeting.roap.doTurnDiscovery, meeting, false);
3532
+ assert.calledOnce(Media.createMediaConnection);
3533
+ assert.calledWith(
3534
+ Media.createMediaConnection,
3535
+ false,
3536
+ meeting.getMediaConnectionDebugId(),
3537
+ meeting.id,
3538
+ sinon.match({
3539
+ turnServerInfo: {
3540
+ url: FAKE_TURN_URL,
3541
+ username: FAKE_TURN_USER,
3542
+ password: FAKE_TURN_PASSWORD,
3543
+ },
3544
+ bundlePolicy: expectedValue,
3545
+ })
3546
+ );
3547
+ assert.calledOnce(fakeMediaConnection.initiateOffer);
3548
+ };
3549
+
3550
+ it('should pass bundlePolicy to createMediaConnection', async () => {
3551
+ await runCheck('max-compat', 'max-compat');
3552
+ });
3553
+
3554
+ it('should pass max-bundle to createMediaConnection if bundlePolicy is not provided', async () => {
3555
+ await runCheck(undefined, 'max-bundle');
3556
+ });
3486
3557
  });
3487
3558
 
3488
3559
  it('succeeds even if getDevices() throws', async () => {
@@ -3676,6 +3747,12 @@ describe('plugin-meetings', () => {
3676
3747
  meeting.setMercuryListener = sinon.stub();
3677
3748
  meeting.locusInfo.onFullLocus = sinon.stub();
3678
3749
  meeting.webex.meetings.geoHintInfo = {regionCode: 'EU', countryCode: 'UK'};
3750
+ meeting.webex.meetings.reachability.getReachabilityReportToAttachToRoap = sinon
3751
+ .stub()
3752
+ .resolves({id: 'fake reachability'});
3753
+ meeting.webex.meetings.reachability.getClientMediaPreferences = sinon
3754
+ .stub()
3755
+ .resolves({id: 'fake clientMediaPreferences'});
3679
3756
  meeting.roap.doTurnDiscovery = sinon.stub().resolves({
3680
3757
  turnServerInfo: {
3681
3758
  url: 'turns:turn-server-url:443?transport=tcp',
@@ -3795,12 +3872,12 @@ describe('plugin-meetings', () => {
3795
3872
  id: 'fake locus from mocked join request',
3796
3873
  locusUrl: 'fake locus url',
3797
3874
  mediaId: 'fake media id',
3798
- })
3875
+ });
3799
3876
  sinon.stub(meeting.meetingRequest, 'joinMeeting').resolves({
3800
3877
  headers: {
3801
3878
  trackingid: 'fake tracking id',
3802
- }
3803
- })
3879
+ },
3880
+ });
3804
3881
  await meeting.join({enableMultistream: isMultistream});
3805
3882
  });
3806
3883
 
@@ -3861,6 +3938,15 @@ describe('plugin-meetings', () => {
3861
3938
  const checkSdpOfferSent = ({audioMuted, videoMuted}) => {
3862
3939
  const {sdp, seq, tieBreaker} = roapOfferMessage;
3863
3940
 
3941
+ assert.calledWith(
3942
+ meeting.webex.meetings.reachability.getClientMediaPreferences,
3943
+ meeting.isMultistream,
3944
+ 0
3945
+ );
3946
+ assert.calledWith(
3947
+ meeting.webex.meetings.reachability.getReachabilityReportToAttachToRoap
3948
+ );
3949
+
3864
3950
  assert.calledWith(locusMediaRequestStub, {
3865
3951
  method: 'PUT',
3866
3952
  uri: `${meeting.selfUrl}/media`,
@@ -3874,14 +3960,12 @@ describe('plugin-meetings', () => {
3874
3960
  correlationId: meeting.correlationId,
3875
3961
  localMedias: [
3876
3962
  {
3877
- localSdp: `{"audioMuted":${audioMuted},"videoMuted":${videoMuted},"roapMessage":{"messageType":"OFFER","sdps":["${sdp}"],"version":"2","seq":"${seq}","tieBreaker":"${tieBreaker}","headers":["includeAnswerInHttpResponse","noOkInTransaction"]}}`,
3963
+ localSdp: `{"audioMuted":${audioMuted},"videoMuted":${videoMuted},"roapMessage":{"messageType":"OFFER","sdps":["${sdp}"],"version":"2","seq":"${seq}","tieBreaker":"${tieBreaker}","headers":["includeAnswerInHttpResponse","noOkInTransaction"]},"reachability":{"id":"fake reachability"}}`,
3878
3964
  mediaId: 'fake media id',
3879
3965
  },
3880
3966
  ],
3881
3967
  clientMediaPreferences: {
3882
- preferTranscoding: !meeting.isMultistream,
3883
- joinCookie: undefined,
3884
- ipver: 0,
3968
+ id: 'fake clientMediaPreferences',
3885
3969
  },
3886
3970
  },
3887
3971
  });
@@ -3902,13 +3986,11 @@ describe('plugin-meetings', () => {
3902
3986
  },
3903
3987
  correlationId: meeting.correlationId,
3904
3988
  clientMediaPreferences: {
3905
- preferTranscoding: !meeting.isMultistream,
3906
- ipver: undefined,
3907
- joinCookie: undefined,
3989
+ id: 'fake clientMediaPreferences',
3908
3990
  },
3909
3991
  localMedias: [
3910
3992
  {
3911
- localSdp: `{"audioMuted":${audioMuted},"videoMuted":${videoMuted},"roapMessage":{"messageType":"OK","version":"2","seq":"${seq}"}}`,
3993
+ localSdp: `{"audioMuted":${audioMuted},"videoMuted":${videoMuted},"roapMessage":{"messageType":"OK","version":"2","seq":"${seq}"},"reachability":{"id":"fake reachability"}}`,
3912
3994
  mediaId: 'fake media id',
3913
3995
  },
3914
3996
  ],
@@ -3934,10 +4016,6 @@ describe('plugin-meetings', () => {
3934
4016
  mediaId: 'fake media id',
3935
4017
  },
3936
4018
  ],
3937
- clientMediaPreferences: {
3938
- preferTranscoding: !meeting.isMultistream,
3939
- ipver: undefined,
3940
- },
3941
4019
  respOnlySdp: true,
3942
4020
  usingResource: null,
3943
4021
  },
@@ -3993,7 +4071,10 @@ describe('plugin-meetings', () => {
3993
4071
  assert.notCalled(
3994
4072
  meeting.sendSlotManager.getSlot(MediaType.AudioMain).publishStream
3995
4073
  );
3996
- assert.throws(meeting.publishStreams(localStreams), `Attempted to publish microphone stream with ended readyState, correlationId=${meeting.correlationId}`);
4074
+ assert.throws(
4075
+ meeting.publishStreams(localStreams),
4076
+ `Attempted to publish microphone stream with ended readyState, correlationId=${meeting.correlationId}`
4077
+ );
3997
4078
  } else {
3998
4079
  assert.calledOnceWithExactly(
3999
4080
  meeting.sendSlotManager.getSlot(MediaType.AudioMain).publishStream,
@@ -4006,7 +4087,10 @@ describe('plugin-meetings', () => {
4006
4087
  assert.notCalled(
4007
4088
  meeting.sendSlotManager.getSlot(MediaType.VideoMain).publishStream
4008
4089
  );
4009
- assert.throws(meeting.publishStreams(localStreams), `Attempted to publish camera stream with ended readyState, correlationId=${meeting.correlationId}`);
4090
+ assert.throws(
4091
+ meeting.publishStreams(localStreams),
4092
+ `Attempted to publish camera stream with ended readyState, correlationId=${meeting.correlationId}`
4093
+ );
4010
4094
  } else {
4011
4095
  assert.calledOnceWithExactly(
4012
4096
  meeting.sendSlotManager.getSlot(MediaType.VideoMain).publishStream,
@@ -4019,7 +4103,10 @@ describe('plugin-meetings', () => {
4019
4103
  assert.notCalled(
4020
4104
  meeting.sendSlotManager.getSlot(MediaType.AudioSlides).publishStream
4021
4105
  );
4022
- assert.throws(meeting.publishStreams(localStreams), `Attempted to publish screenShare audio stream with ended readyState, correlationId=${meeting.correlationId}`);
4106
+ assert.throws(
4107
+ meeting.publishStreams(localStreams),
4108
+ `Attempted to publish screenShare audio stream with ended readyState, correlationId=${meeting.correlationId}`
4109
+ );
4023
4110
  } else {
4024
4111
  assert.calledOnceWithExactly(
4025
4112
  meeting.sendSlotManager.getSlot(MediaType.AudioSlides).publishStream,
@@ -4032,7 +4119,10 @@ describe('plugin-meetings', () => {
4032
4119
  assert.notCalled(
4033
4120
  meeting.sendSlotManager.getSlot(MediaType.VideoSlides).publishStream
4034
4121
  );
4035
- assert.throws(meeting.publishStreams(localStreams), `Attempted to publish screenShare video stream with ended readyState, correlationId=${meeting.correlationId}`);
4122
+ assert.throws(
4123
+ meeting.publishStreams(localStreams),
4124
+ `Attempted to publish screenShare video stream with ended readyState, correlationId=${meeting.correlationId}`
4125
+ );
4036
4126
  } else {
4037
4127
  assert.calledOnceWithExactly(
4038
4128
  meeting.sendSlotManager.getSlot(MediaType.VideoSlides).publishStream,
@@ -4327,14 +4417,14 @@ describe('plugin-meetings', () => {
4327
4417
  const handleDeviceLoggingSpy = sinon.spy(Meeting, 'handleDeviceLogging');
4328
4418
  await meeting.addMedia({audioEnabled: false});
4329
4419
  //calling handleDeviceLogging with audioEnaled as true adn videoEnabled as false
4330
- assert.calledWith(handleDeviceLoggingSpy,false,true);
4420
+ assert.calledWith(handleDeviceLoggingSpy, false, true);
4331
4421
  });
4332
4422
 
4333
4423
  it('addMedia() works correctly when video is disabled with no streams to publish', async () => {
4334
4424
  const handleDeviceLoggingSpy = sinon.spy(Meeting, 'handleDeviceLogging');
4335
4425
  await meeting.addMedia({videoEnabled: false});
4336
4426
  //calling handleDeviceLogging audioEnabled as true videoEnabled as false
4337
- assert.calledWith(handleDeviceLoggingSpy,true,false);
4427
+ assert.calledWith(handleDeviceLoggingSpy, true, false);
4338
4428
  });
4339
4429
 
4340
4430
  it('addMedia() works correctly when video is disabled with no streams to publish', async () => {
@@ -4403,12 +4493,11 @@ describe('plugin-meetings', () => {
4403
4493
  assert.calledTwice(locusMediaRequestStub);
4404
4494
  });
4405
4495
 
4406
-
4407
4496
  it('addMedia() works correctly when both shareAudio and shareVideo is disabled with no streams publish', async () => {
4408
4497
  const handleDeviceLoggingSpy = sinon.spy(Meeting, 'handleDeviceLogging');
4409
4498
  await meeting.addMedia({shareAudioEnabled: false, shareVideoEnabled: false});
4410
4499
  //calling handleDeviceLogging with audioEnabled true and videoEnabled as true
4411
- assert.calledWith(handleDeviceLoggingSpy,true,true);
4500
+ assert.calledWith(handleDeviceLoggingSpy, true, true);
4412
4501
  });
4413
4502
 
4414
4503
  describe('publishStreams()/unpublishStreams() calls', () => {
@@ -6257,20 +6346,73 @@ describe('plugin-meetings', () => {
6257
6346
  assert.equal(meeting.fetchMeetingInfoTimeoutId, undefined);
6258
6347
  });
6259
6348
 
6260
- it('handles meetingInfoProvider webinar need registration error', async () => {
6349
+ it('handles MeetingInfoV2JoinWebinarError webinar need registration', async () => {
6261
6350
  meeting.destination = FAKE_DESTINATION;
6262
6351
  meeting.destinationType = FAKE_TYPE;
6263
6352
  meeting.attrs.meetingInfoProvider = {
6264
6353
  fetchMeetingInfo: sinon
6265
6354
  .stub()
6266
- .throws(new MeetingInfoV2WebinarRegistrationError(403021, FAKE_MEETING_INFO, 'a message')),
6355
+ .throws(
6356
+ new MeetingInfoV2JoinWebinarError(403021, FAKE_MEETING_INFO, 'a message')
6357
+ ),
6267
6358
  };
6268
6359
 
6269
- await assert.isRejected(meeting.fetchMeetingInfo({sendCAevents: true}), WebinarRegistrationError);
6360
+ await assert.isRejected(
6361
+ meeting.fetchMeetingInfo({sendCAevents: true}),
6362
+ JoinWebinarError
6363
+ );
6364
+
6365
+ assert.deepEqual(meeting.meetingInfo, FAKE_MEETING_INFO);
6366
+ assert.equal(
6367
+ meeting.meetingInfoFailureReason,
6368
+ MEETING_INFO_FAILURE_REASON.WEBINAR_REGISTRATION
6369
+ );
6370
+ });
6371
+
6372
+ it('handles MeetingInfoV2JoinWebinarError webinar need join with webcast', async () => {
6373
+ meeting.destination = FAKE_DESTINATION;
6374
+ meeting.destinationType = FAKE_TYPE;
6375
+ meeting.attrs.meetingInfoProvider = {
6376
+ fetchMeetingInfo: sinon
6377
+ .stub()
6378
+ .throws(
6379
+ new MeetingInfoV2JoinWebinarError(403026, FAKE_MEETING_INFO, 'a message')
6380
+ ),
6381
+ };
6382
+
6383
+ await assert.isRejected(
6384
+ meeting.fetchMeetingInfo({sendCAevents: true}),
6385
+ JoinWebinarError
6386
+ );
6387
+
6388
+ assert.deepEqual(meeting.meetingInfo, FAKE_MEETING_INFO);
6389
+ assert.equal(
6390
+ meeting.meetingInfoFailureReason,
6391
+ MEETING_INFO_FAILURE_REASON.NEED_JOIN_WITH_WEBCAST
6392
+ );
6393
+ });
6394
+
6395
+ it('handles MeetingInfoV2JoinWebinarError webinar need registrationId', async () => {
6396
+ meeting.destination = FAKE_DESTINATION;
6397
+ meeting.destinationType = FAKE_TYPE;
6398
+ meeting.attrs.meetingInfoProvider = {
6399
+ fetchMeetingInfo: sinon
6400
+ .stub()
6401
+ .throws(
6402
+ new MeetingInfoV2JoinWebinarError(403037, FAKE_MEETING_INFO, 'a message')
6403
+ ),
6404
+ };
6405
+
6406
+ await assert.isRejected(
6407
+ meeting.fetchMeetingInfo({sendCAevents: true}),
6408
+ JoinWebinarError
6409
+ );
6270
6410
 
6271
6411
  assert.deepEqual(meeting.meetingInfo, FAKE_MEETING_INFO);
6272
- assert.equal(meeting.meetingInfoFailureCode, 403021);
6273
- assert.equal(meeting.meetingInfoFailureReason, MEETING_INFO_FAILURE_REASON.WEBINAR_REGISTRATION);
6412
+ assert.equal(
6413
+ meeting.meetingInfoFailureReason,
6414
+ MEETING_INFO_FAILURE_REASON.WEBINAR_NEED_REGISTRATIONID
6415
+ );
6274
6416
  });
6275
6417
  });
6276
6418
 
@@ -6985,7 +7127,10 @@ describe('plugin-meetings', () => {
6985
7127
  assert.deepEqual(meeting.callStateForMetrics, {correlationId, sessionCorrelationId: ''});
6986
7128
  meeting.setCorrelationId(uuid1);
6987
7129
  assert.equal(meeting.correlationId, uuid1);
6988
- assert.deepEqual(meeting.callStateForMetrics, {correlationId: uuid1, sessionCorrelationId: ''});
7130
+ assert.deepEqual(meeting.callStateForMetrics, {
7131
+ correlationId: uuid1,
7132
+ sessionCorrelationId: '',
7133
+ });
6989
7134
  });
6990
7135
  });
6991
7136
 
@@ -7657,11 +7802,11 @@ describe('plugin-meetings', () => {
7657
7802
  id: 'stream',
7658
7803
  getTracks: () => [{id: 'track', addEventListener: sinon.stub()}],
7659
7804
  };
7660
- const simulateConnectionStateChange = (newState) => {
7805
+ const simulateConnectionStateChange = async (newState) => {
7661
7806
  meeting.mediaProperties.webrtcMediaConnection.getConnectionState = sinon
7662
7807
  .stub()
7663
7808
  .returns(newState);
7664
- eventListeners[MediaConnectionEventNames.PEER_CONNECTION_STATE_CHANGED]();
7809
+ await eventListeners[MediaConnectionEventNames.PEER_CONNECTION_STATE_CHANGED]();
7665
7810
  };
7666
7811
 
7667
7812
  beforeEach(() => {
@@ -7731,11 +7876,19 @@ describe('plugin-meetings', () => {
7731
7876
  });
7732
7877
 
7733
7878
  it('should collect ice candidates', () => {
7734
- eventListeners[MediaConnectionEventNames.ICE_CANDIDATE]({candidate: 'candidate'});
7879
+ eventListeners[MediaConnectionEventNames.ICE_CANDIDATE]({
7880
+ candidate: {candidate: 'candidate'},
7881
+ });
7735
7882
 
7736
7883
  assert.equal(meeting.iceCandidatesCount, 1);
7737
7884
  });
7738
7885
 
7886
+ it('should not collect empty ice candidates', () => {
7887
+ eventListeners[MediaConnectionEventNames.ICE_CANDIDATE]({candidate: {candidate: ''}});
7888
+
7889
+ assert.equal(meeting.iceCandidatesCount, 0);
7890
+ });
7891
+
7739
7892
  it('should not collect null ice candidates', () => {
7740
7893
  eventListeners[MediaConnectionEventNames.ICE_CANDIDATE]({candidate: null});
7741
7894
 
@@ -7917,7 +8070,7 @@ describe('plugin-meetings', () => {
7917
8070
  meeting.reconnectionManager = new ReconnectionManager(meeting);
7918
8071
  meeting.reconnectionManager.iceReconnected = sinon.stub().returns(undefined);
7919
8072
  meeting.setNetworkStatus = sinon.stub().returns(undefined);
7920
- meeting.statsAnalyzer = {startAnalyzer: sinon.stub()};
8073
+ meeting.statsAnalyzer = {startAnalyzer: sinon.stub(), stopAnalyzer: sinon.stub()};
7921
8074
  meeting.mediaProperties.webrtcMediaConnection = {
7922
8075
  // mock the on() method and store all the listeners
7923
8076
  on: sinon.stub().callsFake((event, listener) => {
@@ -7992,10 +8145,10 @@ describe('plugin-meetings', () => {
7992
8145
  });
7993
8146
 
7994
8147
  describe('CONNECTION_STATE_CHANGED event when state = "Failed"', () => {
7995
- const mockFailedEvent = () => {
8148
+ const mockFailedEvent = async () => {
7996
8149
  meeting.setupMediaConnectionListeners();
7997
8150
 
7998
- simulateConnectionStateChange(ConnectionState.Failed);
8151
+ await simulateConnectionStateChange(ConnectionState.Failed);
7999
8152
  };
8000
8153
 
8001
8154
  const checkBehavioralMetricSent = (hasMediaConnectionConnectedAtLeastOnce = false) => {
@@ -8025,6 +8178,22 @@ describe('plugin-meetings', () => {
8025
8178
  assert.notCalled(webex.internal.newMetrics.submitClientEvent);
8026
8179
  checkBehavioralMetricSent(true);
8027
8180
  });
8181
+
8182
+ it('stop stats analyzer during reconnection ', async () => {
8183
+ meeting.hasMediaConnectionConnectedAtLeastOnce = true;
8184
+ meeting.statsAnalyzer.stopAnalyzer = sinon.stub().resolves();
8185
+ meeting.reconnectionManager = {
8186
+ reconnect: sinon.stub().resolves(),
8187
+ resetReconnectionTimer: () => {},
8188
+ };
8189
+ meeting.currentMediaStatus = {
8190
+ video: true,
8191
+ };
8192
+
8193
+ await mockFailedEvent();
8194
+
8195
+ assert.calledOnce(meeting.statsAnalyzer.stopAnalyzer);
8196
+ });
8028
8197
  });
8029
8198
 
8030
8199
  describe('should send correct metrics for ROAP_FAILURE event', () => {
@@ -8569,6 +8738,13 @@ describe('plugin-meetings', () => {
8569
8738
  {payload: test1}
8570
8739
  );
8571
8740
  assert.calledOnce(meeting.updateLLMConnection);
8741
+ assert.calledOnceWithExactly(
8742
+ Metrics.sendBehavioralMetric,
8743
+ BEHAVIORAL_METRICS.GUEST_ENTERED_LOBBY,
8744
+ {
8745
+ correlation_id: meeting.correlationId,
8746
+ }
8747
+ );
8572
8748
  done();
8573
8749
  });
8574
8750
  it('listens to the self admitted guest event', (done) => {
@@ -8590,6 +8766,13 @@ describe('plugin-meetings', () => {
8590
8766
  assert.calledOnce(meeting.updateLLMConnection);
8591
8767
  assert.calledOnceWithExactly(meeting.rtcMetrics.sendNextMetrics);
8592
8768
 
8769
+ assert.calledOnceWithExactly(
8770
+ Metrics.sendBehavioralMetric,
8771
+ BEHAVIORAL_METRICS.GUEST_EXITED_LOBBY,
8772
+ {
8773
+ correlation_id: meeting.correlationId,
8774
+ }
8775
+ );
8593
8776
  done();
8594
8777
  });
8595
8778
 
@@ -8885,6 +9068,81 @@ describe('plugin-meetings', () => {
8885
9068
  );
8886
9069
  });
8887
9070
 
9071
+ it('listens to MEETING_CONTROLS_WEBCAST_UPDATED', async () => {
9072
+ const state = {example: 'value'};
9073
+
9074
+ await meeting.locusInfo.emitScoped(
9075
+ {function: 'test', file: 'test'},
9076
+ LOCUSINFO.EVENTS.CONTROLS_WEBCAST_CHANGED,
9077
+ {state}
9078
+ );
9079
+
9080
+ assert.calledWith(
9081
+ TriggerProxy.trigger,
9082
+ meeting,
9083
+ {file: 'meeting/index', function: 'setupLocusControlsListener'},
9084
+ EVENT_TRIGGERS.MEETING_CONTROLS_WEBCAST_UPDATED,
9085
+ {state}
9086
+ );
9087
+ });
9088
+
9089
+ it('listens to MEETING_CONTROLS_MEETING_FULL_UPDATED', async () => {
9090
+ const state = {example: 'value'};
9091
+
9092
+ await meeting.locusInfo.emitScoped(
9093
+ {function: 'test', file: 'test'},
9094
+ LOCUSINFO.EVENTS.CONTROLS_MEETING_FULL_CHANGED,
9095
+ {state}
9096
+ );
9097
+
9098
+ assert.calledWith(
9099
+ TriggerProxy.trigger,
9100
+ meeting,
9101
+ {file: 'meeting/index', function: 'setupLocusControlsListener'},
9102
+ EVENT_TRIGGERS.MEETING_CONTROLS_MEETING_FULL_UPDATED,
9103
+ {state}
9104
+ );
9105
+ });
9106
+
9107
+ it('listens to MEETING_CONTROLS_PRACTICE_SESSION_STATUS_UPDATED', async () => {
9108
+ meeting.webinar.updatePracticeSessionStatus = sinon.stub();
9109
+
9110
+ const state = {example: 'value'};
9111
+
9112
+ await meeting.locusInfo.emitScoped(
9113
+ {function: 'test', file: 'test'},
9114
+ LOCUSINFO.EVENTS.CONTROLS_PRACTICE_SESSION_STATUS_UPDATED,
9115
+ {state}
9116
+ );
9117
+
9118
+ assert.calledOnceWithExactly(meeting.webinar.updatePracticeSessionStatus, state);
9119
+ assert.calledWith(
9120
+ TriggerProxy.trigger,
9121
+ meeting,
9122
+ {file: 'meeting/index', function: 'setupLocusControlsListener'},
9123
+ EVENT_TRIGGERS.MEETING_CONTROLS_PRACTICE_SESSION_STATUS_UPDATED,
9124
+ {state}
9125
+ );
9126
+ });
9127
+
9128
+ it('listens to MEETING_CONTROLS_STAGE_VIEW_UPDATED', async () => {
9129
+ const state = {example: 'value'};
9130
+
9131
+ await meeting.locusInfo.emitScoped(
9132
+ {function: 'test', file: 'test'},
9133
+ LOCUSINFO.EVENTS.CONTROLS_STAGE_VIEW_UPDATED,
9134
+ {state}
9135
+ );
9136
+
9137
+ assert.calledWith(
9138
+ TriggerProxy.trigger,
9139
+ meeting,
9140
+ {file: 'meeting/index', function: 'setupLocusControlsListener'},
9141
+ EVENT_TRIGGERS.MEETING_CONTROLS_STAGE_VIEW_UPDATED,
9142
+ {state}
9143
+ );
9144
+ });
9145
+
8888
9146
  it('listens to MEETING_CONTROLS_VIDEO_UPDATED', async () => {
8889
9147
  const state = {example: 'value'};
8890
9148
 
@@ -8998,12 +9256,6 @@ describe('plugin-meetings', () => {
8998
9256
  approval: {
8999
9257
  url: 'url',
9000
9258
  },
9001
- webcast: {
9002
- url: 'url',
9003
- },
9004
- webinarAttendeesSearching: {
9005
- url: 'url',
9006
- },
9007
9259
  },
9008
9260
  };
9009
9261
 
@@ -9017,10 +9269,6 @@ describe('plugin-meetings', () => {
9017
9269
  meeting.simultaneousInterpretation = {
9018
9270
  approvalUrlUpdate: sinon.stub().returns(undefined),
9019
9271
  };
9020
- meeting.webinar = {
9021
- webcastUrlUpdate: sinon.stub().returns(undefined),
9022
- webinarAttendeesSearchingUrlUpdate: sinon.stub().returns(undefined),
9023
- };
9024
9272
 
9025
9273
  meeting.locusInfo.emit(
9026
9274
  {function: 'test', file: 'test'},
@@ -9040,19 +9288,37 @@ describe('plugin-meetings', () => {
9040
9288
  meeting.simultaneousInterpretation.approvalUrlUpdate,
9041
9289
  newLocusServices.services.approval.url
9042
9290
  );
9043
- assert.calledWith(
9044
- meeting.webinar.webcastUrlUpdate,
9045
- newLocusServices.services.webcast.url
9046
- );
9047
- assert.calledWith(
9048
- meeting.webinar.webinarAttendeesSearchingUrlUpdate,
9049
- newLocusServices.services.webinarAttendeesSearching.url
9050
- );
9051
9291
  assert.calledOnce(meeting.recordingController.setSessionId);
9052
9292
  done();
9053
9293
  });
9054
9294
  });
9055
9295
 
9296
+ describe('#setUpLocusResourcesListener', () => {
9297
+ it('listens to the locus resources update event', (done) => {
9298
+ const newLocusResources = {
9299
+ resources: {
9300
+ webcastInstance: {
9301
+ url: 'url',
9302
+ },
9303
+ },
9304
+ };
9305
+
9306
+ meeting.webinar = {
9307
+ updateWebcastUrl: sinon.stub().returns(undefined),
9308
+ };
9309
+
9310
+ meeting.locusInfo.emit(
9311
+ {function: 'test', file: 'test'},
9312
+ 'LINKS_RESOURCES',
9313
+ newLocusResources
9314
+ );
9315
+
9316
+ assert.calledWith(meeting.webinar.updateWebcastUrl, newLocusResources);
9317
+
9318
+ done();
9319
+ });
9320
+ });
9321
+
9056
9322
  describe('#setUpLocusInfoMediaInactiveListener', () => {
9057
9323
  it('listens to disconnect due to un activity ', (done) => {
9058
9324
  TriggerProxy.trigger.reset();
@@ -12205,14 +12471,10 @@ describe('plugin-meetings', () => {
12205
12471
  const testEmit = async (unmuteAllowed) => {
12206
12472
  meeting.audio = {
12207
12473
  handleServerLocalUnmuteRequired: sinon.stub(),
12208
- }
12209
- await meeting.locusInfo.emitScoped(
12210
- {},
12211
- LOCUSINFO.EVENTS.LOCAL_UNMUTE_REQUIRED,
12212
- {
12213
- unmuteAllowed,
12214
- }
12215
- );
12474
+ };
12475
+ await meeting.locusInfo.emitScoped({}, LOCUSINFO.EVENTS.LOCAL_UNMUTE_REQUIRED, {
12476
+ unmuteAllowed,
12477
+ });
12216
12478
 
12217
12479
  assert.calledWith(
12218
12480
  TriggerProxy.trigger,
@@ -12228,7 +12490,11 @@ describe('plugin-meetings', () => {
12228
12490
  },
12229
12491
  }
12230
12492
  );
12231
- assert.calledOnceWithExactly(meeting.audio.handleServerLocalUnmuteRequired, meeting, unmuteAllowed)
12493
+ assert.calledOnceWithExactly(
12494
+ meeting.audio.handleServerLocalUnmuteRequired,
12495
+ meeting,
12496
+ unmuteAllowed
12497
+ );
12232
12498
  };
12233
12499
 
12234
12500
  [true, false].forEach((unmuteAllowed) => {