@webex/plugin-meetings 3.8.0-web-workers-keepalive.1 → 3.8.1-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 +70 -6
- package/dist/breakouts/index.js.map +1 -1
- package/dist/common/errors/webex-errors.js +12 -2
- package/dist/common/errors/webex-errors.js.map +1 -1
- package/dist/config.js +4 -1
- package/dist/config.js.map +1 -1
- package/dist/constants.js +22 -123
- package/dist/constants.js.map +1 -1
- package/dist/controls-options-manager/enums.js +2 -0
- package/dist/controls-options-manager/enums.js.map +1 -1
- package/dist/controls-options-manager/types.js.map +1 -1
- package/dist/controls-options-manager/util.js +52 -0
- package/dist/controls-options-manager/util.js.map +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/controlsUtils.js +30 -10
- package/dist/locus-info/controlsUtils.js.map +1 -1
- package/dist/locus-info/index.js +83 -12
- package/dist/locus-info/index.js.map +1 -1
- package/dist/locus-info/selfUtils.js +432 -418
- package/dist/locus-info/selfUtils.js.map +1 -1
- package/dist/media/index.js +17 -17
- package/dist/media/index.js.map +1 -1
- package/dist/media/properties.js +94 -6
- package/dist/media/properties.js.map +1 -1
- package/dist/meeting/brbState.js +9 -2
- package/dist/meeting/brbState.js.map +1 -1
- package/dist/meeting/in-meeting-actions.js +17 -1
- package/dist/meeting/in-meeting-actions.js.map +1 -1
- package/dist/meeting/index.js +568 -328
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/locusMediaRequest.js +0 -17
- package/dist/meeting/locusMediaRequest.js.map +1 -1
- package/dist/meeting/muteState.js +4 -4
- package/dist/meeting/muteState.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 +9 -1
- package/dist/meeting/util.js.map +1 -1
- package/dist/meeting-info/meeting-info-v2.js +19 -13
- 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/meetings/index.js +76 -0
- package/dist/meetings/index.js.map +1 -1
- package/dist/meetings/util.js +14 -0
- package/dist/meetings/util.js.map +1 -1
- package/dist/member/index.js +45 -9
- package/dist/member/index.js.map +1 -1
- package/dist/member/types.js +3 -0
- package/dist/member/types.js.map +1 -1
- package/dist/member/util.js +335 -356
- package/dist/member/util.js.map +1 -1
- package/dist/members/collection.js.map +1 -1
- package/dist/members/index.js +137 -29
- package/dist/members/index.js.map +1 -1
- package/dist/members/request.js +38 -0
- package/dist/members/request.js.map +1 -1
- package/dist/members/util.js +36 -1
- package/dist/members/util.js.map +1 -1
- package/dist/metrics/constants.js +1 -0
- package/dist/metrics/constants.js.map +1 -1
- package/dist/reachability/clusterReachability.js +23 -31
- package/dist/reachability/clusterReachability.js.map +1 -1
- package/dist/reachability/index.js +42 -2
- package/dist/reachability/index.js.map +1 -1
- package/dist/reconnection-manager/index.js +2 -2
- package/dist/reconnection-manager/index.js.map +1 -1
- package/dist/roap/index.js.map +1 -1
- package/dist/roap/turnDiscovery.js +45 -27
- package/dist/roap/turnDiscovery.js.map +1 -1
- package/dist/roap/types.js +17 -0
- package/dist/roap/types.js.map +1 -0
- package/dist/types/common/errors/webex-errors.d.ts +7 -1
- package/dist/types/config.d.ts +2 -0
- package/dist/types/constants.d.ts +15 -85
- package/dist/types/controls-options-manager/enums.d.ts +3 -1
- package/dist/types/controls-options-manager/types.d.ts +7 -1
- package/dist/types/locus-info/index.d.ts +3 -3
- package/dist/types/locus-info/selfUtils.d.ts +216 -1
- package/dist/types/media/properties.d.ts +15 -0
- package/dist/types/meeting/in-meeting-actions.d.ts +16 -0
- package/dist/types/meeting/index.d.ts +35 -1
- package/dist/types/meeting/muteState.d.ts +0 -1
- 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 +3 -1
- package/dist/types/meeting-info/meeting-info-v2.d.ts +2 -1
- package/dist/types/meetings/index.d.ts +28 -0
- package/dist/types/member/index.d.ts +20 -6
- package/dist/types/member/types.d.ts +73 -14
- package/dist/types/member/util.d.ts +156 -1
- package/dist/types/members/collection.d.ts +6 -5
- package/dist/types/members/index.d.ts +32 -43
- package/dist/types/members/request.d.ts +26 -0
- package/dist/types/members/util.d.ts +27 -0
- package/dist/types/metrics/constants.d.ts +1 -0
- package/dist/types/reachability/clusterReachability.d.ts +2 -6
- package/dist/types/reachability/index.d.ts +8 -0
- package/dist/types/roap/index.d.ts +3 -2
- package/dist/types/roap/turnDiscovery.d.ts +5 -17
- package/dist/types/roap/types.d.ts +16 -0
- package/dist/webinar/index.js +1 -1
- package/package.json +24 -23
- package/src/breakouts/index.ts +69 -0
- package/src/common/errors/webex-errors.ts +8 -1
- package/src/config.ts +2 -0
- package/src/constants.ts +23 -90
- package/src/controls-options-manager/enums.ts +2 -0
- package/src/controls-options-manager/types.ts +11 -1
- package/src/controls-options-manager/util.ts +62 -0
- package/src/locus-info/controlsUtils.ts +48 -12
- package/src/locus-info/index.ts +88 -13
- package/src/locus-info/selfUtils.ts +496 -442
- package/src/media/index.ts +23 -21
- package/src/media/properties.ts +96 -0
- package/src/meeting/brbState.ts +11 -2
- package/src/meeting/in-meeting-actions.ts +32 -0
- package/src/meeting/index.ts +356 -87
- package/src/meeting/locusMediaRequest.ts +0 -18
- package/src/meeting/muteState.ts +4 -4
- package/src/meeting/request.ts +36 -1
- package/src/meeting/request.type.ts +7 -0
- package/src/meeting/util.ts +9 -1
- package/src/meeting-info/meeting-info-v2.ts +7 -2
- package/src/meeting-info/utilv2.ts +5 -0
- package/src/meetings/index.ts +76 -0
- package/src/meetings/util.ts +18 -0
- package/src/member/index.ts +57 -22
- package/src/member/types.ts +82 -16
- package/src/member/util.ts +357 -353
- package/src/members/collection.ts +4 -3
- package/src/members/index.ts +137 -18
- package/src/members/request.ts +44 -0
- package/src/members/util.ts +43 -1
- package/src/metrics/constants.ts +1 -0
- package/src/reachability/clusterReachability.ts +26 -25
- package/src/reachability/index.ts +55 -1
- package/src/reconnection-manager/index.ts +2 -2
- package/src/roap/index.ts +3 -7
- package/src/roap/turnDiscovery.ts +34 -39
- package/src/roap/types.ts +23 -0
- package/test/unit/spec/breakouts/index.ts +167 -95
- package/test/unit/spec/controls-options-manager/util.js +120 -0
- package/test/unit/spec/locus-info/controlsUtils.js +131 -9
- package/test/unit/spec/locus-info/index.js +195 -73
- package/test/unit/spec/locus-info/selfUtils.js +98 -24
- package/test/unit/spec/media/index.ts +150 -18
- package/test/unit/spec/media/properties.ts +130 -0
- package/test/unit/spec/meeting/brbState.ts +40 -2
- package/test/unit/spec/meeting/in-meeting-actions.ts +19 -4
- package/test/unit/spec/meeting/index.js +553 -36
- package/test/unit/spec/meeting/locusMediaRequest.ts +0 -30
- package/test/unit/spec/meeting/muteState.js +73 -2
- package/test/unit/spec/meeting/request.js +32 -1
- package/test/unit/spec/meeting/utils.js +79 -33
- package/test/unit/spec/meeting-info/meetinginfov2.js +41 -0
- package/test/unit/spec/meeting-info/utilv2.js +19 -0
- package/test/unit/spec/meetings/index.js +68 -1
- package/test/unit/spec/members/index.js +304 -78
- package/test/unit/spec/members/request.js +68 -22
- package/test/unit/spec/members/utils.js +75 -0
- package/test/unit/spec/reachability/clusterReachability.ts +41 -55
- package/test/unit/spec/reachability/index.ts +89 -0
- package/test/unit/spec/reconnection-manager/index.js +4 -4
- package/test/unit/spec/roap/turnDiscovery.ts +110 -28
@@ -1,6 +1,7 @@
|
|
1
1
|
/*!
|
2
2
|
* Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
|
3
3
|
*/
|
4
|
+
import {v4 as uuidv4} from 'uuid';
|
4
5
|
import 'jsdom-global/register';
|
5
6
|
import {cloneDeep, forEach, isEqual, isUndefined} from 'lodash';
|
6
7
|
import sinon from 'sinon';
|
@@ -115,9 +116,9 @@ import {ERROR_DESCRIPTIONS} from '@webex/internal-plugin-metrics/src/call-diagno
|
|
115
116
|
import MeetingCollection from '@webex/plugin-meetings/src/meetings/collection';
|
116
117
|
|
117
118
|
import {EVENT_TRIGGERS as VOICEAEVENTS} from '@webex/internal-plugin-voicea';
|
118
|
-
import {
|
119
|
+
import {createBrbState} from '@webex/plugin-meetings/src/meeting/brbState';
|
119
120
|
import JoinForbiddenError from '../../../../src/common/errors/join-forbidden-error';
|
120
|
-
import {
|
121
|
+
import {EventEmitter} from 'stream';
|
121
122
|
|
122
123
|
describe('plugin-meetings', () => {
|
123
124
|
const logger = {
|
@@ -242,6 +243,7 @@ describe('plugin-meetings', () => {
|
|
242
243
|
},
|
243
244
|
});
|
244
245
|
|
246
|
+
webex.internal.newMetrics.callDiagnosticMetrics.clearErrorCache = sinon.stub();
|
245
247
|
webex.internal.support.submitLogs = sinon.stub().returns(Promise.resolve());
|
246
248
|
webex.internal.services = {get: sinon.stub().returns('locus-url')};
|
247
249
|
webex.credentials.getOrgId = sinon.stub().returns('fake-org-id');
|
@@ -252,6 +254,7 @@ describe('plugin-meetings', () => {
|
|
252
254
|
getReachabilityResults: sinon.stub().resolves(undefined),
|
253
255
|
getReachabilityMetrics: sinon.stub().resolves({}),
|
254
256
|
stopReachability: sinon.stub(),
|
257
|
+
isSubnetReachable: sinon.stub().returns(true),
|
255
258
|
};
|
256
259
|
webex.internal.llm.on = sinon.stub();
|
257
260
|
webex.internal.newMetrics.callDiagnosticLatencies = new CallDiagnosticLatencies(
|
@@ -282,7 +285,7 @@ describe('plugin-meetings', () => {
|
|
282
285
|
testDestination = `testDestination-${uuid.v4()}`;
|
283
286
|
correlationId = uuid.v4();
|
284
287
|
uploadEvent = new EventEmitter();
|
285
|
-
uploadEvent.addListener('progress', () => {})
|
288
|
+
uploadEvent.addListener('progress', () => {});
|
286
289
|
|
287
290
|
meeting = new Meeting(
|
288
291
|
{
|
@@ -611,6 +614,22 @@ describe('plugin-meetings', () => {
|
|
611
614
|
assert.calledWith(meeting.members.cancelPhoneInvite, uuid1);
|
612
615
|
});
|
613
616
|
});
|
617
|
+
describe('#cancelSIPInvite', () => {
|
618
|
+
it('should have #cancelSIPInvite', () => {
|
619
|
+
assert.exists(meeting.cancelSIPInvite);
|
620
|
+
});
|
621
|
+
beforeEach(() => {
|
622
|
+
meeting.members.cancelSIPInvite = sinon.stub().returns(Promise.resolve(test1));
|
623
|
+
});
|
624
|
+
it('should proxy members #cancelSIPInvite and return a promise', async () => {
|
625
|
+
const cancel = meeting.cancelSIPInvite({memberId: uuid1});
|
626
|
+
|
627
|
+
assert.exists(cancel.then);
|
628
|
+
await cancel;
|
629
|
+
assert.calledOnce(meeting.members.cancelSIPInvite);
|
630
|
+
assert.calledWith(meeting.members.cancelSIPInvite, {memberId: uuid1});
|
631
|
+
});
|
632
|
+
});
|
614
633
|
describe('#admit', () => {
|
615
634
|
it('should have #admit', () => {
|
616
635
|
assert.exists(meeting.admit);
|
@@ -1204,6 +1223,46 @@ describe('plugin-meetings', () => {
|
|
1204
1223
|
});
|
1205
1224
|
});
|
1206
1225
|
|
1226
|
+
describe('#update spoken language', () => {
|
1227
|
+
beforeEach(() => {
|
1228
|
+
webex.internal.voicea.onSpokenLanguageUpdate = sinon.stub();
|
1229
|
+
meeting.transcription = {languageOptions: {currentSpokenLanguage: 'en'}};
|
1230
|
+
});
|
1231
|
+
afterEach(() => {
|
1232
|
+
// Restore the original methods after each test
|
1233
|
+
sinon.restore();
|
1234
|
+
});
|
1235
|
+
it('should call voicea.onSpokenLanguageUpdate when joined', async () => {
|
1236
|
+
|
1237
|
+
meeting.joinedWith = {state: 'JOINED'};
|
1238
|
+
await meeting.locusInfo.emitScoped(
|
1239
|
+
{function: 'test', file: 'test'},
|
1240
|
+
LOCUSINFO.EVENTS.CONTROLS_MEETING_TRANSCRIPTION_SPOKEN_LANGUAGE_UPDATED,
|
1241
|
+
{spokenLanguage: 'fr'},
|
1242
|
+
);
|
1243
|
+
assert.calledWith(webex.internal.voicea.onSpokenLanguageUpdate, 'fr');
|
1244
|
+
assert.equal(meeting.transcription.languageOptions.currentSpokenLanguage, 'fr');
|
1245
|
+
assert.calledWith(
|
1246
|
+
TriggerProxy.trigger,
|
1247
|
+
meeting,
|
1248
|
+
{file: 'meeting/index', function: 'setupLocusControlsListener'},
|
1249
|
+
EVENT_TRIGGERS.MEETING_TRANSCRIPTION_SPOKEN_LANGUAGE_UPDATED
|
1250
|
+
);
|
1251
|
+
});
|
1252
|
+
|
1253
|
+
it('should also call voicea.onSpokenLanguageUpdate when not joined', async () => {
|
1254
|
+
|
1255
|
+
meeting.joinedWith = {state: 'NOT_JOINED'};
|
1256
|
+
await meeting.locusInfo.emitScoped(
|
1257
|
+
{function: 'test', file: 'test'},
|
1258
|
+
LOCUSINFO.EVENTS.CONTROLS_MEETING_TRANSCRIPTION_SPOKEN_LANGUAGE_UPDATED,
|
1259
|
+
{spokenLanguage: 'de'},
|
1260
|
+
);
|
1261
|
+
assert.calledWith(webex.internal.voicea.onSpokenLanguageUpdate, 'de');
|
1262
|
+
assert.equal(meeting.transcription.languageOptions.currentSpokenLanguage, 'de');
|
1263
|
+
});
|
1264
|
+
});
|
1265
|
+
|
1207
1266
|
describe('#startTranscription', () => {
|
1208
1267
|
beforeEach(() => {
|
1209
1268
|
webex.internal.voicea.on = sinon.stub();
|
@@ -2044,7 +2103,12 @@ describe('plugin-meetings', () => {
|
|
2044
2103
|
meeting.mediaProperties.waitForMediaConnectionConnected = sinon.stub().resolves();
|
2045
2104
|
meeting.mediaProperties.getCurrentConnectionInfo = sinon
|
2046
2105
|
.stub()
|
2047
|
-
.resolves({
|
2106
|
+
.resolves({
|
2107
|
+
connectionType: 'udp',
|
2108
|
+
selectedCandidatePairChanges: 2,
|
2109
|
+
numTransports: 1,
|
2110
|
+
ipVersion: 'IPv6',
|
2111
|
+
});
|
2048
2112
|
meeting.audio = muteStateStub;
|
2049
2113
|
meeting.video = muteStateStub;
|
2050
2114
|
sinon.stub(Media, 'createMediaConnection').returns(fakeMediaConnection);
|
@@ -2107,6 +2171,7 @@ describe('plugin-meetings', () => {
|
|
2107
2171
|
someReachabilityMetric2: 'some value2',
|
2108
2172
|
}),
|
2109
2173
|
stopReachability: sinon.stub(),
|
2174
|
+
isSubnetReachable: sinon.stub().returns(false),
|
2110
2175
|
};
|
2111
2176
|
|
2112
2177
|
const forceRtcMetricsSend = sinon.stub().resolves();
|
@@ -2162,6 +2227,8 @@ describe('plugin-meetings', () => {
|
|
2162
2227
|
someReachabilityMetric1: 'some value1',
|
2163
2228
|
someReachabilityMetric2: 'some value2',
|
2164
2229
|
selectedCandidatePairChanges: 2,
|
2230
|
+
isSubnetReachable: null,
|
2231
|
+
selectedCluster: null,
|
2165
2232
|
numTransports: 1,
|
2166
2233
|
iceCandidatesCount: 0,
|
2167
2234
|
}
|
@@ -2208,6 +2275,8 @@ describe('plugin-meetings', () => {
|
|
2208
2275
|
signalingState: 'unknown',
|
2209
2276
|
connectionState: 'unknown',
|
2210
2277
|
iceConnectionState: 'unknown',
|
2278
|
+
isSubnetReachable: null,
|
2279
|
+
selectedCluster: null,
|
2211
2280
|
})
|
2212
2281
|
);
|
2213
2282
|
|
@@ -2222,6 +2291,7 @@ describe('plugin-meetings', () => {
|
|
2222
2291
|
someReachabilityMetric1: 'some value1',
|
2223
2292
|
someReachabilityMetric2: 'some value2',
|
2224
2293
|
}),
|
2294
|
+
isSubnetReachable: sinon.stub().returns(true),
|
2225
2295
|
};
|
2226
2296
|
|
2227
2297
|
meeting.waitForRemoteSDPAnswer = sinon.stub().rejects();
|
@@ -2272,6 +2342,8 @@ describe('plugin-meetings', () => {
|
|
2272
2342
|
selectedCandidatePairChanges: 2,
|
2273
2343
|
numTransports: 1,
|
2274
2344
|
iceCandidatesCount: 0,
|
2345
|
+
isSubnetReachable: null,
|
2346
|
+
selectedCluster: null,
|
2275
2347
|
}
|
2276
2348
|
);
|
2277
2349
|
});
|
@@ -2329,6 +2401,8 @@ describe('plugin-meetings', () => {
|
|
2329
2401
|
signalingState: 'have-local-offer',
|
2330
2402
|
connectionState: 'connecting',
|
2331
2403
|
iceConnectionState: 'checking',
|
2404
|
+
isSubnetReachable: null,
|
2405
|
+
selectedCluster: null,
|
2332
2406
|
})
|
2333
2407
|
);
|
2334
2408
|
|
@@ -2386,6 +2460,8 @@ describe('plugin-meetings', () => {
|
|
2386
2460
|
signalingState: 'have-local-offer',
|
2387
2461
|
connectionState: 'connecting',
|
2388
2462
|
iceConnectionState: 'checking',
|
2463
|
+
isSubnetReachable: null,
|
2464
|
+
selectedCluster: null,
|
2389
2465
|
})
|
2390
2466
|
);
|
2391
2467
|
|
@@ -2664,7 +2740,7 @@ describe('plugin-meetings', () => {
|
|
2664
2740
|
|
2665
2741
|
meeting.roap.doTurnDiscovery = sinon.stub().resolves({
|
2666
2742
|
turnServerInfo: {
|
2667
|
-
|
2743
|
+
urls: [FAKE_TURN_URL],
|
2668
2744
|
username: FAKE_TURN_USER,
|
2669
2745
|
password: FAKE_TURN_PASSWORD,
|
2670
2746
|
},
|
@@ -2686,7 +2762,7 @@ describe('plugin-meetings', () => {
|
|
2686
2762
|
meeting.id,
|
2687
2763
|
sinon.match({
|
2688
2764
|
turnServerInfo: {
|
2689
|
-
|
2765
|
+
urls: [FAKE_TURN_URL],
|
2690
2766
|
username: FAKE_TURN_USER,
|
2691
2767
|
password: FAKE_TURN_PASSWORD,
|
2692
2768
|
},
|
@@ -2721,8 +2797,9 @@ describe('plugin-meetings', () => {
|
|
2721
2797
|
sinon.stub().returns(FAKE_ERROR));
|
2722
2798
|
webex.meetings.reachability = {
|
2723
2799
|
isWebexMediaBackendUnreachable: sinon.stub().resolves(false),
|
2724
|
-
getReachabilityMetrics: sinon.stub().resolves(),
|
2800
|
+
getReachabilityMetrics: sinon.stub().resolves({}),
|
2725
2801
|
stopReachability: sinon.stub(),
|
2802
|
+
isSubnetReachable: sinon.stub().returns(true),
|
2726
2803
|
};
|
2727
2804
|
const MOCK_CLIENT_ERROR_CODE = 2004;
|
2728
2805
|
const generateClientErrorCodeForIceFailureStub = sinon
|
@@ -2744,14 +2821,15 @@ describe('plugin-meetings', () => {
|
|
2744
2821
|
.onSecondCall()
|
2745
2822
|
.returns({
|
2746
2823
|
turnServerInfo: {
|
2747
|
-
|
2824
|
+
urls: [FAKE_TURN_URL],
|
2748
2825
|
username: FAKE_TURN_USER,
|
2749
2826
|
password: FAKE_TURN_PASSWORD,
|
2750
2827
|
},
|
2751
2828
|
turnDiscoverySkippedReason: undefined,
|
2752
2829
|
});
|
2753
2830
|
meeting.meetingState = 'ACTIVE';
|
2754
|
-
|
2831
|
+
const error = {iceConnected: false};
|
2832
|
+
meeting.mediaProperties.waitForMediaConnectionConnected.rejects(error);
|
2755
2833
|
|
2756
2834
|
const forceRtcMetricsSend = sinon.stub().resolves();
|
2757
2835
|
const closeMediaConnectionStub = sinon.stub();
|
@@ -2769,6 +2847,7 @@ describe('plugin-meetings', () => {
|
|
2769
2847
|
})
|
2770
2848
|
.catch((err) => {
|
2771
2849
|
errorThrown = err;
|
2850
|
+
assert.instanceOf(err.cause, Error);
|
2772
2851
|
assert.instanceOf(err, AddMediaFailed);
|
2773
2852
|
});
|
2774
2853
|
|
@@ -2825,6 +2904,7 @@ describe('plugin-meetings', () => {
|
|
2825
2904
|
},
|
2826
2905
|
options: {
|
2827
2906
|
meetingId: meeting.id,
|
2907
|
+
rawError: error,
|
2828
2908
|
},
|
2829
2909
|
});
|
2830
2910
|
assert.calledWith(webex.internal.newMetrics.submitClientEvent.thirdCall, {
|
@@ -2836,6 +2916,7 @@ describe('plugin-meetings', () => {
|
|
2836
2916
|
},
|
2837
2917
|
options: {
|
2838
2918
|
meetingId: meeting.id,
|
2919
|
+
rawError: error,
|
2839
2920
|
},
|
2840
2921
|
});
|
2841
2922
|
|
@@ -2902,6 +2983,8 @@ describe('plugin-meetings', () => {
|
|
2902
2983
|
selectedCandidatePairChanges: 2,
|
2903
2984
|
numTransports: 1,
|
2904
2985
|
iceCandidatesCount: 0,
|
2986
|
+
isSubnetReachable: null,
|
2987
|
+
selectedCluster: null,
|
2905
2988
|
},
|
2906
2989
|
]);
|
2907
2990
|
|
@@ -2932,6 +3015,7 @@ describe('plugin-meetings', () => {
|
|
2932
3015
|
.resolves(false),
|
2933
3016
|
getReachabilityMetrics: sinon.stub().resolves({}),
|
2934
3017
|
stopReachability: sinon.stub(),
|
3018
|
+
isSubnetReachable: sinon.stub().returns(true),
|
2935
3019
|
};
|
2936
3020
|
const getErrorPayloadForClientErrorCodeStub =
|
2937
3021
|
(webex.internal.newMetrics.callDiagnosticMetrics.getErrorPayloadForClientErrorCode =
|
@@ -2956,16 +3040,19 @@ describe('plugin-meetings', () => {
|
|
2956
3040
|
.onSecondCall()
|
2957
3041
|
.returns({
|
2958
3042
|
turnServerInfo: {
|
2959
|
-
|
3043
|
+
urls: [FAKE_TURN_URL],
|
2960
3044
|
username: FAKE_TURN_USER,
|
2961
3045
|
password: FAKE_TURN_PASSWORD,
|
2962
3046
|
},
|
2963
3047
|
turnDiscoverySkippedReason: undefined,
|
2964
3048
|
});
|
3049
|
+
|
3050
|
+
const mediaConnectionError = new Error('fake error');
|
3051
|
+
|
2965
3052
|
meeting.mediaProperties.waitForMediaConnectionConnected = sinon
|
2966
3053
|
.stub()
|
2967
3054
|
.onFirstCall()
|
2968
|
-
.rejects()
|
3055
|
+
.rejects(mediaConnectionError)
|
2969
3056
|
.onSecondCall()
|
2970
3057
|
.resolves();
|
2971
3058
|
|
@@ -3034,10 +3121,14 @@ describe('plugin-meetings', () => {
|
|
3034
3121
|
},
|
3035
3122
|
options: {
|
3036
3123
|
meetingId: meeting.id,
|
3124
|
+
rawError: mediaConnectionError,
|
3037
3125
|
},
|
3038
3126
|
});
|
3039
3127
|
assert.calledWith(webex.internal.newMetrics.submitClientEvent.thirdCall, {
|
3040
3128
|
name: 'client.media-engine.ready',
|
3129
|
+
payload: {
|
3130
|
+
ipVersion: 'IPv6',
|
3131
|
+
},
|
3041
3132
|
options: {
|
3042
3133
|
meetingId: meeting.id,
|
3043
3134
|
},
|
@@ -3094,11 +3185,14 @@ describe('plugin-meetings', () => {
|
|
3094
3185
|
locus_id: meeting.locusUrl.split('/').pop(),
|
3095
3186
|
connectionType: 'udp',
|
3096
3187
|
selectedCandidatePairChanges: 2,
|
3188
|
+
ipVersion: 'IPv6',
|
3097
3189
|
numTransports: 1,
|
3098
3190
|
isMultistream: false,
|
3099
3191
|
retriedWithTurnServer: true,
|
3100
3192
|
isJoinWithMediaRetry: false,
|
3101
3193
|
iceCandidatesCount: 0,
|
3194
|
+
isSubnetReachable: null,
|
3195
|
+
selectedCluster: null,
|
3102
3196
|
},
|
3103
3197
|
]);
|
3104
3198
|
meeting.roap.doTurnDiscovery;
|
@@ -3133,7 +3227,7 @@ describe('plugin-meetings', () => {
|
|
3133
3227
|
.onSecondCall()
|
3134
3228
|
.returns({
|
3135
3229
|
turnServerInfo: {
|
3136
|
-
|
3230
|
+
urls: [FAKE_TURN_URL],
|
3137
3231
|
username: FAKE_TURN_USER,
|
3138
3232
|
password: FAKE_TURN_PASSWORD,
|
3139
3233
|
},
|
@@ -3185,7 +3279,7 @@ describe('plugin-meetings', () => {
|
|
3185
3279
|
.onSecondCall()
|
3186
3280
|
.returns({
|
3187
3281
|
turnServerInfo: {
|
3188
|
-
|
3282
|
+
urls: [FAKE_TURN_URL],
|
3189
3283
|
username: FAKE_TURN_USER,
|
3190
3284
|
password: FAKE_TURN_PASSWORD,
|
3191
3285
|
},
|
@@ -3227,7 +3321,13 @@ describe('plugin-meetings', () => {
|
|
3227
3321
|
someReachabilityMetric2: 'some value2',
|
3228
3322
|
}),
|
3229
3323
|
stopReachability: sinon.stub(),
|
3324
|
+
isSubnetReachable: sinon.stub().returns(true),
|
3230
3325
|
};
|
3326
|
+
meeting.mediaConnections = [
|
3327
|
+
{
|
3328
|
+
mediaAgentCluster: 'some.cluster',
|
3329
|
+
}
|
3330
|
+
]
|
3231
3331
|
meeting.iceCandidatesCount = 3;
|
3232
3332
|
meeting.iceCandidateErrors.set('701_error', 3);
|
3233
3333
|
meeting.iceCandidateErrors.set('701_turn_host_lookup_received_error', 1);
|
@@ -3245,6 +3345,7 @@ describe('plugin-meetings', () => {
|
|
3245
3345
|
locus_id: meeting.locusUrl.split('/').pop(),
|
3246
3346
|
connectionType: 'udp',
|
3247
3347
|
selectedCandidatePairChanges: 2,
|
3348
|
+
ipVersion: 'IPv6',
|
3248
3349
|
numTransports: 1,
|
3249
3350
|
isMultistream: false,
|
3250
3351
|
retriedWithTurnServer: false,
|
@@ -3254,6 +3355,8 @@ describe('plugin-meetings', () => {
|
|
3254
3355
|
iceCandidatesCount: 3,
|
3255
3356
|
'701_error': 3,
|
3256
3357
|
'701_turn_host_lookup_received_error': 1,
|
3358
|
+
isSubnetReachable: null,
|
3359
|
+
selectedCluster: 'some.cluster',
|
3257
3360
|
}
|
3258
3361
|
);
|
3259
3362
|
|
@@ -3316,6 +3419,8 @@ describe('plugin-meetings', () => {
|
|
3316
3419
|
iceConnectionState: 'unknown',
|
3317
3420
|
selectedCandidatePairChanges: 2,
|
3318
3421
|
numTransports: 1,
|
3422
|
+
isSubnetReachable: null,
|
3423
|
+
selectedCluster: null,
|
3319
3424
|
iceCandidatesCount: 0,
|
3320
3425
|
}
|
3321
3426
|
);
|
@@ -3377,6 +3482,120 @@ describe('plugin-meetings', () => {
|
|
3377
3482
|
numTransports: 1,
|
3378
3483
|
'701_error': 2,
|
3379
3484
|
'701_turn_host_lookup_received_error': 1,
|
3485
|
+
isSubnetReachable: null,
|
3486
|
+
selectedCluster: null,
|
3487
|
+
iceCandidatesCount: 0,
|
3488
|
+
}
|
3489
|
+
);
|
3490
|
+
|
3491
|
+
assert.isOk(errorThrown);
|
3492
|
+
});
|
3493
|
+
|
3494
|
+
it('should send valid isSubnetReachability if media connection success', async () => {
|
3495
|
+
meeting.roap.doTurnDiscovery = sinon.stub().returns({
|
3496
|
+
turnServerInfo: undefined,
|
3497
|
+
turnDiscoverySkippedReason: undefined,
|
3498
|
+
});
|
3499
|
+
meeting.meetingState = 'ACTIVE';
|
3500
|
+
meeting.mediaProperties.waitForMediaConnectionConnected.resolves();
|
3501
|
+
meeting.webex.meetings.reachability = {
|
3502
|
+
getReachabilityMetrics: sinon.stub().resolves({
|
3503
|
+
reachability_public_udp_success: 5,
|
3504
|
+
}),
|
3505
|
+
stopReachability: sinon.stub(),
|
3506
|
+
isSubnetReachable: sinon.stub().returns(false),
|
3507
|
+
};
|
3508
|
+
|
3509
|
+
const forceRtcMetricsSend = sinon.stub().resolves();
|
3510
|
+
const closeMediaConnectionStub = sinon.stub();
|
3511
|
+
Media.createMediaConnection = sinon.stub().returns({
|
3512
|
+
close: closeMediaConnectionStub,
|
3513
|
+
forceRtcMetricsSend,
|
3514
|
+
getConnectionState: sinon.stub().returns(ConnectionState.Connected),
|
3515
|
+
initiateOffer: sinon.stub().resolves({}),
|
3516
|
+
on: sinon.stub(),
|
3517
|
+
});
|
3518
|
+
|
3519
|
+
await meeting.addMedia({
|
3520
|
+
mediaSettings: {},
|
3521
|
+
});
|
3522
|
+
|
3523
|
+
assert.calledWith(Metrics.sendBehavioralMetric, BEHAVIORAL_METRICS.ADD_MEDIA_SUCCESS, {
|
3524
|
+
correlation_id: meeting.correlationId,
|
3525
|
+
locus_id: meeting.locusUrl.split('/').pop(),
|
3526
|
+
connectionType: 'udp',
|
3527
|
+
ipVersion: 'IPv6',
|
3528
|
+
selectedCandidatePairChanges: 2,
|
3529
|
+
numTransports: 1,
|
3530
|
+
isMultistream: false,
|
3531
|
+
retriedWithTurnServer: false,
|
3532
|
+
isJoinWithMediaRetry: false,
|
3533
|
+
iceCandidatesCount: 0,
|
3534
|
+
reachability_public_udp_success: 5,
|
3535
|
+
isSubnetReachable: false,
|
3536
|
+
selectedCluster: null,
|
3537
|
+
});
|
3538
|
+
});
|
3539
|
+
|
3540
|
+
it('should send valid isSubnetReachability if media connection fails', async () => {
|
3541
|
+
let errorThrown = undefined;
|
3542
|
+
|
3543
|
+
meeting.roap.doTurnDiscovery = sinon.stub().returns({
|
3544
|
+
turnServerInfo: undefined,
|
3545
|
+
turnDiscoverySkippedReason: undefined,
|
3546
|
+
});
|
3547
|
+
meeting.meetingState = 'ACTIVE';
|
3548
|
+
meeting.mediaProperties.waitForMediaConnectionConnected.rejects({iceConnected: false});
|
3549
|
+
meeting.webex.meetings.reachability = {
|
3550
|
+
getReachabilityMetrics: sinon.stub().resolves({
|
3551
|
+
reachability_public_udp_success: 5,
|
3552
|
+
}),
|
3553
|
+
stopReachability: sinon.stub(),
|
3554
|
+
isSubnetReachable: sinon.stub().returns(true),
|
3555
|
+
};
|
3556
|
+
|
3557
|
+
const forceRtcMetricsSend = sinon.stub().resolves();
|
3558
|
+
const closeMediaConnectionStub = sinon.stub();
|
3559
|
+
Media.createMediaConnection = sinon.stub().returns({
|
3560
|
+
close: closeMediaConnectionStub,
|
3561
|
+
forceRtcMetricsSend,
|
3562
|
+
getConnectionState: sinon.stub().returns(ConnectionState.Connected),
|
3563
|
+
initiateOffer: sinon.stub().resolves({}),
|
3564
|
+
on: sinon.stub(),
|
3565
|
+
});
|
3566
|
+
|
3567
|
+
await meeting
|
3568
|
+
.addMedia({
|
3569
|
+
mediaSettings: {},
|
3570
|
+
})
|
3571
|
+
.catch((err) => {
|
3572
|
+
errorThrown = err;
|
3573
|
+
assert.instanceOf(err, AddMediaFailed);
|
3574
|
+
});
|
3575
|
+
|
3576
|
+
// Check that the only metric sent is ADD_MEDIA_FAILURE
|
3577
|
+
assert.calledOnceWithExactly(
|
3578
|
+
Metrics.sendBehavioralMetric,
|
3579
|
+
BEHAVIORAL_METRICS.ADD_MEDIA_FAILURE,
|
3580
|
+
{
|
3581
|
+
correlation_id: meeting.correlationId,
|
3582
|
+
locus_id: meeting.locusUrl.split('/').pop(),
|
3583
|
+
reason: errorThrown.message,
|
3584
|
+
stack: errorThrown.stack,
|
3585
|
+
code: errorThrown.code,
|
3586
|
+
turnDiscoverySkippedReason: undefined,
|
3587
|
+
turnServerUsed: true,
|
3588
|
+
retriedWithTurnServer: false,
|
3589
|
+
isMultistream: false,
|
3590
|
+
isJoinWithMediaRetry: false,
|
3591
|
+
signalingState: 'unknown',
|
3592
|
+
connectionState: 'unknown',
|
3593
|
+
iceConnectionState: 'unknown',
|
3594
|
+
selectedCandidatePairChanges: 2,
|
3595
|
+
numTransports: 1,
|
3596
|
+
reachability_public_udp_success: 5,
|
3597
|
+
isSubnetReachable: true,
|
3598
|
+
selectedCluster: null,
|
3380
3599
|
iceCandidatesCount: 0,
|
3381
3600
|
}
|
3382
3601
|
);
|
@@ -3396,6 +3615,8 @@ describe('plugin-meetings', () => {
|
|
3396
3615
|
meeting.config.stats.enableStatsAnalyzer = true;
|
3397
3616
|
|
3398
3617
|
statsAnalyzerStub = new EventsScope();
|
3618
|
+
statsAnalyzerStub.getNetworkType = sinon.stub().returns('wifi');
|
3619
|
+
|
3399
3620
|
// mock the StatsAnalyzer constructor
|
3400
3621
|
sinon.stub(InternalMediaCoreModule, 'StatsAnalyzer').returns(statsAnalyzerStub);
|
3401
3622
|
|
@@ -3436,6 +3657,40 @@ describe('plugin-meetings', () => {
|
|
3436
3657
|
});
|
3437
3658
|
});
|
3438
3659
|
|
3660
|
+
it('LOCAL_MEDIA_STARTED triggers "meeting:media:local:start" event and does not send metric because we already have', async () => {
|
3661
|
+
meeting.shareCAEventSentStatus = {
|
3662
|
+
transmitStart: true,
|
3663
|
+
transmitStop: false,
|
3664
|
+
receiveStart: false,
|
3665
|
+
receiveStop: false,
|
3666
|
+
};
|
3667
|
+
statsAnalyzerStub.emit(
|
3668
|
+
{file: 'test', function: 'test'},
|
3669
|
+
StatsAnalyzerEventNames.LOCAL_MEDIA_STARTED,
|
3670
|
+
{mediaType: 'share'}
|
3671
|
+
);
|
3672
|
+
|
3673
|
+
assert.calledWith(
|
3674
|
+
TriggerProxy.trigger,
|
3675
|
+
sinon.match.instanceOf(Meeting),
|
3676
|
+
{
|
3677
|
+
file: 'meeting/index',
|
3678
|
+
function: 'addMedia',
|
3679
|
+
},
|
3680
|
+
EVENT_TRIGGERS.MEETING_MEDIA_LOCAL_STARTED,
|
3681
|
+
{
|
3682
|
+
mediaType: 'share',
|
3683
|
+
}
|
3684
|
+
);
|
3685
|
+
assert.neverCalledWith(webex.internal.newMetrics.submitClientEvent, {
|
3686
|
+
name: 'client.media.tx.start',
|
3687
|
+
payload: {mediaType: 'share', shareInstanceId: meeting.remoteShareInstanceId},
|
3688
|
+
options: {
|
3689
|
+
meetingId: meeting.id,
|
3690
|
+
},
|
3691
|
+
});
|
3692
|
+
});
|
3693
|
+
|
3439
3694
|
it('LOCAL_MEDIA_STOPPED triggers the right metrics', async () => {
|
3440
3695
|
statsAnalyzerStub.emit(
|
3441
3696
|
{file: 'test', function: 'test'},
|
@@ -3452,6 +3707,28 @@ describe('plugin-meetings', () => {
|
|
3452
3707
|
});
|
3453
3708
|
});
|
3454
3709
|
|
3710
|
+
it('LOCAL_MEDIA_STOPPED does not send metric because we already have', async () => {
|
3711
|
+
meeting.shareCAEventSentStatus = {
|
3712
|
+
transmitStart: false,
|
3713
|
+
transmitStop: true,
|
3714
|
+
receiveStart: false,
|
3715
|
+
receiveStop: false,
|
3716
|
+
};
|
3717
|
+
statsAnalyzerStub.emit(
|
3718
|
+
{file: 'test', function: 'test'},
|
3719
|
+
StatsAnalyzerEventNames.LOCAL_MEDIA_STOPPED,
|
3720
|
+
{mediaType: 'share'}
|
3721
|
+
);
|
3722
|
+
|
3723
|
+
assert.neverCalledWith(webex.internal.newMetrics.submitClientEvent, {
|
3724
|
+
name: 'client.media.tx.stop',
|
3725
|
+
payload: {mediaType: 'share', shareInstanceId: meeting.remoteShareInstanceId},
|
3726
|
+
options: {
|
3727
|
+
meetingId: meeting.id,
|
3728
|
+
},
|
3729
|
+
});
|
3730
|
+
});
|
3731
|
+
|
3455
3732
|
it('REMOTE_MEDIA_STARTED triggers "meeting:media:remote:start" event and sends metrics', async () => {
|
3456
3733
|
statsAnalyzerStub.emit(
|
3457
3734
|
{file: 'test', function: 'test'},
|
@@ -3532,6 +3809,47 @@ describe('plugin-meetings', () => {
|
|
3532
3809
|
});
|
3533
3810
|
});
|
3534
3811
|
|
3812
|
+
it('REMOTE_MEDIA_STARTED triggers "meeting:media:remote:start" event and does not send metric because we already have', async () => {
|
3813
|
+
meeting.shareCAEventSentStatus = {
|
3814
|
+
transmitStart: false,
|
3815
|
+
transmitStop: false,
|
3816
|
+
receiveStart: true,
|
3817
|
+
receiveStop: false,
|
3818
|
+
};
|
3819
|
+
statsAnalyzerStub.emit(
|
3820
|
+
{file: 'test', function: 'test'},
|
3821
|
+
StatsAnalyzerEventNames.REMOTE_MEDIA_STARTED,
|
3822
|
+
{mediaType: 'share'}
|
3823
|
+
);
|
3824
|
+
|
3825
|
+
assert.calledWith(
|
3826
|
+
TriggerProxy.trigger,
|
3827
|
+
sinon.match.instanceOf(Meeting),
|
3828
|
+
{
|
3829
|
+
file: 'meeting/index',
|
3830
|
+
function: 'addMedia',
|
3831
|
+
},
|
3832
|
+
EVENT_TRIGGERS.MEETING_MEDIA_REMOTE_STARTED,
|
3833
|
+
{
|
3834
|
+
mediaType: 'share',
|
3835
|
+
}
|
3836
|
+
);
|
3837
|
+
assert.neverCalledWith(webex.internal.newMetrics.submitClientEvent, {
|
3838
|
+
name: 'client.media.render.start',
|
3839
|
+
payload: {mediaType: 'share', shareInstanceId: meeting.remoteShareInstanceId},
|
3840
|
+
options: {
|
3841
|
+
meetingId: meeting.id,
|
3842
|
+
},
|
3843
|
+
});
|
3844
|
+
assert.neverCalledWith(webex.internal.newMetrics.submitClientEvent, {
|
3845
|
+
name: 'client.media.rx.start',
|
3846
|
+
payload: {mediaType: 'share', shareInstanceId: meeting.remoteShareInstanceId},
|
3847
|
+
options: {
|
3848
|
+
meetingId: meeting.id,
|
3849
|
+
},
|
3850
|
+
});
|
3851
|
+
});
|
3852
|
+
|
3535
3853
|
it('REMOTE_MEDIA_STOPPED triggers the right metrics for share', async () => {
|
3536
3854
|
statsAnalyzerStub.emit(
|
3537
3855
|
{file: 'test', function: 'test'},
|
@@ -3556,6 +3874,34 @@ describe('plugin-meetings', () => {
|
|
3556
3874
|
});
|
3557
3875
|
});
|
3558
3876
|
|
3877
|
+
it('REMOTE_MEDIA_STOPPED does not send metric because we already have', async () => {
|
3878
|
+
meeting.shareCAEventSentStatus = {
|
3879
|
+
transmitStart: false,
|
3880
|
+
transmitStop: false,
|
3881
|
+
receiveStart: true,
|
3882
|
+
receiveStop: true,
|
3883
|
+
};
|
3884
|
+
statsAnalyzerStub.emit(
|
3885
|
+
{file: 'test', function: 'test'},
|
3886
|
+
StatsAnalyzerEventNames.REMOTE_MEDIA_STOPPED,
|
3887
|
+
{mediaType: 'share'}
|
3888
|
+
);
|
3889
|
+
assert.neverCalledWith(webex.internal.newMetrics.submitClientEvent, {
|
3890
|
+
name: 'client.media.render.stop',
|
3891
|
+
payload: {mediaType: 'share', shareInstanceId: meeting.remoteShareInstanceId},
|
3892
|
+
options: {
|
3893
|
+
meetingId: meeting.id,
|
3894
|
+
},
|
3895
|
+
});
|
3896
|
+
assert.neverCalledWith(webex.internal.newMetrics.submitClientEvent, {
|
3897
|
+
name: 'client.media.rx.stop',
|
3898
|
+
payload: {mediaType: 'share', shareInstanceId: meeting.remoteShareInstanceId},
|
3899
|
+
options: {
|
3900
|
+
meetingId: meeting.id,
|
3901
|
+
},
|
3902
|
+
});
|
3903
|
+
});
|
3904
|
+
|
3559
3905
|
it('counts the number of members that are in the meeting for MEDIA_QUALITY event', async () => {
|
3560
3906
|
let fakeMembersCollection = {
|
3561
3907
|
members: {
|
@@ -3565,7 +3911,7 @@ describe('plugin-meetings', () => {
|
|
3565
3911
|
},
|
3566
3912
|
};
|
3567
3913
|
sinon.stub(meeting, 'getMembers').returns({membersCollection: fakeMembersCollection});
|
3568
|
-
const fakeData = {intervalMetadata: {}
|
3914
|
+
const fakeData = {intervalMetadata: {}};
|
3569
3915
|
|
3570
3916
|
statsAnalyzerStub.emit(
|
3571
3917
|
{file: 'test', function: 'test'},
|
@@ -3606,7 +3952,7 @@ describe('plugin-meetings', () => {
|
|
3606
3952
|
});
|
3607
3953
|
|
3608
3954
|
it('calls submitMQE correctly', async () => {
|
3609
|
-
const fakeData = {intervalMetadata: {bla: 'bla'}
|
3955
|
+
const fakeData = {intervalMetadata: {bla: 'bla'}};
|
3610
3956
|
|
3611
3957
|
statsAnalyzerStub.emit(
|
3612
3958
|
{file: 'test', function: 'test'},
|
@@ -3637,7 +3983,7 @@ describe('plugin-meetings', () => {
|
|
3637
3983
|
|
3638
3984
|
meeting.roap.doTurnDiscovery = sinon.stub().resolves({
|
3639
3985
|
turnServerInfo: {
|
3640
|
-
|
3986
|
+
urls: [FAKE_TURN_URL],
|
3641
3987
|
username: FAKE_TURN_USER,
|
3642
3988
|
password: FAKE_TURN_PASSWORD,
|
3643
3989
|
},
|
@@ -3663,7 +4009,7 @@ describe('plugin-meetings', () => {
|
|
3663
4009
|
meeting.id,
|
3664
4010
|
sinon.match({
|
3665
4011
|
turnServerInfo: {
|
3666
|
-
|
4012
|
+
urls: [FAKE_TURN_URL],
|
3667
4013
|
username: FAKE_TURN_USER,
|
3668
4014
|
password: FAKE_TURN_PASSWORD,
|
3669
4015
|
},
|
@@ -3814,6 +4160,9 @@ describe('plugin-meetings', () => {
|
|
3814
4160
|
},
|
3815
4161
|
options: {
|
3816
4162
|
meetingId: meeting.id,
|
4163
|
+
rawError: {
|
4164
|
+
iceConnected: false,
|
4165
|
+
},
|
3817
4166
|
},
|
3818
4167
|
},
|
3819
4168
|
]);
|
@@ -3843,7 +4192,7 @@ describe('plugin-meetings', () => {
|
|
3843
4192
|
meeting.deviceUrl = 'device url';
|
3844
4193
|
meeting.selfId = 'self id';
|
3845
4194
|
meeting.brbState = createBrbState(meeting, false);
|
3846
|
-
meeting.brbState
|
4195
|
+
sinon.stub(meeting.brbState, 'enable').resolves();
|
3847
4196
|
});
|
3848
4197
|
|
3849
4198
|
afterEach(() => {
|
@@ -3884,9 +4233,42 @@ describe('plugin-meetings', () => {
|
|
3884
4233
|
} catch (err) {
|
3885
4234
|
assert.instanceOf(err, Error);
|
3886
4235
|
assert.equal(err.message, 'setBrb failed');
|
3887
|
-
assert.isRejected(
|
4236
|
+
assert.isRejected(Promise.reject());
|
3888
4237
|
}
|
3889
4238
|
});
|
4239
|
+
|
4240
|
+
it('updates remote mute state when brb is enabled', async () => {
|
4241
|
+
meeting.audio = {handleServerRemoteMuteUpdate: sinon.stub()};
|
4242
|
+
|
4243
|
+
await meeting.beRightBack(true);
|
4244
|
+
|
4245
|
+
sinon.assert.calledOnceWithExactly(
|
4246
|
+
meeting.audio.handleServerRemoteMuteUpdate,
|
4247
|
+
meeting,
|
4248
|
+
true
|
4249
|
+
);
|
4250
|
+
});
|
4251
|
+
|
4252
|
+
it('does not update remote mute state when brb is disabled', async () => {
|
4253
|
+
meeting.audio = {handleServerRemoteMuteUpdate: sinon.stub()};
|
4254
|
+
|
4255
|
+
await meeting.beRightBack(false);
|
4256
|
+
|
4257
|
+
assert.notCalled(meeting.audio.handleServerRemoteMuteUpdate);
|
4258
|
+
});
|
4259
|
+
|
4260
|
+
it('should reject when brb enable fails', async () => {
|
4261
|
+
meeting.brbState.enable.restore();
|
4262
|
+
|
4263
|
+
const error = new Error();
|
4264
|
+
meeting.meetingRequest.setBrb = sinon.stub().rejects(error);
|
4265
|
+
|
4266
|
+
await expect(
|
4267
|
+
meeting.beRightBack(true)
|
4268
|
+
).to.be.rejectedWith(error);
|
4269
|
+
|
4270
|
+
assert.isFalse(meeting.brbState.state.syncToServerInProgress);
|
4271
|
+
});
|
3890
4272
|
});
|
3891
4273
|
});
|
3892
4274
|
|
@@ -3940,7 +4322,10 @@ describe('plugin-meetings', () => {
|
|
3940
4322
|
.resolves({id: 'fake clientMediaPreferences'});
|
3941
4323
|
meeting.roap.doTurnDiscovery = sinon.stub().resolves({
|
3942
4324
|
turnServerInfo: {
|
3943
|
-
|
4325
|
+
urls: [
|
4326
|
+
'turns:turn-server-url1:443?transport=tcp',
|
4327
|
+
'turns:turn-server-url2:443?transport=tcp',
|
4328
|
+
],
|
3944
4329
|
username: 'turn user',
|
3945
4330
|
password: 'turn password',
|
3946
4331
|
},
|
@@ -3958,12 +4343,10 @@ describe('plugin-meetings', () => {
|
|
3958
4343
|
expectedMediaConnectionConfig = {
|
3959
4344
|
iceServers: [
|
3960
4345
|
{
|
3961
|
-
urls:
|
3962
|
-
|
3963
|
-
|
3964
|
-
|
3965
|
-
{
|
3966
|
-
urls: 'turns:turn-server-url:443?transport=tcp',
|
4346
|
+
urls: [
|
4347
|
+
'turns:turn-server-url1:443?transport=tcp',
|
4348
|
+
'turns:turn-server-url2:443?transport=tcp',
|
4349
|
+
],
|
3967
4350
|
username: 'turn user',
|
3968
4351
|
credential: 'turn password',
|
3969
4352
|
},
|
@@ -4045,9 +4428,11 @@ describe('plugin-meetings', () => {
|
|
4045
4428
|
.stub(InternalMediaCoreModule, 'MultistreamRoapMediaConnection')
|
4046
4429
|
.returns(fakeMultistreamRoapMediaConnection);
|
4047
4430
|
|
4048
|
-
locusMediaRequestStub = sinon
|
4049
|
-
|
4050
|
-
|
4431
|
+
locusMediaRequestStub = sinon.stub(WebexPlugin.prototype, 'request').resolves({
|
4432
|
+
body: {locus: {fullState: {}}},
|
4433
|
+
upload: sinon.match.instanceOf(EventEmitter),
|
4434
|
+
download: sinon.match.instanceOf(EventEmitter),
|
4435
|
+
});
|
4051
4436
|
|
4052
4437
|
// setup some things and mocks so that the call to join() works
|
4053
4438
|
// (we need to call join() because it creates the LocusMediaRequest instance
|
@@ -5231,7 +5616,10 @@ describe('plugin-meetings', () => {
|
|
5231
5616
|
// and check that when we fallback to transcoded we still do another TURN discovery
|
5232
5617
|
await runCheck(
|
5233
5618
|
{
|
5234
|
-
|
5619
|
+
urls: [
|
5620
|
+
'turns:turn-server-url1:443?transport=tcp',
|
5621
|
+
'turns:turn-server-url2:443?transport=tcp',
|
5622
|
+
],
|
5235
5623
|
username: 'turn user',
|
5236
5624
|
password: 'turn password',
|
5237
5625
|
},
|
@@ -5245,7 +5633,10 @@ describe('plugin-meetings', () => {
|
|
5245
5633
|
// but doing it just for completeness
|
5246
5634
|
await runCheck(
|
5247
5635
|
{
|
5248
|
-
|
5636
|
+
urls: [
|
5637
|
+
'turns:turn-server-url1:443?transport=tcp',
|
5638
|
+
'turns:turn-server-url2:443?transport=tcp',
|
5639
|
+
],
|
5249
5640
|
username: 'turn user',
|
5250
5641
|
password: 'turn password',
|
5251
5642
|
},
|
@@ -7527,19 +7918,19 @@ describe('plugin-meetings', () => {
|
|
7527
7918
|
});
|
7528
7919
|
});
|
7529
7920
|
|
7530
|
-
describe('#setIsoLocalClientMeetingJoinTime', () => {
|
7921
|
+
describe('#setIsoLocalClientMeetingJoinTime', () => {
|
7531
7922
|
it('should fallback to system clock ISO string when given an undefined value', () => {
|
7532
7923
|
const currentSystemTime = new Date().toISOString();
|
7533
7924
|
meeting.isoLocalClientMeetingJoinTime = undefined;
|
7534
7925
|
assert.equal(meeting.isoLocalClientMeetingJoinTime, currentSystemTime);
|
7535
7926
|
});
|
7536
|
-
|
7927
|
+
|
7537
7928
|
it('should fallback to system clock ISO string when given an invalid value', () => {
|
7538
7929
|
const currentSystemTime = new Date().toISOString();
|
7539
7930
|
meeting.isoLocalClientMeetingJoinTime = 'invalid-date';
|
7540
7931
|
assert.equal(meeting.isoLocalClientMeetingJoinTime, currentSystemTime);
|
7541
7932
|
});
|
7542
|
-
|
7933
|
+
|
7543
7934
|
it('should set the isoLocalClientMeetingJoinTime correctly for a valid date string', () => {
|
7544
7935
|
const validDateString = 'Tue, 01 Apr 2025 13:00:36 GMT';
|
7545
7936
|
const expectedISOString = new Date(validDateString).toISOString();
|
@@ -7629,6 +8020,12 @@ describe('plugin-meetings', () => {
|
|
7629
8020
|
meeting.audio = {handleLocalStreamChange: sinon.stub()};
|
7630
8021
|
meeting.video = {handleLocalStreamChange: sinon.stub()};
|
7631
8022
|
meeting.statsAnalyzer = {updateMediaStatus: sinon.stub()};
|
8023
|
+
meeting.shareCAEventSentStatus = {
|
8024
|
+
transmitStart: false,
|
8025
|
+
transmitStop: false,
|
8026
|
+
receiveStart: false,
|
8027
|
+
receiveStop: false,
|
8028
|
+
};
|
7632
8029
|
fakeMultistreamRoapMediaConnection = {
|
7633
8030
|
createSendSlot: () => {
|
7634
8031
|
return {
|
@@ -7696,6 +8093,9 @@ describe('plugin-meetings', () => {
|
|
7696
8093
|
});
|
7697
8094
|
assert.equal(meeting.mediaProperties.mediaDirection.sendShare, true);
|
7698
8095
|
|
8096
|
+
assert.equal(meeting.shareCAEventSentStatus.transmitStart, false);
|
8097
|
+
assert.equal(meeting.shareCAEventSentStatus.transmitStop, false);
|
8098
|
+
|
7699
8099
|
assert.calledWith(meeting.statsAnalyzer.updateMediaStatus, {
|
7700
8100
|
expected: {sendShare: true},
|
7701
8101
|
});
|
@@ -7716,18 +8116,23 @@ describe('plugin-meetings', () => {
|
|
7716
8116
|
assert.equal(meeting.mediaProperties.shareAudioStream, stream);
|
7717
8117
|
assert.equal(meeting.mediaProperties.mediaDirection.sendShare, true);
|
7718
8118
|
|
8119
|
+
assert.equal(meeting.shareCAEventSentStatus.transmitStart, false);
|
8120
|
+
assert.equal(meeting.shareCAEventSentStatus.transmitStop, false);
|
8121
|
+
|
7719
8122
|
assert.calledWith(meeting.statsAnalyzer.updateMediaStatus, {
|
7720
8123
|
expected: {sendShare: true},
|
7721
8124
|
});
|
7722
8125
|
};
|
7723
8126
|
|
7724
8127
|
it('requests screen share floor and publishes the screen share video stream', async () => {
|
8128
|
+
meeting.shareCAEventSentStatus.transmitStart = true;
|
7725
8129
|
await meeting.publishStreams({screenShare: {video: videoShareStream}});
|
7726
8130
|
|
7727
8131
|
checkScreenShareVideoPublished(videoShareStream);
|
7728
8132
|
});
|
7729
8133
|
|
7730
8134
|
it('requests screen share floor and publishes the screen share audio stream', async () => {
|
8135
|
+
meeting.shareCAEventSentStatus.transmitStart = true;
|
7731
8136
|
await meeting.publishStreams({screenShare: {audio: audioShareStream}});
|
7732
8137
|
|
7733
8138
|
checkScreenShareAudioPublished(audioShareStream);
|
@@ -8699,7 +9104,7 @@ describe('plugin-meetings', () => {
|
|
8699
9104
|
meeting.deferSDPAnswer = {
|
8700
9105
|
reject: sinon.stub(),
|
8701
9106
|
};
|
8702
|
-
|
9107
|
+
|
8703
9108
|
const clearTimeoutSpy = sinon.spy(clock, 'clearTimeout');
|
8704
9109
|
|
8705
9110
|
const fakeError = new Errors.SdpAnswerHandlingError(fakeErrorMessage, {
|
@@ -9623,6 +10028,42 @@ describe('plugin-meetings', () => {
|
|
9623
10028
|
);
|
9624
10029
|
});
|
9625
10030
|
|
10031
|
+
it('listens to CONTROLS_ANNOTATION_CHANGED', async () => {
|
10032
|
+
const state = {example: 'value'};
|
10033
|
+
|
10034
|
+
await meeting.locusInfo.emitScoped(
|
10035
|
+
{function: 'test', file: 'test'},
|
10036
|
+
LOCUSINFO.EVENTS.CONTROLS_ANNOTATION_CHANGED,
|
10037
|
+
{state}
|
10038
|
+
);
|
10039
|
+
|
10040
|
+
assert.calledWith(
|
10041
|
+
TriggerProxy.trigger,
|
10042
|
+
meeting,
|
10043
|
+
{file: 'meeting/index', function: 'setupLocusControlsListener'},
|
10044
|
+
EVENT_TRIGGERS.MEETING_CONTROLS_ANNOTATION_UPDATED,
|
10045
|
+
{state}
|
10046
|
+
);
|
10047
|
+
});
|
10048
|
+
|
10049
|
+
it('listens to CONTROLS_REMOTE_DESKTOP_CONTROL_CHANGED', async () => {
|
10050
|
+
const state = {example: 'value'};
|
10051
|
+
|
10052
|
+
await meeting.locusInfo.emitScoped(
|
10053
|
+
{function: 'test', file: 'test'},
|
10054
|
+
LOCUSINFO.EVENTS.CONTROLS_REMOTE_DESKTOP_CONTROL_CHANGED,
|
10055
|
+
{state}
|
10056
|
+
);
|
10057
|
+
|
10058
|
+
assert.calledWith(
|
10059
|
+
TriggerProxy.trigger,
|
10060
|
+
meeting,
|
10061
|
+
{file: 'meeting/index', function: 'setupLocusControlsListener'},
|
10062
|
+
EVENT_TRIGGERS.MEETING_CONTROLS_REMOTE_DESKTOP_CONTROL_UPDATED,
|
10063
|
+
{state}
|
10064
|
+
);
|
10065
|
+
});
|
10066
|
+
|
9626
10067
|
it('listens to the locus interpretation update event', () => {
|
9627
10068
|
const interpretation = {
|
9628
10069
|
siLanguages: [{languageCode: 20, languageName: 'en'}],
|
@@ -10518,9 +10959,11 @@ describe('plugin-meetings', () => {
|
|
10518
10959
|
let canUserLowerSomeoneElsesHandSpy;
|
10519
10960
|
let waitingForOthersToJoinSpy;
|
10520
10961
|
let canSendReactionsSpy;
|
10962
|
+
let requiresPostMeetingDataConsentPromptSpy;
|
10521
10963
|
let canUserRenameSelfAndObservedSpy;
|
10522
10964
|
let canUserRenameOthersSpy;
|
10523
10965
|
let canShareWhiteBoardSpy;
|
10966
|
+
let canMoveToLobbySpy;
|
10524
10967
|
// Due to import tree issues, hasHints must be stubed within the scope of the `it`.
|
10525
10968
|
|
10526
10969
|
beforeEach(() => {
|
@@ -10545,8 +10988,13 @@ describe('plugin-meetings', () => {
|
|
10545
10988
|
waitingForOthersToJoinSpy = sinon.spy(MeetingUtil, 'waitingForOthersToJoin');
|
10546
10989
|
canSendReactionsSpy = sinon.spy(MeetingUtil, 'canSendReactions');
|
10547
10990
|
canUserRenameSelfAndObservedSpy = sinon.spy(MeetingUtil, 'canUserRenameSelfAndObserved');
|
10991
|
+
requiresPostMeetingDataConsentPromptSpy = sinon.spy(
|
10992
|
+
MeetingUtil,
|
10993
|
+
'requiresPostMeetingDataConsentPrompt'
|
10994
|
+
);
|
10548
10995
|
canUserRenameOthersSpy = sinon.spy(MeetingUtil, 'canUserRenameOthers');
|
10549
10996
|
canShareWhiteBoardSpy = sinon.spy(MeetingUtil, 'canShareWhiteBoard');
|
10997
|
+
canMoveToLobbySpy = sinon.spy(MeetingUtil, 'canMoveToLobby');
|
10550
10998
|
});
|
10551
10999
|
|
10552
11000
|
afterEach(() => {
|
@@ -10644,6 +11092,16 @@ describe('plugin-meetings', () => {
|
|
10644
11092
|
requiredDisplayHints: [],
|
10645
11093
|
requiredPolicies: [SELF_POLICY.SUPPORT_FILE_TRANSFER],
|
10646
11094
|
},
|
11095
|
+
{
|
11096
|
+
actionName: 'canRealtimeCloseCaption',
|
11097
|
+
requiredDisplayHints: [],
|
11098
|
+
requiredPolicies: [SELF_POLICY.SUPPORT_REALTIME_CLOSE_CAPTION],
|
11099
|
+
},
|
11100
|
+
{
|
11101
|
+
actionName: 'canRealtimeCloseCaptionManual',
|
11102
|
+
requiredDisplayHints: [],
|
11103
|
+
requiredPolicies: [SELF_POLICY.SUPPORT_REALTIME_CLOSE_CAPTION_MANUAL],
|
11104
|
+
},
|
10647
11105
|
{
|
10648
11106
|
actionName: 'canChat',
|
10649
11107
|
requiredDisplayHints: [],
|
@@ -10673,6 +11131,11 @@ describe('plugin-meetings', () => {
|
|
10673
11131
|
requiredDisplayHints: [],
|
10674
11132
|
requiredPolicies: [SELF_POLICY.SUPPORT_POLLING_AND_QA],
|
10675
11133
|
},
|
11134
|
+
{
|
11135
|
+
actionName: 'canShareWhiteBoard',
|
11136
|
+
requiredDisplayHints: [DISPLAY_HINTS.SHARE_WHITEBOARD],
|
11137
|
+
requiredPolicies: [SELF_POLICY.SUPPORT_WHITEBOARD],
|
11138
|
+
},
|
10676
11139
|
],
|
10677
11140
|
({
|
10678
11141
|
actionName,
|
@@ -11080,8 +11543,10 @@ describe('plugin-meetings', () => {
|
|
11080
11543
|
assert.calledWith(waitingForOthersToJoinSpy, userDisplayHints);
|
11081
11544
|
assert.calledWith(canSendReactionsSpy, null, userDisplayHints);
|
11082
11545
|
assert.calledWith(canUserRenameSelfAndObservedSpy, userDisplayHints);
|
11546
|
+
assert.calledWith(requiresPostMeetingDataConsentPromptSpy, userDisplayHints);
|
11083
11547
|
assert.calledWith(canUserRenameOthersSpy, userDisplayHints);
|
11084
|
-
assert.calledWith(canShareWhiteBoardSpy, userDisplayHints);
|
11548
|
+
assert.calledWith(canShareWhiteBoardSpy, userDisplayHints, selfUserPolicies);
|
11549
|
+
assert.calledWith(canMoveToLobbySpy, userDisplayHints);
|
11085
11550
|
|
11086
11551
|
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
11087
11552
|
requiredHints: [DISPLAY_HINTS.MUTE_ALL],
|
@@ -11175,6 +11640,22 @@ describe('plugin-meetings', () => {
|
|
11175
11640
|
requiredPolicies: [SELF_POLICY.SUPPORT_VOIP],
|
11176
11641
|
policies: selfUserPolicies,
|
11177
11642
|
});
|
11643
|
+
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
11644
|
+
requiredHints: [DISPLAY_HINTS.ENABLE_ANNOTATION_MEETING_OPTION],
|
11645
|
+
displayHints: userDisplayHints,
|
11646
|
+
});
|
11647
|
+
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
11648
|
+
requiredHints: [DISPLAY_HINTS.DISABLE_ANNOTATION_MEETING_OPTION],
|
11649
|
+
displayHints: userDisplayHints,
|
11650
|
+
});
|
11651
|
+
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
11652
|
+
requiredHints: [DISPLAY_HINTS.ENABLE_RDC_MEETING_OPTION],
|
11653
|
+
displayHints: userDisplayHints,
|
11654
|
+
});
|
11655
|
+
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
11656
|
+
requiredHints: [DISPLAY_HINTS.DISABLE_RDC_MEETING_OPTION],
|
11657
|
+
displayHints: userDisplayHints,
|
11658
|
+
});
|
11178
11659
|
|
11179
11660
|
assert.calledWith(
|
11180
11661
|
TriggerProxy.trigger,
|
@@ -12021,6 +12502,8 @@ describe('plugin-meetings', () => {
|
|
12021
12502
|
// Set the webinar attendee flag
|
12022
12503
|
meeting.webinar = {selfIsAttendee: true};
|
12023
12504
|
meeting.locusInfo.info.isWebinar = true;
|
12505
|
+
meeting.shareCAEventSentStatus.receiveStart = true;
|
12506
|
+
meeting.shareCAEventSentStatus.receiveStop = true;
|
12024
12507
|
|
12025
12508
|
// Step 1: Start sharing whiteboard A
|
12026
12509
|
const data1 = generateData(
|
@@ -12044,6 +12527,8 @@ describe('plugin-meetings', () => {
|
|
12044
12527
|
|
12045
12528
|
// Specific assertions for webinar attendee status
|
12046
12529
|
assert.equal(meeting.shareStatus, SHARE_STATUS.REMOTE_SHARE_ACTIVE);
|
12530
|
+
assert.equal(meeting.shareCAEventSentStatus.receiveStart, false);
|
12531
|
+
assert.equal(meeting.shareCAEventSentStatus.receiveStop, false);
|
12047
12532
|
});
|
12048
12533
|
});
|
12049
12534
|
|
@@ -12891,6 +13376,38 @@ describe('plugin-meetings', () => {
|
|
12891
13376
|
});
|
12892
13377
|
});
|
12893
13378
|
|
13379
|
+
describe('#setPostMeetingDataConsent', () => {
|
13380
|
+
it('should have #setPostMeetingDataConsent', () => {
|
13381
|
+
assert.exists(meeting.setPostMeetingDataConsent);
|
13382
|
+
});
|
13383
|
+
|
13384
|
+
beforeEach(() => {
|
13385
|
+
meeting.meetingRequest.setPostMeetingDataConsent = sinon
|
13386
|
+
.stub()
|
13387
|
+
.returns(Promise.resolve());
|
13388
|
+
});
|
13389
|
+
|
13390
|
+
[true, false].forEach((accept) => {
|
13391
|
+
it(`should send consent with ${accept}`, async () => {
|
13392
|
+
const id = uuidv4();
|
13393
|
+
meeting.locusUrl = `https://locus-test.wbx2.com/locus/api/v1/loci/${accept}`;
|
13394
|
+
meeting.deviceUrl = `https://wdm-test.wbx2.com/wdm/api/v1/devices/${accept}`;
|
13395
|
+
meeting.members.selfId = id;
|
13396
|
+
|
13397
|
+
const consentPromise = meeting.setPostMeetingDataConsent(accept);
|
13398
|
+
|
13399
|
+
assert.exists(consentPromise.then);
|
13400
|
+
await consentPromise;
|
13401
|
+
assert.calledOnceWithExactly(meeting.meetingRequest.setPostMeetingDataConsent, {
|
13402
|
+
locusUrl: `https://locus-test.wbx2.com/locus/api/v1/loci/${accept}`,
|
13403
|
+
postMeetingDataConsent: accept,
|
13404
|
+
selfId: id,
|
13405
|
+
deviceUrl: `https://wdm-test.wbx2.com/wdm/api/v1/devices/${accept}`,
|
13406
|
+
});
|
13407
|
+
});
|
13408
|
+
});
|
13409
|
+
});
|
13410
|
+
|
12894
13411
|
describe('#sendReaction', () => {
|
12895
13412
|
it('should have #sendReaction', () => {
|
12896
13413
|
assert.exists(meeting.sendReaction);
|