@webex/plugin-meetings 3.10.0-next.1 → 3.10.0-next.10

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.
Files changed (246) hide show
  1. package/dist/annotation/annotation.types.js.map +1 -1
  2. package/dist/annotation/constants.js.map +1 -1
  3. package/dist/annotation/index.js +19 -22
  4. package/dist/annotation/index.js.map +1 -1
  5. package/dist/breakouts/breakout.js +6 -6
  6. package/dist/breakouts/breakout.js.map +1 -1
  7. package/dist/breakouts/collection.js.map +1 -1
  8. package/dist/breakouts/edit-lock-error.js +9 -11
  9. package/dist/breakouts/edit-lock-error.js.map +1 -1
  10. package/dist/breakouts/events.js.map +1 -1
  11. package/dist/breakouts/index.js +126 -127
  12. package/dist/breakouts/index.js.map +1 -1
  13. package/dist/breakouts/request.js +6 -8
  14. package/dist/breakouts/request.js.map +1 -1
  15. package/dist/breakouts/utils.js.map +1 -1
  16. package/dist/common/browser-detection.js.map +1 -1
  17. package/dist/common/collection.js +1 -2
  18. package/dist/common/collection.js.map +1 -1
  19. package/dist/common/config.js.map +1 -1
  20. package/dist/common/errors/captcha-error.js +9 -11
  21. package/dist/common/errors/captcha-error.js.map +1 -1
  22. package/dist/common/errors/intent-to-join.js +10 -12
  23. package/dist/common/errors/intent-to-join.js.map +1 -1
  24. package/dist/common/errors/join-forbidden-error.js +10 -12
  25. package/dist/common/errors/join-forbidden-error.js.map +1 -1
  26. package/dist/common/errors/join-meeting.js +10 -12
  27. package/dist/common/errors/join-meeting.js.map +1 -1
  28. package/dist/common/errors/join-webinar-error.js +9 -11
  29. package/dist/common/errors/join-webinar-error.js.map +1 -1
  30. package/dist/common/errors/media.js +9 -11
  31. package/dist/common/errors/media.js.map +1 -1
  32. package/dist/common/errors/multistream-not-supported-error.js +9 -11
  33. package/dist/common/errors/multistream-not-supported-error.js.map +1 -1
  34. package/dist/common/errors/no-meeting-info.js +9 -11
  35. package/dist/common/errors/no-meeting-info.js.map +1 -1
  36. package/dist/common/errors/parameter.js +11 -14
  37. package/dist/common/errors/parameter.js.map +1 -1
  38. package/dist/common/errors/password-error.js +9 -11
  39. package/dist/common/errors/password-error.js.map +1 -1
  40. package/dist/common/errors/permission.js +9 -11
  41. package/dist/common/errors/permission.js.map +1 -1
  42. package/dist/common/errors/reclaim-host-role-errors.js +32 -38
  43. package/dist/common/errors/reclaim-host-role-errors.js.map +1 -1
  44. package/dist/common/errors/reconnection-not-started.js +5 -6
  45. package/dist/common/errors/reconnection-not-started.js.map +1 -1
  46. package/dist/common/errors/reconnection.js +9 -11
  47. package/dist/common/errors/reconnection.js.map +1 -1
  48. package/dist/common/errors/stats.js +9 -11
  49. package/dist/common/errors/stats.js.map +1 -1
  50. package/dist/common/errors/webex-errors.js +38 -27
  51. package/dist/common/errors/webex-errors.js.map +1 -1
  52. package/dist/common/errors/webex-meetings-error.js +9 -12
  53. package/dist/common/errors/webex-meetings-error.js.map +1 -1
  54. package/dist/common/events/events-scope.js +9 -10
  55. package/dist/common/events/events-scope.js.map +1 -1
  56. package/dist/common/events/events.js +9 -10
  57. package/dist/common/events/events.js.map +1 -1
  58. package/dist/common/events/trigger-proxy.js.map +1 -1
  59. package/dist/common/events/util.js.map +1 -1
  60. package/dist/common/logs/logger-config.js.map +1 -1
  61. package/dist/common/logs/logger-proxy.js.map +1 -1
  62. package/dist/common/logs/request.js +17 -17
  63. package/dist/common/logs/request.js.map +1 -1
  64. package/dist/common/queue.js +1 -2
  65. package/dist/common/queue.js.map +1 -1
  66. package/dist/config.js +0 -1
  67. package/dist/config.js.map +1 -1
  68. package/dist/constants.js +12 -8
  69. package/dist/constants.js.map +1 -1
  70. package/dist/controls-options-manager/constants.js.map +1 -1
  71. package/dist/controls-options-manager/enums.js.map +1 -1
  72. package/dist/controls-options-manager/index.js +1 -2
  73. package/dist/controls-options-manager/index.js.map +1 -1
  74. package/dist/controls-options-manager/types.js.map +1 -1
  75. package/dist/controls-options-manager/util.js +1 -2
  76. package/dist/controls-options-manager/util.js.map +1 -1
  77. package/dist/hashTree/hashTreeParser.js +165 -0
  78. package/dist/hashTree/hashTreeParser.js.map +1 -0
  79. package/dist/hashTree/types.js +15 -0
  80. package/dist/hashTree/types.js.map +1 -0
  81. package/dist/index.js +8 -2
  82. package/dist/index.js.map +1 -1
  83. package/dist/interceptors/index.js.map +1 -1
  84. package/dist/interceptors/locusRetry.js +6 -8
  85. package/dist/interceptors/locusRetry.js.map +1 -1
  86. package/dist/interceptors/locusRouteToken.js +6 -8
  87. package/dist/interceptors/locusRouteToken.js.map +1 -1
  88. package/dist/interpretation/collection.js.map +1 -1
  89. package/dist/interpretation/index.js +1 -2
  90. package/dist/interpretation/index.js.map +1 -1
  91. package/dist/interpretation/siLanguage.js +1 -1
  92. package/dist/interpretation/siLanguage.js.map +1 -1
  93. package/dist/locus-info/controlsUtils.js.map +1 -1
  94. package/dist/locus-info/embeddedAppsUtils.js.map +1 -1
  95. package/dist/locus-info/fullState.js.map +1 -1
  96. package/dist/locus-info/hostUtils.js.map +1 -1
  97. package/dist/locus-info/index.js +532 -94
  98. package/dist/locus-info/index.js.map +1 -1
  99. package/dist/locus-info/infoUtils.js.map +1 -1
  100. package/dist/locus-info/mediaSharesUtils.js.map +1 -1
  101. package/dist/locus-info/parser.js +3 -4
  102. package/dist/locus-info/parser.js.map +1 -1
  103. package/dist/locus-info/selfUtils.js.map +1 -1
  104. package/dist/locus-info/types.js +7 -0
  105. package/dist/locus-info/types.js.map +1 -0
  106. package/dist/media/MediaConnectionAwaiter.js +1 -2
  107. package/dist/media/MediaConnectionAwaiter.js.map +1 -1
  108. package/dist/media/index.js +5 -2
  109. package/dist/media/index.js.map +1 -1
  110. package/dist/media/properties.js +15 -17
  111. package/dist/media/properties.js.map +1 -1
  112. package/dist/media/util.js.map +1 -1
  113. package/dist/meeting/brbState.js +8 -9
  114. package/dist/meeting/brbState.js.map +1 -1
  115. package/dist/meeting/connectionStateHandler.js +10 -13
  116. package/dist/meeting/connectionStateHandler.js.map +1 -1
  117. package/dist/meeting/in-meeting-actions.js.map +1 -1
  118. package/dist/meeting/index.js +1556 -1528
  119. package/dist/meeting/index.js.map +1 -1
  120. package/dist/meeting/locusMediaRequest.js +13 -17
  121. package/dist/meeting/locusMediaRequest.js.map +1 -1
  122. package/dist/meeting/muteState.js +11 -12
  123. package/dist/meeting/muteState.js.map +1 -1
  124. package/dist/meeting/request.js +101 -104
  125. package/dist/meeting/request.js.map +1 -1
  126. package/dist/meeting/request.type.js.map +1 -1
  127. package/dist/meeting/state.js.map +1 -1
  128. package/dist/meeting/type.js.map +1 -1
  129. package/dist/meeting/util.js +24 -23
  130. package/dist/meeting/util.js.map +1 -1
  131. package/dist/meeting/voicea-meeting.js +3 -3
  132. package/dist/meeting/voicea-meeting.js.map +1 -1
  133. package/dist/meeting-info/collection.js +7 -10
  134. package/dist/meeting-info/collection.js.map +1 -1
  135. package/dist/meeting-info/index.js +1 -2
  136. package/dist/meeting-info/index.js.map +1 -1
  137. package/dist/meeting-info/meeting-info-v2.js +135 -146
  138. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  139. package/dist/meeting-info/request.js +1 -2
  140. package/dist/meeting-info/request.js.map +1 -1
  141. package/dist/meeting-info/util.js +36 -37
  142. package/dist/meeting-info/util.js.map +1 -1
  143. package/dist/meeting-info/utilv2.js +30 -31
  144. package/dist/meeting-info/utilv2.js.map +1 -1
  145. package/dist/meetings/collection.js +6 -8
  146. package/dist/meetings/collection.js.map +1 -1
  147. package/dist/meetings/index.js +179 -141
  148. package/dist/meetings/index.js.map +1 -1
  149. package/dist/meetings/meetings.types.js.map +1 -1
  150. package/dist/meetings/request.js +6 -8
  151. package/dist/meetings/request.js.map +1 -1
  152. package/dist/meetings/util.js +25 -23
  153. package/dist/meetings/util.js.map +1 -1
  154. package/dist/member/index.js +1 -2
  155. package/dist/member/index.js.map +1 -1
  156. package/dist/member/types.js +6 -3
  157. package/dist/member/types.js.map +1 -1
  158. package/dist/member/util.js.map +1 -1
  159. package/dist/members/collection.js +1 -2
  160. package/dist/members/collection.js.map +1 -1
  161. package/dist/members/index.js +18 -21
  162. package/dist/members/index.js.map +1 -1
  163. package/dist/members/request.js +8 -11
  164. package/dist/members/request.js.map +1 -1
  165. package/dist/members/types.js.map +1 -1
  166. package/dist/members/util.js.map +1 -1
  167. package/dist/metrics/constants.js +3 -1
  168. package/dist/metrics/constants.js.map +1 -1
  169. package/dist/metrics/index.js +3 -4
  170. package/dist/metrics/index.js.map +1 -1
  171. package/dist/multistream/mediaRequestManager.js +1 -2
  172. package/dist/multistream/mediaRequestManager.js.map +1 -1
  173. package/dist/multistream/receiveSlot.js +34 -45
  174. package/dist/multistream/receiveSlot.js.map +1 -1
  175. package/dist/multistream/receiveSlotManager.js +8 -9
  176. package/dist/multistream/receiveSlotManager.js.map +1 -1
  177. package/dist/multistream/remoteMedia.js +12 -15
  178. package/dist/multistream/remoteMedia.js.map +1 -1
  179. package/dist/multistream/remoteMediaGroup.js +1 -2
  180. package/dist/multistream/remoteMediaGroup.js.map +1 -1
  181. package/dist/multistream/remoteMediaManager.js +122 -123
  182. package/dist/multistream/remoteMediaManager.js.map +1 -1
  183. package/dist/multistream/sendSlotManager.js +29 -30
  184. package/dist/multistream/sendSlotManager.js.map +1 -1
  185. package/dist/personal-meeting-room/index.js +16 -19
  186. package/dist/personal-meeting-room/index.js.map +1 -1
  187. package/dist/personal-meeting-room/request.js +7 -10
  188. package/dist/personal-meeting-room/request.js.map +1 -1
  189. package/dist/personal-meeting-room/util.js.map +1 -1
  190. package/dist/reachability/clusterReachability.js +36 -39
  191. package/dist/reachability/clusterReachability.js.map +1 -1
  192. package/dist/reachability/index.js +203 -205
  193. package/dist/reachability/index.js.map +1 -1
  194. package/dist/reachability/reachability.types.js.map +1 -1
  195. package/dist/reachability/request.js.map +1 -1
  196. package/dist/reachability/util.js.map +1 -1
  197. package/dist/reactions/constants.js.map +1 -1
  198. package/dist/reactions/reactions.js.map +1 -1
  199. package/dist/reactions/reactions.type.js.map +1 -1
  200. package/dist/reconnection-manager/index.js +178 -176
  201. package/dist/reconnection-manager/index.js.map +1 -1
  202. package/dist/recording-controller/enums.js.map +1 -1
  203. package/dist/recording-controller/index.js +1 -2
  204. package/dist/recording-controller/index.js.map +1 -1
  205. package/dist/recording-controller/util.js.map +1 -1
  206. package/dist/roap/index.js +12 -15
  207. package/dist/roap/index.js.map +1 -1
  208. package/dist/roap/request.js +24 -26
  209. package/dist/roap/request.js.map +1 -1
  210. package/dist/roap/turnDiscovery.js +75 -76
  211. package/dist/roap/turnDiscovery.js.map +1 -1
  212. package/dist/roap/types.js.map +1 -1
  213. package/dist/transcription/index.js +4 -5
  214. package/dist/transcription/index.js.map +1 -1
  215. package/dist/types/common/errors/webex-errors.d.ts +12 -0
  216. package/dist/types/constants.d.ts +26 -21
  217. package/dist/types/hashTree/hashTreeParser.d.ts +109 -0
  218. package/dist/types/hashTree/types.d.ts +16 -0
  219. package/dist/types/index.d.ts +2 -1
  220. package/dist/types/locus-info/index.d.ts +91 -42
  221. package/dist/types/locus-info/types.d.ts +45 -0
  222. package/dist/types/meeting/index.d.ts +22 -9
  223. package/dist/types/meetings/index.d.ts +9 -2
  224. package/dist/types/metrics/constants.d.ts +2 -0
  225. package/dist/webinar/collection.js +1 -2
  226. package/dist/webinar/collection.js.map +1 -1
  227. package/dist/webinar/index.js +148 -158
  228. package/dist/webinar/index.js.map +1 -1
  229. package/package.json +16 -16
  230. package/src/common/errors/webex-errors.ts +19 -0
  231. package/src/constants.ts +14 -2
  232. package/src/hashTree/hashTreeParser.ts +146 -0
  233. package/src/hashTree/types.ts +20 -0
  234. package/src/index.ts +2 -0
  235. package/src/locus-info/index.ts +534 -85
  236. package/src/locus-info/types.ts +46 -0
  237. package/src/media/index.ts +6 -0
  238. package/src/meeting/index.ts +61 -27
  239. package/src/meeting/util.ts +1 -0
  240. package/src/meetings/index.ts +104 -51
  241. package/src/metrics/constants.ts +2 -0
  242. package/test/unit/spec/locus-info/index.js +576 -1
  243. package/test/unit/spec/media/index.ts +140 -9
  244. package/test/unit/spec/meeting/index.js +178 -94
  245. package/test/unit/spec/meeting/utils.js +77 -0
  246. package/test/unit/spec/meetings/index.js +71 -28
