@webex/plugin-meetings 3.10.0 → 3.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/annotation/annotation.types.js.map +1 -1
- package/dist/annotation/constants.js.map +1 -1
- package/dist/annotation/index.js +19 -22
- package/dist/annotation/index.js.map +1 -1
- package/dist/breakouts/breakout.js +6 -6
- package/dist/breakouts/breakout.js.map +1 -1
- package/dist/breakouts/collection.js.map +1 -1
- package/dist/breakouts/edit-lock-error.js +9 -11
- package/dist/breakouts/edit-lock-error.js.map +1 -1
- package/dist/breakouts/events.js.map +1 -1
- package/dist/breakouts/index.js +126 -127
- package/dist/breakouts/index.js.map +1 -1
- package/dist/breakouts/request.js +6 -8
- package/dist/breakouts/request.js.map +1 -1
- package/dist/breakouts/utils.js.map +1 -1
- package/dist/common/browser-detection.js.map +1 -1
- package/dist/common/collection.js +1 -2
- package/dist/common/collection.js.map +1 -1
- package/dist/common/config.js.map +1 -1
- package/dist/common/errors/captcha-error.js +9 -11
- package/dist/common/errors/captcha-error.js.map +1 -1
- package/dist/common/errors/intent-to-join.js +10 -12
- package/dist/common/errors/intent-to-join.js.map +1 -1
- package/dist/common/errors/join-forbidden-error.js +10 -12
- package/dist/common/errors/join-forbidden-error.js.map +1 -1
- package/dist/common/errors/join-meeting.js +10 -12
- package/dist/common/errors/join-meeting.js.map +1 -1
- package/dist/common/errors/join-webinar-error.js +9 -11
- package/dist/common/errors/join-webinar-error.js.map +1 -1
- package/dist/common/errors/media.js +9 -11
- package/dist/common/errors/media.js.map +1 -1
- package/dist/common/errors/multistream-not-supported-error.js +9 -11
- package/dist/common/errors/multistream-not-supported-error.js.map +1 -1
- package/dist/common/errors/no-meeting-info.js +9 -11
- package/dist/common/errors/no-meeting-info.js.map +1 -1
- package/dist/common/errors/parameter.js +11 -14
- package/dist/common/errors/parameter.js.map +1 -1
- package/dist/common/errors/password-error.js +9 -11
- package/dist/common/errors/password-error.js.map +1 -1
- package/dist/common/errors/permission.js +9 -11
- package/dist/common/errors/permission.js.map +1 -1
- package/dist/common/errors/reclaim-host-role-errors.js +32 -38
- package/dist/common/errors/reclaim-host-role-errors.js.map +1 -1
- package/dist/common/errors/reconnection-not-started.js +5 -6
- package/dist/common/errors/reconnection-not-started.js.map +1 -1
- package/dist/common/errors/reconnection.js +9 -11
- package/dist/common/errors/reconnection.js.map +1 -1
- package/dist/common/errors/stats.js +9 -11
- package/dist/common/errors/stats.js.map +1 -1
- package/dist/common/errors/webex-errors.js +38 -27
- package/dist/common/errors/webex-errors.js.map +1 -1
- package/dist/common/errors/webex-meetings-error.js +9 -12
- package/dist/common/errors/webex-meetings-error.js.map +1 -1
- package/dist/common/events/events-scope.js +9 -10
- package/dist/common/events/events-scope.js.map +1 -1
- package/dist/common/events/events.js +9 -10
- package/dist/common/events/events.js.map +1 -1
- package/dist/common/events/trigger-proxy.js.map +1 -1
- package/dist/common/events/util.js.map +1 -1
- package/dist/common/logs/logger-config.js.map +1 -1
- package/dist/common/logs/logger-proxy.js.map +1 -1
- package/dist/common/logs/request.js +17 -17
- package/dist/common/logs/request.js.map +1 -1
- package/dist/common/queue.js +1 -2
- package/dist/common/queue.js.map +1 -1
- package/dist/config.js +2 -2
- package/dist/config.js.map +1 -1
- package/dist/constants.js +13 -8
- package/dist/constants.js.map +1 -1
- package/dist/controls-options-manager/constants.js.map +1 -1
- package/dist/controls-options-manager/enums.js.map +1 -1
- package/dist/controls-options-manager/index.js +1 -2
- package/dist/controls-options-manager/index.js.map +1 -1
- package/dist/controls-options-manager/types.js.map +1 -1
- package/dist/controls-options-manager/util.js +1 -2
- package/dist/controls-options-manager/util.js.map +1 -1
- package/dist/hashTree/constants.js +20 -0
- package/dist/hashTree/constants.js.map +1 -0
- package/dist/hashTree/hashTree.js +515 -0
- package/dist/hashTree/hashTree.js.map +1 -0
- package/dist/hashTree/hashTreeParser.js +1250 -0
- package/dist/hashTree/hashTreeParser.js.map +1 -0
- package/dist/hashTree/types.js +23 -0
- package/dist/hashTree/types.js.map +1 -0
- package/dist/hashTree/utils.js +59 -0
- package/dist/hashTree/utils.js.map +1 -0
- package/dist/index.js +8 -2
- package/dist/index.js.map +1 -1
- package/dist/interceptors/index.js.map +1 -1
- package/dist/interceptors/locusRetry.js +6 -8
- package/dist/interceptors/locusRetry.js.map +1 -1
- package/dist/interceptors/locusRouteToken.js +33 -13
- package/dist/interceptors/locusRouteToken.js.map +1 -1
- package/dist/interpretation/collection.js.map +1 -1
- package/dist/interpretation/index.js +1 -2
- package/dist/interpretation/index.js.map +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/interpretation/siLanguage.js.map +1 -1
- package/dist/locus-info/controlsUtils.js +5 -3
- package/dist/locus-info/controlsUtils.js.map +1 -1
- package/dist/locus-info/embeddedAppsUtils.js.map +1 -1
- package/dist/locus-info/fullState.js.map +1 -1
- package/dist/locus-info/hostUtils.js.map +1 -1
- package/dist/locus-info/index.js +619 -177
- package/dist/locus-info/index.js.map +1 -1
- package/dist/locus-info/infoUtils.js.map +1 -1
- package/dist/locus-info/mediaSharesUtils.js.map +1 -1
- package/dist/locus-info/parser.js +3 -4
- package/dist/locus-info/parser.js.map +1 -1
- package/dist/locus-info/selfUtils.js.map +1 -1
- package/dist/locus-info/types.js +7 -0
- package/dist/locus-info/types.js.map +1 -0
- package/dist/media/MediaConnectionAwaiter.js +1 -2
- package/dist/media/MediaConnectionAwaiter.js.map +1 -1
- package/dist/media/index.js +5 -2
- package/dist/media/index.js.map +1 -1
- package/dist/media/properties.js +15 -17
- package/dist/media/properties.js.map +1 -1
- package/dist/media/util.js.map +1 -1
- package/dist/meeting/brbState.js +8 -9
- package/dist/meeting/brbState.js.map +1 -1
- package/dist/meeting/connectionStateHandler.js +10 -13
- package/dist/meeting/connectionStateHandler.js.map +1 -1
- package/dist/meeting/in-meeting-actions.js.map +1 -1
- package/dist/meeting/index.js +1632 -1535
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/locusMediaRequest.js +13 -17
- package/dist/meeting/locusMediaRequest.js.map +1 -1
- package/dist/meeting/muteState.js +11 -12
- package/dist/meeting/muteState.js.map +1 -1
- package/dist/meeting/request.js +101 -104
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/request.type.js.map +1 -1
- package/dist/meeting/state.js.map +1 -1
- package/dist/meeting/type.js.map +1 -1
- package/dist/meeting/util.js +24 -23
- package/dist/meeting/util.js.map +1 -1
- package/dist/meeting/voicea-meeting.js +3 -3
- package/dist/meeting/voicea-meeting.js.map +1 -1
- package/dist/meeting-info/collection.js +7 -10
- package/dist/meeting-info/collection.js.map +1 -1
- package/dist/meeting-info/index.js +1 -2
- package/dist/meeting-info/index.js.map +1 -1
- package/dist/meeting-info/meeting-info-v2.js +135 -146
- package/dist/meeting-info/meeting-info-v2.js.map +1 -1
- package/dist/meeting-info/request.js +1 -2
- package/dist/meeting-info/request.js.map +1 -1
- package/dist/meeting-info/util.js +36 -37
- package/dist/meeting-info/util.js.map +1 -1
- package/dist/meeting-info/utilv2.js +30 -31
- package/dist/meeting-info/utilv2.js.map +1 -1
- package/dist/meetings/collection.js +6 -8
- package/dist/meetings/collection.js.map +1 -1
- package/dist/meetings/index.js +200 -148
- package/dist/meetings/index.js.map +1 -1
- package/dist/meetings/meetings.types.js.map +1 -1
- package/dist/meetings/request.js +6 -8
- package/dist/meetings/request.js.map +1 -1
- package/dist/meetings/util.js +36 -30
- package/dist/meetings/util.js.map +1 -1
- package/dist/member/index.js +1 -2
- package/dist/member/index.js.map +1 -1
- package/dist/member/types.js +6 -3
- package/dist/member/types.js.map +1 -1
- package/dist/member/util.js.map +1 -1
- package/dist/members/collection.js +1 -2
- package/dist/members/collection.js.map +1 -1
- package/dist/members/index.js +18 -21
- package/dist/members/index.js.map +1 -1
- package/dist/members/request.js +8 -11
- package/dist/members/request.js.map +1 -1
- package/dist/members/types.js.map +1 -1
- package/dist/members/util.js.map +1 -1
- package/dist/metrics/constants.js +4 -1
- package/dist/metrics/constants.js.map +1 -1
- package/dist/metrics/index.js +3 -4
- package/dist/metrics/index.js.map +1 -1
- package/dist/multistream/mediaRequestManager.js +1 -2
- package/dist/multistream/mediaRequestManager.js.map +1 -1
- package/dist/multistream/receiveSlot.js +34 -45
- package/dist/multistream/receiveSlot.js.map +1 -1
- package/dist/multistream/receiveSlotManager.js +8 -9
- package/dist/multistream/receiveSlotManager.js.map +1 -1
- package/dist/multistream/remoteMedia.js +12 -15
- package/dist/multistream/remoteMedia.js.map +1 -1
- package/dist/multistream/remoteMediaGroup.js +1 -2
- package/dist/multistream/remoteMediaGroup.js.map +1 -1
- package/dist/multistream/remoteMediaManager.js +122 -123
- package/dist/multistream/remoteMediaManager.js.map +1 -1
- package/dist/multistream/sendSlotManager.js +29 -30
- package/dist/multistream/sendSlotManager.js.map +1 -1
- package/dist/personal-meeting-room/index.js +16 -19
- package/dist/personal-meeting-room/index.js.map +1 -1
- package/dist/personal-meeting-room/request.js +7 -10
- package/dist/personal-meeting-room/request.js.map +1 -1
- package/dist/personal-meeting-room/util.js.map +1 -1
- package/dist/reachability/clusterReachability.js +188 -352
- package/dist/reachability/clusterReachability.js.map +1 -1
- package/dist/reachability/index.js +206 -206
- package/dist/reachability/index.js.map +1 -1
- package/dist/reachability/reachability.types.js +14 -1
- package/dist/reachability/reachability.types.js.map +1 -1
- package/dist/reachability/reachabilityPeerConnection.js +445 -0
- package/dist/reachability/reachabilityPeerConnection.js.map +1 -0
- package/dist/reachability/request.js.map +1 -1
- package/dist/reachability/util.js.map +1 -1
- package/dist/reactions/constants.js.map +1 -1
- package/dist/reactions/reactions.js.map +1 -1
- package/dist/reactions/reactions.type.js.map +1 -1
- package/dist/reconnection-manager/index.js +178 -176
- package/dist/reconnection-manager/index.js.map +1 -1
- package/dist/recording-controller/enums.js.map +1 -1
- package/dist/recording-controller/index.js +1 -2
- package/dist/recording-controller/index.js.map +1 -1
- package/dist/recording-controller/util.js.map +1 -1
- package/dist/roap/index.js +12 -15
- package/dist/roap/index.js.map +1 -1
- package/dist/roap/request.js +24 -26
- package/dist/roap/request.js.map +1 -1
- package/dist/roap/turnDiscovery.js +75 -76
- package/dist/roap/turnDiscovery.js.map +1 -1
- package/dist/roap/types.js.map +1 -1
- package/dist/transcription/index.js +4 -5
- package/dist/transcription/index.js.map +1 -1
- package/dist/types/common/errors/webex-errors.d.ts +12 -0
- package/dist/types/config.d.ts +1 -0
- package/dist/types/constants.d.ts +27 -21
- package/dist/types/hashTree/constants.d.ts +8 -0
- package/dist/types/hashTree/hashTree.d.ts +129 -0
- package/dist/types/hashTree/hashTreeParser.d.ts +250 -0
- package/dist/types/hashTree/types.d.ts +33 -0
- package/dist/types/hashTree/utils.d.ts +16 -0
- package/dist/types/index.d.ts +2 -1
- package/dist/types/interceptors/locusRouteToken.d.ts +2 -0
- package/dist/types/locus-info/index.d.ts +98 -80
- package/dist/types/locus-info/types.d.ts +54 -0
- package/dist/types/meeting/index.d.ts +35 -9
- package/dist/types/meetings/index.d.ts +9 -2
- package/dist/types/metrics/constants.d.ts +3 -0
- package/dist/types/reachability/clusterReachability.d.ts +33 -84
- package/dist/types/reachability/reachability.types.d.ts +12 -1
- package/dist/types/reachability/reachabilityPeerConnection.d.ts +111 -0
- package/dist/webinar/collection.js +1 -2
- package/dist/webinar/collection.js.map +1 -1
- package/dist/webinar/index.js +148 -158
- package/dist/webinar/index.js.map +1 -1
- package/package.json +24 -23
- package/src/common/errors/webex-errors.ts +19 -0
- package/src/config.ts +1 -0
- package/src/constants.ts +15 -2
- package/src/hashTree/constants.ts +9 -0
- package/src/hashTree/hashTree.ts +463 -0
- package/src/hashTree/hashTreeParser.ts +1143 -0
- package/src/hashTree/types.ts +39 -0
- package/src/hashTree/utils.ts +53 -0
- package/src/index.ts +2 -0
- package/src/interceptors/locusRouteToken.ts +22 -5
- package/src/locus-info/controlsUtils.ts +6 -0
- package/src/locus-info/index.ts +641 -164
- package/src/locus-info/types.ts +53 -0
- package/src/media/index.ts +6 -0
- package/src/meeting/index.ts +137 -28
- package/src/meeting/util.ts +1 -0
- package/src/meetings/index.ts +119 -59
- package/src/meetings/util.ts +10 -9
- package/src/metrics/constants.ts +3 -0
- package/src/reachability/clusterReachability.ts +159 -330
- package/src/reachability/index.ts +6 -1
- package/src/reachability/reachability.types.ts +15 -1
- package/src/reachability/reachabilityPeerConnection.ts +418 -0
- package/test/unit/spec/hashTree/hashTree.ts +655 -0
- package/test/unit/spec/hashTree/hashTreeParser.ts +1524 -0
- package/test/unit/spec/hashTree/utils.ts +140 -0
- package/test/unit/spec/interceptors/locusRouteToken.ts +44 -0
- package/test/unit/spec/locus-info/controlsUtils.js +27 -1
- package/test/unit/spec/locus-info/index.js +879 -16
- package/test/unit/spec/media/index.ts +140 -9
- package/test/unit/spec/meeting/index.js +299 -94
- package/test/unit/spec/meeting/utils.js +78 -1
- package/test/unit/spec/meetings/index.js +263 -29
- package/test/unit/spec/meetings/utils.js +51 -1
- package/test/unit/spec/reachability/clusterReachability.ts +404 -137
- package/test/unit/spec/reachability/index.ts +3 -3
|
@@ -12,6 +12,7 @@ import EmbeddedAppsUtils from '@webex/plugin-meetings/src/locus-info/embeddedApp
|
|
|
12
12
|
import MediaSharesUtils from '@webex/plugin-meetings/src/locus-info//mediaSharesUtils';
|
|
13
13
|
import LocusDeltaParser from '@webex/plugin-meetings/src/locus-info/parser';
|
|
14
14
|
import Metrics from '@webex/plugin-meetings/src/metrics';
|
|
15
|
+
import * as HashTreeParserModule from '@webex/plugin-meetings/src/hashTree/hashTreeParser';
|
|
15
16
|
|
|
16
17
|
import {
|
|
17
18
|
LOCUSINFO,
|
|
@@ -28,6 +29,7 @@ import {
|
|
|
28
29
|
} from '../../../../src/constants';
|
|
29
30
|
|
|
30
31
|
import {self, selfWithInactivity} from './selfConstant';
|
|
32
|
+
import { MEETING_REMOVED_REASON } from '@webex/plugin-meetings/src/constants';
|
|
31
33
|
|
|
32
34
|
describe('plugin-meetings', () => {
|
|
33
35
|
describe('LocusInfo index', () => {
|
|
@@ -77,6 +79,771 @@ describe('plugin-meetings', () => {
|
|
|
77
79
|
sinon.restore();
|
|
78
80
|
});
|
|
79
81
|
|
|
82
|
+
describe('#initialSetup', () => {
|
|
83
|
+
let HashTreeParserStub;
|
|
84
|
+
let mockHashTreeParser;
|
|
85
|
+
let updateLocusCacheStub;
|
|
86
|
+
let updateLocusInfoStub;
|
|
87
|
+
let isNewFullLocusStub;
|
|
88
|
+
|
|
89
|
+
beforeEach(() => {
|
|
90
|
+
mockHashTreeParser = {
|
|
91
|
+
initializeFromMessage: sinon.stub().resolves(),
|
|
92
|
+
initializeFromGetLociResponse: sinon.stub().resolves(),
|
|
93
|
+
};
|
|
94
|
+
HashTreeParserStub = sinon
|
|
95
|
+
.stub(HashTreeParserModule, 'default')
|
|
96
|
+
.returns(mockHashTreeParser);
|
|
97
|
+
updateLocusCacheStub = sinon.stub(locusInfo, 'updateLocusCache');
|
|
98
|
+
updateLocusInfoStub = sinon.stub(locusInfo, 'updateLocusInfo');
|
|
99
|
+
isNewFullLocusStub = sinon.stub(locusInfo.locusParser, 'isNewFullLocus').returns(true);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
afterEach(() => {
|
|
103
|
+
sinon.restore();
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
const createHashTreeMessage = (visibleDataSets) => ({
|
|
107
|
+
locusStateElements: [
|
|
108
|
+
{
|
|
109
|
+
htMeta: {elementId: {type: 'self'}},
|
|
110
|
+
data: {visibleDataSets},
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
dataSets: [{name: 'dataset1', url: 'test-url'}],
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
const createLocusWithVisibleDataSets = (visibleDataSets) => ({
|
|
117
|
+
self: {visibleDataSets},
|
|
118
|
+
participants: [],
|
|
119
|
+
links: {
|
|
120
|
+
resources: {
|
|
121
|
+
visibleDataSets: {url: 'http://visible-datasets-url.com'},
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('should initialize the hash tree parser when triggered from a hash tree locus message', async () => {
|
|
127
|
+
const visibleDataSets = ['dataset1', 'dataset2'];
|
|
128
|
+
const hashTreeMessage = createHashTreeMessage(visibleDataSets);
|
|
129
|
+
|
|
130
|
+
await locusInfo.initialSetup({
|
|
131
|
+
trigger: 'locus-message',
|
|
132
|
+
hashTreeMessage,
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
assert.calledOnceWithExactly(
|
|
136
|
+
HashTreeParserStub,
|
|
137
|
+
sinon.match({
|
|
138
|
+
initialLocus: {
|
|
139
|
+
locus: {self: {visibleDataSets}},
|
|
140
|
+
dataSets: [],
|
|
141
|
+
},
|
|
142
|
+
webexRequest: sinon.match.func,
|
|
143
|
+
locusInfoUpdateCallback: sinon.match.func,
|
|
144
|
+
debugId: sinon.match.string,
|
|
145
|
+
})
|
|
146
|
+
);
|
|
147
|
+
assert.calledOnceWithExactly(mockHashTreeParser.initializeFromMessage, hashTreeMessage);
|
|
148
|
+
assert.notCalled(updateLocusCacheStub);
|
|
149
|
+
assert.notCalled(updateLocusInfoStub);
|
|
150
|
+
assert.isTrue(locusInfo.emitChange);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('should not initialize the hash tree when triggered from a non-hash tree locus message', async () => {
|
|
154
|
+
const locus = {url: 'http://locus-url.com', participants: []};
|
|
155
|
+
|
|
156
|
+
await locusInfo.initialSetup({
|
|
157
|
+
trigger: 'locus-message',
|
|
158
|
+
locus,
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
assert.notCalled(HashTreeParserStub);
|
|
162
|
+
assert.notCalled(mockHashTreeParser.initializeFromMessage);
|
|
163
|
+
assert.calledOnceWithExactly(updateLocusCacheStub, locus);
|
|
164
|
+
assert.calledOnce(updateLocusInfoStub);
|
|
165
|
+
assert.isTrue(locusInfo.emitChange);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('should initialize the hash tree parser correctly when triggered from a join response containing datasets', async () => {
|
|
169
|
+
const visibleDataSets = ['dataset1', 'dataset2'];
|
|
170
|
+
const locus = createLocusWithVisibleDataSets(visibleDataSets);
|
|
171
|
+
const dataSets = [{name: 'dataset1', url: 'http://dataset-url.com'}];
|
|
172
|
+
|
|
173
|
+
await locusInfo.initialSetup({
|
|
174
|
+
trigger: 'join-response',
|
|
175
|
+
locus,
|
|
176
|
+
dataSets,
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
assert.calledOnceWithExactly(
|
|
180
|
+
HashTreeParserStub,
|
|
181
|
+
sinon.match({
|
|
182
|
+
initialLocus: {
|
|
183
|
+
locus,
|
|
184
|
+
dataSets,
|
|
185
|
+
},
|
|
186
|
+
webexRequest: sinon.match.func,
|
|
187
|
+
locusInfoUpdateCallback: sinon.match.func,
|
|
188
|
+
debugId: sinon.match.string,
|
|
189
|
+
})
|
|
190
|
+
);
|
|
191
|
+
assert.calledOnceWithExactly(updateLocusCacheStub, locus);
|
|
192
|
+
assert.calledOnce(updateLocusInfoStub);
|
|
193
|
+
assert.isTrue(locusInfo.emitChange);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('should do normal (classic) initialization when triggered from a join response without datasets', async () => {
|
|
197
|
+
const locus = {url: 'http://locus-url.com', participants: []};
|
|
198
|
+
|
|
199
|
+
await locusInfo.initialSetup({
|
|
200
|
+
trigger: 'join-response',
|
|
201
|
+
locus,
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
assert.notCalled(HashTreeParserStub);
|
|
205
|
+
assert.calledOnceWithExactly(updateLocusCacheStub, locus);
|
|
206
|
+
assert.calledOnce(updateLocusInfoStub);
|
|
207
|
+
assert.isTrue(locusInfo.emitChange);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it('should initialize the hash tree parser correctly when triggered from a get loci response containing visible datasets', async () => {
|
|
211
|
+
const visibleDataSets = ['dataset1', 'dataset2'];
|
|
212
|
+
const locus = createLocusWithVisibleDataSets(visibleDataSets);
|
|
213
|
+
|
|
214
|
+
await locusInfo.initialSetup({
|
|
215
|
+
trigger: 'get-loci-response',
|
|
216
|
+
locus,
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
assert.calledOnceWithExactly(
|
|
220
|
+
HashTreeParserStub,
|
|
221
|
+
sinon.match({
|
|
222
|
+
initialLocus: {
|
|
223
|
+
locus: {self: {visibleDataSets}},
|
|
224
|
+
dataSets: [],
|
|
225
|
+
},
|
|
226
|
+
webexRequest: sinon.match.func,
|
|
227
|
+
locusInfoUpdateCallback: sinon.match.func,
|
|
228
|
+
debugId: sinon.match.string,
|
|
229
|
+
})
|
|
230
|
+
);
|
|
231
|
+
assert.calledOnceWithExactly(mockHashTreeParser.initializeFromGetLociResponse, locus);
|
|
232
|
+
assert.notCalled(updateLocusCacheStub);
|
|
233
|
+
assert.notCalled(updateLocusInfoStub);
|
|
234
|
+
assert.isTrue(locusInfo.emitChange);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it('should do normal (classic) initialization when triggered from a get loci response without visible datasets', async () => {
|
|
238
|
+
const locus = {url: 'http://locus-url.com', participants: []};
|
|
239
|
+
|
|
240
|
+
await locusInfo.initialSetup({
|
|
241
|
+
trigger: 'get-loci-response',
|
|
242
|
+
locus,
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
assert.notCalled(HashTreeParserStub);
|
|
246
|
+
assert.notCalled(mockHashTreeParser.initializeFromGetLociResponse);
|
|
247
|
+
assert.calledOnceWithExactly(updateLocusCacheStub, locus);
|
|
248
|
+
assert.calledOnce(updateLocusInfoStub);
|
|
249
|
+
assert.isTrue(locusInfo.emitChange);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
describe('should setup correct locusInfoUpdateCallback when creating HashTreeParser', () => {
|
|
253
|
+
const OBJECTS_UPDATED = HashTreeParserModule.LocusInfoUpdateType.OBJECTS_UPDATED;
|
|
254
|
+
const MEETING_ENDED = HashTreeParserModule.LocusInfoUpdateType.MEETING_ENDED;
|
|
255
|
+
|
|
256
|
+
let locusInfoUpdateCallback;
|
|
257
|
+
let onDeltaLocusStub;
|
|
258
|
+
let expectedLocusInfo;
|
|
259
|
+
|
|
260
|
+
beforeEach(async () => {
|
|
261
|
+
onDeltaLocusStub = sinon.stub(locusInfo, 'onDeltaLocus');
|
|
262
|
+
|
|
263
|
+
await locusInfo.initialSetup({
|
|
264
|
+
trigger: 'locus-message',
|
|
265
|
+
hashTreeMessage: {
|
|
266
|
+
locusStateElements: [
|
|
267
|
+
{
|
|
268
|
+
htMeta: {elementId: {type: 'self'}},
|
|
269
|
+
data: {visibleDataSets: ['dataset1']},
|
|
270
|
+
},
|
|
271
|
+
],
|
|
272
|
+
dataSets: [{name: 'dataset1', url: 'test-url'}],
|
|
273
|
+
},
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
locusInfoUpdateCallback = HashTreeParserStub.firstCall.args[0].locusInfoUpdateCallback;
|
|
277
|
+
|
|
278
|
+
assert.isDefined(locusInfoUpdateCallback);
|
|
279
|
+
|
|
280
|
+
// setup fake initial locusInfo state
|
|
281
|
+
locusInfo.controls = {id: 'fake-controls'};
|
|
282
|
+
locusInfo.fullState = {id: 'fake-full-state'};
|
|
283
|
+
locusInfo.host = {id: 'fake-host'};
|
|
284
|
+
locusInfo.info = {id: 'fake-info'};
|
|
285
|
+
locusInfo.links = {id: 'fake-links'};
|
|
286
|
+
locusInfo.mediaShares = [
|
|
287
|
+
{
|
|
288
|
+
id: 'fake-media-share-1',
|
|
289
|
+
htMeta: {elementId: {type: 'mediashare', id: 'fake-ht-mediaShare-1', version: 1}},
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
id: 'fake-media-share-2',
|
|
293
|
+
htMeta: {elementId: {type: 'mediashare', id: 'fake-ht-mediaShare-2', version: 1}},
|
|
294
|
+
},
|
|
295
|
+
];
|
|
296
|
+
locusInfo.meetings = {id: 'fake-meetings'};
|
|
297
|
+
locusInfo.participants = [
|
|
298
|
+
{id: 'fake-participant-1', name: 'Participant One'},
|
|
299
|
+
{id: 'fake-participant-2', name: 'Participant Two'},
|
|
300
|
+
];
|
|
301
|
+
locusInfo.hashTreeObjectId2ParticipantId.set(
|
|
302
|
+
'fake-ht-participant-1',
|
|
303
|
+
'fake-participant-1'
|
|
304
|
+
);
|
|
305
|
+
locusInfo.hashTreeObjectId2ParticipantId.set(
|
|
306
|
+
'fake-ht-participant-2',
|
|
307
|
+
'fake-participant-2'
|
|
308
|
+
);
|
|
309
|
+
locusInfo.replaces = {id: 'fake-replaces'};
|
|
310
|
+
locusInfo.self = {id: 'fake-self'};
|
|
311
|
+
locusInfo.url = 'fake-locus-url';
|
|
312
|
+
locusInfo.htMeta = {elementId: {type: 'locus', id: 'fake-ht-locus-id', version: 1}};
|
|
313
|
+
|
|
314
|
+
// setup the default expected locus info state that each test builds upon
|
|
315
|
+
expectedLocusInfo = {
|
|
316
|
+
controls: {id: 'fake-controls'},
|
|
317
|
+
fullState: {id: 'fake-full-state'},
|
|
318
|
+
host: {id: 'fake-host'},
|
|
319
|
+
info: {id: 'fake-info'},
|
|
320
|
+
links: {id: 'fake-links'},
|
|
321
|
+
mediaShares: [
|
|
322
|
+
{
|
|
323
|
+
id: 'fake-media-share-1',
|
|
324
|
+
htMeta: {elementId: {type: 'mediashare', id: 'fake-ht-mediaShare-1', version: 1}},
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
id: 'fake-media-share-2',
|
|
328
|
+
htMeta: {elementId: {type: 'mediashare', id: 'fake-ht-mediaShare-2', version: 1}},
|
|
329
|
+
},
|
|
330
|
+
],
|
|
331
|
+
meetings: {id: 'fake-meetings'},
|
|
332
|
+
jsSdkMeta: {removedParticipantIds: []},
|
|
333
|
+
participants: [], // empty means there were no participant updates
|
|
334
|
+
replaces: {id: 'fake-replaces'},
|
|
335
|
+
self: {id: 'fake-self'},
|
|
336
|
+
url: 'fake-locus-url',
|
|
337
|
+
htMeta: {elementId: {type: 'locus', id: 'fake-ht-locus-id', version: 1}},
|
|
338
|
+
sequence: null, // not relevant for hash trees, so should remain null
|
|
339
|
+
syncUrl: undefined, // not relevant for hash trees, so should remain undefined
|
|
340
|
+
};
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
it('should process locus update correctly when called with updated SELF', () => {
|
|
344
|
+
const newSelf = {
|
|
345
|
+
id: 'new-self',
|
|
346
|
+
visibleDataSets: ['dataset1', 'dataset2'],
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
350
|
+
locusInfoUpdateCallback(OBJECTS_UPDATED, {
|
|
351
|
+
updatedObjects: [{htMeta: {elementId: {type: 'self'}}, data: newSelf}],
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
// check onDeltaLocus() was called with correctly updated locus info
|
|
355
|
+
assert.calledOnceWithExactly(onDeltaLocusStub, {
|
|
356
|
+
...expectedLocusInfo,
|
|
357
|
+
self: newSelf,
|
|
358
|
+
});
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
it('should process locus update correctly when called with updated SELF (webinar non-attendee)', () => {
|
|
362
|
+
const newSelf = {
|
|
363
|
+
id: 'new-self',
|
|
364
|
+
visibleDataSets: ['dataset1', 'dataset2'],
|
|
365
|
+
controls: {
|
|
366
|
+
role: {
|
|
367
|
+
roles: [
|
|
368
|
+
{type: 'PANELIST', hasRole: true},
|
|
369
|
+
{type: 'ATTENDEE', hasRole: false},
|
|
370
|
+
],
|
|
371
|
+
},
|
|
372
|
+
},
|
|
373
|
+
};
|
|
374
|
+
locusInfo.info.isWebinar = true;
|
|
375
|
+
|
|
376
|
+
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
377
|
+
locusInfoUpdateCallback(OBJECTS_UPDATED, {
|
|
378
|
+
updatedObjects: [{htMeta: {elementId: {type: 'self'}}, data: newSelf}],
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
// check onDeltaLocus() was called with correctly updated locus info
|
|
382
|
+
// without any participant generated
|
|
383
|
+
assert.calledOnceWithExactly(onDeltaLocusStub, {
|
|
384
|
+
...expectedLocusInfo,
|
|
385
|
+
info: {
|
|
386
|
+
...expectedLocusInfo.info,
|
|
387
|
+
isWebinar: true,
|
|
388
|
+
},
|
|
389
|
+
self: newSelf,
|
|
390
|
+
});
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
it('should generate a participant when called with updated SELF for webinar attendee', () => {
|
|
394
|
+
const newSelf = {
|
|
395
|
+
id: 'new-self',
|
|
396
|
+
visibleDataSets: ['dataset1', 'dataset2'],
|
|
397
|
+
controls: {
|
|
398
|
+
role: {
|
|
399
|
+
roles: [
|
|
400
|
+
{type: 'something else - should be ignored', hasRole: true},
|
|
401
|
+
{type: 'ATTENDEE', hasRole: true},
|
|
402
|
+
],
|
|
403
|
+
},
|
|
404
|
+
},
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
locusInfo.info.isWebinar = true;
|
|
408
|
+
|
|
409
|
+
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
410
|
+
locusInfoUpdateCallback(OBJECTS_UPDATED, {
|
|
411
|
+
updatedObjects: [{htMeta: {elementId: {type: 'self'}}, data: newSelf}],
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
// check onDeltaLocus() was called with correctly updated locus info
|
|
415
|
+
// that contains a participant created from self
|
|
416
|
+
assert.calledOnceWithExactly(onDeltaLocusStub, {
|
|
417
|
+
...expectedLocusInfo,
|
|
418
|
+
info: {
|
|
419
|
+
...expectedLocusInfo.info,
|
|
420
|
+
isWebinar: true,
|
|
421
|
+
},
|
|
422
|
+
self: newSelf,
|
|
423
|
+
participants: [
|
|
424
|
+
{
|
|
425
|
+
...newSelf,
|
|
426
|
+
},
|
|
427
|
+
],
|
|
428
|
+
});
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
it('should process locus update correctly when called with updated fullState', () => {
|
|
432
|
+
const newFullState = {
|
|
433
|
+
id: 'new-fullState',
|
|
434
|
+
visibleDataSets: ['dataset1', 'dataset2'],
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
438
|
+
locusInfoUpdateCallback(OBJECTS_UPDATED, {
|
|
439
|
+
updatedObjects: [{htMeta: {elementId: {type: 'fullState'}}, data: newFullState}],
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
// check onDeltaLocus() was called with correctly updated locus info
|
|
443
|
+
assert.calledOnceWithExactly(onDeltaLocusStub, {
|
|
444
|
+
...expectedLocusInfo,
|
|
445
|
+
fullState: newFullState,
|
|
446
|
+
});
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
it('should process locus update correctly when called with updated info', () => {
|
|
450
|
+
const newInfo = {
|
|
451
|
+
id: 'new-info',
|
|
452
|
+
visibleDataSets: ['dataset1', 'dataset2'],
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
456
|
+
locusInfoUpdateCallback(OBJECTS_UPDATED, {
|
|
457
|
+
updatedObjects: [{htMeta: {elementId: {type: 'info'}}, data: newInfo}],
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
// check onDeltaLocus() was called with correctly updated locus info
|
|
461
|
+
assert.calledOnceWithExactly(onDeltaLocusStub, {
|
|
462
|
+
...expectedLocusInfo,
|
|
463
|
+
info: newInfo,
|
|
464
|
+
});
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
it('should process locus update correctly when called with updated links', () => {
|
|
468
|
+
const newLinks = {
|
|
469
|
+
id: 'new-links',
|
|
470
|
+
visibleDataSets: ['dataset1', 'dataset2'],
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
474
|
+
locusInfoUpdateCallback(OBJECTS_UPDATED, {
|
|
475
|
+
updatedObjects: [{htMeta: {elementId: {type: 'links'}}, data: newLinks}],
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
// check onDeltaLocus() was called with correctly updated locus info
|
|
479
|
+
assert.calledOnceWithExactly(onDeltaLocusStub, {
|
|
480
|
+
...expectedLocusInfo,
|
|
481
|
+
links: newLinks,
|
|
482
|
+
});
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
it('should process locus update correctly when called with updated LOCUS object', () => {
|
|
486
|
+
// setup new updated locus that has many things missing
|
|
487
|
+
const newLocusHtMeta = {elementId: {type: 'locus', version: 42}};
|
|
488
|
+
const newLocus = {
|
|
489
|
+
host: 'new-host',
|
|
490
|
+
htMeta: newLocusHtMeta,
|
|
491
|
+
};
|
|
492
|
+
|
|
493
|
+
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
494
|
+
locusInfoUpdateCallback(OBJECTS_UPDATED, {
|
|
495
|
+
updatedObjects: [{htMeta: newLocusHtMeta, data: newLocus}],
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
// check onDeltaLocus() was called with correctly updated locus info
|
|
499
|
+
assert.calledOnceWithExactly(onDeltaLocusStub, {
|
|
500
|
+
// these fields are not part of Locus object, so should keep their old values:
|
|
501
|
+
controls: {id: 'fake-controls'},
|
|
502
|
+
info: {id: 'fake-info'},
|
|
503
|
+
fullState: {id: 'fake-full-state'},
|
|
504
|
+
self: {id: 'fake-self'},
|
|
505
|
+
links: {id: 'fake-links'},
|
|
506
|
+
mediaShares: expectedLocusInfo.mediaShares,
|
|
507
|
+
// and now the new fields
|
|
508
|
+
...newLocus,
|
|
509
|
+
htMeta: newLocusHtMeta,
|
|
510
|
+
participants: [], // empty means there were no participant updates
|
|
511
|
+
jsSdkMeta: {removedParticipantIds: []}, // no participants were removed
|
|
512
|
+
});
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
// this test is checking that we cope with an edge case if Locus
|
|
516
|
+
// sends us something that they shouldn't
|
|
517
|
+
it('should process locus update correctly when called with updated LOCUS object that contains info/fullState/self/participants etc', () => {
|
|
518
|
+
// setup new updated locus that has many things missing
|
|
519
|
+
const newLocusHtMeta = {elementId: {type: 'locus', version: 42}};
|
|
520
|
+
const newLocus = {
|
|
521
|
+
host: 'new-host',
|
|
522
|
+
htMeta: newLocusHtMeta,
|
|
523
|
+
};
|
|
524
|
+
|
|
525
|
+
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
526
|
+
locusInfoUpdateCallback(OBJECTS_UPDATED, {
|
|
527
|
+
updatedObjects: [
|
|
528
|
+
{
|
|
529
|
+
htMeta: newLocusHtMeta,
|
|
530
|
+
data: {
|
|
531
|
+
...newLocus,
|
|
532
|
+
// all these fields below should be ignored and not override the existing ones in our "old" Locus
|
|
533
|
+
controls: {id: 'new-controls'},
|
|
534
|
+
info: 'new-info',
|
|
535
|
+
fullState: 'new-fullState',
|
|
536
|
+
self: 'new-self',
|
|
537
|
+
participants: 'new-participants',
|
|
538
|
+
mediaShares: 'new-mediaShares',
|
|
539
|
+
},
|
|
540
|
+
},
|
|
541
|
+
],
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
// check onDeltaLocus() was called with correctly updated locus info
|
|
545
|
+
// with old values for the fields that should be ignored (like "info" or "fullState")
|
|
546
|
+
assert.calledOnceWithExactly(onDeltaLocusStub, {
|
|
547
|
+
// these fields have the "old" values:
|
|
548
|
+
controls: {id: 'fake-controls'},
|
|
549
|
+
info: {id: 'fake-info'},
|
|
550
|
+
fullState: {id: 'fake-full-state'},
|
|
551
|
+
self: {id: 'fake-self'},
|
|
552
|
+
links: {id: 'fake-links'},
|
|
553
|
+
mediaShares: expectedLocusInfo.mediaShares,
|
|
554
|
+
participants: [], // empty means there were no participant updates
|
|
555
|
+
jsSdkMeta: {removedParticipantIds: []}, // no participants were removed
|
|
556
|
+
...newLocus,
|
|
557
|
+
htMeta: newLocusHtMeta,
|
|
558
|
+
});
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
it('should process locus update correctly when called with removed LOCUS object followed by updated LOCUS object', () => {
|
|
562
|
+
// setup new updated locus that has many things missing
|
|
563
|
+
const newLocusHtMeta = {elementId: {type: 'locus', version: 99}};
|
|
564
|
+
const newLocus = {
|
|
565
|
+
info: 'new-info',
|
|
566
|
+
links: 'new-links',
|
|
567
|
+
htMeta: newLocusHtMeta,
|
|
568
|
+
};
|
|
569
|
+
|
|
570
|
+
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
571
|
+
locusInfoUpdateCallback(OBJECTS_UPDATED, {
|
|
572
|
+
updatedObjects: [
|
|
573
|
+
// first, a removal of LOCUS object
|
|
574
|
+
{htMeta: {elementId: {type: 'locus'}}, data: null},
|
|
575
|
+
// followed by an update of LOCUS object
|
|
576
|
+
{htMeta: newLocusHtMeta, data: newLocus},
|
|
577
|
+
],
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
// check onDeltaLocus() was called with correctly updated locus info
|
|
581
|
+
assert.calledOnceWithExactly(onDeltaLocusStub, {
|
|
582
|
+
// these fields are not part of Locus object, so should keep their old values:
|
|
583
|
+
controls: {id: 'fake-controls'},
|
|
584
|
+
info: {id: 'fake-info'},
|
|
585
|
+
fullState: {id: 'fake-full-state'},
|
|
586
|
+
self: {id: 'fake-self'},
|
|
587
|
+
links: {id: 'fake-links'},
|
|
588
|
+
mediaShares: expectedLocusInfo.mediaShares,
|
|
589
|
+
// and now the new fields
|
|
590
|
+
...newLocus,
|
|
591
|
+
htMeta: newLocusHtMeta,
|
|
592
|
+
participants: [], // empty means there were no participant updates
|
|
593
|
+
jsSdkMeta: {removedParticipantIds: []}, // no participants were removed
|
|
594
|
+
});
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
it('should send a metric if unsupported sequence of LOCUS object updates occurs (update followed by removal)', () => {
|
|
598
|
+
const newLocus = {
|
|
599
|
+
info: 'new-info',
|
|
600
|
+
};
|
|
601
|
+
const newLocusHtMeta = {elementId: {type: 'locus', version: 99}};
|
|
602
|
+
|
|
603
|
+
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
604
|
+
locusInfoUpdateCallback(OBJECTS_UPDATED, {
|
|
605
|
+
updatedObjects: [
|
|
606
|
+
// first, an update
|
|
607
|
+
{htMeta: newLocusHtMeta, data: newLocus},
|
|
608
|
+
// followed by removal
|
|
609
|
+
{htMeta: {elementId: {type: 'locus'}}, data: null},
|
|
610
|
+
],
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
assert.calledWith(
|
|
614
|
+
sendBehavioralMetricStub,
|
|
615
|
+
'js_sdk_locus_hash_tree_unsupported_operation',
|
|
616
|
+
{
|
|
617
|
+
locusUrl: 'fake-locus-url',
|
|
618
|
+
message: 'LOCUS object update followed by removal',
|
|
619
|
+
}
|
|
620
|
+
);
|
|
621
|
+
});
|
|
622
|
+
|
|
623
|
+
it('should send a metric if unsupported sequence of LOCUS object updates occurs (multiple updates)', () => {
|
|
624
|
+
const newLocus1 = {
|
|
625
|
+
info: 'new-info-1',
|
|
626
|
+
};
|
|
627
|
+
const newLocus2 = {
|
|
628
|
+
info: 'new-info-2',
|
|
629
|
+
url: 'new-url-2',
|
|
630
|
+
};
|
|
631
|
+
|
|
632
|
+
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
633
|
+
locusInfoUpdateCallback(OBJECTS_UPDATED, {
|
|
634
|
+
updatedObjects: [
|
|
635
|
+
// first, an update
|
|
636
|
+
{htMeta: {elementId: {type: 'locus'}}, data: newLocus1},
|
|
637
|
+
// followed by another update
|
|
638
|
+
{htMeta: {elementId: {type: 'locus'}}, data: newLocus2},
|
|
639
|
+
],
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
assert.calledWith(
|
|
643
|
+
sendBehavioralMetricStub,
|
|
644
|
+
'js_sdk_locus_hash_tree_unsupported_operation',
|
|
645
|
+
{
|
|
646
|
+
locusUrl: 'new-url-2',
|
|
647
|
+
message: 'multiple LOCUS object updates',
|
|
648
|
+
}
|
|
649
|
+
);
|
|
650
|
+
});
|
|
651
|
+
|
|
652
|
+
it('should process locus update correctly when called with added/updated/removed PARTICIPANT objects', () => {
|
|
653
|
+
const newParticipant = {
|
|
654
|
+
id: 'fake-participant-3',
|
|
655
|
+
name: 'New Participant',
|
|
656
|
+
};
|
|
657
|
+
const updatedParticipant2 = {
|
|
658
|
+
id: 'fake-participant-2',
|
|
659
|
+
name: 'Updated Participant Two',
|
|
660
|
+
};
|
|
661
|
+
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
662
|
+
// with 1 participant added, 1 updated, and 1 removed
|
|
663
|
+
locusInfoUpdateCallback(OBJECTS_UPDATED, {
|
|
664
|
+
updatedObjects: [
|
|
665
|
+
{htMeta: {elementId: {type: 'participant', id: 'fake-ht-participant-1'}}, data: null},
|
|
666
|
+
{
|
|
667
|
+
htMeta: {elementId: {type: 'participant', id: 'fake-ht-participant-3'}},
|
|
668
|
+
data: newParticipant,
|
|
669
|
+
},
|
|
670
|
+
{
|
|
671
|
+
htMeta: {elementId: {type: 'participant', id: 'fake-ht-participant-2'}},
|
|
672
|
+
data: updatedParticipant2,
|
|
673
|
+
},
|
|
674
|
+
],
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
// check onDeltaLocus() was called with correctly updated locus info
|
|
678
|
+
assert.calledOnceWithExactly(onDeltaLocusStub, {
|
|
679
|
+
...expectedLocusInfo,
|
|
680
|
+
participants: [newParticipant, updatedParticipant2],
|
|
681
|
+
jsSdkMeta: {removedParticipantIds: ['fake-participant-1']},
|
|
682
|
+
});
|
|
683
|
+
// and that the hashTreeObjectId2ParticipantId map was updated correctly
|
|
684
|
+
assert.isUndefined(locusInfo.hashTreeObjectId2ParticipantId.get('fake-ht-participant-1'));
|
|
685
|
+
assert.equal(
|
|
686
|
+
locusInfo.hashTreeObjectId2ParticipantId.get('fake-ht-participant-2'),
|
|
687
|
+
'fake-participant-2'
|
|
688
|
+
);
|
|
689
|
+
assert.equal(
|
|
690
|
+
locusInfo.hashTreeObjectId2ParticipantId.get('fake-ht-participant-3'),
|
|
691
|
+
'fake-participant-3'
|
|
692
|
+
);
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
it('should process locus update correctly when called with updated MEDIASHARE objects', () => {
|
|
696
|
+
const newMediaShare = {
|
|
697
|
+
id: 'new-mediaShare-3',
|
|
698
|
+
htMeta: {elementId: {type: 'mediashare', id: 'fake-ht-mediaShare-3', version: 100}},
|
|
699
|
+
};
|
|
700
|
+
const updatedMediaShare2 = {
|
|
701
|
+
id: 'fake-media-share-2',
|
|
702
|
+
someNewProp: 'newValue',
|
|
703
|
+
htMeta: {elementId: {type: 'mediashare', id: 'fake-ht-mediaShare-2', version: 100}},
|
|
704
|
+
};
|
|
705
|
+
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
706
|
+
// with 1 participant added, 1 updated, and 1 removed
|
|
707
|
+
locusInfoUpdateCallback(OBJECTS_UPDATED, {
|
|
708
|
+
updatedObjects: [
|
|
709
|
+
{htMeta: {elementId: {type: 'mediashare', id: 'fake-ht-mediaShare-1'}}, data: null},
|
|
710
|
+
{
|
|
711
|
+
htMeta: {elementId: {type: 'mediashare', id: 'fake-ht-mediaShare-2'}},
|
|
712
|
+
data: updatedMediaShare2,
|
|
713
|
+
},
|
|
714
|
+
{
|
|
715
|
+
htMeta: {elementId: {type: 'mediashare', id: 'fake-ht-mediaShare-3'}},
|
|
716
|
+
data: newMediaShare,
|
|
717
|
+
},
|
|
718
|
+
],
|
|
719
|
+
});
|
|
720
|
+
|
|
721
|
+
// check onDeltaLocus() was called with correctly updated locus info
|
|
722
|
+
assert.calledOnceWithExactly(onDeltaLocusStub, {
|
|
723
|
+
...expectedLocusInfo,
|
|
724
|
+
mediaShares: [updatedMediaShare2, newMediaShare],
|
|
725
|
+
});
|
|
726
|
+
});
|
|
727
|
+
|
|
728
|
+
it('should process locus update correctly when called with a combination of various updated objects', () => {
|
|
729
|
+
const newSelf = {
|
|
730
|
+
id: 'new-self',
|
|
731
|
+
visibleDataSets: ['dataset1', 'dataset2'],
|
|
732
|
+
};
|
|
733
|
+
const updatedMediaShare2 = {
|
|
734
|
+
id: 'fake-media-share-2',
|
|
735
|
+
someNewProp: 'newValue',
|
|
736
|
+
htMeta: {elementId: {type: 'mediashare', id: 'fake-ht-mediaShare-2', version: 100}},
|
|
737
|
+
};
|
|
738
|
+
const updatedParticipant2 = {
|
|
739
|
+
id: 'fake-participant-2',
|
|
740
|
+
name: 'Updated Participant Two',
|
|
741
|
+
};
|
|
742
|
+
|
|
743
|
+
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
744
|
+
locusInfoUpdateCallback(OBJECTS_UPDATED, {
|
|
745
|
+
updatedObjects: [
|
|
746
|
+
{
|
|
747
|
+
htMeta: {elementId: {type: 'mediashare', id: 'fake-ht-mediaShare-2'}},
|
|
748
|
+
data: updatedMediaShare2,
|
|
749
|
+
},
|
|
750
|
+
{htMeta: {elementId: {type: 'self'}}, data: newSelf},
|
|
751
|
+
{
|
|
752
|
+
htMeta: {elementId: {type: 'participant', id: 'fake-ht-participant-2'}},
|
|
753
|
+
data: updatedParticipant2,
|
|
754
|
+
},
|
|
755
|
+
],
|
|
756
|
+
});
|
|
757
|
+
|
|
758
|
+
// check onDeltaLocus() was called with correctly updated locus info
|
|
759
|
+
assert.calledOnceWithExactly(onDeltaLocusStub, {
|
|
760
|
+
...expectedLocusInfo,
|
|
761
|
+
mediaShares: [
|
|
762
|
+
{
|
|
763
|
+
id: 'fake-media-share-1',
|
|
764
|
+
htMeta: {elementId: {type: 'mediashare', id: 'fake-ht-mediaShare-1', version: 1}},
|
|
765
|
+
},
|
|
766
|
+
updatedMediaShare2,
|
|
767
|
+
],
|
|
768
|
+
participants: [updatedParticipant2],
|
|
769
|
+
self: newSelf,
|
|
770
|
+
});
|
|
771
|
+
});
|
|
772
|
+
|
|
773
|
+
it('should process locus update correctly when called with multiple CONTROL object updates', () => {
|
|
774
|
+
const firstControl = {
|
|
775
|
+
muteOnEntry: {enabled: true},
|
|
776
|
+
lock: {locked: true, meta: {lastModified: 'YESTERDAY', modifiedBy: 'John Doe'}},
|
|
777
|
+
};
|
|
778
|
+
const secondControl = {
|
|
779
|
+
reactions: {enabled: true},
|
|
780
|
+
};
|
|
781
|
+
|
|
782
|
+
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
783
|
+
locusInfoUpdateCallback(OBJECTS_UPDATED, {
|
|
784
|
+
updatedObjects: [
|
|
785
|
+
{
|
|
786
|
+
htMeta: {elementId: {type: 'controlentry', id: 'control-1'}},
|
|
787
|
+
data: firstControl,
|
|
788
|
+
},
|
|
789
|
+
{
|
|
790
|
+
htMeta: {elementId: {type: 'controlentry', id: 'control-2'}},
|
|
791
|
+
data: secondControl,
|
|
792
|
+
},
|
|
793
|
+
],
|
|
794
|
+
});
|
|
795
|
+
|
|
796
|
+
// check onDeltaLocus() was called with correctly updated locus info
|
|
797
|
+
// all keys from both controls should be merged into the controls object
|
|
798
|
+
assert.calledOnceWithExactly(onDeltaLocusStub, {
|
|
799
|
+
...expectedLocusInfo,
|
|
800
|
+
controls: {
|
|
801
|
+
id: 'fake-controls',
|
|
802
|
+
muteOnEntry: {enabled: true},
|
|
803
|
+
lock: {locked: true, meta: {lastModified: 'YESTERDAY', modifiedBy: 'John Doe'}},
|
|
804
|
+
reactions: {enabled: true},
|
|
805
|
+
},
|
|
806
|
+
});
|
|
807
|
+
});
|
|
808
|
+
|
|
809
|
+
it('should process locus update correctly when CONTROL object is received with no data', () => {
|
|
810
|
+
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
811
|
+
locusInfoUpdateCallback(OBJECTS_UPDATED, {
|
|
812
|
+
updatedObjects: [
|
|
813
|
+
{
|
|
814
|
+
htMeta: {elementId: {type: 'controlentry', id: 'some-control-id'}},
|
|
815
|
+
data: null,
|
|
816
|
+
},
|
|
817
|
+
],
|
|
818
|
+
});
|
|
819
|
+
|
|
820
|
+
// check onDeltaLocus() was called with correctly updated locus info
|
|
821
|
+
// when data is null, it should be ignored and not change the controls
|
|
822
|
+
assert.calledOnceWithExactly(onDeltaLocusStub, {
|
|
823
|
+
...expectedLocusInfo,
|
|
824
|
+
});
|
|
825
|
+
});
|
|
826
|
+
|
|
827
|
+
it('should handle MEETING_ENDED correctly', () => {
|
|
828
|
+
const fakeMeeting = {id: 'fake-meeting-from-collection'};
|
|
829
|
+
const collectionGetStub = sinon
|
|
830
|
+
.stub(locusInfo.webex.meetings.meetingCollection, 'get')
|
|
831
|
+
.returns(fakeMeeting);
|
|
832
|
+
const destroyStub = sinon.stub(locusInfo.webex.meetings, 'destroy');
|
|
833
|
+
|
|
834
|
+
// simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
|
|
835
|
+
locusInfoUpdateCallback(MEETING_ENDED);
|
|
836
|
+
|
|
837
|
+
assert.calledOnceWithExactly(collectionGetStub, locusInfo.meetingId);
|
|
838
|
+
assert.calledOnceWithExactly(
|
|
839
|
+
destroyStub,
|
|
840
|
+
fakeMeeting,
|
|
841
|
+
MEETING_REMOVED_REASON.SELF_REMOVED
|
|
842
|
+
);
|
|
843
|
+
});
|
|
844
|
+
});
|
|
845
|
+
});
|
|
846
|
+
|
|
80
847
|
describe('#updateControls', () => {
|
|
81
848
|
let newControls;
|
|
82
849
|
|
|
@@ -571,6 +1338,34 @@ describe('plugin-meetings', () => {
|
|
|
571
1338
|
);
|
|
572
1339
|
});
|
|
573
1340
|
|
|
1341
|
+
it('should update the hesiod llm id', () => {
|
|
1342
|
+
locusInfo.emitScoped = sinon.stub();
|
|
1343
|
+
locusInfo.controls = {
|
|
1344
|
+
transcribe: {
|
|
1345
|
+
transcribing: false,
|
|
1346
|
+
caption: true,
|
|
1347
|
+
hesiodLlmId: '123a-456b-789c',
|
|
1348
|
+
},
|
|
1349
|
+
};
|
|
1350
|
+
newControls.transcribe.transcribing = false;
|
|
1351
|
+
newControls.transcribe.caption = true;
|
|
1352
|
+
newControls.transcribe.hesiodLlmId = '789d-456e-123f';
|
|
1353
|
+
|
|
1354
|
+
locusInfo.updateControls(newControls);
|
|
1355
|
+
|
|
1356
|
+
assert.calledWith(
|
|
1357
|
+
locusInfo.emitScoped,
|
|
1358
|
+
{
|
|
1359
|
+
file: 'locus-info',
|
|
1360
|
+
function: 'updateControls',
|
|
1361
|
+
},
|
|
1362
|
+
LOCUSINFO.EVENTS.CONTROLS_MEETING_HESIOD_LLM_ID_UPDATED,
|
|
1363
|
+
{
|
|
1364
|
+
hesiodLlmId: '789d-456e-123f',
|
|
1365
|
+
}
|
|
1366
|
+
);
|
|
1367
|
+
});
|
|
1368
|
+
|
|
574
1369
|
it('should update the transcribe spoken language', () => {
|
|
575
1370
|
locusInfo.emitScoped = sinon.stub();
|
|
576
1371
|
locusInfo.controls = {
|
|
@@ -2041,7 +2836,7 @@ describe('plugin-meetings', () => {
|
|
|
2041
2836
|
});
|
|
2042
2837
|
|
|
2043
2838
|
describe('#handleLocusAPIResponse', () => {
|
|
2044
|
-
it('calls handleLocusDelta', () => {
|
|
2839
|
+
it('calls handleLocusDelta when we are not using hash trees', () => {
|
|
2045
2840
|
const fakeLocus = {eventType: LOCUSEVENT.DIFFERENCE};
|
|
2046
2841
|
|
|
2047
2842
|
sinon.stub(locusInfo, 'handleLocusDelta');
|
|
@@ -2050,6 +2845,23 @@ describe('plugin-meetings', () => {
|
|
|
2050
2845
|
|
|
2051
2846
|
assert.calledWith(locusInfo.handleLocusDelta, fakeLocus, mockMeeting);
|
|
2052
2847
|
});
|
|
2848
|
+
it('calls hash tree parser when we are using hash trees', () => {
|
|
2849
|
+
const fakeLocus = {eventType: LOCUSEVENT.DIFFERENCE};
|
|
2850
|
+
const fakeDataSets = [{name: 'dataset1', url: 'http://test.com'}];
|
|
2851
|
+
const responseBody = {locus: fakeLocus, dataSets: fakeDataSets};
|
|
2852
|
+
|
|
2853
|
+
// Create a mock hash tree parser
|
|
2854
|
+
const mockHashTreeParser = {
|
|
2855
|
+
handleLocusUpdate: sinon.stub(),
|
|
2856
|
+
};
|
|
2857
|
+
locusInfo.hashTreeParser = mockHashTreeParser;
|
|
2858
|
+
|
|
2859
|
+
sinon.stub(locusInfo, 'onDeltaLocus');
|
|
2860
|
+
|
|
2861
|
+
locusInfo.handleLocusAPIResponse(mockMeeting, responseBody);
|
|
2862
|
+
|
|
2863
|
+
assert.calledOnceWithExactly(mockHashTreeParser.handleLocusUpdate, responseBody);
|
|
2864
|
+
});
|
|
2053
2865
|
});
|
|
2054
2866
|
|
|
2055
2867
|
describe('#LocusDeltaEvents', () => {
|
|
@@ -2122,8 +2934,7 @@ describe('plugin-meetings', () => {
|
|
|
2122
2934
|
callOrder.push("updateMeetingInfo");
|
|
2123
2935
|
});
|
|
2124
2936
|
sinon.stub(locusInfo, "updateMediaShares");
|
|
2125
|
-
sinon.stub(locusInfo, "
|
|
2126
|
-
sinon.stub(locusInfo, "updateReplace");
|
|
2937
|
+
sinon.stub(locusInfo, "updateReplaces");
|
|
2127
2938
|
sinon.stub(locusInfo, "updateSelf");
|
|
2128
2939
|
sinon.stub(locusInfo, "updateLocusUrl").callsFake(() => {
|
|
2129
2940
|
callOrder.push("updateLocusUrl");
|
|
@@ -2131,10 +2942,8 @@ describe('plugin-meetings', () => {
|
|
|
2131
2942
|
sinon.stub(locusInfo, "updateAclUrl");
|
|
2132
2943
|
sinon.stub(locusInfo, "updateBasequence");
|
|
2133
2944
|
sinon.stub(locusInfo, "updateSequence");
|
|
2134
|
-
sinon.stub(locusInfo, "updateMemberShip");
|
|
2135
|
-
sinon.stub(locusInfo, "updateIdentifiers");
|
|
2136
2945
|
sinon.stub(locusInfo, "updateEmbeddedApps");
|
|
2137
|
-
sinon.stub(locusInfo, "
|
|
2946
|
+
sinon.stub(locusInfo, "updateLinks");
|
|
2138
2947
|
sinon.stub(locusInfo, "compareAndUpdate");
|
|
2139
2948
|
|
|
2140
2949
|
locusInfo.updateLocusInfo(locus);
|
|
@@ -2158,17 +2967,14 @@ describe('plugin-meetings', () => {
|
|
|
2158
2967
|
locusInfo.updateHostInfo = sinon.stub();
|
|
2159
2968
|
locusInfo.updateMeetingInfo = sinon.stub();
|
|
2160
2969
|
locusInfo.updateMediaShares = sinon.stub();
|
|
2161
|
-
locusInfo.
|
|
2162
|
-
locusInfo.updateReplace = sinon.stub();
|
|
2970
|
+
locusInfo.updateReplaces = sinon.stub();
|
|
2163
2971
|
locusInfo.updateSelf = sinon.stub();
|
|
2164
2972
|
locusInfo.updateLocusUrl = sinon.stub();
|
|
2165
2973
|
locusInfo.updateAclUrl = sinon.stub();
|
|
2166
2974
|
locusInfo.updateBasequence = sinon.stub();
|
|
2167
2975
|
locusInfo.updateSequence = sinon.stub();
|
|
2168
|
-
locusInfo.updateMemberShip = sinon.stub();
|
|
2169
|
-
locusInfo.updateIdentifiers = sinon.stub();
|
|
2170
2976
|
locusInfo.updateEmbeddedApps = sinon.stub();
|
|
2171
|
-
locusInfo.
|
|
2977
|
+
locusInfo.updateLinks = sinon.stub();
|
|
2172
2978
|
locusInfo.compareAndUpdate = sinon.stub();
|
|
2173
2979
|
|
|
2174
2980
|
locusInfo.updateLocusInfo(newLocus);
|
|
@@ -2180,21 +2986,49 @@ describe('plugin-meetings', () => {
|
|
|
2180
2986
|
assert.notCalled(locusInfo.updateHostInfo);
|
|
2181
2987
|
assert.notCalled(locusInfo.updateMeetingInfo);
|
|
2182
2988
|
assert.notCalled(locusInfo.updateMediaShares);
|
|
2183
|
-
assert.notCalled(locusInfo.
|
|
2184
|
-
assert.notCalled(locusInfo.updateReplace);
|
|
2989
|
+
assert.notCalled(locusInfo.updateReplaces);
|
|
2185
2990
|
assert.notCalled(locusInfo.updateSelf);
|
|
2186
2991
|
assert.notCalled(locusInfo.updateLocusUrl);
|
|
2187
2992
|
assert.notCalled(locusInfo.updateAclUrl);
|
|
2188
2993
|
assert.notCalled(locusInfo.updateBasequence);
|
|
2189
2994
|
assert.notCalled(locusInfo.updateSequence);
|
|
2190
|
-
assert.notCalled(locusInfo.updateMemberShip);
|
|
2191
|
-
assert.notCalled(locusInfo.updateIdentifiers);
|
|
2192
2995
|
assert.notCalled(locusInfo.updateEmbeddedApps);
|
|
2193
|
-
assert.notCalled(locusInfo.
|
|
2996
|
+
assert.notCalled(locusInfo.updateLinks);
|
|
2194
2997
|
assert.notCalled(locusInfo.compareAndUpdate);
|
|
2195
2998
|
});
|
|
2196
2999
|
|
|
3000
|
+
it('#updateLocusInfo puts the Locus DTO top level properties at the right place in LocusInfo class', () => {
|
|
3001
|
+
// this test verifies that the top-level properties of Locus DTO are copied
|
|
3002
|
+
// into LocusInfo class and set as top level properties too
|
|
3003
|
+
// this is important, because the code handling Locus hass trees relies on it, see updateFromHashTree()
|
|
3004
|
+
const info = {id: 'info id'};
|
|
3005
|
+
const fullState = {id: 'fullState id'};
|
|
3006
|
+
const links = {services: {id: 'service links'}, resources: {id: 'resource links'}};
|
|
3007
|
+
const self = {id: 'self id'};
|
|
3008
|
+
const mediaShares = [{id: 'fake media share'}];
|
|
2197
3009
|
|
|
3010
|
+
sinon.stub(SelfUtils, 'getSelves').returns({
|
|
3011
|
+
current: {},
|
|
3012
|
+
previous: {},
|
|
3013
|
+
updates: {},
|
|
3014
|
+
});
|
|
3015
|
+
|
|
3016
|
+
const newLocus = {
|
|
3017
|
+
info,
|
|
3018
|
+
fullState,
|
|
3019
|
+
links,
|
|
3020
|
+
self,
|
|
3021
|
+
mediaShares,
|
|
3022
|
+
};
|
|
3023
|
+
|
|
3024
|
+
locusInfo.updateLocusInfo(newLocus);
|
|
3025
|
+
|
|
3026
|
+
assert.deepEqual(locusInfo.info, newLocus.info);
|
|
3027
|
+
assert.deepEqual(locusInfo.fullState, newLocus.fullState);
|
|
3028
|
+
assert.deepEqual(locusInfo.links, newLocus.links);
|
|
3029
|
+
assert.deepEqual(locusInfo.self, newLocus.self);
|
|
3030
|
+
assert.deepEqual(locusInfo.mediaShares, newLocus.mediaShares);
|
|
3031
|
+
});
|
|
2198
3032
|
|
|
2199
3033
|
it('onFullLocus() updates the working-copy of locus parser', () => {
|
|
2200
3034
|
const eventType = 'fakeEvent';
|
|
@@ -3351,5 +4185,34 @@ describe('plugin-meetings', () => {
|
|
|
3351
4185
|
assert.calledWith(updateLocusInfoStub.getCall(2), deltaEvents[7]);
|
|
3352
4186
|
});
|
|
3353
4187
|
});
|
|
4188
|
+
|
|
4189
|
+
describe('#parse', () => {
|
|
4190
|
+
it('handles hash tree messages correctly', () => {
|
|
4191
|
+
const fakeHashTreeMessage = {
|
|
4192
|
+
locusStateElements: [
|
|
4193
|
+
{
|
|
4194
|
+
htMeta: {elementId: {type: 'self'}},
|
|
4195
|
+
data: {visibleDataSets: ['dataset1']},
|
|
4196
|
+
},
|
|
4197
|
+
],
|
|
4198
|
+
dataSets: [{name: 'dataset1', url: 'http://test.com'}],
|
|
4199
|
+
};
|
|
4200
|
+
|
|
4201
|
+
const data = {
|
|
4202
|
+
eventType: LOCUSEVENT.HASH_TREE_DATA_UPDATED,
|
|
4203
|
+
stateElementsMessage: fakeHashTreeMessage,
|
|
4204
|
+
};
|
|
4205
|
+
|
|
4206
|
+
// Create a mock hash tree parser
|
|
4207
|
+
const mockHashTreeParser = {
|
|
4208
|
+
handleMessage: sinon.stub(),
|
|
4209
|
+
};
|
|
4210
|
+
locusInfo.hashTreeParser = mockHashTreeParser;
|
|
4211
|
+
|
|
4212
|
+
locusInfo.parse(mockMeeting, data);
|
|
4213
|
+
|
|
4214
|
+
assert.calledOnceWithExactly(mockHashTreeParser.handleMessage, fakeHashTreeMessage);
|
|
4215
|
+
});
|
|
4216
|
+
});
|
|
3354
4217
|
});
|
|
3355
4218
|
});
|