@webex/plugin-meetings 3.8.0-next.7 → 3.8.0-next.70

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 (166) hide show
  1. package/dist/breakouts/breakout.js +1 -1
  2. package/dist/breakouts/index.js +1 -1
  3. package/dist/common/errors/webex-errors.js +12 -2
  4. package/dist/common/errors/webex-errors.js.map +1 -1
  5. package/dist/config.js +4 -1
  6. package/dist/config.js.map +1 -1
  7. package/dist/constants.js +17 -121
  8. package/dist/constants.js.map +1 -1
  9. package/dist/controls-options-manager/enums.js +2 -0
  10. package/dist/controls-options-manager/enums.js.map +1 -1
  11. package/dist/controls-options-manager/types.js.map +1 -1
  12. package/dist/controls-options-manager/util.js +52 -0
  13. package/dist/controls-options-manager/util.js.map +1 -1
  14. package/dist/interpretation/index.js +1 -1
  15. package/dist/interpretation/siLanguage.js +1 -1
  16. package/dist/locus-info/controlsUtils.js +28 -10
  17. package/dist/locus-info/controlsUtils.js.map +1 -1
  18. package/dist/locus-info/index.js +20 -1
  19. package/dist/locus-info/index.js.map +1 -1
  20. package/dist/locus-info/selfUtils.js +405 -418
  21. package/dist/locus-info/selfUtils.js.map +1 -1
  22. package/dist/media/index.js +14 -16
  23. package/dist/media/index.js.map +1 -1
  24. package/dist/media/properties.js +94 -6
  25. package/dist/media/properties.js.map +1 -1
  26. package/dist/meeting/brbState.js +6 -0
  27. package/dist/meeting/brbState.js.map +1 -1
  28. package/dist/meeting/in-meeting-actions.js +17 -1
  29. package/dist/meeting/in-meeting-actions.js.map +1 -1
  30. package/dist/meeting/index.js +541 -302
  31. package/dist/meeting/index.js.map +1 -1
  32. package/dist/meeting/locusMediaRequest.js +0 -17
  33. package/dist/meeting/locusMediaRequest.js.map +1 -1
  34. package/dist/meeting/muteState.js +0 -2
  35. package/dist/meeting/muteState.js.map +1 -1
  36. package/dist/meeting/request.js +30 -0
  37. package/dist/meeting/request.js.map +1 -1
  38. package/dist/meeting/request.type.js.map +1 -1
  39. package/dist/meeting/util.js +13 -2
  40. package/dist/meeting/util.js.map +1 -1
  41. package/dist/meeting-info/meeting-info-v2.js +359 -60
  42. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  43. package/dist/meetings/index.js +114 -1
  44. package/dist/meetings/index.js.map +1 -1
  45. package/dist/meetings/util.js +14 -0
  46. package/dist/meetings/util.js.map +1 -1
  47. package/dist/member/index.js +10 -0
  48. package/dist/member/index.js.map +1 -1
  49. package/dist/member/util.js +330 -353
  50. package/dist/member/util.js.map +1 -1
  51. package/dist/members/index.js +23 -0
  52. package/dist/members/index.js.map +1 -1
  53. package/dist/members/request.js +21 -0
  54. package/dist/members/request.js.map +1 -1
  55. package/dist/members/util.js +15 -0
  56. package/dist/members/util.js.map +1 -1
  57. package/dist/metrics/constants.js +9 -0
  58. package/dist/metrics/constants.js.map +1 -1
  59. package/dist/reachability/clusterReachability.js +63 -27
  60. package/dist/reachability/clusterReachability.js.map +1 -1
  61. package/dist/reachability/index.js +112 -47
  62. package/dist/reachability/index.js.map +1 -1
  63. package/dist/reachability/reachability.types.js +14 -0
  64. package/dist/reachability/reachability.types.js.map +1 -1
  65. package/dist/reachability/request.js +19 -3
  66. package/dist/reachability/request.js.map +1 -1
  67. package/dist/reconnection-manager/index.js +2 -2
  68. package/dist/reconnection-manager/index.js.map +1 -1
  69. package/dist/recording-controller/util.js +5 -5
  70. package/dist/recording-controller/util.js.map +1 -1
  71. package/dist/roap/index.js.map +1 -1
  72. package/dist/roap/turnDiscovery.js +45 -27
  73. package/dist/roap/turnDiscovery.js.map +1 -1
  74. package/dist/roap/types.js +17 -0
  75. package/dist/roap/types.js.map +1 -0
  76. package/dist/types/common/errors/webex-errors.d.ts +7 -1
  77. package/dist/types/config.d.ts +2 -0
  78. package/dist/types/constants.d.ts +12 -85
  79. package/dist/types/controls-options-manager/enums.d.ts +3 -1
  80. package/dist/types/controls-options-manager/types.d.ts +7 -1
  81. package/dist/types/locus-info/index.d.ts +1 -0
  82. package/dist/types/locus-info/selfUtils.d.ts +247 -1
  83. package/dist/types/media/properties.d.ts +15 -0
  84. package/dist/types/meeting/in-meeting-actions.d.ts +16 -0
  85. package/dist/types/meeting/index.d.ts +32 -1
  86. package/dist/types/meeting/muteState.d.ts +0 -1
  87. package/dist/types/meeting/request.d.ts +12 -1
  88. package/dist/types/meeting/request.type.d.ts +6 -0
  89. package/dist/types/meeting/util.d.ts +3 -1
  90. package/dist/types/meeting-info/meeting-info-v2.d.ts +80 -0
  91. package/dist/types/meetings/index.d.ts +48 -0
  92. package/dist/types/member/index.d.ts +1 -0
  93. package/dist/types/member/util.d.ts +159 -1
  94. package/dist/types/members/index.d.ts +8 -0
  95. package/dist/types/members/request.d.ts +19 -0
  96. package/dist/types/members/util.d.ts +13 -0
  97. package/dist/types/metrics/constants.d.ts +9 -0
  98. package/dist/types/reachability/clusterReachability.d.ts +15 -7
  99. package/dist/types/reachability/index.d.ts +10 -1
  100. package/dist/types/reachability/reachability.types.d.ts +5 -0
  101. package/dist/types/roap/index.d.ts +3 -2
  102. package/dist/types/roap/turnDiscovery.d.ts +5 -17
  103. package/dist/types/roap/types.d.ts +16 -0
  104. package/dist/webinar/index.js +1 -1
  105. package/package.json +24 -23
  106. package/src/common/errors/webex-errors.ts +8 -1
  107. package/src/config.ts +2 -0
  108. package/src/constants.ts +19 -90
  109. package/src/controls-options-manager/enums.ts +2 -0
  110. package/src/controls-options-manager/types.ts +11 -1
  111. package/src/controls-options-manager/util.ts +62 -0
  112. package/src/locus-info/controlsUtils.ts +44 -14
  113. package/src/locus-info/index.ts +23 -1
  114. package/src/locus-info/selfUtils.ts +451 -447
  115. package/src/media/index.ts +20 -21
  116. package/src/media/properties.ts +96 -0
  117. package/src/meeting/brbState.ts +7 -0
  118. package/src/meeting/in-meeting-actions.ts +32 -0
  119. package/src/meeting/index.ts +346 -93
  120. package/src/meeting/locusMediaRequest.ts +0 -18
  121. package/src/meeting/muteState.ts +0 -2
  122. package/src/meeting/request.ts +36 -1
  123. package/src/meeting/request.type.ts +7 -0
  124. package/src/meeting/util.ts +11 -2
  125. package/src/meeting-info/meeting-info-v2.ts +247 -6
  126. package/src/meetings/index.ts +128 -1
  127. package/src/meetings/util.ts +18 -0
  128. package/src/member/index.ts +13 -2
  129. package/src/member/util.ts +351 -348
  130. package/src/members/index.ts +25 -0
  131. package/src/members/request.ts +26 -0
  132. package/src/members/util.ts +16 -0
  133. package/src/metrics/constants.ts +9 -0
  134. package/src/reachability/clusterReachability.ts +73 -26
  135. package/src/reachability/index.ts +70 -1
  136. package/src/reachability/reachability.types.ts +6 -0
  137. package/src/reachability/request.ts +7 -0
  138. package/src/reconnection-manager/index.ts +2 -2
  139. package/src/recording-controller/util.ts +17 -13
  140. package/src/roap/index.ts +3 -7
  141. package/src/roap/turnDiscovery.ts +34 -39
  142. package/src/roap/types.ts +23 -0
  143. package/test/unit/spec/controls-options-manager/util.js +120 -0
  144. package/test/unit/spec/locus-info/controlsUtils.js +103 -9
  145. package/test/unit/spec/locus-info/index.js +28 -0
  146. package/test/unit/spec/media/index.ts +98 -16
  147. package/test/unit/spec/media/properties.ts +130 -0
  148. package/test/unit/spec/meeting/brbState.ts +19 -0
  149. package/test/unit/spec/meeting/in-meeting-actions.ts +19 -4
  150. package/test/unit/spec/meeting/index.js +524 -35
  151. package/test/unit/spec/meeting/locusMediaRequest.ts +0 -30
  152. package/test/unit/spec/meeting/muteState.js +0 -2
  153. package/test/unit/spec/meeting/request.js +32 -1
  154. package/test/unit/spec/meeting/utils.js +119 -18
  155. package/test/unit/spec/meeting-info/meetinginfov2.js +443 -114
  156. package/test/unit/spec/meetings/index.js +133 -2
  157. package/test/unit/spec/member/index.js +7 -0
  158. package/test/unit/spec/member/util.js +24 -0
  159. package/test/unit/spec/members/index.js +103 -26
  160. package/test/unit/spec/members/request.js +45 -22
  161. package/test/unit/spec/members/utils.js +33 -0
  162. package/test/unit/spec/reachability/clusterReachability.ts +88 -56
  163. package/test/unit/spec/reachability/index.ts +101 -0
  164. package/test/unit/spec/reachability/request.js +47 -2
  165. package/test/unit/spec/reconnection-manager/index.js +4 -4
  166. package/test/unit/spec/roap/turnDiscovery.ts +110 -28
