@webex/plugin-meetings 3.9.0-webinar5k.1 → 3.10.0
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 +24 -0
- package/dist/constants.js.map +1 -1
- package/dist/controls-options-manager/index.js +22 -5
- package/dist/controls-options-manager/index.js.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/interceptors/index.js +7 -0
- package/dist/interceptors/index.js.map +1 -1
- package/dist/interceptors/locusRouteToken.js +116 -0
- package/dist/interceptors/locusRouteToken.js.map +1 -0
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/controlsUtils.js +11 -2
- package/dist/locus-info/controlsUtils.js.map +1 -1
- package/dist/locus-info/index.js +76 -322
- 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 +14 -0
- package/dist/meeting/in-meeting-actions.js.map +1 -1
- package/dist/meeting/index.js +467 -277
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/request.js +177 -14
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/type.js +7 -0
- package/dist/meeting/type.js.map +1 -0
- package/dist/meeting/util.js +100 -3
- 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 +20 -16
- package/dist/meetings/index.js.map +1 -1
- package/dist/member/index.js +9 -0
- package/dist/member/index.js.map +1 -1
- package/dist/member/util.js +10 -0
- package/dist/member/util.js.map +1 -1
- package/dist/members/index.js +10 -7
- 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/multistream/mediaRequestManager.js +1 -1
- package/dist/multistream/mediaRequestManager.js.map +1 -1
- package/dist/multistream/remoteMedia.js +34 -5
- package/dist/multistream/remoteMedia.js.map +1 -1
- package/dist/multistream/remoteMediaGroup.js +42 -2
- package/dist/multistream/remoteMediaGroup.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 +23 -0
- package/dist/types/controls-options-manager/index.d.ts +9 -1
- package/dist/types/interceptors/index.d.ts +2 -1
- package/dist/types/interceptors/locusRouteToken.d.ts +38 -0
- package/dist/types/locus-info/index.d.ts +9 -54
- package/dist/types/media/properties.d.ts +21 -0
- package/dist/types/meeting/in-meeting-actions.d.ts +14 -0
- package/dist/types/meeting/index.d.ts +64 -29
- package/dist/types/meeting/request.d.ts +42 -0
- package/dist/types/meeting/type.d.ts +9 -0
- package/dist/types/meeting/util.d.ts +13 -0
- 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/index.d.ts +1 -0
- package/dist/types/member/util.d.ts +5 -0
- package/dist/types/members/index.d.ts +12 -11
- package/dist/types/members/util.d.ts +8 -4
- package/dist/types/metrics/constants.d.ts +1 -0
- package/dist/types/multistream/remoteMedia.d.ts +20 -1
- package/dist/types/multistream/remoteMediaGroup.d.ts +11 -0
- package/dist/webinar/index.js +1 -1
- package/package.json +25 -27
- package/src/constants.ts +26 -2
- package/src/controls-options-manager/index.ts +26 -5
- package/src/index.ts +2 -1
- package/src/interceptors/index.ts +2 -1
- package/src/interceptors/locusRouteToken.ts +80 -0
- package/src/locus-info/controlsUtils.ts +18 -0
- package/src/locus-info/index.ts +69 -357
- package/src/locus-info/parser.ts +5 -1
- package/src/media/properties.ts +43 -0
- package/src/meeting/in-meeting-actions.ts +29 -0
- package/src/meeting/index.ts +296 -87
- package/src/meeting/request.ts +141 -0
- package/src/meeting/type.ts +9 -0
- package/src/meeting/util.ts +107 -3
- package/src/meeting-info/meeting-info-v2.ts +24 -5
- package/src/meetings/index.ts +15 -22
- package/src/member/index.ts +10 -0
- package/src/member/util.ts +14 -0
- package/src/members/index.ts +20 -10
- package/src/members/util.ts +20 -3
- package/src/metrics/constants.ts +1 -0
- package/src/multistream/mediaRequestManager.ts +7 -7
- package/src/multistream/remoteMedia.ts +34 -4
- package/src/multistream/remoteMediaGroup.ts +37 -2
- package/src/reachability/index.ts +3 -3
- package/test/unit/spec/common/browser-detection.js +0 -24
- package/test/unit/spec/controls-options-manager/index.js +47 -0
- package/test/unit/spec/fixture/locus.js +1 -0
- package/test/unit/spec/interceptors/locusRouteToken.ts +87 -0
- package/test/unit/spec/locus-info/index.js +80 -361
- 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 +14 -0
- package/test/unit/spec/meeting/index.js +637 -53
- 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 +171 -18
- package/test/unit/spec/meeting-info/meetinginfov2.js +8 -3
- package/test/unit/spec/meetings/index.js +12 -5
- package/test/unit/spec/member/util.js +24 -0
- package/test/unit/spec/members/collection.js +120 -0
- package/test/unit/spec/members/index.js +107 -2
- package/test/unit/spec/members/request.js +55 -0
- package/test/unit/spec/members/utils.js +116 -14
- package/test/unit/spec/multistream/mediaRequestManager.ts +19 -6
- package/test/unit/spec/multistream/remoteMedia.ts +66 -2
- package/test/unit/spec/reachability/index.ts +158 -3
- package/test/unit/spec/roap/turnDiscovery.ts +3 -3
- package/dist/hashTree/constants.js +0 -23
- package/dist/hashTree/constants.js.map +0 -1
- package/dist/hashTree/hashTree.js +0 -516
- package/dist/hashTree/hashTree.js.map +0 -1
- package/dist/hashTree/hashTreeParser.js +0 -521
- package/dist/hashTree/hashTreeParser.js.map +0 -1
- package/dist/types/hashTree/constants.d.ts +0 -8
- package/dist/types/hashTree/hashTree.d.ts +0 -128
- package/dist/types/hashTree/hashTreeParser.d.ts +0 -152
- package/src/hashTree/constants.ts +0 -12
- package/src/hashTree/hashTree.ts +0 -460
- package/src/hashTree/hashTreeParser.ts +0 -556
- package/test/unit/spec/hashTree/hashTree.ts +0 -394
- package/test/unit/spec/hashTree/hashTreeParser.ts +0 -156
|
@@ -836,39 +836,6 @@ describe('plugin-meetings', () => {
|
|
|
836
836
|
);
|
|
837
837
|
});
|
|
838
838
|
|
|
839
|
-
it('should update the deltaParticipants object', () => {
|
|
840
|
-
const prev = locusInfo.deltaParticipants;
|
|
841
|
-
|
|
842
|
-
locusInfo.updateParticipantDeltas(newParticipants);
|
|
843
|
-
|
|
844
|
-
assert.notEqual(locusInfo.deltaParticipants, prev);
|
|
845
|
-
});
|
|
846
|
-
|
|
847
|
-
it('should update the delta property on all changed states', () => {
|
|
848
|
-
locusInfo.updateParticipantDeltas(newParticipants);
|
|
849
|
-
|
|
850
|
-
const [exampleParticipant] = locusInfo.deltaParticipants;
|
|
851
|
-
|
|
852
|
-
assert.isTrue(exampleParticipant.delta.audioStatus);
|
|
853
|
-
assert.isTrue(exampleParticipant.delta.videoSlidesStatus);
|
|
854
|
-
assert.isTrue(exampleParticipant.delta.videoStatus);
|
|
855
|
-
});
|
|
856
|
-
|
|
857
|
-
it('should include the person details of the changed participant', () => {
|
|
858
|
-
locusInfo.updateParticipantDeltas(newParticipants);
|
|
859
|
-
|
|
860
|
-
const [exampleParticipant] = locusInfo.deltaParticipants;
|
|
861
|
-
|
|
862
|
-
assert.equal(exampleParticipant.person, newParticipants[0].person);
|
|
863
|
-
});
|
|
864
|
-
|
|
865
|
-
it('should clear deltaParticipants when no changes occured', () => {
|
|
866
|
-
locusInfo.participants = [...newParticipants];
|
|
867
|
-
|
|
868
|
-
locusInfo.updateParticipantDeltas(locusInfo.participants);
|
|
869
|
-
|
|
870
|
-
assert.isTrue(locusInfo.deltaParticipants.length === 0);
|
|
871
|
-
});
|
|
872
839
|
|
|
873
840
|
it('should call with participant display name', () => {
|
|
874
841
|
const failureParticipant = [
|
|
@@ -1676,6 +1643,28 @@ describe('plugin-meetings', () => {
|
|
|
1676
1643
|
);
|
|
1677
1644
|
});
|
|
1678
1645
|
|
|
1646
|
+
it('should trigger MEETING_INFO_UPDATED even if the roles array is empty', () => {
|
|
1647
|
+
const initialInfo = cloneDeep(meetingInfo);
|
|
1648
|
+
|
|
1649
|
+
const updateSelf = cloneDeep(self);
|
|
1650
|
+
updateSelf.controls.role.roles = [];
|
|
1651
|
+
|
|
1652
|
+
locusInfo.emitScoped = sinon.stub();
|
|
1653
|
+
locusInfo.updateMeetingInfo(initialInfo, updateSelf);
|
|
1654
|
+
|
|
1655
|
+
assert.calledWith(
|
|
1656
|
+
locusInfo.emitScoped,
|
|
1657
|
+
{
|
|
1658
|
+
file: 'locus-info',
|
|
1659
|
+
function: 'updateMeetingInfo',
|
|
1660
|
+
},
|
|
1661
|
+
LOCUSINFO.EVENTS.MEETING_INFO_UPDATED,
|
|
1662
|
+
{
|
|
1663
|
+
isInitializing: !self,
|
|
1664
|
+
}
|
|
1665
|
+
);
|
|
1666
|
+
});
|
|
1667
|
+
|
|
1679
1668
|
const checkMeetingInfoUpdatedCalled = (expected, payload) => {
|
|
1680
1669
|
const expectedArgs = [
|
|
1681
1670
|
locusInfo.emitScoped,
|
|
@@ -2051,6 +2040,18 @@ describe('plugin-meetings', () => {
|
|
|
2051
2040
|
});
|
|
2052
2041
|
});
|
|
2053
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
|
+
|
|
2054
2055
|
describe('#LocusDeltaEvents', () => {
|
|
2055
2056
|
const fakeMeeting = 'fakeMeeting';
|
|
2056
2057
|
let sandbox = null;
|
|
@@ -2198,7 +2199,6 @@ describe('plugin-meetings', () => {
|
|
|
2198
2199
|
it('onFullLocus() updates the working-copy of locus parser', () => {
|
|
2199
2200
|
const eventType = 'fakeEvent';
|
|
2200
2201
|
|
|
2201
|
-
sandbox.stub(locusInfo, 'updateParticipantDeltas');
|
|
2202
2202
|
sandbox.stub(locusInfo, 'updateLocusInfo');
|
|
2203
2203
|
sandbox.stub(locusInfo, 'updateParticipants');
|
|
2204
2204
|
sandbox.stub(locusInfo, 'isMeetingActive');
|
|
@@ -2218,7 +2218,6 @@ describe('plugin-meetings', () => {
|
|
|
2218
2218
|
const oldWorkingCopy = locusParser.workingCopy;
|
|
2219
2219
|
|
|
2220
2220
|
const spies = [
|
|
2221
|
-
sandbox.stub(locusInfo, 'updateParticipantDeltas'),
|
|
2222
2221
|
sandbox.stub(locusInfo, 'updateLocusInfo'),
|
|
2223
2222
|
sandbox.stub(locusInfo, 'updateParticipants'),
|
|
2224
2223
|
sandbox.stub(locusInfo, 'isMeetingActive'),
|
|
@@ -2384,23 +2383,23 @@ describe('plugin-meetings', () => {
|
|
|
2384
2383
|
|
|
2385
2384
|
it('applyLocusDeltaData handles LOCUS_URL_CHANGED action correctly', () => {
|
|
2386
2385
|
const {LOCUS_URL_CHANGED} = LocusDeltaParser.loci;
|
|
2387
|
-
const
|
|
2386
|
+
const fakeFullLocus = {
|
|
2387
|
+
url: 'new full loci url',
|
|
2388
|
+
};
|
|
2388
2389
|
const meeting = {
|
|
2389
2390
|
meetingRequest: {
|
|
2390
|
-
getLocusDTO: sandbox.stub().resolves({body:
|
|
2391
|
+
getLocusDTO: sandbox.stub().resolves({body: fakeFullLocus}),
|
|
2391
2392
|
},
|
|
2392
2393
|
locusInfo: {
|
|
2393
2394
|
handleLocusDelta: sandbox.stub(),
|
|
2394
2395
|
},
|
|
2395
|
-
locusUrl: 'current locus url',
|
|
2396
|
+
locusUrl: 'current BO session locus url',
|
|
2396
2397
|
};
|
|
2397
2398
|
|
|
2398
|
-
locusInfo.locusParser.workingCopy =
|
|
2399
|
-
syncUrl: 'current sync url',
|
|
2400
|
-
};
|
|
2399
|
+
locusInfo.locusParser.workingCopy = null;
|
|
2401
2400
|
|
|
2402
2401
|
locusInfo.applyLocusDeltaData(LOCUS_URL_CHANGED, fakeLocus, meeting);
|
|
2403
|
-
assert.calledOnceWithExactly(meeting.meetingRequest.getLocusDTO, {url:
|
|
2402
|
+
assert.calledOnceWithExactly(meeting.meetingRequest.getLocusDTO, {url: fakeLocus.url});
|
|
2404
2403
|
});
|
|
2405
2404
|
|
|
2406
2405
|
describe('edge cases for sync failing', () => {
|
|
@@ -3021,6 +3020,45 @@ describe('plugin-meetings', () => {
|
|
|
3021
3020
|
});
|
|
3022
3021
|
});
|
|
3023
3022
|
|
|
3023
|
+
describe('#updateLocusUrl', () => {
|
|
3024
|
+
it('trigger LOCUS_INFO_UPDATE_URL event with isMainLocus is true as default', () => {
|
|
3025
|
+
const fakeUrl = "https://fake.com/locus";
|
|
3026
|
+
locusInfo.emitScoped = sinon.stub();
|
|
3027
|
+
locusInfo.updateLocusUrl(fakeUrl);
|
|
3028
|
+
|
|
3029
|
+
assert.calledWith(
|
|
3030
|
+
locusInfo.emitScoped,
|
|
3031
|
+
{
|
|
3032
|
+
file: 'locus-info',
|
|
3033
|
+
function: 'updateLocusUrl',
|
|
3034
|
+
},
|
|
3035
|
+
EVENTS.LOCUS_INFO_UPDATE_URL,
|
|
3036
|
+
{
|
|
3037
|
+
url: fakeUrl,
|
|
3038
|
+
isMainLocus: true
|
|
3039
|
+
},
|
|
3040
|
+
);
|
|
3041
|
+
});
|
|
3042
|
+
it('trigger LOCUS_INFO_UPDATE_URL event with isMainLocus is false', () => {
|
|
3043
|
+
const fakeUrl = "https://fake.com/locus";
|
|
3044
|
+
locusInfo.emitScoped = sinon.stub();
|
|
3045
|
+
locusInfo.updateLocusUrl(fakeUrl, false);
|
|
3046
|
+
|
|
3047
|
+
assert.calledWith(
|
|
3048
|
+
locusInfo.emitScoped,
|
|
3049
|
+
{
|
|
3050
|
+
file: 'locus-info',
|
|
3051
|
+
function: 'updateLocusUrl',
|
|
3052
|
+
},
|
|
3053
|
+
EVENTS.LOCUS_INFO_UPDATE_URL,
|
|
3054
|
+
{
|
|
3055
|
+
url: fakeUrl,
|
|
3056
|
+
isMainLocus: false
|
|
3057
|
+
},
|
|
3058
|
+
);
|
|
3059
|
+
});
|
|
3060
|
+
});
|
|
3061
|
+
|
|
3024
3062
|
// semi-integration tests that use real LocusInfo with real Parser
|
|
3025
3063
|
// and test various scenarios related to handling out-of-order Locus delta events
|
|
3026
3064
|
describe('handling of out-of-order Locus delta events', () => {
|
|
@@ -3066,7 +3104,6 @@ describe('plugin-meetings', () => {
|
|
|
3066
3104
|
beforeEach(() => {
|
|
3067
3105
|
clock = sinon.useFakeTimers();
|
|
3068
3106
|
|
|
3069
|
-
sinon.stub(locusInfo, 'updateParticipantDeltas');
|
|
3070
3107
|
sinon.stub(locusInfo, 'updateParticipants');
|
|
3071
3108
|
sinon.stub(locusInfo, 'isMeetingActive');
|
|
3072
3109
|
sinon.stub(locusInfo, 'handleOneOnOneEvent');
|
|
@@ -3239,7 +3276,6 @@ describe('plugin-meetings', () => {
|
|
|
3239
3276
|
await testUtils.flushPromises();
|
|
3240
3277
|
|
|
3241
3278
|
assert.calledOnceWithExactly(syncRequestStub, {url: mockMeeting.locusUrl});
|
|
3242
|
-
assert.calledOnce(updateLocusInfoStub);
|
|
3243
3279
|
assert.calledOnceWithExactly(updateLocusInfoStub, fullLocus);
|
|
3244
3280
|
});
|
|
3245
3281
|
|
|
@@ -3315,322 +3351,5 @@ describe('plugin-meetings', () => {
|
|
|
3315
3351
|
assert.calledWith(updateLocusInfoStub.getCall(2), deltaEvents[7]);
|
|
3316
3352
|
});
|
|
3317
3353
|
});
|
|
3318
|
-
|
|
3319
|
-
describe('Hash trees - webinar 5k', () => {
|
|
3320
|
-
let mockFullLocus;
|
|
3321
|
-
let clock;
|
|
3322
|
-
|
|
3323
|
-
beforeEach(() => {
|
|
3324
|
-
|
|
3325
|
-
sinon.stub(Math, 'random').returns(0.5); // to make sure the backoff timer is predictable
|
|
3326
|
-
|
|
3327
|
-
mockFullLocus = {
|
|
3328
|
-
dataSets: [
|
|
3329
|
-
{
|
|
3330
|
-
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/session/a73e9f2c/datasets/main',
|
|
3331
|
-
root: '9bb9d5a911a74d53a915b4dfbec7329f',
|
|
3332
|
-
version: 51118,
|
|
3333
|
-
leafCount: 16,
|
|
3334
|
-
name: 'main',
|
|
3335
|
-
idleMs : 5000,
|
|
3336
|
-
backoff : {
|
|
3337
|
-
maxMs : 500,
|
|
3338
|
-
exponent : 0.5
|
|
3339
|
-
},
|
|
3340
|
-
},
|
|
3341
|
-
{
|
|
3342
|
-
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/session/a73e9f2c/participant/713e9f99/datasets/self',
|
|
3343
|
-
root: '5b8cc7ffda1346d2bfb1c0b60b8ab601',
|
|
3344
|
-
version: 89891,
|
|
3345
|
-
leafCount: 1,
|
|
3346
|
-
name: 'self',
|
|
3347
|
-
idleMs : 10000,
|
|
3348
|
-
backoff : {
|
|
3349
|
-
maxMs : 500,
|
|
3350
|
-
exponent : 0.5
|
|
3351
|
-
},
|
|
3352
|
-
},
|
|
3353
|
-
{
|
|
3354
|
-
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/session/a73e9f2c/datasets/atd-unmuted',
|
|
3355
|
-
root: '9279d2e149da43a1b8e2cd7cbf77f9f0',
|
|
3356
|
-
version: 91277,
|
|
3357
|
-
leafCount: 16,
|
|
3358
|
-
name: 'atd-unmuted',
|
|
3359
|
-
idleMs : 15000,
|
|
3360
|
-
backoff : {
|
|
3361
|
-
maxMs : 500,
|
|
3362
|
-
exponent : 0.5
|
|
3363
|
-
},
|
|
3364
|
-
},
|
|
3365
|
-
],
|
|
3366
|
-
locus: {
|
|
3367
|
-
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f',
|
|
3368
|
-
htMeta: {
|
|
3369
|
-
elementId: {
|
|
3370
|
-
type: 'LOCUS',
|
|
3371
|
-
id: 0,
|
|
3372
|
-
version: 5678,
|
|
3373
|
-
},
|
|
3374
|
-
dataSetNames: ['main'],
|
|
3375
|
-
},
|
|
3376
|
-
participants: [
|
|
3377
|
-
{
|
|
3378
|
-
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/participant/11941033',
|
|
3379
|
-
person: {
|
|
3380
|
-
id: '111',
|
|
3381
|
-
name: '1st participant',
|
|
3382
|
-
},
|
|
3383
|
-
htMeta: {
|
|
3384
|
-
elementId: {
|
|
3385
|
-
type: 'PARTICIPANT',
|
|
3386
|
-
id: 2,
|
|
3387
|
-
version: 5678,
|
|
3388
|
-
},
|
|
3389
|
-
dataSetNames: ['atd-active', 'attendees', 'atd-unmuted'],
|
|
3390
|
-
},
|
|
3391
|
-
},
|
|
3392
|
-
{
|
|
3393
|
-
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/participant/11941034',
|
|
3394
|
-
person: {
|
|
3395
|
-
id: '222',
|
|
3396
|
-
name: '2nd participant',
|
|
3397
|
-
},
|
|
3398
|
-
htMeta: {
|
|
3399
|
-
elementId: {
|
|
3400
|
-
type: 'PARTICIPANT',
|
|
3401
|
-
id: 3,
|
|
3402
|
-
version: 5678,
|
|
3403
|
-
},
|
|
3404
|
-
dataSetNames: ['attendees'],
|
|
3405
|
-
},
|
|
3406
|
-
},
|
|
3407
|
-
],
|
|
3408
|
-
self: {
|
|
3409
|
-
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/participant/11941033',
|
|
3410
|
-
visibleDataSets: ['main', 'self', 'atd-unmuted'],
|
|
3411
|
-
person: {
|
|
3412
|
-
id: '333',
|
|
3413
|
-
name: 'myself',
|
|
3414
|
-
},
|
|
3415
|
-
htMeta: {
|
|
3416
|
-
elementId: {
|
|
3417
|
-
type: 'SELF',
|
|
3418
|
-
id: 4,
|
|
3419
|
-
version: 5678,
|
|
3420
|
-
},
|
|
3421
|
-
dataSetNames: ['self'],
|
|
3422
|
-
},
|
|
3423
|
-
},
|
|
3424
|
-
},
|
|
3425
|
-
};
|
|
3426
|
-
|
|
3427
|
-
clock = sinon.useFakeTimers();
|
|
3428
|
-
|
|
3429
|
-
});
|
|
3430
|
-
|
|
3431
|
-
afterEach(() => {
|
|
3432
|
-
clock.restore();
|
|
3433
|
-
});
|
|
3434
|
-
|
|
3435
|
-
const getMaxBackoffTime = (dataSets) => {
|
|
3436
|
-
return Object.values(dataSets).reduce((max, dataSet) => {
|
|
3437
|
-
const maxBackOff = dataSet.idleMs + dataSet.backoff.maxMs;
|
|
3438
|
-
return Math.max(max, maxBackOff);
|
|
3439
|
-
}, 0);
|
|
3440
|
-
}
|
|
3441
|
-
|
|
3442
|
-
const waitForMaxPossibleBackoffTime = async (dataSets) => {
|
|
3443
|
-
const maxBackoffTime = getMaxBackoffTime(dataSets);
|
|
3444
|
-
clock.tick(maxBackoffTime);
|
|
3445
|
-
await testUtils.flushPromises();
|
|
3446
|
-
}
|
|
3447
|
-
|
|
3448
|
-
it('initializes hash trees correctly from initial full locus', () => {
|
|
3449
|
-
locusInfo.initialSetup(mockFullLocus.locus, mockFullLocus.dataSets);
|
|
3450
|
-
|
|
3451
|
-
// check that the hash tree parser is initialized correctly
|
|
3452
|
-
assert.isDefined(locusInfo.hashTreeParser);
|
|
3453
|
-
assert.deepEqual(Object.keys(locusInfo.hashTreeParser.dataSets), ['main', 'self', 'atd-unmuted']);
|
|
3454
|
-
assert.deepEqual(locusInfo.hashTreeParser.dataSets['main'].hashTree.getLeafData(0), [ { type: 'LOCUS', id: 0, version: 5678 } ]);
|
|
3455
|
-
assert.deepEqual(locusInfo.hashTreeParser.dataSets['self'].hashTree.getLeafData(0), [ { type: 'SELF', id: 4, version: 5678 } ]);
|
|
3456
|
-
assert.deepEqual(locusInfo.hashTreeParser.dataSets['atd-unmuted'].hashTree.getLeafData(2), [ { type: 'PARTICIPANT', id: 2, version: 5678 } ]);
|
|
3457
|
-
|
|
3458
|
-
// participant with id=3 is not part of any of our datasets, so should be undefined
|
|
3459
|
-
assert.deepEqual(locusInfo.hashTreeParser.dataSets['atd-unmuted'].hashTree.getLeafData(3), []);
|
|
3460
|
-
});
|
|
3461
|
-
|
|
3462
|
-
it('handles hash tree messages correctly', async () => {
|
|
3463
|
-
locusInfo.initialSetup(mockFullLocus.locus, mockFullLocus.dataSets);
|
|
3464
|
-
|
|
3465
|
-
// simulate a hash tree message for a participant
|
|
3466
|
-
locusInfo.parse(mockMeeting, {
|
|
3467
|
-
"locusUrl" : "https://locus.wbx2.com/locus/api/v1/loci/dbe6eeb9-49c7-4727-bdb1-1c59fa6e56d2",
|
|
3468
|
-
"locusSessionId" : "fe3d019c-08ca-0f21-919f-f2cc646030ae",
|
|
3469
|
-
"dataSets" : [ {
|
|
3470
|
-
"url" : "https://locus.wbx2.com/locus/api/v1/loci/dbe6eeb9-49c7-4727-bdb1-1c59fa6e56d2/session/fe3d019c-08ca-0f21-919f-f2cc646030ae/datasets/attendees",
|
|
3471
|
-
"name" : "attendees",
|
|
3472
|
-
"root" : "7cfc14bdae7909e6fe7acd37bebf66db",
|
|
3473
|
-
"version" : 4739733700975,
|
|
3474
|
-
"leafCount" : 16,
|
|
3475
|
-
"idleMs" : 5000,
|
|
3476
|
-
"backoff" : {
|
|
3477
|
-
"maxMs" : 500,
|
|
3478
|
-
"exponent" : 0.5
|
|
3479
|
-
}
|
|
3480
|
-
}, {
|
|
3481
|
-
"url" : "https://locus.wbx2.com/locus/api/v1/loci/dbe6eeb9-49c7-4727-bdb1-1c59fa6e56d2/session/fe3d019c-08ca-0f21-919f-f2cc646030ae/datasets/atd-active",
|
|
3482
|
-
"name" : "atd-unmuted",
|
|
3483
|
-
"root" : "178bff6e3344f551a811712c57a9eac3",
|
|
3484
|
-
"version" : 5738696122316,
|
|
3485
|
-
"leafCount" : 4,
|
|
3486
|
-
"idleMs" : 5000,
|
|
3487
|
-
"backoff" : {
|
|
3488
|
-
"maxMs" : 500,
|
|
3489
|
-
"exponent" : 0.5
|
|
3490
|
-
}
|
|
3491
|
-
} ],
|
|
3492
|
-
"locusStateElements" : [ {
|
|
3493
|
-
"htMeta" : {
|
|
3494
|
-
"elementId" : {
|
|
3495
|
-
"type" : "PARTICIPANT",
|
|
3496
|
-
"id" : 2,
|
|
3497
|
-
"version" : 5679
|
|
3498
|
-
},
|
|
3499
|
-
"dataSetNames" : [ "attendees", "atd-unmuted" ]
|
|
3500
|
-
},
|
|
3501
|
-
"data" : "{\"isCreator\":false,\"url\":\"https://locus.wbx2.com/locus/api/v1/loci/dbe6eeb9-49c7-4727-bdb1-1c59fa6e56d2/participant/18ef210c-234a-49ac-83d6-1803bc401bc9\",\"id\":\"18ef210c-234a-49ac-83d6-1803bc401bc9\",\"guest\":false,\"resourceGuest\":false,\"moderator\":false,\"panelist\":false}"
|
|
3502
|
-
}, {
|
|
3503
|
-
"htMeta" : {
|
|
3504
|
-
"elementId" : {
|
|
3505
|
-
"type" : "PARTICIPANT",
|
|
3506
|
-
"id" : 3,
|
|
3507
|
-
"version" : 999999
|
|
3508
|
-
},
|
|
3509
|
-
"dataSetNames" : [ "attendees" ]
|
|
3510
|
-
},
|
|
3511
|
-
"data" : "{\"isCreator\":false,\"url\":\"https://locus.wbx2.com/locus/api/v1/loci/dbe6eeb9-49c7-4727-bdb1-1c59fa6e56d2/participant/29c0751a-7ada-40a1-94a4-eb5f5c80c863\",\"id\":\"29c0751a-7ada-40a1-94a4-eb5f5c80c863\",\"guest\":false,\"resourceGuest\":false,\"moderator\":false,\"panelist\":false}"
|
|
3512
|
-
} ]
|
|
3513
|
-
});
|
|
3514
|
-
|
|
3515
|
-
// main and self should be unchanged
|
|
3516
|
-
assert.deepEqual(Object.keys(locusInfo.hashTreeParser.dataSets), ['main', 'self', 'atd-unmuted']);
|
|
3517
|
-
assert.deepEqual(locusInfo.hashTreeParser.dataSets['main'].hashTree.getLeafData(0), [ { type: 'LOCUS', id: 0, version: 5678 } ]);
|
|
3518
|
-
assert.deepEqual(locusInfo.hashTreeParser.dataSets['self'].hashTree.getLeafData(0), [ { type: 'SELF', id: 4, version: 5678 } ]);
|
|
3519
|
-
|
|
3520
|
-
// participant should be updated
|
|
3521
|
-
assert.deepEqual(locusInfo.hashTreeParser.dataSets['atd-unmuted'].hashTree.getLeafData(2), [ { type: 'PARTICIPANT', id: 2, version: 5679 } ]);
|
|
3522
|
-
|
|
3523
|
-
// there should be no requests to Locus sent
|
|
3524
|
-
await waitForMaxPossibleBackoffTime(locusInfo.hashTreeParser.dataSets);
|
|
3525
|
-
assert.notCalled(webex.request);
|
|
3526
|
-
});
|
|
3527
|
-
|
|
3528
|
-
it('does a sync if hashes don\'t match after a timer fires', async () => {
|
|
3529
|
-
const atdUnmutedDataSetUrl = mockFullLocus.dataSets[2].url;
|
|
3530
|
-
|
|
3531
|
-
locusInfo.initialSetup(mockFullLocus.locus, mockFullLocus.dataSets);
|
|
3532
|
-
|
|
3533
|
-
// simulate a hash tree message for a participant
|
|
3534
|
-
locusInfo.parse(mockMeeting, {
|
|
3535
|
-
"locusUrl" : "https://locus.wbx2.com/locus/api/v1/loci/dbe6eeb9-49c7-4727-bdb1-1c59fa6e56d2",
|
|
3536
|
-
"locusSessionId" : "fe3d019c-08ca-0f21-919f-f2cc646030ae",
|
|
3537
|
-
"dataSets" : [ {
|
|
3538
|
-
"url" : atdUnmutedDataSetUrl,
|
|
3539
|
-
"name" : "atd-unmuted",
|
|
3540
|
-
"root" : "deadbeef", // wrong to trigger a sync
|
|
3541
|
-
"version" : 5738696122316,
|
|
3542
|
-
"leafCount" : 4,
|
|
3543
|
-
"idleMs" : 25000,
|
|
3544
|
-
"backoff" : {
|
|
3545
|
-
"maxMs" : 500,
|
|
3546
|
-
"exponent" : 0.5
|
|
3547
|
-
}
|
|
3548
|
-
} ],
|
|
3549
|
-
"locusStateElements" : [ {
|
|
3550
|
-
"htMeta" : {
|
|
3551
|
-
"elementId" : {
|
|
3552
|
-
"type" : "PARTICIPANT",
|
|
3553
|
-
"id" : 2,
|
|
3554
|
-
"version" : 5679
|
|
3555
|
-
},
|
|
3556
|
-
"dataSetNames" : [ "attendees", "atd-unmuted" ]
|
|
3557
|
-
},
|
|
3558
|
-
"data" : "{\"isCreator\":false,\"url\":\"https://locus.wbx2.com/locus/api/v1/loci/dbe6eeb9-49c7-4727-bdb1-1c59fa6e56d2/participant/18ef210c-234a-49ac-83d6-1803bc401bc9\",\"id\":\"18ef210c-234a-49ac-83d6-1803bc401bc9\",\"guest\":false,\"resourceGuest\":false,\"moderator\":false,\"panelist\":false}"
|
|
3559
|
-
}]
|
|
3560
|
-
});
|
|
3561
|
-
|
|
3562
|
-
// main and self should be unchanged
|
|
3563
|
-
assert.deepEqual(Object.keys(locusInfo.hashTreeParser.dataSets), ['main', 'self', 'atd-unmuted']);
|
|
3564
|
-
assert.deepEqual(locusInfo.hashTreeParser.dataSets['main'].hashTree.getLeafData(0), [ { type: 'LOCUS', id: 0, version: 5678 } ]);
|
|
3565
|
-
assert.deepEqual(locusInfo.hashTreeParser.dataSets['self'].hashTree.getLeafData(0), [ { type: 'SELF', id: 4, version: 5678 } ]);
|
|
3566
|
-
|
|
3567
|
-
// participant should be updated
|
|
3568
|
-
assert.deepEqual(locusInfo.hashTreeParser.dataSets['atd-unmuted'].hashTree.getLeafData(2), [ { type: 'PARTICIPANT', id: 2, version: 5679 } ]);
|
|
3569
|
-
|
|
3570
|
-
await testUtils.flushPromises();
|
|
3571
|
-
|
|
3572
|
-
// the root hash doesn't match, but we shouldn't send a request to Locus for hashes just yet
|
|
3573
|
-
assert.notCalled(webex.request);
|
|
3574
|
-
|
|
3575
|
-
webex.request.callsFake(async (options) => {
|
|
3576
|
-
if (options?.method === 'GET' && options?.uri?.endsWith('/hashtree')) {
|
|
3577
|
-
return {
|
|
3578
|
-
body: {
|
|
3579
|
-
hashes: [
|
|
3580
|
-
'178bff6e3344f551a811712c57a9eac3',
|
|
3581
|
-
'b113a76304e3a7121afecfe1606ee1c1',
|
|
3582
|
-
'ae70773ebb3be3653209648071b9bdad',
|
|
3583
|
-
'99aa06d3014798d86001c324468d497f',
|
|
3584
|
-
'99aa06d3014798d86001c324468d497f',
|
|
3585
|
-
'deadbeef', // wrong hash that should cause the participant with id=2 to be deemed out of sync
|
|
3586
|
-
'99aa06d3014798d86001c324468d497f'
|
|
3587
|
-
]
|
|
3588
|
-
}
|
|
3589
|
-
};
|
|
3590
|
-
} else if (options?.method === 'POST' && options?.uri?.endsWith('/sync')) {
|
|
3591
|
-
return {
|
|
3592
|
-
body: {},
|
|
3593
|
-
statusCode: 202,
|
|
3594
|
-
};
|
|
3595
|
-
}
|
|
3596
|
-
return {};
|
|
3597
|
-
});
|
|
3598
|
-
|
|
3599
|
-
// only after timeout there should be requests to get hashes and sync sent to Locus
|
|
3600
|
-
await waitForMaxPossibleBackoffTime(locusInfo.hashTreeParser.dataSets);
|
|
3601
|
-
assert.calledTwice(webex.request);
|
|
3602
|
-
|
|
3603
|
-
const firstCallArgs = webex.request.getCall(0).args[0];
|
|
3604
|
-
const secondCallArgs = webex.request.getCall(1).args[0];
|
|
3605
|
-
|
|
3606
|
-
assert.deepEqual(firstCallArgs, {method: 'GET', uri: `${atdUnmutedDataSetUrl}/hashtree`});
|
|
3607
|
-
|
|
3608
|
-
assert.deepEqual(secondCallArgs, {method: 'POST', uri: `${atdUnmutedDataSetUrl}/sync`,
|
|
3609
|
-
body: {
|
|
3610
|
-
dataSet: {
|
|
3611
|
-
name: 'atd-unmuted',
|
|
3612
|
-
leafCount: 16,
|
|
3613
|
-
root: '178bff6e3344f551a811712c57a9eac3',
|
|
3614
|
-
},
|
|
3615
|
-
leafDataEntries: [{
|
|
3616
|
-
leafIndex: 2,
|
|
3617
|
-
elementIds: [
|
|
3618
|
-
{
|
|
3619
|
-
id: 2,
|
|
3620
|
-
type: "PARTICIPANT",
|
|
3621
|
-
version: 5679
|
|
3622
|
-
}
|
|
3623
|
-
],
|
|
3624
|
-
}]
|
|
3625
|
-
}
|
|
3626
|
-
});
|
|
3627
|
-
});
|
|
3628
|
-
|
|
3629
|
-
it('handles end meeting message correctly', async () => {
|
|
3630
|
-
});
|
|
3631
|
-
it('handles heartbeat messages correctly', async () => {
|
|
3632
|
-
});
|
|
3633
|
-
|
|
3634
|
-
});
|
|
3635
3354
|
});
|
|
3636
3355
|
});
|
|
@@ -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
|
});
|