@webex/plugin-meetings 3.12.0-next.5 → 3.12.0-next.50
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 +6 -2
- package/dist/breakouts/breakout.js.map +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/constants.js +6 -3
- 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 +38 -24
- 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 +10 -1
- package/dist/hashTree/constants.js.map +1 -1
- package/dist/hashTree/hashTreeParser.js +593 -358
- package/dist/hashTree/hashTreeParser.js.map +1 -1
- package/dist/hashTree/utils.js +22 -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/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/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 +277 -86
- package/dist/locus-info/index.js.map +1 -1
- package/dist/locus-info/types.js +16 -0
- package/dist/locus-info/types.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 +3 -1
- package/dist/meeting/in-meeting-actions.js.map +1 -1
- package/dist/meeting/index.js +842 -521
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/util.js +19 -2
- package/dist/meeting/util.js.map +1 -1
- package/dist/meetings/index.js +199 -77
- 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 +67 -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 +3 -0
- package/dist/member/util.js.map +1 -1
- package/dist/metrics/constants.js +2 -1
- package/dist/metrics/constants.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 +1 -0
- package/dist/types/constants.d.ts +2 -0
- package/dist/types/controls-options-manager/constants.d.ts +6 -1
- package/dist/types/controls-options-manager/index.d.ts +10 -0
- package/dist/types/hashTree/constants.d.ts +1 -0
- package/dist/types/hashTree/hashTreeParser.d.ts +61 -15
- package/dist/types/hashTree/utils.d.ts +11 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/interceptors/locusRetry.d.ts +4 -4
- package/dist/types/locus-info/index.d.ts +46 -6
- package/dist/types/locus-info/types.d.ts +17 -1
- package/dist/types/media/properties.d.ts +1 -0
- package/dist/types/meeting/in-meeting-actions.d.ts +2 -0
- package/dist/types/meeting/index.d.ts +70 -1
- package/dist/types/meeting/util.d.ts +8 -0
- package/dist/types/meetings/index.d.ts +18 -1
- 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 +1 -0
- package/dist/types/metrics/constants.d.ts +1 -0
- package/dist/webinar/index.js +361 -235
- 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 +2 -1
- package/src/config.ts +1 -0
- package/src/constants.ts +5 -1
- package/src/controls-options-manager/constants.ts +14 -1
- package/src/controls-options-manager/index.ts +47 -24
- package/src/controls-options-manager/util.ts +81 -1
- package/src/hashTree/constants.ts +9 -0
- package/src/hashTree/hashTreeParser.ts +306 -160
- package/src/hashTree/utils.ts +17 -0
- package/src/index.ts +5 -0
- package/src/interceptors/locusRetry.ts +25 -4
- package/src/interpretation/index.ts +25 -8
- package/src/locus-info/controlsUtils.ts +3 -1
- package/src/locus-info/index.ts +276 -93
- package/src/locus-info/types.ts +19 -1
- package/src/media/properties.ts +1 -0
- package/src/meeting/in-meeting-actions.ts +4 -0
- package/src/meeting/index.ts +315 -26
- package/src/meeting/util.ts +20 -2
- package/src/meetings/index.ts +104 -43
- package/src/meetings/meetings.types.ts +19 -0
- package/src/meetings/request.ts +43 -0
- package/src/meetings/util.ts +80 -1
- package/src/member/index.ts +10 -0
- package/src/member/types.ts +1 -0
- package/src/member/util.ts +3 -0
- package/src/metrics/constants.ts +1 -0
- package/src/recording-controller/index.ts +1 -2
- package/src/webinar/index.ts +162 -21
- package/test/unit/spec/aiEnableRequest/index.ts +86 -0
- package/test/unit/spec/breakouts/breakout.ts +7 -3
- package/test/unit/spec/controls-options-manager/index.js +140 -29
- package/test/unit/spec/controls-options-manager/util.js +165 -0
- package/test/unit/spec/hashTree/hashTreeParser.ts +1294 -191
- package/test/unit/spec/hashTree/utils.ts +88 -1
- 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 +443 -81
- package/test/unit/spec/meeting/in-meeting-actions.ts +2 -0
- package/test/unit/spec/meeting/index.js +836 -41
- package/test/unit/spec/meeting/muteState.js +3 -0
- package/test/unit/spec/meeting/utils.js +33 -0
- package/test/unit/spec/meetings/index.js +275 -10
- package/test/unit/spec/meetings/request.js +141 -0
- package/test/unit/spec/meetings/utils.js +161 -0
- package/test/unit/spec/member/index.js +7 -0
- package/test/unit/spec/member/util.js +24 -0
- package/test/unit/spec/recording-controller/index.js +9 -8
- package/test/unit/spec/webinar/index.ts +141 -16
|
@@ -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);
|
|
@@ -413,7 +455,7 @@ describe('plugin-meetings', () => {
|
|
|
413
455
|
};
|
|
414
456
|
|
|
415
457
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
416
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
458
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
417
459
|
updatedObjects: [{htMeta: {elementId: {type: 'self'}}, data: newSelf}],
|
|
418
460
|
});
|
|
419
461
|
|
|
@@ -440,7 +482,7 @@ describe('plugin-meetings', () => {
|
|
|
440
482
|
locusInfo.info.isWebinar = true;
|
|
441
483
|
|
|
442
484
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
443
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
485
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
444
486
|
updatedObjects: [{htMeta: {elementId: {type: 'self'}}, data: newSelf}],
|
|
445
487
|
});
|
|
446
488
|
|
|
@@ -473,7 +515,7 @@ describe('plugin-meetings', () => {
|
|
|
473
515
|
locusInfo.info.isWebinar = true;
|
|
474
516
|
|
|
475
517
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
476
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
518
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
477
519
|
updatedObjects: [{htMeta: {elementId: {type: 'self'}}, data: newSelf}],
|
|
478
520
|
});
|
|
479
521
|
|
|
@@ -501,7 +543,7 @@ describe('plugin-meetings', () => {
|
|
|
501
543
|
};
|
|
502
544
|
|
|
503
545
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
504
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
546
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
505
547
|
updatedObjects: [{htMeta: {elementId: {type: 'fullState'}}, data: newFullState}],
|
|
506
548
|
});
|
|
507
549
|
|
|
@@ -519,7 +561,7 @@ describe('plugin-meetings', () => {
|
|
|
519
561
|
};
|
|
520
562
|
|
|
521
563
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
522
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
564
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
523
565
|
updatedObjects: [{htMeta: {elementId: {type: 'info'}}, data: newInfo}],
|
|
524
566
|
});
|
|
525
567
|
|
|
@@ -537,7 +579,7 @@ describe('plugin-meetings', () => {
|
|
|
537
579
|
};
|
|
538
580
|
|
|
539
581
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
540
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
582
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
541
583
|
updatedObjects: [{htMeta: {elementId: {type: 'links'}}, data: newLinks}],
|
|
542
584
|
});
|
|
543
585
|
|
|
@@ -557,7 +599,7 @@ describe('plugin-meetings', () => {
|
|
|
557
599
|
};
|
|
558
600
|
|
|
559
601
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
560
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
602
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
561
603
|
updatedObjects: [{htMeta: newLocusHtMeta, data: newLocus}],
|
|
562
604
|
});
|
|
563
605
|
|
|
@@ -590,7 +632,7 @@ describe('plugin-meetings', () => {
|
|
|
590
632
|
};
|
|
591
633
|
|
|
592
634
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
593
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
635
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
594
636
|
updatedObjects: [
|
|
595
637
|
{
|
|
596
638
|
htMeta: newLocusHtMeta,
|
|
@@ -637,7 +679,7 @@ describe('plugin-meetings', () => {
|
|
|
637
679
|
};
|
|
638
680
|
|
|
639
681
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
640
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
682
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
641
683
|
updatedObjects: [
|
|
642
684
|
// first, a removal of LOCUS object
|
|
643
685
|
{htMeta: {elementId: {type: 'locus'}}, data: null},
|
|
@@ -671,7 +713,7 @@ describe('plugin-meetings', () => {
|
|
|
671
713
|
const newLocusHtMeta = {elementId: {type: 'locus', version: 99}};
|
|
672
714
|
|
|
673
715
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
674
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
716
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
675
717
|
updatedObjects: [
|
|
676
718
|
// first, an update
|
|
677
719
|
{htMeta: newLocusHtMeta, data: newLocus},
|
|
@@ -700,7 +742,7 @@ describe('plugin-meetings', () => {
|
|
|
700
742
|
};
|
|
701
743
|
|
|
702
744
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
703
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
745
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
704
746
|
updatedObjects: [
|
|
705
747
|
// first, an update
|
|
706
748
|
{htMeta: {elementId: {type: 'locus'}}, data: newLocus1},
|
|
@@ -730,7 +772,7 @@ describe('plugin-meetings', () => {
|
|
|
730
772
|
};
|
|
731
773
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
732
774
|
// with 1 participant added, 1 updated, and 1 removed
|
|
733
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
775
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
734
776
|
updatedObjects: [
|
|
735
777
|
{htMeta: {elementId: {type: 'participant', id: 'fake-ht-participant-1'}}, data: null},
|
|
736
778
|
{
|
|
@@ -774,7 +816,7 @@ describe('plugin-meetings', () => {
|
|
|
774
816
|
};
|
|
775
817
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
776
818
|
// with 1 participant added, 1 updated, and 1 removed
|
|
777
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
819
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
778
820
|
updatedObjects: [
|
|
779
821
|
{htMeta: {elementId: {type: 'mediashare', id: 'fake-ht-mediaShare-1'}}, data: null},
|
|
780
822
|
{
|
|
@@ -807,7 +849,7 @@ describe('plugin-meetings', () => {
|
|
|
807
849
|
};
|
|
808
850
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
809
851
|
// with 1 embedded app added, 1 updated, and 1 removed
|
|
810
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
852
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
811
853
|
updatedObjects: [
|
|
812
854
|
{htMeta: {elementId: {type: 'embeddedapp', id: 'fake-ht-embeddedApp-1'}}, data: null},
|
|
813
855
|
{
|
|
@@ -844,7 +886,7 @@ describe('plugin-meetings', () => {
|
|
|
844
886
|
};
|
|
845
887
|
|
|
846
888
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
847
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
889
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
848
890
|
updatedObjects: [
|
|
849
891
|
{
|
|
850
892
|
htMeta: {elementId: {type: 'mediashare', id: 'fake-ht-mediaShare-2'}},
|
|
@@ -883,7 +925,7 @@ describe('plugin-meetings', () => {
|
|
|
883
925
|
};
|
|
884
926
|
|
|
885
927
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
886
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
928
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
887
929
|
updatedObjects: [
|
|
888
930
|
{
|
|
889
931
|
htMeta: {elementId: {type: 'controlentry', id: 'control-1'}},
|
|
@@ -911,7 +953,7 @@ describe('plugin-meetings', () => {
|
|
|
911
953
|
|
|
912
954
|
it('should process locus update correctly when CONTROL object is received with no data', () => {
|
|
913
955
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
914
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
956
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
915
957
|
updatedObjects: [
|
|
916
958
|
{
|
|
917
959
|
htMeta: {elementId: {type: 'controlentry', id: 'some-control-id'}},
|
|
@@ -935,7 +977,7 @@ describe('plugin-meetings', () => {
|
|
|
935
977
|
const destroyStub = sinon.stub(locusInfo.webex.meetings, 'destroy');
|
|
936
978
|
|
|
937
979
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
938
|
-
locusInfoUpdateCallback(MEETING_ENDED);
|
|
980
|
+
locusInfoUpdateCallback({updateType: MEETING_ENDED});
|
|
939
981
|
|
|
940
982
|
assert.calledOnceWithExactly(collectionGetStub, locusInfo.meetingId);
|
|
941
983
|
assert.calledOnceWithExactly(
|
|
@@ -953,7 +995,7 @@ describe('plugin-meetings', () => {
|
|
|
953
995
|
const destroyStub = sinon.stub(locusInfo.webex.meetings, 'destroy');
|
|
954
996
|
|
|
955
997
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
956
|
-
locusInfoUpdateCallback(MEETING_ENDED);
|
|
998
|
+
locusInfoUpdateCallback({updateType: MEETING_ENDED});
|
|
957
999
|
|
|
958
1000
|
assert.calledOnceWithExactly(collectionGetStub, locusInfo.meetingId);
|
|
959
1001
|
assert.notCalled(destroyStub);
|
|
@@ -963,7 +1005,7 @@ describe('plugin-meetings', () => {
|
|
|
963
1005
|
const createdHashTreeParser = locusInfo.hashTreeParsers.get('fake-locus-url');
|
|
964
1006
|
createdHashTreeParser.initializedFromHashTree = false;
|
|
965
1007
|
|
|
966
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
1008
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
967
1009
|
updatedObjects: [
|
|
968
1010
|
{
|
|
969
1011
|
htMeta: {elementId: {type: 'self'}},
|
|
@@ -978,7 +1020,7 @@ describe('plugin-meetings', () => {
|
|
|
978
1020
|
});
|
|
979
1021
|
|
|
980
1022
|
it('should set forceReplaceMembers to false on subsequent updates (initializedFromHashTree is true)', () => {
|
|
981
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
1023
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
982
1024
|
updatedObjects: [
|
|
983
1025
|
{
|
|
984
1026
|
htMeta: {elementId: {type: 'self'}},
|
|
@@ -994,7 +1036,7 @@ describe('plugin-meetings', () => {
|
|
|
994
1036
|
it('should copy participant data to self when participant matches self identity and state is LEFT with reason MOVED', () => {
|
|
995
1037
|
locusInfo.self = {id: 'fake-self', identity: 'user-123'};
|
|
996
1038
|
|
|
997
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
1039
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
998
1040
|
updatedObjects: [
|
|
999
1041
|
{
|
|
1000
1042
|
htMeta: {elementId: {type: 'participant', id: 99}},
|
|
@@ -1289,6 +1331,8 @@ describe('plugin-meetings', () => {
|
|
|
1289
1331
|
state: RECORDING_STATE.IDLE,
|
|
1290
1332
|
modifiedBy: 'George Kittle',
|
|
1291
1333
|
lastModified: 'TODAY',
|
|
1334
|
+
modifiedByServiceAppName: undefined,
|
|
1335
|
+
modifiedByServiceAppId: undefined,
|
|
1292
1336
|
}
|
|
1293
1337
|
);
|
|
1294
1338
|
});
|
|
@@ -1323,6 +1367,8 @@ describe('plugin-meetings', () => {
|
|
|
1323
1367
|
state: RECORDING_STATE.RECORDING,
|
|
1324
1368
|
modifiedBy: 'George Kittle',
|
|
1325
1369
|
lastModified: 'TODAY',
|
|
1370
|
+
modifiedByServiceAppName: undefined,
|
|
1371
|
+
modifiedByServiceAppId: undefined,
|
|
1326
1372
|
}
|
|
1327
1373
|
);
|
|
1328
1374
|
});
|
|
@@ -1358,6 +1404,8 @@ describe('plugin-meetings', () => {
|
|
|
1358
1404
|
state: RECORDING_STATE.PAUSED,
|
|
1359
1405
|
modifiedBy: 'George Kittle',
|
|
1360
1406
|
lastModified: 'TODAY',
|
|
1407
|
+
modifiedByServiceAppName: undefined,
|
|
1408
|
+
modifiedByServiceAppId: undefined,
|
|
1361
1409
|
}
|
|
1362
1410
|
);
|
|
1363
1411
|
});
|
|
@@ -1394,6 +1442,8 @@ describe('plugin-meetings', () => {
|
|
|
1394
1442
|
state: RECORDING_STATE.RESUMED,
|
|
1395
1443
|
modifiedBy: 'George Kittle',
|
|
1396
1444
|
lastModified: 'TODAY',
|
|
1445
|
+
modifiedByServiceAppName: undefined,
|
|
1446
|
+
modifiedByServiceAppId: undefined,
|
|
1397
1447
|
}
|
|
1398
1448
|
);
|
|
1399
1449
|
});
|
|
@@ -1429,6 +1479,44 @@ describe('plugin-meetings', () => {
|
|
|
1429
1479
|
state: RECORDING_STATE.IDLE,
|
|
1430
1480
|
modifiedBy: 'George Kittle',
|
|
1431
1481
|
lastModified: 'TODAY',
|
|
1482
|
+
modifiedByServiceAppName: undefined,
|
|
1483
|
+
modifiedByServiceAppId: undefined,
|
|
1484
|
+
}
|
|
1485
|
+
);
|
|
1486
|
+
});
|
|
1487
|
+
|
|
1488
|
+
it('should include service app fields in the recording event when present', () => {
|
|
1489
|
+
locusInfo.controls = {
|
|
1490
|
+
record: {
|
|
1491
|
+
recording: false,
|
|
1492
|
+
paused: false,
|
|
1493
|
+
meta: {
|
|
1494
|
+
lastModified: 'TODAY',
|
|
1495
|
+
modifiedBy: 'George Kittle',
|
|
1496
|
+
},
|
|
1497
|
+
},
|
|
1498
|
+
shareControl: {},
|
|
1499
|
+
transcribe: {},
|
|
1500
|
+
};
|
|
1501
|
+
newControls.record.recording = true;
|
|
1502
|
+
newControls.record.meta.modifiedByServiceAppName = 'My Bot';
|
|
1503
|
+
newControls.record.meta.modifiedByServiceAppId = 'app-id-123';
|
|
1504
|
+
locusInfo.emitScoped = sinon.stub();
|
|
1505
|
+
locusInfo.updateControls(newControls);
|
|
1506
|
+
|
|
1507
|
+
assert.calledWith(
|
|
1508
|
+
locusInfo.emitScoped,
|
|
1509
|
+
{
|
|
1510
|
+
file: 'locus-info',
|
|
1511
|
+
function: 'updateControls',
|
|
1512
|
+
},
|
|
1513
|
+
LOCUSINFO.EVENTS.CONTROLS_RECORDING_UPDATED,
|
|
1514
|
+
{
|
|
1515
|
+
state: RECORDING_STATE.RECORDING,
|
|
1516
|
+
modifiedBy: 'George Kittle',
|
|
1517
|
+
lastModified: 'TODAY',
|
|
1518
|
+
modifiedByServiceAppName: 'My Bot',
|
|
1519
|
+
modifiedByServiceAppId: 'app-id-123',
|
|
1432
1520
|
}
|
|
1433
1521
|
);
|
|
1434
1522
|
});
|
|
@@ -2024,7 +2112,7 @@ describe('plugin-meetings', () => {
|
|
|
2024
2112
|
function: 'updateSelf',
|
|
2025
2113
|
},
|
|
2026
2114
|
LOCUSINFO.EVENTS.SELF_REMOTE_MUTE_STATUS_UPDATED,
|
|
2027
|
-
{muted: true, unmuteAllowed: true}
|
|
2115
|
+
{muted: true, unmuteAllowed: true, modifiedBy: null}
|
|
2028
2116
|
);
|
|
2029
2117
|
|
|
2030
2118
|
// but sometimes "previous self" is defined, but without controls.audio.muted, so we test this here:
|
|
@@ -2039,7 +2127,7 @@ describe('plugin-meetings', () => {
|
|
|
2039
2127
|
function: 'updateSelf',
|
|
2040
2128
|
},
|
|
2041
2129
|
LOCUSINFO.EVENTS.SELF_REMOTE_MUTE_STATUS_UPDATED,
|
|
2042
|
-
{muted: true, unmuteAllowed: true}
|
|
2130
|
+
{muted: true, unmuteAllowed: true, modifiedBy: null}
|
|
2043
2131
|
);
|
|
2044
2132
|
});
|
|
2045
2133
|
|
|
@@ -2098,7 +2186,7 @@ describe('plugin-meetings', () => {
|
|
|
2098
2186
|
function: 'updateSelf',
|
|
2099
2187
|
},
|
|
2100
2188
|
LOCUSINFO.EVENTS.SELF_REMOTE_MUTE_STATUS_UPDATED,
|
|
2101
|
-
{muted: true, unmuteAllowed: true}
|
|
2189
|
+
{muted: true, unmuteAllowed: true, modifiedBy: null}
|
|
2102
2190
|
);
|
|
2103
2191
|
});
|
|
2104
2192
|
|
|
@@ -2237,7 +2325,7 @@ describe('plugin-meetings', () => {
|
|
|
2237
2325
|
function: 'updateSelf',
|
|
2238
2326
|
},
|
|
2239
2327
|
LOCUSINFO.EVENTS.SELF_REMOTE_MUTE_STATUS_UPDATED,
|
|
2240
|
-
{muted: true, unmuteAllowed: false}
|
|
2328
|
+
{muted: true, unmuteAllowed: false, modifiedBy: null}
|
|
2241
2329
|
);
|
|
2242
2330
|
|
|
2243
2331
|
// now change only disallowUnmute
|
|
@@ -2255,7 +2343,28 @@ describe('plugin-meetings', () => {
|
|
|
2255
2343
|
function: 'updateSelf',
|
|
2256
2344
|
},
|
|
2257
2345
|
LOCUSINFO.EVENTS.SELF_REMOTE_MUTE_STATUS_UPDATED,
|
|
2258
|
-
{muted: true, unmuteAllowed: true}
|
|
2346
|
+
{muted: true, unmuteAllowed: true, modifiedBy: null}
|
|
2347
|
+
);
|
|
2348
|
+
});
|
|
2349
|
+
|
|
2350
|
+
it('should include modifiedBy in payload when muted by host', () => {
|
|
2351
|
+
locusInfo.webex.internal.device.url = self.deviceUrl;
|
|
2352
|
+
locusInfo.updateSelf(self);
|
|
2353
|
+
const newSelf = cloneDeep(self);
|
|
2354
|
+
newSelf.controls.audio.muted = true;
|
|
2355
|
+
newSelf.controls.audio.meta = {modifiedBy: 'host-uuid-123'};
|
|
2356
|
+
|
|
2357
|
+
locusInfo.emitScoped = sinon.stub();
|
|
2358
|
+
locusInfo.updateSelf(newSelf);
|
|
2359
|
+
|
|
2360
|
+
assert.calledWith(
|
|
2361
|
+
locusInfo.emitScoped,
|
|
2362
|
+
{
|
|
2363
|
+
file: 'locus-info',
|
|
2364
|
+
function: 'updateSelf',
|
|
2365
|
+
},
|
|
2366
|
+
LOCUSINFO.EVENTS.SELF_REMOTE_MUTE_STATUS_UPDATED,
|
|
2367
|
+
{muted: true, unmuteAllowed: true, modifiedBy: 'host-uuid-123'}
|
|
2259
2368
|
);
|
|
2260
2369
|
});
|
|
2261
2370
|
|
|
@@ -3154,13 +3263,14 @@ describe('plugin-meetings', () => {
|
|
|
3154
3263
|
const createMockParser = (state = 'active') => ({
|
|
3155
3264
|
state,
|
|
3156
3265
|
stop: sinon.stub(),
|
|
3157
|
-
|
|
3266
|
+
resumeFromMessage: sinon.stub(),
|
|
3158
3267
|
handleMessage: sinon.stub(),
|
|
3159
3268
|
});
|
|
3160
3269
|
|
|
3161
3270
|
const createSelfElementWithReplaces = (replacedLocusUrl, replacedAt) => ({
|
|
3162
3271
|
htMeta: {elementId: {type: 'Self'}},
|
|
3163
3272
|
data: {
|
|
3273
|
+
deviceUrl,
|
|
3164
3274
|
devices: [{url: deviceUrl, replaces: [{locusUrl: replacedLocusUrl, replacedAt}]}],
|
|
3165
3275
|
},
|
|
3166
3276
|
});
|
|
@@ -3236,7 +3346,7 @@ describe('plugin-meetings', () => {
|
|
|
3236
3346
|
stateElementsMessage: message,
|
|
3237
3347
|
});
|
|
3238
3348
|
|
|
3239
|
-
assert.calledOnce(parserA.
|
|
3349
|
+
assert.calledOnce(parserA.resumeFromMessage);
|
|
3240
3350
|
assert.calledOnce(parserB.stop);
|
|
3241
3351
|
});
|
|
3242
3352
|
|
|
@@ -3259,7 +3369,7 @@ describe('plugin-meetings', () => {
|
|
|
3259
3369
|
stateElementsMessage: message,
|
|
3260
3370
|
});
|
|
3261
3371
|
|
|
3262
|
-
assert.notCalled(parserA.
|
|
3372
|
+
assert.notCalled(parserA.resumeFromMessage);
|
|
3263
3373
|
assert.notCalled(parserB.stop);
|
|
3264
3374
|
});
|
|
3265
3375
|
|
|
@@ -3278,7 +3388,7 @@ describe('plugin-meetings', () => {
|
|
|
3278
3388
|
stateElementsMessage: message,
|
|
3279
3389
|
});
|
|
3280
3390
|
|
|
3281
|
-
assert.notCalled(parserA.
|
|
3391
|
+
assert.notCalled(parserA.resumeFromMessage);
|
|
3282
3392
|
assert.notCalled(parserA.handleMessage);
|
|
3283
3393
|
});
|
|
3284
3394
|
|
|
@@ -3362,6 +3472,51 @@ describe('plugin-meetings', () => {
|
|
|
3362
3472
|
|
|
3363
3473
|
assert.calledOnceWithExactly(parserA.handleMessage, message);
|
|
3364
3474
|
});
|
|
3475
|
+
|
|
3476
|
+
it('should send mismatch metric when eventType is not HASH_TREE_DATA_UPDATED', () => {
|
|
3477
|
+
const locusUrlA = 'http://locus-url-A.com';
|
|
3478
|
+
const parserA = {state: 'active', handleMessage: sinon.stub()};
|
|
3479
|
+
locusInfo.hashTreeParsers.set(locusUrlA, {parser: parserA, initializedFromHashTree: true});
|
|
3480
|
+
|
|
3481
|
+
locusInfo.parse(mockMeeting, {
|
|
3482
|
+
eventType: LOCUSEVENT.SELF_CHANGED,
|
|
3483
|
+
stateElementsMessage: {locusUrl: locusUrlA, locusStateElements: [], dataSets: []},
|
|
3484
|
+
});
|
|
3485
|
+
|
|
3486
|
+
assert.calledOnceWithExactly(
|
|
3487
|
+
sendBehavioralMetricStub,
|
|
3488
|
+
'js_sdk_locus_classic_vs_hash_tree_mismatch',
|
|
3489
|
+
{
|
|
3490
|
+
correlationId: mockMeeting.correlationId,
|
|
3491
|
+
message: `got ${LOCUSEVENT.SELF_CHANGED}, expected ${LOCUSEVENT.HASH_TREE_DATA_UPDATED}`,
|
|
3492
|
+
}
|
|
3493
|
+
);
|
|
3494
|
+
assert.notCalled(parserA.handleMessage);
|
|
3495
|
+
});
|
|
3496
|
+
});
|
|
3497
|
+
|
|
3498
|
+
describe('#sendClassicVsHashTreeMismatchMetric', () => {
|
|
3499
|
+
it('should send the metric when called for the first time', () => {
|
|
3500
|
+
locusInfo.sendClassicVsHashTreeMismatchMetric(mockMeeting, 'some mismatch');
|
|
3501
|
+
|
|
3502
|
+
assert.calledOnceWithExactly(
|
|
3503
|
+
sendBehavioralMetricStub,
|
|
3504
|
+
'js_sdk_locus_classic_vs_hash_tree_mismatch',
|
|
3505
|
+
{
|
|
3506
|
+
correlationId: mockMeeting.correlationId,
|
|
3507
|
+
message: 'some mismatch',
|
|
3508
|
+
}
|
|
3509
|
+
);
|
|
3510
|
+
});
|
|
3511
|
+
|
|
3512
|
+
it('should send the metric up to 5 times and stop after that', () => {
|
|
3513
|
+
for (let i = 0; i < 7; i += 1) {
|
|
3514
|
+
locusInfo.sendClassicVsHashTreeMismatchMetric(mockMeeting, `mismatch ${i}`);
|
|
3515
|
+
}
|
|
3516
|
+
|
|
3517
|
+
assert.callCount(sendBehavioralMetricStub, 5);
|
|
3518
|
+
assert.equal(locusInfo.classicVsHashTreeMismatchMetricCounter, 5);
|
|
3519
|
+
});
|
|
3365
3520
|
});
|
|
3366
3521
|
|
|
3367
3522
|
describe('#handleLocusAPIResponse', () => {
|
|
@@ -3417,19 +3572,174 @@ describe('plugin-meetings', () => {
|
|
|
3417
3572
|
assert.calledOnceWithExactly(locusInfo.handleLocusDelta, fakeLocus, mockMeeting);
|
|
3418
3573
|
});
|
|
3419
3574
|
|
|
3420
|
-
it('should send mismatch metric
|
|
3575
|
+
it('should send mismatch metric in classic mode when wrapped response has dataSets', () => {
|
|
3421
3576
|
const fakeLocus = {url: 'http://locus-url.com'};
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3577
|
+
sinon.stub(locusInfo, 'handleLocusDelta');
|
|
3578
|
+
|
|
3579
|
+
locusInfo.handleLocusAPIResponse(mockMeeting, {
|
|
3580
|
+
locus: fakeLocus,
|
|
3581
|
+
dataSets: [{name: 'dataset1', url: 'test-url'}],
|
|
3426
3582
|
});
|
|
3427
|
-
sinon.stub(locusInfo, 'sendClassicVsHashTreeMismatchMetric');
|
|
3428
3583
|
|
|
3429
|
-
|
|
3584
|
+
assert.calledOnceWithExactly(
|
|
3585
|
+
sendBehavioralMetricStub,
|
|
3586
|
+
'js_sdk_locus_classic_vs_hash_tree_mismatch',
|
|
3587
|
+
{
|
|
3588
|
+
correlationId: mockMeeting.correlationId,
|
|
3589
|
+
message: 'unexpected hash tree dataSets in API response',
|
|
3590
|
+
}
|
|
3591
|
+
);
|
|
3592
|
+
assert.calledOnce(locusInfo.handleLocusDelta);
|
|
3593
|
+
});
|
|
3430
3594
|
|
|
3431
|
-
|
|
3432
|
-
|
|
3595
|
+
describe('parser switch via API response', () => {
|
|
3596
|
+
const deviceUrl = 'http://device-url.com';
|
|
3597
|
+
const locusUrlA = 'http://locus-url-A.com';
|
|
3598
|
+
const locusUrlB = 'http://locus-url-B.com';
|
|
3599
|
+
|
|
3600
|
+
let HashTreeParserStub;
|
|
3601
|
+
|
|
3602
|
+
const createMockApiParser = (state = 'active') => ({
|
|
3603
|
+
state,
|
|
3604
|
+
stop: sinon.stub(),
|
|
3605
|
+
resumeFromApiResponse: sinon.stub(),
|
|
3606
|
+
handleLocusUpdate: sinon.stub(),
|
|
3607
|
+
initializeFromGetLociResponse: sinon.stub(),
|
|
3608
|
+
});
|
|
3609
|
+
|
|
3610
|
+
const createLocusWithReplaces = (url, replacedLocusUrl, replacedAt) => ({
|
|
3611
|
+
url,
|
|
3612
|
+
self: {
|
|
3613
|
+
devices: [{url: deviceUrl, replaces: [{locusUrl: replacedLocusUrl, replacedAt}]}],
|
|
3614
|
+
},
|
|
3615
|
+
});
|
|
3616
|
+
|
|
3617
|
+
const createLocusWithoutReplaces = (url) => ({
|
|
3618
|
+
url,
|
|
3619
|
+
self: {devices: [{url: deviceUrl}]},
|
|
3620
|
+
});
|
|
3621
|
+
|
|
3622
|
+
beforeEach(() => {
|
|
3623
|
+
locusInfo.webex.internal.device.url = deviceUrl;
|
|
3624
|
+
HashTreeParserStub = sinon
|
|
3625
|
+
.stub(HashTreeParserModule, 'default')
|
|
3626
|
+
.returns(createMockApiParser());
|
|
3627
|
+
});
|
|
3628
|
+
|
|
3629
|
+
it('should create a new parser and initialize it when no entry exists for the locusUrl', () => {
|
|
3630
|
+
// existing parser for a different url so hashTreeParsers.size > 0
|
|
3631
|
+
locusInfo.hashTreeParsers.set(locusUrlA, {parser: createMockApiParser(), initializedFromHashTree: true});
|
|
3632
|
+
|
|
3633
|
+
const locus = createLocusWithReplaces(locusUrlB, locusUrlA, '2026-01-01T00:00:00Z');
|
|
3634
|
+
sinon.stub(locusInfo, 'handleLocusDelta');
|
|
3635
|
+
|
|
3636
|
+
locusInfo.handleLocusAPIResponse(mockMeeting, {locus});
|
|
3637
|
+
|
|
3638
|
+
assert.isTrue(locusInfo.hashTreeParsers.has(locusUrlB));
|
|
3639
|
+
const newEntry = locusInfo.hashTreeParsers.get(locusUrlB);
|
|
3640
|
+
assert.isFalse(newEntry.initializedFromHashTree);
|
|
3641
|
+
|
|
3642
|
+
// the stub returns the mock, so initializeFromGetLociResponse should be called on it
|
|
3643
|
+
const createdParser = HashTreeParserStub.returnValues[0];
|
|
3644
|
+
assert.calledOnceWithExactly(createdParser.initializeFromGetLociResponse, locus);
|
|
3645
|
+
assert.notCalled(locusInfo.handleLocusDelta);
|
|
3646
|
+
});
|
|
3647
|
+
|
|
3648
|
+
it('should reactivate a stopped parser when replaces info is newer', () => {
|
|
3649
|
+
const parserA = createMockApiParser('stopped');
|
|
3650
|
+
const parserB = createMockApiParser('active');
|
|
3651
|
+
locusInfo.hashTreeParsers.set(locusUrlA, {parser: parserA, replacedAt: '2026-01-01T00:00:00Z', initializedFromHashTree: true});
|
|
3652
|
+
locusInfo.hashTreeParsers.set(locusUrlB, {parser: parserB, initializedFromHashTree: true});
|
|
3653
|
+
|
|
3654
|
+
const locus = createLocusWithReplaces(locusUrlA, locusUrlB, '2026-02-01T00:00:00Z');
|
|
3655
|
+
|
|
3656
|
+
locusInfo.handleLocusAPIResponse(mockMeeting, {locus});
|
|
3657
|
+
|
|
3658
|
+
assert.calledOnce(parserA.resumeFromApiResponse);
|
|
3659
|
+
assert.calledWithExactly(parserA.resumeFromApiResponse, locus);
|
|
3660
|
+
assert.calledOnce(parserB.stop);
|
|
3661
|
+
assert.equal(locusInfo.hashTreeParsers.get(locusUrlB).replacedAt, '2026-02-01T00:00:00Z');
|
|
3662
|
+
assert.isFalse(locusInfo.hashTreeParsers.get(locusUrlA).initializedFromHashTree);
|
|
3663
|
+
});
|
|
3664
|
+
|
|
3665
|
+
it('should not reactivate a stopped parser when replaces info is not newer', () => {
|
|
3666
|
+
const parserA = createMockApiParser('stopped');
|
|
3667
|
+
const parserB = createMockApiParser('active');
|
|
3668
|
+
locusInfo.hashTreeParsers.set(locusUrlA, {parser: parserA, replacedAt: '2026-03-01T00:00:00Z', initializedFromHashTree: true});
|
|
3669
|
+
locusInfo.hashTreeParsers.set(locusUrlB, {parser: parserB, initializedFromHashTree: true});
|
|
3670
|
+
|
|
3671
|
+
const locus = createLocusWithReplaces(locusUrlA, locusUrlB, '2026-01-01T00:00:00Z');
|
|
3672
|
+
|
|
3673
|
+
locusInfo.handleLocusAPIResponse(mockMeeting, {locus});
|
|
3674
|
+
|
|
3675
|
+
assert.notCalled(parserA.resumeFromApiResponse);
|
|
3676
|
+
assert.notCalled(parserB.stop);
|
|
3677
|
+
});
|
|
3678
|
+
|
|
3679
|
+
it('should not reactivate a stopped parser when no replaces info is available', () => {
|
|
3680
|
+
const parserA = createMockApiParser('stopped');
|
|
3681
|
+
locusInfo.hashTreeParsers.set(locusUrlA, {parser: parserA, initializedFromHashTree: true});
|
|
3682
|
+
|
|
3683
|
+
const locus = createLocusWithoutReplaces(locusUrlA);
|
|
3684
|
+
|
|
3685
|
+
locusInfo.handleLocusAPIResponse(mockMeeting, {locus});
|
|
3686
|
+
|
|
3687
|
+
assert.notCalled(parserA.resumeFromApiResponse);
|
|
3688
|
+
});
|
|
3689
|
+
});
|
|
3690
|
+
});
|
|
3691
|
+
|
|
3692
|
+
describe('#syncAllHashTreeDatasets', () => {
|
|
3693
|
+
it('should call syncAllDatasets on each parser that has an entry', async () => {
|
|
3694
|
+
const parser1 = {syncAllDatasets: sinon.stub().resolves()};
|
|
3695
|
+
const parser2 = {syncAllDatasets: sinon.stub().resolves()};
|
|
3696
|
+
locusInfo.hashTreeParsers.set('url1', {parser: parser1});
|
|
3697
|
+
locusInfo.hashTreeParsers.set('url2', {parser: parser2});
|
|
3698
|
+
|
|
3699
|
+
await locusInfo.syncAllHashTreeDatasets();
|
|
3700
|
+
|
|
3701
|
+
assert.calledOnce(parser1.syncAllDatasets);
|
|
3702
|
+
assert.calledOnce(parser2.syncAllDatasets);
|
|
3703
|
+
});
|
|
3704
|
+
|
|
3705
|
+
it('should skip parser entries without a parser object', async () => {
|
|
3706
|
+
const parser1 = {syncAllDatasets: sinon.stub().resolves()};
|
|
3707
|
+
locusInfo.hashTreeParsers.set('url1', {parser: parser1});
|
|
3708
|
+
locusInfo.hashTreeParsers.set('url2', {parser: undefined});
|
|
3709
|
+
|
|
3710
|
+
await locusInfo.syncAllHashTreeDatasets();
|
|
3711
|
+
|
|
3712
|
+
assert.calledOnce(parser1.syncAllDatasets);
|
|
3713
|
+
});
|
|
3714
|
+
|
|
3715
|
+
it('should await each parsers syncAllDatasets sequentially', async () => {
|
|
3716
|
+
const callOrder = [];
|
|
3717
|
+
const parser1 = {syncAllDatasets: sinon.stub().callsFake(() => {
|
|
3718
|
+
callOrder.push('start1');
|
|
3719
|
+
return new Promise((resolve) => {
|
|
3720
|
+
setTimeout(() => {
|
|
3721
|
+
callOrder.push('end1');
|
|
3722
|
+
resolve();
|
|
3723
|
+
}, 100);
|
|
3724
|
+
});
|
|
3725
|
+
})};
|
|
3726
|
+
const parser2 = {syncAllDatasets: sinon.stub().callsFake(() => {
|
|
3727
|
+
callOrder.push('start2');
|
|
3728
|
+
return Promise.resolve();
|
|
3729
|
+
})};
|
|
3730
|
+
locusInfo.hashTreeParsers.set('url1', {parser: parser1});
|
|
3731
|
+
locusInfo.hashTreeParsers.set('url2', {parser: parser2});
|
|
3732
|
+
|
|
3733
|
+
const clock = sinon.useFakeTimers();
|
|
3734
|
+
const promise = locusInfo.syncAllHashTreeDatasets();
|
|
3735
|
+
// parser1 started but parser2 not yet
|
|
3736
|
+
assert.deepEqual(callOrder, ['start1']);
|
|
3737
|
+
|
|
3738
|
+
await clock.tickAsync(100);
|
|
3739
|
+
await promise;
|
|
3740
|
+
// parser1 finished, then parser2 started and finished
|
|
3741
|
+
assert.deepEqual(callOrder, ['start1', 'end1', 'start2']);
|
|
3742
|
+
clock.restore();
|
|
3433
3743
|
});
|
|
3434
3744
|
});
|
|
3435
3745
|
|
|
@@ -3521,49 +3831,23 @@ describe('plugin-meetings', () => {
|
|
|
3521
3831
|
assert.deepEqual(callOrder, ['updateLocusUrl', 'updateMeetingInfo']);
|
|
3522
3832
|
});
|
|
3523
3833
|
|
|
3524
|
-
it('#updateLocusInfo ignores
|
|
3525
|
-
const newLocus = {
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
state: 'LEFT',
|
|
3529
|
-
},
|
|
3530
|
-
};
|
|
3834
|
+
it('#updateLocusInfo ignores locus when isSelfMovedOrBreakoutEnded returns true', () => {
|
|
3835
|
+
const newLocus = {self: {state: 'JOINED'}};
|
|
3836
|
+
|
|
3837
|
+
sinon.stub(MeetingsUtil, 'isSelfMovedOrBreakoutEnded').returns(true);
|
|
3531
3838
|
|
|
3532
3839
|
locusInfo.updateControls = sinon.stub();
|
|
3533
|
-
locusInfo.updateConversationUrl = sinon.stub();
|
|
3534
|
-
locusInfo.updateCreated = sinon.stub();
|
|
3535
3840
|
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
3841
|
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
3842
|
|
|
3549
3843
|
locusInfo.updateLocusInfo(newLocus);
|
|
3550
3844
|
|
|
3845
|
+
assert.calledOnceWithExactly(MeetingsUtil.isSelfMovedOrBreakoutEnded, newLocus);
|
|
3551
3846
|
assert.notCalled(locusInfo.updateControls);
|
|
3552
|
-
assert.notCalled(locusInfo.updateConversationUrl);
|
|
3553
|
-
assert.notCalled(locusInfo.updateCreated);
|
|
3554
3847
|
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
3848
|
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);
|
|
3849
|
+
|
|
3850
|
+
MeetingsUtil.isSelfMovedOrBreakoutEnded.restore();
|
|
3567
3851
|
});
|
|
3568
3852
|
|
|
3569
3853
|
it('#updateLocusInfo puts the Locus DTO top level properties at the right place in LocusInfo class', () => {
|
|
@@ -4413,6 +4697,31 @@ describe('plugin-meetings', () => {
|
|
|
4413
4697
|
});
|
|
4414
4698
|
});
|
|
4415
4699
|
|
|
4700
|
+
describe('#cleanUp', () => {
|
|
4701
|
+
it('calls cleanUp on all hash tree parsers and clears maps', () => {
|
|
4702
|
+
const parser1 = {cleanUp: sinon.stub()};
|
|
4703
|
+
const parser2 = {cleanUp: sinon.stub()};
|
|
4704
|
+
|
|
4705
|
+
locusInfo.hashTreeParsers.set('url1', {parser: parser1, initializedFromHashTree: true});
|
|
4706
|
+
locusInfo.hashTreeParsers.set('url2', {parser: parser2, initializedFromHashTree: true});
|
|
4707
|
+
locusInfo.hashTreeObjectId2ParticipantId.set(1, 'participant1');
|
|
4708
|
+
|
|
4709
|
+
locusInfo.cleanUp();
|
|
4710
|
+
|
|
4711
|
+
assert.calledOnce(parser1.cleanUp);
|
|
4712
|
+
assert.calledOnce(parser2.cleanUp);
|
|
4713
|
+
assert.equal(locusInfo.hashTreeParsers.size, 0);
|
|
4714
|
+
assert.equal(locusInfo.hashTreeObjectId2ParticipantId.size, 0);
|
|
4715
|
+
});
|
|
4716
|
+
|
|
4717
|
+
it('works when there are no hash tree parsers', () => {
|
|
4718
|
+
locusInfo.cleanUp();
|
|
4719
|
+
|
|
4720
|
+
assert.equal(locusInfo.hashTreeParsers.size, 0);
|
|
4721
|
+
assert.equal(locusInfo.hashTreeObjectId2ParticipantId.size, 0);
|
|
4722
|
+
});
|
|
4723
|
+
});
|
|
4724
|
+
|
|
4416
4725
|
describe('#handleOneonOneEvent', () => {
|
|
4417
4726
|
beforeEach(() => {
|
|
4418
4727
|
locusInfo.emitScoped = sinon.stub();
|
|
@@ -4455,6 +4764,9 @@ describe('plugin-meetings', () => {
|
|
|
4455
4764
|
});
|
|
4456
4765
|
|
|
4457
4766
|
describe('#isMeetingActive', () => {
|
|
4767
|
+
beforeEach(() => {
|
|
4768
|
+
webex.internal.newMetrics.submitClientEvent.resetHistory();
|
|
4769
|
+
});
|
|
4458
4770
|
forEach([_CALL_, _SIP_BRIDGE_, _SPACE_SHARE_], (type) => {
|
|
4459
4771
|
describe(`type = ${type}`, () => {
|
|
4460
4772
|
it('sends client event correctly for state = inactive', () => {
|
|
@@ -4521,7 +4833,7 @@ describe('plugin-meetings', () => {
|
|
|
4521
4833
|
});
|
|
4522
4834
|
});
|
|
4523
4835
|
|
|
4524
|
-
it('sends client event correctly for state =
|
|
4836
|
+
it('sends client event correctly for state = MEETING_INACTIVE', () => {
|
|
4525
4837
|
locusInfo.getLocusPartner = sinon.stub().returns({state: MEETING_STATE.STATES.LEFT});
|
|
4526
4838
|
locusInfo.parsedLocus = {
|
|
4527
4839
|
fullState: {
|
|
@@ -4543,7 +4855,7 @@ describe('plugin-meetings', () => {
|
|
|
4543
4855
|
});
|
|
4544
4856
|
});
|
|
4545
4857
|
|
|
4546
|
-
it('
|
|
4858
|
+
it('does not send client event when state = INACTIVE and endMeetingReason = BREAKOUT_ENDED', () => {
|
|
4547
4859
|
locusInfo.getLocusPartner = sinon.stub().returns({state: MEETING_STATE.STATES.LEFT});
|
|
4548
4860
|
locusInfo.parsedLocus = {
|
|
4549
4861
|
fullState: {
|
|
@@ -4552,17 +4864,41 @@ describe('plugin-meetings', () => {
|
|
|
4552
4864
|
};
|
|
4553
4865
|
|
|
4554
4866
|
locusInfo.fullState = {
|
|
4555
|
-
|
|
4867
|
+
state: LOCUS.STATE.INACTIVE,
|
|
4868
|
+
endMeetingReason: 'BREAKOUT_ENDED',
|
|
4556
4869
|
};
|
|
4557
4870
|
|
|
4558
4871
|
locusInfo.isMeetingActive();
|
|
4559
4872
|
|
|
4560
|
-
assert.
|
|
4561
|
-
|
|
4562
|
-
|
|
4563
|
-
|
|
4873
|
+
assert.notCalled(webex.internal.newMetrics.submitClientEvent);
|
|
4874
|
+
});
|
|
4875
|
+
|
|
4876
|
+
it('sends client event correctly for state self removed', () => {
|
|
4877
|
+
locusInfo.emitScoped = sinon.stub();
|
|
4878
|
+
locusInfo.parsedLocus = {
|
|
4879
|
+
fullState: {
|
|
4880
|
+
type: _MEETING_,
|
|
4564
4881
|
},
|
|
4565
|
-
|
|
4882
|
+
self: {
|
|
4883
|
+
removed: true,
|
|
4884
|
+
}
|
|
4885
|
+
};
|
|
4886
|
+
|
|
4887
|
+
locusInfo.isMeetingActive();
|
|
4888
|
+
|
|
4889
|
+
assert.notCalled(webex.internal.newMetrics.submitClientEvent);
|
|
4890
|
+
assert.calledOnceWithExactly(
|
|
4891
|
+
locusInfo.emitScoped,
|
|
4892
|
+
{
|
|
4893
|
+
file: 'locus-info',
|
|
4894
|
+
function: 'isMeetingActive',
|
|
4895
|
+
},
|
|
4896
|
+
EVENTS.DESTROY_MEETING,
|
|
4897
|
+
{
|
|
4898
|
+
reason: MEETING_REMOVED_REASON.SELF_REMOVED,
|
|
4899
|
+
shouldLeave: false,
|
|
4900
|
+
}
|
|
4901
|
+
);
|
|
4566
4902
|
});
|
|
4567
4903
|
});
|
|
4568
4904
|
|
|
@@ -4954,6 +5290,31 @@ describe('plugin-meetings', () => {
|
|
|
4954
5290
|
);
|
|
4955
5291
|
assert.notCalled(getTheLocusToUpdateStub);
|
|
4956
5292
|
});
|
|
5293
|
+
|
|
5294
|
+
it('should call handleLocusAPIResponse for SDK_LOCUS_FROM_SYNC_MEETINGS when hash tree parsers exist', () => {
|
|
5295
|
+
const fakeLocusUrl = 'http://locus-url.com';
|
|
5296
|
+
const fakeLocus = {url: fakeLocusUrl, fullState: {state: 'ACTIVE'}};
|
|
5297
|
+
const mockHashTreeParser = {
|
|
5298
|
+
handleMessage: sinon.stub(),
|
|
5299
|
+
handleLocusUpdate: sinon.stub(),
|
|
5300
|
+
};
|
|
5301
|
+
locusInfo.hashTreeParsers.set(fakeLocusUrl, {
|
|
5302
|
+
parser: mockHashTreeParser,
|
|
5303
|
+
initializedFromHashTree: true,
|
|
5304
|
+
});
|
|
5305
|
+
|
|
5306
|
+
sinon.stub(locusInfo, 'handleLocusDelta');
|
|
5307
|
+
|
|
5308
|
+
locusInfo.parse(mockMeeting, {
|
|
5309
|
+
eventType: LOCUSEVENT.SDK_LOCUS_FROM_SYNC_MEETINGS,
|
|
5310
|
+
locus: fakeLocus,
|
|
5311
|
+
});
|
|
5312
|
+
|
|
5313
|
+
// should route through handleLocusAPIResponse which passes unwrapped LocusDTO to parser
|
|
5314
|
+
assert.calledOnce(mockHashTreeParser.handleLocusUpdate);
|
|
5315
|
+
assert.notCalled(mockHashTreeParser.handleMessage);
|
|
5316
|
+
assert.notCalled(locusInfo.handleLocusDelta);
|
|
5317
|
+
});
|
|
4957
5318
|
});
|
|
4958
5319
|
});
|
|
4959
5320
|
|
|
@@ -5146,6 +5507,7 @@ describe('plugin-meetings', () => {
|
|
|
5146
5507
|
return {
|
|
5147
5508
|
htMeta: {elementId: {type: 'Self'}},
|
|
5148
5509
|
data: {
|
|
5510
|
+
deviceUrl,
|
|
5149
5511
|
devices,
|
|
5150
5512
|
},
|
|
5151
5513
|
};
|