@webex/plugin-meetings 3.8.0-next.6 → 3.8.0-next.61
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/webex-errors.js +12 -2
- package/dist/common/errors/webex-errors.js.map +1 -1
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/constants.js +17 -121
- 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 +28 -10
- package/dist/locus-info/controlsUtils.js.map +1 -1
- package/dist/locus-info/index.js +20 -1
- package/dist/locus-info/index.js.map +1 -1
- package/dist/locus-info/selfUtils.js +405 -418
- package/dist/locus-info/selfUtils.js.map +1 -1
- package/dist/media/index.js +8 -16
- package/dist/media/index.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 +556 -288
- 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 +0 -2
- 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 +13 -2
- package/dist/meeting/util.js.map +1 -1
- package/dist/meeting-info/meeting-info-v2.js +359 -60
- package/dist/meeting-info/meeting-info-v2.js.map +1 -1
- package/dist/meetings/index.js +91 -1
- 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 +10 -0
- package/dist/member/index.js.map +1 -1
- package/dist/member/util.js +3 -0
- package/dist/member/util.js.map +1 -1
- package/dist/members/index.js +23 -0
- package/dist/members/index.js.map +1 -1
- package/dist/members/request.js +21 -0
- package/dist/members/request.js.map +1 -1
- package/dist/members/util.js +15 -0
- package/dist/members/util.js.map +1 -1
- package/dist/metrics/constants.js +9 -0
- package/dist/metrics/constants.js.map +1 -1
- package/dist/reachability/clusterReachability.js +63 -27
- package/dist/reachability/clusterReachability.js.map +1 -1
- package/dist/reachability/index.js +112 -47
- package/dist/reachability/index.js.map +1 -1
- package/dist/reachability/reachability.types.js +14 -0
- package/dist/reachability/reachability.types.js.map +1 -1
- package/dist/reachability/request.js +19 -3
- package/dist/reachability/request.js.map +1 -1
- package/dist/reconnection-manager/index.js +2 -2
- package/dist/reconnection-manager/index.js.map +1 -1
- package/dist/recording-controller/util.js +5 -5
- package/dist/recording-controller/util.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 +1 -0
- package/dist/types/constants.d.ts +12 -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 +1 -0
- package/dist/types/locus-info/selfUtils.d.ts +247 -1
- package/dist/types/meeting/in-meeting-actions.d.ts +16 -0
- package/dist/types/meeting/index.d.ts +54 -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 +80 -0
- package/dist/types/meetings/index.d.ts +38 -0
- package/dist/types/member/index.d.ts +1 -0
- package/dist/types/members/index.d.ts +8 -0
- package/dist/types/members/request.d.ts +19 -0
- package/dist/types/members/util.d.ts +13 -0
- package/dist/types/metrics/constants.d.ts +9 -0
- package/dist/types/reachability/clusterReachability.d.ts +15 -7
- package/dist/types/reachability/index.d.ts +10 -1
- package/dist/types/reachability/reachability.types.d.ts +5 -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 +23 -23
- package/src/common/errors/webex-errors.ts +8 -1
- package/src/config.ts +1 -0
- package/src/constants.ts +19 -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 +44 -14
- package/src/locus-info/index.ts +23 -1
- package/src/locus-info/selfUtils.ts +451 -447
- package/src/media/index.ts +11 -21
- package/src/meeting/in-meeting-actions.ts +32 -0
- package/src/meeting/index.ts +372 -92
- package/src/meeting/locusMediaRequest.ts +0 -18
- package/src/meeting/muteState.ts +0 -2
- package/src/meeting/request.ts +36 -1
- package/src/meeting/request.type.ts +7 -0
- package/src/meeting/util.ts +11 -2
- package/src/meeting-info/meeting-info-v2.ts +247 -6
- package/src/meetings/index.ts +107 -1
- package/src/meetings/util.ts +18 -0
- package/src/member/index.ts +11 -0
- package/src/member/util.ts +3 -0
- package/src/members/index.ts +25 -0
- package/src/members/request.ts +26 -0
- package/src/members/util.ts +16 -0
- package/src/metrics/constants.ts +9 -0
- package/src/reachability/clusterReachability.ts +73 -26
- package/src/reachability/index.ts +70 -1
- package/src/reachability/reachability.types.ts +6 -0
- package/src/reachability/request.ts +7 -0
- package/src/reconnection-manager/index.ts +2 -2
- package/src/recording-controller/util.ts +17 -13
- 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/controls-options-manager/util.js +120 -0
- package/test/unit/spec/locus-info/controlsUtils.js +103 -9
- package/test/unit/spec/locus-info/index.js +28 -0
- package/test/unit/spec/media/index.ts +36 -16
- package/test/unit/spec/meeting/in-meeting-actions.ts +19 -4
- package/test/unit/spec/meeting/index.js +528 -34
- package/test/unit/spec/meeting/locusMediaRequest.ts +0 -30
- package/test/unit/spec/meeting/muteState.js +0 -2
- package/test/unit/spec/meeting/request.js +32 -1
- package/test/unit/spec/meeting/utils.js +119 -18
- package/test/unit/spec/meeting-info/meetinginfov2.js +443 -114
- package/test/unit/spec/meetings/index.js +120 -2
- package/test/unit/spec/member/index.js +7 -0
- package/test/unit/spec/member/util.js +24 -0
- package/test/unit/spec/members/index.js +103 -26
- package/test/unit/spec/members/request.js +45 -22
- package/test/unit/spec/members/utils.js +33 -0
- package/test/unit/spec/reachability/clusterReachability.ts +88 -56
- package/test/unit/spec/reachability/index.ts +101 -0
- package/test/unit/spec/reachability/request.js +47 -2
- 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 = {
|
@@ -210,6 +211,7 @@ describe('plugin-meetings', () => {
|
|
210
211
|
let membersSpy;
|
211
212
|
let meetingRequestSpy;
|
212
213
|
let correlationId;
|
214
|
+
let isoLocalClientMeetingJoinTime;
|
213
215
|
let uploadEvent;
|
214
216
|
|
215
217
|
beforeEach(() => {
|
@@ -241,6 +243,7 @@ describe('plugin-meetings', () => {
|
|
241
243
|
},
|
242
244
|
});
|
243
245
|
|
246
|
+
webex.internal.newMetrics.callDiagnosticMetrics.clearErrorCache = sinon.stub();
|
244
247
|
webex.internal.support.submitLogs = sinon.stub().returns(Promise.resolve());
|
245
248
|
webex.internal.services = {get: sinon.stub().returns('locus-url')};
|
246
249
|
webex.credentials.getOrgId = sinon.stub().returns('fake-org-id');
|
@@ -251,6 +254,7 @@ describe('plugin-meetings', () => {
|
|
251
254
|
getReachabilityResults: sinon.stub().resolves(undefined),
|
252
255
|
getReachabilityMetrics: sinon.stub().resolves({}),
|
253
256
|
stopReachability: sinon.stub(),
|
257
|
+
isSubnetReachable: sinon.stub().returns(true),
|
254
258
|
};
|
255
259
|
webex.internal.llm.on = sinon.stub();
|
256
260
|
webex.internal.newMetrics.callDiagnosticLatencies = new CallDiagnosticLatencies(
|
@@ -281,7 +285,7 @@ describe('plugin-meetings', () => {
|
|
281
285
|
testDestination = `testDestination-${uuid.v4()}`;
|
282
286
|
correlationId = uuid.v4();
|
283
287
|
uploadEvent = new EventEmitter();
|
284
|
-
uploadEvent.addListener('progress', () => {})
|
288
|
+
uploadEvent.addListener('progress', () => {});
|
285
289
|
|
286
290
|
meeting = new Meeting(
|
287
291
|
{
|
@@ -1692,10 +1696,6 @@ describe('plugin-meetings', () => {
|
|
1692
1696
|
sandbox.stub(MeetingUtil, 'joinMeeting').returns(Promise.resolve(joinMeetingResult));
|
1693
1697
|
});
|
1694
1698
|
|
1695
|
-
afterEach(() => {
|
1696
|
-
assert.exists(meeting.isoLocalClientMeetingJoinTime);
|
1697
|
-
});
|
1698
|
-
|
1699
1699
|
it('should join the meeting and return promise', async () => {
|
1700
1700
|
const join = meeting.join({pstnAudioType: 'dial-in'});
|
1701
1701
|
meeting.config.enableAutomaticLLM = true;
|
@@ -2110,6 +2110,7 @@ describe('plugin-meetings', () => {
|
|
2110
2110
|
someReachabilityMetric2: 'some value2',
|
2111
2111
|
}),
|
2112
2112
|
stopReachability: sinon.stub(),
|
2113
|
+
isSubnetReachable: sinon.stub().returns(false),
|
2113
2114
|
};
|
2114
2115
|
|
2115
2116
|
const forceRtcMetricsSend = sinon.stub().resolves();
|
@@ -2165,6 +2166,7 @@ describe('plugin-meetings', () => {
|
|
2165
2166
|
someReachabilityMetric1: 'some value1',
|
2166
2167
|
someReachabilityMetric2: 'some value2',
|
2167
2168
|
selectedCandidatePairChanges: 2,
|
2169
|
+
isSubnetReachable: null,
|
2168
2170
|
numTransports: 1,
|
2169
2171
|
iceCandidatesCount: 0,
|
2170
2172
|
}
|
@@ -2211,6 +2213,7 @@ describe('plugin-meetings', () => {
|
|
2211
2213
|
signalingState: 'unknown',
|
2212
2214
|
connectionState: 'unknown',
|
2213
2215
|
iceConnectionState: 'unknown',
|
2216
|
+
isSubnetReachable: null,
|
2214
2217
|
})
|
2215
2218
|
);
|
2216
2219
|
|
@@ -2225,6 +2228,7 @@ describe('plugin-meetings', () => {
|
|
2225
2228
|
someReachabilityMetric1: 'some value1',
|
2226
2229
|
someReachabilityMetric2: 'some value2',
|
2227
2230
|
}),
|
2231
|
+
isSubnetReachable: sinon.stub().returns(true),
|
2228
2232
|
};
|
2229
2233
|
|
2230
2234
|
meeting.waitForRemoteSDPAnswer = sinon.stub().rejects();
|
@@ -2275,6 +2279,7 @@ describe('plugin-meetings', () => {
|
|
2275
2279
|
selectedCandidatePairChanges: 2,
|
2276
2280
|
numTransports: 1,
|
2277
2281
|
iceCandidatesCount: 0,
|
2282
|
+
isSubnetReachable: null,
|
2278
2283
|
}
|
2279
2284
|
);
|
2280
2285
|
});
|
@@ -2332,6 +2337,7 @@ describe('plugin-meetings', () => {
|
|
2332
2337
|
signalingState: 'have-local-offer',
|
2333
2338
|
connectionState: 'connecting',
|
2334
2339
|
iceConnectionState: 'checking',
|
2340
|
+
isSubnetReachable: null,
|
2335
2341
|
})
|
2336
2342
|
);
|
2337
2343
|
|
@@ -2389,6 +2395,7 @@ describe('plugin-meetings', () => {
|
|
2389
2395
|
signalingState: 'have-local-offer',
|
2390
2396
|
connectionState: 'connecting',
|
2391
2397
|
iceConnectionState: 'checking',
|
2398
|
+
isSubnetReachable: null,
|
2392
2399
|
})
|
2393
2400
|
);
|
2394
2401
|
|
@@ -2667,7 +2674,7 @@ describe('plugin-meetings', () => {
|
|
2667
2674
|
|
2668
2675
|
meeting.roap.doTurnDiscovery = sinon.stub().resolves({
|
2669
2676
|
turnServerInfo: {
|
2670
|
-
|
2677
|
+
urls: [FAKE_TURN_URL],
|
2671
2678
|
username: FAKE_TURN_USER,
|
2672
2679
|
password: FAKE_TURN_PASSWORD,
|
2673
2680
|
},
|
@@ -2689,7 +2696,7 @@ describe('plugin-meetings', () => {
|
|
2689
2696
|
meeting.id,
|
2690
2697
|
sinon.match({
|
2691
2698
|
turnServerInfo: {
|
2692
|
-
|
2699
|
+
urls: [FAKE_TURN_URL],
|
2693
2700
|
username: FAKE_TURN_USER,
|
2694
2701
|
password: FAKE_TURN_PASSWORD,
|
2695
2702
|
},
|
@@ -2724,8 +2731,9 @@ describe('plugin-meetings', () => {
|
|
2724
2731
|
sinon.stub().returns(FAKE_ERROR));
|
2725
2732
|
webex.meetings.reachability = {
|
2726
2733
|
isWebexMediaBackendUnreachable: sinon.stub().resolves(false),
|
2727
|
-
getReachabilityMetrics: sinon.stub().resolves(),
|
2734
|
+
getReachabilityMetrics: sinon.stub().resolves({}),
|
2728
2735
|
stopReachability: sinon.stub(),
|
2736
|
+
isSubnetReachable: sinon.stub().returns(true),
|
2729
2737
|
};
|
2730
2738
|
const MOCK_CLIENT_ERROR_CODE = 2004;
|
2731
2739
|
const generateClientErrorCodeForIceFailureStub = sinon
|
@@ -2747,14 +2755,15 @@ describe('plugin-meetings', () => {
|
|
2747
2755
|
.onSecondCall()
|
2748
2756
|
.returns({
|
2749
2757
|
turnServerInfo: {
|
2750
|
-
|
2758
|
+
urls: [FAKE_TURN_URL],
|
2751
2759
|
username: FAKE_TURN_USER,
|
2752
2760
|
password: FAKE_TURN_PASSWORD,
|
2753
2761
|
},
|
2754
2762
|
turnDiscoverySkippedReason: undefined,
|
2755
2763
|
});
|
2756
2764
|
meeting.meetingState = 'ACTIVE';
|
2757
|
-
|
2765
|
+
const error = {iceConnected: false};
|
2766
|
+
meeting.mediaProperties.waitForMediaConnectionConnected.rejects(error);
|
2758
2767
|
|
2759
2768
|
const forceRtcMetricsSend = sinon.stub().resolves();
|
2760
2769
|
const closeMediaConnectionStub = sinon.stub();
|
@@ -2772,6 +2781,7 @@ describe('plugin-meetings', () => {
|
|
2772
2781
|
})
|
2773
2782
|
.catch((err) => {
|
2774
2783
|
errorThrown = err;
|
2784
|
+
assert.instanceOf(err.cause, Error);
|
2775
2785
|
assert.instanceOf(err, AddMediaFailed);
|
2776
2786
|
});
|
2777
2787
|
|
@@ -2828,6 +2838,7 @@ describe('plugin-meetings', () => {
|
|
2828
2838
|
},
|
2829
2839
|
options: {
|
2830
2840
|
meetingId: meeting.id,
|
2841
|
+
rawError: error,
|
2831
2842
|
},
|
2832
2843
|
});
|
2833
2844
|
assert.calledWith(webex.internal.newMetrics.submitClientEvent.thirdCall, {
|
@@ -2839,6 +2850,7 @@ describe('plugin-meetings', () => {
|
|
2839
2850
|
},
|
2840
2851
|
options: {
|
2841
2852
|
meetingId: meeting.id,
|
2853
|
+
rawError: error,
|
2842
2854
|
},
|
2843
2855
|
});
|
2844
2856
|
|
@@ -2905,6 +2917,7 @@ describe('plugin-meetings', () => {
|
|
2905
2917
|
selectedCandidatePairChanges: 2,
|
2906
2918
|
numTransports: 1,
|
2907
2919
|
iceCandidatesCount: 0,
|
2920
|
+
isSubnetReachable: null,
|
2908
2921
|
},
|
2909
2922
|
]);
|
2910
2923
|
|
@@ -2935,6 +2948,7 @@ describe('plugin-meetings', () => {
|
|
2935
2948
|
.resolves(false),
|
2936
2949
|
getReachabilityMetrics: sinon.stub().resolves({}),
|
2937
2950
|
stopReachability: sinon.stub(),
|
2951
|
+
isSubnetReachable: sinon.stub().returns(true),
|
2938
2952
|
};
|
2939
2953
|
const getErrorPayloadForClientErrorCodeStub =
|
2940
2954
|
(webex.internal.newMetrics.callDiagnosticMetrics.getErrorPayloadForClientErrorCode =
|
@@ -2959,16 +2973,19 @@ describe('plugin-meetings', () => {
|
|
2959
2973
|
.onSecondCall()
|
2960
2974
|
.returns({
|
2961
2975
|
turnServerInfo: {
|
2962
|
-
|
2976
|
+
urls: [FAKE_TURN_URL],
|
2963
2977
|
username: FAKE_TURN_USER,
|
2964
2978
|
password: FAKE_TURN_PASSWORD,
|
2965
2979
|
},
|
2966
2980
|
turnDiscoverySkippedReason: undefined,
|
2967
2981
|
});
|
2982
|
+
|
2983
|
+
const mediaConnectionError = new Error('fake error');
|
2984
|
+
|
2968
2985
|
meeting.mediaProperties.waitForMediaConnectionConnected = sinon
|
2969
2986
|
.stub()
|
2970
2987
|
.onFirstCall()
|
2971
|
-
.rejects()
|
2988
|
+
.rejects(mediaConnectionError)
|
2972
2989
|
.onSecondCall()
|
2973
2990
|
.resolves();
|
2974
2991
|
|
@@ -3037,6 +3054,7 @@ describe('plugin-meetings', () => {
|
|
3037
3054
|
},
|
3038
3055
|
options: {
|
3039
3056
|
meetingId: meeting.id,
|
3057
|
+
rawError: mediaConnectionError,
|
3040
3058
|
},
|
3041
3059
|
});
|
3042
3060
|
assert.calledWith(webex.internal.newMetrics.submitClientEvent.thirdCall, {
|
@@ -3102,6 +3120,7 @@ describe('plugin-meetings', () => {
|
|
3102
3120
|
retriedWithTurnServer: true,
|
3103
3121
|
isJoinWithMediaRetry: false,
|
3104
3122
|
iceCandidatesCount: 0,
|
3123
|
+
isSubnetReachable: null,
|
3105
3124
|
},
|
3106
3125
|
]);
|
3107
3126
|
meeting.roap.doTurnDiscovery;
|
@@ -3136,7 +3155,7 @@ describe('plugin-meetings', () => {
|
|
3136
3155
|
.onSecondCall()
|
3137
3156
|
.returns({
|
3138
3157
|
turnServerInfo: {
|
3139
|
-
|
3158
|
+
urls: [FAKE_TURN_URL],
|
3140
3159
|
username: FAKE_TURN_USER,
|
3141
3160
|
password: FAKE_TURN_PASSWORD,
|
3142
3161
|
},
|
@@ -3188,7 +3207,7 @@ describe('plugin-meetings', () => {
|
|
3188
3207
|
.onSecondCall()
|
3189
3208
|
.returns({
|
3190
3209
|
turnServerInfo: {
|
3191
|
-
|
3210
|
+
urls: [FAKE_TURN_URL],
|
3192
3211
|
username: FAKE_TURN_USER,
|
3193
3212
|
password: FAKE_TURN_PASSWORD,
|
3194
3213
|
},
|
@@ -3230,6 +3249,7 @@ describe('plugin-meetings', () => {
|
|
3230
3249
|
someReachabilityMetric2: 'some value2',
|
3231
3250
|
}),
|
3232
3251
|
stopReachability: sinon.stub(),
|
3252
|
+
isSubnetReachable: sinon.stub().returns(true),
|
3233
3253
|
};
|
3234
3254
|
meeting.iceCandidatesCount = 3;
|
3235
3255
|
meeting.iceCandidateErrors.set('701_error', 3);
|
@@ -3257,6 +3277,7 @@ describe('plugin-meetings', () => {
|
|
3257
3277
|
iceCandidatesCount: 3,
|
3258
3278
|
'701_error': 3,
|
3259
3279
|
'701_turn_host_lookup_received_error': 1,
|
3280
|
+
isSubnetReachable: null,
|
3260
3281
|
}
|
3261
3282
|
);
|
3262
3283
|
|
@@ -3319,6 +3340,7 @@ describe('plugin-meetings', () => {
|
|
3319
3340
|
iceConnectionState: 'unknown',
|
3320
3341
|
selectedCandidatePairChanges: 2,
|
3321
3342
|
numTransports: 1,
|
3343
|
+
isSubnetReachable: null,
|
3322
3344
|
iceCandidatesCount: 0,
|
3323
3345
|
}
|
3324
3346
|
);
|
@@ -3380,6 +3402,116 @@ describe('plugin-meetings', () => {
|
|
3380
3402
|
numTransports: 1,
|
3381
3403
|
'701_error': 2,
|
3382
3404
|
'701_turn_host_lookup_received_error': 1,
|
3405
|
+
isSubnetReachable: null,
|
3406
|
+
iceCandidatesCount: 0,
|
3407
|
+
}
|
3408
|
+
);
|
3409
|
+
|
3410
|
+
assert.isOk(errorThrown);
|
3411
|
+
});
|
3412
|
+
|
3413
|
+
it('should send valid isSubnetReachability if media connection success', async () => {
|
3414
|
+
meeting.roap.doTurnDiscovery = sinon.stub().returns({
|
3415
|
+
turnServerInfo: undefined,
|
3416
|
+
turnDiscoverySkippedReason: undefined,
|
3417
|
+
});
|
3418
|
+
meeting.meetingState = 'ACTIVE';
|
3419
|
+
meeting.mediaProperties.waitForMediaConnectionConnected.resolves();
|
3420
|
+
meeting.webex.meetings.reachability = {
|
3421
|
+
getReachabilityMetrics: sinon.stub().resolves({
|
3422
|
+
reachability_public_udp_success: 5,
|
3423
|
+
}),
|
3424
|
+
stopReachability: sinon.stub(),
|
3425
|
+
isSubnetReachable: sinon.stub().returns(false),
|
3426
|
+
};
|
3427
|
+
|
3428
|
+
const forceRtcMetricsSend = sinon.stub().resolves();
|
3429
|
+
const closeMediaConnectionStub = sinon.stub();
|
3430
|
+
Media.createMediaConnection = sinon.stub().returns({
|
3431
|
+
close: closeMediaConnectionStub,
|
3432
|
+
forceRtcMetricsSend,
|
3433
|
+
getConnectionState: sinon.stub().returns(ConnectionState.Connected),
|
3434
|
+
initiateOffer: sinon.stub().resolves({}),
|
3435
|
+
on: sinon.stub(),
|
3436
|
+
});
|
3437
|
+
|
3438
|
+
await meeting.addMedia({
|
3439
|
+
mediaSettings: {},
|
3440
|
+
});
|
3441
|
+
|
3442
|
+
assert.calledWith(Metrics.sendBehavioralMetric, BEHAVIORAL_METRICS.ADD_MEDIA_SUCCESS, {
|
3443
|
+
correlation_id: meeting.correlationId,
|
3444
|
+
locus_id: meeting.locusUrl.split('/').pop(),
|
3445
|
+
connectionType: 'udp',
|
3446
|
+
selectedCandidatePairChanges: 2,
|
3447
|
+
numTransports: 1,
|
3448
|
+
isMultistream: false,
|
3449
|
+
retriedWithTurnServer: false,
|
3450
|
+
isJoinWithMediaRetry: false,
|
3451
|
+
iceCandidatesCount: 0,
|
3452
|
+
reachability_public_udp_success: 5,
|
3453
|
+
isSubnetReachable: false,
|
3454
|
+
});
|
3455
|
+
});
|
3456
|
+
|
3457
|
+
it('should send valid isSubnetReachability if media connection fails', async () => {
|
3458
|
+
let errorThrown = undefined;
|
3459
|
+
|
3460
|
+
meeting.roap.doTurnDiscovery = sinon.stub().returns({
|
3461
|
+
turnServerInfo: undefined,
|
3462
|
+
turnDiscoverySkippedReason: undefined,
|
3463
|
+
});
|
3464
|
+
meeting.meetingState = 'ACTIVE';
|
3465
|
+
meeting.mediaProperties.waitForMediaConnectionConnected.rejects({iceConnected: false});
|
3466
|
+
meeting.webex.meetings.reachability = {
|
3467
|
+
getReachabilityMetrics: sinon.stub().resolves({
|
3468
|
+
reachability_public_udp_success: 5,
|
3469
|
+
}),
|
3470
|
+
stopReachability: sinon.stub(),
|
3471
|
+
isSubnetReachable: sinon.stub().returns(true),
|
3472
|
+
};
|
3473
|
+
|
3474
|
+
const forceRtcMetricsSend = sinon.stub().resolves();
|
3475
|
+
const closeMediaConnectionStub = sinon.stub();
|
3476
|
+
Media.createMediaConnection = sinon.stub().returns({
|
3477
|
+
close: closeMediaConnectionStub,
|
3478
|
+
forceRtcMetricsSend,
|
3479
|
+
getConnectionState: sinon.stub().returns(ConnectionState.Connected),
|
3480
|
+
initiateOffer: sinon.stub().resolves({}),
|
3481
|
+
on: sinon.stub(),
|
3482
|
+
});
|
3483
|
+
|
3484
|
+
await meeting
|
3485
|
+
.addMedia({
|
3486
|
+
mediaSettings: {},
|
3487
|
+
})
|
3488
|
+
.catch((err) => {
|
3489
|
+
errorThrown = err;
|
3490
|
+
assert.instanceOf(err, AddMediaFailed);
|
3491
|
+
});
|
3492
|
+
|
3493
|
+
// Check that the only metric sent is ADD_MEDIA_FAILURE
|
3494
|
+
assert.calledOnceWithExactly(
|
3495
|
+
Metrics.sendBehavioralMetric,
|
3496
|
+
BEHAVIORAL_METRICS.ADD_MEDIA_FAILURE,
|
3497
|
+
{
|
3498
|
+
correlation_id: meeting.correlationId,
|
3499
|
+
locus_id: meeting.locusUrl.split('/').pop(),
|
3500
|
+
reason: errorThrown.message,
|
3501
|
+
stack: errorThrown.stack,
|
3502
|
+
code: errorThrown.code,
|
3503
|
+
turnDiscoverySkippedReason: undefined,
|
3504
|
+
turnServerUsed: true,
|
3505
|
+
retriedWithTurnServer: false,
|
3506
|
+
isMultistream: false,
|
3507
|
+
isJoinWithMediaRetry: false,
|
3508
|
+
signalingState: 'unknown',
|
3509
|
+
connectionState: 'unknown',
|
3510
|
+
iceConnectionState: 'unknown',
|
3511
|
+
selectedCandidatePairChanges: 2,
|
3512
|
+
numTransports: 1,
|
3513
|
+
reachability_public_udp_success: 5,
|
3514
|
+
isSubnetReachable: true,
|
3383
3515
|
iceCandidatesCount: 0,
|
3384
3516
|
}
|
3385
3517
|
);
|
@@ -3399,6 +3531,8 @@ describe('plugin-meetings', () => {
|
|
3399
3531
|
meeting.config.stats.enableStatsAnalyzer = true;
|
3400
3532
|
|
3401
3533
|
statsAnalyzerStub = new EventsScope();
|
3534
|
+
statsAnalyzerStub.getNetworkType = sinon.stub().returns('wifi');
|
3535
|
+
|
3402
3536
|
// mock the StatsAnalyzer constructor
|
3403
3537
|
sinon.stub(InternalMediaCoreModule, 'StatsAnalyzer').returns(statsAnalyzerStub);
|
3404
3538
|
|
@@ -3439,6 +3573,40 @@ describe('plugin-meetings', () => {
|
|
3439
3573
|
});
|
3440
3574
|
});
|
3441
3575
|
|
3576
|
+
it('LOCAL_MEDIA_STARTED triggers "meeting:media:local:start" event and does not send metric because we already have', async () => {
|
3577
|
+
meeting.shareCAEventSentStatus = {
|
3578
|
+
transmitStart: true,
|
3579
|
+
transmitStop: false,
|
3580
|
+
receiveStart: false,
|
3581
|
+
receiveStop: false,
|
3582
|
+
};
|
3583
|
+
statsAnalyzerStub.emit(
|
3584
|
+
{file: 'test', function: 'test'},
|
3585
|
+
StatsAnalyzerEventNames.LOCAL_MEDIA_STARTED,
|
3586
|
+
{mediaType: 'share'}
|
3587
|
+
);
|
3588
|
+
|
3589
|
+
assert.calledWith(
|
3590
|
+
TriggerProxy.trigger,
|
3591
|
+
sinon.match.instanceOf(Meeting),
|
3592
|
+
{
|
3593
|
+
file: 'meeting/index',
|
3594
|
+
function: 'addMedia',
|
3595
|
+
},
|
3596
|
+
EVENT_TRIGGERS.MEETING_MEDIA_LOCAL_STARTED,
|
3597
|
+
{
|
3598
|
+
mediaType: 'share',
|
3599
|
+
}
|
3600
|
+
);
|
3601
|
+
assert.neverCalledWith(webex.internal.newMetrics.submitClientEvent, {
|
3602
|
+
name: 'client.media.tx.start',
|
3603
|
+
payload: {mediaType: 'share', shareInstanceId: meeting.remoteShareInstanceId},
|
3604
|
+
options: {
|
3605
|
+
meetingId: meeting.id,
|
3606
|
+
},
|
3607
|
+
});
|
3608
|
+
});
|
3609
|
+
|
3442
3610
|
it('LOCAL_MEDIA_STOPPED triggers the right metrics', async () => {
|
3443
3611
|
statsAnalyzerStub.emit(
|
3444
3612
|
{file: 'test', function: 'test'},
|
@@ -3455,6 +3623,28 @@ describe('plugin-meetings', () => {
|
|
3455
3623
|
});
|
3456
3624
|
});
|
3457
3625
|
|
3626
|
+
it('LOCAL_MEDIA_STOPPED does not send metric because we already have', async () => {
|
3627
|
+
meeting.shareCAEventSentStatus = {
|
3628
|
+
transmitStart: false,
|
3629
|
+
transmitStop: true,
|
3630
|
+
receiveStart: false,
|
3631
|
+
receiveStop: false,
|
3632
|
+
};
|
3633
|
+
statsAnalyzerStub.emit(
|
3634
|
+
{file: 'test', function: 'test'},
|
3635
|
+
StatsAnalyzerEventNames.LOCAL_MEDIA_STOPPED,
|
3636
|
+
{mediaType: 'share'}
|
3637
|
+
);
|
3638
|
+
|
3639
|
+
assert.neverCalledWith(webex.internal.newMetrics.submitClientEvent, {
|
3640
|
+
name: 'client.media.tx.stop',
|
3641
|
+
payload: {mediaType: 'share', shareInstanceId: meeting.remoteShareInstanceId},
|
3642
|
+
options: {
|
3643
|
+
meetingId: meeting.id,
|
3644
|
+
},
|
3645
|
+
});
|
3646
|
+
});
|
3647
|
+
|
3458
3648
|
it('REMOTE_MEDIA_STARTED triggers "meeting:media:remote:start" event and sends metrics', async () => {
|
3459
3649
|
statsAnalyzerStub.emit(
|
3460
3650
|
{file: 'test', function: 'test'},
|
@@ -3535,6 +3725,47 @@ describe('plugin-meetings', () => {
|
|
3535
3725
|
});
|
3536
3726
|
});
|
3537
3727
|
|
3728
|
+
it('REMOTE_MEDIA_STARTED triggers "meeting:media:remote:start" event and does not send metric because we already have', async () => {
|
3729
|
+
meeting.shareCAEventSentStatus = {
|
3730
|
+
transmitStart: false,
|
3731
|
+
transmitStop: false,
|
3732
|
+
receiveStart: true,
|
3733
|
+
receiveStop: false,
|
3734
|
+
};
|
3735
|
+
statsAnalyzerStub.emit(
|
3736
|
+
{file: 'test', function: 'test'},
|
3737
|
+
StatsAnalyzerEventNames.REMOTE_MEDIA_STARTED,
|
3738
|
+
{mediaType: 'share'}
|
3739
|
+
);
|
3740
|
+
|
3741
|
+
assert.calledWith(
|
3742
|
+
TriggerProxy.trigger,
|
3743
|
+
sinon.match.instanceOf(Meeting),
|
3744
|
+
{
|
3745
|
+
file: 'meeting/index',
|
3746
|
+
function: 'addMedia',
|
3747
|
+
},
|
3748
|
+
EVENT_TRIGGERS.MEETING_MEDIA_REMOTE_STARTED,
|
3749
|
+
{
|
3750
|
+
mediaType: 'share',
|
3751
|
+
}
|
3752
|
+
);
|
3753
|
+
assert.neverCalledWith(webex.internal.newMetrics.submitClientEvent, {
|
3754
|
+
name: 'client.media.render.start',
|
3755
|
+
payload: {mediaType: 'share', shareInstanceId: meeting.remoteShareInstanceId},
|
3756
|
+
options: {
|
3757
|
+
meetingId: meeting.id,
|
3758
|
+
},
|
3759
|
+
});
|
3760
|
+
assert.neverCalledWith(webex.internal.newMetrics.submitClientEvent, {
|
3761
|
+
name: 'client.media.rx.start',
|
3762
|
+
payload: {mediaType: 'share', shareInstanceId: meeting.remoteShareInstanceId},
|
3763
|
+
options: {
|
3764
|
+
meetingId: meeting.id,
|
3765
|
+
},
|
3766
|
+
});
|
3767
|
+
});
|
3768
|
+
|
3538
3769
|
it('REMOTE_MEDIA_STOPPED triggers the right metrics for share', async () => {
|
3539
3770
|
statsAnalyzerStub.emit(
|
3540
3771
|
{file: 'test', function: 'test'},
|
@@ -3559,6 +3790,34 @@ describe('plugin-meetings', () => {
|
|
3559
3790
|
});
|
3560
3791
|
});
|
3561
3792
|
|
3793
|
+
it('REMOTE_MEDIA_STOPPED does not send metric because we already have', async () => {
|
3794
|
+
meeting.shareCAEventSentStatus = {
|
3795
|
+
transmitStart: false,
|
3796
|
+
transmitStop: false,
|
3797
|
+
receiveStart: true,
|
3798
|
+
receiveStop: true,
|
3799
|
+
};
|
3800
|
+
statsAnalyzerStub.emit(
|
3801
|
+
{file: 'test', function: 'test'},
|
3802
|
+
StatsAnalyzerEventNames.REMOTE_MEDIA_STOPPED,
|
3803
|
+
{mediaType: 'share'}
|
3804
|
+
);
|
3805
|
+
assert.neverCalledWith(webex.internal.newMetrics.submitClientEvent, {
|
3806
|
+
name: 'client.media.render.stop',
|
3807
|
+
payload: {mediaType: 'share', shareInstanceId: meeting.remoteShareInstanceId},
|
3808
|
+
options: {
|
3809
|
+
meetingId: meeting.id,
|
3810
|
+
},
|
3811
|
+
});
|
3812
|
+
assert.neverCalledWith(webex.internal.newMetrics.submitClientEvent, {
|
3813
|
+
name: 'client.media.rx.stop',
|
3814
|
+
payload: {mediaType: 'share', shareInstanceId: meeting.remoteShareInstanceId},
|
3815
|
+
options: {
|
3816
|
+
meetingId: meeting.id,
|
3817
|
+
},
|
3818
|
+
});
|
3819
|
+
});
|
3820
|
+
|
3562
3821
|
it('counts the number of members that are in the meeting for MEDIA_QUALITY event', async () => {
|
3563
3822
|
let fakeMembersCollection = {
|
3564
3823
|
members: {
|
@@ -3568,7 +3827,7 @@ describe('plugin-meetings', () => {
|
|
3568
3827
|
},
|
3569
3828
|
};
|
3570
3829
|
sinon.stub(meeting, 'getMembers').returns({membersCollection: fakeMembersCollection});
|
3571
|
-
const fakeData = {intervalMetadata: {}
|
3830
|
+
const fakeData = {intervalMetadata: {}};
|
3572
3831
|
|
3573
3832
|
statsAnalyzerStub.emit(
|
3574
3833
|
{file: 'test', function: 'test'},
|
@@ -3609,7 +3868,7 @@ describe('plugin-meetings', () => {
|
|
3609
3868
|
});
|
3610
3869
|
|
3611
3870
|
it('calls submitMQE correctly', async () => {
|
3612
|
-
const fakeData = {intervalMetadata: {bla: 'bla'}
|
3871
|
+
const fakeData = {intervalMetadata: {bla: 'bla'}};
|
3613
3872
|
|
3614
3873
|
statsAnalyzerStub.emit(
|
3615
3874
|
{file: 'test', function: 'test'},
|
@@ -3640,7 +3899,7 @@ describe('plugin-meetings', () => {
|
|
3640
3899
|
|
3641
3900
|
meeting.roap.doTurnDiscovery = sinon.stub().resolves({
|
3642
3901
|
turnServerInfo: {
|
3643
|
-
|
3902
|
+
urls: [FAKE_TURN_URL],
|
3644
3903
|
username: FAKE_TURN_USER,
|
3645
3904
|
password: FAKE_TURN_PASSWORD,
|
3646
3905
|
},
|
@@ -3666,7 +3925,7 @@ describe('plugin-meetings', () => {
|
|
3666
3925
|
meeting.id,
|
3667
3926
|
sinon.match({
|
3668
3927
|
turnServerInfo: {
|
3669
|
-
|
3928
|
+
urls: [FAKE_TURN_URL],
|
3670
3929
|
username: FAKE_TURN_USER,
|
3671
3930
|
password: FAKE_TURN_PASSWORD,
|
3672
3931
|
},
|
@@ -3817,6 +4076,9 @@ describe('plugin-meetings', () => {
|
|
3817
4076
|
},
|
3818
4077
|
options: {
|
3819
4078
|
meetingId: meeting.id,
|
4079
|
+
rawError: {
|
4080
|
+
iceConnected: false,
|
4081
|
+
},
|
3820
4082
|
},
|
3821
4083
|
},
|
3822
4084
|
]);
|
@@ -3887,9 +4149,29 @@ describe('plugin-meetings', () => {
|
|
3887
4149
|
} catch (err) {
|
3888
4150
|
assert.instanceOf(err, Error);
|
3889
4151
|
assert.equal(err.message, 'setBrb failed');
|
3890
|
-
assert.isRejected(
|
4152
|
+
assert.isRejected(Promise.reject());
|
3891
4153
|
}
|
3892
4154
|
});
|
4155
|
+
|
4156
|
+
it('updates remote mute state when brb is enabled', async () => {
|
4157
|
+
meeting.audio = {handleServerRemoteMuteUpdate: sinon.stub()};
|
4158
|
+
|
4159
|
+
await meeting.beRightBack(true);
|
4160
|
+
|
4161
|
+
sinon.assert.calledOnceWithExactly(
|
4162
|
+
meeting.audio.handleServerRemoteMuteUpdate,
|
4163
|
+
meeting,
|
4164
|
+
true
|
4165
|
+
);
|
4166
|
+
});
|
4167
|
+
|
4168
|
+
it('does not update remote mute state when brb is disabled', async () => {
|
4169
|
+
meeting.audio = {handleServerRemoteMuteUpdate: sinon.stub()};
|
4170
|
+
|
4171
|
+
await meeting.beRightBack(false);
|
4172
|
+
|
4173
|
+
assert.notCalled(meeting.audio.handleServerRemoteMuteUpdate);
|
4174
|
+
});
|
3893
4175
|
});
|
3894
4176
|
});
|
3895
4177
|
|
@@ -3943,7 +4225,10 @@ describe('plugin-meetings', () => {
|
|
3943
4225
|
.resolves({id: 'fake clientMediaPreferences'});
|
3944
4226
|
meeting.roap.doTurnDiscovery = sinon.stub().resolves({
|
3945
4227
|
turnServerInfo: {
|
3946
|
-
|
4228
|
+
urls: [
|
4229
|
+
'turns:turn-server-url1:443?transport=tcp',
|
4230
|
+
'turns:turn-server-url2:443?transport=tcp',
|
4231
|
+
],
|
3947
4232
|
username: 'turn user',
|
3948
4233
|
password: 'turn password',
|
3949
4234
|
},
|
@@ -3961,12 +4246,10 @@ describe('plugin-meetings', () => {
|
|
3961
4246
|
expectedMediaConnectionConfig = {
|
3962
4247
|
iceServers: [
|
3963
4248
|
{
|
3964
|
-
urls:
|
3965
|
-
|
3966
|
-
|
3967
|
-
|
3968
|
-
{
|
3969
|
-
urls: 'turns:turn-server-url:443?transport=tcp',
|
4249
|
+
urls: [
|
4250
|
+
'turns:turn-server-url1:443?transport=tcp',
|
4251
|
+
'turns:turn-server-url2:443?transport=tcp',
|
4252
|
+
],
|
3970
4253
|
username: 'turn user',
|
3971
4254
|
credential: 'turn password',
|
3972
4255
|
},
|
@@ -4048,9 +4331,11 @@ describe('plugin-meetings', () => {
|
|
4048
4331
|
.stub(InternalMediaCoreModule, 'MultistreamRoapMediaConnection')
|
4049
4332
|
.returns(fakeMultistreamRoapMediaConnection);
|
4050
4333
|
|
4051
|
-
locusMediaRequestStub = sinon
|
4052
|
-
|
4053
|
-
|
4334
|
+
locusMediaRequestStub = sinon.stub(WebexPlugin.prototype, 'request').resolves({
|
4335
|
+
body: {locus: {fullState: {}}},
|
4336
|
+
upload: sinon.match.instanceOf(EventEmitter),
|
4337
|
+
download: sinon.match.instanceOf(EventEmitter),
|
4338
|
+
});
|
4054
4339
|
|
4055
4340
|
// setup some things and mocks so that the call to join() works
|
4056
4341
|
// (we need to call join() because it creates the LocusMediaRequest instance
|
@@ -5234,7 +5519,10 @@ describe('plugin-meetings', () => {
|
|
5234
5519
|
// and check that when we fallback to transcoded we still do another TURN discovery
|
5235
5520
|
await runCheck(
|
5236
5521
|
{
|
5237
|
-
|
5522
|
+
urls: [
|
5523
|
+
'turns:turn-server-url1:443?transport=tcp',
|
5524
|
+
'turns:turn-server-url2:443?transport=tcp',
|
5525
|
+
],
|
5238
5526
|
username: 'turn user',
|
5239
5527
|
password: 'turn password',
|
5240
5528
|
},
|
@@ -5248,7 +5536,10 @@ describe('plugin-meetings', () => {
|
|
5248
5536
|
// but doing it just for completeness
|
5249
5537
|
await runCheck(
|
5250
5538
|
{
|
5251
|
-
|
5539
|
+
urls: [
|
5540
|
+
'turns:turn-server-url1:443?transport=tcp',
|
5541
|
+
'turns:turn-server-url2:443?transport=tcp',
|
5542
|
+
],
|
5252
5543
|
username: 'turn user',
|
5253
5544
|
password: 'turn password',
|
5254
5545
|
},
|
@@ -7530,6 +7821,27 @@ describe('plugin-meetings', () => {
|
|
7530
7821
|
});
|
7531
7822
|
});
|
7532
7823
|
|
7824
|
+
describe('#setIsoLocalClientMeetingJoinTime', () => {
|
7825
|
+
it('should fallback to system clock ISO string when given an undefined value', () => {
|
7826
|
+
const currentSystemTime = new Date().toISOString();
|
7827
|
+
meeting.isoLocalClientMeetingJoinTime = undefined;
|
7828
|
+
assert.equal(meeting.isoLocalClientMeetingJoinTime, currentSystemTime);
|
7829
|
+
});
|
7830
|
+
|
7831
|
+
it('should fallback to system clock ISO string when given an invalid value', () => {
|
7832
|
+
const currentSystemTime = new Date().toISOString();
|
7833
|
+
meeting.isoLocalClientMeetingJoinTime = 'invalid-date';
|
7834
|
+
assert.equal(meeting.isoLocalClientMeetingJoinTime, currentSystemTime);
|
7835
|
+
});
|
7836
|
+
|
7837
|
+
it('should set the isoLocalClientMeetingJoinTime correctly for a valid date string', () => {
|
7838
|
+
const validDateString = 'Tue, 01 Apr 2025 13:00:36 GMT';
|
7839
|
+
const expectedISOString = new Date(validDateString).toISOString();
|
7840
|
+
meeting.isoLocalClientMeetingJoinTime = validDateString;
|
7841
|
+
assert.equal(meeting.isoLocalClientMeetingJoinTime, expectedISOString);
|
7842
|
+
});
|
7843
|
+
});
|
7844
|
+
|
7533
7845
|
describe('#updateCallStateForMetrics', () => {
|
7534
7846
|
it('should update the callState, overriding existing values', () => {
|
7535
7847
|
assert.deepEqual(meeting.callStateForMetrics, {correlationId, sessionCorrelationId: ''});
|
@@ -7611,6 +7923,12 @@ describe('plugin-meetings', () => {
|
|
7611
7923
|
meeting.audio = {handleLocalStreamChange: sinon.stub()};
|
7612
7924
|
meeting.video = {handleLocalStreamChange: sinon.stub()};
|
7613
7925
|
meeting.statsAnalyzer = {updateMediaStatus: sinon.stub()};
|
7926
|
+
meeting.shareCAEventSentStatus = {
|
7927
|
+
transmitStart: false,
|
7928
|
+
transmitStop: false,
|
7929
|
+
receiveStart: false,
|
7930
|
+
receiveStop: false,
|
7931
|
+
};
|
7614
7932
|
fakeMultistreamRoapMediaConnection = {
|
7615
7933
|
createSendSlot: () => {
|
7616
7934
|
return {
|
@@ -7678,6 +7996,9 @@ describe('plugin-meetings', () => {
|
|
7678
7996
|
});
|
7679
7997
|
assert.equal(meeting.mediaProperties.mediaDirection.sendShare, true);
|
7680
7998
|
|
7999
|
+
assert.equal(meeting.shareCAEventSentStatus.transmitStart, false);
|
8000
|
+
assert.equal(meeting.shareCAEventSentStatus.transmitStop, false);
|
8001
|
+
|
7681
8002
|
assert.calledWith(meeting.statsAnalyzer.updateMediaStatus, {
|
7682
8003
|
expected: {sendShare: true},
|
7683
8004
|
});
|
@@ -7698,18 +8019,23 @@ describe('plugin-meetings', () => {
|
|
7698
8019
|
assert.equal(meeting.mediaProperties.shareAudioStream, stream);
|
7699
8020
|
assert.equal(meeting.mediaProperties.mediaDirection.sendShare, true);
|
7700
8021
|
|
8022
|
+
assert.equal(meeting.shareCAEventSentStatus.transmitStart, false);
|
8023
|
+
assert.equal(meeting.shareCAEventSentStatus.transmitStop, false);
|
8024
|
+
|
7701
8025
|
assert.calledWith(meeting.statsAnalyzer.updateMediaStatus, {
|
7702
8026
|
expected: {sendShare: true},
|
7703
8027
|
});
|
7704
8028
|
};
|
7705
8029
|
|
7706
8030
|
it('requests screen share floor and publishes the screen share video stream', async () => {
|
8031
|
+
meeting.shareCAEventSentStatus.transmitStart = true;
|
7707
8032
|
await meeting.publishStreams({screenShare: {video: videoShareStream}});
|
7708
8033
|
|
7709
8034
|
checkScreenShareVideoPublished(videoShareStream);
|
7710
8035
|
});
|
7711
8036
|
|
7712
8037
|
it('requests screen share floor and publishes the screen share audio stream', async () => {
|
8038
|
+
meeting.shareCAEventSentStatus.transmitStart = true;
|
7713
8039
|
await meeting.publishStreams({screenShare: {audio: audioShareStream}});
|
7714
8040
|
|
7715
8041
|
checkScreenShareAudioPublished(audioShareStream);
|
@@ -8596,13 +8922,19 @@ describe('plugin-meetings', () => {
|
|
8596
8922
|
const fakeErrorMessage = 'test error';
|
8597
8923
|
const fakeRootCauseName = 'root cause name';
|
8598
8924
|
const fakeErrorName = 'test error name';
|
8925
|
+
let clock;
|
8599
8926
|
|
8600
8927
|
beforeEach(() => {
|
8928
|
+
clock = sinon.useFakeTimers();
|
8601
8929
|
meeting.setupMediaConnectionListeners();
|
8602
8930
|
webex.internal.newMetrics.submitClientEvent.resetHistory();
|
8603
8931
|
Metrics.sendBehavioralMetric.resetHistory();
|
8604
8932
|
});
|
8605
8933
|
|
8934
|
+
afterEach(() => {
|
8935
|
+
clock.restore();
|
8936
|
+
});
|
8937
|
+
|
8606
8938
|
const checkMetricSent = (event, error) => {
|
8607
8939
|
assert.calledOnce(webex.internal.newMetrics.submitClientEvent);
|
8608
8940
|
assert.calledWithMatch(webex.internal.newMetrics.submitClientEvent, {
|
@@ -8671,6 +9003,13 @@ describe('plugin-meetings', () => {
|
|
8671
9003
|
});
|
8672
9004
|
|
8673
9005
|
it('should send metrics for SdpAnswerHandlingError error', () => {
|
9006
|
+
meeting.sdpResponseTimer = '1234';
|
9007
|
+
meeting.deferSDPAnswer = {
|
9008
|
+
reject: sinon.stub(),
|
9009
|
+
};
|
9010
|
+
|
9011
|
+
const clearTimeoutSpy = sinon.spy(clock, 'clearTimeout');
|
9012
|
+
|
8674
9013
|
const fakeError = new Errors.SdpAnswerHandlingError(fakeErrorMessage, {
|
8675
9014
|
name: fakeErrorName,
|
8676
9015
|
cause: {name: fakeRootCauseName},
|
@@ -8685,6 +9024,8 @@ describe('plugin-meetings', () => {
|
|
8685
9024
|
fakeErrorMessage,
|
8686
9025
|
fakeRootCauseName
|
8687
9026
|
);
|
9027
|
+
assert.calledOnce(meeting.deferSDPAnswer.reject);
|
9028
|
+
assert.calledOnce(clearTimeoutSpy);
|
8688
9029
|
});
|
8689
9030
|
|
8690
9031
|
it('should send metrics for SdpError error', () => {
|
@@ -9590,6 +9931,42 @@ describe('plugin-meetings', () => {
|
|
9590
9931
|
);
|
9591
9932
|
});
|
9592
9933
|
|
9934
|
+
it('listens to CONTROLS_ANNOTATION_CHANGED', async () => {
|
9935
|
+
const state = {example: 'value'};
|
9936
|
+
|
9937
|
+
await meeting.locusInfo.emitScoped(
|
9938
|
+
{function: 'test', file: 'test'},
|
9939
|
+
LOCUSINFO.EVENTS.CONTROLS_ANNOTATION_CHANGED,
|
9940
|
+
{state}
|
9941
|
+
);
|
9942
|
+
|
9943
|
+
assert.calledWith(
|
9944
|
+
TriggerProxy.trigger,
|
9945
|
+
meeting,
|
9946
|
+
{file: 'meeting/index', function: 'setupLocusControlsListener'},
|
9947
|
+
EVENT_TRIGGERS.MEETING_CONTROLS_ANNOTATION_UPDATED,
|
9948
|
+
{state}
|
9949
|
+
);
|
9950
|
+
});
|
9951
|
+
|
9952
|
+
it('listens to CONTROLS_REMOTE_DESKTOP_CONTROL_CHANGED', async () => {
|
9953
|
+
const state = {example: 'value'};
|
9954
|
+
|
9955
|
+
await meeting.locusInfo.emitScoped(
|
9956
|
+
{function: 'test', file: 'test'},
|
9957
|
+
LOCUSINFO.EVENTS.CONTROLS_REMOTE_DESKTOP_CONTROL_CHANGED,
|
9958
|
+
{state}
|
9959
|
+
);
|
9960
|
+
|
9961
|
+
assert.calledWith(
|
9962
|
+
TriggerProxy.trigger,
|
9963
|
+
meeting,
|
9964
|
+
{file: 'meeting/index', function: 'setupLocusControlsListener'},
|
9965
|
+
EVENT_TRIGGERS.MEETING_CONTROLS_REMOTE_DESKTOP_CONTROL_UPDATED,
|
9966
|
+
{state}
|
9967
|
+
);
|
9968
|
+
});
|
9969
|
+
|
9593
9970
|
it('listens to the locus interpretation update event', () => {
|
9594
9971
|
const interpretation = {
|
9595
9972
|
siLanguages: [{languageCode: 20, languageName: 'en'}],
|
@@ -9928,6 +10305,22 @@ describe('plugin-meetings', () => {
|
|
9928
10305
|
});
|
9929
10306
|
});
|
9930
10307
|
|
10308
|
+
describe('#emailInput', () => {
|
10309
|
+
it('should set the email input', () => {
|
10310
|
+
assert.notOk(meeting.emailInput);
|
10311
|
+
meeting.emailInput = 'current';
|
10312
|
+
assert.equal(meeting.emailInput, 'current');
|
10313
|
+
});
|
10314
|
+
});
|
10315
|
+
|
10316
|
+
describe('#userNameInput', () => {
|
10317
|
+
it('should set the user name input', () => {
|
10318
|
+
assert.notOk(meeting.userNameInput);
|
10319
|
+
meeting.userNameInput = 'current';
|
10320
|
+
assert.equal(meeting.userNameInput, 'current');
|
10321
|
+
});
|
10322
|
+
});
|
10323
|
+
|
9931
10324
|
describe('#setPermissionTokenPayload', () => {
|
9932
10325
|
let now;
|
9933
10326
|
let clock;
|
@@ -10469,9 +10862,11 @@ describe('plugin-meetings', () => {
|
|
10469
10862
|
let canUserLowerSomeoneElsesHandSpy;
|
10470
10863
|
let waitingForOthersToJoinSpy;
|
10471
10864
|
let canSendReactionsSpy;
|
10865
|
+
let requiresPostMeetingDataConsentPromptSpy;
|
10472
10866
|
let canUserRenameSelfAndObservedSpy;
|
10473
10867
|
let canUserRenameOthersSpy;
|
10474
10868
|
let canShareWhiteBoardSpy;
|
10869
|
+
let canMoveToLobbySpy;
|
10475
10870
|
// Due to import tree issues, hasHints must be stubed within the scope of the `it`.
|
10476
10871
|
|
10477
10872
|
beforeEach(() => {
|
@@ -10496,8 +10891,13 @@ describe('plugin-meetings', () => {
|
|
10496
10891
|
waitingForOthersToJoinSpy = sinon.spy(MeetingUtil, 'waitingForOthersToJoin');
|
10497
10892
|
canSendReactionsSpy = sinon.spy(MeetingUtil, 'canSendReactions');
|
10498
10893
|
canUserRenameSelfAndObservedSpy = sinon.spy(MeetingUtil, 'canUserRenameSelfAndObserved');
|
10894
|
+
requiresPostMeetingDataConsentPromptSpy = sinon.spy(
|
10895
|
+
MeetingUtil,
|
10896
|
+
'requiresPostMeetingDataConsentPrompt'
|
10897
|
+
);
|
10499
10898
|
canUserRenameOthersSpy = sinon.spy(MeetingUtil, 'canUserRenameOthers');
|
10500
10899
|
canShareWhiteBoardSpy = sinon.spy(MeetingUtil, 'canShareWhiteBoard');
|
10900
|
+
canMoveToLobbySpy = sinon.spy(MeetingUtil, 'canMoveToLobby');
|
10501
10901
|
});
|
10502
10902
|
|
10503
10903
|
afterEach(() => {
|
@@ -10595,6 +10995,16 @@ describe('plugin-meetings', () => {
|
|
10595
10995
|
requiredDisplayHints: [],
|
10596
10996
|
requiredPolicies: [SELF_POLICY.SUPPORT_FILE_TRANSFER],
|
10597
10997
|
},
|
10998
|
+
{
|
10999
|
+
actionName: 'canRealtimeCloseCaption',
|
11000
|
+
requiredDisplayHints: [],
|
11001
|
+
requiredPolicies: [SELF_POLICY.SUPPORT_REALTIME_CLOSE_CAPTION],
|
11002
|
+
},
|
11003
|
+
{
|
11004
|
+
actionName: 'canRealtimeCloseCaptionManual',
|
11005
|
+
requiredDisplayHints: [],
|
11006
|
+
requiredPolicies: [SELF_POLICY.SUPPORT_REALTIME_CLOSE_CAPTION_MANUAL],
|
11007
|
+
},
|
10598
11008
|
{
|
10599
11009
|
actionName: 'canChat',
|
10600
11010
|
requiredDisplayHints: [],
|
@@ -10624,6 +11034,11 @@ describe('plugin-meetings', () => {
|
|
10624
11034
|
requiredDisplayHints: [],
|
10625
11035
|
requiredPolicies: [SELF_POLICY.SUPPORT_POLLING_AND_QA],
|
10626
11036
|
},
|
11037
|
+
{
|
11038
|
+
actionName: 'canShareWhiteBoard',
|
11039
|
+
requiredDisplayHints: [DISPLAY_HINTS.SHARE_WHITEBOARD],
|
11040
|
+
requiredPolicies: [SELF_POLICY.SUPPORT_WHITEBOARD],
|
11041
|
+
},
|
10627
11042
|
],
|
10628
11043
|
({
|
10629
11044
|
actionName,
|
@@ -11031,8 +11446,10 @@ describe('plugin-meetings', () => {
|
|
11031
11446
|
assert.calledWith(waitingForOthersToJoinSpy, userDisplayHints);
|
11032
11447
|
assert.calledWith(canSendReactionsSpy, null, userDisplayHints);
|
11033
11448
|
assert.calledWith(canUserRenameSelfAndObservedSpy, userDisplayHints);
|
11449
|
+
assert.calledWith(requiresPostMeetingDataConsentPromptSpy, userDisplayHints);
|
11034
11450
|
assert.calledWith(canUserRenameOthersSpy, userDisplayHints);
|
11035
|
-
assert.calledWith(canShareWhiteBoardSpy, userDisplayHints);
|
11451
|
+
assert.calledWith(canShareWhiteBoardSpy, userDisplayHints, selfUserPolicies);
|
11452
|
+
assert.calledWith(canMoveToLobbySpy, userDisplayHints);
|
11036
11453
|
|
11037
11454
|
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
11038
11455
|
requiredHints: [DISPLAY_HINTS.MUTE_ALL],
|
@@ -11126,6 +11543,22 @@ describe('plugin-meetings', () => {
|
|
11126
11543
|
requiredPolicies: [SELF_POLICY.SUPPORT_VOIP],
|
11127
11544
|
policies: selfUserPolicies,
|
11128
11545
|
});
|
11546
|
+
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
11547
|
+
requiredHints: [DISPLAY_HINTS.ENABLE_ANNOTATION_MEETING_OPTION],
|
11548
|
+
displayHints: userDisplayHints,
|
11549
|
+
});
|
11550
|
+
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
11551
|
+
requiredHints: [DISPLAY_HINTS.DISABLE_ANNOTATION_MEETING_OPTION],
|
11552
|
+
displayHints: userDisplayHints,
|
11553
|
+
});
|
11554
|
+
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
11555
|
+
requiredHints: [DISPLAY_HINTS.ENABLE_RDC_MEETING_OPTION],
|
11556
|
+
displayHints: userDisplayHints,
|
11557
|
+
});
|
11558
|
+
assert.calledWith(ControlsOptionsUtil.hasHints, {
|
11559
|
+
requiredHints: [DISPLAY_HINTS.DISABLE_RDC_MEETING_OPTION],
|
11560
|
+
displayHints: userDisplayHints,
|
11561
|
+
});
|
11129
11562
|
|
11130
11563
|
assert.calledWith(
|
11131
11564
|
TriggerProxy.trigger,
|
@@ -11972,6 +12405,8 @@ describe('plugin-meetings', () => {
|
|
11972
12405
|
// Set the webinar attendee flag
|
11973
12406
|
meeting.webinar = {selfIsAttendee: true};
|
11974
12407
|
meeting.locusInfo.info.isWebinar = true;
|
12408
|
+
meeting.shareCAEventSentStatus.receiveStart = true;
|
12409
|
+
meeting.shareCAEventSentStatus.receiveStop = true;
|
11975
12410
|
|
11976
12411
|
// Step 1: Start sharing whiteboard A
|
11977
12412
|
const data1 = generateData(
|
@@ -11995,6 +12430,8 @@ describe('plugin-meetings', () => {
|
|
11995
12430
|
|
11996
12431
|
// Specific assertions for webinar attendee status
|
11997
12432
|
assert.equal(meeting.shareStatus, SHARE_STATUS.REMOTE_SHARE_ACTIVE);
|
12433
|
+
assert.equal(meeting.shareCAEventSentStatus.receiveStart, false);
|
12434
|
+
assert.equal(meeting.shareCAEventSentStatus.receiveStop, false);
|
11998
12435
|
});
|
11999
12436
|
});
|
12000
12437
|
|
@@ -12650,6 +13087,31 @@ describe('plugin-meetings', () => {
|
|
12650
13087
|
});
|
12651
13088
|
});
|
12652
13089
|
});
|
13090
|
+
|
13091
|
+
describe('handleShareVideoStreamMuteStateChange', () => {
|
13092
|
+
it('should emit MEETING_SHARE_VIDEO_MUTE_STATE_CHANGE event with correct fields', () => {
|
13093
|
+
meeting.isMultistream = true;
|
13094
|
+
meeting.statsAnalyzer = {shareVideoEncoderImplementation: 'OpenH264'};
|
13095
|
+
meeting.mediaProperties.shareVideoStream = {
|
13096
|
+
getSettings: sinon.stub().returns({displaySurface: 'monitor', frameRate: 30}),
|
13097
|
+
};
|
13098
|
+
|
13099
|
+
meeting.handleShareVideoStreamMuteStateChange(true);
|
13100
|
+
|
13101
|
+
assert.calledOnceWithExactly(
|
13102
|
+
Metrics.sendBehavioralMetric,
|
13103
|
+
BEHAVIORAL_METRICS.MEETING_SHARE_VIDEO_MUTE_STATE_CHANGE,
|
13104
|
+
{
|
13105
|
+
correlationId: meeting.correlationId,
|
13106
|
+
muted: true,
|
13107
|
+
encoderImplementation: 'OpenH264',
|
13108
|
+
displaySurface: 'monitor',
|
13109
|
+
isMultistream: true,
|
13110
|
+
frameRate: 30,
|
13111
|
+
}
|
13112
|
+
);
|
13113
|
+
});
|
13114
|
+
});
|
12653
13115
|
});
|
12654
13116
|
|
12655
13117
|
describe('#startKeepAlive', () => {
|
@@ -12817,6 +13279,38 @@ describe('plugin-meetings', () => {
|
|
12817
13279
|
});
|
12818
13280
|
});
|
12819
13281
|
|
13282
|
+
describe('#setPostMeetingDataConsent', () => {
|
13283
|
+
it('should have #setPostMeetingDataConsent', () => {
|
13284
|
+
assert.exists(meeting.setPostMeetingDataConsent);
|
13285
|
+
});
|
13286
|
+
|
13287
|
+
beforeEach(() => {
|
13288
|
+
meeting.meetingRequest.setPostMeetingDataConsent = sinon
|
13289
|
+
.stub()
|
13290
|
+
.returns(Promise.resolve());
|
13291
|
+
});
|
13292
|
+
|
13293
|
+
[true, false].forEach((accept) => {
|
13294
|
+
it(`should send consent with ${accept}`, async () => {
|
13295
|
+
const id = uuidv4();
|
13296
|
+
meeting.locusUrl = `https://locus-test.wbx2.com/locus/api/v1/loci/${accept}`;
|
13297
|
+
meeting.deviceUrl = `https://wdm-test.wbx2.com/wdm/api/v1/devices/${accept}`;
|
13298
|
+
meeting.members.selfId = id;
|
13299
|
+
|
13300
|
+
const consentPromise = meeting.setPostMeetingDataConsent(accept);
|
13301
|
+
|
13302
|
+
assert.exists(consentPromise.then);
|
13303
|
+
await consentPromise;
|
13304
|
+
assert.calledOnceWithExactly(meeting.meetingRequest.setPostMeetingDataConsent, {
|
13305
|
+
locusUrl: `https://locus-test.wbx2.com/locus/api/v1/loci/${accept}`,
|
13306
|
+
postMeetingDataConsent: accept,
|
13307
|
+
selfId: id,
|
13308
|
+
deviceUrl: `https://wdm-test.wbx2.com/wdm/api/v1/devices/${accept}`,
|
13309
|
+
});
|
13310
|
+
});
|
13311
|
+
});
|
13312
|
+
});
|
13313
|
+
|
12820
13314
|
describe('#sendReaction', () => {
|
12821
13315
|
it('should have #sendReaction', () => {
|
12822
13316
|
assert.exists(meeting.sendReaction);
|