@webex/plugin-meetings 3.8.0-next.8 → 3.8.0-next.80

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 (171) hide show
  1. package/dist/breakouts/breakout.js +1 -1
  2. package/dist/breakouts/index.js +70 -6
  3. package/dist/breakouts/index.js.map +1 -1
  4. package/dist/common/errors/webex-errors.js +12 -2
  5. package/dist/common/errors/webex-errors.js.map +1 -1
  6. package/dist/config.js +5 -1
  7. package/dist/config.js.map +1 -1
  8. package/dist/constants.js +20 -123
  9. package/dist/constants.js.map +1 -1
  10. package/dist/controls-options-manager/enums.js +2 -0
  11. package/dist/controls-options-manager/enums.js.map +1 -1
  12. package/dist/controls-options-manager/types.js.map +1 -1
  13. package/dist/controls-options-manager/util.js +52 -0
  14. package/dist/controls-options-manager/util.js.map +1 -1
  15. package/dist/interpretation/index.js +1 -1
  16. package/dist/interpretation/siLanguage.js +1 -1
  17. package/dist/locus-info/controlsUtils.js +28 -10
  18. package/dist/locus-info/controlsUtils.js.map +1 -1
  19. package/dist/locus-info/index.js +62 -12
  20. package/dist/locus-info/index.js.map +1 -1
  21. package/dist/locus-info/selfUtils.js +432 -418
  22. package/dist/locus-info/selfUtils.js.map +1 -1
  23. package/dist/media/index.js +17 -17
  24. package/dist/media/index.js.map +1 -1
  25. package/dist/media/properties.js +94 -6
  26. package/dist/media/properties.js.map +1 -1
  27. package/dist/meeting/brbState.js +6 -0
  28. package/dist/meeting/brbState.js.map +1 -1
  29. package/dist/meeting/in-meeting-actions.js +17 -1
  30. package/dist/meeting/in-meeting-actions.js.map +1 -1
  31. package/dist/meeting/index.js +570 -302
  32. package/dist/meeting/index.js.map +1 -1
  33. package/dist/meeting/locusMediaRequest.js +0 -17
  34. package/dist/meeting/locusMediaRequest.js.map +1 -1
  35. package/dist/meeting/muteState.js +0 -2
  36. package/dist/meeting/muteState.js.map +1 -1
  37. package/dist/meeting/request.js +30 -0
  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 +13 -2
  41. package/dist/meeting/util.js.map +1 -1
  42. package/dist/meeting-info/meeting-info-v2.js +373 -68
  43. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  44. package/dist/meeting-info/utilv2.js +5 -1
  45. package/dist/meeting-info/utilv2.js.map +1 -1
  46. package/dist/meetings/index.js +136 -1
  47. package/dist/meetings/index.js.map +1 -1
  48. package/dist/meetings/util.js +14 -0
  49. package/dist/meetings/util.js.map +1 -1
  50. package/dist/member/index.js +10 -0
  51. package/dist/member/index.js.map +1 -1
  52. package/dist/member/util.js +330 -353
  53. package/dist/member/util.js.map +1 -1
  54. package/dist/members/index.js +42 -0
  55. package/dist/members/index.js.map +1 -1
  56. package/dist/members/request.js +38 -0
  57. package/dist/members/request.js.map +1 -1
  58. package/dist/members/util.js +36 -1
  59. package/dist/members/util.js.map +1 -1
  60. package/dist/metrics/constants.js +9 -0
  61. package/dist/metrics/constants.js.map +1 -1
  62. package/dist/reachability/clusterReachability.js +63 -27
  63. package/dist/reachability/clusterReachability.js.map +1 -1
  64. package/dist/reachability/index.js +112 -47
  65. package/dist/reachability/index.js.map +1 -1
  66. package/dist/reachability/reachability.types.js +14 -0
  67. package/dist/reachability/reachability.types.js.map +1 -1
  68. package/dist/reachability/request.js +19 -3
  69. package/dist/reachability/request.js.map +1 -1
  70. package/dist/reconnection-manager/index.js +2 -2
  71. package/dist/reconnection-manager/index.js.map +1 -1
  72. package/dist/roap/index.js.map +1 -1
  73. package/dist/roap/turnDiscovery.js +45 -27
  74. package/dist/roap/turnDiscovery.js.map +1 -1
  75. package/dist/roap/types.js +17 -0
  76. package/dist/roap/types.js.map +1 -0
  77. package/dist/types/common/errors/webex-errors.d.ts +7 -1
  78. package/dist/types/config.d.ts +3 -0
  79. package/dist/types/constants.d.ts +13 -85
  80. package/dist/types/controls-options-manager/enums.d.ts +3 -1
  81. package/dist/types/controls-options-manager/types.d.ts +7 -1
  82. package/dist/types/locus-info/index.d.ts +3 -3
  83. package/dist/types/locus-info/selfUtils.d.ts +216 -1
  84. package/dist/types/media/properties.d.ts +15 -0
  85. package/dist/types/meeting/in-meeting-actions.d.ts +16 -0
  86. package/dist/types/meeting/index.d.ts +43 -1
  87. package/dist/types/meeting/muteState.d.ts +0 -1
  88. package/dist/types/meeting/request.d.ts +12 -1
  89. package/dist/types/meeting/request.type.d.ts +6 -0
  90. package/dist/types/meeting/util.d.ts +3 -1
  91. package/dist/types/meeting-info/meeting-info-v2.d.ts +82 -1
  92. package/dist/types/meetings/index.d.ts +57 -0
  93. package/dist/types/member/index.d.ts +1 -0
  94. package/dist/types/member/util.d.ts +159 -1
  95. package/dist/types/members/index.d.ts +15 -0
  96. package/dist/types/members/request.d.ts +26 -0
  97. package/dist/types/members/util.d.ts +27 -0
  98. package/dist/types/metrics/constants.d.ts +9 -0
  99. package/dist/types/reachability/clusterReachability.d.ts +15 -7
  100. package/dist/types/reachability/index.d.ts +10 -1
  101. package/dist/types/reachability/reachability.types.d.ts +5 -0
  102. package/dist/types/roap/index.d.ts +3 -2
  103. package/dist/types/roap/turnDiscovery.d.ts +5 -17
  104. package/dist/types/roap/types.d.ts +16 -0
  105. package/dist/webinar/index.js +1 -1
  106. package/package.json +24 -23
  107. package/src/breakouts/index.ts +69 -0
  108. package/src/common/errors/webex-errors.ts +8 -1
  109. package/src/config.ts +3 -0
  110. package/src/constants.ts +20 -90
  111. package/src/controls-options-manager/enums.ts +2 -0
  112. package/src/controls-options-manager/types.ts +11 -1
  113. package/src/controls-options-manager/util.ts +62 -0
  114. package/src/locus-info/controlsUtils.ts +44 -14
  115. package/src/locus-info/index.ts +56 -13
  116. package/src/locus-info/selfUtils.ts +496 -442
  117. package/src/media/index.ts +23 -21
  118. package/src/media/properties.ts +96 -0
  119. package/src/meeting/brbState.ts +7 -0
  120. package/src/meeting/in-meeting-actions.ts +32 -0
  121. package/src/meeting/index.ts +382 -93
  122. package/src/meeting/locusMediaRequest.ts +0 -18
  123. package/src/meeting/muteState.ts +0 -2
  124. package/src/meeting/request.ts +36 -1
  125. package/src/meeting/request.type.ts +7 -0
  126. package/src/meeting/util.ts +11 -2
  127. package/src/meeting-info/meeting-info-v2.ts +254 -8
  128. package/src/meeting-info/utilv2.ts +5 -0
  129. package/src/meetings/index.ts +148 -1
  130. package/src/meetings/util.ts +18 -0
  131. package/src/member/index.ts +13 -2
  132. package/src/member/util.ts +351 -348
  133. package/src/members/index.ts +47 -0
  134. package/src/members/request.ts +44 -0
  135. package/src/members/util.ts +43 -1
  136. package/src/metrics/constants.ts +9 -0
  137. package/src/reachability/clusterReachability.ts +73 -26
  138. package/src/reachability/index.ts +70 -1
  139. package/src/reachability/reachability.types.ts +6 -0
  140. package/src/reachability/request.ts +7 -0
  141. package/src/reconnection-manager/index.ts +2 -2
  142. package/src/roap/index.ts +3 -7
  143. package/src/roap/turnDiscovery.ts +34 -39
  144. package/src/roap/types.ts +23 -0
  145. package/test/unit/spec/breakouts/index.ts +167 -95
  146. package/test/unit/spec/controls-options-manager/util.js +120 -0
  147. package/test/unit/spec/locus-info/controlsUtils.js +103 -9
  148. package/test/unit/spec/locus-info/index.js +167 -73
  149. package/test/unit/spec/locus-info/selfUtils.js +98 -24
  150. package/test/unit/spec/media/index.ts +150 -18
  151. package/test/unit/spec/media/properties.ts +130 -0
  152. package/test/unit/spec/meeting/brbState.ts +19 -0
  153. package/test/unit/spec/meeting/in-meeting-actions.ts +19 -4
  154. package/test/unit/spec/meeting/index.js +557 -35
  155. package/test/unit/spec/meeting/locusMediaRequest.ts +0 -30
  156. package/test/unit/spec/meeting/muteState.js +0 -2
  157. package/test/unit/spec/meeting/request.js +32 -1
  158. package/test/unit/spec/meeting/utils.js +119 -18
  159. package/test/unit/spec/meeting-info/meetinginfov2.js +484 -114
  160. package/test/unit/spec/meeting-info/utilv2.js +19 -0
  161. package/test/unit/spec/meetings/index.js +146 -2
  162. package/test/unit/spec/member/index.js +7 -0
  163. package/test/unit/spec/member/util.js +24 -0
  164. package/test/unit/spec/members/index.js +140 -26
  165. package/test/unit/spec/members/request.js +68 -22
  166. package/test/unit/spec/members/utils.js +75 -0
  167. package/test/unit/spec/reachability/clusterReachability.ts +88 -56
  168. package/test/unit/spec/reachability/index.ts +101 -0
  169. package/test/unit/spec/reachability/request.js +47 -2
  170. package/test/unit/spec/reconnection-manager/index.js +4 -4
  171. package/test/unit/spec/roap/turnDiscovery.ts +110 -28
