@webex/plugin-meetings 3.7.0-next.3 → 3.7.0-next.31
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/common/errors/{webinar-registration-error.js → join-webinar-error.js} +12 -12
- package/dist/common/errors/join-webinar-error.js.map +1 -0
- package/dist/config.js +1 -1
- package/dist/config.js.map +1 -1
- package/dist/constants.js +31 -6
- package/dist/constants.js.map +1 -1
- package/dist/index.js +8 -15
- package/dist/index.js.map +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/index.js +13 -2
- package/dist/locus-info/index.js.map +1 -1
- package/dist/locus-info/selfUtils.js +30 -17
- package/dist/locus-info/selfUtils.js.map +1 -1
- package/dist/meeting/in-meeting-actions.js +11 -1
- package/dist/meeting/in-meeting-actions.js.map +1 -1
- package/dist/meeting/index.js +810 -779
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/request.js +30 -0
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/request.type.js.map +1 -1
- package/dist/meeting/util.js +3 -8
- package/dist/meeting/util.js.map +1 -1
- package/dist/meeting-info/meeting-info-v2.js +29 -17
- package/dist/meeting-info/meeting-info-v2.js.map +1 -1
- package/dist/meetings/index.js +6 -3
- package/dist/meetings/index.js.map +1 -1
- package/dist/member/index.js +9 -0
- package/dist/member/index.js.map +1 -1
- package/dist/member/types.js.map +1 -1
- package/dist/member/util.js +39 -28
- package/dist/member/util.js.map +1 -1
- package/dist/members/util.js +4 -2
- package/dist/members/util.js.map +1 -1
- package/dist/metrics/constants.js +1 -1
- package/dist/metrics/constants.js.map +1 -1
- package/dist/multistream/remoteMedia.js +30 -15
- package/dist/multistream/remoteMedia.js.map +1 -1
- package/dist/multistream/sendSlotManager.js +24 -0
- package/dist/multistream/sendSlotManager.js.map +1 -1
- package/dist/reachability/clusterReachability.js +12 -11
- package/dist/reachability/clusterReachability.js.map +1 -1
- package/dist/recording-controller/enums.js +8 -4
- package/dist/recording-controller/enums.js.map +1 -1
- package/dist/recording-controller/index.js +18 -9
- package/dist/recording-controller/index.js.map +1 -1
- package/dist/recording-controller/util.js +13 -9
- package/dist/recording-controller/util.js.map +1 -1
- package/dist/types/common/errors/{webinar-registration-error.d.ts → join-webinar-error.d.ts} +2 -2
- package/dist/types/constants.d.ts +23 -1
- package/dist/types/index.d.ts +3 -3
- package/dist/types/locus-info/index.d.ts +2 -1
- package/dist/types/meeting/in-meeting-actions.d.ts +10 -0
- package/dist/types/meeting/index.d.ts +9 -10
- package/dist/types/meeting/request.d.ts +12 -1
- package/dist/types/meeting/request.type.d.ts +6 -0
- package/dist/types/meeting/util.d.ts +1 -1
- package/dist/types/meeting-info/meeting-info-v2.d.ts +4 -4
- package/dist/types/meetings/index.d.ts +3 -0
- package/dist/types/member/index.d.ts +1 -0
- package/dist/types/member/types.d.ts +7 -0
- package/dist/types/members/util.d.ts +2 -0
- package/dist/types/metrics/constants.d.ts +1 -1
- package/dist/types/multistream/sendSlotManager.d.ts +8 -1
- package/dist/types/recording-controller/enums.d.ts +5 -2
- package/dist/types/recording-controller/index.d.ts +1 -0
- package/dist/types/recording-controller/util.d.ts +2 -1
- package/dist/webinar/index.js +390 -7
- package/dist/webinar/index.js.map +1 -1
- package/package.json +23 -22
- package/src/common/errors/join-webinar-error.ts +24 -0
- package/src/config.ts +1 -1
- package/src/constants.ts +28 -3
- package/src/index.ts +2 -3
- package/src/locus-info/index.ts +17 -2
- package/src/locus-info/selfUtils.ts +19 -6
- package/src/meeting/in-meeting-actions.ts +21 -0
- package/src/meeting/index.ts +147 -54
- package/src/meeting/request.ts +26 -1
- package/src/meeting/request.type.ts +7 -0
- package/src/meeting/util.ts +3 -9
- package/src/meeting-info/meeting-info-v2.ts +23 -11
- package/src/meetings/index.ts +8 -2
- package/src/member/index.ts +9 -0
- package/src/member/types.ts +8 -0
- package/src/member/util.ts +34 -24
- package/src/members/util.ts +1 -0
- package/src/metrics/constants.ts +1 -1
- package/src/multistream/remoteMedia.ts +28 -15
- package/src/multistream/sendSlotManager.ts +31 -0
- package/src/reachability/clusterReachability.ts +4 -1
- package/src/recording-controller/enums.ts +5 -2
- package/src/recording-controller/index.ts +17 -4
- package/src/recording-controller/util.ts +20 -5
- package/src/webinar/index.ts +235 -9
- package/test/unit/spec/locus-info/index.js +222 -0
- package/test/unit/spec/locus-info/selfConstant.js +7 -0
- package/test/unit/spec/locus-info/selfUtils.js +91 -1
- package/test/unit/spec/meeting/in-meeting-actions.ts +13 -1
- package/test/unit/spec/meeting/index.js +318 -81
- package/test/unit/spec/meeting/utils.js +11 -19
- package/test/unit/spec/meeting-info/meetinginfov2.js +9 -4
- package/test/unit/spec/meetings/index.js +9 -5
- package/test/unit/spec/member/util.js +52 -11
- package/test/unit/spec/members/utils.js +95 -0
- package/test/unit/spec/multistream/remoteMedia.ts +11 -7
- package/test/unit/spec/reachability/clusterReachability.ts +7 -0
- package/test/unit/spec/recording-controller/index.js +61 -5
- package/test/unit/spec/recording-controller/util.js +39 -3
- package/test/unit/spec/webinar/index.ts +504 -0
- package/dist/common/errors/webinar-registration-error.js.map +0 -1
- package/src/common/errors/webinar-registration-error.ts +0 -27
|
@@ -91,14 +91,14 @@ import ParameterError from '../../../../src/common/errors/parameter';
|
|
|
91
91
|
import PasswordError from '../../../../src/common/errors/password-error';
|
|
92
92
|
import CaptchaError from '../../../../src/common/errors/captcha-error';
|
|
93
93
|
import PermissionError from '../../../../src/common/errors/permission';
|
|
94
|
-
import
|
|
94
|
+
import JoinWebinarError from '../../../../src/common/errors/join-webinar-error';
|
|
95
95
|
import IntentToJoinError from '../../../../src/common/errors/intent-to-join';
|
|
96
96
|
import testUtils from '../../../utils/testUtils';
|
|
97
97
|
import {
|
|
98
98
|
MeetingInfoV2CaptchaError,
|
|
99
99
|
MeetingInfoV2PasswordError,
|
|
100
100
|
MeetingInfoV2PolicyError,
|
|
101
|
-
|
|
101
|
+
MeetingInfoV2JoinWebinarError,
|
|
102
102
|
} from '../../../../src/meeting-info/meeting-info-v2';
|
|
103
103
|
import {
|
|
104
104
|
DTLS_HANDSHAKE_FAILED_CLIENT_CODE,
|
|
@@ -1705,6 +1705,12 @@ describe('plugin-meetings', () => {
|
|
|
1705
1705
|
sinon.assert.called(setCorrelationIdSpy);
|
|
1706
1706
|
assert.equal(meeting.correlationId, '123');
|
|
1707
1707
|
});
|
|
1708
|
+
|
|
1709
|
+
it('should not send client.call.initiated if told not to', async () => {
|
|
1710
|
+
await meeting.join({sendCallInitiated: false});
|
|
1711
|
+
|
|
1712
|
+
sinon.assert.notCalled(webex.internal.newMetrics.submitClientEvent);
|
|
1713
|
+
});
|
|
1708
1714
|
});
|
|
1709
1715
|
|
|
1710
1716
|
describe('failure', () => {
|
|
@@ -2492,9 +2498,11 @@ describe('plugin-meetings', () => {
|
|
|
2492
2498
|
mediaSettings: {},
|
|
2493
2499
|
});
|
|
2494
2500
|
|
|
2495
|
-
const checkLogCounter = (
|
|
2501
|
+
const checkLogCounter = (delayInMinutes, expectedCounter) => {
|
|
2502
|
+
const delayInMilliseconds = delayInMinutes * 60 * 1000;
|
|
2503
|
+
|
|
2496
2504
|
// first check that the counter is not increased just before the delay
|
|
2497
|
-
clock.tick(
|
|
2505
|
+
clock.tick(delayInMilliseconds - 50);
|
|
2498
2506
|
assert.equal(logUploadCounter, expectedCounter - 1);
|
|
2499
2507
|
|
|
2500
2508
|
// and now check that it has reached expected value after the delay
|
|
@@ -2502,22 +2510,18 @@ describe('plugin-meetings', () => {
|
|
|
2502
2510
|
assert.equal(logUploadCounter, expectedCounter);
|
|
2503
2511
|
};
|
|
2504
2512
|
|
|
2505
|
-
checkLogCounter(
|
|
2506
|
-
checkLogCounter(
|
|
2507
|
-
checkLogCounter(
|
|
2508
|
-
checkLogCounter(
|
|
2509
|
-
checkLogCounter(
|
|
2510
|
-
checkLogCounter(30000, 6);
|
|
2511
|
-
checkLogCounter(30000, 7);
|
|
2512
|
-
checkLogCounter(60000, 8);
|
|
2513
|
-
checkLogCounter(60000, 9);
|
|
2514
|
-
checkLogCounter(60000, 10);
|
|
2513
|
+
checkLogCounter(0.1, 1);
|
|
2514
|
+
checkLogCounter(15, 2);
|
|
2515
|
+
checkLogCounter(30, 3);
|
|
2516
|
+
checkLogCounter(60, 4);
|
|
2517
|
+
checkLogCounter(60, 5);
|
|
2515
2518
|
|
|
2516
|
-
// simulate media connection being removed ->
|
|
2519
|
+
// simulate media connection being removed -> 1 more upload should happen, but nothing more afterwards
|
|
2517
2520
|
meeting.mediaProperties.webrtcMediaConnection = undefined;
|
|
2521
|
+
checkLogCounter(60, 6);
|
|
2518
2522
|
|
|
2519
|
-
clock.tick(
|
|
2520
|
-
assert.equal(logUploadCounter,
|
|
2523
|
+
clock.tick(120 * 1000 * 60);
|
|
2524
|
+
assert.equal(logUploadCounter, 6);
|
|
2521
2525
|
|
|
2522
2526
|
clock.restore();
|
|
2523
2527
|
});
|
|
@@ -3475,6 +3479,51 @@ describe('plugin-meetings', () => {
|
|
|
3475
3479
|
});
|
|
3476
3480
|
});
|
|
3477
3481
|
|
|
3482
|
+
it('counts the number of members that are in the meeting for MEDIA_QUALITY event', async () => {
|
|
3483
|
+
let fakeMembersCollection = {
|
|
3484
|
+
members: {
|
|
3485
|
+
member1: { isInMeeting: true },
|
|
3486
|
+
member2: { isInMeeting: true },
|
|
3487
|
+
member3: { isInMeeting: false },
|
|
3488
|
+
},
|
|
3489
|
+
};
|
|
3490
|
+
sinon.stub(meeting, 'getMembers').returns({ membersCollection: fakeMembersCollection });
|
|
3491
|
+
const fakeData = { intervalMetadata: {}, networkType: 'wifi' };
|
|
3492
|
+
|
|
3493
|
+
statsAnalyzerStub.emit(
|
|
3494
|
+
{ file: 'test', function: 'test' },
|
|
3495
|
+
StatsAnalyzerEventNames.MEDIA_QUALITY,
|
|
3496
|
+
{ data: fakeData }
|
|
3497
|
+
);
|
|
3498
|
+
|
|
3499
|
+
assert.calledWithMatch(webex.internal.newMetrics.submitMQE, {
|
|
3500
|
+
name: 'client.mediaquality.event',
|
|
3501
|
+
options: {
|
|
3502
|
+
meetingId: meeting.id,
|
|
3503
|
+
},
|
|
3504
|
+
payload: {
|
|
3505
|
+
intervals: [sinon.match.has('intervalMetadata', sinon.match.has('meetingUserCount', 2))],
|
|
3506
|
+
},
|
|
3507
|
+
});
|
|
3508
|
+
fakeMembersCollection.members.member2.isInMeeting = false;
|
|
3509
|
+
|
|
3510
|
+
statsAnalyzerStub.emit(
|
|
3511
|
+
{ file: 'test', function: 'test' },
|
|
3512
|
+
StatsAnalyzerEventNames.MEDIA_QUALITY,
|
|
3513
|
+
{ data: fakeData }
|
|
3514
|
+
);
|
|
3515
|
+
|
|
3516
|
+
assert.calledWithMatch(webex.internal.newMetrics.submitMQE, {
|
|
3517
|
+
name: 'client.mediaquality.event',
|
|
3518
|
+
options: {
|
|
3519
|
+
meetingId: meeting.id,
|
|
3520
|
+
},
|
|
3521
|
+
payload: {
|
|
3522
|
+
intervals: [sinon.match.has('intervalMetadata', sinon.match.has('meetingUserCount', 1))],
|
|
3523
|
+
},
|
|
3524
|
+
});
|
|
3525
|
+
});
|
|
3526
|
+
|
|
3478
3527
|
it('calls submitMQE correctly', async () => {
|
|
3479
3528
|
const fakeData = {intervalMetadata: {bla: 'bla'}, networkType: 'wifi'};
|
|
3480
3529
|
|
|
@@ -3552,14 +3601,6 @@ describe('plugin-meetings', () => {
|
|
|
3552
3601
|
});
|
|
3553
3602
|
});
|
|
3554
3603
|
|
|
3555
|
-
it('succeeds even if getDevices() throws', async () => {
|
|
3556
|
-
meeting.meetingState = 'ACTIVE';
|
|
3557
|
-
|
|
3558
|
-
sinon.stub(InternalMediaCoreModule, 'getDevices').rejects(new Error('fake error'));
|
|
3559
|
-
|
|
3560
|
-
await meeting.addMedia();
|
|
3561
|
-
});
|
|
3562
|
-
|
|
3563
3604
|
describe('CA ice failures checks', () => {
|
|
3564
3605
|
[
|
|
3565
3606
|
{
|
|
@@ -3701,6 +3742,93 @@ describe('plugin-meetings', () => {
|
|
|
3701
3742
|
});
|
|
3702
3743
|
});
|
|
3703
3744
|
|
|
3745
|
+
describe(`#beRightBack`, () => {
|
|
3746
|
+
const fakeMultistreamRoapMediaConnection = {
|
|
3747
|
+
createSendSlot: sinon.stub().returns({
|
|
3748
|
+
setSourceStateOverride: sinon.stub().resolves(),
|
|
3749
|
+
clearSourceStateOverride: sinon.stub().resolves(),
|
|
3750
|
+
}),
|
|
3751
|
+
};
|
|
3752
|
+
|
|
3753
|
+
beforeEach(() => {
|
|
3754
|
+
meeting.meetingRequest.setBrb = sinon.stub().resolves({body: 'test'});
|
|
3755
|
+
meeting.mediaProperties.webrtcMediaConnection = {createSendSlot: sinon.stub()};
|
|
3756
|
+
meeting.sendSlotManager.createSlot(
|
|
3757
|
+
fakeMultistreamRoapMediaConnection,
|
|
3758
|
+
MediaType.VideoMain
|
|
3759
|
+
);
|
|
3760
|
+
|
|
3761
|
+
meeting.locusUrl = 'locus url';
|
|
3762
|
+
meeting.deviceUrl = 'device url';
|
|
3763
|
+
meeting.selfId = 'self id';
|
|
3764
|
+
});
|
|
3765
|
+
|
|
3766
|
+
afterEach(() => {
|
|
3767
|
+
sinon.restore();
|
|
3768
|
+
});
|
|
3769
|
+
|
|
3770
|
+
it('should have #beRightBack', () => {
|
|
3771
|
+
assert.exists(meeting.beRightBack);
|
|
3772
|
+
});
|
|
3773
|
+
|
|
3774
|
+
describe('when in a multistream meeting', () => {
|
|
3775
|
+
|
|
3776
|
+
beforeEach(() => {
|
|
3777
|
+
meeting.isMultistream = true;
|
|
3778
|
+
});
|
|
3779
|
+
|
|
3780
|
+
it('should enable #beRightBack and return a promise', async () => {
|
|
3781
|
+
const brbResult = meeting.beRightBack(true);
|
|
3782
|
+
|
|
3783
|
+
await brbResult;
|
|
3784
|
+
assert.exists(brbResult.then);
|
|
3785
|
+
assert.calledOnce(meeting.meetingRequest.setBrb);
|
|
3786
|
+
})
|
|
3787
|
+
|
|
3788
|
+
it('should disable #beRightBack and return a promise', async () => {
|
|
3789
|
+
const brbResult = meeting.beRightBack(false);
|
|
3790
|
+
|
|
3791
|
+
await brbResult;
|
|
3792
|
+
assert.exists(brbResult.then);
|
|
3793
|
+
assert.calledOnce(meeting.meetingRequest.setBrb);
|
|
3794
|
+
})
|
|
3795
|
+
|
|
3796
|
+
it('should throw an error and reject the promise if setBrb fails', async () => {
|
|
3797
|
+
const error = new Error('setBrb failed');
|
|
3798
|
+
meeting.meetingRequest.setBrb.rejects(error);
|
|
3799
|
+
|
|
3800
|
+
try {
|
|
3801
|
+
await meeting.beRightBack(true);
|
|
3802
|
+
} catch (err) {
|
|
3803
|
+
assert.instanceOf(err, Error);
|
|
3804
|
+
assert.equal(err.message, 'setBrb failed');
|
|
3805
|
+
assert.isRejected((Promise.reject()));
|
|
3806
|
+
}
|
|
3807
|
+
})
|
|
3808
|
+
});
|
|
3809
|
+
|
|
3810
|
+
describe('when in a transcoded meeting', () => {
|
|
3811
|
+
|
|
3812
|
+
beforeEach(() => {
|
|
3813
|
+
meeting.isMultistream = false;
|
|
3814
|
+
});
|
|
3815
|
+
|
|
3816
|
+
it('should ignore enabling #beRightBack', async () => {
|
|
3817
|
+
meeting.beRightBack(true);
|
|
3818
|
+
|
|
3819
|
+
assert.isRejected((Promise.reject()));
|
|
3820
|
+
assert.notCalled(meeting.meetingRequest.setBrb);
|
|
3821
|
+
})
|
|
3822
|
+
|
|
3823
|
+
it('should ignore disabling #beRightBack', async () => {
|
|
3824
|
+
meeting.beRightBack(false);
|
|
3825
|
+
|
|
3826
|
+
assert.isRejected((Promise.reject()));
|
|
3827
|
+
assert.notCalled(meeting.meetingRequest.setBrb);
|
|
3828
|
+
})
|
|
3829
|
+
});
|
|
3830
|
+
});
|
|
3831
|
+
|
|
3704
3832
|
/* This set of tests are like semi-integration tests, they use real MuteState, Media, LocusMediaRequest and Roap classes.
|
|
3705
3833
|
They mock the @webex/internal-media-core and sending of /media http requests to Locus.
|
|
3706
3834
|
Their main purpose is to test that we send the right http requests to Locus and make right calls
|
|
@@ -3743,8 +3871,12 @@ describe('plugin-meetings', () => {
|
|
|
3743
3871
|
meeting.setMercuryListener = sinon.stub();
|
|
3744
3872
|
meeting.locusInfo.onFullLocus = sinon.stub();
|
|
3745
3873
|
meeting.webex.meetings.geoHintInfo = {regionCode: 'EU', countryCode: 'UK'};
|
|
3746
|
-
meeting.webex.meetings.reachability.getReachabilityReportToAttachToRoap = sinon
|
|
3747
|
-
|
|
3874
|
+
meeting.webex.meetings.reachability.getReachabilityReportToAttachToRoap = sinon
|
|
3875
|
+
.stub()
|
|
3876
|
+
.resolves({id: 'fake reachability'});
|
|
3877
|
+
meeting.webex.meetings.reachability.getClientMediaPreferences = sinon
|
|
3878
|
+
.stub()
|
|
3879
|
+
.resolves({id: 'fake clientMediaPreferences'});
|
|
3748
3880
|
meeting.roap.doTurnDiscovery = sinon.stub().resolves({
|
|
3749
3881
|
turnServerInfo: {
|
|
3750
3882
|
url: 'turns:turn-server-url:443?transport=tcp',
|
|
@@ -3930,8 +4062,14 @@ describe('plugin-meetings', () => {
|
|
|
3930
4062
|
const checkSdpOfferSent = ({audioMuted, videoMuted}) => {
|
|
3931
4063
|
const {sdp, seq, tieBreaker} = roapOfferMessage;
|
|
3932
4064
|
|
|
3933
|
-
assert.calledWith(
|
|
3934
|
-
|
|
4065
|
+
assert.calledWith(
|
|
4066
|
+
meeting.webex.meetings.reachability.getClientMediaPreferences,
|
|
4067
|
+
meeting.isMultistream,
|
|
4068
|
+
0
|
|
4069
|
+
);
|
|
4070
|
+
assert.calledWith(
|
|
4071
|
+
meeting.webex.meetings.reachability.getReachabilityReportToAttachToRoap
|
|
4072
|
+
);
|
|
3935
4073
|
|
|
3936
4074
|
assert.calledWith(locusMediaRequestStub, {
|
|
3937
4075
|
method: 'PUT',
|
|
@@ -4176,7 +4314,6 @@ describe('plugin-meetings', () => {
|
|
|
4176
4314
|
});
|
|
4177
4315
|
|
|
4178
4316
|
it('addMedia() works correctly when media is enabled with streams to publish', async () => {
|
|
4179
|
-
const handleDeviceLoggingSpy = sinon.spy(Meeting, 'handleDeviceLogging');
|
|
4180
4317
|
await meeting.addMedia({localStreams: {microphone: fakeMicrophoneStream}});
|
|
4181
4318
|
await simulateRoapOffer();
|
|
4182
4319
|
await simulateRoapOk();
|
|
@@ -4207,12 +4344,9 @@ describe('plugin-meetings', () => {
|
|
|
4207
4344
|
|
|
4208
4345
|
// and that these were the only /media requests that were sent
|
|
4209
4346
|
assert.calledTwice(locusMediaRequestStub);
|
|
4210
|
-
|
|
4211
|
-
assert.calledOnce(handleDeviceLoggingSpy);
|
|
4212
4347
|
});
|
|
4213
4348
|
|
|
4214
4349
|
it('addMedia() works correctly when media is enabled with streams to publish and stream is user muted', async () => {
|
|
4215
|
-
const handleDeviceLoggingSpy = sinon.spy(Meeting, 'handleDeviceLogging');
|
|
4216
4350
|
fakeMicrophoneStream.userMuted = true;
|
|
4217
4351
|
|
|
4218
4352
|
await meeting.addMedia({localStreams: {microphone: fakeMicrophoneStream}});
|
|
@@ -4244,7 +4378,6 @@ describe('plugin-meetings', () => {
|
|
|
4244
4378
|
|
|
4245
4379
|
// and that these were the only /media requests that were sent
|
|
4246
4380
|
assert.calledTwice(locusMediaRequestStub);
|
|
4247
|
-
assert.calledOnce(handleDeviceLoggingSpy);
|
|
4248
4381
|
});
|
|
4249
4382
|
|
|
4250
4383
|
it('addMedia() works correctly when media is enabled with tracks to publish and track is ended', async () => {
|
|
@@ -4316,7 +4449,6 @@ describe('plugin-meetings', () => {
|
|
|
4316
4449
|
});
|
|
4317
4450
|
|
|
4318
4451
|
it('addMedia() works correctly when media is disabled with streams to publish', async () => {
|
|
4319
|
-
const handleDeviceLoggingSpy = sinon.spy(Meeting, 'handleDeviceLogging');
|
|
4320
4452
|
await meeting.addMedia({
|
|
4321
4453
|
localStreams: {microphone: fakeMicrophoneStream},
|
|
4322
4454
|
audioEnabled: false,
|
|
@@ -4350,20 +4482,6 @@ describe('plugin-meetings', () => {
|
|
|
4350
4482
|
|
|
4351
4483
|
// and that these were the only /media requests that were sent
|
|
4352
4484
|
assert.calledTwice(locusMediaRequestStub);
|
|
4353
|
-
assert.calledOnce(handleDeviceLoggingSpy);
|
|
4354
|
-
});
|
|
4355
|
-
|
|
4356
|
-
it('handleDeviceLogging not called when media is disabled', async () => {
|
|
4357
|
-
const handleDeviceLoggingSpy = sinon.spy(Meeting, 'handleDeviceLogging');
|
|
4358
|
-
await meeting.addMedia({
|
|
4359
|
-
localStreams: {microphone: fakeMicrophoneStream},
|
|
4360
|
-
audioEnabled: false,
|
|
4361
|
-
videoEnabled: false,
|
|
4362
|
-
});
|
|
4363
|
-
await simulateRoapOffer();
|
|
4364
|
-
await simulateRoapOk();
|
|
4365
|
-
|
|
4366
|
-
assert.notCalled(handleDeviceLoggingSpy);
|
|
4367
4485
|
});
|
|
4368
4486
|
|
|
4369
4487
|
it('addMedia() works correctly when media is disabled with no streams to publish', async () => {
|
|
@@ -4399,20 +4517,6 @@ describe('plugin-meetings', () => {
|
|
|
4399
4517
|
assert.calledTwice(locusMediaRequestStub);
|
|
4400
4518
|
});
|
|
4401
4519
|
|
|
4402
|
-
it('addMedia() works correctly when media is disabled with no streams to publish', async () => {
|
|
4403
|
-
const handleDeviceLoggingSpy = sinon.spy(Meeting, 'handleDeviceLogging');
|
|
4404
|
-
await meeting.addMedia({audioEnabled: false});
|
|
4405
|
-
//calling handleDeviceLogging with audioEnaled as true adn videoEnabled as false
|
|
4406
|
-
assert.calledWith(handleDeviceLoggingSpy, false, true);
|
|
4407
|
-
});
|
|
4408
|
-
|
|
4409
|
-
it('addMedia() works correctly when video is disabled with no streams to publish', async () => {
|
|
4410
|
-
const handleDeviceLoggingSpy = sinon.spy(Meeting, 'handleDeviceLogging');
|
|
4411
|
-
await meeting.addMedia({videoEnabled: false});
|
|
4412
|
-
//calling handleDeviceLogging audioEnabled as true videoEnabled as false
|
|
4413
|
-
assert.calledWith(handleDeviceLoggingSpy, true, false);
|
|
4414
|
-
});
|
|
4415
|
-
|
|
4416
4520
|
it('addMedia() works correctly when video is disabled with no streams to publish', async () => {
|
|
4417
4521
|
await meeting.addMedia({videoEnabled: false});
|
|
4418
4522
|
await simulateRoapOffer();
|
|
@@ -4479,13 +4583,6 @@ describe('plugin-meetings', () => {
|
|
|
4479
4583
|
assert.calledTwice(locusMediaRequestStub);
|
|
4480
4584
|
});
|
|
4481
4585
|
|
|
4482
|
-
it('addMedia() works correctly when both shareAudio and shareVideo is disabled with no streams publish', async () => {
|
|
4483
|
-
const handleDeviceLoggingSpy = sinon.spy(Meeting, 'handleDeviceLogging');
|
|
4484
|
-
await meeting.addMedia({shareAudioEnabled: false, shareVideoEnabled: false});
|
|
4485
|
-
//calling handleDeviceLogging with audioEnabled true and videoEnabled as true
|
|
4486
|
-
assert.calledWith(handleDeviceLoggingSpy, true, true);
|
|
4487
|
-
});
|
|
4488
|
-
|
|
4489
4586
|
describe('publishStreams()/unpublishStreams() calls', () => {
|
|
4490
4587
|
[
|
|
4491
4588
|
{mediaEnabled: true, expected: {direction: 'sendrecv', localMuteSentValue: false}},
|
|
@@ -6332,29 +6429,74 @@ describe('plugin-meetings', () => {
|
|
|
6332
6429
|
assert.equal(meeting.fetchMeetingInfoTimeoutId, undefined);
|
|
6333
6430
|
});
|
|
6334
6431
|
|
|
6335
|
-
it('handles
|
|
6432
|
+
it('handles MeetingInfoV2JoinWebinarError webinar need registration', async () => {
|
|
6336
6433
|
meeting.destination = FAKE_DESTINATION;
|
|
6337
6434
|
meeting.destinationType = FAKE_TYPE;
|
|
6338
6435
|
meeting.attrs.meetingInfoProvider = {
|
|
6339
6436
|
fetchMeetingInfo: sinon
|
|
6340
6437
|
.stub()
|
|
6341
6438
|
.throws(
|
|
6342
|
-
new
|
|
6439
|
+
new MeetingInfoV2JoinWebinarError(403021, FAKE_MEETING_INFO, 'a message')
|
|
6343
6440
|
),
|
|
6344
6441
|
};
|
|
6345
6442
|
|
|
6346
6443
|
await assert.isRejected(
|
|
6347
6444
|
meeting.fetchMeetingInfo({sendCAevents: true}),
|
|
6348
|
-
|
|
6445
|
+
JoinWebinarError
|
|
6349
6446
|
);
|
|
6350
6447
|
|
|
6351
6448
|
assert.deepEqual(meeting.meetingInfo, FAKE_MEETING_INFO);
|
|
6352
|
-
assert.equal(meeting.meetingInfoFailureCode, 403021);
|
|
6353
6449
|
assert.equal(
|
|
6354
6450
|
meeting.meetingInfoFailureReason,
|
|
6355
6451
|
MEETING_INFO_FAILURE_REASON.WEBINAR_REGISTRATION
|
|
6356
6452
|
);
|
|
6357
6453
|
});
|
|
6454
|
+
|
|
6455
|
+
it('handles MeetingInfoV2JoinWebinarError webinar need join with webcast', async () => {
|
|
6456
|
+
meeting.destination = FAKE_DESTINATION;
|
|
6457
|
+
meeting.destinationType = FAKE_TYPE;
|
|
6458
|
+
meeting.attrs.meetingInfoProvider = {
|
|
6459
|
+
fetchMeetingInfo: sinon
|
|
6460
|
+
.stub()
|
|
6461
|
+
.throws(
|
|
6462
|
+
new MeetingInfoV2JoinWebinarError(403026, FAKE_MEETING_INFO, 'a message')
|
|
6463
|
+
),
|
|
6464
|
+
};
|
|
6465
|
+
|
|
6466
|
+
await assert.isRejected(
|
|
6467
|
+
meeting.fetchMeetingInfo({sendCAevents: true}),
|
|
6468
|
+
JoinWebinarError
|
|
6469
|
+
);
|
|
6470
|
+
|
|
6471
|
+
assert.deepEqual(meeting.meetingInfo, FAKE_MEETING_INFO);
|
|
6472
|
+
assert.equal(
|
|
6473
|
+
meeting.meetingInfoFailureReason,
|
|
6474
|
+
MEETING_INFO_FAILURE_REASON.NEED_JOIN_WITH_WEBCAST
|
|
6475
|
+
);
|
|
6476
|
+
});
|
|
6477
|
+
|
|
6478
|
+
it('handles MeetingInfoV2JoinWebinarError webinar need registrationId', async () => {
|
|
6479
|
+
meeting.destination = FAKE_DESTINATION;
|
|
6480
|
+
meeting.destinationType = FAKE_TYPE;
|
|
6481
|
+
meeting.attrs.meetingInfoProvider = {
|
|
6482
|
+
fetchMeetingInfo: sinon
|
|
6483
|
+
.stub()
|
|
6484
|
+
.throws(
|
|
6485
|
+
new MeetingInfoV2JoinWebinarError(403037, FAKE_MEETING_INFO, 'a message')
|
|
6486
|
+
),
|
|
6487
|
+
};
|
|
6488
|
+
|
|
6489
|
+
await assert.isRejected(
|
|
6490
|
+
meeting.fetchMeetingInfo({sendCAevents: true}),
|
|
6491
|
+
JoinWebinarError
|
|
6492
|
+
);
|
|
6493
|
+
|
|
6494
|
+
assert.deepEqual(meeting.meetingInfo, FAKE_MEETING_INFO);
|
|
6495
|
+
assert.equal(
|
|
6496
|
+
meeting.meetingInfoFailureReason,
|
|
6497
|
+
MEETING_INFO_FAILURE_REASON.WEBINAR_NEED_REGISTRATIONID
|
|
6498
|
+
);
|
|
6499
|
+
});
|
|
6358
6500
|
});
|
|
6359
6501
|
|
|
6360
6502
|
describe('#refreshPermissionToken', () => {
|
|
@@ -7817,7 +7959,9 @@ describe('plugin-meetings', () => {
|
|
|
7817
7959
|
});
|
|
7818
7960
|
|
|
7819
7961
|
it('should collect ice candidates', () => {
|
|
7820
|
-
eventListeners[MediaConnectionEventNames.ICE_CANDIDATE]({
|
|
7962
|
+
eventListeners[MediaConnectionEventNames.ICE_CANDIDATE]({
|
|
7963
|
+
candidate: {candidate: 'candidate'},
|
|
7964
|
+
});
|
|
7821
7965
|
|
|
7822
7966
|
assert.equal(meeting.iceCandidatesCount, 1);
|
|
7823
7967
|
});
|
|
@@ -8123,10 +8267,10 @@ describe('plugin-meetings', () => {
|
|
|
8123
8267
|
meeting.statsAnalyzer.stopAnalyzer = sinon.stub().resolves();
|
|
8124
8268
|
meeting.reconnectionManager = {
|
|
8125
8269
|
reconnect: sinon.stub().resolves(),
|
|
8126
|
-
resetReconnectionTimer: () => {}
|
|
8270
|
+
resetReconnectionTimer: () => {},
|
|
8127
8271
|
};
|
|
8128
8272
|
meeting.currentMediaStatus = {
|
|
8129
|
-
video: true
|
|
8273
|
+
video: true,
|
|
8130
8274
|
};
|
|
8131
8275
|
|
|
8132
8276
|
await mockFailedEvent();
|
|
@@ -8662,6 +8806,7 @@ describe('plugin-meetings', () => {
|
|
|
8662
8806
|
});
|
|
8663
8807
|
});
|
|
8664
8808
|
});
|
|
8809
|
+
|
|
8665
8810
|
describe('#setUpLocusInfoSelfListener', () => {
|
|
8666
8811
|
it('listens to the self unadmitted guest event', (done) => {
|
|
8667
8812
|
meeting.startKeepAlive = sinon.stub();
|
|
@@ -8756,6 +8901,26 @@ describe('plugin-meetings', () => {
|
|
|
8756
8901
|
);
|
|
8757
8902
|
});
|
|
8758
8903
|
|
|
8904
|
+
it('listens to the brb state changed event', () => {
|
|
8905
|
+
const assertBrb = (enabled) => {
|
|
8906
|
+
meeting.locusInfo.emit(
|
|
8907
|
+
{ function: 'test', file: 'test' },
|
|
8908
|
+
LOCUSINFO.EVENTS.SELF_MEETING_BRB_CHANGED,
|
|
8909
|
+
{ brb: { enabled } },
|
|
8910
|
+
)
|
|
8911
|
+
assert.calledWithExactly(
|
|
8912
|
+
TriggerProxy.trigger,
|
|
8913
|
+
meeting,
|
|
8914
|
+
{file: 'meeting/index', function: 'setUpLocusInfoSelfListener'},
|
|
8915
|
+
EVENT_TRIGGERS.MEETING_SELF_BRB_UPDATE,
|
|
8916
|
+
{ payload: { brb: { enabled } } },
|
|
8917
|
+
);
|
|
8918
|
+
}
|
|
8919
|
+
|
|
8920
|
+
assertBrb(true);
|
|
8921
|
+
assertBrb(false);
|
|
8922
|
+
})
|
|
8923
|
+
|
|
8759
8924
|
it('listens to the interpretation changed event', () => {
|
|
8760
8925
|
meeting.simultaneousInterpretation.updateSelfInterpretation = sinon.stub();
|
|
8761
8926
|
|
|
@@ -9044,6 +9209,8 @@ describe('plugin-meetings', () => {
|
|
|
9044
9209
|
});
|
|
9045
9210
|
|
|
9046
9211
|
it('listens to MEETING_CONTROLS_PRACTICE_SESSION_STATUS_UPDATED', async () => {
|
|
9212
|
+
meeting.webinar.updatePracticeSessionStatus = sinon.stub();
|
|
9213
|
+
|
|
9047
9214
|
const state = {example: 'value'};
|
|
9048
9215
|
|
|
9049
9216
|
await meeting.locusInfo.emitScoped(
|
|
@@ -9052,6 +9219,7 @@ describe('plugin-meetings', () => {
|
|
|
9052
9219
|
{state}
|
|
9053
9220
|
);
|
|
9054
9221
|
|
|
9222
|
+
assert.calledOnceWithExactly(meeting.webinar.updatePracticeSessionStatus, state);
|
|
9055
9223
|
assert.calledWith(
|
|
9056
9224
|
TriggerProxy.trigger,
|
|
9057
9225
|
meeting,
|
|
@@ -10671,6 +10839,7 @@ describe('plugin-meetings', () => {
|
|
|
10671
10839
|
meeting.webex.internal.llm.on = sinon.stub();
|
|
10672
10840
|
meeting.webex.internal.llm.off = sinon.stub();
|
|
10673
10841
|
meeting.processRelayEvent = sinon.stub();
|
|
10842
|
+
meeting.webinar.isJoinPracticeSessionDataChannel = sinon.stub().returns(false);
|
|
10674
10843
|
});
|
|
10675
10844
|
|
|
10676
10845
|
it('does not connect if the call is not joined yet', async () => {
|
|
@@ -10802,6 +10971,19 @@ describe('plugin-meetings', () => {
|
|
|
10802
10971
|
meeting.processRelayEvent
|
|
10803
10972
|
);
|
|
10804
10973
|
});
|
|
10974
|
+
|
|
10975
|
+
|
|
10976
|
+
it('connect ps data channel if ps started in webinar', async () => {
|
|
10977
|
+
meeting.joinedWith = {state: 'JOINED'};
|
|
10978
|
+
meeting.locusInfo = {url: 'a url', info: {datachannelUrl: 'a datachannel url', practiceSessionDatachannelUrl: 'a ps datachannel url'}};
|
|
10979
|
+
meeting.webinar.isJoinPracticeSessionDataChannel = sinon.stub().returns(true);
|
|
10980
|
+
await meeting.updateLLMConnection();
|
|
10981
|
+
|
|
10982
|
+
assert.notCalled(webex.internal.llm.disconnectLLM);
|
|
10983
|
+
assert.calledWith(webex.internal.llm.registerAndConnect, 'a url', 'a ps datachannel url');
|
|
10984
|
+
|
|
10985
|
+
});
|
|
10986
|
+
|
|
10805
10987
|
});
|
|
10806
10988
|
|
|
10807
10989
|
describe('#setLocus', () => {
|
|
@@ -10993,6 +11175,7 @@ describe('plugin-meetings', () => {
|
|
|
10993
11175
|
beforeEach(() => {
|
|
10994
11176
|
meeting.selfId = '9528d952-e4de-46cf-8157-fd4823b98377';
|
|
10995
11177
|
meeting.deviceUrl = 'my-web-url';
|
|
11178
|
+
meeting.locusInfo.info = {isWebinar: false};
|
|
10996
11179
|
});
|
|
10997
11180
|
|
|
10998
11181
|
const USER_IDS = {
|
|
@@ -11218,13 +11401,24 @@ describe('plugin-meetings', () => {
|
|
|
11218
11401
|
|
|
11219
11402
|
activeSharingId.whiteboard = beneficiaryId;
|
|
11220
11403
|
|
|
11221
|
-
eventTrigger.share.push({
|
|
11404
|
+
eventTrigger.share.push(meeting.webinar.selfIsAttendee ? {
|
|
11405
|
+
eventName: EVENT_TRIGGERS.MEETING_STARTED_SHARING_REMOTE,
|
|
11406
|
+
functionName: 'remoteShare',
|
|
11407
|
+
eventPayload: {
|
|
11408
|
+
memberId: null,
|
|
11409
|
+
url,
|
|
11410
|
+
shareInstanceId,
|
|
11411
|
+
annotationInfo: undefined,
|
|
11412
|
+
resourceType: undefined,
|
|
11413
|
+
},
|
|
11414
|
+
} : {
|
|
11222
11415
|
eventName: EVENT_TRIGGERS.MEETING_STARTED_SHARING_WHITEBOARD,
|
|
11223
11416
|
functionName: 'startWhiteboardShare',
|
|
11224
11417
|
eventPayload: {resourceUrl, memberId: beneficiaryId},
|
|
11225
11418
|
});
|
|
11226
11419
|
|
|
11227
|
-
shareStatus = SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE;
|
|
11420
|
+
shareStatus = meeting.webinar.selfIsAttendee ? SHARE_STATUS.REMOTE_SHARE_ACTIVE : SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE;
|
|
11421
|
+
|
|
11228
11422
|
}
|
|
11229
11423
|
|
|
11230
11424
|
if (eventTrigger.member) {
|
|
@@ -11256,13 +11450,24 @@ describe('plugin-meetings', () => {
|
|
|
11256
11450
|
newPayload.current.content.disposition = FLOOR_ACTION.ACCEPTED;
|
|
11257
11451
|
newPayload.current.content.beneficiaryId = otherBeneficiaryId;
|
|
11258
11452
|
|
|
11259
|
-
eventTrigger.share.push({
|
|
11453
|
+
eventTrigger.share.push(meeting.webinar.selfIsAttendee ? {
|
|
11454
|
+
eventName: EVENT_TRIGGERS.MEETING_STARTED_SHARING_REMOTE,
|
|
11455
|
+
functionName: 'remoteShare',
|
|
11456
|
+
eventPayload: {
|
|
11457
|
+
memberId: null,
|
|
11458
|
+
url,
|
|
11459
|
+
shareInstanceId,
|
|
11460
|
+
annotationInfo: undefined,
|
|
11461
|
+
resourceType: undefined,
|
|
11462
|
+
},
|
|
11463
|
+
} : {
|
|
11260
11464
|
eventName: EVENT_TRIGGERS.MEETING_STARTED_SHARING_WHITEBOARD,
|
|
11261
11465
|
functionName: 'startWhiteboardShare',
|
|
11262
11466
|
eventPayload: {resourceUrl, memberId: beneficiaryId},
|
|
11263
11467
|
});
|
|
11264
11468
|
|
|
11265
|
-
shareStatus = SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE;
|
|
11469
|
+
shareStatus = meeting.webinar.selfIsAttendee ? SHARE_STATUS.REMOTE_SHARE_ACTIVE : SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE;
|
|
11470
|
+
|
|
11266
11471
|
} else {
|
|
11267
11472
|
eventTrigger.share.push({
|
|
11268
11473
|
eventName: EVENT_TRIGGERS.MEETING_STOPPED_SHARING_WHITEBOARD,
|
|
@@ -11389,6 +11594,38 @@ describe('plugin-meetings', () => {
|
|
|
11389
11594
|
assert.exists(meeting.setUpLocusMediaSharesListener);
|
|
11390
11595
|
});
|
|
11391
11596
|
|
|
11597
|
+
describe('Whiteboard Share - Webinar Attendee', () => {
|
|
11598
|
+
it('Scenario #1: Whiteboard sharing as a webinar attendee', () => {
|
|
11599
|
+
// Set the webinar attendee flag
|
|
11600
|
+
meeting.webinar = { selfIsAttendee: true };
|
|
11601
|
+
meeting.locusInfo.info.isWebinar = true;
|
|
11602
|
+
|
|
11603
|
+
// Step 1: Start sharing whiteboard A
|
|
11604
|
+
const data1 = generateData(
|
|
11605
|
+
blankPayload, // Initial payload
|
|
11606
|
+
true, // isGranting: Granting share
|
|
11607
|
+
false, // isContent: Whiteboard (not content)
|
|
11608
|
+
USER_IDS.REMOTE_A, // Beneficiary ID: Remote user A
|
|
11609
|
+
RESOURCE_URLS.WHITEBOARD_A // Resource URL: Whiteboard A
|
|
11610
|
+
);
|
|
11611
|
+
|
|
11612
|
+
// Step 2: Stop sharing whiteboard A
|
|
11613
|
+
const data2 = generateData(
|
|
11614
|
+
data1.payload, // Updated payload from Step 1
|
|
11615
|
+
false, // isGranting: Stopping share
|
|
11616
|
+
false, // isContent: Whiteboard
|
|
11617
|
+
USER_IDS.REMOTE_A // Beneficiary ID: Remote user A
|
|
11618
|
+
);
|
|
11619
|
+
|
|
11620
|
+
// Validate the payload changes and status updates
|
|
11621
|
+
payloadTestHelper([data1]);
|
|
11622
|
+
|
|
11623
|
+
// Specific assertions for webinar attendee status
|
|
11624
|
+
assert.equal(meeting.shareStatus, SHARE_STATUS.REMOTE_SHARE_ACTIVE);
|
|
11625
|
+
});
|
|
11626
|
+
});
|
|
11627
|
+
|
|
11628
|
+
|
|
11392
11629
|
describe('Whiteboard A --> Whiteboard B', () => {
|
|
11393
11630
|
it('Scenario #1: you share both whiteboards', () => {
|
|
11394
11631
|
const data1 = generateData(
|