@webex/plugin-meetings 3.12.0-next.9 → 3.12.0-next.91
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/AGENTS.md +9 -0
- package/dist/aiEnableRequest/index.js +15 -2
- package/dist/aiEnableRequest/index.js.map +1 -1
- package/dist/breakouts/breakout.js +8 -3
- package/dist/breakouts/breakout.js.map +1 -1
- package/dist/breakouts/index.js +26 -2
- package/dist/breakouts/index.js.map +1 -1
- package/dist/config.js +2 -2
- package/dist/config.js.map +1 -1
- package/dist/constants.js +35 -9
- package/dist/constants.js.map +1 -1
- package/dist/controls-options-manager/constants.js +11 -1
- package/dist/controls-options-manager/constants.js.map +1 -1
- package/dist/controls-options-manager/index.js +67 -29
- package/dist/controls-options-manager/index.js.map +1 -1
- package/dist/controls-options-manager/util.js +91 -0
- package/dist/controls-options-manager/util.js.map +1 -1
- package/dist/hashTree/constants.js +13 -1
- package/dist/hashTree/constants.js.map +1 -1
- package/dist/hashTree/hashTreeParser.js +880 -382
- package/dist/hashTree/hashTreeParser.js.map +1 -1
- package/dist/hashTree/utils.js +42 -0
- package/dist/hashTree/utils.js.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/interceptors/dataChannelAuthToken.js +75 -15
- package/dist/interceptors/dataChannelAuthToken.js.map +1 -1
- package/dist/interceptors/locusRetry.js +23 -8
- package/dist/interceptors/locusRetry.js.map +1 -1
- package/dist/interpretation/index.js +10 -1
- package/dist/interpretation/index.js.map +1 -1
- package/dist/interpretation/interpretation.types.js +7 -0
- package/dist/interpretation/interpretation.types.js.map +1 -0
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/controlsUtils.js +4 -1
- package/dist/locus-info/controlsUtils.js.map +1 -1
- package/dist/locus-info/index.js +303 -88
- package/dist/locus-info/index.js.map +1 -1
- package/dist/locus-info/infoUtils.js +36 -5
- package/dist/locus-info/infoUtils.js.map +1 -1
- package/dist/locus-info/types.js +19 -0
- package/dist/locus-info/types.js.map +1 -1
- package/dist/media/index.js +3 -1
- package/dist/media/index.js.map +1 -1
- package/dist/media/properties.js +1 -0
- package/dist/media/properties.js.map +1 -1
- package/dist/meeting/in-meeting-actions.js +5 -1
- package/dist/meeting/in-meeting-actions.js.map +1 -1
- package/dist/meeting/index.js +1233 -793
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/locusMediaRequest.js +229 -82
- package/dist/meeting/locusMediaRequest.js.map +1 -1
- package/dist/meeting/muteState.js +10 -1
- package/dist/meeting/muteState.js.map +1 -1
- package/dist/meeting/request.js +5 -2
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/util.js +35 -2
- package/dist/meeting/util.js.map +1 -1
- package/dist/meeting-info/meeting-info-v2.js +2 -2
- package/dist/meeting-info/meeting-info-v2.js.map +1 -1
- package/dist/meetings/index.js +231 -118
- package/dist/meetings/index.js.map +1 -1
- package/dist/meetings/meetings.types.js +6 -1
- package/dist/meetings/meetings.types.js.map +1 -1
- package/dist/meetings/request.js +39 -0
- package/dist/meetings/request.js.map +1 -1
- package/dist/meetings/util.js +79 -5
- 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/types.js.map +1 -1
- package/dist/member/util.js +30 -0
- package/dist/member/util.js.map +1 -1
- package/dist/members/index.js +101 -43
- package/dist/members/index.js.map +1 -1
- package/dist/members/util.js +8 -1
- package/dist/members/util.js.map +1 -1
- package/dist/metrics/constants.js +5 -1
- package/dist/metrics/constants.js.map +1 -1
- package/dist/multistream/codec/constants.js +63 -0
- package/dist/multistream/codec/constants.js.map +1 -0
- package/dist/multistream/mediaRequestManager.js +62 -15
- package/dist/multistream/mediaRequestManager.js.map +1 -1
- package/dist/multistream/receiveSlot.js +9 -0
- package/dist/multistream/receiveSlot.js.map +1 -1
- package/dist/reachability/index.js +10 -13
- package/dist/reachability/index.js.map +1 -1
- package/dist/reachability/reachability.types.js +2 -0
- package/dist/reachability/reachability.types.js.map +1 -1
- package/dist/reachability/util.js +45 -0
- package/dist/reachability/util.js.map +1 -1
- package/dist/reactions/reactions.type.js.map +1 -1
- package/dist/recording-controller/index.js +1 -3
- package/dist/recording-controller/index.js.map +1 -1
- package/dist/types/config.d.ts +2 -2
- package/dist/types/constants.d.ts +12 -1
- package/dist/types/controls-options-manager/constants.d.ts +6 -1
- package/dist/types/controls-options-manager/index.d.ts +16 -0
- package/dist/types/hashTree/constants.d.ts +2 -0
- package/dist/types/hashTree/hashTreeParser.d.ts +146 -17
- package/dist/types/hashTree/utils.d.ts +18 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/interceptors/locusRetry.d.ts +4 -4
- package/dist/types/interpretation/interpretation.types.d.ts +10 -0
- package/dist/types/locus-info/index.d.ts +50 -6
- package/dist/types/locus-info/infoUtils.d.ts +8 -0
- package/dist/types/locus-info/types.d.ts +21 -1
- package/dist/types/media/properties.d.ts +1 -0
- package/dist/types/meeting/in-meeting-actions.d.ts +4 -0
- package/dist/types/meeting/index.d.ts +94 -7
- package/dist/types/meeting/locusMediaRequest.d.ts +12 -0
- package/dist/types/meeting/request.d.ts +1 -0
- package/dist/types/meeting/util.d.ts +9 -0
- package/dist/types/meetings/index.d.ts +30 -18
- package/dist/types/meetings/meetings.types.d.ts +15 -0
- package/dist/types/meetings/request.d.ts +14 -0
- package/dist/types/member/index.d.ts +1 -0
- package/dist/types/member/types.d.ts +1 -0
- package/dist/types/member/util.d.ts +9 -0
- package/dist/types/members/index.d.ts +15 -2
- package/dist/types/members/util.d.ts +1 -5
- package/dist/types/metrics/constants.d.ts +4 -0
- package/dist/types/multistream/codec/constants.d.ts +7 -0
- package/dist/types/multistream/mediaRequestManager.d.ts +22 -5
- package/dist/types/reachability/index.d.ts +1 -1
- package/dist/types/reachability/reachability.types.d.ts +7 -0
- package/dist/types/reachability/util.d.ts +17 -0
- package/dist/types/reactions/reactions.type.d.ts +3 -0
- package/dist/webinar/index.js +305 -159
- package/dist/webinar/index.js.map +1 -1
- package/package.json +22 -22
- package/src/aiEnableRequest/index.ts +16 -0
- package/src/breakouts/breakout.ts +3 -1
- package/src/breakouts/index.ts +31 -0
- package/src/config.ts +2 -2
- package/src/constants.ts +20 -3
- package/src/controls-options-manager/constants.ts +14 -1
- package/src/controls-options-manager/index.ts +87 -28
- package/src/controls-options-manager/util.ts +81 -1
- package/src/hashTree/constants.ts +16 -0
- package/src/hashTree/hashTreeParser.ts +580 -196
- package/src/hashTree/utils.ts +36 -0
- package/src/index.ts +6 -0
- package/src/interceptors/dataChannelAuthToken.ts +88 -12
- package/src/interceptors/locusRetry.ts +25 -4
- package/src/interpretation/index.ts +27 -9
- package/src/interpretation/interpretation.types.ts +11 -0
- package/src/locus-info/controlsUtils.ts +3 -1
- package/src/locus-info/index.ts +304 -100
- package/src/locus-info/infoUtils.ts +41 -6
- package/src/locus-info/types.ts +25 -1
- package/src/media/index.ts +3 -0
- package/src/media/properties.ts +1 -0
- package/src/meeting/in-meeting-actions.ts +8 -0
- package/src/meeting/index.ts +494 -67
- package/src/meeting/locusMediaRequest.ts +106 -6
- package/src/meeting/muteState.ts +10 -1
- package/src/meeting/request.ts +11 -0
- package/src/meeting/util.ts +47 -5
- package/src/meeting-info/meeting-info-v2.ts +4 -2
- package/src/meetings/index.ts +133 -78
- package/src/meetings/meetings.types.ts +19 -0
- package/src/meetings/request.ts +43 -0
- package/src/meetings/util.ts +97 -1
- package/src/member/index.ts +10 -0
- package/src/member/types.ts +1 -0
- package/src/member/util.ts +29 -0
- package/src/members/index.ts +70 -7
- package/src/members/util.ts +12 -4
- package/src/metrics/constants.ts +4 -0
- package/src/multistream/codec/constants.ts +58 -0
- package/src/multistream/mediaRequestManager.ts +119 -28
- package/src/multistream/receiveSlot.ts +18 -0
- package/src/reachability/index.ts +19 -15
- package/src/reachability/reachability.types.ts +11 -0
- package/src/reachability/util.ts +36 -0
- package/src/reactions/reactions.type.ts +3 -0
- package/src/recording-controller/index.ts +1 -2
- package/src/webinar/index.ts +214 -36
- package/test/unit/spec/aiEnableRequest/index.ts +86 -0
- package/test/unit/spec/breakouts/breakout.ts +9 -3
- package/test/unit/spec/breakouts/index.ts +49 -0
- package/test/unit/spec/controls-options-manager/index.js +244 -29
- package/test/unit/spec/controls-options-manager/util.js +165 -0
- package/test/unit/spec/hashTree/hashTreeParser.ts +1838 -180
- package/test/unit/spec/hashTree/utils.ts +125 -1
- package/test/unit/spec/interceptors/dataChannelAuthToken.ts +196 -0
- package/test/unit/spec/interceptors/locusRetry.ts +205 -4
- package/test/unit/spec/interpretation/index.ts +26 -4
- package/test/unit/spec/locus-info/controlsUtils.js +172 -57
- package/test/unit/spec/locus-info/index.js +499 -81
- package/test/unit/spec/locus-info/infoUtils.js +184 -1
- package/test/unit/spec/media/index.ts +31 -0
- package/test/unit/spec/meeting/in-meeting-actions.ts +4 -0
- package/test/unit/spec/meeting/index.js +1615 -42
- package/test/unit/spec/meeting/locusMediaRequest.ts +302 -4
- package/test/unit/spec/meeting/muteState.js +81 -0
- package/test/unit/spec/meeting/request.js +12 -0
- package/test/unit/spec/meeting/utils.js +157 -1
- package/test/unit/spec/meeting-info/meetinginfov2.js +19 -10
- package/test/unit/spec/meetings/index.js +363 -36
- package/test/unit/spec/meetings/request.js +141 -0
- package/test/unit/spec/meetings/utils.js +189 -0
- package/test/unit/spec/member/index.js +7 -0
- package/test/unit/spec/member/util.js +89 -0
- package/test/unit/spec/members/index.js +216 -0
- package/test/unit/spec/members/request.js +37 -1
- package/test/unit/spec/members/utils.js +63 -0
- package/test/unit/spec/multistream/mediaRequestManager.ts +501 -37
- package/test/unit/spec/reachability/index.ts +59 -46
- package/test/unit/spec/reachability/util.ts +31 -0
- package/test/unit/spec/recording-controller/index.js +9 -8
- package/test/unit/spec/webinar/index.ts +329 -28
|
@@ -13,6 +13,7 @@ import MediaSharesUtils from '@webex/plugin-meetings/src/locus-info//mediaShares
|
|
|
13
13
|
import LocusDeltaParser from '@webex/plugin-meetings/src/locus-info/parser';
|
|
14
14
|
import Metrics from '@webex/plugin-meetings/src/metrics';
|
|
15
15
|
import * as HashTreeParserModule from '@webex/plugin-meetings/src/hashTree/hashTreeParser';
|
|
16
|
+
import MeetingsUtil from '@webex/plugin-meetings/src/meetings/util';
|
|
16
17
|
|
|
17
18
|
import {
|
|
18
19
|
LOCUSINFO,
|
|
@@ -220,6 +221,47 @@ describe('plugin-meetings', () => {
|
|
|
220
221
|
assert.isTrue(locusInfo.emitChange);
|
|
221
222
|
});
|
|
222
223
|
|
|
224
|
+
it('calls onLocusSynced callback passed as second argument with full locus from join response', async () => {
|
|
225
|
+
const syncedLocus = {url: 'http://locus-url.com', participants: []};
|
|
226
|
+
const onLocusSynced = sinon.stub();
|
|
227
|
+
|
|
228
|
+
await locusInfo.initialSetup(
|
|
229
|
+
{
|
|
230
|
+
trigger: 'join-response',
|
|
231
|
+
locus: syncedLocus,
|
|
232
|
+
},
|
|
233
|
+
onLocusSynced
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
assert.calledOnceWithExactly(onLocusSynced, syncedLocus);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it('swallows onLocusSynced callback errors and logs warn', async () => {
|
|
240
|
+
const syncedLocus = {url: 'http://locus-url.com', participants: []};
|
|
241
|
+
const callbackError = new Error('onLocusSynced failed');
|
|
242
|
+
const onLocusSynced = sinon.stub().throws(callbackError);
|
|
243
|
+
const loggerWarnStub = LoggerProxy.logger.warn?.isSinonProxy
|
|
244
|
+
? LoggerProxy.logger.warn
|
|
245
|
+
: sinon.stub(LoggerProxy.logger, 'warn');
|
|
246
|
+
|
|
247
|
+
loggerWarnStub.resetHistory();
|
|
248
|
+
|
|
249
|
+
await locusInfo.initialSetup(
|
|
250
|
+
{
|
|
251
|
+
trigger: 'join-response',
|
|
252
|
+
locus: syncedLocus,
|
|
253
|
+
},
|
|
254
|
+
onLocusSynced
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
assert.calledOnceWithExactly(onLocusSynced, syncedLocus);
|
|
258
|
+
assert.calledOnce(loggerWarnStub);
|
|
259
|
+
assert.match(
|
|
260
|
+
loggerWarnStub.firstCall.args[0],
|
|
261
|
+
/Locus-info:index#initialSetup --> onLocusSynced callback failed/
|
|
262
|
+
);
|
|
263
|
+
});
|
|
264
|
+
|
|
223
265
|
it('should initialize the hash tree parser correctly when triggered from a get loci response containing visible datasets', async () => {
|
|
224
266
|
const visibleDataSets = ['dataset1', 'dataset2'];
|
|
225
267
|
const locus = createLocusWithVisibleDataSets(visibleDataSets);
|
|
@@ -290,6 +332,7 @@ describe('plugin-meetings', () => {
|
|
|
290
332
|
describe('should setup correct locusInfoUpdateCallback when creating HashTreeParser', () => {
|
|
291
333
|
const OBJECTS_UPDATED = HashTreeParserModule.LocusInfoUpdateType.OBJECTS_UPDATED;
|
|
292
334
|
const MEETING_ENDED = HashTreeParserModule.LocusInfoUpdateType.MEETING_ENDED;
|
|
335
|
+
const LOCUS_NOT_FOUND = HashTreeParserModule.LocusInfoUpdateType.LOCUS_NOT_FOUND;
|
|
293
336
|
|
|
294
337
|
let locusInfoUpdateCallback;
|
|
295
338
|
let onDeltaLocusStub;
|
|
@@ -413,7 +456,7 @@ describe('plugin-meetings', () => {
|
|
|
413
456
|
};
|
|
414
457
|
|
|
415
458
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
416
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
459
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
417
460
|
updatedObjects: [{htMeta: {elementId: {type: 'self'}}, data: newSelf}],
|
|
418
461
|
});
|
|
419
462
|
|
|
@@ -440,7 +483,7 @@ describe('plugin-meetings', () => {
|
|
|
440
483
|
locusInfo.info.isWebinar = true;
|
|
441
484
|
|
|
442
485
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
443
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
486
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
444
487
|
updatedObjects: [{htMeta: {elementId: {type: 'self'}}, data: newSelf}],
|
|
445
488
|
});
|
|
446
489
|
|
|
@@ -473,7 +516,7 @@ describe('plugin-meetings', () => {
|
|
|
473
516
|
locusInfo.info.isWebinar = true;
|
|
474
517
|
|
|
475
518
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
476
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
519
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
477
520
|
updatedObjects: [{htMeta: {elementId: {type: 'self'}}, data: newSelf}],
|
|
478
521
|
});
|
|
479
522
|
|
|
@@ -501,7 +544,7 @@ describe('plugin-meetings', () => {
|
|
|
501
544
|
};
|
|
502
545
|
|
|
503
546
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
504
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
547
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
505
548
|
updatedObjects: [{htMeta: {elementId: {type: 'fullState'}}, data: newFullState}],
|
|
506
549
|
});
|
|
507
550
|
|
|
@@ -519,7 +562,7 @@ describe('plugin-meetings', () => {
|
|
|
519
562
|
};
|
|
520
563
|
|
|
521
564
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
522
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
565
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
523
566
|
updatedObjects: [{htMeta: {elementId: {type: 'info'}}, data: newInfo}],
|
|
524
567
|
});
|
|
525
568
|
|
|
@@ -537,7 +580,7 @@ describe('plugin-meetings', () => {
|
|
|
537
580
|
};
|
|
538
581
|
|
|
539
582
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
540
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
583
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
541
584
|
updatedObjects: [{htMeta: {elementId: {type: 'links'}}, data: newLinks}],
|
|
542
585
|
});
|
|
543
586
|
|
|
@@ -557,7 +600,7 @@ describe('plugin-meetings', () => {
|
|
|
557
600
|
};
|
|
558
601
|
|
|
559
602
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
560
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
603
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
561
604
|
updatedObjects: [{htMeta: newLocusHtMeta, data: newLocus}],
|
|
562
605
|
});
|
|
563
606
|
|
|
@@ -590,7 +633,7 @@ describe('plugin-meetings', () => {
|
|
|
590
633
|
};
|
|
591
634
|
|
|
592
635
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
593
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
636
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
594
637
|
updatedObjects: [
|
|
595
638
|
{
|
|
596
639
|
htMeta: newLocusHtMeta,
|
|
@@ -637,7 +680,7 @@ describe('plugin-meetings', () => {
|
|
|
637
680
|
};
|
|
638
681
|
|
|
639
682
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
640
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
683
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
641
684
|
updatedObjects: [
|
|
642
685
|
// first, a removal of LOCUS object
|
|
643
686
|
{htMeta: {elementId: {type: 'locus'}}, data: null},
|
|
@@ -671,7 +714,7 @@ describe('plugin-meetings', () => {
|
|
|
671
714
|
const newLocusHtMeta = {elementId: {type: 'locus', version: 99}};
|
|
672
715
|
|
|
673
716
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
674
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
717
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
675
718
|
updatedObjects: [
|
|
676
719
|
// first, an update
|
|
677
720
|
{htMeta: newLocusHtMeta, data: newLocus},
|
|
@@ -700,7 +743,7 @@ describe('plugin-meetings', () => {
|
|
|
700
743
|
};
|
|
701
744
|
|
|
702
745
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
703
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
746
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
704
747
|
updatedObjects: [
|
|
705
748
|
// first, an update
|
|
706
749
|
{htMeta: {elementId: {type: 'locus'}}, data: newLocus1},
|
|
@@ -730,7 +773,7 @@ describe('plugin-meetings', () => {
|
|
|
730
773
|
};
|
|
731
774
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
732
775
|
// with 1 participant added, 1 updated, and 1 removed
|
|
733
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
776
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
734
777
|
updatedObjects: [
|
|
735
778
|
{htMeta: {elementId: {type: 'participant', id: 'fake-ht-participant-1'}}, data: null},
|
|
736
779
|
{
|
|
@@ -774,7 +817,7 @@ describe('plugin-meetings', () => {
|
|
|
774
817
|
};
|
|
775
818
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
776
819
|
// with 1 participant added, 1 updated, and 1 removed
|
|
777
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
820
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
778
821
|
updatedObjects: [
|
|
779
822
|
{htMeta: {elementId: {type: 'mediashare', id: 'fake-ht-mediaShare-1'}}, data: null},
|
|
780
823
|
{
|
|
@@ -807,7 +850,7 @@ describe('plugin-meetings', () => {
|
|
|
807
850
|
};
|
|
808
851
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
809
852
|
// with 1 embedded app added, 1 updated, and 1 removed
|
|
810
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
853
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
811
854
|
updatedObjects: [
|
|
812
855
|
{htMeta: {elementId: {type: 'embeddedapp', id: 'fake-ht-embeddedApp-1'}}, data: null},
|
|
813
856
|
{
|
|
@@ -844,7 +887,7 @@ describe('plugin-meetings', () => {
|
|
|
844
887
|
};
|
|
845
888
|
|
|
846
889
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
847
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
890
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
848
891
|
updatedObjects: [
|
|
849
892
|
{
|
|
850
893
|
htMeta: {elementId: {type: 'mediashare', id: 'fake-ht-mediaShare-2'}},
|
|
@@ -883,7 +926,7 @@ describe('plugin-meetings', () => {
|
|
|
883
926
|
};
|
|
884
927
|
|
|
885
928
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
886
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
929
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
887
930
|
updatedObjects: [
|
|
888
931
|
{
|
|
889
932
|
htMeta: {elementId: {type: 'controlentry', id: 'control-1'}},
|
|
@@ -911,7 +954,7 @@ describe('plugin-meetings', () => {
|
|
|
911
954
|
|
|
912
955
|
it('should process locus update correctly when CONTROL object is received with no data', () => {
|
|
913
956
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
914
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
957
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
915
958
|
updatedObjects: [
|
|
916
959
|
{
|
|
917
960
|
htMeta: {elementId: {type: 'controlentry', id: 'some-control-id'}},
|
|
@@ -935,7 +978,7 @@ describe('plugin-meetings', () => {
|
|
|
935
978
|
const destroyStub = sinon.stub(locusInfo.webex.meetings, 'destroy');
|
|
936
979
|
|
|
937
980
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
938
|
-
locusInfoUpdateCallback(MEETING_ENDED);
|
|
981
|
+
locusInfoUpdateCallback({updateType: MEETING_ENDED});
|
|
939
982
|
|
|
940
983
|
assert.calledOnceWithExactly(collectionGetStub, locusInfo.meetingId);
|
|
941
984
|
assert.calledOnceWithExactly(
|
|
@@ -953,17 +996,48 @@ describe('plugin-meetings', () => {
|
|
|
953
996
|
const destroyStub = sinon.stub(locusInfo.webex.meetings, 'destroy');
|
|
954
997
|
|
|
955
998
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
956
|
-
locusInfoUpdateCallback(MEETING_ENDED);
|
|
999
|
+
locusInfoUpdateCallback({updateType: MEETING_ENDED});
|
|
957
1000
|
|
|
958
1001
|
assert.calledOnceWithExactly(collectionGetStub, locusInfo.meetingId);
|
|
959
1002
|
assert.notCalled(destroyStub);
|
|
960
1003
|
});
|
|
961
1004
|
|
|
1005
|
+
it('should handle LOCUS_NOT_FOUND by calling syncMeetings with skipHashTreeSync', () => {
|
|
1006
|
+
const syncMeetingsStub = sinon.stub(locusInfo.webex.meetings, 'syncMeetings').resolves();
|
|
1007
|
+
|
|
1008
|
+
locusInfoUpdateCallback({updateType: LOCUS_NOT_FOUND});
|
|
1009
|
+
|
|
1010
|
+
assert.calledOnceWithExactly(syncMeetingsStub, {keepOnlyLocusMeetings: false, skipHashTreeSync: true});
|
|
1011
|
+
});
|
|
1012
|
+
|
|
1013
|
+
it('should handle LOCUS_NOT_FOUND and log error if syncMeetings fails', async () => {
|
|
1014
|
+
const syncError = new Error('sync failed');
|
|
1015
|
+
const syncMeetingsStub = sinon.stub(locusInfo.webex.meetings, 'syncMeetings').rejects(syncError);
|
|
1016
|
+
const logErrorStub = LoggerProxy.logger.error?.isSinonProxy
|
|
1017
|
+
? LoggerProxy.logger.error
|
|
1018
|
+
: sinon.stub(LoggerProxy.logger, 'error');
|
|
1019
|
+
|
|
1020
|
+
logErrorStub.resetHistory();
|
|
1021
|
+
|
|
1022
|
+
locusInfoUpdateCallback({updateType: LOCUS_NOT_FOUND});
|
|
1023
|
+
|
|
1024
|
+
assert.calledOnceWithExactly(syncMeetingsStub, {keepOnlyLocusMeetings: false, skipHashTreeSync: true});
|
|
1025
|
+
|
|
1026
|
+
// wait for the promise rejection to be handled
|
|
1027
|
+
await testUtils.flushPromises();
|
|
1028
|
+
|
|
1029
|
+
assert.calledOnce(logErrorStub);
|
|
1030
|
+
assert.match(
|
|
1031
|
+
logErrorStub.firstCall.args[0],
|
|
1032
|
+
/syncMeetings failed after LOCUS_NOT_FOUND/
|
|
1033
|
+
);
|
|
1034
|
+
});
|
|
1035
|
+
|
|
962
1036
|
it('should set forceReplaceMembers to true on the first update for a locusUrl (initializedFromHashTree is false)', () => {
|
|
963
1037
|
const createdHashTreeParser = locusInfo.hashTreeParsers.get('fake-locus-url');
|
|
964
1038
|
createdHashTreeParser.initializedFromHashTree = false;
|
|
965
1039
|
|
|
966
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
1040
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
967
1041
|
updatedObjects: [
|
|
968
1042
|
{
|
|
969
1043
|
htMeta: {elementId: {type: 'self'}},
|
|
@@ -978,7 +1052,7 @@ describe('plugin-meetings', () => {
|
|
|
978
1052
|
});
|
|
979
1053
|
|
|
980
1054
|
it('should set forceReplaceMembers to false on subsequent updates (initializedFromHashTree is true)', () => {
|
|
981
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
1055
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
982
1056
|
updatedObjects: [
|
|
983
1057
|
{
|
|
984
1058
|
htMeta: {elementId: {type: 'self'}},
|
|
@@ -994,7 +1068,7 @@ describe('plugin-meetings', () => {
|
|
|
994
1068
|
it('should copy participant data to self when participant matches self identity and state is LEFT with reason MOVED', () => {
|
|
995
1069
|
locusInfo.self = {id: 'fake-self', identity: 'user-123'};
|
|
996
1070
|
|
|
997
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
1071
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
998
1072
|
updatedObjects: [
|
|
999
1073
|
{
|
|
1000
1074
|
htMeta: {elementId: {type: 'participant', id: 99}},
|
|
@@ -1289,6 +1363,8 @@ describe('plugin-meetings', () => {
|
|
|
1289
1363
|
state: RECORDING_STATE.IDLE,
|
|
1290
1364
|
modifiedBy: 'George Kittle',
|
|
1291
1365
|
lastModified: 'TODAY',
|
|
1366
|
+
modifiedByServiceAppName: undefined,
|
|
1367
|
+
modifiedByServiceAppId: undefined,
|
|
1292
1368
|
}
|
|
1293
1369
|
);
|
|
1294
1370
|
});
|
|
@@ -1323,6 +1399,8 @@ describe('plugin-meetings', () => {
|
|
|
1323
1399
|
state: RECORDING_STATE.RECORDING,
|
|
1324
1400
|
modifiedBy: 'George Kittle',
|
|
1325
1401
|
lastModified: 'TODAY',
|
|
1402
|
+
modifiedByServiceAppName: undefined,
|
|
1403
|
+
modifiedByServiceAppId: undefined,
|
|
1326
1404
|
}
|
|
1327
1405
|
);
|
|
1328
1406
|
});
|
|
@@ -1358,6 +1436,8 @@ describe('plugin-meetings', () => {
|
|
|
1358
1436
|
state: RECORDING_STATE.PAUSED,
|
|
1359
1437
|
modifiedBy: 'George Kittle',
|
|
1360
1438
|
lastModified: 'TODAY',
|
|
1439
|
+
modifiedByServiceAppName: undefined,
|
|
1440
|
+
modifiedByServiceAppId: undefined,
|
|
1361
1441
|
}
|
|
1362
1442
|
);
|
|
1363
1443
|
});
|
|
@@ -1394,6 +1474,8 @@ describe('plugin-meetings', () => {
|
|
|
1394
1474
|
state: RECORDING_STATE.RESUMED,
|
|
1395
1475
|
modifiedBy: 'George Kittle',
|
|
1396
1476
|
lastModified: 'TODAY',
|
|
1477
|
+
modifiedByServiceAppName: undefined,
|
|
1478
|
+
modifiedByServiceAppId: undefined,
|
|
1397
1479
|
}
|
|
1398
1480
|
);
|
|
1399
1481
|
});
|
|
@@ -1429,6 +1511,44 @@ describe('plugin-meetings', () => {
|
|
|
1429
1511
|
state: RECORDING_STATE.IDLE,
|
|
1430
1512
|
modifiedBy: 'George Kittle',
|
|
1431
1513
|
lastModified: 'TODAY',
|
|
1514
|
+
modifiedByServiceAppName: undefined,
|
|
1515
|
+
modifiedByServiceAppId: undefined,
|
|
1516
|
+
}
|
|
1517
|
+
);
|
|
1518
|
+
});
|
|
1519
|
+
|
|
1520
|
+
it('should include service app fields in the recording event when present', () => {
|
|
1521
|
+
locusInfo.controls = {
|
|
1522
|
+
record: {
|
|
1523
|
+
recording: false,
|
|
1524
|
+
paused: false,
|
|
1525
|
+
meta: {
|
|
1526
|
+
lastModified: 'TODAY',
|
|
1527
|
+
modifiedBy: 'George Kittle',
|
|
1528
|
+
},
|
|
1529
|
+
},
|
|
1530
|
+
shareControl: {},
|
|
1531
|
+
transcribe: {},
|
|
1532
|
+
};
|
|
1533
|
+
newControls.record.recording = true;
|
|
1534
|
+
newControls.record.meta.modifiedByServiceAppName = 'My Bot';
|
|
1535
|
+
newControls.record.meta.modifiedByServiceAppId = 'app-id-123';
|
|
1536
|
+
locusInfo.emitScoped = sinon.stub();
|
|
1537
|
+
locusInfo.updateControls(newControls);
|
|
1538
|
+
|
|
1539
|
+
assert.calledWith(
|
|
1540
|
+
locusInfo.emitScoped,
|
|
1541
|
+
{
|
|
1542
|
+
file: 'locus-info',
|
|
1543
|
+
function: 'updateControls',
|
|
1544
|
+
},
|
|
1545
|
+
LOCUSINFO.EVENTS.CONTROLS_RECORDING_UPDATED,
|
|
1546
|
+
{
|
|
1547
|
+
state: RECORDING_STATE.RECORDING,
|
|
1548
|
+
modifiedBy: 'George Kittle',
|
|
1549
|
+
lastModified: 'TODAY',
|
|
1550
|
+
modifiedByServiceAppName: 'My Bot',
|
|
1551
|
+
modifiedByServiceAppId: 'app-id-123',
|
|
1432
1552
|
}
|
|
1433
1553
|
);
|
|
1434
1554
|
});
|
|
@@ -2024,7 +2144,7 @@ describe('plugin-meetings', () => {
|
|
|
2024
2144
|
function: 'updateSelf',
|
|
2025
2145
|
},
|
|
2026
2146
|
LOCUSINFO.EVENTS.SELF_REMOTE_MUTE_STATUS_UPDATED,
|
|
2027
|
-
{muted: true, unmuteAllowed: true}
|
|
2147
|
+
{muted: true, unmuteAllowed: true, modifiedBy: null}
|
|
2028
2148
|
);
|
|
2029
2149
|
|
|
2030
2150
|
// but sometimes "previous self" is defined, but without controls.audio.muted, so we test this here:
|
|
@@ -2039,7 +2159,7 @@ describe('plugin-meetings', () => {
|
|
|
2039
2159
|
function: 'updateSelf',
|
|
2040
2160
|
},
|
|
2041
2161
|
LOCUSINFO.EVENTS.SELF_REMOTE_MUTE_STATUS_UPDATED,
|
|
2042
|
-
{muted: true, unmuteAllowed: true}
|
|
2162
|
+
{muted: true, unmuteAllowed: true, modifiedBy: null}
|
|
2043
2163
|
);
|
|
2044
2164
|
});
|
|
2045
2165
|
|
|
@@ -2098,7 +2218,7 @@ describe('plugin-meetings', () => {
|
|
|
2098
2218
|
function: 'updateSelf',
|
|
2099
2219
|
},
|
|
2100
2220
|
LOCUSINFO.EVENTS.SELF_REMOTE_MUTE_STATUS_UPDATED,
|
|
2101
|
-
{muted: true, unmuteAllowed: true}
|
|
2221
|
+
{muted: true, unmuteAllowed: true, modifiedBy: null}
|
|
2102
2222
|
);
|
|
2103
2223
|
});
|
|
2104
2224
|
|
|
@@ -2237,7 +2357,7 @@ describe('plugin-meetings', () => {
|
|
|
2237
2357
|
function: 'updateSelf',
|
|
2238
2358
|
},
|
|
2239
2359
|
LOCUSINFO.EVENTS.SELF_REMOTE_MUTE_STATUS_UPDATED,
|
|
2240
|
-
{muted: true, unmuteAllowed: false}
|
|
2360
|
+
{muted: true, unmuteAllowed: false, modifiedBy: null}
|
|
2241
2361
|
);
|
|
2242
2362
|
|
|
2243
2363
|
// now change only disallowUnmute
|
|
@@ -2255,7 +2375,28 @@ describe('plugin-meetings', () => {
|
|
|
2255
2375
|
function: 'updateSelf',
|
|
2256
2376
|
},
|
|
2257
2377
|
LOCUSINFO.EVENTS.SELF_REMOTE_MUTE_STATUS_UPDATED,
|
|
2258
|
-
{muted: true, unmuteAllowed: true}
|
|
2378
|
+
{muted: true, unmuteAllowed: true, modifiedBy: null}
|
|
2379
|
+
);
|
|
2380
|
+
});
|
|
2381
|
+
|
|
2382
|
+
it('should include modifiedBy in payload when muted by host', () => {
|
|
2383
|
+
locusInfo.webex.internal.device.url = self.deviceUrl;
|
|
2384
|
+
locusInfo.updateSelf(self);
|
|
2385
|
+
const newSelf = cloneDeep(self);
|
|
2386
|
+
newSelf.controls.audio.muted = true;
|
|
2387
|
+
newSelf.controls.audio.meta = {modifiedBy: 'host-uuid-123'};
|
|
2388
|
+
|
|
2389
|
+
locusInfo.emitScoped = sinon.stub();
|
|
2390
|
+
locusInfo.updateSelf(newSelf);
|
|
2391
|
+
|
|
2392
|
+
assert.calledWith(
|
|
2393
|
+
locusInfo.emitScoped,
|
|
2394
|
+
{
|
|
2395
|
+
file: 'locus-info',
|
|
2396
|
+
function: 'updateSelf',
|
|
2397
|
+
},
|
|
2398
|
+
LOCUSINFO.EVENTS.SELF_REMOTE_MUTE_STATUS_UPDATED,
|
|
2399
|
+
{muted: true, unmuteAllowed: true, modifiedBy: 'host-uuid-123'}
|
|
2259
2400
|
);
|
|
2260
2401
|
});
|
|
2261
2402
|
|
|
@@ -2715,6 +2856,7 @@ describe('plugin-meetings', () => {
|
|
|
2715
2856
|
|
|
2716
2857
|
// set the info initially as locusInfo.info starts as undefined
|
|
2717
2858
|
expectedMeeting = {
|
|
2859
|
+
attendee: {},
|
|
2718
2860
|
coHost: {
|
|
2719
2861
|
LOWER_SOMEONE_ELSES_HAND: true,
|
|
2720
2862
|
},
|
|
@@ -2723,10 +2865,12 @@ describe('plugin-meetings', () => {
|
|
|
2723
2865
|
moderator: {
|
|
2724
2866
|
LOWER_SOMEONE_ELSES_HAND: true,
|
|
2725
2867
|
},
|
|
2868
|
+
panelist: {},
|
|
2726
2869
|
policy: {
|
|
2727
2870
|
LOCK_STATUS_UNLOCKED: true,
|
|
2728
2871
|
ROSTER_IN_MEETING: true,
|
|
2729
2872
|
},
|
|
2873
|
+
presenter: {},
|
|
2730
2874
|
userDisplayHints: ['ROSTER_IN_MEETING', 'LOCK_STATUS_UNLOCKED'],
|
|
2731
2875
|
};
|
|
2732
2876
|
locusInfo.updateMeetingInfo(initialInfo, self);
|
|
@@ -2741,6 +2885,7 @@ describe('plugin-meetings', () => {
|
|
|
2741
2885
|
|
|
2742
2886
|
// Updating with different info should trigger the event
|
|
2743
2887
|
expectedMeeting = {
|
|
2888
|
+
attendee: {},
|
|
2744
2889
|
coHost: {
|
|
2745
2890
|
LOWER_SOMEONE_ELSES_HAND: true,
|
|
2746
2891
|
LOCK_CONTROL_LOCK: true,
|
|
@@ -2750,10 +2895,12 @@ describe('plugin-meetings', () => {
|
|
|
2750
2895
|
moderator: {
|
|
2751
2896
|
LOWER_SOMEONE_ELSES_HAND: true,
|
|
2752
2897
|
},
|
|
2898
|
+
panelist: {},
|
|
2753
2899
|
policy: {
|
|
2754
2900
|
LOCK_STATUS_UNLOCKED: true,
|
|
2755
2901
|
ROSTER_IN_MEETING: true,
|
|
2756
2902
|
},
|
|
2903
|
+
presenter: {},
|
|
2757
2904
|
userDisplayHints: ['ROSTER_IN_MEETING', 'LOCK_STATUS_UNLOCKED'],
|
|
2758
2905
|
};
|
|
2759
2906
|
locusInfo.updateMeetingInfo(newInfo, self);
|
|
@@ -2762,6 +2909,7 @@ describe('plugin-meetings', () => {
|
|
|
2762
2909
|
|
|
2763
2910
|
// update it with the same info
|
|
2764
2911
|
expectedMeeting = {
|
|
2912
|
+
attendee: {},
|
|
2765
2913
|
coHost: {
|
|
2766
2914
|
LOWER_SOMEONE_ELSES_HAND: true,
|
|
2767
2915
|
LOCK_CONTROL_LOCK: true,
|
|
@@ -2771,10 +2919,12 @@ describe('plugin-meetings', () => {
|
|
|
2771
2919
|
moderator: {
|
|
2772
2920
|
LOWER_SOMEONE_ELSES_HAND: true,
|
|
2773
2921
|
},
|
|
2922
|
+
panelist: {},
|
|
2774
2923
|
policy: {
|
|
2775
2924
|
LOCK_STATUS_UNLOCKED: true,
|
|
2776
2925
|
ROSTER_IN_MEETING: true,
|
|
2777
2926
|
},
|
|
2927
|
+
presenter: {},
|
|
2778
2928
|
userDisplayHints: ['ROSTER_IN_MEETING', 'LOCK_STATUS_UNLOCKED'],
|
|
2779
2929
|
};
|
|
2780
2930
|
locusInfo.updateMeetingInfo(newInfo, self);
|
|
@@ -2789,6 +2939,7 @@ describe('plugin-meetings', () => {
|
|
|
2789
2939
|
hasRole: true,
|
|
2790
2940
|
});
|
|
2791
2941
|
expectedMeeting = {
|
|
2942
|
+
attendee: {},
|
|
2792
2943
|
coHost: {
|
|
2793
2944
|
LOWER_SOMEONE_ELSES_HAND: true,
|
|
2794
2945
|
LOCK_CONTROL_LOCK: true,
|
|
@@ -2798,10 +2949,12 @@ describe('plugin-meetings', () => {
|
|
|
2798
2949
|
moderator: {
|
|
2799
2950
|
LOWER_SOMEONE_ELSES_HAND: true,
|
|
2800
2951
|
},
|
|
2952
|
+
panelist: {},
|
|
2801
2953
|
policy: {
|
|
2802
2954
|
LOCK_STATUS_UNLOCKED: true,
|
|
2803
2955
|
ROSTER_IN_MEETING: true,
|
|
2804
2956
|
},
|
|
2957
|
+
presenter: {},
|
|
2805
2958
|
userDisplayHints: [
|
|
2806
2959
|
'ROSTER_IN_MEETING',
|
|
2807
2960
|
'LOCK_STATUS_UNLOCKED',
|
|
@@ -3154,13 +3307,14 @@ describe('plugin-meetings', () => {
|
|
|
3154
3307
|
const createMockParser = (state = 'active') => ({
|
|
3155
3308
|
state,
|
|
3156
3309
|
stop: sinon.stub(),
|
|
3157
|
-
|
|
3310
|
+
resumeFromMessage: sinon.stub(),
|
|
3158
3311
|
handleMessage: sinon.stub(),
|
|
3159
3312
|
});
|
|
3160
3313
|
|
|
3161
3314
|
const createSelfElementWithReplaces = (replacedLocusUrl, replacedAt) => ({
|
|
3162
3315
|
htMeta: {elementId: {type: 'Self'}},
|
|
3163
3316
|
data: {
|
|
3317
|
+
deviceUrl,
|
|
3164
3318
|
devices: [{url: deviceUrl, replaces: [{locusUrl: replacedLocusUrl, replacedAt}]}],
|
|
3165
3319
|
},
|
|
3166
3320
|
});
|
|
@@ -3236,7 +3390,7 @@ describe('plugin-meetings', () => {
|
|
|
3236
3390
|
stateElementsMessage: message,
|
|
3237
3391
|
});
|
|
3238
3392
|
|
|
3239
|
-
assert.calledOnce(parserA.
|
|
3393
|
+
assert.calledOnce(parserA.resumeFromMessage);
|
|
3240
3394
|
assert.calledOnce(parserB.stop);
|
|
3241
3395
|
});
|
|
3242
3396
|
|
|
@@ -3259,7 +3413,7 @@ describe('plugin-meetings', () => {
|
|
|
3259
3413
|
stateElementsMessage: message,
|
|
3260
3414
|
});
|
|
3261
3415
|
|
|
3262
|
-
assert.notCalled(parserA.
|
|
3416
|
+
assert.notCalled(parserA.resumeFromMessage);
|
|
3263
3417
|
assert.notCalled(parserB.stop);
|
|
3264
3418
|
});
|
|
3265
3419
|
|
|
@@ -3278,7 +3432,7 @@ describe('plugin-meetings', () => {
|
|
|
3278
3432
|
stateElementsMessage: message,
|
|
3279
3433
|
});
|
|
3280
3434
|
|
|
3281
|
-
assert.notCalled(parserA.
|
|
3435
|
+
assert.notCalled(parserA.resumeFromMessage);
|
|
3282
3436
|
assert.notCalled(parserA.handleMessage);
|
|
3283
3437
|
});
|
|
3284
3438
|
|
|
@@ -3362,6 +3516,51 @@ describe('plugin-meetings', () => {
|
|
|
3362
3516
|
|
|
3363
3517
|
assert.calledOnceWithExactly(parserA.handleMessage, message);
|
|
3364
3518
|
});
|
|
3519
|
+
|
|
3520
|
+
it('should send mismatch metric when eventType is not HASH_TREE_DATA_UPDATED', () => {
|
|
3521
|
+
const locusUrlA = 'http://locus-url-A.com';
|
|
3522
|
+
const parserA = {state: 'active', handleMessage: sinon.stub()};
|
|
3523
|
+
locusInfo.hashTreeParsers.set(locusUrlA, {parser: parserA, initializedFromHashTree: true});
|
|
3524
|
+
|
|
3525
|
+
locusInfo.parse(mockMeeting, {
|
|
3526
|
+
eventType: LOCUSEVENT.SELF_CHANGED,
|
|
3527
|
+
stateElementsMessage: {locusUrl: locusUrlA, locusStateElements: [], dataSets: []},
|
|
3528
|
+
});
|
|
3529
|
+
|
|
3530
|
+
assert.calledOnceWithExactly(
|
|
3531
|
+
sendBehavioralMetricStub,
|
|
3532
|
+
'js_sdk_locus_classic_vs_hash_tree_mismatch',
|
|
3533
|
+
{
|
|
3534
|
+
correlationId: mockMeeting.correlationId,
|
|
3535
|
+
message: `got ${LOCUSEVENT.SELF_CHANGED}, expected ${LOCUSEVENT.HASH_TREE_DATA_UPDATED}`,
|
|
3536
|
+
}
|
|
3537
|
+
);
|
|
3538
|
+
assert.notCalled(parserA.handleMessage);
|
|
3539
|
+
});
|
|
3540
|
+
});
|
|
3541
|
+
|
|
3542
|
+
describe('#sendClassicVsHashTreeMismatchMetric', () => {
|
|
3543
|
+
it('should send the metric when called for the first time', () => {
|
|
3544
|
+
locusInfo.sendClassicVsHashTreeMismatchMetric(mockMeeting, 'some mismatch');
|
|
3545
|
+
|
|
3546
|
+
assert.calledOnceWithExactly(
|
|
3547
|
+
sendBehavioralMetricStub,
|
|
3548
|
+
'js_sdk_locus_classic_vs_hash_tree_mismatch',
|
|
3549
|
+
{
|
|
3550
|
+
correlationId: mockMeeting.correlationId,
|
|
3551
|
+
message: 'some mismatch',
|
|
3552
|
+
}
|
|
3553
|
+
);
|
|
3554
|
+
});
|
|
3555
|
+
|
|
3556
|
+
it('should send the metric up to 5 times and stop after that', () => {
|
|
3557
|
+
for (let i = 0; i < 7; i += 1) {
|
|
3558
|
+
locusInfo.sendClassicVsHashTreeMismatchMetric(mockMeeting, `mismatch ${i}`);
|
|
3559
|
+
}
|
|
3560
|
+
|
|
3561
|
+
assert.callCount(sendBehavioralMetricStub, 5);
|
|
3562
|
+
assert.equal(locusInfo.classicVsHashTreeMismatchMetricCounter, 5);
|
|
3563
|
+
});
|
|
3365
3564
|
});
|
|
3366
3565
|
|
|
3367
3566
|
describe('#handleLocusAPIResponse', () => {
|
|
@@ -3417,19 +3616,186 @@ describe('plugin-meetings', () => {
|
|
|
3417
3616
|
assert.calledOnceWithExactly(locusInfo.handleLocusDelta, fakeLocus, mockMeeting);
|
|
3418
3617
|
});
|
|
3419
3618
|
|
|
3420
|
-
it('should send mismatch metric
|
|
3619
|
+
it('should send mismatch metric in classic mode when wrapped response has dataSets', () => {
|
|
3421
3620
|
const fakeLocus = {url: 'http://locus-url.com'};
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3621
|
+
sinon.stub(locusInfo, 'handleLocusDelta');
|
|
3622
|
+
|
|
3623
|
+
locusInfo.handleLocusAPIResponse(mockMeeting, {
|
|
3624
|
+
locus: fakeLocus,
|
|
3625
|
+
dataSets: [{name: 'dataset1', url: 'test-url'}],
|
|
3426
3626
|
});
|
|
3427
|
-
sinon.stub(locusInfo, 'sendClassicVsHashTreeMismatchMetric');
|
|
3428
3627
|
|
|
3429
|
-
|
|
3628
|
+
assert.calledOnceWithExactly(
|
|
3629
|
+
sendBehavioralMetricStub,
|
|
3630
|
+
'js_sdk_locus_classic_vs_hash_tree_mismatch',
|
|
3631
|
+
{
|
|
3632
|
+
correlationId: mockMeeting.correlationId,
|
|
3633
|
+
message: 'unexpected hash tree dataSets in API response',
|
|
3634
|
+
}
|
|
3635
|
+
);
|
|
3636
|
+
assert.calledOnce(locusInfo.handleLocusDelta);
|
|
3637
|
+
});
|
|
3430
3638
|
|
|
3431
|
-
|
|
3432
|
-
|
|
3639
|
+
describe('parser switch via API response', () => {
|
|
3640
|
+
const deviceUrl = 'http://device-url.com';
|
|
3641
|
+
const locusUrlA = 'http://locus-url-A.com';
|
|
3642
|
+
const locusUrlB = 'http://locus-url-B.com';
|
|
3643
|
+
|
|
3644
|
+
let HashTreeParserStub;
|
|
3645
|
+
|
|
3646
|
+
const createMockApiParser = (state = 'active') => ({
|
|
3647
|
+
state,
|
|
3648
|
+
stop: sinon.stub(),
|
|
3649
|
+
resumeFromApiResponse: sinon.stub(),
|
|
3650
|
+
handleLocusUpdate: sinon.stub(),
|
|
3651
|
+
initializeFromGetLociResponse: sinon.stub(),
|
|
3652
|
+
});
|
|
3653
|
+
|
|
3654
|
+
const createLocusWithReplaces = (url, replacedLocusUrl, replacedAt) => ({
|
|
3655
|
+
url,
|
|
3656
|
+
self: {
|
|
3657
|
+
devices: [{url: deviceUrl, replaces: [{locusUrl: replacedLocusUrl, replacedAt}]}],
|
|
3658
|
+
},
|
|
3659
|
+
});
|
|
3660
|
+
|
|
3661
|
+
const createLocusWithoutReplaces = (url) => ({
|
|
3662
|
+
url,
|
|
3663
|
+
self: {devices: [{url: deviceUrl}]},
|
|
3664
|
+
});
|
|
3665
|
+
|
|
3666
|
+
beforeEach(() => {
|
|
3667
|
+
locusInfo.webex.internal.device.url = deviceUrl;
|
|
3668
|
+
HashTreeParserStub = sinon
|
|
3669
|
+
.stub(HashTreeParserModule, 'default')
|
|
3670
|
+
.returns(createMockApiParser());
|
|
3671
|
+
});
|
|
3672
|
+
|
|
3673
|
+
it('should create a new parser and initialize it when no entry exists for the locusUrl', () => {
|
|
3674
|
+
// existing parser for a different url so hashTreeParsers.size > 0
|
|
3675
|
+
locusInfo.hashTreeParsers.set(locusUrlA, {parser: createMockApiParser(), initializedFromHashTree: true});
|
|
3676
|
+
|
|
3677
|
+
const locus = createLocusWithReplaces(locusUrlB, locusUrlA, '2026-01-01T00:00:00Z');
|
|
3678
|
+
sinon.stub(locusInfo, 'handleLocusDelta');
|
|
3679
|
+
|
|
3680
|
+
locusInfo.handleLocusAPIResponse(mockMeeting, {locus});
|
|
3681
|
+
|
|
3682
|
+
assert.isTrue(locusInfo.hashTreeParsers.has(locusUrlB));
|
|
3683
|
+
const newEntry = locusInfo.hashTreeParsers.get(locusUrlB);
|
|
3684
|
+
assert.isFalse(newEntry.initializedFromHashTree);
|
|
3685
|
+
|
|
3686
|
+
// the stub returns the mock, so initializeFromGetLociResponse should be called on it
|
|
3687
|
+
const createdParser = HashTreeParserStub.returnValues[0];
|
|
3688
|
+
assert.calledOnceWithExactly(createdParser.initializeFromGetLociResponse, locus);
|
|
3689
|
+
assert.notCalled(locusInfo.handleLocusDelta);
|
|
3690
|
+
});
|
|
3691
|
+
|
|
3692
|
+
it('should reactivate a stopped parser when replaces info is newer', () => {
|
|
3693
|
+
const parserA = createMockApiParser('stopped');
|
|
3694
|
+
const parserB = createMockApiParser('active');
|
|
3695
|
+
locusInfo.hashTreeParsers.set(locusUrlA, {parser: parserA, replacedAt: '2026-01-01T00:00:00Z', initializedFromHashTree: true});
|
|
3696
|
+
locusInfo.hashTreeParsers.set(locusUrlB, {parser: parserB, initializedFromHashTree: true});
|
|
3697
|
+
|
|
3698
|
+
const locus = createLocusWithReplaces(locusUrlA, locusUrlB, '2026-02-01T00:00:00Z');
|
|
3699
|
+
|
|
3700
|
+
locusInfo.handleLocusAPIResponse(mockMeeting, {locus});
|
|
3701
|
+
|
|
3702
|
+
assert.calledOnce(parserA.resumeFromApiResponse);
|
|
3703
|
+
assert.calledWithExactly(parserA.resumeFromApiResponse, locus);
|
|
3704
|
+
assert.calledOnce(parserB.stop);
|
|
3705
|
+
assert.equal(locusInfo.hashTreeParsers.get(locusUrlB).replacedAt, '2026-02-01T00:00:00Z');
|
|
3706
|
+
assert.isFalse(locusInfo.hashTreeParsers.get(locusUrlA).initializedFromHashTree);
|
|
3707
|
+
});
|
|
3708
|
+
|
|
3709
|
+
it('should not reactivate a stopped parser when replaces info is not newer', () => {
|
|
3710
|
+
const parserA = createMockApiParser('stopped');
|
|
3711
|
+
const parserB = createMockApiParser('active');
|
|
3712
|
+
locusInfo.hashTreeParsers.set(locusUrlA, {parser: parserA, replacedAt: '2026-03-01T00:00:00Z', initializedFromHashTree: true});
|
|
3713
|
+
locusInfo.hashTreeParsers.set(locusUrlB, {parser: parserB, initializedFromHashTree: true});
|
|
3714
|
+
|
|
3715
|
+
const locus = createLocusWithReplaces(locusUrlA, locusUrlB, '2026-01-01T00:00:00Z');
|
|
3716
|
+
|
|
3717
|
+
locusInfo.handleLocusAPIResponse(mockMeeting, {locus});
|
|
3718
|
+
|
|
3719
|
+
assert.notCalled(parserA.resumeFromApiResponse);
|
|
3720
|
+
assert.notCalled(parserB.stop);
|
|
3721
|
+
});
|
|
3722
|
+
|
|
3723
|
+
it('should not reactivate a stopped parser when no replaces info is available', () => {
|
|
3724
|
+
const parserA = createMockApiParser('stopped');
|
|
3725
|
+
locusInfo.hashTreeParsers.set(locusUrlA, {parser: parserA, initializedFromHashTree: true});
|
|
3726
|
+
|
|
3727
|
+
const locus = createLocusWithoutReplaces(locusUrlA);
|
|
3728
|
+
|
|
3729
|
+
locusInfo.handleLocusAPIResponse(mockMeeting, {locus});
|
|
3730
|
+
|
|
3731
|
+
assert.notCalled(parserA.resumeFromApiResponse);
|
|
3732
|
+
});
|
|
3733
|
+
});
|
|
3734
|
+
});
|
|
3735
|
+
|
|
3736
|
+
describe('#syncAllHashTreeDatasets', () => {
|
|
3737
|
+
it('should call syncAllDatasets on each parser that has an entry', async () => {
|
|
3738
|
+
const parser1 = {syncAllDatasets: sinon.stub().resolves()};
|
|
3739
|
+
const parser2 = {syncAllDatasets: sinon.stub().resolves()};
|
|
3740
|
+
locusInfo.hashTreeParsers.set('url1', {parser: parser1});
|
|
3741
|
+
locusInfo.hashTreeParsers.set('url2', {parser: parser2});
|
|
3742
|
+
|
|
3743
|
+
await locusInfo.syncAllHashTreeDatasets();
|
|
3744
|
+
|
|
3745
|
+
assert.calledOnce(parser1.syncAllDatasets);
|
|
3746
|
+
assert.calledOnce(parser2.syncAllDatasets);
|
|
3747
|
+
});
|
|
3748
|
+
|
|
3749
|
+
it('should skip parser entries without a parser object', async () => {
|
|
3750
|
+
const parser1 = {syncAllDatasets: sinon.stub().resolves()};
|
|
3751
|
+
locusInfo.hashTreeParsers.set('url1', {parser: parser1});
|
|
3752
|
+
locusInfo.hashTreeParsers.set('url2', {parser: undefined});
|
|
3753
|
+
|
|
3754
|
+
await locusInfo.syncAllHashTreeDatasets();
|
|
3755
|
+
|
|
3756
|
+
assert.calledOnce(parser1.syncAllDatasets);
|
|
3757
|
+
});
|
|
3758
|
+
|
|
3759
|
+
it('should forward options to each parser syncAllDatasets', async () => {
|
|
3760
|
+
const parser1 = {syncAllDatasets: sinon.stub().resolves()};
|
|
3761
|
+
const parser2 = {syncAllDatasets: sinon.stub().resolves()};
|
|
3762
|
+
locusInfo.hashTreeParsers.set('url1', {parser: parser1});
|
|
3763
|
+
locusInfo.hashTreeParsers.set('url2', {parser: parser2});
|
|
3764
|
+
|
|
3765
|
+
await locusInfo.syncAllHashTreeDatasets({onlyLLM: true});
|
|
3766
|
+
|
|
3767
|
+
assert.calledOnceWithExactly(parser1.syncAllDatasets, {onlyLLM: true});
|
|
3768
|
+
assert.calledOnceWithExactly(parser2.syncAllDatasets, {onlyLLM: true});
|
|
3769
|
+
});
|
|
3770
|
+
|
|
3771
|
+
it('should await each parsers syncAllDatasets sequentially', async () => {
|
|
3772
|
+
const callOrder = [];
|
|
3773
|
+
const parser1 = {syncAllDatasets: sinon.stub().callsFake(() => {
|
|
3774
|
+
callOrder.push('start1');
|
|
3775
|
+
return new Promise((resolve) => {
|
|
3776
|
+
setTimeout(() => {
|
|
3777
|
+
callOrder.push('end1');
|
|
3778
|
+
resolve();
|
|
3779
|
+
}, 100);
|
|
3780
|
+
});
|
|
3781
|
+
})};
|
|
3782
|
+
const parser2 = {syncAllDatasets: sinon.stub().callsFake(() => {
|
|
3783
|
+
callOrder.push('start2');
|
|
3784
|
+
return Promise.resolve();
|
|
3785
|
+
})};
|
|
3786
|
+
locusInfo.hashTreeParsers.set('url1', {parser: parser1});
|
|
3787
|
+
locusInfo.hashTreeParsers.set('url2', {parser: parser2});
|
|
3788
|
+
|
|
3789
|
+
const clock = sinon.useFakeTimers();
|
|
3790
|
+
const promise = locusInfo.syncAllHashTreeDatasets();
|
|
3791
|
+
// parser1 started but parser2 not yet
|
|
3792
|
+
assert.deepEqual(callOrder, ['start1']);
|
|
3793
|
+
|
|
3794
|
+
await clock.tickAsync(100);
|
|
3795
|
+
await promise;
|
|
3796
|
+
// parser1 finished, then parser2 started and finished
|
|
3797
|
+
assert.deepEqual(callOrder, ['start1', 'end1', 'start2']);
|
|
3798
|
+
clock.restore();
|
|
3433
3799
|
});
|
|
3434
3800
|
});
|
|
3435
3801
|
|
|
@@ -3521,49 +3887,23 @@ describe('plugin-meetings', () => {
|
|
|
3521
3887
|
assert.deepEqual(callOrder, ['updateLocusUrl', 'updateMeetingInfo']);
|
|
3522
3888
|
});
|
|
3523
3889
|
|
|
3524
|
-
it('#updateLocusInfo ignores
|
|
3525
|
-
const newLocus = {
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
state: 'LEFT',
|
|
3529
|
-
},
|
|
3530
|
-
};
|
|
3890
|
+
it('#updateLocusInfo ignores locus when isSelfMovedOrBreakoutEnded returns true', () => {
|
|
3891
|
+
const newLocus = {self: {state: 'JOINED'}};
|
|
3892
|
+
|
|
3893
|
+
sinon.stub(MeetingsUtil, 'isSelfMovedOrBreakoutEnded').returns(true);
|
|
3531
3894
|
|
|
3532
3895
|
locusInfo.updateControls = sinon.stub();
|
|
3533
|
-
locusInfo.updateConversationUrl = sinon.stub();
|
|
3534
|
-
locusInfo.updateCreated = sinon.stub();
|
|
3535
3896
|
locusInfo.updateFullState = sinon.stub();
|
|
3536
|
-
locusInfo.updateHostInfo = sinon.stub();
|
|
3537
|
-
locusInfo.updateMeetingInfo = sinon.stub();
|
|
3538
|
-
locusInfo.updateMediaShares = sinon.stub();
|
|
3539
|
-
locusInfo.updateReplaces = sinon.stub();
|
|
3540
3897
|
locusInfo.updateSelf = sinon.stub();
|
|
3541
|
-
locusInfo.updateLocusUrl = sinon.stub();
|
|
3542
|
-
locusInfo.updateAclUrl = sinon.stub();
|
|
3543
|
-
locusInfo.updateBasequence = sinon.stub();
|
|
3544
|
-
locusInfo.updateSequence = sinon.stub();
|
|
3545
|
-
locusInfo.updateEmbeddedApps = sinon.stub();
|
|
3546
|
-
locusInfo.updateLinks = sinon.stub();
|
|
3547
|
-
locusInfo.compareAndUpdate = sinon.stub();
|
|
3548
3898
|
|
|
3549
3899
|
locusInfo.updateLocusInfo(newLocus);
|
|
3550
3900
|
|
|
3901
|
+
assert.calledOnceWithExactly(MeetingsUtil.isSelfMovedOrBreakoutEnded, newLocus);
|
|
3551
3902
|
assert.notCalled(locusInfo.updateControls);
|
|
3552
|
-
assert.notCalled(locusInfo.updateConversationUrl);
|
|
3553
|
-
assert.notCalled(locusInfo.updateCreated);
|
|
3554
3903
|
assert.notCalled(locusInfo.updateFullState);
|
|
3555
|
-
assert.notCalled(locusInfo.updateHostInfo);
|
|
3556
|
-
assert.notCalled(locusInfo.updateMeetingInfo);
|
|
3557
|
-
assert.notCalled(locusInfo.updateMediaShares);
|
|
3558
|
-
assert.notCalled(locusInfo.updateReplaces);
|
|
3559
3904
|
assert.notCalled(locusInfo.updateSelf);
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
assert.notCalled(locusInfo.updateBasequence);
|
|
3563
|
-
assert.notCalled(locusInfo.updateSequence);
|
|
3564
|
-
assert.notCalled(locusInfo.updateEmbeddedApps);
|
|
3565
|
-
assert.notCalled(locusInfo.updateLinks);
|
|
3566
|
-
assert.notCalled(locusInfo.compareAndUpdate);
|
|
3905
|
+
|
|
3906
|
+
MeetingsUtil.isSelfMovedOrBreakoutEnded.restore();
|
|
3567
3907
|
});
|
|
3568
3908
|
|
|
3569
3909
|
it('#updateLocusInfo puts the Locus DTO top level properties at the right place in LocusInfo class', () => {
|
|
@@ -4413,6 +4753,31 @@ describe('plugin-meetings', () => {
|
|
|
4413
4753
|
});
|
|
4414
4754
|
});
|
|
4415
4755
|
|
|
4756
|
+
describe('#cleanUp', () => {
|
|
4757
|
+
it('calls cleanUp on all hash tree parsers and clears maps', () => {
|
|
4758
|
+
const parser1 = {cleanUp: sinon.stub()};
|
|
4759
|
+
const parser2 = {cleanUp: sinon.stub()};
|
|
4760
|
+
|
|
4761
|
+
locusInfo.hashTreeParsers.set('url1', {parser: parser1, initializedFromHashTree: true});
|
|
4762
|
+
locusInfo.hashTreeParsers.set('url2', {parser: parser2, initializedFromHashTree: true});
|
|
4763
|
+
locusInfo.hashTreeObjectId2ParticipantId.set(1, 'participant1');
|
|
4764
|
+
|
|
4765
|
+
locusInfo.cleanUp();
|
|
4766
|
+
|
|
4767
|
+
assert.calledOnce(parser1.cleanUp);
|
|
4768
|
+
assert.calledOnce(parser2.cleanUp);
|
|
4769
|
+
assert.equal(locusInfo.hashTreeParsers.size, 0);
|
|
4770
|
+
assert.equal(locusInfo.hashTreeObjectId2ParticipantId.size, 0);
|
|
4771
|
+
});
|
|
4772
|
+
|
|
4773
|
+
it('works when there are no hash tree parsers', () => {
|
|
4774
|
+
locusInfo.cleanUp();
|
|
4775
|
+
|
|
4776
|
+
assert.equal(locusInfo.hashTreeParsers.size, 0);
|
|
4777
|
+
assert.equal(locusInfo.hashTreeObjectId2ParticipantId.size, 0);
|
|
4778
|
+
});
|
|
4779
|
+
});
|
|
4780
|
+
|
|
4416
4781
|
describe('#handleOneonOneEvent', () => {
|
|
4417
4782
|
beforeEach(() => {
|
|
4418
4783
|
locusInfo.emitScoped = sinon.stub();
|
|
@@ -4455,6 +4820,9 @@ describe('plugin-meetings', () => {
|
|
|
4455
4820
|
});
|
|
4456
4821
|
|
|
4457
4822
|
describe('#isMeetingActive', () => {
|
|
4823
|
+
beforeEach(() => {
|
|
4824
|
+
webex.internal.newMetrics.submitClientEvent.resetHistory();
|
|
4825
|
+
});
|
|
4458
4826
|
forEach([_CALL_, _SIP_BRIDGE_, _SPACE_SHARE_], (type) => {
|
|
4459
4827
|
describe(`type = ${type}`, () => {
|
|
4460
4828
|
it('sends client event correctly for state = inactive', () => {
|
|
@@ -4521,7 +4889,7 @@ describe('plugin-meetings', () => {
|
|
|
4521
4889
|
});
|
|
4522
4890
|
});
|
|
4523
4891
|
|
|
4524
|
-
it('sends client event correctly for state =
|
|
4892
|
+
it('sends client event correctly for state = MEETING_INACTIVE', () => {
|
|
4525
4893
|
locusInfo.getLocusPartner = sinon.stub().returns({state: MEETING_STATE.STATES.LEFT});
|
|
4526
4894
|
locusInfo.parsedLocus = {
|
|
4527
4895
|
fullState: {
|
|
@@ -4543,7 +4911,7 @@ describe('plugin-meetings', () => {
|
|
|
4543
4911
|
});
|
|
4544
4912
|
});
|
|
4545
4913
|
|
|
4546
|
-
it('
|
|
4914
|
+
it('does not send client event when state = INACTIVE and endMeetingReason = BREAKOUT_ENDED', () => {
|
|
4547
4915
|
locusInfo.getLocusPartner = sinon.stub().returns({state: MEETING_STATE.STATES.LEFT});
|
|
4548
4916
|
locusInfo.parsedLocus = {
|
|
4549
4917
|
fullState: {
|
|
@@ -4552,17 +4920,41 @@ describe('plugin-meetings', () => {
|
|
|
4552
4920
|
};
|
|
4553
4921
|
|
|
4554
4922
|
locusInfo.fullState = {
|
|
4555
|
-
|
|
4923
|
+
state: LOCUS.STATE.INACTIVE,
|
|
4924
|
+
endMeetingReason: 'BREAKOUT_ENDED',
|
|
4556
4925
|
};
|
|
4557
4926
|
|
|
4558
4927
|
locusInfo.isMeetingActive();
|
|
4559
4928
|
|
|
4560
|
-
assert.
|
|
4561
|
-
|
|
4562
|
-
|
|
4563
|
-
|
|
4929
|
+
assert.notCalled(webex.internal.newMetrics.submitClientEvent);
|
|
4930
|
+
});
|
|
4931
|
+
|
|
4932
|
+
it('sends client event correctly for state self removed', () => {
|
|
4933
|
+
locusInfo.emitScoped = sinon.stub();
|
|
4934
|
+
locusInfo.parsedLocus = {
|
|
4935
|
+
fullState: {
|
|
4936
|
+
type: _MEETING_,
|
|
4564
4937
|
},
|
|
4565
|
-
|
|
4938
|
+
self: {
|
|
4939
|
+
removed: true,
|
|
4940
|
+
}
|
|
4941
|
+
};
|
|
4942
|
+
|
|
4943
|
+
locusInfo.isMeetingActive();
|
|
4944
|
+
|
|
4945
|
+
assert.notCalled(webex.internal.newMetrics.submitClientEvent);
|
|
4946
|
+
assert.calledOnceWithExactly(
|
|
4947
|
+
locusInfo.emitScoped,
|
|
4948
|
+
{
|
|
4949
|
+
file: 'locus-info',
|
|
4950
|
+
function: 'isMeetingActive',
|
|
4951
|
+
},
|
|
4952
|
+
EVENTS.DESTROY_MEETING,
|
|
4953
|
+
{
|
|
4954
|
+
reason: MEETING_REMOVED_REASON.SELF_REMOVED,
|
|
4955
|
+
shouldLeave: false,
|
|
4956
|
+
}
|
|
4957
|
+
);
|
|
4566
4958
|
});
|
|
4567
4959
|
});
|
|
4568
4960
|
|
|
@@ -4954,6 +5346,31 @@ describe('plugin-meetings', () => {
|
|
|
4954
5346
|
);
|
|
4955
5347
|
assert.notCalled(getTheLocusToUpdateStub);
|
|
4956
5348
|
});
|
|
5349
|
+
|
|
5350
|
+
it('should call handleLocusAPIResponse for SDK_LOCUS_FROM_SYNC_MEETINGS when hash tree parsers exist', () => {
|
|
5351
|
+
const fakeLocusUrl = 'http://locus-url.com';
|
|
5352
|
+
const fakeLocus = {url: fakeLocusUrl, fullState: {state: 'ACTIVE'}};
|
|
5353
|
+
const mockHashTreeParser = {
|
|
5354
|
+
handleMessage: sinon.stub(),
|
|
5355
|
+
handleLocusUpdate: sinon.stub(),
|
|
5356
|
+
};
|
|
5357
|
+
locusInfo.hashTreeParsers.set(fakeLocusUrl, {
|
|
5358
|
+
parser: mockHashTreeParser,
|
|
5359
|
+
initializedFromHashTree: true,
|
|
5360
|
+
});
|
|
5361
|
+
|
|
5362
|
+
sinon.stub(locusInfo, 'handleLocusDelta');
|
|
5363
|
+
|
|
5364
|
+
locusInfo.parse(mockMeeting, {
|
|
5365
|
+
eventType: LOCUSEVENT.SDK_LOCUS_FROM_SYNC_MEETINGS,
|
|
5366
|
+
locus: fakeLocus,
|
|
5367
|
+
});
|
|
5368
|
+
|
|
5369
|
+
// should route through handleLocusAPIResponse which passes unwrapped LocusDTO to parser
|
|
5370
|
+
assert.calledOnce(mockHashTreeParser.handleLocusUpdate);
|
|
5371
|
+
assert.notCalled(mockHashTreeParser.handleMessage);
|
|
5372
|
+
assert.notCalled(locusInfo.handleLocusDelta);
|
|
5373
|
+
});
|
|
4957
5374
|
});
|
|
4958
5375
|
});
|
|
4959
5376
|
|
|
@@ -5146,6 +5563,7 @@ describe('plugin-meetings', () => {
|
|
|
5146
5563
|
return {
|
|
5147
5564
|
htMeta: {elementId: {type: 'Self'}},
|
|
5148
5565
|
data: {
|
|
5566
|
+
deviceUrl,
|
|
5149
5567
|
devices,
|
|
5150
5568
|
},
|
|
5151
5569
|
};
|