@@ -0,0 +1,23 @@
1
+ import {Enum} from '../constants';
2
+
3
+ export const TurnDiscoverySkipReason = {
4
+ missingHttpResponse: 'missing http response', // when we asked for the TURN discovery response to be in the http response, but it wasn't there
5
+ reachability: 'reachability', // when udp reachability to public clusters is ok, so we don't need TURN (this doens't apply when joinWithMedia() is used)
6
+ alreadyInProgress: 'already in progress', // when we try to start TURN discovery while it's already in progress
7
+ } as const;
8
+
9
+ export type TurnDiscoverySkipReason =
10
+ | Enum<typeof TurnDiscoverySkipReason> // this is a kind of FYI, because in practice typescript will infer the type of TurnDiscoverySkipReason as a string
11
+ | string // used in case of errors, contains the error message
12
+ | undefined; // used when TURN discovery is not skipped
13
+
14
+ export type TurnServerInfo = {
15
+ urls: string[];
16
+ username: string;
17
+ password: string;
18
+ };
19
+
20
+ export type TurnDiscoveryResult = {
21
+ turnServerInfo?: TurnServerInfo;
22
+ turnDiscoverySkippedReason: TurnDiscoverySkipReason;
23
+ };
@@ -406,6 +406,74 @@ describe('plugin-meetings', () => {
406
406
  });
