@webex/plugin-meetings 3.12.0-next.3 → 3.12.0-next.31
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 +1 -1
- package/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/constants.js +3 -1
- 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 +23 -21
- 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 +550 -346
- 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/interceptors/locusRetry.js +23 -8
- package/dist/interceptors/locusRetry.js.map +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/index.js +222 -61
- package/dist/locus-info/index.js.map +1 -1
- package/dist/meeting/index.js +372 -292
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/util.js +1 -0
- package/dist/meeting/util.js.map +1 -1
- package/dist/meetings/index.js +146 -62
- package/dist/meetings/index.js.map +1 -1
- package/dist/meetings/util.js +39 -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 +5 -1
- package/dist/metrics/constants.js.map +1 -1
- package/dist/multistream/sendSlotManager.js +116 -2
- package/dist/multistream/sendSlotManager.js.map +1 -1
- package/dist/types/constants.d.ts +1 -0
- package/dist/types/controls-options-manager/constants.d.ts +6 -1
- package/dist/types/hashTree/constants.d.ts +1 -0
- package/dist/types/hashTree/hashTreeParser.d.ts +53 -15
- package/dist/types/hashTree/utils.d.ts +11 -0
- package/dist/types/interceptors/locusRetry.d.ts +4 -4
- package/dist/types/locus-info/index.d.ts +38 -5
- package/dist/types/meeting/index.d.ts +11 -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 +4 -0
- package/dist/types/multistream/sendSlotManager.d.ts +23 -1
- package/dist/webinar/index.js +301 -226
- package/dist/webinar/index.js.map +1 -1
- package/package.json +16 -16
- package/src/constants.ts +1 -0
- package/src/controls-options-manager/constants.ts +14 -1
- package/src/controls-options-manager/index.ts +26 -19
- package/src/controls-options-manager/util.ts +81 -1
- package/src/hashTree/constants.ts +9 -0
- package/src/hashTree/hashTreeParser.ts +273 -154
- package/src/hashTree/utils.ts +17 -0
- package/src/interceptors/locusRetry.ts +25 -4
- package/src/locus-info/index.ts +233 -79
- package/src/meeting/index.ts +98 -11
- package/src/meeting/util.ts +1 -0
- package/src/meetings/index.ts +58 -34
- package/src/meetings/util.ts +44 -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 +5 -0
- package/src/multistream/sendSlotManager.ts +97 -3
- package/src/webinar/index.ts +75 -1
- package/test/unit/spec/controls-options-manager/index.js +114 -6
- package/test/unit/spec/controls-options-manager/util.js +165 -0
- package/test/unit/spec/hashTree/hashTreeParser.ts +839 -37
- package/test/unit/spec/hashTree/utils.ts +88 -1
- package/test/unit/spec/interceptors/locusRetry.ts +205 -4
- package/test/unit/spec/locus-info/index.js +262 -64
- package/test/unit/spec/meeting/index.js +54 -36
- package/test/unit/spec/meeting/utils.js +4 -0
- package/test/unit/spec/meetings/index.js +190 -8
- package/test/unit/spec/meetings/utils.js +124 -0
- package/test/unit/spec/member/index.js +7 -0
- package/test/unit/spec/member/util.js +24 -0
- package/test/unit/spec/multistream/sendSlotManager.ts +135 -36
- package/test/unit/spec/webinar/index.ts +60 -0
|
@@ -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,
|
|
@@ -413,7 +414,7 @@ describe('plugin-meetings', () => {
|
|
|
413
414
|
};
|
|
414
415
|
|
|
415
416
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
416
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
417
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
417
418
|
updatedObjects: [{htMeta: {elementId: {type: 'self'}}, data: newSelf}],
|
|
418
419
|
});
|
|
419
420
|
|
|
@@ -440,7 +441,7 @@ describe('plugin-meetings', () => {
|
|
|
440
441
|
locusInfo.info.isWebinar = true;
|
|
441
442
|
|
|
442
443
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
443
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
444
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
444
445
|
updatedObjects: [{htMeta: {elementId: {type: 'self'}}, data: newSelf}],
|
|
445
446
|
});
|
|
446
447
|
|
|
@@ -473,7 +474,7 @@ describe('plugin-meetings', () => {
|
|
|
473
474
|
locusInfo.info.isWebinar = true;
|
|
474
475
|
|
|
475
476
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
476
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
477
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
477
478
|
updatedObjects: [{htMeta: {elementId: {type: 'self'}}, data: newSelf}],
|
|
478
479
|
});
|
|
479
480
|
|
|
@@ -501,7 +502,7 @@ describe('plugin-meetings', () => {
|
|
|
501
502
|
};
|
|
502
503
|
|
|
503
504
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
504
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
505
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
505
506
|
updatedObjects: [{htMeta: {elementId: {type: 'fullState'}}, data: newFullState}],
|
|
506
507
|
});
|
|
507
508
|
|
|
@@ -519,7 +520,7 @@ describe('plugin-meetings', () => {
|
|
|
519
520
|
};
|
|
520
521
|
|
|
521
522
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
522
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
523
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
523
524
|
updatedObjects: [{htMeta: {elementId: {type: 'info'}}, data: newInfo}],
|
|
524
525
|
});
|
|
525
526
|
|
|
@@ -537,7 +538,7 @@ describe('plugin-meetings', () => {
|
|
|
537
538
|
};
|
|
538
539
|
|
|
539
540
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
540
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
541
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
541
542
|
updatedObjects: [{htMeta: {elementId: {type: 'links'}}, data: newLinks}],
|
|
542
543
|
});
|
|
543
544
|
|
|
@@ -557,7 +558,7 @@ describe('plugin-meetings', () => {
|
|
|
557
558
|
};
|
|
558
559
|
|
|
559
560
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
560
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
561
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
561
562
|
updatedObjects: [{htMeta: newLocusHtMeta, data: newLocus}],
|
|
562
563
|
});
|
|
563
564
|
|
|
@@ -590,7 +591,7 @@ describe('plugin-meetings', () => {
|
|
|
590
591
|
};
|
|
591
592
|
|
|
592
593
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
593
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
594
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
594
595
|
updatedObjects: [
|
|
595
596
|
{
|
|
596
597
|
htMeta: newLocusHtMeta,
|
|
@@ -637,7 +638,7 @@ describe('plugin-meetings', () => {
|
|
|
637
638
|
};
|
|
638
639
|
|
|
639
640
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
640
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
641
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
641
642
|
updatedObjects: [
|
|
642
643
|
// first, a removal of LOCUS object
|
|
643
644
|
{htMeta: {elementId: {type: 'locus'}}, data: null},
|
|
@@ -671,7 +672,7 @@ describe('plugin-meetings', () => {
|
|
|
671
672
|
const newLocusHtMeta = {elementId: {type: 'locus', version: 99}};
|
|
672
673
|
|
|
673
674
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
674
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
675
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
675
676
|
updatedObjects: [
|
|
676
677
|
// first, an update
|
|
677
678
|
{htMeta: newLocusHtMeta, data: newLocus},
|
|
@@ -700,7 +701,7 @@ describe('plugin-meetings', () => {
|
|
|
700
701
|
};
|
|
701
702
|
|
|
702
703
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
703
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
704
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
704
705
|
updatedObjects: [
|
|
705
706
|
// first, an update
|
|
706
707
|
{htMeta: {elementId: {type: 'locus'}}, data: newLocus1},
|
|
@@ -730,7 +731,7 @@ describe('plugin-meetings', () => {
|
|
|
730
731
|
};
|
|
731
732
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
732
733
|
// with 1 participant added, 1 updated, and 1 removed
|
|
733
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
734
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
734
735
|
updatedObjects: [
|
|
735
736
|
{htMeta: {elementId: {type: 'participant', id: 'fake-ht-participant-1'}}, data: null},
|
|
736
737
|
{
|
|
@@ -774,7 +775,7 @@ describe('plugin-meetings', () => {
|
|
|
774
775
|
};
|
|
775
776
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
776
777
|
// with 1 participant added, 1 updated, and 1 removed
|
|
777
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
778
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
778
779
|
updatedObjects: [
|
|
779
780
|
{htMeta: {elementId: {type: 'mediashare', id: 'fake-ht-mediaShare-1'}}, data: null},
|
|
780
781
|
{
|
|
@@ -807,7 +808,7 @@ describe('plugin-meetings', () => {
|
|
|
807
808
|
};
|
|
808
809
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
809
810
|
// with 1 embedded app added, 1 updated, and 1 removed
|
|
810
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
811
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
811
812
|
updatedObjects: [
|
|
812
813
|
{htMeta: {elementId: {type: 'embeddedapp', id: 'fake-ht-embeddedApp-1'}}, data: null},
|
|
813
814
|
{
|
|
@@ -844,7 +845,7 @@ describe('plugin-meetings', () => {
|
|
|
844
845
|
};
|
|
845
846
|
|
|
846
847
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
847
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
848
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
848
849
|
updatedObjects: [
|
|
849
850
|
{
|
|
850
851
|
htMeta: {elementId: {type: 'mediashare', id: 'fake-ht-mediaShare-2'}},
|
|
@@ -883,7 +884,7 @@ describe('plugin-meetings', () => {
|
|
|
883
884
|
};
|
|
884
885
|
|
|
885
886
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
886
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
887
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
887
888
|
updatedObjects: [
|
|
888
889
|
{
|
|
889
890
|
htMeta: {elementId: {type: 'controlentry', id: 'control-1'}},
|
|
@@ -911,7 +912,7 @@ describe('plugin-meetings', () => {
|
|
|
911
912
|
|
|
912
913
|
it('should process locus update correctly when CONTROL object is received with no data', () => {
|
|
913
914
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
914
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
915
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
915
916
|
updatedObjects: [
|
|
916
917
|
{
|
|
917
918
|
htMeta: {elementId: {type: 'controlentry', id: 'some-control-id'}},
|
|
@@ -935,7 +936,7 @@ describe('plugin-meetings', () => {
|
|
|
935
936
|
const destroyStub = sinon.stub(locusInfo.webex.meetings, 'destroy');
|
|
936
937
|
|
|
937
938
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
938
|
-
locusInfoUpdateCallback(MEETING_ENDED);
|
|
939
|
+
locusInfoUpdateCallback({updateType: MEETING_ENDED});
|
|
939
940
|
|
|
940
941
|
assert.calledOnceWithExactly(collectionGetStub, locusInfo.meetingId);
|
|
941
942
|
assert.calledOnceWithExactly(
|
|
@@ -953,7 +954,7 @@ describe('plugin-meetings', () => {
|
|
|
953
954
|
const destroyStub = sinon.stub(locusInfo.webex.meetings, 'destroy');
|
|
954
955
|
|
|
955
956
|
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
956
|
-
locusInfoUpdateCallback(MEETING_ENDED);
|
|
957
|
+
locusInfoUpdateCallback({updateType: MEETING_ENDED});
|
|
957
958
|
|
|
958
959
|
assert.calledOnceWithExactly(collectionGetStub, locusInfo.meetingId);
|
|
959
960
|
assert.notCalled(destroyStub);
|
|
@@ -963,7 +964,7 @@ describe('plugin-meetings', () => {
|
|
|
963
964
|
const createdHashTreeParser = locusInfo.hashTreeParsers.get('fake-locus-url');
|
|
964
965
|
createdHashTreeParser.initializedFromHashTree = false;
|
|
965
966
|
|
|
966
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
967
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
967
968
|
updatedObjects: [
|
|
968
969
|
{
|
|
969
970
|
htMeta: {elementId: {type: 'self'}},
|
|
@@ -978,7 +979,7 @@ describe('plugin-meetings', () => {
|
|
|
978
979
|
});
|
|
979
980
|
|
|
980
981
|
it('should set forceReplaceMembers to false on subsequent updates (initializedFromHashTree is true)', () => {
|
|
981
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
982
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
982
983
|
updatedObjects: [
|
|
983
984
|
{
|
|
984
985
|
htMeta: {elementId: {type: 'self'}},
|
|
@@ -994,7 +995,7 @@ describe('plugin-meetings', () => {
|
|
|
994
995
|
it('should copy participant data to self when participant matches self identity and state is LEFT with reason MOVED', () => {
|
|
995
996
|
locusInfo.self = {id: 'fake-self', identity: 'user-123'};
|
|
996
997
|
|
|
997
|
-
locusInfoUpdateCallback(OBJECTS_UPDATED,
|
|
998
|
+
locusInfoUpdateCallback({updateType: OBJECTS_UPDATED,
|
|
998
999
|
updatedObjects: [
|
|
999
1000
|
{
|
|
1000
1001
|
htMeta: {elementId: {type: 'participant', id: 99}},
|
|
@@ -2024,7 +2025,7 @@ describe('plugin-meetings', () => {
|
|
|
2024
2025
|
function: 'updateSelf',
|
|
2025
2026
|
},
|
|
2026
2027
|
LOCUSINFO.EVENTS.SELF_REMOTE_MUTE_STATUS_UPDATED,
|
|
2027
|
-
{muted: true, unmuteAllowed: true}
|
|
2028
|
+
{muted: true, unmuteAllowed: true, modifiedBy: null}
|
|
2028
2029
|
);
|
|
2029
2030
|
|
|
2030
2031
|
// but sometimes "previous self" is defined, but without controls.audio.muted, so we test this here:
|
|
@@ -2039,7 +2040,7 @@ describe('plugin-meetings', () => {
|
|
|
2039
2040
|
function: 'updateSelf',
|
|
2040
2041
|
},
|
|
2041
2042
|
LOCUSINFO.EVENTS.SELF_REMOTE_MUTE_STATUS_UPDATED,
|
|
2042
|
-
{muted: true, unmuteAllowed: true}
|
|
2043
|
+
{muted: true, unmuteAllowed: true, modifiedBy: null}
|
|
2043
2044
|
);
|
|
2044
2045
|
});
|
|
2045
2046
|
|
|
@@ -2098,7 +2099,7 @@ describe('plugin-meetings', () => {
|
|
|
2098
2099
|
function: 'updateSelf',
|
|
2099
2100
|
},
|
|
2100
2101
|
LOCUSINFO.EVENTS.SELF_REMOTE_MUTE_STATUS_UPDATED,
|
|
2101
|
-
{muted: true, unmuteAllowed: true}
|
|
2102
|
+
{muted: true, unmuteAllowed: true, modifiedBy: null}
|
|
2102
2103
|
);
|
|
2103
2104
|
});
|
|
2104
2105
|
|
|
@@ -2237,7 +2238,7 @@ describe('plugin-meetings', () => {
|
|
|
2237
2238
|
function: 'updateSelf',
|
|
2238
2239
|
},
|
|
2239
2240
|
LOCUSINFO.EVENTS.SELF_REMOTE_MUTE_STATUS_UPDATED,
|
|
2240
|
-
{muted: true, unmuteAllowed: false}
|
|
2241
|
+
{muted: true, unmuteAllowed: false, modifiedBy: null}
|
|
2241
2242
|
);
|
|
2242
2243
|
|
|
2243
2244
|
// now change only disallowUnmute
|
|
@@ -2255,7 +2256,28 @@ describe('plugin-meetings', () => {
|
|
|
2255
2256
|
function: 'updateSelf',
|
|
2256
2257
|
},
|
|
2257
2258
|
LOCUSINFO.EVENTS.SELF_REMOTE_MUTE_STATUS_UPDATED,
|
|
2258
|
-
{muted: true, unmuteAllowed: true}
|
|
2259
|
+
{muted: true, unmuteAllowed: true, modifiedBy: null}
|
|
2260
|
+
);
|
|
2261
|
+
});
|
|
2262
|
+
|
|
2263
|
+
it('should include modifiedBy in payload when muted by host', () => {
|
|
2264
|
+
locusInfo.webex.internal.device.url = self.deviceUrl;
|
|
2265
|
+
locusInfo.updateSelf(self);
|
|
2266
|
+
const newSelf = cloneDeep(self);
|
|
2267
|
+
newSelf.controls.audio.muted = true;
|
|
2268
|
+
newSelf.controls.audio.meta = {modifiedBy: 'host-uuid-123'};
|
|
2269
|
+
|
|
2270
|
+
locusInfo.emitScoped = sinon.stub();
|
|
2271
|
+
locusInfo.updateSelf(newSelf);
|
|
2272
|
+
|
|
2273
|
+
assert.calledWith(
|
|
2274
|
+
locusInfo.emitScoped,
|
|
2275
|
+
{
|
|
2276
|
+
file: 'locus-info',
|
|
2277
|
+
function: 'updateSelf',
|
|
2278
|
+
},
|
|
2279
|
+
LOCUSINFO.EVENTS.SELF_REMOTE_MUTE_STATUS_UPDATED,
|
|
2280
|
+
{muted: true, unmuteAllowed: true, modifiedBy: 'host-uuid-123'}
|
|
2259
2281
|
);
|
|
2260
2282
|
});
|
|
2261
2283
|
|
|
@@ -3154,13 +3176,14 @@ describe('plugin-meetings', () => {
|
|
|
3154
3176
|
const createMockParser = (state = 'active') => ({
|
|
3155
3177
|
state,
|
|
3156
3178
|
stop: sinon.stub(),
|
|
3157
|
-
|
|
3179
|
+
resumeFromMessage: sinon.stub(),
|
|
3158
3180
|
handleMessage: sinon.stub(),
|
|
3159
3181
|
});
|
|
3160
3182
|
|
|
3161
3183
|
const createSelfElementWithReplaces = (replacedLocusUrl, replacedAt) => ({
|
|
3162
3184
|
htMeta: {elementId: {type: 'Self'}},
|
|
3163
3185
|
data: {
|
|
3186
|
+
deviceUrl,
|
|
3164
3187
|
devices: [{url: deviceUrl, replaces: [{locusUrl: replacedLocusUrl, replacedAt}]}],
|
|
3165
3188
|
},
|
|
3166
3189
|
});
|
|
@@ -3236,7 +3259,7 @@ describe('plugin-meetings', () => {
|
|
|
3236
3259
|
stateElementsMessage: message,
|
|
3237
3260
|
});
|
|
3238
3261
|
|
|
3239
|
-
assert.calledOnce(parserA.
|
|
3262
|
+
assert.calledOnce(parserA.resumeFromMessage);
|
|
3240
3263
|
assert.calledOnce(parserB.stop);
|
|
3241
3264
|
});
|
|
3242
3265
|
|
|
@@ -3259,7 +3282,7 @@ describe('plugin-meetings', () => {
|
|
|
3259
3282
|
stateElementsMessage: message,
|
|
3260
3283
|
});
|
|
3261
3284
|
|
|
3262
|
-
assert.notCalled(parserA.
|
|
3285
|
+
assert.notCalled(parserA.resumeFromMessage);
|
|
3263
3286
|
assert.notCalled(parserB.stop);
|
|
3264
3287
|
});
|
|
3265
3288
|
|
|
@@ -3278,7 +3301,7 @@ describe('plugin-meetings', () => {
|
|
|
3278
3301
|
stateElementsMessage: message,
|
|
3279
3302
|
});
|
|
3280
3303
|
|
|
3281
|
-
assert.notCalled(parserA.
|
|
3304
|
+
assert.notCalled(parserA.resumeFromMessage);
|
|
3282
3305
|
assert.notCalled(parserA.handleMessage);
|
|
3283
3306
|
});
|
|
3284
3307
|
|
|
@@ -3431,6 +3454,156 @@ describe('plugin-meetings', () => {
|
|
|
3431
3454
|
assert.calledOnce(locusInfo.sendClassicVsHashTreeMismatchMetric);
|
|
3432
3455
|
assert.calledOnce(mockHashTreeParser.handleLocusUpdate);
|
|
3433
3456
|
});
|
|
3457
|
+
|
|
3458
|
+
describe('parser switch via API response', () => {
|
|
3459
|
+
const deviceUrl = 'http://device-url.com';
|
|
3460
|
+
const locusUrlA = 'http://locus-url-A.com';
|
|
3461
|
+
const locusUrlB = 'http://locus-url-B.com';
|
|
3462
|
+
|
|
3463
|
+
let HashTreeParserStub;
|
|
3464
|
+
|
|
3465
|
+
const createMockApiParser = (state = 'active') => ({
|
|
3466
|
+
state,
|
|
3467
|
+
stop: sinon.stub(),
|
|
3468
|
+
resumeFromApiResponse: sinon.stub(),
|
|
3469
|
+
handleLocusUpdate: sinon.stub(),
|
|
3470
|
+
initializeFromGetLociResponse: sinon.stub(),
|
|
3471
|
+
});
|
|
3472
|
+
|
|
3473
|
+
const createLocusWithReplaces = (url, replacedLocusUrl, replacedAt) => ({
|
|
3474
|
+
url,
|
|
3475
|
+
self: {
|
|
3476
|
+
devices: [{url: deviceUrl, replaces: [{locusUrl: replacedLocusUrl, replacedAt}]}],
|
|
3477
|
+
},
|
|
3478
|
+
});
|
|
3479
|
+
|
|
3480
|
+
const createLocusWithoutReplaces = (url) => ({
|
|
3481
|
+
url,
|
|
3482
|
+
self: {devices: [{url: deviceUrl}]},
|
|
3483
|
+
});
|
|
3484
|
+
|
|
3485
|
+
beforeEach(() => {
|
|
3486
|
+
locusInfo.webex.internal.device.url = deviceUrl;
|
|
3487
|
+
HashTreeParserStub = sinon
|
|
3488
|
+
.stub(HashTreeParserModule, 'default')
|
|
3489
|
+
.returns(createMockApiParser());
|
|
3490
|
+
});
|
|
3491
|
+
|
|
3492
|
+
it('should create a new parser and initialize it when no entry exists for the locusUrl', () => {
|
|
3493
|
+
// existing parser for a different url so hashTreeParsers.size > 0
|
|
3494
|
+
locusInfo.hashTreeParsers.set(locusUrlA, {parser: createMockApiParser(), initializedFromHashTree: true});
|
|
3495
|
+
|
|
3496
|
+
const locus = createLocusWithReplaces(locusUrlB, locusUrlA, '2026-01-01T00:00:00Z');
|
|
3497
|
+
sinon.stub(locusInfo, 'handleLocusDelta');
|
|
3498
|
+
|
|
3499
|
+
locusInfo.handleLocusAPIResponse(mockMeeting, {locus});
|
|
3500
|
+
|
|
3501
|
+
assert.isTrue(locusInfo.hashTreeParsers.has(locusUrlB));
|
|
3502
|
+
const newEntry = locusInfo.hashTreeParsers.get(locusUrlB);
|
|
3503
|
+
assert.isFalse(newEntry.initializedFromHashTree);
|
|
3504
|
+
|
|
3505
|
+
// the stub returns the mock, so initializeFromGetLociResponse should be called on it
|
|
3506
|
+
const createdParser = HashTreeParserStub.returnValues[0];
|
|
3507
|
+
assert.calledOnceWithExactly(createdParser.initializeFromGetLociResponse, locus);
|
|
3508
|
+
assert.notCalled(locusInfo.handleLocusDelta);
|
|
3509
|
+
});
|
|
3510
|
+
|
|
3511
|
+
it('should reactivate a stopped parser when replaces info is newer', () => {
|
|
3512
|
+
const parserA = createMockApiParser('stopped');
|
|
3513
|
+
const parserB = createMockApiParser('active');
|
|
3514
|
+
locusInfo.hashTreeParsers.set(locusUrlA, {parser: parserA, replacedAt: '2026-01-01T00:00:00Z', initializedFromHashTree: true});
|
|
3515
|
+
locusInfo.hashTreeParsers.set(locusUrlB, {parser: parserB, initializedFromHashTree: true});
|
|
3516
|
+
|
|
3517
|
+
const locus = createLocusWithReplaces(locusUrlA, locusUrlB, '2026-02-01T00:00:00Z');
|
|
3518
|
+
|
|
3519
|
+
locusInfo.handleLocusAPIResponse(mockMeeting, {locus});
|
|
3520
|
+
|
|
3521
|
+
assert.calledOnce(parserA.resumeFromApiResponse);
|
|
3522
|
+
assert.calledWithExactly(parserA.resumeFromApiResponse, locus);
|
|
3523
|
+
assert.calledOnce(parserB.stop);
|
|
3524
|
+
assert.equal(locusInfo.hashTreeParsers.get(locusUrlB).replacedAt, '2026-02-01T00:00:00Z');
|
|
3525
|
+
assert.isFalse(locusInfo.hashTreeParsers.get(locusUrlA).initializedFromHashTree);
|
|
3526
|
+
});
|
|
3527
|
+
|
|
3528
|
+
it('should not reactivate a stopped parser when replaces info is not newer', () => {
|
|
3529
|
+
const parserA = createMockApiParser('stopped');
|
|
3530
|
+
const parserB = createMockApiParser('active');
|
|
3531
|
+
locusInfo.hashTreeParsers.set(locusUrlA, {parser: parserA, replacedAt: '2026-03-01T00:00:00Z', initializedFromHashTree: true});
|
|
3532
|
+
locusInfo.hashTreeParsers.set(locusUrlB, {parser: parserB, initializedFromHashTree: true});
|
|
3533
|
+
|
|
3534
|
+
const locus = createLocusWithReplaces(locusUrlA, locusUrlB, '2026-01-01T00:00:00Z');
|
|
3535
|
+
|
|
3536
|
+
locusInfo.handleLocusAPIResponse(mockMeeting, {locus});
|
|
3537
|
+
|
|
3538
|
+
assert.notCalled(parserA.resumeFromApiResponse);
|
|
3539
|
+
assert.notCalled(parserB.stop);
|
|
3540
|
+
});
|
|
3541
|
+
|
|
3542
|
+
it('should not reactivate a stopped parser when no replaces info is available', () => {
|
|
3543
|
+
const parserA = createMockApiParser('stopped');
|
|
3544
|
+
locusInfo.hashTreeParsers.set(locusUrlA, {parser: parserA, initializedFromHashTree: true});
|
|
3545
|
+
|
|
3546
|
+
const locus = createLocusWithoutReplaces(locusUrlA);
|
|
3547
|
+
|
|
3548
|
+
locusInfo.handleLocusAPIResponse(mockMeeting, {locus});
|
|
3549
|
+
|
|
3550
|
+
assert.notCalled(parserA.resumeFromApiResponse);
|
|
3551
|
+
});
|
|
3552
|
+
});
|
|
3553
|
+
});
|
|
3554
|
+
|
|
3555
|
+
describe('#syncAllHashTreeDatasets', () => {
|
|
3556
|
+
it('should call syncAllDatasets on each parser that has an entry', async () => {
|
|
3557
|
+
const parser1 = {syncAllDatasets: sinon.stub().resolves()};
|
|
3558
|
+
const parser2 = {syncAllDatasets: sinon.stub().resolves()};
|
|
3559
|
+
locusInfo.hashTreeParsers.set('url1', {parser: parser1});
|
|
3560
|
+
locusInfo.hashTreeParsers.set('url2', {parser: parser2});
|
|
3561
|
+
|
|
3562
|
+
await locusInfo.syncAllHashTreeDatasets();
|
|
3563
|
+
|
|
3564
|
+
assert.calledOnce(parser1.syncAllDatasets);
|
|
3565
|
+
assert.calledOnce(parser2.syncAllDatasets);
|
|
3566
|
+
});
|
|
3567
|
+
|
|
3568
|
+
it('should skip parser entries without a parser object', async () => {
|
|
3569
|
+
const parser1 = {syncAllDatasets: sinon.stub().resolves()};
|
|
3570
|
+
locusInfo.hashTreeParsers.set('url1', {parser: parser1});
|
|
3571
|
+
locusInfo.hashTreeParsers.set('url2', {parser: undefined});
|
|
3572
|
+
|
|
3573
|
+
await locusInfo.syncAllHashTreeDatasets();
|
|
3574
|
+
|
|
3575
|
+
assert.calledOnce(parser1.syncAllDatasets);
|
|
3576
|
+
});
|
|
3577
|
+
|
|
3578
|
+
it('should await each parsers syncAllDatasets sequentially', async () => {
|
|
3579
|
+
const callOrder = [];
|
|
3580
|
+
const parser1 = {syncAllDatasets: sinon.stub().callsFake(() => {
|
|
3581
|
+
callOrder.push('start1');
|
|
3582
|
+
return new Promise((resolve) => {
|
|
3583
|
+
setTimeout(() => {
|
|
3584
|
+
callOrder.push('end1');
|
|
3585
|
+
resolve();
|
|
3586
|
+
}, 100);
|
|
3587
|
+
});
|
|
3588
|
+
})};
|
|
3589
|
+
const parser2 = {syncAllDatasets: sinon.stub().callsFake(() => {
|
|
3590
|
+
callOrder.push('start2');
|
|
3591
|
+
return Promise.resolve();
|
|
3592
|
+
})};
|
|
3593
|
+
locusInfo.hashTreeParsers.set('url1', {parser: parser1});
|
|
3594
|
+
locusInfo.hashTreeParsers.set('url2', {parser: parser2});
|
|
3595
|
+
|
|
3596
|
+
const clock = sinon.useFakeTimers();
|
|
3597
|
+
const promise = locusInfo.syncAllHashTreeDatasets();
|
|
3598
|
+
// parser1 started but parser2 not yet
|
|
3599
|
+
assert.deepEqual(callOrder, ['start1']);
|
|
3600
|
+
|
|
3601
|
+
await clock.tickAsync(100);
|
|
3602
|
+
await promise;
|
|
3603
|
+
// parser1 finished, then parser2 started and finished
|
|
3604
|
+
assert.deepEqual(callOrder, ['start1', 'end1', 'start2']);
|
|
3605
|
+
clock.restore();
|
|
3606
|
+
});
|
|
3434
3607
|
});
|
|
3435
3608
|
|
|
3436
3609
|
describe('#LocusDeltaEvents', () => {
|
|
@@ -3521,49 +3694,23 @@ describe('plugin-meetings', () => {
|
|
|
3521
3694
|
assert.deepEqual(callOrder, ['updateLocusUrl', 'updateMeetingInfo']);
|
|
3522
3695
|
});
|
|
3523
3696
|
|
|
3524
|
-
it('#updateLocusInfo ignores
|
|
3525
|
-
const newLocus = {
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
state: 'LEFT',
|
|
3529
|
-
},
|
|
3530
|
-
};
|
|
3697
|
+
it('#updateLocusInfo ignores locus when isSelfMovedOrBreakoutEnded returns true', () => {
|
|
3698
|
+
const newLocus = {self: {state: 'JOINED'}};
|
|
3699
|
+
|
|
3700
|
+
sinon.stub(MeetingsUtil, 'isSelfMovedOrBreakoutEnded').returns(true);
|
|
3531
3701
|
|
|
3532
3702
|
locusInfo.updateControls = sinon.stub();
|
|
3533
|
-
locusInfo.updateConversationUrl = sinon.stub();
|
|
3534
|
-
locusInfo.updateCreated = sinon.stub();
|
|
3535
3703
|
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
3704
|
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
3705
|
|
|
3549
3706
|
locusInfo.updateLocusInfo(newLocus);
|
|
3550
3707
|
|
|
3708
|
+
assert.calledOnceWithExactly(MeetingsUtil.isSelfMovedOrBreakoutEnded, newLocus);
|
|
3551
3709
|
assert.notCalled(locusInfo.updateControls);
|
|
3552
|
-
assert.notCalled(locusInfo.updateConversationUrl);
|
|
3553
|
-
assert.notCalled(locusInfo.updateCreated);
|
|
3554
3710
|
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
3711
|
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);
|
|
3712
|
+
|
|
3713
|
+
MeetingsUtil.isSelfMovedOrBreakoutEnded.restore();
|
|
3567
3714
|
});
|
|
3568
3715
|
|
|
3569
3716
|
it('#updateLocusInfo puts the Locus DTO top level properties at the right place in LocusInfo class', () => {
|
|
@@ -4413,6 +4560,31 @@ describe('plugin-meetings', () => {
|
|
|
4413
4560
|
});
|
|
4414
4561
|
});
|
|
4415
4562
|
|
|
4563
|
+
describe('#cleanUp', () => {
|
|
4564
|
+
it('calls cleanUp on all hash tree parsers and clears maps', () => {
|
|
4565
|
+
const parser1 = {cleanUp: sinon.stub()};
|
|
4566
|
+
const parser2 = {cleanUp: sinon.stub()};
|
|
4567
|
+
|
|
4568
|
+
locusInfo.hashTreeParsers.set('url1', {parser: parser1, initializedFromHashTree: true});
|
|
4569
|
+
locusInfo.hashTreeParsers.set('url2', {parser: parser2, initializedFromHashTree: true});
|
|
4570
|
+
locusInfo.hashTreeObjectId2ParticipantId.set(1, 'participant1');
|
|
4571
|
+
|
|
4572
|
+
locusInfo.cleanUp();
|
|
4573
|
+
|
|
4574
|
+
assert.calledOnce(parser1.cleanUp);
|
|
4575
|
+
assert.calledOnce(parser2.cleanUp);
|
|
4576
|
+
assert.equal(locusInfo.hashTreeParsers.size, 0);
|
|
4577
|
+
assert.equal(locusInfo.hashTreeObjectId2ParticipantId.size, 0);
|
|
4578
|
+
});
|
|
4579
|
+
|
|
4580
|
+
it('works when there are no hash tree parsers', () => {
|
|
4581
|
+
locusInfo.cleanUp();
|
|
4582
|
+
|
|
4583
|
+
assert.equal(locusInfo.hashTreeParsers.size, 0);
|
|
4584
|
+
assert.equal(locusInfo.hashTreeObjectId2ParticipantId.size, 0);
|
|
4585
|
+
});
|
|
4586
|
+
});
|
|
4587
|
+
|
|
4416
4588
|
describe('#handleOneonOneEvent', () => {
|
|
4417
4589
|
beforeEach(() => {
|
|
4418
4590
|
locusInfo.emitScoped = sinon.stub();
|
|
@@ -4954,6 +5126,31 @@ describe('plugin-meetings', () => {
|
|
|
4954
5126
|
);
|
|
4955
5127
|
assert.notCalled(getTheLocusToUpdateStub);
|
|
4956
5128
|
});
|
|
5129
|
+
|
|
5130
|
+
it('should call handleLocusAPIResponse for SDK_LOCUS_FROM_SYNC_MEETINGS when hash tree parsers exist', () => {
|
|
5131
|
+
const fakeLocusUrl = 'http://locus-url.com';
|
|
5132
|
+
const fakeLocus = {url: fakeLocusUrl, fullState: {state: 'ACTIVE'}};
|
|
5133
|
+
const mockHashTreeParser = {
|
|
5134
|
+
handleMessage: sinon.stub(),
|
|
5135
|
+
handleLocusUpdate: sinon.stub(),
|
|
5136
|
+
};
|
|
5137
|
+
locusInfo.hashTreeParsers.set(fakeLocusUrl, {
|
|
5138
|
+
parser: mockHashTreeParser,
|
|
5139
|
+
initializedFromHashTree: true,
|
|
5140
|
+
});
|
|
5141
|
+
|
|
5142
|
+
sinon.stub(locusInfo, 'handleLocusDelta');
|
|
5143
|
+
|
|
5144
|
+
locusInfo.parse(mockMeeting, {
|
|
5145
|
+
eventType: LOCUSEVENT.SDK_LOCUS_FROM_SYNC_MEETINGS,
|
|
5146
|
+
locus: fakeLocus,
|
|
5147
|
+
});
|
|
5148
|
+
|
|
5149
|
+
// should route through handleLocusAPIResponse which passes unwrapped LocusDTO to parser
|
|
5150
|
+
assert.calledOnce(mockHashTreeParser.handleLocusUpdate);
|
|
5151
|
+
assert.notCalled(mockHashTreeParser.handleMessage);
|
|
5152
|
+
assert.notCalled(locusInfo.handleLocusDelta);
|
|
5153
|
+
});
|
|
4957
5154
|
});
|
|
4958
5155
|
});
|
|
4959
5156
|
|
|
@@ -5146,6 +5343,7 @@ describe('plugin-meetings', () => {
|
|
|
5146
5343
|
return {
|
|
5147
5344
|
htMeta: {elementId: {type: 'Self'}},
|
|
5148
5345
|
data: {
|
|
5346
|
+
deviceUrl,
|
|
5149
5347
|
devices,
|
|
5150
5348
|
},
|
|
5151
5349
|
};
|