@@ -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,532 @@ 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 LOCUS object', () => {
362
+ // setup new updated locus that has many things missing
363
+ const newLocusHtMeta = {elementId: {type: 'locus', version: 42}};
364
+ const newLocus = {
365
+ conntrols: 'new-controls',
366
+ host: 'new-host',
367
+ htMeta: newLocusHtMeta,
368
+ };
369
+
370
+ // simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
371
+ locusInfoUpdateCallback(OBJECTS_UPDATED, {
372
+ updatedObjects: [{htMeta: newLocusHtMeta, data: newLocus}],
373
+ });
374
+
375
+ // check onDeltaLocus() was called with correctly updated locus info
376
+ assert.calledOnceWithExactly(onDeltaLocusStub, {
377
+ ...newLocus,
378
+ htMeta: newLocusHtMeta,
379
+ participants: [], // empty means there were no participant updates
380
+ jsSdkMeta: {removedParticipantIds: []}, // no participants were removed
381
+ });
382
+ });
383
+
384
+ it('should process locus update correctly when called with removed LOCUS object followed by updated LOCUS object', () => {
385
+ // setup new updated locus that has many things missing
386
+ const newLocusHtMeta = {elementId: {type: 'locus', version: 99}};
387
+ const newLocus = {
388
+ info: 'new-info',
389
+ links: 'new-links',
390
+ htMeta: newLocusHtMeta,
391
+ };
392
+
393
+ // simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
394
+ locusInfoUpdateCallback(OBJECTS_UPDATED, {
395
+ updatedObjects: [
396
+ // first, a removal of LOCUS object
397
+ {htMeta: {elementId: {type: 'locus'}}, data: null},
398
+ // followed by an update of LOCUS object
399
+ {htMeta: newLocusHtMeta, data: newLocus},
400
+ ],
401
+ });
402
+
403
+ // check onDeltaLocus() was called with correctly updated locus info
404
+ assert.calledOnceWithExactly(onDeltaLocusStub, {
405
+ ...newLocus,
406
+ htMeta: newLocusHtMeta,
407
+ participants: [], // empty means there were no participant updates
408
+ jsSdkMeta: {removedParticipantIds: []}, // no participants were removed
409
+ });
410
+ });
411
+
412
+ it('should send a metric if unsupported sequence of LOCUS object updates occurs (update followed by removal)', () => {
413
+ const newLocus = {
414
+ info: 'new-info',
415
+ };
416
+ const newLocusHtMeta = {elementId: {type: 'locus', version: 99}};
417
+
418
+ // simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
419
+ locusInfoUpdateCallback(OBJECTS_UPDATED, {
420
+ updatedObjects: [
421
+ // first, an update
422
+ {htMeta: newLocusHtMeta, data: newLocus},
423
+ // followed by removal
424
+ {htMeta: {elementId: {type: 'locus'}}, data: null},
425
+ ],
426
+ });
427
+
428
+ assert.calledWith(
429
+ sendBehavioralMetricStub,
430
+ 'js_sdk_locus_hash_tree_unsupported_operation',
431
+ {
432
+ locusUrl: 'fake-locus-url',
433
+ message: 'LOCUS object update followed by removal',
434
+ }
435
+ );
436
+ });
437
+
438
+ it('should send a metric if unsupported sequence of LOCUS object updates occurs (multiple updates)', () => {
439
+ const newLocus1 = {
440
+ info: 'new-info-1',
441
+ };
442
+ const newLocus2 = {
443
+ info: 'new-info-2',
444
+ url: 'new-url-2',
445
+ };
446
+
447
+ // simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
448
+ locusInfoUpdateCallback(OBJECTS_UPDATED, {
449
+ updatedObjects: [
450
+ // first, an update
451
+ {htMeta: {elementId: {type: 'locus'}}, data: newLocus1},
452
+ // followed by another update
453
+ {htMeta: {elementId: {type: 'locus'}}, data: newLocus2},
454
+ ],
455
+ });
456
+
457
+ assert.calledWith(
458
+ sendBehavioralMetricStub,
459
+ 'js_sdk_locus_hash_tree_unsupported_operation',
460
+ {
461
+ locusUrl: 'new-url-2',
462
+ message: 'multiple LOCUS object updates',
463
+ }
464
+ );
465
+ });
466
+
467
+ it('should process locus update correctly when called with added/updated/removed PARTICIPANT objects', () => {
468
+ const newParticipant = {
469
+ id: 'fake-participant-3',
470
+ name: 'New Participant',
471
+ };
472
+ const updatedParticipant2 = {
473
+ id: 'fake-participant-2',
474
+ name: 'Updated Participant Two',
475
+ };
476
+ // simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
477
+ // with 1 participant added, 1 updated, and 1 removed
478
+ locusInfoUpdateCallback(OBJECTS_UPDATED, {
479
+ updatedObjects: [
480
+ {htMeta: {elementId: {type: 'participant', id: 'fake-ht-participant-1'}}, data: null},
481
+ {
482
+ htMeta: {elementId: {type: 'participant', id: 'fake-ht-participant-3'}},
483
+ data: newParticipant,
484
+ },
485
+ {
486
+ htMeta: {elementId: {type: 'participant', id: 'fake-ht-participant-2'}},
487
+ data: updatedParticipant2,
488
+ },
489
+ ],
490
+ });
491
+
492
+ // check onDeltaLocus() was called with correctly updated locus info
493
+ assert.calledOnceWithExactly(onDeltaLocusStub, {
494
+ ...expectedLocusInfo,
495
+ participants: [newParticipant, updatedParticipant2],
496
+ jsSdkMeta: {removedParticipantIds: ['fake-participant-1']},
497
+ });
498
+ // and that the hashTreeObjectId2ParticipantId map was updated correctly
499
+ assert.isUndefined(locusInfo.hashTreeObjectId2ParticipantId.get('fake-ht-participant-1'));
500
+ assert.equal(
501
+ locusInfo.hashTreeObjectId2ParticipantId.get('fake-ht-participant-2'),
502
+ 'fake-participant-2'
503
+ );
504
+ assert.equal(
505
+ locusInfo.hashTreeObjectId2ParticipantId.get('fake-ht-participant-3'),
506
+ 'fake-participant-3'
507
+ );
508
+ });
509
+
510
+ it('should process locus update correctly when called with updated MEDIASHARE objects', () => {
511
+ const newMediaShare = {
512
+ id: 'new-mediaShare-3',
513
+ htMeta: {elementId: {type: 'mediashare', id: 'fake-ht-mediaShare-3', version: 100}},
514
+ };
515
+ const updatedMediaShare2 = {
516
+ id: 'fake-media-share-2',
517
+ someNewProp: 'newValue',
518
+ htMeta: {elementId: {type: 'mediashare', id: 'fake-ht-mediaShare-2', version: 100}},
519
+ };
520
+ // simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
521
+ // with 1 participant added, 1 updated, and 1 removed
522
+ locusInfoUpdateCallback(OBJECTS_UPDATED, {
523
+ updatedObjects: [
524
+ {htMeta: {elementId: {type: 'mediashare', id: 'fake-ht-mediaShare-1'}}, data: null},
525
+ {
526
+ htMeta: {elementId: {type: 'mediashare', id: 'fake-ht-mediaShare-2'}},
527
+ data: updatedMediaShare2,
528
+ },
529
+ {
530
+ htMeta: {elementId: {type: 'mediashare', id: 'fake-ht-mediaShare-3'}},
531
+ data: newMediaShare,
532
+ },
533
+ ],
534
+ });
535
+
536
+ // check onDeltaLocus() was called with correctly updated locus info
537
+ assert.calledOnceWithExactly(onDeltaLocusStub, {
538
+ ...expectedLocusInfo,
539
+ mediaShares: [updatedMediaShare2, newMediaShare],
540
+ });
541
+ });
542
+
543
+ it('should process locus update correctly when called with a combination of various updated objects', () => {
544
+ const newSelf = {
545
+ id: 'new-self',
546
+ visibleDataSets: ['dataset1', 'dataset2'],
547
+ };
548
+ const updatedMediaShare2 = {
549
+ id: 'fake-media-share-2',
550
+ someNewProp: 'newValue',
551
+ htMeta: {elementId: {type: 'mediashare', id: 'fake-ht-mediaShare-2', version: 100}},
552
+ };
553
+ const updatedParticipant2 = {
554
+ id: 'fake-participant-2',
555
+ name: 'Updated Participant Two',
556
+ };
557
+
558
+ // simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
559
+ locusInfoUpdateCallback(OBJECTS_UPDATED, {
560
+ updatedObjects: [
561
+ {
562
+ htMeta: {elementId: {type: 'mediashare', id: 'fake-ht-mediaShare-2'}},
563
+ data: updatedMediaShare2,
564
+ },
565
+ {htMeta: {elementId: {type: 'self'}}, data: newSelf},
566
+ {
567
+ htMeta: {elementId: {type: 'participant', id: 'fake-ht-participant-2'}},
568
+ data: updatedParticipant2,
569
+ },
570
+ ],
571
+ });
572
+
573
+ // check onDeltaLocus() was called with correctly updated locus info
574
+ assert.calledOnceWithExactly(onDeltaLocusStub, {
575
+ ...expectedLocusInfo,
576
+ mediaShares: [
577
+ {
578
+ id: 'fake-media-share-1',
579
+ htMeta: {elementId: {type: 'mediashare', id: 'fake-ht-mediaShare-1', version: 1}},
580
+ },
581
+ updatedMediaShare2,
582
+ ],
583
+ participants: [updatedParticipant2],
584
+ self: newSelf,
585
+ });
586
+ });
587
+
588
+ it('should handle MEETING_ENDED correctly', () => {
589
+ const fakeMeeting = {id: 'fake-meeting-from-collection'};
590
+ const collectionGetStub = sinon
591
+ .stub(locusInfo.webex.meetings.meetingCollection, 'get')
592
+ .returns(fakeMeeting);
593
+ const destroyStub = sinon.stub(locusInfo.webex.meetings, 'destroy');
594
+
595
+ // simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
596
+ locusInfoUpdateCallback(MEETING_ENDED);
597
+
598
+ assert.calledOnceWithExactly(collectionGetStub, locusInfo.meetingId);
599
+ assert.calledOnceWithExactly(
600
+ destroyStub,
601
+ fakeMeeting,
602
+ MEETING_REMOVED_REASON.SELF_REMOVED
603
+ );
604
+ });
605
+ });
606
+ });
607
+
80
608
  describe('#updateControls', () => {
81
609
  let newControls;
82
610
 
@@ -2041,7 +2569,7 @@ describe('plugin-meetings', () => {
2041
2569
  });
2042
2570
 
2043
2571
  describe('#handleLocusAPIResponse', () => {
2044
- it('calls handleLocusDelta', () => {
2572
+ it('calls handleLocusDelta when we are not using hash trees', () => {
2045
2573
  const fakeLocus = {eventType: LOCUSEVENT.DIFFERENCE};
2046
2574
 
2047
2575
  sinon.stub(locusInfo, 'handleLocusDelta');
@@ -2050,6 +2578,24 @@ describe('plugin-meetings', () => {
2050
2578
 
2051
2579
  assert.calledWith(locusInfo.handleLocusDelta, fakeLocus, mockMeeting);
2052
2580
  });
2581
+ it('does nothing when we are using hash trees', () => {
2582
+ const fakeLocus = {eventType: LOCUSEVENT.DIFFERENCE};
2583
+ const fakeDataSets = [{name: 'dataset1', url: 'http://test.com'}];
2584
+ const responseBody = {locus: fakeLocus, dataSets: fakeDataSets};
2585
+
2586
+ // Create a mock hash tree parser
2587
+ const mockHashTreeParser = {
2588
+ handleLocusUpdate: sinon.stub(),
2589
+ };
2590
+ locusInfo.hashTreeParser = mockHashTreeParser;
2591
+
2592
+ sinon.stub(locusInfo, 'onDeltaLocus');
2593
+
2594
+ locusInfo.handleLocusAPIResponse(mockMeeting, responseBody);
2595
+
2596
+ assert.notCalled(mockHashTreeParser.handleLocusUpdate);
2597
+ assert.notCalled(locusInfo.onDeltaLocus);
2598
+ });
2053
2599
  });
2054
2600
 
2055
2601
  describe('#LocusDeltaEvents', () => {
@@ -3351,5 +3897,34 @@ describe('plugin-meetings', () => {
3351
3897
  assert.calledWith(updateLocusInfoStub.getCall(2), deltaEvents[7]);
3352
3898
  });
3353
3899
  });
3900
+
3901
+ describe('#parse', () => {
3902
+ it('handles hash tree messages correctly', () => {
3903
+ const fakeHashTreeMessage = {
3904
+ locusStateElements: [
3905
+ {
3906
+ htMeta: {elementId: {type: 'self'}},
3907
+ data: {visibleDataSets: ['dataset1']},
3908
+ },
3909
+ ],
3910
+ dataSets: [{name: 'dataset1', url: 'http://test.com'}],
3911
+ };
3912
+
3913
+ const data = {
3914
+ eventType: LOCUSEVENT.HASH_TREE_DATA_UPDATED,
3915
+ stateElementsMessage: fakeHashTreeMessage,
3916
+ };
3917
+
3918
+ // Create a mock hash tree parser
3919
+ const mockHashTreeParser = {
3920
+ handleMessage: sinon.stub(),
3921
+ };
3922
+ locusInfo.hashTreeParser = mockHashTreeParser;
3923
+
3924
+ locusInfo.parse(mockMeeting, data);
3925
+
3926
+ assert.calledOnceWithExactly(mockHashTreeParser.handleMessage, fakeHashTreeMessage);
3927
+ });
3928
+ });
3354
3929
  });
3355
3930
  });