@@ -241,6 +241,25 @@ describe('plugin-meetings', () => {
241
241
  }
242
242
  );
243
243
  });
244
+
245
+ it('allows for disableWebRedirect', () => {
246
+
247
+ const res = MeetingInfoUtil.getRequestBody({
248
+ type: DESTINATION_TYPE.CONVERSATION_URL,
249
+ destination: 'https://conv-a.wbx2.com/conversation/api/v1/conversations/bfb49281',
250
+ disableWebRedirect: true,
251
+ });
252
+
253
+ assert.deepEqual(
254
+ res,
255
+ {
256
+ conversationUrl: 'https://conv-a.wbx2.com/conversation/api/v1/conversations/bfb49281',
257
+ supportHostKey: true,
258
+ supportCountryList: true,
259
+ disableWebRedirect: true,
260
+ }
261
+ );
262
+ });
244
263
  });
245
264
 
246
265
  describe('#getWebexSite', () => {
@@ -182,6 +182,15 @@ describe('plugin-meetings', () => {
182
182
  metrics: {
183
183
  submitClientMetrics: sinon.stub().returns(Promise.resolve()),
184
184
  },
185
+ newMetrics: {
186
+ submitClientEvent: sinon.stub(),
187
+ callDiagnosticLatencies: {
188
+ measureLatency: sinon.stub().returns(Promise.resolve()),
189
+ },
190
+ callDiagnosticMetrics: {
191
+ clearErrorCache: sinon.stub(),
192
+ },
193
+ },
185
194
  });
186
195
  webex.emit('ready');
187
196
  });