407
407
  });
408
408
 
409
+ describe('canUpdateAnnotation()', () => {
410
+ beforeEach(() => {
411
+ sinon.stub(ControlsOptionsUtil, 'hasHints').returns(true);
412
+ });
413
+
414
+ it('should call hasHints() with proper hints when `enabled` is true', () => {
415
+ ControlsOptionsUtil.canUpdateAnnotation({properties: {enabled: true}}, []);
416
+
417
+ assert.calledWith(ControlsOptionsUtil.hasHints, {
418
+ requiredHints: [DISPLAY_HINTS.ENABLE_ANNOTATION_MEETING_OPTION],
419
+ displayHints: [],
420
+ });
421
+ });
422
+
423
+ it('should call hasHints() with proper hints when `enabled` is false', () => {
424
+ ControlsOptionsUtil.canUpdateAnnotation({properties: {enabled: false}}, []);
425
+
426
+ assert.calledWith(ControlsOptionsUtil.hasHints, {
427
+ requiredHints: [DISPLAY_HINTS.DISABLE_ANNOTATION_MEETING_OPTION],
428
+ displayHints: [],
429
+ });
430
+ });
431
+
432
+ it('should return the resolution of hasHints()', () => {
433
+ const expected = 'example-return-value';
434
+ ControlsOptionsUtil.hasHints.returns(expected);
435
+
436
+ const results = ControlsOptionsUtil.canUpdateAnnotation({properties: {}}, []);
437
+
438
+ assert.calledOnce(ControlsOptionsUtil.hasHints);
439
+ assert.equal(results, expected);
440
+ });
441
+ });
442
+
443
+ describe('canUpdateRemoteDesktopControl()', () => {
444
+ beforeEach(() => {
445
+ sinon.stub(ControlsOptionsUtil, 'hasHints').returns(true);
446
+ });
447
+
448
+ it('should call hasHints() with proper hints when `enabled` is true', () => {
449
+ ControlsOptionsUtil.canUpdateRemoteDesktopControl({properties: {enabled: true}}, []);
450
+
451
+ assert.calledWith(ControlsOptionsUtil.hasHints, {
452
+ requiredHints: [DISPLAY_HINTS.ENABLE_RDC_MEETING_OPTION],
453
+ displayHints: [],
454
+ });
455
+ });
456
+
457
+ it('should call hasHints() with proper hints when `enabled` is false', () => {
458
+ ControlsOptionsUtil.canUpdateRemoteDesktopControl({properties: {enabled: false}}, []);
459
+
460
+ assert.calledWith(ControlsOptionsUtil.hasHints, {
461
+ requiredHints: [DISPLAY_HINTS.DISABLE_RDC_MEETING_OPTION],
462
+ displayHints: [],
463
+ });
464
+ });
465
+
466
+ it('should return the resolution of hasHints()', () => {
467
+ const expected = 'example-return-value';
468
+ ControlsOptionsUtil.hasHints.returns(expected);
469
+
470
+ const results = ControlsOptionsUtil.canUpdateRemoteDesktopControl({properties: {}}, []);
471
+
472
+ assert.calledOnce(ControlsOptionsUtil.hasHints);
473
+ assert.equal(results, expected);
474
+ });
475
+ });
476
+
409
477
  describe('canUpdate()', () => {
410
478
  const displayHints = [];
411
479
 
@@ -416,6 +484,8 @@ describe('plugin-meetings', () => {
416
484
  ControlsOptionsUtil.canUpdateShareControl = sinon.stub().returns(true);
417
485
  ControlsOptionsUtil.canUpdateVideo = sinon.stub().returns(true);
418
486
  ControlsOptionsUtil.canUpdateViewTheParticipantsList = sinon.stub().returns(true);
487
+ ControlsOptionsUtil.canUpdateAnnotation = sinon.stub().returns(true);
488
+ ControlsOptionsUtil.canUpdateRemoteDesktopControl = sinon.stub().returns(true);
419
489
  });
420
490
 
421
491
  it('should only call canUpdateAudio() if the scope is audio', () => {
@@ -429,6 +499,8 @@ describe('plugin-meetings', () => {
429
499
  assert.callCount(ControlsOptionsUtil.canUpdateShareControl, 0);
430
500
  assert.callCount(ControlsOptionsUtil.canUpdateVideo, 0);
431
501
  assert.callCount(ControlsOptionsUtil.canUpdateViewTheParticipantsList, 0);
502
+ assert.callCount(ControlsOptionsUtil.canUpdateAnnotation, 0);
503
+ assert.callCount(ControlsOptionsUtil.canUpdateRemoteDesktopControl, 0);
432
504
  assert.isTrue(results);
433
505
  });
434
506
 
@@ -443,6 +515,8 @@ describe('plugin-meetings', () => {
443
515
  assert.callCount(ControlsOptionsUtil.canUpdateShareControl, 0);
444
516
  assert.callCount(ControlsOptionsUtil.canUpdateVideo, 0);
445
517
  assert.callCount(ControlsOptionsUtil.canUpdateViewTheParticipantsList, 0);
518
+ assert.callCount(ControlsOptionsUtil.canUpdateAnnotation, 0);
519
+ assert.callCount(ControlsOptionsUtil.canUpdateRemoteDesktopControl, 0);
446
520
  assert.isTrue(results);
447
521
  });
448
522
 
@@ -457,6 +531,8 @@ describe('plugin-meetings', () => {
457
531
  assert.callCount(ControlsOptionsUtil.canUpdateShareControl, 0);
458
532
  assert.callCount(ControlsOptionsUtil.canUpdateVideo, 0);
459
533
  assert.callCount(ControlsOptionsUtil.canUpdateViewTheParticipantsList, 0);
534
+ assert.callCount(ControlsOptionsUtil.canUpdateAnnotation, 0);
535
+ assert.callCount(ControlsOptionsUtil.canUpdateRemoteDesktopControl, 0);
460
536
  assert.isTrue(results);
461
537
  });
462
538
 
@@ -471,6 +547,8 @@ describe('plugin-meetings', () => {
471
547
  assert.calledWith(ControlsOptionsUtil.canUpdateShareControl, displayHints);
472
548
  assert.callCount(ControlsOptionsUtil.canUpdateVideo, 0);
473
549
  assert.callCount(ControlsOptionsUtil.canUpdateViewTheParticipantsList, 0);
550
+ assert.callCount(ControlsOptionsUtil.canUpdateAnnotation, 0);
551
+ assert.callCount(ControlsOptionsUtil.canUpdateRemoteDesktopControl, 0);
474
552
  assert.isTrue(results);
475
553
  });
476
554
 
@@ -485,6 +563,8 @@ describe('plugin-meetings', () => {
485
563
  assert.callCount(ControlsOptionsUtil.canUpdateShareControl, 0);
486
564
  assert.calledWith(ControlsOptionsUtil.canUpdateVideo, control, displayHints);
487
565
  assert.callCount(ControlsOptionsUtil.canUpdateViewTheParticipantsList, 0);
566
+ assert.callCount(ControlsOptionsUtil.canUpdateAnnotation, 0);
567
+ assert.callCount(ControlsOptionsUtil.canUpdateRemoteDesktopControl, 0);
488
568
  assert.isTrue(results);
489
569
  });
490
570
 
@@ -503,6 +583,44 @@ describe('plugin-meetings', () => {
503
583
  control,
504
584
  displayHints
505
585
  );
586
+ assert.callCount(ControlsOptionsUtil.canUpdateAnnotation, 0);
587
+ assert.callCount(ControlsOptionsUtil.canUpdateRemoteDesktopControl, 0);
588
+ assert.isTrue(results);
589
+ });
590
+
591
+ it('should only call canUpdateAnnotation() if the scope is annotation', () => {
592
+ const control = {scope: 'annotation'};
593
+
594
+ const results = ControlsOptionsUtil.canUpdate(control, displayHints);
595
+
596
+ assert.callCount(ControlsOptionsUtil.canUpdateAudio, 0);
597
+ assert.callCount(ControlsOptionsUtil.canUpdateRaiseHand, 0);
598
+ assert.callCount(ControlsOptionsUtil.canUpdateReactions, 0);
599
+ assert.callCount(ControlsOptionsUtil.canUpdateShareControl, 0);
600
+ assert.callCount(ControlsOptionsUtil.canUpdateVideo, 0);
601
+ assert.callCount(ControlsOptionsUtil.canUpdateViewTheParticipantsList, 0);
602
+ assert.calledWith(ControlsOptionsUtil.canUpdateAnnotation, control, displayHints);
603
+ assert.callCount(ControlsOptionsUtil.canUpdateRemoteDesktopControl, 0);
604
+ assert.isTrue(results);
605
+ });
606
+
607
+ it('should only call canUpdateRemoteDesktopControl() if the scope is rdc', () => {
608
+ const control = {scope: 'rdc'};
609
+
610
+ const results = ControlsOptionsUtil.canUpdate(control, displayHints);
611
+
612
+ assert.callCount(ControlsOptionsUtil.canUpdateAudio, 0);
613
+ assert.callCount(ControlsOptionsUtil.canUpdateRaiseHand, 0);
614
+ assert.callCount(ControlsOptionsUtil.canUpdateReactions, 0);
615
+ assert.callCount(ControlsOptionsUtil.canUpdateShareControl, 0);
616
+ assert.callCount(ControlsOptionsUtil.canUpdateVideo, 0);
617
+ assert.callCount(ControlsOptionsUtil.canUpdateViewTheParticipantsList, 0);
618
+ assert.callCount(ControlsOptionsUtil.canUpdateAnnotation, 0);
619
+ assert.calledWith(
620
+ ControlsOptionsUtil.canUpdateRemoteDesktopControl,
621
+ control,
622
+ displayHints
623
+ );
506
624
  assert.isTrue(results);
507
625
  });
508
626
 
@@ -517,6 +635,8 @@ describe('plugin-meetings', () => {
517
635
  assert.callCount(ControlsOptionsUtil.canUpdateShareControl, 0);
518
636
  assert.callCount(ControlsOptionsUtil.canUpdateVideo, 0);
519
637
  assert.callCount(ControlsOptionsUtil.canUpdateViewTheParticipantsList, 0);
638
+ assert.callCount(ControlsOptionsUtil.canUpdateAnnotation, 0);
639
+ assert.callCount(ControlsOptionsUtil.canUpdateRemoteDesktopControl, 0);
520
640
  assert.isFalse(results);
521
641
  });
522
642
  });
@@ -1,6 +1,10 @@
1
1
  import {assert} from '@webex/test-helper-chai';
2
2
  import ControlsUtils from '@webex/plugin-meetings/src/locus-info/controlsUtils';
3
3
  import controlsUtils from "@webex/plugin-meetings/src/locus-info/controlsUtils";
4
+ import {
5
+ MEETING_STATE,
6
+ BREAKOUTS,
7
+ } from '../../../../src/constants';
4
8
 
5
9
  const defaultControls = {
6
10
  entryExitTone: {
@@ -140,7 +144,25 @@ describe('plugin-meetings', () => {
140
144
  assert.equal(parsedControls.videoLayout.overrideDefault, newControls.videoLayout.overrideDefault);
141
145
  assert.equal(parsedControls.videoLayout.lockAttendeeViewOnStageOnly, newControls.videoLayout.lockAttendeeViewOnStageOnly);
142
146
  assert.equal(parsedControls.videoLayout.stageParameters, newControls.videoLayout.stageParameters);
147
+ });
148
+
149
+ it('should parse the annotationControl control', () => {
150
+ const newControls = {annotationControl: {enabled: true}};
151
+
152
+ const parsedControls = ControlsUtils.parse(newControls);
143
153
 
154
+ assert.equal(
155
+ parsedControls.annotationControl.enabled,
156
+ newControls.annotationControl.enabled
157
+ );
158
+ });
159
+
160
+ it('should parse the rdcControl control', () => {
161
+ const newControls = {rdcControl: {enabled: true}};
162
+
163
+ const parsedControls = ControlsUtils.parse(newControls);
164
+
165
+ assert.equal(parsedControls.rdcControl.enabled, newControls.rdcControl.enabled);
144
166
  });
145
167
 
146
168
  describe('videoEnabled', () => {
@@ -269,6 +291,14 @@ describe('plugin-meetings', () => {
269
291
  assert.equal(updates.hasPracticeSessionEnabledChanged, true);
270
292
  });
271
293
 
294
+ it('returns hasPracticeSessionEnabledChanged = false when enabled is false and previous state is false', () => {
295
+ const newControls = {practiceSession: {enabled: false}};
296
+
297
+ const {updates} = ControlsUtils.getControls(defaultControls, newControls);
298
+
299
+ assert.equal(updates.hasPracticeSessionEnabledChanged, false);
300
+ });
301
+
272
302
  it('returns hasEntryExitToneChanged = true when mode changed', () => {
273
303
  const newControls = {
274
304
  entryExitTone: {
@@ -365,6 +395,22 @@ describe('plugin-meetings', () => {
365
395
  assert.equal(updates.hasManualCaptionChanged, false);
366
396
  });
367
397
 
398
+ it('returns hasAnnotationControlChanged = true when changed', () => {
399
+ const newControls = {annotationControl: {enabled: true}};
400
+
401
+ const {updates} = ControlsUtils.getControls(defaultControls, newControls);
402
+
403
+ assert.equal(updates.hasAnnotationControlChanged, true);
404
+ });
405
+
406
+ it('returns hasRemoteDesktopControlChanged = true when changed', () => {
407
+ const newControls = {rdcControl: {enabled: true}};
408
+
409
+ const {updates} = ControlsUtils.getControls(defaultControls, newControls);
410
+
411
+ assert.equal(updates.hasRemoteDesktopControlChanged, true);
412
+ });
413
+
368
414
  describe('videoEnabled', () => {
369
415
  const testVideoEnabled = (oldControls, newControls, updatedProperty) => {
370
416
  const result = ControlsUtils.getControls(oldControls, newControls);
@@ -424,28 +470,76 @@ describe('plugin-meetings', () => {
424
470
 
425
471
  describe('getSessionSwitchStatus', () => {
426
472
  it('if no breakout control, return switch status both false', () => {
427
- const oldControls = {};
428
- const newControls = {};
429
- assert.deepEqual(controlsUtils.getSessionSwitchStatus(oldControls, newControls), {
473
+ const oldLocus = {};
474
+ const newLocus = {};
475
+ assert.deepEqual(controlsUtils.getSessionSwitchStatus(oldLocus, newLocus), {
430
476
  isReturnToMain: false, isJoinToBreakout: false
431
477
  });
432
478
  });
433
479
 
434
480
  it('if switch session from breakout to main, return isReturnToMain as true', () => {
435
- const oldControls = {breakout: {sessionType: 'BREAKOUT'}};
436
- const newControls = {breakout: {sessionType: 'MAIN'}};
437
- assert.deepEqual(controlsUtils.getSessionSwitchStatus(oldControls, newControls), {
481
+ const oldLocus = {controls: {breakout: {sessionType: 'BREAKOUT'}}};
482
+ const newLocus = {controls: {breakout: {sessionType: 'MAIN'}}};
483
+ assert.deepEqual(controlsUtils.getSessionSwitchStatus(oldLocus, newLocus), {
438
484
  isReturnToMain: true, isJoinToBreakout: false
439
485
  });
440
486
  });
441
487
 
442
488
  it('if switch session from main to breakout, return isJoinToBreakout as true', () => {
443
- const oldControls = {breakout: {sessionType: 'MAIN'}};
444
- const newControls = {breakout: {sessionType: 'BREAKOUT'}};
445
- assert.deepEqual(controlsUtils.getSessionSwitchStatus(oldControls, newControls), {
489
+ const oldLocus = {controls: {breakout: {sessionType: 'MAIN'}}};
490
+ const newLocus = {controls: {breakout: {sessionType: 'BREAKOUT'}}};
491
+ assert.deepEqual(controlsUtils.getSessionSwitchStatus(oldLocus, newLocus), {
446
492
  isReturnToMain: false, isJoinToBreakout: true
447
493
  });
448
494
  });
495
+
496
+ it('if needUseCache conditions are met, return isJoinToBreakout as true', () => {
497
+ const oldLocus = {
498
+ self: { isCreator: true },
499
+ controls: { breakout: { sessionType: BREAKOUTS.SESSION_TYPES.MAIN} },
500
+ };
501
+
502
+ const newLocus = {
503
+ participants: [
504
+ { isCreator: true, state: MEETING_STATE.STATES.JOINED },
505
+ ],
506
+ controls: {
507
+ breakout: {
508
+ sessionType: BREAKOUTS.SESSION_TYPES.MAIN,
509
+ groups: [{ id: 'group1' }]
510
+ },
511
+ },
512
+ };
513
+
514
+ assert.deepEqual(controlsUtils.getSessionSwitchStatus(oldLocus, newLocus), {
515
+ isReturnToMain: true,
516
+ isJoinToBreakout: false
517
+ });
518
+ });
519
+
520
+ it('if needUseCache conditions are not met, return newLocus and isReturnToMain as false', () => {
521
+ const oldLocus = {
522
+ self: { isCreator: false },
523
+ controls: { breakout: { sessionType: BREAKOUTS.SESSION_TYPES.BREAKOUT} },
524
+ };
525
+
526
+ const newLocus = {
527
+ participants: [
528
+ { isCreator: true, state: MEETING_STATE.STATES.JOINED },
529
+ ],
530
+ controls: {
531
+ breakout: {
532
+ sessionType: BREAKOUTS.SESSION_TYPES.BREAKOUT,
533
+ groups: []
534
+ },
535
+ },
536
+ };
537
+
538
+ assert.deepEqual(controlsUtils.getSessionSwitchStatus(oldLocus, newLocus), {
539
+ isReturnToMain: false,
540
+ isJoinToBreakout: false
541
+ });
542
+ });
449
543
  });
450
544
 
451
545
  describe('#isMainSessionDTO', () => {
@@ -111,6 +111,8 @@ describe('plugin-meetings', () => {
111
111
  },
112
112
  webcastControl: {streaming: false},
113
113
  practiceSession: {enabled: true},
114
+ annotationControl: {enabled: true},
115
+ rdcControl: {enabled: true},
114
116
  };
115
117
  });
116
118
 
@@ -278,6 +280,32 @@ describe('plugin-meetings', () => {
278
280
  });
279
281
  });
280
282
 
283
+ it('should trigger the CONTROLS_ANNOTATION_CHANGED event when necessary', () => {
284
+ locusInfo.controls = {};
285
+ locusInfo.emitScoped = sinon.stub();
286
+ locusInfo.updateControls(newControls);
287
+
288
+ assert.calledWith(
289
+ locusInfo.emitScoped,
290
+ {file: 'locus-info', function: 'updateControls'},
291
+ LOCUSINFO.EVENTS.CONTROLS_ANNOTATION_CHANGED,
292
+ {state: newControls.annotationControl}
293
+ );
294
+ });
295
+
296
+ it('should trigger the CONTROLS_REMOTE_DESKTOP_CONTROL_CHANGED event when necessary', () => {
297
+ locusInfo.controls = {};
298
+ locusInfo.emitScoped = sinon.stub();
299
+ locusInfo.updateControls(newControls);
300
+
301
+ assert.calledWith(
302
+ locusInfo.emitScoped,
303
+ {file: 'locus-info', function: 'updateControls'},
304
+ LOCUSINFO.EVENTS.CONTROLS_REMOTE_DESKTOP_CONTROL_CHANGED,
305
+ {state: newControls.rdcControl}
306
+ );
307
+ });
308
+
281
309
  it('should keep the recording state to `IDLE`', () => {
282
310
  locusInfo.controls = {
283
311
  record: {
@@ -4,6 +4,7 @@ import Media from '@webex/plugin-meetings/src/media/index';
4
4
  import {assert} from '@webex/test-helper-chai';
5
5
  import sinon from 'sinon';
6
6
  import StaticConfig from '@webex/plugin-meetings/src/common/config';
7
+ import { BrowserInfo } from '@webex/web-capabilities';
7
8
 
8
9
  describe('createMediaConnection', () => {
9
10
  let clock;
@@ -79,7 +80,7 @@ describe('createMediaConnection', () => {
79
80
  enableRtx: ENABLE_RTX,
80
81
  enableExtmap: ENABLE_EXTMAP,
81
82
  turnServerInfo: {
82
- url: 'turns:turn-server-url:443?transport=tcp',
83
+ urls: ['turns:turn-server-url-1:443?transport=tcp', 'turns:turn-server-url-2:443?transport=tcp'],
83
84
  username: 'turn username',
84
85
  password: 'turn password',
85
86
  },
@@ -91,12 +92,7 @@ describe('createMediaConnection', () => {
91
92
  {
92
93
  iceServers: [
93
94
  {
94
- urls: 'turn:turn-server-url:5004?transport=tcp',
95
- username: 'turn username',
96
- credential: 'turn password',
97
- },
98
- {
99
- urls: 'turns:turn-server-url:443?transport=tcp',
95
+ urls: ['turns:turn-server-url-1:443?transport=tcp', 'turns:turn-server-url-2:443?transport=tcp'],
100
96
  username: 'turn username',
101
97
  credential: 'turn password',
102
98
  },
@@ -159,11 +155,12 @@ describe('createMediaConnection', () => {
159
155
  },
160
156
  rtcMetrics,
161
157
  turnServerInfo: {
162
- url: 'turns:turn-server-url:443?transport=tcp',
158
+ urls: ['turns:turn-server-url-1:443?transport=tcp', 'turns:turn-server-url-2:443?transport=tcp'],
163
159
  username: 'turn username',
164
160
  password: 'turn password',
165
161
  },
166
162
  bundlePolicy: 'max-bundle',
163
+ disableAudioMainDtx: false,
167
164
  });
168
165
  assert.calledOnce(multistreamRoapMediaConnectionConstructorStub);
169
166
  assert.calledWith(
@@ -171,17 +168,13 @@ describe('createMediaConnection', () => {
171
168
  {
172
169
  iceServers: [
173
170
  {
174
- urls: 'turn:turn-server-url:5004?transport=tcp',
175
- username: 'turn username',
176
- credential: 'turn password',
177
- },
178
- {
179
- urls: 'turns:turn-server-url:443?transport=tcp',
171
+ urls: ['turns:turn-server-url-1:443?transport=tcp', 'turns:turn-server-url-2:443?transport=tcp'],
180
172
  username: 'turn username',
181
173
  credential: 'turn password',
182
174
  },
183
175
  ],
184
176
  bundlePolicy: 'max-bundle',
177
+ disableAudioMainDtx: false,
185
178
  },
186
179
  'meeting id'
187
180
  );
@@ -205,14 +198,75 @@ describe('createMediaConnection', () => {
205
198
 
206
199
  sendMetricsInQueueCallback();
207
200
  assert.calledOnce(rtcMetrics.sendMetricsInQueue);
201
+ });
202
+
203
+ it('multistream non-firefox does not care about stopIceGatheringAfterFirstRelayCandidate', () => {
204
+ const multistreamRoapMediaConnectionConstructorStub = sinon
205
+ .stub(InternalMediaCoreModule, 'MultistreamRoapMediaConnection')
206
+ .returns(fakeRoapMediaConnection);
207
+
208
+ Media.createMediaConnection(true, 'some debug id', 'meeting id', {
209
+ stopIceGatheringAfterFirstRelayCandidate: true,
210
+ });
211
+ assert.calledOnce(multistreamRoapMediaConnectionConstructorStub);
212
+ assert.calledWith(
213
+ multistreamRoapMediaConnectionConstructorStub,
214
+ {
215
+ iceServers: []
216
+ },
217
+ 'meeting id'
218
+ );
219
+ });
220
+
221
+ it('multistream firefox stops gathering after first relay if stopIceGatheringAfterFirstRelayCandidate is true', () => {
222
+ const multistreamRoapMediaConnectionConstructorStub = sinon
223
+ .stub(InternalMediaCoreModule, 'MultistreamRoapMediaConnection')
224
+ .returns(fakeRoapMediaConnection);
225
+
226
+ sinon.stub(BrowserInfo, 'isFirefox').returns(true);
227
+
228
+ Media.createMediaConnection(true, 'some debug id', 'meeting id', {
229
+ stopIceGatheringAfterFirstRelayCandidate: true,
230
+ });
231
+ assert.calledOnce(multistreamRoapMediaConnectionConstructorStub);
232
+ assert.calledWith(
233
+ multistreamRoapMediaConnectionConstructorStub,
234
+ {
235
+ iceServers: [],
236
+ doFullIce: true,
237
+ stopIceGatheringAfterFirstRelayCandidate: true,
238
+ },
239
+ 'meeting id'
240
+ );
241
+ });
208
242
 
243
+ it('multistream firefox continues gathering if stopIceGatheringAfterFirstRelayCandidate is false', () => {
244
+ const multistreamRoapMediaConnectionConstructorStub = sinon
245
+ .stub(InternalMediaCoreModule, 'MultistreamRoapMediaConnection')
246
+ .returns(fakeRoapMediaConnection);
247
+
248
+ sinon.stub(BrowserInfo, 'isFirefox').returns(true);
249
+
250
+ Media.createMediaConnection(true, 'some debug id', 'meeting id', {
251
+ stopIceGatheringAfterFirstRelayCandidate: false,
252
+ });
253
+ assert.calledOnce(multistreamRoapMediaConnectionConstructorStub);
254
+ assert.calledWith(
255
+ multistreamRoapMediaConnectionConstructorStub,
256
+ {
257
+ iceServers: [],
258
+ doFullIce: true,
259
+ stopIceGatheringAfterFirstRelayCandidate: false,
260
+ },
261
+ 'meeting id'
262
+ );
209
263
  });
210
264
 
211
265
  [
212
266
  {testCase: 'turnServerInfo is undefined', turnServerInfo: undefined},
213
267
  {
214
268
  testCase: 'turnServerInfo.url is empty string',
215
- turnServerInfo: {url: '', username: 'turn username', password: 'turn password'},
269
+ turnServerInfo: {urls: [], username: 'turn username', password: 'turn password'},
216
270
  },
217
271
  ].forEach(({testCase, turnServerInfo}) => {
218
272
  it(`passes empty ICE servers array to MultistreamRoapMediaConnection if ${testCase} (multistream enabled)`, () => {
@@ -272,11 +326,39 @@ describe('createMediaConnection', () => {
272
326
  );
273
327
  });
274
328
 
329
+ it('does not pass disableAudioMainDtx to MultistreamRoapMediaConnection if disableAudioMainDtx is undefined', () => {
330
+ const multistreamRoapMediaConnectionConstructorStub = sinon
331
+ .stub(InternalMediaCoreModule, 'MultistreamRoapMediaConnection')
332
+ .returns(fakeRoapMediaConnection);
333
+
334
+ Media.createMediaConnection(true, 'debug string', 'meeting id', {
335
+ mediaProperties: {
336
+ mediaDirection: {
337
+ sendAudio: true,
338
+ sendVideo: true,
339
+ sendShare: false,
340
+ receiveAudio: true,
341
+ receiveVideo: true,
342
+ receiveShare: true,
343
+ },
344
+ },
345
+ disableAudioMainDtx: undefined,
346
+ });
347
+ assert.calledOnce(multistreamRoapMediaConnectionConstructorStub);
348
+ assert.calledWith(
349
+ multistreamRoapMediaConnectionConstructorStub,
350
+ {
351
+ iceServers: [],
352
+ },
353
+ 'meeting id'
354
+ );
355
+ });
356
+
275
357
  [
276
358
  {testCase: 'turnServerInfo is undefined', turnServerInfo: undefined},
277
359
  {
278
360
  testCase: 'turnServerInfo.url is empty string',
279
- turnServerInfo: {url: '', username: 'turn username', password: 'turn password'},
361
+ turnServerInfo: {urls: [], username: 'turn username', password: 'turn password'},
280
362
  },
281
363
  ].forEach(({testCase, turnServerInfo}) => {
282
364
  it(`passes empty ICE servers array to RoapMediaConnection if ${testCase} (multistream disabled)`, () => {