@webex/plugin-meetings 3.7.0-wxcc.1 → 3.8.0-next.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 +15 -3
- 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/selfUtils.js +5 -0
- package/dist/locus-info/selfUtils.js.map +1 -1
- package/dist/media/MediaConnectionAwaiter.js +1 -0
- package/dist/media/MediaConnectionAwaiter.js.map +1 -1
- package/dist/media/properties.js +30 -16
- package/dist/media/properties.js.map +1 -1
- package/dist/meeting/brbState.js +167 -0
- package/dist/meeting/brbState.js.map +1 -0
- package/dist/meeting/index.js +367 -296
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/muteState.js +1 -6
- package/dist/meeting/muteState.js.map +1 -1
- package/dist/meeting-info/meeting-info-v2.js +19 -12
- package/dist/meeting-info/meeting-info-v2.js.map +1 -1
- package/dist/meeting-info/utilv2.js +5 -1
- package/dist/meeting-info/utilv2.js.map +1 -1
- package/dist/metrics/constants.js +2 -0
- package/dist/metrics/constants.js.map +1 -1
- package/dist/reachability/index.js +31 -3
- package/dist/reachability/index.js.map +1 -1
- package/dist/types/constants.d.ts +9 -2
- package/dist/types/meeting/brbState.d.ts +54 -0
- package/dist/types/meeting/index.d.ts +23 -0
- package/dist/types/meeting-info/meeting-info-v2.d.ts +3 -1
- package/dist/types/metrics/constants.d.ts +2 -0
- package/dist/types/reachability/index.d.ts +9 -1
- package/dist/webinar/index.js +1 -1
- package/package.json +23 -23
- package/src/constants.ts +10 -2
- package/src/locus-info/selfUtils.ts +5 -0
- package/src/media/MediaConnectionAwaiter.ts +2 -0
- package/src/media/properties.ts +34 -13
- package/src/meeting/brbState.ts +169 -0
- package/src/meeting/index.ts +120 -27
- package/src/meeting/muteState.ts +1 -6
- package/src/meeting-info/meeting-info-v2.ts +9 -1
- package/src/meeting-info/utilv2.ts +14 -2
- package/src/metrics/constants.ts +2 -0
- package/src/reachability/index.ts +29 -1
- package/test/unit/spec/locus-info/selfUtils.js +10 -0
- package/test/unit/spec/media/properties.ts +15 -0
- package/test/unit/spec/meeting/brbState.ts +114 -0
- package/test/unit/spec/meeting/index.js +101 -33
- package/test/unit/spec/meeting/muteState.js +0 -24
- package/test/unit/spec/meeting-info/utilv2.js +9 -0
- package/test/unit/spec/reachability/index.ts +120 -10
|
@@ -17,6 +17,8 @@ const CAPTCHA_ERROR_DEFAULT_MESSAGE =
|
|
|
17
17
|
const ADHOC_MEETING_DEFAULT_ERROR =
|
|
18
18
|
'Failed starting the adhoc meeting, Please contact support team ';
|
|
19
19
|
const CAPTCHA_ERROR_REQUIRES_PASSWORD_CODES = [423005, 423006];
|
|
20
|
+
const CAPTCHA_ERROR_REQUIRES_REGISTRATION_ID_CODES = [423007];
|
|
21
|
+
|
|
20
22
|
const POLICY_ERROR_CODES = [403049, 403104, 403103, 403048, 403102, 403101];
|
|
21
23
|
const JOIN_FORBIDDEN_CODES = [403003];
|
|
22
24
|
/**
|
|
@@ -113,6 +115,7 @@ export class MeetingInfoV2PolicyError extends Error {
|
|
|
113
115
|
export class MeetingInfoV2CaptchaError extends Error {
|
|
114
116
|
captchaInfo: any;
|
|
115
117
|
isPasswordRequired: any;
|
|
118
|
+
isRegistrationIdRequired: any;
|
|
116
119
|
sdkMessage: any;
|
|
117
120
|
wbxAppApiCode: any;
|
|
118
121
|
body: any;
|
|
@@ -134,6 +137,8 @@ export class MeetingInfoV2CaptchaError extends Error {
|
|
|
134
137
|
this.stack = new Error().stack;
|
|
135
138
|
this.wbxAppApiCode = wbxAppApiErrorCode;
|
|
136
139
|
this.isPasswordRequired = CAPTCHA_ERROR_REQUIRES_PASSWORD_CODES.includes(wbxAppApiErrorCode);
|
|
140
|
+
this.isRegistrationIdRequired =
|
|
141
|
+
CAPTCHA_ERROR_REQUIRES_REGISTRATION_ID_CODES.includes(wbxAppApiErrorCode);
|
|
137
142
|
this.captchaInfo = captchaInfo;
|
|
138
143
|
}
|
|
139
144
|
}
|
|
@@ -370,6 +375,7 @@ export default class MeetingInfoV2 {
|
|
|
370
375
|
* @param {String} locusId
|
|
371
376
|
* @param {Object} extraParams
|
|
372
377
|
* @param {Object} options
|
|
378
|
+
* @param {String} registrationId
|
|
373
379
|
* @returns {Promise} returns a meeting info object
|
|
374
380
|
* @public
|
|
375
381
|
* @memberof MeetingInfo
|
|
@@ -385,7 +391,8 @@ export default class MeetingInfoV2 {
|
|
|
385
391
|
installedOrgID = null,
|
|
386
392
|
locusId = null,
|
|
387
393
|
extraParams: object = {},
|
|
388
|
-
options: {meetingId?: string; sendCAevents?: boolean} = {}
|
|
394
|
+
options: {meetingId?: string; sendCAevents?: boolean} = {},
|
|
395
|
+
registrationId: string = null
|
|
389
396
|
) {
|
|
390
397
|
const {meetingId, sendCAevents} = options;
|
|
391
398
|
|
|
@@ -410,6 +417,7 @@ export default class MeetingInfoV2 {
|
|
|
410
417
|
installedOrgID,
|
|
411
418
|
locusId,
|
|
412
419
|
extraParams,
|
|
420
|
+
registrationId,
|
|
413
421
|
});
|
|
414
422
|
|
|
415
423
|
// If the body only contains the default properties, we don't have enough to
|
|
@@ -228,8 +228,16 @@ export default class MeetingInfoUtil {
|
|
|
228
228
|
* @returns {Object} returns an object with {resource, method}
|
|
229
229
|
*/
|
|
230
230
|
static getRequestBody(options: {type: DESTINATION_TYPE; destination: object} | any) {
|
|
231
|
-
const {
|
|
232
|
-
|
|
231
|
+
const {
|
|
232
|
+
type,
|
|
233
|
+
destination,
|
|
234
|
+
password,
|
|
235
|
+
captchaInfo,
|
|
236
|
+
installedOrgID,
|
|
237
|
+
locusId,
|
|
238
|
+
extraParams,
|
|
239
|
+
registrationId,
|
|
240
|
+
} = options;
|
|
233
241
|
const body: any = {
|
|
234
242
|
...DEFAULT_MEETING_INFO_REQUEST_BODY,
|
|
235
243
|
...extraParams,
|
|
@@ -271,6 +279,10 @@ export default class MeetingInfoUtil {
|
|
|
271
279
|
body.password = password;
|
|
272
280
|
}
|
|
273
281
|
|
|
282
|
+
if (registrationId) {
|
|
283
|
+
body.registrationId = registrationId;
|
|
284
|
+
}
|
|
285
|
+
|
|
274
286
|
if (captchaInfo) {
|
|
275
287
|
body.captchaID = captchaInfo.id;
|
|
276
288
|
body.captchaVerifyCode = captchaInfo.code;
|
package/src/metrics/constants.ts
CHANGED
|
@@ -73,6 +73,8 @@ const BEHAVIORAL_METRICS = {
|
|
|
73
73
|
JOIN_WEBINAR_ERROR: 'js_sdk_join_webinar_error',
|
|
74
74
|
GUEST_ENTERED_LOBBY: 'js_sdk_guest_entered_lobby',
|
|
75
75
|
GUEST_EXITED_LOBBY: 'js_sdk_guest_exited_lobby',
|
|
76
|
+
VERIFY_REGISTRATION_ID_SUCCESS: 'js_sdk_verify_registrationId_success',
|
|
77
|
+
VERIFY_REGISTRATION_ID_ERROR: 'js_sdk_verify_registrationId_error',
|
|
76
78
|
JOIN_FORBIDDEN_ERROR: 'js_sdk_join_forbidden_error',
|
|
77
79
|
};
|
|
78
80
|
|
|
@@ -259,6 +259,32 @@ export default class Reachability extends EventsScope {
|
|
|
259
259
|
}
|
|
260
260
|
}
|
|
261
261
|
|
|
262
|
+
/**
|
|
263
|
+
* Stops all reachability checks that are in progress
|
|
264
|
+
* @public
|
|
265
|
+
* @memberof Reachability
|
|
266
|
+
* @returns {void}
|
|
267
|
+
*/
|
|
268
|
+
public stopReachability() {
|
|
269
|
+
// overallTimer is always there only if there is reachability in progress
|
|
270
|
+
if (this.overallTimer) {
|
|
271
|
+
LoggerProxy.logger.log(
|
|
272
|
+
'Reachability:index#stopReachability --> stopping reachability checks'
|
|
273
|
+
);
|
|
274
|
+
this.abortCurrentChecks();
|
|
275
|
+
this.emit(
|
|
276
|
+
{
|
|
277
|
+
file: 'reachability',
|
|
278
|
+
function: 'stopReachability',
|
|
279
|
+
},
|
|
280
|
+
'reachability:stopped',
|
|
281
|
+
{}
|
|
282
|
+
);
|
|
283
|
+
this.sendMetric(true);
|
|
284
|
+
this.resolveReachabilityPromise();
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
262
288
|
/**
|
|
263
289
|
* Returns statistics about last reachability results. The returned value is an object
|
|
264
290
|
* with a flat list of properties so that it can be easily sent with metrics
|
|
@@ -637,9 +663,10 @@ export default class Reachability extends EventsScope {
|
|
|
637
663
|
/**
|
|
638
664
|
* Sends a metric with all the statistics about how long reachability took
|
|
639
665
|
*
|
|
666
|
+
* @param {boolean} aborted true if the reachability checks were aborted
|
|
640
667
|
* @returns {void}
|
|
641
668
|
*/
|
|
642
|
-
protected async sendMetric() {
|
|
669
|
+
protected async sendMetric(aborted = false) {
|
|
643
670
|
const results = [];
|
|
644
671
|
|
|
645
672
|
Object.values(this.clusterReachability).forEach((clusterReachability) => {
|
|
@@ -650,6 +677,7 @@ export default class Reachability extends EventsScope {
|
|
|
650
677
|
});
|
|
651
678
|
|
|
652
679
|
const stats = {
|
|
680
|
+
aborted,
|
|
653
681
|
vmn: {
|
|
654
682
|
udp: this.getStatistics(results, 'udp', true),
|
|
655
683
|
},
|
|
@@ -450,6 +450,16 @@ describe('plugin-meetings', () => {
|
|
|
450
450
|
assert.equal(SelfUtils.mutedByOthersChanged(null, {remoteMuted: true}), true);
|
|
451
451
|
});
|
|
452
452
|
|
|
453
|
+
it('should return false when selfIdentity and modifiedBy are the same', function () {
|
|
454
|
+
assert.equal(
|
|
455
|
+
SelfUtils.mutedByOthersChanged(
|
|
456
|
+
{remoteMuted: false},
|
|
457
|
+
{remoteMuted: true, selfIdentity: 'user1', modifiedBy: 'user1'}
|
|
458
|
+
),
|
|
459
|
+
false
|
|
460
|
+
);
|
|
461
|
+
});
|
|
462
|
+
|
|
453
463
|
it('should return true when remoteMuted values are different', function () {
|
|
454
464
|
assert.equal(
|
|
455
465
|
SelfUtils.mutedByOthersChanged(
|
|
@@ -66,6 +66,21 @@ describe('MediaProperties', () => {
|
|
|
66
66
|
assert.equal(numTransports, 0);
|
|
67
67
|
});
|
|
68
68
|
|
|
69
|
+
it('handles time out in the case when getStats() is not resolving', async () => {
|
|
70
|
+
// Promise that never resolves
|
|
71
|
+
mockMC.getStats = new Promise(() => {});
|
|
72
|
+
|
|
73
|
+
const promise = mediaProperties.getCurrentConnectionInfo();
|
|
74
|
+
|
|
75
|
+
await clock.tickAsync(1000);
|
|
76
|
+
|
|
77
|
+
const {connectionType, selectedCandidatePairChanges, numTransports} = await promise;
|
|
78
|
+
|
|
79
|
+
assert.equal(connectionType, 'unknown');
|
|
80
|
+
assert.equal(selectedCandidatePairChanges, -1);
|
|
81
|
+
assert.equal(numTransports, 0);
|
|
82
|
+
});
|
|
83
|
+
|
|
69
84
|
describe('selectedCandidatePairChanges and numTransports', () => {
|
|
70
85
|
it('returns correct values when getStats() returns no transport stats at all', async () => {
|
|
71
86
|
mockMC.getStats.resolves([{type: 'something', id: '1234'}]);
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import sinon from 'sinon';
|
|
2
|
+
import {assert} from '@webex/test-helper-chai';
|
|
3
|
+
|
|
4
|
+
import testUtils from '../../../utils/testUtils';
|
|
5
|
+
import {BrbState, createBrbState} from '@webex/plugin-meetings/src/meeting/brbState';
|
|
6
|
+
|
|
7
|
+
describe('plugin-meetings', () => {
|
|
8
|
+
let meeting: any;
|
|
9
|
+
let brbState: BrbState;
|
|
10
|
+
|
|
11
|
+
beforeEach(async () => {
|
|
12
|
+
meeting = {
|
|
13
|
+
isMultistream: true,
|
|
14
|
+
locusUrl: 'locus url',
|
|
15
|
+
deviceUrl: 'device url',
|
|
16
|
+
selfId: 'self id',
|
|
17
|
+
mediaProperties: {
|
|
18
|
+
webrtcMediaConnection: true,
|
|
19
|
+
},
|
|
20
|
+
sendSlotManager: {
|
|
21
|
+
setSourceStateOverride: sinon.stub(),
|
|
22
|
+
},
|
|
23
|
+
meetingRequest: {
|
|
24
|
+
setBrb: sinon.stub().resolves(),
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
brbState = new BrbState(meeting, false);
|
|
29
|
+
await testUtils.flushPromises();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe('brbState library', () => {
|
|
33
|
+
it('takes into account current status when instantiated', async () => {
|
|
34
|
+
// create a new BrbState instance
|
|
35
|
+
brbState = createBrbState(meeting, true);
|
|
36
|
+
await testUtils.flushPromises();
|
|
37
|
+
|
|
38
|
+
assert.isTrue(brbState.state.client.enabled);
|
|
39
|
+
|
|
40
|
+
// now check the opposite case
|
|
41
|
+
brbState = createBrbState(meeting, false);
|
|
42
|
+
await testUtils.flushPromises();
|
|
43
|
+
|
|
44
|
+
assert.isFalse(brbState.state.client.enabled);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('can be enabled', async () => {
|
|
48
|
+
brbState.enable(true, meeting.sendSlotManager);
|
|
49
|
+
brbState.handleServerBrbUpdate(true);
|
|
50
|
+
await testUtils.flushPromises();
|
|
51
|
+
|
|
52
|
+
assert.isTrue(brbState.state.client.enabled);
|
|
53
|
+
assert.isTrue(brbState.state.server.enabled);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('can be disabled', async () => {
|
|
57
|
+
brbState.enable(false, meeting.sendSlotManager);
|
|
58
|
+
brbState.handleServerBrbUpdate(false);
|
|
59
|
+
await testUtils.flushPromises();
|
|
60
|
+
|
|
61
|
+
assert.isFalse(brbState.state.client.enabled);
|
|
62
|
+
assert.isFalse(brbState.state.server.enabled);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('does not send local brb state to server if it is not a multistream meeting', async () => {
|
|
66
|
+
meeting.isMultistream = false;
|
|
67
|
+
brbState.enable(true, meeting.sendSlotManager);
|
|
68
|
+
brbState.handleServerBrbUpdate(true);
|
|
69
|
+
await testUtils.flushPromises();
|
|
70
|
+
|
|
71
|
+
assert.isTrue(meeting.meetingRequest.setBrb.notCalled);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('does not send local brb state to server if webrtc media connection is not defined', async () => {
|
|
75
|
+
meeting.mediaProperties.webrtcMediaConnection = undefined;
|
|
76
|
+
brbState.enable(true, meeting.sendSlotManager);
|
|
77
|
+
brbState.handleServerBrbUpdate(true);
|
|
78
|
+
await testUtils.flushPromises();
|
|
79
|
+
|
|
80
|
+
assert.isTrue(meeting.meetingRequest.setBrb.notCalled);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('does not send request twice when in progress', async () => {
|
|
84
|
+
brbState.state.syncToServerInProgress = true;
|
|
85
|
+
brbState.enable(true, meeting.sendSlotManager);
|
|
86
|
+
await testUtils.flushPromises();
|
|
87
|
+
|
|
88
|
+
assert.isTrue(meeting.meetingRequest.setBrb.notCalled);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('syncs with server when client state does not match server state', async () => {
|
|
92
|
+
brbState.enable(true, meeting.sendSlotManager);
|
|
93
|
+
brbState.handleServerBrbUpdate(true);
|
|
94
|
+
await testUtils.flushPromises();
|
|
95
|
+
|
|
96
|
+
assert.isTrue(meeting.meetingRequest.setBrb.calledOnce);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('sets source state override when client state does not match server state', async () => {
|
|
100
|
+
brbState.enable(true, meeting.sendSlotManager);
|
|
101
|
+
brbState.handleServerBrbUpdate(true);
|
|
102
|
+
await testUtils.flushPromises();
|
|
103
|
+
|
|
104
|
+
assert.isTrue(meeting.sendSlotManager.setSourceStateOverride.calledOnce);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('handles server update', async () => {
|
|
108
|
+
brbState.handleServerBrbUpdate(true);
|
|
109
|
+
await testUtils.flushPromises();
|
|
110
|
+
|
|
111
|
+
assert.isTrue(brbState.state.server.enabled);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
});
|
|
@@ -114,6 +114,7 @@ import {ERROR_DESCRIPTIONS} from '@webex/internal-plugin-metrics/src/call-diagno
|
|
|
114
114
|
import MeetingCollection from '@webex/plugin-meetings/src/meetings/collection';
|
|
115
115
|
|
|
116
116
|
import {EVENT_TRIGGERS as VOICEAEVENTS} from '@webex/internal-plugin-voicea';
|
|
117
|
+
import { createBrbState } from '@webex/plugin-meetings/src/meeting/brbState';
|
|
117
118
|
import JoinForbiddenError from '../../../../src/common/errors/join-forbidden-error';
|
|
118
119
|
|
|
119
120
|
describe('plugin-meetings', () => {
|
|
@@ -246,6 +247,7 @@ describe('plugin-meetings', () => {
|
|
|
246
247
|
isAnyPublicClusterReachable: sinon.stub().resolves(true),
|
|
247
248
|
getReachabilityResults: sinon.stub().resolves(undefined),
|
|
248
249
|
getReachabilityMetrics: sinon.stub().resolves({}),
|
|
250
|
+
stopReachability: sinon.stub(),
|
|
249
251
|
};
|
|
250
252
|
webex.internal.llm.on = sinon.stub();
|
|
251
253
|
webex.internal.newMetrics.callDiagnosticLatencies = new CallDiagnosticLatencies(
|
|
@@ -2095,6 +2097,7 @@ describe('plugin-meetings', () => {
|
|
|
2095
2097
|
someReachabilityMetric1: 'some value1',
|
|
2096
2098
|
someReachabilityMetric2: 'some value2',
|
|
2097
2099
|
}),
|
|
2100
|
+
stopReachability: sinon.stub(),
|
|
2098
2101
|
};
|
|
2099
2102
|
|
|
2100
2103
|
const forceRtcMetricsSend = sinon.stub().resolves();
|
|
@@ -2514,6 +2517,7 @@ describe('plugin-meetings', () => {
|
|
|
2514
2517
|
assert.calledOnce(meeting.setMercuryListener);
|
|
2515
2518
|
assert.calledOnce(fakeMediaConnection.initiateOffer);
|
|
2516
2519
|
assert.equal(meeting.allowMediaInLobby, allowMediaInLobby);
|
|
2520
|
+
assert.calledOnce(webex.meetings.reachability.stopReachability);
|
|
2517
2521
|
};
|
|
2518
2522
|
|
|
2519
2523
|
it('should attach the media and return promise', async () => {
|
|
@@ -2709,6 +2713,7 @@ describe('plugin-meetings', () => {
|
|
|
2709
2713
|
webex.meetings.reachability = {
|
|
2710
2714
|
isWebexMediaBackendUnreachable: sinon.stub().resolves(false),
|
|
2711
2715
|
getReachabilityMetrics: sinon.stub().resolves(),
|
|
2716
|
+
stopReachability: sinon.stub(),
|
|
2712
2717
|
};
|
|
2713
2718
|
const MOCK_CLIENT_ERROR_CODE = 2004;
|
|
2714
2719
|
const generateClientErrorCodeForIceFailureStub = sinon
|
|
@@ -2917,6 +2922,7 @@ describe('plugin-meetings', () => {
|
|
|
2917
2922
|
.onCall(2)
|
|
2918
2923
|
.resolves(false),
|
|
2919
2924
|
getReachabilityMetrics: sinon.stub().resolves({}),
|
|
2925
|
+
stopReachability: sinon.stub(),
|
|
2920
2926
|
};
|
|
2921
2927
|
const getErrorPayloadForClientErrorCodeStub =
|
|
2922
2928
|
(webex.internal.newMetrics.callDiagnosticMetrics.getErrorPayloadForClientErrorCode =
|
|
@@ -3211,6 +3217,7 @@ describe('plugin-meetings', () => {
|
|
|
3211
3217
|
someReachabilityMetric1: 'some value1',
|
|
3212
3218
|
someReachabilityMetric2: 'some value2',
|
|
3213
3219
|
}),
|
|
3220
|
+
stopReachability: sinon.stub(),
|
|
3214
3221
|
};
|
|
3215
3222
|
meeting.iceCandidatesCount = 3;
|
|
3216
3223
|
meeting.iceCandidateErrors.set('701_error', 3);
|
|
@@ -3715,6 +3722,7 @@ describe('plugin-meetings', () => {
|
|
|
3715
3722
|
|
|
3716
3723
|
webex.meetings.reachability = {
|
|
3717
3724
|
isWebexMediaBackendUnreachable: sinon.stub().resolves(unreachable || false),
|
|
3725
|
+
stopReachability: sinon.stub(),
|
|
3718
3726
|
};
|
|
3719
3727
|
|
|
3720
3728
|
const generateClientErrorCodeForIceFailureStub = sinon
|
|
@@ -3812,7 +3820,6 @@ describe('plugin-meetings', () => {
|
|
|
3812
3820
|
};
|
|
3813
3821
|
|
|
3814
3822
|
beforeEach(() => {
|
|
3815
|
-
meeting.meetingRequest.setBrb = sinon.stub().resolves({body: 'test'});
|
|
3816
3823
|
meeting.mediaProperties.webrtcMediaConnection = {createSendSlot: sinon.stub()};
|
|
3817
3824
|
meeting.sendSlotManager.createSlot(
|
|
3818
3825
|
fakeMultistreamRoapMediaConnection,
|
|
@@ -3822,6 +3829,8 @@ describe('plugin-meetings', () => {
|
|
|
3822
3829
|
meeting.locusUrl = 'locus url';
|
|
3823
3830
|
meeting.deviceUrl = 'device url';
|
|
3824
3831
|
meeting.selfId = 'self id';
|
|
3832
|
+
meeting.brbState = createBrbState(meeting, false);
|
|
3833
|
+
meeting.brbState.enable = sinon.stub().resolves();
|
|
3825
3834
|
});
|
|
3826
3835
|
|
|
3827
3836
|
afterEach(() => {
|
|
@@ -3843,7 +3852,7 @@ describe('plugin-meetings', () => {
|
|
|
3843
3852
|
|
|
3844
3853
|
await brbResult;
|
|
3845
3854
|
assert.exists(brbResult.then);
|
|
3846
|
-
assert.calledOnce(meeting.
|
|
3855
|
+
assert.calledOnce(meeting.brbState.enable);
|
|
3847
3856
|
})
|
|
3848
3857
|
|
|
3849
3858
|
it('should disable #beRightBack and return a promise', async () => {
|
|
@@ -3851,12 +3860,12 @@ describe('plugin-meetings', () => {
|
|
|
3851
3860
|
|
|
3852
3861
|
await brbResult;
|
|
3853
3862
|
assert.exists(brbResult.then);
|
|
3854
|
-
assert.calledOnce(meeting.
|
|
3863
|
+
assert.calledOnce(meeting.brbState.enable);
|
|
3855
3864
|
})
|
|
3856
3865
|
|
|
3857
3866
|
it('should throw an error and reject the promise if setBrb fails', async () => {
|
|
3858
3867
|
const error = new Error('setBrb failed');
|
|
3859
|
-
meeting.
|
|
3868
|
+
meeting.brbState.enable.rejects(error);
|
|
3860
3869
|
|
|
3861
3870
|
try {
|
|
3862
3871
|
await meeting.beRightBack(true);
|
|
@@ -3867,27 +3876,6 @@ describe('plugin-meetings', () => {
|
|
|
3867
3876
|
}
|
|
3868
3877
|
})
|
|
3869
3878
|
});
|
|
3870
|
-
|
|
3871
|
-
describe('when in a transcoded meeting', () => {
|
|
3872
|
-
|
|
3873
|
-
beforeEach(() => {
|
|
3874
|
-
meeting.isMultistream = false;
|
|
3875
|
-
});
|
|
3876
|
-
|
|
3877
|
-
it('should ignore enabling #beRightBack', async () => {
|
|
3878
|
-
meeting.beRightBack(true);
|
|
3879
|
-
|
|
3880
|
-
assert.isRejected((Promise.reject()));
|
|
3881
|
-
assert.notCalled(meeting.meetingRequest.setBrb);
|
|
3882
|
-
})
|
|
3883
|
-
|
|
3884
|
-
it('should ignore disabling #beRightBack', async () => {
|
|
3885
|
-
meeting.beRightBack(false);
|
|
3886
|
-
|
|
3887
|
-
assert.isRejected((Promise.reject()));
|
|
3888
|
-
assert.notCalled(meeting.meetingRequest.setBrb);
|
|
3889
|
-
})
|
|
3890
|
-
});
|
|
3891
3879
|
});
|
|
3892
3880
|
|
|
3893
3881
|
/* This set of tests are like semi-integration tests, they use real MuteState, Media, LocusMediaRequest and Roap classes.
|
|
@@ -6804,7 +6792,7 @@ describe('plugin-meetings', () => {
|
|
|
6804
6792
|
assert.deepEqual(meeting.meetingInfo, FAKE_MEETING_INFO);
|
|
6805
6793
|
assert.equal(
|
|
6806
6794
|
meeting.meetingInfoFailureReason,
|
|
6807
|
-
MEETING_INFO_FAILURE_REASON.
|
|
6795
|
+
MEETING_INFO_FAILURE_REASON.WEBINAR_NEED_REGISTRATION_ID
|
|
6808
6796
|
);
|
|
6809
6797
|
});
|
|
6810
6798
|
});
|
|
@@ -6864,7 +6852,8 @@ describe('plugin-meetings', () => {
|
|
|
6864
6852
|
'fake-installed-org-id',
|
|
6865
6853
|
'locus-id',
|
|
6866
6854
|
{extraParam1: 'value1', permissionToken: FAKE_PERMISSION_TOKEN},
|
|
6867
|
-
{meetingId: meeting.id, sendCAevents: true}
|
|
6855
|
+
{meetingId: meeting.id, sendCAevents: true},
|
|
6856
|
+
null
|
|
6868
6857
|
);
|
|
6869
6858
|
assert.deepEqual(meeting.meetingInfo, {
|
|
6870
6859
|
...FAKE_MEETING_INFO,
|
|
@@ -6909,7 +6898,8 @@ describe('plugin-meetings', () => {
|
|
|
6909
6898
|
'fake-installed-org-id',
|
|
6910
6899
|
'locus-id',
|
|
6911
6900
|
{extraParam1: 'value1', permissionToken: FAKE_PERMISSION_TOKEN},
|
|
6912
|
-
{meetingId: meeting.id, sendCAevents: true}
|
|
6901
|
+
{meetingId: meeting.id, sendCAevents: true},
|
|
6902
|
+
null
|
|
6913
6903
|
);
|
|
6914
6904
|
assert.deepEqual(meeting.meetingInfo, {
|
|
6915
6905
|
...FAKE_MEETING_INFO,
|
|
@@ -6963,7 +6953,8 @@ describe('plugin-meetings', () => {
|
|
|
6963
6953
|
extraParam1: 'value1',
|
|
6964
6954
|
permissionToken: FAKE_PERMISSION_TOKEN,
|
|
6965
6955
|
},
|
|
6966
|
-
{meetingId: meeting.id, sendCAevents: true}
|
|
6956
|
+
{meetingId: meeting.id, sendCAevents: true},
|
|
6957
|
+
null
|
|
6967
6958
|
);
|
|
6968
6959
|
assert.deepEqual(meeting.meetingInfo, {
|
|
6969
6960
|
...FAKE_MEETING_INFO,
|
|
@@ -9230,6 +9221,7 @@ describe('plugin-meetings', () => {
|
|
|
9230
9221
|
|
|
9231
9222
|
it('listens to the brb state changed event', () => {
|
|
9232
9223
|
const assertBrb = (enabled) => {
|
|
9224
|
+
meeting.brbState = createBrbState(meeting, false);
|
|
9233
9225
|
meeting.locusInfo.emit(
|
|
9234
9226
|
{ function: 'test', file: 'test' },
|
|
9235
9227
|
LOCUSINFO.EVENTS.SELF_MEETING_BRB_CHANGED,
|
|
@@ -11259,7 +11251,10 @@ describe('plugin-meetings', () => {
|
|
|
11259
11251
|
|
|
11260
11252
|
const result = await meeting.updateLLMConnection();
|
|
11261
11253
|
|
|
11262
|
-
assert.calledWith(webex.internal.llm.disconnectLLM
|
|
11254
|
+
assert.calledWith(webex.internal.llm.disconnectLLM, {
|
|
11255
|
+
code: 3050,
|
|
11256
|
+
reason: 'done (permanent)',
|
|
11257
|
+
});
|
|
11263
11258
|
assert.calledWith(
|
|
11264
11259
|
webex.internal.llm.registerAndConnect,
|
|
11265
11260
|
'a different url',
|
|
@@ -11289,7 +11284,10 @@ describe('plugin-meetings', () => {
|
|
|
11289
11284
|
|
|
11290
11285
|
const result = await meeting.updateLLMConnection();
|
|
11291
11286
|
|
|
11292
|
-
assert.calledWith(webex.internal.llm.disconnectLLM
|
|
11287
|
+
assert.calledWith(webex.internal.llm.disconnectLLM, {
|
|
11288
|
+
code: 3050,
|
|
11289
|
+
reason: 'done (permanent)',
|
|
11290
|
+
});
|
|
11293
11291
|
assert.calledWith(
|
|
11294
11292
|
webex.internal.llm.registerAndConnect,
|
|
11295
11293
|
'a url',
|
|
@@ -11318,7 +11316,7 @@ describe('plugin-meetings', () => {
|
|
|
11318
11316
|
|
|
11319
11317
|
const result = await meeting.updateLLMConnection();
|
|
11320
11318
|
|
|
11321
|
-
assert.calledWith(webex.internal.llm.disconnectLLM);
|
|
11319
|
+
assert.calledWith(webex.internal.llm.disconnectLLM, undefined);
|
|
11322
11320
|
assert.notCalled(webex.internal.llm.registerAndConnect);
|
|
11323
11321
|
assert.equal(result, undefined);
|
|
11324
11322
|
assert.calledOnceWithExactly(
|
|
@@ -13260,7 +13258,7 @@ describe('plugin-meetings', () => {
|
|
|
13260
13258
|
|
|
13261
13259
|
describe('#roapMessageReceived', () => {
|
|
13262
13260
|
it('calls roapMessageReceived on the webrtc media connection', () => {
|
|
13263
|
-
const fakeMessage = {messageType: '
|
|
13261
|
+
const fakeMessage = {messageType: 'ANSWER', sdp: 'fake sdp'};
|
|
13264
13262
|
|
|
13265
13263
|
const getMediaServer = sinon.stub(MeetingsUtil, 'getMediaServer').returns('homer');
|
|
13266
13264
|
|
|
@@ -13298,5 +13296,75 @@ describe('plugin-meetings', () => {
|
|
|
13298
13296
|
|
|
13299
13297
|
assert.notCalled(meeting.mediaProperties.webrtcMediaConnection.roapMessageReceived);
|
|
13300
13298
|
});
|
|
13299
|
+
|
|
13300
|
+
it('does not call getMediaServer for a roap message other than ANSWER', async () => {
|
|
13301
|
+
const fakeMessage = {messageType: 'ERROR', sdp: 'fake sdp'};
|
|
13302
|
+
|
|
13303
|
+
meeting.isMultistream = true;
|
|
13304
|
+
meeting.mediaProperties.webrtcMediaConnection = {
|
|
13305
|
+
roapMessageReceived: sinon.stub(),
|
|
13306
|
+
};
|
|
13307
|
+
meeting.mediaProperties.webrtcMediaConnection.mediaServer = 'linus';
|
|
13308
|
+
|
|
13309
|
+
const getMediaServerStub = sinon.stub(MeetingsUtil, 'getMediaServer').returns('something');
|
|
13310
|
+
|
|
13311
|
+
meeting.roapMessageReceived(fakeMessage);
|
|
13312
|
+
|
|
13313
|
+
assert.calledOnceWithExactly(
|
|
13314
|
+
meeting.mediaProperties.webrtcMediaConnection.roapMessageReceived,
|
|
13315
|
+
fakeMessage
|
|
13316
|
+
);
|
|
13317
|
+
assert.notCalled(getMediaServerStub);
|
|
13318
|
+
assert.equal(meeting.mediaProperties.webrtcMediaConnection.mediaServer, 'linus'); // check that it hasn't been overwritten
|
|
13319
|
+
});
|
|
13320
|
+
});
|
|
13321
|
+
|
|
13322
|
+
describe('#verifyRegistrationId', () => {
|
|
13323
|
+
it('calls fetchMeetingInfo() with the passed registrationId and captcha code', async () => {
|
|
13324
|
+
// simulate successful case
|
|
13325
|
+
meeting.fetchMeetingInfo = sinon.stub().resolves();
|
|
13326
|
+
const result = await meeting.verifyRegistrationId('registrationId', 'captcha id');
|
|
13327
|
+
|
|
13328
|
+
assert(Metrics.sendBehavioralMetric.calledOnce);
|
|
13329
|
+
assert.calledWith(
|
|
13330
|
+
Metrics.sendBehavioralMetric,
|
|
13331
|
+
BEHAVIORAL_METRICS.VERIFY_REGISTRATION_ID_SUCCESS
|
|
13332
|
+
);
|
|
13333
|
+
assert.equal(result.isRegistrationIdValid, true);
|
|
13334
|
+
assert.equal(result.requiredCaptcha, null);
|
|
13335
|
+
assert.equal(result.failureReason, MEETING_INFO_FAILURE_REASON.NONE);
|
|
13336
|
+
assert.calledWith(meeting.fetchMeetingInfo, {
|
|
13337
|
+
registrationId: 'registrationId',
|
|
13338
|
+
captchaCode: 'captcha id',
|
|
13339
|
+
sendCAevents: false,
|
|
13340
|
+
});
|
|
13341
|
+
});
|
|
13342
|
+
it('handles registrationIdError returned by fetchMeetingInfo', async () => {
|
|
13343
|
+
meeting.fetchMeetingInfo = sinon.stub().callsFake(() => {
|
|
13344
|
+
meeting.meetingInfoFailureReason = MEETING_INFO_FAILURE_REASON.WRONG_REGISTRATIONID;
|
|
13345
|
+
|
|
13346
|
+
return Promise.reject(new JoinWebinarError());
|
|
13347
|
+
});
|
|
13348
|
+
const result = await meeting.verifyRegistrationId('registrationId', 'captcha id');
|
|
13349
|
+
|
|
13350
|
+
assert.equal(result.isRegistrationIdValid, false);
|
|
13351
|
+
assert.equal(result.requiredCaptcha, null);
|
|
13352
|
+
assert.equal(result.failureReason, MEETING_INFO_FAILURE_REASON.WRONG_REGISTRATION_ID);
|
|
13353
|
+
});
|
|
13354
|
+
it('handles CaptchaError returned by fetchMeetingInfo', async () => {
|
|
13355
|
+
const FAKE_CAPTCHA = {captchaId: 'some catcha id...'};
|
|
13356
|
+
|
|
13357
|
+
meeting.fetchMeetingInfo = sinon.stub().callsFake(() => {
|
|
13358
|
+
meeting.meetingInfoFailureReason = MEETING_INFO_FAILURE_REASON.WRONG_CAPTCHA;
|
|
13359
|
+
meeting.requiredCaptcha = FAKE_CAPTCHA;
|
|
13360
|
+
|
|
13361
|
+
return Promise.reject(new CaptchaError());
|
|
13362
|
+
});
|
|
13363
|
+
const result = await meeting.verifyRegistrationId('registrationId', 'captcha id');
|
|
13364
|
+
|
|
13365
|
+
assert.equal(result.isRegistrationIdValid, false);
|
|
13366
|
+
assert.deepEqual(result.requiredCaptcha, FAKE_CAPTCHA);
|
|
13367
|
+
assert.equal(result.failureReason, MEETING_INFO_FAILURE_REASON.WRONG_CAPTCHA);
|
|
13368
|
+
});
|
|
13301
13369
|
});
|
|
13302
13370
|
});
|
|
@@ -113,30 +113,6 @@ describe('plugin-meetings', () => {
|
|
|
113
113
|
assert.isTrue(audio.isRemotelyMuted());
|
|
114
114
|
});
|
|
115
115
|
|
|
116
|
-
it('does not locally unmute on a server unmute', async () => {
|
|
117
|
-
const setServerMutedSpy = meeting.mediaProperties.audioStream.setServerMuted;
|
|
118
|
-
|
|
119
|
-
// simulate remote mute
|
|
120
|
-
audio.handleServerRemoteMuteUpdate(meeting, true, true);
|
|
121
|
-
|
|
122
|
-
assert.isTrue(audio.isRemotelyMuted());
|
|
123
|
-
assert.isTrue(audio.isLocallyMuted());
|
|
124
|
-
|
|
125
|
-
// mutes local
|
|
126
|
-
assert.calledOnceWithExactly(setServerMutedSpy, true, 'remotelyMuted');
|
|
127
|
-
|
|
128
|
-
setServerMutedSpy.resetHistory();
|
|
129
|
-
|
|
130
|
-
// simulate remote unmute
|
|
131
|
-
audio.handleServerRemoteMuteUpdate(meeting, false, true);
|
|
132
|
-
|
|
133
|
-
assert.isFalse(audio.isRemotelyMuted());
|
|
134
|
-
assert.isTrue(audio.isLocallyMuted());
|
|
135
|
-
|
|
136
|
-
// does not unmute local
|
|
137
|
-
assert.notCalled(setServerMutedSpy);
|
|
138
|
-
});
|
|
139
|
-
|
|
140
116
|
it('does local audio unmute if localAudioUnmuteRequired is received', async () => {
|
|
141
117
|
// first we need to have the local stream user muted
|
|
142
118
|
meeting.mediaProperties.audioStream.userMuted = true;
|
|
@@ -192,6 +192,15 @@ describe('plugin-meetings', () => {
|
|
|
192
192
|
assert.equal(res.meetingUUID, 'xsddsdsdsdssdsdsdsdsd');
|
|
193
193
|
});
|
|
194
194
|
|
|
195
|
+
it('for registrationId', () => {
|
|
196
|
+
const res = MeetingInfoUtil.getRequestBody({
|
|
197
|
+
type: DESTINATION_TYPE.MEETING_UUID,
|
|
198
|
+
registrationId: 'registrationId',
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
assert.equal(res.registrationId, 'registrationId');
|
|
202
|
+
});
|
|
203
|
+
|
|
195
204
|
it('for DESTINATION_TYPE.LOCUS_ID', () => {
|
|
196
205
|
const res = MeetingInfoUtil.getRequestBody({
|
|
197
206
|
type: DESTINATION_TYPE.LOCUS_ID,
|