@@ -285,7 +294,8 @@ describe('plugin-meetings', () => {
285
294
 
286
295
  describe('failure', () => {
287
296
  it('should not accept non-number input', () => {
288
- const logUploadIntervalMultiplicationFactor = webex.meetings.config.logUploadIntervalMultiplicationFactor;
297
+ const logUploadIntervalMultiplicationFactor =
298
+ webex.meetings.config.logUploadIntervalMultiplicationFactor;
289
299
 
290
300
  webex.meetings._setLogUploadIntervalMultiplicationFactor('test');
291
301
  assert.equal(
@@ -390,6 +400,45 @@ describe('plugin-meetings', () => {
390
400
  });
391
401
  });
392
402
 
403
+ describe('#_toggleDisableAudioMainDtx', () => {
404
+ it('should have _toggleDisableAudioMainDtx', () => {
405
+ assert.equal(typeof webex.meetings._toggleDisableAudioMainDtx, 'function');
406
+ });
407
+
408
+ describe('success', () => {
409
+ it('should update meetings to disable audio main dtx', () => {
410
+ webex.meetings._toggleDisableAudioMainDtx(true);
411
+ assert.equal(webex.meetings.config.experimental.disableAudioMainDtx, true);
412
+ });
413
+ });
414
+ });
415
+
416
+ describe('#_toggleEnableAudioTwccForMultistream', () => {
417
+ it('should have _toggleEnableAudioTwccForMultistream', () => {
418
+ assert.equal(typeof webex.meetings._toggleEnableAudioTwccForMultistream, 'function');
419
+ });
420
+
421
+ describe('success', () => {
422
+ it('should update meetings to enable audio twcc support', () => {
423
+ webex.meetings._toggleEnableAudioTwccForMultistream(true);
424
+ assert.equal(webex.meetings.config.enableAudioTwccForMultistream, true);
425
+ });
426
+ });
427
+ });
428
+
429
+ describe('#_toggleStopIceGatheringAfterFirstRelayCandidate', () => {
430
+ it('should have _toggleStopIceGatheringAfterFirstRelayCandidate', () => {
431
+ assert.equal(typeof webex.meetings._toggleStopIceGatheringAfterFirstRelayCandidate, 'function');
432
+ });
433
+
434
+ describe('success', () => {
435
+ it('should update meetings to stop ICE candidates gathering after first relay candidate', () => {
436
+ webex.meetings._toggleStopIceGatheringAfterFirstRelayCandidate(true);
437
+ assert.equal(webex.meetings.config.stopIceGatheringAfterFirstRelayCandidate, true);
438
+ });
439
+ });
440
+ });
441
+
393
442
  describe('Public API Contracts', () => {
394
443
  describe('#register', () => {
395
444
  it('emits an event and resolves when register succeeds', async () => {
@@ -441,6 +490,19 @@ describe('plugin-meetings', () => {
441
490
  assert.isTrue(webex.meetings.registered);
442
491
  });
443
492
 
493
+ it('resolves even if startReachability() rejects', async () => {
494
+ webex.canAuthorize = true;
495
+ webex.meetings.registered = false;
496
+ webex.meetings.startReachability = sinon.stub().rejects(new Error('fake error'));
497
+
498
+ await webex.meetings.register();
499
+ assert.calledOnceWithExactly(webex.internal.device.register, undefined);
500
+ assert.called(webex.internal.services.getMeetingPreferences);
501
+ assert.called(webex.internal.services.fetchClientRegionInfo);
502
+ assert.called(webex.internal.mercury.connect);
503
+ assert.isTrue(webex.meetings.registered);
504
+ });
505
+
444
506
  it('passes on the device registration options', async () => {
445
507
  webex.canAuthorize = true;
446
508
  webex.meetings.registered = false;
@@ -569,6 +631,24 @@ describe('plugin-meetings', () => {
569
631
  await assert.isRejected(webex.meetings.unregister());
570
632
  });
571
633
 
634
+ it('does not reject when device.unregister fails with statusCode 404', (done) => {
635
+ webex.meetings.registered = true;
636
+ webex.internal.device.unregister = sinon.stub().rejects({statusCode: 404});
637
+ webex.meetings.unregister().then(() => {
638
+ assert.calledWith(
639
+ TriggerProxy.trigger,
640
+ sinon.match.instanceOf(Meetings),
641
+ {
642
+ file: 'meetings',
643
+ function: 'unregister',
644
+ },
645
+ 'meetings:unregistered'
646
+ );
647
+ assert.isFalse(webex.meetings.registered);
648
+ done();
649
+ });
650
+ });
651
+
572
652
  it('rejects when mercury.disconnect fails', async () => {
573
653
  webex.meetings.registered = true;
574
654
  webex.internal.mercury.disconnect = sinon.stub().returns(Promise.reject());
@@ -617,6 +697,7 @@ describe('plugin-meetings', () => {
617
697
  quality: 'LOW',
618
698
  authToken: 'fake_token',
619
699
  mirror: false,
700
+ canvasResolutionScaling: 1,
620
701
  });
621
702
  assert.exists(result.enable);
622
703
  assert.exists(result.disable);
@@ -632,6 +713,7 @@ describe('plugin-meetings', () => {
632
713
  quality: 'HIGH',
633
714
  blurStrength: 'STRONG',
634
715
  bgImageUrl: 'https://test.webex.com/landscape.5a535788.jpg',
716
+ canvasResolutionScaling: 1,
635
717
  };
636
718
 
637
719
  const result = await webex.meetings.createVirtualBackgroundEffect(effectOptions);
@@ -666,7 +748,6 @@ describe('plugin-meetings', () => {
666
748
  audioContext: {},
667
749
  authToken: 'fake_token',
668
750
  mode: 'WORKLET',
669
- env: 'prod',
670
751
  avoidSimd: false,
671
752
  });
672
753
  assert.exists(result.enable);
@@ -918,6 +999,69 @@ describe('plugin-meetings', () => {
918
999
  });
919
1000
  });
920
1001
  });
1002
+ describe('#fetchStaticMeetingLink', () => {
1003
+ const conversationUrl = 'conv.fakeconversationurl.com';
1004
+
1005
+ afterEach(() => {
1006
+ sinon.restore();
1007
+ });
1008
+
1009
+ it('should have #fetchStaticMeetingLink', () => {
1010
+ assert.exists(webex.meetings.fetchStaticMeetingLink);
1011
+ });
1012
+
1013
+ it('should call MeetingInfo#fetchStaticMeetingLink() with proper params', () => {
1014
+ webex.meetings.meetingInfo.fetchStaticMeetingLink = sinon
1015
+ .stub()
1016
+ .resolves(conversationUrl);
1017
+
1018
+ return webex.meetings.fetchStaticMeetingLink(conversationUrl).then(() => {
1019
+ assert.calledWith(webex.meetings.meetingInfo.fetchStaticMeetingLink, conversationUrl);
1020
+ });
1021
+ });
1022
+ });
1023
+ describe('#enableStaticMeetingLink', () => {
1024
+ const conversationUrl = 'conv.fakeconversationurl.com';
1025
+
1026
+ afterEach(() => {
1027
+ sinon.restore();
1028
+ });
1029
+
1030
+ it('should have #enableStaticMeetingLink', () => {
1031
+ assert.exists(webex.meetings.enableStaticMeetingLink);
1032
+ });
1033
+
1034
+ it('should call MeetingInfo#enableStaticMeetingLink() with proper params', () => {
1035
+ webex.meetings.meetingInfo.enableStaticMeetingLink = sinon
1036
+ .stub()
1037
+ .resolves(conversationUrl);
1038
+
1039
+ return webex.meetings.enableStaticMeetingLink(conversationUrl).then(() => {
1040
+ assert.calledWith(webex.meetings.meetingInfo.enableStaticMeetingLink, conversationUrl);
1041
+ });
1042
+ });
1043
+ });
1044
+ describe('#disableStaticMeetingLink', () => {
1045
+ const conversationUrl = 'conv.fakeconversationurl.com';
1046
+
1047
+ afterEach(() => {
1048
+ sinon.restore();
1049
+ });
1050
+
1051
+ it('should have #disableStaticMeetingLink', () => {
1052
+ assert.exists(webex.meetings.disableStaticMeetingLink);
1053
+ });
1054
+
1055
+ it('should call MeetingInfo#disableStaticMeetingLink() with proper params', () => {
1056
+ webex.meetings.meetingInfo.disableStaticMeetingLink = sinon
1057
+ .stub()
1058
+ .resolves(conversationUrl);
1059
+
1060
+ return webex.meetings.disableStaticMeetingLink(conversationUrl).then(() => {
1061
+ assert.calledWith(webex.meetings.meetingInfo.disableStaticMeetingLink, conversationUrl);
1062
+ });
1063
+ });
1064
+ });
921
1065
  describe('#create', () => {
922
1066
  let infoOptions;
923
1067
 
@@ -50,6 +50,13 @@ describe('member', () => {
50
50
 
51
51
  assert.calledOnceWithExactly(MemberUtil.canReclaimHost, participant);
52
52
  });
53
+
54
+ it('checks that processParticipant calls isPresenterAssignmentProhibited', () => {
55
+ sinon.spy(MemberUtil, 'isPresenterAssignmentProhibited');
56
+ member.processParticipant(participant);
57
+
58
+ assert.calledOnceWithExactly(MemberUtil.isPresenterAssignmentProhibited, participant);
59
+ });
53
60
  })
54
61
 
55
62
  describe('#processMember', () => {
@@ -557,6 +557,30 @@ describe('plugin-meetings', () => {
557
557
  testResult(false, undefined, false);
558
558
  });
559
559
  });
560
+
561
+ describe('MemberUtil.isPresenterAssignmentProhibited', () => {
562
+ it('returns true when isPresenterAssignmentProhibited is true', () => {
563
+ const participant = {
564
+ presenterAssignmentNotAllowed: true
565
+ };
566
+
567
+ assert.isTrue(MemberUtil.isPresenterAssignmentProhibited(participant));
568
+ });
569
+
570
+ it('returns false when isPresenterAssignmentProhibited is false', () => {
571
+ const participant = {
572
+ presenterAssignmentNotAllowed: false,
573
+ };
574
+
575
+ assert.isFalse(MemberUtil.isPresenterAssignmentProhibited(participant));
576
+ });
577
+
578
+ it('returns undefined when isPresenterAssignmentProhibited is undefined', () => {
579
+ const participant = {};
580
+
581
+ assert.isUndefined(MemberUtil.isPresenterAssignmentProhibited(participant));
582
+ });
583
+ });
560
584
  });
561
585
 
562
586
  describe('extractMediaStatus', () => {
@@ -120,9 +120,9 @@ describe('plugin-meetings', () => {
120
120
  meeting = {
121
121
  request: sinon.mock().returns(Promise.resolve()),
122
122
  locusInfo: {
123
- sequence: {}
124
- }
125
- }
123
+ sequence: {},
124
+ },
125
+ };
126
126
 
127
127
  createMembers = (options) => new Members({locusUrl: options.url, meeting}, {parent: webex});
128
128
  });
@@ -157,6 +157,17 @@ describe('plugin-meetings', () => {
157
157
 
158
158
  assert.isRejected(members.addMember({email: 'test@cisco.com'}));
159
159
  });
160
+
161
+ it('should accept valid SIP email addresses', async () => {
162
+ sandbox.spy(MembersUtil, 'isInvalidInvitee');
163
+
164
+ const members = createMembers({url: true});
165
+
166
+ await members.addMember({email: 'sip:test@cisco.com'});
167
+
168
+ assert.calledOnce(MembersUtil.isInvalidInvitee);
169
+ assert.isFalse(MembersUtil.isInvalidInvitee({email: 'sip:test@cisco.com'}), 'SIP email should be valid');
170
+ });
160
171
  });
161
172
 
162
173
  describe('#admitMembers', () => {
@@ -342,6 +353,32 @@ describe('plugin-meetings', () => {
342
353
  });
343
354
  });
344
355
 
356
+ describe('#cancelSIPInvite', () => {
357
+ const memberId = uuid.v4();
358
+ it('should invoke cancelSIPInviteOptions from MembersUtil when cancelSIPInvite is called with valid params', async () => {
359
+ sandbox.spy(MembersUtil, 'cancelSIPInviteOptions');
360
+
361
+ const members = createMembers({url: url1});
362
+
363
+ await members.cancelSIPInvite({memberId});
364
+ assert.calledOnce(MembersUtil.cancelSIPInviteOptions);
365
+ });
366
+
367
+ it('should throw a rejection if there is no locus url', async () => {
368
+ const members = createMembers({url: false});
369
+
370
+ assert.isRejected(members.cancelSIPInvite({memberId}));
371
+ });
372
+
373
+ it('should throw a rejection if memberId is not provided', async () => {
374
+ const members = createMembers({url: url1});
375
+
376
+ assert.isRejected(members.cancelSIPInvite({}));
377
+ assert.isRejected(members.cancelSIPInvite({memberId: null}));
378
+ assert.isRejected(members.cancelSIPInvite({memberId: undefined}));
379
+ });
380
+ });
381
+
345
382
  describe('#assignRoles', () => {
346
383
  const fakeRoles = [
347
384
  {type: 'PRESENTER', hasRole: true},
@@ -349,7 +386,7 @@ describe('plugin-meetings', () => {
349
386
  {type: 'COHOST', hasRole: true},
350
387
  ];
351
388
 
352
- const resolvedValue = "it worked";
389
+ const resolvedValue = 'it worked';
353
390
 
354
391
  const genericMessage = 'Generic error from the API';
355
392
 
@@ -364,9 +401,13 @@ describe('plugin-meetings', () => {
364
401
  };
365
402
 
366
403
  if (errorCode) {
367
- spies.assignRolesMember = sandbox.stub(members.membersRequest, 'assignRolesMember').rejects({body: {errorCode}, message: genericMessage});
404
+ spies.assignRolesMember = sandbox
405
+ .stub(members.membersRequest, 'assignRolesMember')
406
+ .rejects({body: {errorCode}, message: genericMessage});
368
407
  } else {
369
- spies.assignRolesMember = sandbox.stub(members.membersRequest, 'assignRolesMember').resolves(resolvedValue);
408
+ spies.assignRolesMember = sandbox
409
+ .stub(members.membersRequest, 'assignRolesMember')
410
+ .resolves(resolvedValue);
370
411
  }
371
412
 
372
413
  return {members, spies};
@@ -378,7 +419,15 @@ describe('plugin-meetings', () => {
378
419
  assert.notCalled(spies.assignRolesMember);
379
420
  };
380
421
 
381
- const checkError = async (error, expectedMemberId, expectedRoles, expectedLocusUrl, resultPromise, expectedMessage, spies) => {
422
+ const checkError = async (
423
+ error,
424
+ expectedMemberId,
425
+ expectedRoles,
426
+ expectedLocusUrl,
427
+ resultPromise,
428
+ expectedMessage,
429
+ spies
430
+ ) => {
382
431
  await assert.isRejected(resultPromise, error, expectedMessage);
383
432
  assert.calledOnceWithExactly(
384
433
  spies.generateRoleAssignmentMemberOptions,
@@ -423,7 +472,7 @@ describe('plugin-meetings', () => {
423
472
  await checkInvalid(
424
473
  resultPromise,
425
474
  'The member id must be defined to assign the roles to a member.',
426
- spies,
475
+ spies
427
476
  );
428
477
  });
429
478
 
@@ -435,7 +484,7 @@ describe('plugin-meetings', () => {
435
484
  await checkInvalid(
436
485
  resultPromise,
437
486
  'The associated locus url for this meetings members object must be defined.',
438
- spies,
487
+ spies
439
488
  );
440
489
  });
441
490
 
@@ -452,7 +501,7 @@ describe('plugin-meetings', () => {
452
501
  url1,
453
502
  resultPromise,
454
503
  'Non converged meetings, PSTN or SIP users in converged meetings are not supported currently.',
455
- spies,
504
+ spies
456
505
  );
457
506
  });
458
507
 
@@ -469,7 +518,7 @@ describe('plugin-meetings', () => {
469
518
  url1,
470
519
  resultPromise,
471
520
  'Reclaim Host Role Not Allowed For Other Participants. Participants cannot claim host role in PMR meeting, space instant meeting or escalated instant meeting. However, the original host still can reclaim host role when it manually makes another participant to be the host.',
472
- spies,
521
+ spies
473
522
  );
474
523
  });
475
524
 
@@ -486,7 +535,7 @@ describe('plugin-meetings', () => {
486
535
  url1,
487
536
  resultPromise,
488
537
  'Host Key Not Specified Or Matched. The original host can reclaim the host role without entering the host key. However, any other person who claims the host role must enter the host key to get it.',
489
- spies,
538
+ spies
490
539
  );
491
540
  });
492
541
 
@@ -503,7 +552,7 @@ describe('plugin-meetings', () => {
503
552
  url1,
504
553
  resultPromise,
505
554
  'Participant Having Host Role Already. Participant who sends request to reclaim host role has already a host role.',
506
- spies,
555
+ spies
507
556
  );
508
557
  });
509
558
 
@@ -520,7 +569,7 @@ describe('plugin-meetings', () => {
520
569
  url1,
521
570
  resultPromise,
522
571
  genericMessage,
523
- spies,
572
+ spies
524
573
  );
525
574
  });
526
575
 
@@ -530,13 +579,7 @@ describe('plugin-meetings', () => {
530
579
 
531
580
  const resultPromise = members.assignRoles(memberId, fakeRoles);
532
581
 
533
- await checkValid(
534
- resultPromise,
535
- spies,
536
- memberId,
537
- fakeRoles,
538
- url1,
539
- );
582
+ await checkValid(resultPromise, spies, memberId, fakeRoles, url1);
540
583
  });
541
584
  });
542
585
 
@@ -661,19 +704,19 @@ describe('plugin-meetings', () => {
661
704
  spies,
662
705
  expectedRequestingMemberId,
663
706
  expectedLocusUrl,
664
- expectedRoles,
707
+ expectedRoles
665
708
  ) => {
666
709
  await assert.isFulfilled(resultPromise);
667
710
  assert.calledOnceWithExactly(
668
711
  spies.generateLowerAllHandsMemberOptions,
669
712
  expectedRequestingMemberId,
670
713
  expectedLocusUrl,
671
- expectedRoles,
714
+ expectedRoles
672
715
  );
673
716
  assert.calledOnceWithExactly(spies.lowerAllHandsMember, {
674
717
  requestingParticipantId: expectedRequestingMemberId,
675
718
  locusUrl: expectedLocusUrl,
676
- ...(expectedRoles !== undefined && { roles: expectedRoles })
719
+ ...(expectedRoles !== undefined && {roles: expectedRoles}),
677
720
  });
678
721
  assert.strictEqual(resultPromise, spies.lowerAllHandsMember.getCall(0).returnValue);
679
722
  };
@@ -714,7 +757,7 @@ describe('plugin-meetings', () => {
714
757
  it('should make the correct request when called with valid requestingMemberId and roles', async () => {
715
758
  const requestingMemberId = 'test-member-id';
716
759
  const roles = ['panelist', 'attendee'];
717
- const { members, spies } = setup('test-locus-url');
760
+ const {members, spies} = setup('test-locus-url');
718
761
 
719
762
  const resultPromise = members.lowerAllHands(requestingMemberId, roles);
720
763
 
@@ -724,7 +767,7 @@ describe('plugin-meetings', () => {
724
767
  it('should handle an empty roles array correctly', async () => {
725
768
  const requestingMemberId = 'test-member-id';
726
769
  const roles = [];
727
- const { members, spies } = setup('test-locus-url');
770
+ const {members, spies} = setup('test-locus-url');
728
771
 
729
772
  const resultPromise = members.lowerAllHands(requestingMemberId, roles);
730
773
 
@@ -977,5 +1020,76 @@ describe('plugin-meetings', () => {
977
1020
  );
978
1021
  });
979
1022
  });
1023
+
1024
+ describe('#moveToLobby', () => {
1025
+ const setup = (locusUrl) => {
1026
+ const members = createMembers({url: locusUrl});
1027
+
1028
+ const spies = {
1029
+ getMoveMemberToLobbyRequestBody: sandbox.spy(
1030
+ MembersUtil,
1031
+ 'getMoveMemberToLobbyRequestBody'
1032
+ ),
1033
+ moveToLobbyMember: sandbox.spy(members.membersRequest, 'moveToLobbyMember'),
1034
+ };
1035
+
1036
+ return {members, spies};
1037
+ };
1038
+
1039
+ const checkInvalid = async (resultPromise, expectedMessage, spies) => {
1040
+ await assert.isRejected(resultPromise, ParameterError, expectedMessage);
1041
+ assert.notCalled(spies.getMoveMemberToLobbyRequestBody);
1042
+ assert.notCalled(spies.moveToLobbyMember);
1043
+ };
1044
+
1045
+ const checkValid = async (resultPromise, spies, expectedMemberId, expectedLocusUrl) => {
1046
+ await assert.isFulfilled(resultPromise);
1047
+ assert.calledOnceWithExactly(spies.getMoveMemberToLobbyRequestBody, expectedMemberId);
1048
+ assert.calledOnceWithExactly(
1049
+ spies.moveToLobbyMember,
1050
+ {
1051
+ locusUrl: expectedLocusUrl,
1052
+ memberId: expectedMemberId,
1053
+ },
1054
+ {
1055
+ moveToLobby: {participantIds: [expectedMemberId]},
1056
+ }
1057
+ );
1058
+ assert.strictEqual(resultPromise, spies.moveToLobbyMember.getCall(0).returnValue);
1059
+ };
1060
+
1061
+ it('should not make a request if there is no member id', async () => {
1062
+ const {members, spies} = setup(url1);
1063
+
1064
+ const resultPromise = members.moveToLobby();
1065
+
1066
+ await checkInvalid(
1067
+ resultPromise,
1068
+ 'The member id must be defined to move the member to lobby.',
1069
+ spies
1070
+ );
1071
+ });
1072
+
1073
+ it('should not make a request if there is no locus url', async () => {
1074
+ const {members, spies} = setup();
1075
+
1076
+ const resultPromise = members.moveToLobby(uuid.v4());
1077
+
1078
+ await checkInvalid(
1079
+ resultPromise,
1080
+ 'The associated locus url for this meetings members object must be defined.',
1081
+ spies
1082
+ );
1083
+ });
1084
+
1085
+ it('should make the correct request when called with valid memberId and locusUrl', async () => {
1086
+ const memberId = uuid.v4();
1087
+ const {members, spies} = setup(url1);
1088
+
1089
+ const resultPromise = members.moveToLobby(memberId);
1090
+
1091
+ await checkValid(resultPromise, spies, memberId, url1);
1092
+ });
1093
+ });
980
1094
  });
981
1095
  });