@webex/plugin-meetings 3.9.0-next.2 → 3.9.0-next.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/constants.js +2 -0
- package/dist/constants.js.map +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/index.js +38 -10
- package/dist/locus-info/index.js.map +1 -1
- package/dist/locus-info/parser.js +4 -1
- package/dist/locus-info/parser.js.map +1 -1
- package/dist/media/properties.js +53 -5
- package/dist/media/properties.js.map +1 -1
- package/dist/meeting/in-meeting-actions.js +2 -0
- package/dist/meeting/in-meeting-actions.js.map +1 -1
- package/dist/meeting/index.js +189 -122
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/muteState.js +2 -5
- package/dist/meeting/muteState.js.map +1 -1
- package/dist/meeting/request.js +25 -0
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/util.js +30 -11
- package/dist/meeting/util.js.map +1 -1
- package/dist/meeting-info/meeting-info-v2.js +29 -21
- package/dist/meeting-info/meeting-info-v2.js.map +1 -1
- package/dist/meetings/index.js +31 -25
- package/dist/meetings/index.js.map +1 -1
- package/dist/member/types.js.map +1 -1
- package/dist/members/collection.js +13 -0
- package/dist/members/collection.js.map +1 -1
- package/dist/members/index.js +42 -20
- package/dist/members/index.js.map +1 -1
- package/dist/members/util.js +7 -2
- package/dist/members/util.js.map +1 -1
- package/dist/metrics/constants.js +2 -1
- package/dist/metrics/constants.js.map +1 -1
- package/dist/reachability/index.js +3 -3
- package/dist/reachability/index.js.map +1 -1
- package/dist/types/constants.d.ts +2 -0
- package/dist/types/locus-info/index.d.ts +54 -1
- package/dist/types/media/properties.d.ts +21 -0
- package/dist/types/meeting/in-meeting-actions.d.ts +2 -0
- package/dist/types/meeting/index.d.ts +11 -1
- package/dist/types/meeting/request.d.ts +9 -0
- package/dist/types/meeting/util.d.ts +10 -3
- package/dist/types/meeting-info/meeting-info-v2.d.ts +6 -3
- package/dist/types/meetings/index.d.ts +3 -1
- package/dist/types/member/types.d.ts +1 -0
- package/dist/types/members/collection.d.ts +6 -0
- package/dist/types/members/index.d.ts +12 -2
- package/dist/types/members/util.d.ts +6 -3
- package/dist/types/metrics/constants.d.ts +1 -0
- package/dist/webinar/index.js +1 -1
- package/package.json +16 -16
- package/src/constants.ts +2 -0
- package/src/locus-info/index.ts +84 -9
- package/src/locus-info/parser.ts +5 -1
- package/src/media/properties.ts +43 -0
- package/src/meeting/in-meeting-actions.ts +4 -0
- package/src/meeting/index.ts +91 -4
- package/src/meeting/muteState.ts +2 -6
- package/src/meeting/request.ts +23 -0
- package/src/meeting/util.ts +41 -20
- package/src/meeting-info/meeting-info-v2.ts +24 -5
- package/src/meetings/index.ts +9 -3
- package/src/member/types.ts +1 -0
- package/src/members/collection.ts +11 -0
- package/src/members/index.ts +38 -5
- package/src/members/util.ts +18 -2
- package/src/metrics/constants.ts +1 -0
- package/src/reachability/index.ts +3 -3
- package/test/unit/spec/locus-info/index.js +30 -15
- package/test/unit/spec/locus-info/parser.js +3 -2
- package/test/unit/spec/media/properties.ts +137 -0
- package/test/unit/spec/meeting/in-meeting-actions.ts +2 -0
- package/test/unit/spec/meeting/index.js +255 -27
- package/test/unit/spec/meeting/muteState.js +32 -6
- package/test/unit/spec/meeting/request.js +21 -0
- package/test/unit/spec/meeting/utils.js +45 -16
- package/test/unit/spec/meeting-info/meetinginfov2.js +8 -3
- package/test/unit/spec/meetings/index.js +10 -1
- package/test/unit/spec/members/collection.js +120 -0
- package/test/unit/spec/members/index.js +72 -3
- package/test/unit/spec/members/request.js +55 -0
- package/test/unit/spec/members/utils.js +116 -14
- package/test/unit/spec/reachability/index.ts +158 -3
package/src/members/index.ts
CHANGED
@@ -74,7 +74,11 @@ import {Invitee} from '../meeting/type';
|
|
74
74
|
* @memberof Members
|
75
75
|
*/
|
76
76
|
|
77
|
-
type UpdatedMembers = {
|
77
|
+
type UpdatedMembers = {
|
78
|
+
added: Array<Member>;
|
79
|
+
updated: Array<Member>;
|
80
|
+
removedIds?: Array<string>; // removed member ids
|
81
|
+
};
|
78
82
|
/**
|
79
83
|
* @class Members
|
80
84
|
*/
|
@@ -384,12 +388,18 @@ export default class Members extends StatelessWebexPlugin {
|
|
384
388
|
* when new participant updates come in, both delta and full participants, update them in members collection
|
385
389
|
* delta object in the event will have {updated, added} and full will be the full membersCollection
|
386
390
|
* @param {Object} payload
|
387
|
-
* @param {Object} payload.participants
|
391
|
+
* @param {Object} payload.participants new/updated participants
|
392
|
+
* @param {Boolean} payload.isReplace whether to replace the whole members collection
|
393
|
+
* @param {Object} payload.removedParticipantIds ids of the removed participants
|
388
394
|
* @returns {undefined}
|
389
395
|
* @private
|
390
396
|
* @memberof Members
|
391
397
|
*/
|
392
|
-
locusParticipantsUpdate(payload: {
|
398
|
+
locusParticipantsUpdate(payload: {
|
399
|
+
participants: object;
|
400
|
+
isReplace?: boolean;
|
401
|
+
removedParticipantIds?: Array<string>;
|
402
|
+
}) {
|
393
403
|
if (payload) {
|
394
404
|
if (payload.isReplace) {
|
395
405
|
this.clearMembers();
|
@@ -547,10 +557,22 @@ export default class Members extends StatelessWebexPlugin {
|
|
547
557
|
private handleMembersUpdate(membersUpdate: UpdatedMembers) {
|
548
558
|
this.constructMembers(membersUpdate.updated, true);
|
549
559
|
this.constructMembers(membersUpdate.added, false);
|
560
|
+
this.removeMembers(membersUpdate.removedIds);
|
550
561
|
|
551
562
|
return this.membersCollection.getAll();
|
552
563
|
}
|
553
564
|
|
565
|
+
/**
|
566
|
+
* removes members from the collection
|
567
|
+
* @param {Array<string>} removedMembers removed members ids
|
568
|
+
* @returns {void}
|
569
|
+
*/
|
570
|
+
private removeMembers(removedMembers: Array<string>) {
|
571
|
+
removedMembers.forEach((memberId) => {
|
572
|
+
this.membersCollection.remove(memberId);
|
573
|
+
});
|
574
|
+
}
|
575
|
+
|
554
576
|
/**
|
555
577
|
* set members to the member collection from each updated/added lists as passed in
|
556
578
|
* @param {Array} list
|
@@ -600,6 +622,10 @@ export default class Members extends StatelessWebexPlugin {
|
|
600
622
|
}
|
601
623
|
const memberUpdate = this.update(payload.participants);
|
602
624
|
|
625
|
+
// this code depends on memberIds being the same as participantIds
|
626
|
+
// if MemberUtil.extractId() ever changes, this will need to be updated
|
627
|
+
memberUpdate.removedIds = payload.removedParticipantIds || [];
|
628
|
+
|
603
629
|
return memberUpdate;
|
604
630
|
}
|
605
631
|
|
@@ -1181,11 +1207,17 @@ export default class Members extends StatelessWebexPlugin {
|
|
1181
1207
|
* @param {string} memberId - id of the participant who is receiving request
|
1182
1208
|
* @param {string} requestingParticipantId - id of the participant who is sending request (optional)
|
1183
1209
|
* @param {string} [alias] - alias name
|
1210
|
+
* @param {string} [suffix] - name suffix (optional)
|
1184
1211
|
* @returns {Promise}
|
1185
1212
|
* @public
|
1186
1213
|
* @memberof Members
|
1187
1214
|
*/
|
1188
|
-
public editDisplayName(
|
1215
|
+
public editDisplayName(
|
1216
|
+
memberId: string,
|
1217
|
+
requestingParticipantId: string,
|
1218
|
+
alias: string,
|
1219
|
+
suffix?: string
|
1220
|
+
) {
|
1189
1221
|
if (!this.locusUrl) {
|
1190
1222
|
return Promise.reject(
|
1191
1223
|
new ParameterError(
|
@@ -1205,7 +1237,8 @@ export default class Members extends StatelessWebexPlugin {
|
|
1205
1237
|
memberId,
|
1206
1238
|
requestingParticipantId,
|
1207
1239
|
alias,
|
1208
|
-
locusUrl
|
1240
|
+
locusUrl,
|
1241
|
+
suffix
|
1209
1242
|
);
|
1210
1243
|
|
1211
1244
|
return this.membersRequest.editDisplayNameMember(options);
|
package/src/members/util.ts
CHANGED
@@ -190,13 +190,21 @@ const MembersUtil = {
|
|
190
190
|
* @param {String} requestingParticipantId id of the participant who is sending request (optional)
|
191
191
|
* @param {String} alias alias name
|
192
192
|
* @param {String} locusUrl url
|
193
|
+
* @param {String} suffix optional suffix
|
193
194
|
* @returns {Object} consists of {memberID: string, requestingParticipantId: string, alias: string, locusUrl: string}
|
194
195
|
*/
|
195
|
-
generateEditDisplayNameMemberOptions: (
|
196
|
+
generateEditDisplayNameMemberOptions: (
|
196
197
|
memberId,
|
197
198
|
requestingParticipantId,
|
198
199
|
alias,
|
199
200
|
locusUrl,
|
201
|
+
suffix
|
202
|
+
) => ({
|
203
|
+
memberId,
|
204
|
+
requestingParticipantId,
|
205
|
+
alias,
|
206
|
+
locusUrl,
|
207
|
+
suffix,
|
200
208
|
}),
|
201
209
|
|
202
210
|
getMuteMemberRequestParams: (options) => {
|
@@ -301,10 +309,18 @@ const MembersUtil = {
|
|
301
309
|
* @returns {Object} request parameters (method, uri, body) needed to make a editDisplayName request
|
302
310
|
*/
|
303
311
|
editDisplayNameMemberRequestParams: (options) => {
|
304
|
-
const body
|
312
|
+
const body: {
|
313
|
+
aliasValue: string;
|
314
|
+
requestingParticipantId: string;
|
315
|
+
suffixValue?: string;
|
316
|
+
} = {
|
305
317
|
aliasValue: options.alias,
|
306
318
|
requestingParticipantId: options.requestingParticipantId,
|
307
319
|
};
|
320
|
+
|
321
|
+
if (options.suffix !== undefined) {
|
322
|
+
body.suffixValue = options.suffix;
|
323
|
+
}
|
308
324
|
const uri = `${options.locusUrl}/${PARTICIPANT}/${options.memberId}/${ALIAS}`;
|
309
325
|
|
310
326
|
return {
|
package/src/metrics/constants.ts
CHANGED
@@ -86,6 +86,7 @@ const BEHAVIORAL_METRICS = {
|
|
86
86
|
VERIFY_REGISTRATION_ID_SUCCESS: 'js_sdk_verify_registrationId_success',
|
87
87
|
VERIFY_REGISTRATION_ID_ERROR: 'js_sdk_verify_registrationId_error',
|
88
88
|
JOIN_FORBIDDEN_ERROR: 'js_sdk_join_forbidden_error',
|
89
|
+
MEDIA_ISSUE_DETECTED: 'js_sdk_media_issue_detected',
|
89
90
|
};
|
90
91
|
|
91
92
|
export {BEHAVIORAL_METRICS as default};
|
@@ -923,10 +923,10 @@ export default class Reachability extends EventsScope {
|
|
923
923
|
|
924
924
|
// update expected results counters to include this cluster
|
925
925
|
this.expectedResultsCount[cluster.isVideoMesh ? 'videoMesh' : 'public'].udp +=
|
926
|
-
cluster.udp.length;
|
926
|
+
cluster.udp.length > 0 ? 1 : 0;
|
927
927
|
if (!cluster.isVideoMesh) {
|
928
|
-
this.expectedResultsCount.public.tcp += cluster.tcp.length;
|
929
|
-
this.expectedResultsCount.public.xtls += cluster.xtls.length;
|
928
|
+
this.expectedResultsCount.public.tcp += cluster.tcp.length > 0 ? 1 : 0;
|
929
|
+
this.expectedResultsCount.public.xtls += cluster.xtls.length > 0 ? 1 : 0;
|
930
930
|
}
|
931
931
|
});
|
932
932
|
|
@@ -772,7 +772,7 @@ describe('plugin-meetings', () => {
|
|
772
772
|
},
|
773
773
|
};
|
774
774
|
locusInfo.emitScoped = sinon.stub();
|
775
|
-
locusInfo.updateParticipants({});
|
775
|
+
locusInfo.updateParticipants({}, []);
|
776
776
|
|
777
777
|
// if this assertion fails, double-check the attributes used in
|
778
778
|
// the updateParticipants function in locus-info/index.js
|
@@ -790,6 +790,7 @@ describe('plugin-meetings', () => {
|
|
790
790
|
selfId: '2',
|
791
791
|
hostId: '3',
|
792
792
|
isReplace: undefined,
|
793
|
+
removedParticipantIds: [],
|
793
794
|
}
|
794
795
|
);
|
795
796
|
// note: in a real use case, recordingId, selfId, and hostId would all be the same
|
@@ -814,7 +815,7 @@ describe('plugin-meetings', () => {
|
|
814
815
|
};
|
815
816
|
|
816
817
|
locusInfo.emitScoped = sinon.stub();
|
817
|
-
locusInfo.updateParticipants({}, true);
|
818
|
+
locusInfo.updateParticipants({}, [], true);
|
818
819
|
|
819
820
|
assert.calledWith(
|
820
821
|
locusInfo.emitScoped,
|
@@ -830,6 +831,7 @@ describe('plugin-meetings', () => {
|
|
830
831
|
selfId: '2',
|
831
832
|
hostId: '3',
|
832
833
|
isReplace: true,
|
834
|
+
removedParticipantIds: [],
|
833
835
|
}
|
834
836
|
);
|
835
837
|
});
|
@@ -847,7 +849,7 @@ describe('plugin-meetings', () => {
|
|
847
849
|
];
|
848
850
|
|
849
851
|
locusInfo.emitScoped = sinon.stub();
|
850
|
-
locusInfo.updateParticipants(failureParticipant);
|
852
|
+
locusInfo.updateParticipants(failureParticipant, []);
|
851
853
|
assert.calledWith(
|
852
854
|
locusInfo.emitScoped,
|
853
855
|
{
|
@@ -2038,6 +2040,18 @@ describe('plugin-meetings', () => {
|
|
2038
2040
|
});
|
2039
2041
|
});
|
2040
2042
|
|
2043
|
+
describe('#handleLocusAPIResponse', () => {
|
2044
|
+
it('calls handleLocusDelta', () => {
|
2045
|
+
const fakeLocus = {eventType: LOCUSEVENT.DIFFERENCE};
|
2046
|
+
|
2047
|
+
sinon.stub(locusInfo, 'handleLocusDelta');
|
2048
|
+
|
2049
|
+
locusInfo.handleLocusAPIResponse(mockMeeting, {locus: fakeLocus});
|
2050
|
+
|
2051
|
+
assert.calledWith(locusInfo.handleLocusDelta, fakeLocus, mockMeeting);
|
2052
|
+
});
|
2053
|
+
});
|
2054
|
+
|
2041
2055
|
describe('#LocusDeltaEvents', () => {
|
2042
2056
|
const fakeMeeting = 'fakeMeeting';
|
2043
2057
|
let sandbox = null;
|
@@ -2050,7 +2064,7 @@ describe('plugin-meetings', () => {
|
|
2050
2064
|
|
2051
2065
|
fakeLocus = {
|
2052
2066
|
meeting: true,
|
2053
|
-
participants:
|
2067
|
+
participants: [],
|
2054
2068
|
url: 'newLocusUrl',
|
2055
2069
|
syncUrl: 'newSyncUrl',
|
2056
2070
|
};
|
@@ -2369,23 +2383,23 @@ describe('plugin-meetings', () => {
|
|
2369
2383
|
|
2370
2384
|
it('applyLocusDeltaData handles LOCUS_URL_CHANGED action correctly', () => {
|
2371
2385
|
const {LOCUS_URL_CHANGED} = LocusDeltaParser.loci;
|
2372
|
-
const
|
2386
|
+
const fakeFullLocus = {
|
2387
|
+
url: 'new full loci url',
|
2388
|
+
};
|
2373
2389
|
const meeting = {
|
2374
2390
|
meetingRequest: {
|
2375
|
-
getLocusDTO: sandbox.stub().resolves({body:
|
2391
|
+
getLocusDTO: sandbox.stub().resolves({body: fakeFullLocus}),
|
2376
2392
|
},
|
2377
2393
|
locusInfo: {
|
2378
2394
|
handleLocusDelta: sandbox.stub(),
|
2379
2395
|
},
|
2380
|
-
locusUrl: 'current locus url',
|
2396
|
+
locusUrl: 'current BO session locus url',
|
2381
2397
|
};
|
2382
2398
|
|
2383
|
-
locusInfo.locusParser.workingCopy =
|
2384
|
-
syncUrl: 'current sync url',
|
2385
|
-
};
|
2399
|
+
locusInfo.locusParser.workingCopy = null;
|
2386
2400
|
|
2387
2401
|
locusInfo.applyLocusDeltaData(LOCUS_URL_CHANGED, fakeLocus, meeting);
|
2388
|
-
assert.calledOnceWithExactly(meeting.meetingRequest.getLocusDTO, {url:
|
2402
|
+
assert.calledOnceWithExactly(meeting.meetingRequest.getLocusDTO, {url: fakeLocus.url});
|
2389
2403
|
});
|
2390
2404
|
|
2391
2405
|
describe('edge cases for sync failing', () => {
|
@@ -2525,7 +2539,7 @@ describe('plugin-meetings', () => {
|
|
2525
2539
|
});
|
2526
2540
|
|
2527
2541
|
it('onDeltaLocus handle delta data', () => {
|
2528
|
-
fakeLocus.participants =
|
2542
|
+
fakeLocus.participants = [];
|
2529
2543
|
const fakeBreakout = {
|
2530
2544
|
sessionId: 'sessionId',
|
2531
2545
|
groupId: 'groupId',
|
@@ -2542,11 +2556,11 @@ describe('plugin-meetings', () => {
|
|
2542
2556
|
};
|
2543
2557
|
locusInfo.updateParticipants = sinon.stub();
|
2544
2558
|
locusInfo.onDeltaLocus(fakeLocus);
|
2545
|
-
assert.calledWith(locusInfo.updateParticipants,
|
2559
|
+
assert.calledWith(locusInfo.updateParticipants, [], undefined, false);
|
2546
2560
|
|
2547
2561
|
fakeLocus.controls.breakout.sessionId = 'sessionId2';
|
2548
2562
|
locusInfo.onDeltaLocus(fakeLocus);
|
2549
|
-
assert.calledWith(locusInfo.updateParticipants,
|
2563
|
+
assert.calledWith(locusInfo.updateParticipants, [], undefined, true);
|
2550
2564
|
});
|
2551
2565
|
|
2552
2566
|
it('onDeltaLocus merges delta participants with existing participants', () => {
|
@@ -2563,7 +2577,7 @@ describe('plugin-meetings', () => {
|
|
2563
2577
|
existingParticipants,
|
2564
2578
|
FAKE_DELTA_PARTICIPANTS
|
2565
2579
|
);
|
2566
|
-
assert.calledWith(locusInfo.updateParticipants, FAKE_DELTA_PARTICIPANTS, false);
|
2580
|
+
assert.calledWith(locusInfo.updateParticipants, FAKE_DELTA_PARTICIPANTS, undefined, false);
|
2567
2581
|
});
|
2568
2582
|
|
2569
2583
|
[true, false].forEach((isDelta) =>
|
@@ -3074,6 +3088,7 @@ describe('plugin-meetings', () => {
|
|
3074
3088
|
id: 'test person id',
|
3075
3089
|
},
|
3076
3090
|
},
|
3091
|
+
participants: [],
|
3077
3092
|
});
|
3078
3093
|
|
3079
3094
|
updateLocusInfoStub.resetHistory();
|
@@ -253,7 +253,8 @@ describe('locus-info/parser', () => {
|
|
253
253
|
});
|
254
254
|
|
255
255
|
it('replaces current loci when the locus URL changes and incoming sequence is later, even when baseSequence doesn\'t match', () => {
|
256
|
-
const {
|
256
|
+
const {LOCUS_URL_CHANGED} = LocusDeltaParser.loci;
|
257
|
+
sandbox.stub(LocusDeltaParser, 'compare').returns(LOCUS_URL_CHANGED);
|
257
258
|
|
258
259
|
parser.queue.dequeue = sandbox.stub().returns(NEW_LOCI);
|
259
260
|
parser.onDeltaAction = sandbox.stub();
|
@@ -262,7 +263,7 @@ describe('locus-info/parser', () => {
|
|
262
263
|
|
263
264
|
parser.processDeltaEvent();
|
264
265
|
|
265
|
-
assert.equal(parser.workingCopy,
|
266
|
+
assert.equal(parser.workingCopy, null);
|
266
267
|
});
|
267
268
|
|
268
269
|
it('does not replace current loci when the locus URL changes but incoming sequence is not later', () => {
|
@@ -6,6 +6,8 @@ import * as tsSdpModule from '@webex/ts-sdp';
|
|
6
6
|
import MediaProperties from '@webex/plugin-meetings/src/media/properties';
|
7
7
|
import {Defer} from '@webex/common';
|
8
8
|
import MediaConnectionAwaiter from '../../../../src/media/MediaConnectionAwaiter';
|
9
|
+
import Metrics from '../../../../src/metrics';
|
10
|
+
import BEHAVIORAL_METRICS from '../../../../src/metrics/constants';
|
9
11
|
|
10
12
|
describe('MediaProperties', () => {
|
11
13
|
let mediaProperties;
|
@@ -389,4 +391,139 @@ describe('MediaProperties', () => {
|
|
389
391
|
});
|
390
392
|
});
|
391
393
|
});
|
394
|
+
|
395
|
+
// issue types and subtypes used in these tests are just examples
|
396
|
+
// they don't reflect real issue types/subtypes used in production
|
397
|
+
describe('sendMediaIssueMetric', () => {
|
398
|
+
let sendBehavioralMetricStub;
|
399
|
+
let clock;
|
400
|
+
|
401
|
+
beforeEach(() => {
|
402
|
+
clock = sinon.useFakeTimers();
|
403
|
+
sendBehavioralMetricStub = sinon.stub(Metrics, 'sendBehavioralMetric');
|
404
|
+
});
|
405
|
+
|
406
|
+
afterEach(() => {
|
407
|
+
clock.restore();
|
408
|
+
});
|
409
|
+
|
410
|
+
it('should send a behavioral metric with correct parameters', () => {
|
411
|
+
const issueType = 'audio';
|
412
|
+
const issueSubType = 'packet-loss';
|
413
|
+
const correlationId = 'test-correlation-id-123';
|
414
|
+
|
415
|
+
mediaProperties.sendMediaIssueMetric(issueType, issueSubType, correlationId);
|
416
|
+
|
417
|
+
assert.calledOnce(sendBehavioralMetricStub);
|
418
|
+
assert.calledWith(sendBehavioralMetricStub, BEHAVIORAL_METRICS.MEDIA_ISSUE_DETECTED, {
|
419
|
+
correlationId,
|
420
|
+
'audio_packet-loss': 1,
|
421
|
+
});
|
422
|
+
});
|
423
|
+
|
424
|
+
it('should increment count while being throttled and reset it once metric goes out', () => {
|
425
|
+
const issueType = 'video';
|
426
|
+
const issueSubType = 'freeze';
|
427
|
+
const correlationId = 'test-correlation-id';
|
428
|
+
|
429
|
+
// Call multiple times with same issue type/subtype
|
430
|
+
mediaProperties.sendMediaIssueMetric(issueType, issueSubType, correlationId);
|
431
|
+
mediaProperties.sendMediaIssueMetric(issueType, issueSubType, correlationId);
|
432
|
+
mediaProperties.sendMediaIssueMetric(issueType, issueSubType, correlationId);
|
433
|
+
|
434
|
+
// First call should go through immediately, subsequent calls are throttled
|
435
|
+
assert.calledOnce(sendBehavioralMetricStub);
|
436
|
+
assert.calledWith(sendBehavioralMetricStub, BEHAVIORAL_METRICS.MEDIA_ISSUE_DETECTED, {
|
437
|
+
correlationId,
|
438
|
+
video_freeze: 1, // Only the first call goes through due to throttling
|
439
|
+
});
|
440
|
+
sendBehavioralMetricStub.resetHistory();
|
441
|
+
|
442
|
+
assert.equal(mediaProperties.mediaIssueCounters['video_freeze'], 2); // counter should be reset after the first metric goes out, hence only 2 not 3 here
|
443
|
+
|
444
|
+
clock.tick(5 * 60 * 1000); // Advance time by 5 minutes to expire throttle
|
445
|
+
|
446
|
+
assert.calledOnceWithExactly(
|
447
|
+
sendBehavioralMetricStub,
|
448
|
+
BEHAVIORAL_METRICS.MEDIA_ISSUE_DETECTED,
|
449
|
+
{
|
450
|
+
correlationId,
|
451
|
+
video_freeze: 2,
|
452
|
+
}
|
453
|
+
);
|
454
|
+
});
|
455
|
+
|
456
|
+
it('should track different issue types separately in counters', () => {
|
457
|
+
const correlationId = 'test-correlation-id';
|
458
|
+
|
459
|
+
// Send different issue types
|
460
|
+
mediaProperties.sendMediaIssueMetric('audio', 'packet-loss', correlationId);
|
461
|
+
mediaProperties.sendMediaIssueMetric('video', 'freeze', correlationId);
|
462
|
+
mediaProperties.sendMediaIssueMetric('audio', 'packet-loss', correlationId);
|
463
|
+
mediaProperties.sendMediaIssueMetric('audio', 'packet-loss', correlationId);
|
464
|
+
mediaProperties.sendMediaIssueMetric('audio', 'packet-loss', correlationId);
|
465
|
+
mediaProperties.sendMediaIssueMetric('video', 'freeze', correlationId);
|
466
|
+
|
467
|
+
// First call should go through immediately, subsequent calls are throttled
|
468
|
+
assert.calledOnceWithExactly(
|
469
|
+
sendBehavioralMetricStub,
|
470
|
+
BEHAVIORAL_METRICS.MEDIA_ISSUE_DETECTED,
|
471
|
+
{
|
472
|
+
correlationId,
|
473
|
+
'audio_packet-loss': 1,
|
474
|
+
}
|
475
|
+
);
|
476
|
+
|
477
|
+
// But the counters should be tracked separately
|
478
|
+
assert.equal(mediaProperties.mediaIssueCounters['audio_packet-loss'], 3);
|
479
|
+
assert.equal(mediaProperties.mediaIssueCounters['video_freeze'], 2);
|
480
|
+
|
481
|
+
sendBehavioralMetricStub.resetHistory();
|
482
|
+
|
483
|
+
clock.tick(5 * 60 * 1000); // Advance time by 5 minutes to expire throttle
|
484
|
+
|
485
|
+
assert.calledOnceWithExactly(
|
486
|
+
sendBehavioralMetricStub,
|
487
|
+
BEHAVIORAL_METRICS.MEDIA_ISSUE_DETECTED,
|
488
|
+
{
|
489
|
+
correlationId,
|
490
|
+
video_freeze: 2,
|
491
|
+
'audio_packet-loss': 3,
|
492
|
+
}
|
493
|
+
);
|
494
|
+
});
|
495
|
+
|
496
|
+
it('should flush throttled metrics when unsetPeerConnection is called', () => {
|
497
|
+
const issueType = 'share';
|
498
|
+
const issueSubType = 'connection-lost';
|
499
|
+
const correlationId = 'test-correlation-id';
|
500
|
+
|
501
|
+
// Send metrics multiple times
|
502
|
+
mediaProperties.sendMediaIssueMetric(issueType, issueSubType, correlationId);
|
503
|
+
mediaProperties.sendMediaIssueMetric(issueType, issueSubType, correlationId);
|
504
|
+
|
505
|
+
// First call should go through immediately
|
506
|
+
assert.calledOnceWithExactly(
|
507
|
+
sendBehavioralMetricStub,
|
508
|
+
BEHAVIORAL_METRICS.MEDIA_ISSUE_DETECTED,
|
509
|
+
{
|
510
|
+
correlationId,
|
511
|
+
'share_connection-lost': 1,
|
512
|
+
}
|
513
|
+
);
|
514
|
+
sendBehavioralMetricStub.resetHistory();
|
515
|
+
|
516
|
+
// Call unsetPeerConnection which should flush throttled metrics
|
517
|
+
mediaProperties.unsetPeerConnection();
|
518
|
+
|
519
|
+
assert.calledOnceWithExactly(
|
520
|
+
sendBehavioralMetricStub,
|
521
|
+
BEHAVIORAL_METRICS.MEDIA_ISSUE_DETECTED,
|
522
|
+
{
|
523
|
+
correlationId,
|
524
|
+
'share_connection-lost': 1,
|
525
|
+
}
|
526
|
+
);
|
527
|
+
});
|
528
|
+
});
|
392
529
|
});
|
@@ -38,6 +38,7 @@ describe('plugin-meetings', () => {
|
|
38
38
|
isManualCaptionActive: null,
|
39
39
|
isPremiseRecordingEnabled: null,
|
40
40
|
isSaveTranscriptsEnabled: null,
|
41
|
+
isSpokenLanguageAutoDetectionEnabled: null,
|
41
42
|
isWebexAssistantActive: null,
|
42
43
|
canViewCaptionPanel: null,
|
43
44
|
isRealTimeTranslationEnabled: null,
|
@@ -151,6 +152,7 @@ describe('plugin-meetings', () => {
|
|
151
152
|
'isManualCaptionActive',
|
152
153
|
'isPremiseRecordingEnabled',
|
153
154
|
'isSaveTranscriptsEnabled',
|
155
|
+
'isSpokenLanguageAutoDetectionEnabled',
|
154
156
|
'isWebexAssistantActive',
|
155
157
|
'canViewCaptionPanel',
|
156
158
|
'isRealTimeTranslationEnabled',
|