@webex/plugin-meetings 3.8.1-web-workers-keepalive.1 → 3.9.0-multipleLLM.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.
- package/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/constants.js +26 -2
- package/dist/constants.js.map +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/index.js +77 -95
- package/dist/locus-info/index.js.map +1 -1
- package/dist/locus-info/parser.js +4 -1
- package/dist/locus-info/parser.js.map +1 -1
- package/dist/media/properties.js +53 -5
- package/dist/media/properties.js.map +1 -1
- package/dist/meeting/brbState.js +14 -12
- package/dist/meeting/brbState.js.map +1 -1
- package/dist/meeting/in-meeting-actions.js +8 -0
- package/dist/meeting/in-meeting-actions.js.map +1 -1
- package/dist/meeting/index.js +443 -225
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/muteState.js +2 -5
- package/dist/meeting/muteState.js.map +1 -1
- package/dist/meeting/request.js +44 -0
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/request.type.js.map +1 -1
- package/dist/meeting/type.js +7 -0
- package/dist/meeting/type.js.map +1 -0
- package/dist/meeting/util.js +98 -13
- package/dist/meeting/util.js.map +1 -1
- package/dist/meeting-info/meeting-info-v2.js +29 -21
- package/dist/meeting-info/meeting-info-v2.js.map +1 -1
- package/dist/meetings/index.js +18 -10
- package/dist/meetings/index.js.map +1 -1
- package/dist/member/index.js.map +1 -1
- package/dist/member/types.js.map +1 -1
- package/dist/members/collection.js +13 -0
- package/dist/members/collection.js.map +1 -1
- package/dist/members/index.js +53 -29
- package/dist/members/index.js.map +1 -1
- package/dist/members/request.js +3 -3
- package/dist/members/request.js.map +1 -1
- package/dist/members/util.js +25 -8
- package/dist/members/util.js.map +1 -1
- package/dist/metrics/constants.js +2 -1
- package/dist/metrics/constants.js.map +1 -1
- package/dist/multistream/mediaRequestManager.js +1 -1
- package/dist/multistream/mediaRequestManager.js.map +1 -1
- package/dist/multistream/remoteMedia.js +34 -5
- package/dist/multistream/remoteMedia.js.map +1 -1
- package/dist/multistream/remoteMediaGroup.js +42 -2
- package/dist/multistream/remoteMediaGroup.js.map +1 -1
- package/dist/multistream/sendSlotManager.js +32 -2
- package/dist/multistream/sendSlotManager.js.map +1 -1
- package/dist/reachability/index.js +3 -3
- package/dist/reachability/index.js.map +1 -1
- package/dist/types/constants.d.ts +24 -0
- package/dist/types/locus-info/index.d.ts +54 -10
- package/dist/types/media/properties.d.ts +21 -0
- package/dist/types/meeting/brbState.d.ts +0 -1
- package/dist/types/meeting/in-meeting-actions.d.ts +8 -0
- package/dist/types/meeting/index.d.ts +51 -20
- package/dist/types/meeting/request.d.ts +18 -1
- package/dist/types/meeting/request.type.d.ts +74 -0
- package/dist/types/meeting/type.d.ts +9 -0
- package/dist/types/meeting/util.d.ts +13 -3
- package/dist/types/meeting-info/meeting-info-v2.d.ts +6 -3
- package/dist/types/meetings/index.d.ts +3 -1
- package/dist/types/member/types.d.ts +1 -0
- package/dist/types/members/collection.d.ts +6 -0
- package/dist/types/members/index.d.ts +22 -9
- package/dist/types/members/request.d.ts +1 -1
- package/dist/types/members/util.d.ts +13 -6
- package/dist/types/metrics/constants.d.ts +1 -0
- package/dist/types/multistream/remoteMedia.d.ts +20 -1
- package/dist/types/multistream/remoteMediaGroup.d.ts +11 -0
- package/dist/types/multistream/sendSlotManager.d.ts +16 -0
- package/dist/webinar/index.js +1 -1
- package/package.json +23 -24
- package/src/constants.ts +25 -2
- package/src/locus-info/index.ts +133 -96
- package/src/locus-info/parser.ts +5 -1
- package/src/media/properties.ts +43 -0
- package/src/meeting/brbState.ts +9 -7
- package/src/meeting/in-meeting-actions.ts +17 -0
- package/src/meeting/index.ts +273 -42
- package/src/meeting/muteState.ts +2 -6
- package/src/meeting/request.ts +39 -0
- package/src/meeting/request.type.ts +64 -0
- package/src/meeting/type.ts +9 -0
- package/src/meeting/util.ts +114 -22
- package/src/meeting-info/meeting-info-v2.ts +24 -5
- package/src/meetings/index.ts +12 -5
- package/src/member/index.ts +1 -0
- package/src/member/types.ts +1 -0
- package/src/members/collection.ts +11 -0
- package/src/members/index.ts +51 -15
- package/src/members/request.ts +2 -2
- package/src/members/util.ts +34 -6
- package/src/metrics/constants.ts +1 -0
- package/src/multistream/mediaRequestManager.ts +7 -7
- package/src/multistream/remoteMedia.ts +34 -4
- package/src/multistream/remoteMediaGroup.ts +37 -2
- package/src/multistream/sendSlotManager.ts +34 -2
- package/src/reachability/index.ts +3 -3
- package/test/unit/spec/locus-info/index.js +229 -98
- package/test/unit/spec/locus-info/parser.js +3 -2
- package/test/unit/spec/media/properties.ts +137 -0
- package/test/unit/spec/meeting/brbState.ts +9 -9
- package/test/unit/spec/meeting/in-meeting-actions.ts +8 -0
- package/test/unit/spec/meeting/index.js +1022 -93
- package/test/unit/spec/meeting/muteState.js +32 -6
- package/test/unit/spec/meeting/request.js +92 -0
- package/test/unit/spec/meeting/utils.js +167 -17
- package/test/unit/spec/meeting-info/meetinginfov2.js +8 -3
- package/test/unit/spec/meetings/index.js +12 -1
- package/test/unit/spec/members/collection.js +120 -0
- package/test/unit/spec/members/index.js +140 -12
- package/test/unit/spec/members/request.js +57 -2
- package/test/unit/spec/members/utils.js +139 -17
- package/test/unit/spec/multistream/mediaRequestManager.ts +19 -6
- package/test/unit/spec/multistream/remoteMedia.ts +66 -2
- package/test/unit/spec/multistream/sendSlotManager.ts +59 -0
- package/test/unit/spec/reachability/index.ts +158 -1
@@ -12,7 +12,7 @@ describe('plugin-meetings', () => {
|
|
12
12
|
let video;
|
13
13
|
let originalRemoteUpdateAudioVideo;
|
14
14
|
|
15
|
-
const
|
15
|
+
const fakeLocusResponse = {body: {locus: {info: 'this is a fake locus'}}};
|
16
16
|
|
17
17
|
const createFakeLocalStream = (id, userMuted, systemMuted) => {
|
18
18
|
return {
|
@@ -38,9 +38,6 @@ describe('plugin-meetings', () => {
|
|
38
38
|
unmuteAllowed: true,
|
39
39
|
remoteVideoMuted: false,
|
40
40
|
unmuteVideoAllowed: true,
|
41
|
-
locusInfo: {
|
42
|
-
handleLocusDelta: sinon.stub(),
|
43
|
-
},
|
44
41
|
members: {
|
45
42
|
selfId: 'fake self id',
|
46
43
|
muteMember: sinon.stub().resolves(),
|
@@ -49,7 +46,8 @@ describe('plugin-meetings', () => {
|
|
49
46
|
|
50
47
|
originalRemoteUpdateAudioVideo = MeetingUtil.remoteUpdateAudioVideo;
|
51
48
|
|
52
|
-
MeetingUtil.remoteUpdateAudioVideo = sinon.stub().resolves(
|
49
|
+
MeetingUtil.remoteUpdateAudioVideo = sinon.stub().resolves(fakeLocusResponse);
|
50
|
+
MeetingUtil.updateLocusFromApiResponse = sinon.stub();
|
53
51
|
|
54
52
|
audio = createMuteState(AUDIO, meeting, true);
|
55
53
|
video = createMuteState(VIDEO, meeting, true);
|
@@ -141,6 +139,7 @@ describe('plugin-meetings', () => {
|
|
141
139
|
// and local unmute was sent to server
|
142
140
|
assert.calledOnce(MeetingUtil.remoteUpdateAudioVideo);
|
143
141
|
assert.calledWith(MeetingUtil.remoteUpdateAudioVideo, meeting, false, undefined);
|
142
|
+
assert.calledWith(MeetingUtil.updateLocusFromApiResponse, meeting, fakeLocusResponse);
|
144
143
|
|
145
144
|
assert.isFalse(audio.isMuted());
|
146
145
|
});
|
@@ -173,6 +172,7 @@ describe('plugin-meetings', () => {
|
|
173
172
|
|
174
173
|
// system was muted so local unmute was not sent to server
|
175
174
|
assert.notCalled(MeetingUtil.remoteUpdateAudioVideo);
|
175
|
+
assert.notCalled(MeetingUtil.updateLocusFromApiResponse);
|
176
176
|
|
177
177
|
assert.isTrue(audio.isMuted());
|
178
178
|
});
|
@@ -207,6 +207,7 @@ describe('plugin-meetings', () => {
|
|
207
207
|
// and local unmute was sent to server
|
208
208
|
assert.calledOnce(MeetingUtil.remoteUpdateAudioVideo);
|
209
209
|
assert.calledWith(MeetingUtil.remoteUpdateAudioVideo, meeting, undefined, false);
|
210
|
+
assert.calledWith(MeetingUtil.updateLocusFromApiResponse, meeting, fakeLocusResponse);
|
210
211
|
|
211
212
|
assert.isFalse(video.isMuted());
|
212
213
|
});
|
@@ -219,7 +220,9 @@ describe('plugin-meetings', () => {
|
|
219
220
|
|
220
221
|
assert.isTrue(video.isMuted());
|
221
222
|
|
223
|
+
await testUtils.flushPromises();
|
222
224
|
MeetingUtil.remoteUpdateAudioVideo.resetHistory();
|
225
|
+
MeetingUtil.updateLocusFromApiResponse.resetHistory();
|
223
226
|
|
224
227
|
// now simulate server requiring us to locally unmute
|
225
228
|
// assuming setServerMuted succeeds at updating userMuted
|
@@ -239,6 +242,7 @@ describe('plugin-meetings', () => {
|
|
239
242
|
|
240
243
|
// system was muted so local unmute was not sent to server
|
241
244
|
assert.notCalled(MeetingUtil.remoteUpdateAudioVideo);
|
245
|
+
assert.notCalled(MeetingUtil.updateLocusFromApiResponse);
|
242
246
|
|
243
247
|
assert.isTrue(video.isMuted());
|
244
248
|
});
|
@@ -443,6 +447,7 @@ describe('plugin-meetings', () => {
|
|
443
447
|
|
444
448
|
assert.calledOnce(MeetingUtil.remoteUpdateAudioVideo);
|
445
449
|
assert.calledWith(MeetingUtil.remoteUpdateAudioVideo, meeting, true, undefined);
|
450
|
+
assert.calledWith(MeetingUtil.updateLocusFromApiResponse, meeting, fakeLocusResponse);
|
446
451
|
|
447
452
|
// now allow the first request to complete
|
448
453
|
serverResponseResolve();
|
@@ -559,6 +564,7 @@ describe('plugin-meetings', () => {
|
|
559
564
|
await testUtils.flushPromises();
|
560
565
|
|
561
566
|
MeetingUtil.remoteUpdateAudioVideo.resetHistory();
|
567
|
+
MeetingUtil.updateLocusFromApiResponse.resetHistory();
|
562
568
|
};
|
563
569
|
|
564
570
|
const setupSpies = (mediaType) => {
|
@@ -605,13 +611,15 @@ describe('plugin-meetings', () => {
|
|
605
611
|
{mediaType: VIDEO, title: 'video'},
|
606
612
|
];
|
607
613
|
|
614
|
+
const fakeLocusResponse = {body: {locus: {info: 'fake locus'}}};
|
615
|
+
|
608
616
|
tests.forEach(({mediaType, title}) =>
|
609
617
|
describe(title, () => {
|
610
618
|
let originalRemoteUpdateAudioVideo;
|
611
619
|
|
612
620
|
beforeEach(() => {
|
613
621
|
originalRemoteUpdateAudioVideo = MeetingUtil.remoteUpdateAudioVideo;
|
614
|
-
MeetingUtil.remoteUpdateAudioVideo = sinon.stub().resolves(
|
622
|
+
MeetingUtil.remoteUpdateAudioVideo = sinon.stub().resolves(fakeLocusResponse);
|
615
623
|
});
|
616
624
|
|
617
625
|
afterEach(() => {
|
@@ -660,6 +668,7 @@ describe('plugin-meetings', () => {
|
|
660
668
|
assert.calledWith(setUnmuteAllowedSpy, muteState.state.server.unmuteAllowed);
|
661
669
|
assert.notCalled(setServerMutedSpy);
|
662
670
|
assert.notCalled(MeetingUtil.remoteUpdateAudioVideo);
|
671
|
+
assert.notCalled(MeetingUtil.updateLocusFromApiResponse);
|
663
672
|
assert.isTrue(muteState.state.client.localMute);
|
664
673
|
});
|
665
674
|
|
@@ -672,6 +681,7 @@ describe('plugin-meetings', () => {
|
|
672
681
|
assert.calledWith(setUnmuteAllowedSpy, muteState.state.server.unmuteAllowed);
|
673
682
|
assert.notCalled(setServerMutedSpy);
|
674
683
|
assert.notCalled(MeetingUtil.remoteUpdateAudioVideo);
|
684
|
+
assert.notCalled(MeetingUtil.updateLocusFromApiResponse);
|
675
685
|
assert.isTrue(muteState.state.client.localMute);
|
676
686
|
});
|
677
687
|
|
@@ -681,9 +691,12 @@ describe('plugin-meetings', () => {
|
|
681
691
|
|
682
692
|
muteState.init(meeting);
|
683
693
|
|
694
|
+
await testUtils.flushPromises();
|
695
|
+
|
684
696
|
assert.calledWith(setUnmuteAllowedSpy, muteState.state.server.unmuteAllowed);
|
685
697
|
assert.notCalled(setServerMutedSpy);
|
686
698
|
assert.calledOnce(MeetingUtil.remoteUpdateAudioVideo);
|
699
|
+
assert.calledOnceWithExactly(MeetingUtil.updateLocusFromApiResponse, meeting, fakeLocusResponse);
|
687
700
|
assert.isFalse(muteState.state.client.localMute);
|
688
701
|
});
|
689
702
|
|
@@ -707,6 +720,7 @@ describe('plugin-meetings', () => {
|
|
707
720
|
simulateUserMute(mediaType, true);
|
708
721
|
muteState.handleLocalStreamMuteStateChange(meeting);
|
709
722
|
assert.notCalled(MeetingUtil.remoteUpdateAudioVideo);
|
723
|
+
assert.notCalled(MeetingUtil.updateLocusFromApiResponse);
|
710
724
|
|
711
725
|
assert.isFalse(muteState.state.client.localMute);
|
712
726
|
});
|
@@ -716,8 +730,11 @@ describe('plugin-meetings', () => {
|
|
716
730
|
|
717
731
|
simulateUserMute(mediaType, false);
|
718
732
|
muteState.handleLocalStreamMuteStateChange(meeting);
|
733
|
+
await testUtils.flushPromises();
|
734
|
+
|
719
735
|
assert.equal(muteState.state.client.localMute, false);
|
720
736
|
assert.called(MeetingUtil.remoteUpdateAudioVideo);
|
737
|
+
assert.calledOnceWithExactly(MeetingUtil.updateLocusFromApiResponse, meeting, fakeLocusResponse);
|
721
738
|
});
|
722
739
|
|
723
740
|
it('tests localMute - user mute from false to true', async () => {
|
@@ -725,8 +742,11 @@ describe('plugin-meetings', () => {
|
|
725
742
|
|
726
743
|
simulateUserMute(mediaType, true);
|
727
744
|
muteState.handleLocalStreamMuteStateChange(meeting);
|
745
|
+
await testUtils.flushPromises();
|
746
|
+
|
728
747
|
assert.equal(muteState.state.client.localMute, true);
|
729
748
|
assert.called(MeetingUtil.remoteUpdateAudioVideo);
|
749
|
+
assert.calledOnceWithExactly(MeetingUtil.updateLocusFromApiResponse, meeting, fakeLocusResponse);
|
730
750
|
});
|
731
751
|
|
732
752
|
it('tests localMute - system mute from true to false', async () => {
|
@@ -734,8 +754,11 @@ describe('plugin-meetings', () => {
|
|
734
754
|
|
735
755
|
simulateSystemMute(mediaType, false);
|
736
756
|
muteState.handleLocalStreamMuteStateChange(meeting);
|
757
|
+
await testUtils.flushPromises();
|
758
|
+
|
737
759
|
assert.equal(muteState.state.client.localMute, false);
|
738
760
|
assert.called(MeetingUtil.remoteUpdateAudioVideo);
|
761
|
+
assert.calledOnceWithExactly(MeetingUtil.updateLocusFromApiResponse, meeting, fakeLocusResponse);
|
739
762
|
});
|
740
763
|
|
741
764
|
it('tests localMute - system mute from false to true', async () => {
|
@@ -743,8 +766,11 @@ describe('plugin-meetings', () => {
|
|
743
766
|
|
744
767
|
simulateSystemMute(mediaType, true);
|
745
768
|
muteState.handleLocalStreamMuteStateChange(meeting);
|
769
|
+
await testUtils.flushPromises();
|
770
|
+
|
746
771
|
assert.equal(muteState.state.client.localMute, true);
|
747
772
|
assert.called(MeetingUtil.remoteUpdateAudioVideo);
|
773
|
+
assert.calledOnceWithExactly(MeetingUtil.updateLocusFromApiResponse, meeting, fakeLocusResponse);
|
748
774
|
});
|
749
775
|
});
|
750
776
|
|
@@ -826,4 +826,96 @@ describe('plugin-meetings', () => {
|
|
826
826
|
});
|
827
827
|
});
|
828
828
|
});
|
829
|
+
|
830
|
+
describe('#synchronizeStage', () => {
|
831
|
+
[
|
832
|
+
['an unset stage', {overrideDefault: false}],
|
833
|
+
[
|
834
|
+
'a minimally set stage',
|
835
|
+
{
|
836
|
+
overrideDefault: true,
|
837
|
+
lockAttendeeViewOnStageOnly: false,
|
838
|
+
stageParameters: {
|
839
|
+
activeSpeakerProportion: 0.5,
|
840
|
+
showActiveSpeaker: {show: false, order: 0},
|
841
|
+
stageManagerType: 0,
|
842
|
+
},
|
843
|
+
},
|
844
|
+
],
|
845
|
+
[
|
846
|
+
'a fully set stage',
|
847
|
+
{
|
848
|
+
overrideDefault: true,
|
849
|
+
lockAttendeeViewOnStageOnly: true,
|
850
|
+
stageParameters: {
|
851
|
+
activeSpeakerProportion: 0.6,
|
852
|
+
importantParticipants: [
|
853
|
+
{mainCsi: 11111111, participantId: uuidv4(), order: 1},
|
854
|
+
{mainCsi: 22222222, participantId: uuidv4(), order: 2},
|
855
|
+
{mainCsi: 33333333, participantId: uuidv4(), order: 3},
|
856
|
+
{mainCsi: 44444444, participantId: uuidv4(), order: 4},
|
857
|
+
{mainCsi: 55555555, participantId: uuidv4(), order: 5},
|
858
|
+
{mainCsi: 66666666, participantId: uuidv4(), order: 6},
|
859
|
+
{mainCsi: 77777777, participantId: uuidv4(), order: 7},
|
860
|
+
{mainCsi: 88888888, participantId: uuidv4(), order: 8},
|
861
|
+
],
|
862
|
+
showActiveSpeaker: {show: true, order: 0},
|
863
|
+
stageManagerType: 7,
|
864
|
+
},
|
865
|
+
customLayouts: {
|
866
|
+
background: {url: `https://test.wbx2.com/background/${uuidv4()}.jpg`},
|
867
|
+
logo: {url: `https://test.wbx2.com/logo/${uuidv4()}.png`, position: 'UpperMiddle'},
|
868
|
+
},
|
869
|
+
nameLabelStyle: {
|
870
|
+
accentColor: '#00A3FF',
|
871
|
+
background: {color: 'rgba(0, 163, 255, 1)'},
|
872
|
+
border: {color: 'rgba(0, 163, 255, 1)'},
|
873
|
+
content: {
|
874
|
+
displayName: {color: 'rgba(255, 255, 255, 0.95)'},
|
875
|
+
subtitle: {color: 'rgba(255, 255, 255, 0.7)'},
|
876
|
+
},
|
877
|
+
decoration: {color: 'rgba(255, 255, 255, 0.95)'},
|
878
|
+
fadeOut: {delay: 15},
|
879
|
+
type: 'PrimaryInverted',
|
880
|
+
},
|
881
|
+
},
|
882
|
+
],
|
883
|
+
].forEach(([description, videoLayout]) => {
|
884
|
+
it(`sends request to synchronize the stage with ${description} video layout`, async () => {
|
885
|
+
const locusUrl = `https://locus-test.wbx2.com/locus/api/v1/loci/${uuidv4()}`;
|
886
|
+
|
887
|
+
const synchronizePromise = meetingsRequest.synchronizeStage(locusUrl, videoLayout);
|
888
|
+
|
889
|
+
assert.exists(synchronizePromise.then);
|
890
|
+
await synchronizePromise;
|
891
|
+
|
892
|
+
checkRequest({
|
893
|
+
method: 'PATCH',
|
894
|
+
uri: `${locusUrl}/controls`,
|
895
|
+
body: {videoLayout},
|
896
|
+
});
|
897
|
+
});
|
898
|
+
});
|
899
|
+
});
|
900
|
+
|
901
|
+
describe('#notifyHost', () => {
|
902
|
+
it('check locus status', async () => {
|
903
|
+
const siteFullUrl = 'ats062222cvg.webex.com';
|
904
|
+
const locusId = 'locusId';
|
905
|
+
const meetingUuid = 'meetingUuid';
|
906
|
+
const displayName = ['user1', 'user2'];
|
907
|
+
await meetingsRequest.notifyHost(siteFullUrl, locusId, meetingUuid, displayName);
|
908
|
+
assert.deepEqual(meetingsRequest.request.getCall(0).args[0], {
|
909
|
+
method: 'POST',
|
910
|
+
uri: `https://${siteFullUrl}/wbxappapi/v1/meetings/${meetingUuid}/notifyhost`,
|
911
|
+
body: {
|
912
|
+
displayName,
|
913
|
+
size: displayName?.length,
|
914
|
+
},
|
915
|
+
headers: {
|
916
|
+
locusId,
|
917
|
+
}
|
918
|
+
});
|
919
|
+
});
|
920
|
+
});
|
829
921
|
});
|
@@ -3,12 +3,14 @@ import sinon from 'sinon';
|
|
3
3
|
import {assert} from '@webex/test-helper-chai';
|
4
4
|
import Meetings from '@webex/plugin-meetings';
|
5
5
|
import MeetingUtil from '@webex/plugin-meetings/src/meeting/util';
|
6
|
-
import {LOCAL_SHARE_ERRORS} from '@webex/plugin-meetings/src/constants';
|
6
|
+
import {LOCAL_SHARE_ERRORS, PASSWORD_STATUS} from '@webex/plugin-meetings/src/constants';
|
7
7
|
import LoggerProxy from '@webex/plugin-meetings/src/common/logs/logger-proxy';
|
8
8
|
import LoggerConfig from '@webex/plugin-meetings/src/common/logs/logger-config';
|
9
9
|
import {SELF_POLICY, IP_VERSION} from '@webex/plugin-meetings/src/constants';
|
10
10
|
import MockWebex from '@webex/test-helper-mock-webex';
|
11
11
|
import * as BrowserDetectionModule from '@webex/plugin-meetings/src/common/browser-detection';
|
12
|
+
import PasswordError from '@webex/plugin-meetings/src/common/errors/password-error';
|
13
|
+
import CaptchaError from '@webex/plugin-meetings/src/common/errors/captcha-error';
|
12
14
|
|
13
15
|
describe('plugin-meetings', () => {
|
14
16
|
let webex;
|
@@ -57,6 +59,10 @@ describe('plugin-meetings', () => {
|
|
57
59
|
meeting.getWebexObject = sinon.stub().returns(webex);
|
58
60
|
meeting.simultaneousInterpretation = {cleanUp: sinon.stub()};
|
59
61
|
meeting.trigger = sinon.stub();
|
62
|
+
meeting.webex = webex;
|
63
|
+
meeting.webex.internal.newMetrics.callDiagnosticMetrics =
|
64
|
+
meeting.webex.internal.newMetrics.callDiagnosticMetrics || {};
|
65
|
+
meeting.webex.internal.newMetrics.callDiagnosticMetrics.clearEventLimitsForCorrelationId = sinon.stub();
|
60
66
|
});
|
61
67
|
|
62
68
|
afterEach(() => {
|
@@ -81,6 +87,10 @@ describe('plugin-meetings', () => {
|
|
81
87
|
assert.calledOnce(meeting.breakouts.cleanUp);
|
82
88
|
assert.calledOnce(meeting.simultaneousInterpretation.cleanUp);
|
83
89
|
assert.calledOnce(webex.internal.device.meetingEnded);
|
90
|
+
assert.calledOnceWithExactly(
|
91
|
+
meeting.webex.internal.newMetrics.callDiagnosticMetrics.clearEventLimitsForCorrelationId,
|
92
|
+
meeting.correlationId
|
93
|
+
);
|
84
94
|
});
|
85
95
|
|
86
96
|
it('do clean up on meeting object with LLM disabled', async () => {
|
@@ -98,6 +108,10 @@ describe('plugin-meetings', () => {
|
|
98
108
|
assert.calledOnce(meeting.breakouts.cleanUp);
|
99
109
|
assert.calledOnce(meeting.simultaneousInterpretation.cleanUp);
|
100
110
|
assert.calledOnce(webex.internal.device.meetingEnded);
|
111
|
+
assert.calledOnceWithExactly(
|
112
|
+
meeting.webex.internal.newMetrics.callDiagnosticMetrics.clearEventLimitsForCorrelationId,
|
113
|
+
meeting.correlationId
|
114
|
+
);
|
101
115
|
});
|
102
116
|
|
103
117
|
it('do clean up on meeting object with no config', async () => {
|
@@ -114,6 +128,10 @@ describe('plugin-meetings', () => {
|
|
114
128
|
assert.calledOnce(meeting.breakouts.cleanUp);
|
115
129
|
assert.calledOnce(meeting.simultaneousInterpretation.cleanUp);
|
116
130
|
assert.calledOnce(webex.internal.device.meetingEnded);
|
131
|
+
assert.calledOnceWithExactly(
|
132
|
+
meeting.webex.internal.newMetrics.callDiagnosticMetrics.clearEventLimitsForCorrelationId,
|
133
|
+
meeting.correlationId
|
134
|
+
);
|
117
135
|
});
|
118
136
|
});
|
119
137
|
|
@@ -210,11 +228,11 @@ describe('plugin-meetings', () => {
|
|
210
228
|
});
|
211
229
|
});
|
212
230
|
|
213
|
-
describe('
|
214
|
-
it('should call
|
231
|
+
describe('updateLocusFromApiResponse', () => {
|
232
|
+
it('should call handleLocusAPIResponse with the response body', () => {
|
215
233
|
const meeting = {
|
216
234
|
locusInfo: {
|
217
|
-
|
235
|
+
handleLocusAPIResponse: sinon.stub(),
|
218
236
|
},
|
219
237
|
};
|
220
238
|
|
@@ -224,16 +242,16 @@ describe('plugin-meetings', () => {
|
|
224
242
|
},
|
225
243
|
};
|
226
244
|
|
227
|
-
const response = MeetingUtil.
|
245
|
+
const response = MeetingUtil.updateLocusFromApiResponse(meeting, originalResponse);
|
228
246
|
|
229
247
|
assert.deepEqual(response, originalResponse);
|
230
|
-
assert.calledOnceWithExactly(meeting.locusInfo.
|
248
|
+
assert.calledOnceWithExactly(meeting.locusInfo.handleLocusAPIResponse, meeting, originalResponse.body);
|
231
249
|
});
|
232
250
|
|
233
251
|
it('should handle locus being missing from the response', () => {
|
234
252
|
const meeting = {
|
235
253
|
locusInfo: {
|
236
|
-
|
254
|
+
handleLocusAPIResponse: sinon.stub(),
|
237
255
|
},
|
238
256
|
};
|
239
257
|
|
@@ -241,10 +259,10 @@ describe('plugin-meetings', () => {
|
|
241
259
|
body: {},
|
242
260
|
};
|
243
261
|
|
244
|
-
const response = MeetingUtil.
|
262
|
+
const response = MeetingUtil.updateLocusFromApiResponse(meeting, originalResponse);
|
245
263
|
|
246
264
|
assert.deepEqual(response, originalResponse);
|
247
|
-
assert.notCalled(meeting.locusInfo.
|
265
|
+
assert.notCalled(meeting.locusInfo.handleLocusAPIResponse);
|
248
266
|
});
|
249
267
|
|
250
268
|
it('should work with an undefined meeting', () => {
|
@@ -254,14 +272,14 @@ describe('plugin-meetings', () => {
|
|
254
272
|
},
|
255
273
|
};
|
256
274
|
|
257
|
-
const response = MeetingUtil.
|
275
|
+
const response = MeetingUtil.updateLocusFromApiResponse(undefined, originalResponse);
|
258
276
|
assert.deepEqual(response, originalResponse);
|
259
277
|
});
|
260
278
|
});
|
261
279
|
|
262
280
|
describe('generateLocusDeltaRequest', () => {
|
263
281
|
it('generates the correct wrapper function', async () => {
|
264
|
-
const
|
282
|
+
const updateLocusFromApiResponseSpy = sinon.spy(MeetingUtil, 'updateLocusFromApiResponse');
|
265
283
|
const addSequenceSpy = sinon.spy(MeetingUtil, 'addSequence');
|
266
284
|
|
267
285
|
const meeting = {
|
@@ -278,16 +296,16 @@ describe('plugin-meetings', () => {
|
|
278
296
|
let result = await locusDeltaRequest(options);
|
279
297
|
|
280
298
|
assert.equal(result, 'result');
|
281
|
-
assert.calledOnceWithExactly(
|
299
|
+
assert.calledOnceWithExactly(updateLocusFromApiResponseSpy, meeting, 'result');
|
282
300
|
assert.calledOnceWithExactly(addSequenceSpy, meeting, options.body);
|
283
301
|
|
284
|
-
|
302
|
+
updateLocusFromApiResponseSpy.resetHistory();
|
285
303
|
addSequenceSpy.resetHistory();
|
286
304
|
|
287
305
|
// body missing from options
|
288
306
|
result = await locusDeltaRequest({});
|
289
307
|
assert.equal(result, 'result');
|
290
|
-
assert.calledOnceWithExactly(
|
308
|
+
assert.calledOnceWithExactly(updateLocusFromApiResponseSpy, meeting, 'result');
|
291
309
|
assert.calledOnceWithExactly(addSequenceSpy, meeting, options.body);
|
292
310
|
|
293
311
|
// meeting disappears so the WeakRef returns undefined
|
@@ -341,7 +359,11 @@ describe('plugin-meetings', () => {
|
|
341
359
|
});
|
342
360
|
|
343
361
|
describe('remoteUpdateAudioVideo', () => {
|
344
|
-
it('#Should call meetingRequest.locusMediaRequest with correct parameters', async () => {
|
362
|
+
it('#Should call meetingRequest.locusMediaRequest with correct parameters and return the full response', async () => {
|
363
|
+
const fakeResponse = {
|
364
|
+
body: { locus: { url: 'locusUrl'}},
|
365
|
+
headers: { },
|
366
|
+
};
|
345
367
|
const meeting = {
|
346
368
|
id: 'meeting-id',
|
347
369
|
mediaId: '12345',
|
@@ -350,13 +372,14 @@ describe('plugin-meetings', () => {
|
|
350
372
|
sequence: {},
|
351
373
|
},
|
352
374
|
locusMediaRequest: {
|
353
|
-
send: sinon.stub().resolves(
|
375
|
+
send: sinon.stub().resolves(fakeResponse),
|
354
376
|
},
|
355
377
|
getWebexObject: sinon.stub().returns(webex),
|
356
378
|
};
|
357
379
|
|
358
|
-
await MeetingUtil.remoteUpdateAudioVideo(meeting, true, false);
|
380
|
+
const result = await MeetingUtil.remoteUpdateAudioVideo(meeting, true, false);
|
359
381
|
|
382
|
+
assert.deepEqual(result, fakeResponse);
|
360
383
|
assert.calledOnceWithExactly(meeting.locusMediaRequest.send, {
|
361
384
|
mediaId: '12345',
|
362
385
|
muteOptions: {
|
@@ -622,6 +645,28 @@ describe('plugin-meetings', () => {
|
|
622
645
|
|
623
646
|
assert.equal(parameter.locusClusterUrl, 'locusClusterUrl');
|
624
647
|
});
|
648
|
+
|
649
|
+
it('should post client event with error when join fails', async () => {
|
650
|
+
const joinError = new Error('Join failed');
|
651
|
+
meeting.meetingRequest.joinMeeting.rejects(joinError);
|
652
|
+
meeting.meetingInfo = { meetingLookupUrl: 'test-lookup-url' };
|
653
|
+
|
654
|
+
try {
|
655
|
+
await MeetingUtil.joinMeeting(meeting, {});
|
656
|
+
assert.fail('Expected joinMeeting to throw an error');
|
657
|
+
} catch (error) {
|
658
|
+
assert.equal(error, joinError);
|
659
|
+
|
660
|
+
// Verify error client event was submitted
|
661
|
+
assert.calledWith(webex.internal.newMetrics.submitClientEvent, {
|
662
|
+
name: 'client.locus.join.response',
|
663
|
+
payload: {
|
664
|
+
identifiers: { meetingLookupUrl: 'test-lookup-url' },
|
665
|
+
},
|
666
|
+
options: { meetingId: meeting.id, rawError: joinError },
|
667
|
+
});
|
668
|
+
}
|
669
|
+
});
|
625
670
|
});
|
626
671
|
|
627
672
|
describe('joinMeetingOptions', () => {
|
@@ -661,6 +706,82 @@ describe('plugin-meetings', () => {
|
|
661
706
|
joinMeetingSpy.restore();
|
662
707
|
}
|
663
708
|
});
|
709
|
+
|
710
|
+
it('should submit client event and reject with PasswordError when password is required', async () => {
|
711
|
+
const meeting = {
|
712
|
+
id: 'meeting-id',
|
713
|
+
passwordStatus: PASSWORD_STATUS.REQUIRED,
|
714
|
+
resourceId: null,
|
715
|
+
requiredCaptcha: null,
|
716
|
+
getWebexObject: sinon.stub().returns(webex),
|
717
|
+
};
|
718
|
+
|
719
|
+
try {
|
720
|
+
await MeetingUtil.joinMeetingOptions(meeting, {});
|
721
|
+
assert.fail('Expected joinMeetingOptions to throw PasswordError');
|
722
|
+
} catch (error) {
|
723
|
+
assert.instanceOf(error, PasswordError);
|
724
|
+
|
725
|
+
// Verify client event was submitted with error details
|
726
|
+
assert.calledWith(webex.internal.newMetrics.submitClientEvent, {
|
727
|
+
name: 'client.meetinginfo.response',
|
728
|
+
options: {
|
729
|
+
meetingId: meeting.id,
|
730
|
+
},
|
731
|
+
payload: {
|
732
|
+
errors: [
|
733
|
+
{
|
734
|
+
fatal: false,
|
735
|
+
category: 'expected',
|
736
|
+
name: 'other',
|
737
|
+
shownToUser: false,
|
738
|
+
errorCode: error.code,
|
739
|
+
errorDescription: error.name,
|
740
|
+
rawErrorMessage: error.sdkMessage,
|
741
|
+
},
|
742
|
+
],
|
743
|
+
},
|
744
|
+
});
|
745
|
+
}
|
746
|
+
});
|
747
|
+
|
748
|
+
it('should submit client event and reject with CaptchaError when captcha is required', async () => {
|
749
|
+
const meeting = {
|
750
|
+
id: 'meeting-id',
|
751
|
+
passwordStatus: null,
|
752
|
+
resourceId: null,
|
753
|
+
requiredCaptcha: {captchaId: 'test-captcha'},
|
754
|
+
getWebexObject: sinon.stub().returns(webex),
|
755
|
+
};
|
756
|
+
|
757
|
+
try {
|
758
|
+
await MeetingUtil.joinMeetingOptions(meeting, {});
|
759
|
+
assert.fail('Expected joinMeetingOptions to throw CaptchaError');
|
760
|
+
} catch (error) {
|
761
|
+
assert.instanceOf(error, CaptchaError);
|
762
|
+
|
763
|
+
// Verify client event was submitted with error details
|
764
|
+
assert.calledWith(webex.internal.newMetrics.submitClientEvent, {
|
765
|
+
name: 'client.meetinginfo.response',
|
766
|
+
options: {
|
767
|
+
meetingId: meeting.id,
|
768
|
+
},
|
769
|
+
payload: {
|
770
|
+
errors: [
|
771
|
+
{
|
772
|
+
fatal: false,
|
773
|
+
category: 'expected',
|
774
|
+
name: 'other',
|
775
|
+
shownToUser: false,
|
776
|
+
errorCode: error.code,
|
777
|
+
errorDescription: error.name,
|
778
|
+
rawErrorMessage: error.sdkMessage,
|
779
|
+
},
|
780
|
+
],
|
781
|
+
},
|
782
|
+
});
|
783
|
+
}
|
784
|
+
});
|
664
785
|
});
|
665
786
|
|
666
787
|
describe('getUserDisplayHintsFromLocusInfo', () => {
|
@@ -850,7 +971,15 @@ describe('plugin-meetings', () => {
|
|
850
971
|
{functionName: 'isClosedCaptionActive', displayHint: 'CAPTION_STATUS_ACTIVE'},
|
851
972
|
{functionName: 'canStartManualCaption', displayHint: 'MANUAL_CAPTION_START'},
|
852
973
|
{functionName: 'canStopManualCaption', displayHint: 'MANUAL_CAPTION_STOP'},
|
974
|
+
|
975
|
+
{functionName: 'isLocalRecordingStarted',displayHint:'LOCAL_RECORDING_STATUS_STARTED'},
|
976
|
+
{functionName: 'isLocalRecordingStopped', displayHint: 'LOCAL_RECORDING_STATUS_STOPPED'},
|
977
|
+
{functionName: 'isLocalRecordingPaused', displayHint: 'LOCAL_RECORDING_STATUS_PAUSED'},
|
978
|
+
|
853
979
|
{functionName: 'isManualCaptionActive', displayHint: 'MANUAL_CAPTION_STATUS_ACTIVE'},
|
980
|
+
|
981
|
+
{functionName: 'isSpokenLanguageAutoDetectionEnabled', displayHint: 'SPOKEN_LANGUAGE_AUTO_DETECTION_ENABLED'},
|
982
|
+
|
854
983
|
{functionName: 'isWebexAssistantActive', displayHint: 'WEBEX_ASSISTANT_STATUS_ACTIVE'},
|
855
984
|
{functionName: 'canViewCaptionPanel', displayHint: 'ENABLE_CAPTION_PANEL'},
|
856
985
|
{functionName: 'isRealTimeTranslationEnabled', displayHint: 'DISPLAY_REAL_TIME_TRANSLATION'},
|
@@ -1189,6 +1318,27 @@ describe('plugin-meetings', () => {
|
|
1189
1318
|
});
|
1190
1319
|
});
|
1191
1320
|
|
1321
|
+
describe('getCaEventLabelsForIpVersion', () => {
|
1322
|
+
[
|
1323
|
+
{ipver: IP_VERSION.unknown, expectedLabels: undefined},
|
1324
|
+
{ipver: IP_VERSION.only_ipv4, expectedLabels: ['hasIpv4_true']},
|
1325
|
+
{ipver: IP_VERSION.only_ipv6, expectedLabels: ['hasIpv6_true']},
|
1326
|
+
{
|
1327
|
+
ipver: IP_VERSION.ipv4_and_ipv6,
|
1328
|
+
expectedLabels: ['hasIpv4_true', 'hasIpv6_true'],
|
1329
|
+
},
|
1330
|
+
].forEach(({ipver, expectedLabels}) => {
|
1331
|
+
it(`returns expected labels when ipver=${ipver}`, () => {
|
1332
|
+
sinon.stub(MeetingUtil, 'getIpVersion').returns(ipver);
|
1333
|
+
|
1334
|
+
const result = MeetingUtil.getCaEventLabelsForIpVersion(webex);
|
1335
|
+
|
1336
|
+
assert.calledOnceWithExactly(MeetingUtil.getIpVersion, webex);
|
1337
|
+
assert.deepEqual(result, expectedLabels);
|
1338
|
+
});
|
1339
|
+
});
|
1340
|
+
});
|
1341
|
+
|
1192
1342
|
describe('getChangeMeetingFloorErrorPayload', () => {
|
1193
1343
|
[
|
1194
1344
|
{
|
@@ -218,6 +218,7 @@ describe('plugin-meetings', () => {
|
|
218
218
|
invitees: invitee,
|
219
219
|
installedOrgID: undefined,
|
220
220
|
schedule: true,
|
221
|
+
classificationId: undefined,
|
221
222
|
},
|
222
223
|
});
|
223
224
|
|
@@ -652,7 +653,8 @@ describe('plugin-meetings', () => {
|
|
652
653
|
assert.calledOnceWithExactly(
|
653
654
|
meetingInfo.createAdhocSpaceMeeting,
|
654
655
|
'conversationUrl',
|
655
|
-
installedOrgID
|
656
|
+
installedOrgID,
|
657
|
+
null,
|
656
658
|
);
|
657
659
|
assert.notCalled(webex.request);
|
658
660
|
meetingInfo.createAdhocSpaceMeeting.restore();
|
@@ -1148,6 +1150,7 @@ describe('plugin-meetings', () => {
|
|
1148
1150
|
describe('createAdhocSpaceMeeting', () => {
|
1149
1151
|
const conversationUrl = 'https://conversationUrl/xxx';
|
1150
1152
|
const installedOrgID = '12345';
|
1153
|
+
const classificationId = '123456';
|
1151
1154
|
|
1152
1155
|
const setup = () => {
|
1153
1156
|
const invitee = [];
|
@@ -1173,7 +1176,7 @@ describe('plugin-meetings', () => {
|
|
1173
1176
|
body: conversation,
|
1174
1177
|
});
|
1175
1178
|
|
1176
|
-
const result = await meetingInfo.createAdhocSpaceMeeting(conversationUrl, installedOrgID);
|
1179
|
+
const result = await meetingInfo.createAdhocSpaceMeeting(conversationUrl, installedOrgID, classificationId);
|
1177
1180
|
|
1178
1181
|
assert.calledWith(webex.request, {
|
1179
1182
|
uri: conversationUrl,
|
@@ -1192,6 +1195,7 @@ describe('plugin-meetings', () => {
|
|
1192
1195
|
invitees: invitee,
|
1193
1196
|
installedOrgID: installedOrgID,
|
1194
1197
|
schedule: false,
|
1198
|
+
classificationId,
|
1195
1199
|
},
|
1196
1200
|
});
|
1197
1201
|
assert.calledOnce(Metrics.sendBehavioralMetric);
|
@@ -1206,7 +1210,7 @@ describe('plugin-meetings', () => {
|
|
1206
1210
|
webex.request = sinon.stub().resolves({
|
1207
1211
|
body: conversation,
|
1208
1212
|
});
|
1209
|
-
await meetingInfo.createAdhocSpaceMeeting(conversationUrl, installedOrgID);
|
1213
|
+
await meetingInfo.createAdhocSpaceMeeting(conversationUrl, installedOrgID, classificationId);
|
1210
1214
|
|
1211
1215
|
assert.calledWith(webex.request, {
|
1212
1216
|
uri: conversationUrl,
|
@@ -1224,6 +1228,7 @@ describe('plugin-meetings', () => {
|
|
1224
1228
|
invitees: invitee,
|
1225
1229
|
installedOrgID,
|
1226
1230
|
schedule: false,
|
1231
|
+
classificationId,
|
1227
1232
|
},
|
1228
1233
|
});
|
1229
1234
|
assert(Metrics.sendBehavioralMetric.calledOnce);
|