@webex/plugin-meetings 3.12.0-next.8 → 3.12.0-task-refactor.1

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 (201) hide show
  1. package/dist/annotation/index.js +5 -14
  2. package/dist/annotation/index.js.map +1 -1
  3. package/dist/breakouts/breakout.js +1 -1
  4. package/dist/breakouts/index.js +1 -1
  5. package/dist/config.js +2 -8
  6. package/dist/config.js.map +1 -1
  7. package/dist/constants.js +6 -29
  8. package/dist/constants.js.map +1 -1
  9. package/dist/hashTree/hashTreeParser.js +29 -1563
  10. package/dist/hashTree/hashTreeParser.js.map +1 -1
  11. package/dist/hashTree/types.js +3 -13
  12. package/dist/hashTree/types.js.map +1 -1
  13. package/dist/index.js +2 -11
  14. package/dist/index.js.map +1 -1
  15. package/dist/interceptors/index.js +0 -7
  16. package/dist/interceptors/index.js.map +1 -1
  17. package/dist/interceptors/locusRouteToken.js +5 -27
  18. package/dist/interceptors/locusRouteToken.js.map +1 -1
  19. package/dist/interpretation/index.js +2 -2
  20. package/dist/interpretation/index.js.map +1 -1
  21. package/dist/interpretation/siLanguage.js +1 -1
  22. package/dist/locus-info/controlsUtils.js +3 -7
  23. package/dist/locus-info/controlsUtils.js.map +1 -1
  24. package/dist/locus-info/index.js +247 -642
  25. package/dist/locus-info/index.js.map +1 -1
  26. package/dist/locus-info/selfUtils.js +0 -1
  27. package/dist/locus-info/selfUtils.js.map +1 -1
  28. package/dist/locus-info/types.js.map +1 -1
  29. package/dist/media/MediaConnectionAwaiter.js +1 -57
  30. package/dist/media/MediaConnectionAwaiter.js.map +1 -1
  31. package/dist/media/properties.js +2 -4
  32. package/dist/media/properties.js.map +1 -1
  33. package/dist/meeting/in-meeting-actions.js +1 -7
  34. package/dist/meeting/in-meeting-actions.js.map +1 -1
  35. package/dist/meeting/index.js +1036 -1481
  36. package/dist/meeting/index.js.map +1 -1
  37. package/dist/meeting/request.js +0 -50
  38. package/dist/meeting/request.js.map +1 -1
  39. package/dist/meeting/request.type.js.map +1 -1
  40. package/dist/meeting/util.js +3 -133
  41. package/dist/meeting/util.js.map +1 -1
  42. package/dist/meetings/index.js +59 -142
  43. package/dist/meetings/index.js.map +1 -1
  44. package/dist/meetings/util.js +7 -11
  45. package/dist/meetings/util.js.map +1 -1
  46. package/dist/member/index.js +0 -10
  47. package/dist/member/index.js.map +1 -1
  48. package/dist/member/util.js +0 -10
  49. package/dist/member/util.js.map +1 -1
  50. package/dist/metrics/constants.js +1 -7
  51. package/dist/metrics/constants.js.map +1 -1
  52. package/dist/multistream/mediaRequestManager.js +60 -9
  53. package/dist/multistream/mediaRequestManager.js.map +1 -1
  54. package/dist/multistream/remoteMediaManager.js +0 -11
  55. package/dist/multistream/remoteMediaManager.js.map +1 -1
  56. package/dist/multistream/sendSlotManager.js +2 -116
  57. package/dist/multistream/sendSlotManager.js.map +1 -1
  58. package/dist/reachability/clusterReachability.js +18 -171
  59. package/dist/reachability/clusterReachability.js.map +1 -1
  60. package/dist/reachability/index.js +11 -21
  61. package/dist/reachability/index.js.map +1 -1
  62. package/dist/reachability/reachabilityPeerConnection.js +1 -1
  63. package/dist/reachability/reachabilityPeerConnection.js.map +1 -1
  64. package/dist/reactions/reactions.type.js.map +1 -1
  65. package/dist/reconnection-manager/index.js +1 -0
  66. package/dist/reconnection-manager/index.js.map +1 -1
  67. package/dist/types/common/browser-detection.d.ts +0 -1
  68. package/dist/types/common/events/events-scope.d.ts +0 -1
  69. package/dist/types/common/events/events.d.ts +0 -1
  70. package/dist/types/config.d.ts +0 -5
  71. package/dist/types/constants.d.ts +1 -24
  72. package/dist/types/hashTree/hashTreeParser.d.ts +11 -260
  73. package/dist/types/hashTree/types.d.ts +0 -20
  74. package/dist/types/index.d.ts +0 -1
  75. package/dist/types/interceptors/index.d.ts +1 -2
  76. package/dist/types/interceptors/locusRouteToken.d.ts +0 -2
  77. package/dist/types/locus-info/index.d.ts +47 -68
  78. package/dist/types/locus-info/types.d.ts +12 -28
  79. package/dist/types/media/MediaConnectionAwaiter.d.ts +1 -10
  80. package/dist/types/media/properties.d.ts +1 -2
  81. package/dist/types/meeting/in-meeting-actions.d.ts +0 -6
  82. package/dist/types/meeting/index.d.ts +7 -86
  83. package/dist/types/meeting/request.d.ts +1 -16
  84. package/dist/types/meeting/request.type.d.ts +0 -5
  85. package/dist/types/meeting/util.d.ts +0 -31
  86. package/dist/types/meeting-info/util.d.ts +0 -1
  87. package/dist/types/meeting-info/utilv2.d.ts +0 -1
  88. package/dist/types/meetings/index.d.ts +2 -4
  89. package/dist/types/member/index.d.ts +0 -1
  90. package/dist/types/member/types.d.ts +4 -4
  91. package/dist/types/member/util.d.ts +0 -5
  92. package/dist/types/metrics/constants.d.ts +0 -6
  93. package/dist/types/multistream/mediaRequestManager.d.ts +23 -0
  94. package/dist/types/multistream/sendSlotManager.d.ts +1 -23
  95. package/dist/types/reachability/clusterReachability.d.ts +3 -30
  96. package/dist/types/reactions/reactions.type.d.ts +0 -1
  97. package/dist/types/recording-controller/util.d.ts +5 -5
  98. package/dist/types/roap/index.d.ts +1 -1
  99. package/dist/webinar/index.js +163 -438
  100. package/dist/webinar/index.js.map +1 -1
  101. package/package.json +24 -26
  102. package/src/annotation/index.ts +7 -27
  103. package/src/config.ts +0 -5
  104. package/src/constants.ts +1 -30
  105. package/src/hashTree/hashTreeParser.ts +25 -1523
  106. package/src/hashTree/types.ts +1 -24
  107. package/src/index.ts +1 -8
  108. package/src/interceptors/index.ts +1 -2
  109. package/src/interceptors/locusRouteToken.ts +5 -22
  110. package/src/interpretation/index.ts +2 -2
  111. package/src/locus-info/controlsUtils.ts +0 -17
  112. package/src/locus-info/index.ts +213 -707
  113. package/src/locus-info/selfUtils.ts +0 -1
  114. package/src/locus-info/types.ts +12 -27
  115. package/src/media/MediaConnectionAwaiter.ts +1 -41
  116. package/src/media/properties.ts +1 -3
  117. package/src/meeting/in-meeting-actions.ts +0 -12
  118. package/src/meeting/index.ts +84 -461
  119. package/src/meeting/request.ts +0 -42
  120. package/src/meeting/request.type.ts +0 -6
  121. package/src/meeting/util.ts +2 -160
  122. package/src/meetings/index.ts +60 -180
  123. package/src/meetings/util.ts +9 -10
  124. package/src/member/index.ts +0 -10
  125. package/src/member/util.ts +0 -12
  126. package/src/metrics/constants.ts +0 -7
  127. package/src/multistream/mediaRequestManager.ts +54 -4
  128. package/src/multistream/remoteMediaManager.ts +0 -13
  129. package/src/multistream/sendSlotManager.ts +3 -97
  130. package/src/reachability/clusterReachability.ts +27 -153
  131. package/src/reachability/index.ts +1 -15
  132. package/src/reachability/reachabilityPeerConnection.ts +1 -3
  133. package/src/reactions/reactions.type.ts +0 -1
  134. package/src/reconnection-manager/index.ts +1 -0
  135. package/src/webinar/index.ts +6 -265
  136. package/test/unit/spec/annotation/index.ts +7 -69
  137. package/test/unit/spec/interceptors/locusRouteToken.ts +0 -44
  138. package/test/unit/spec/locus-info/controlsUtils.js +1 -56
  139. package/test/unit/spec/locus-info/index.js +90 -1457
  140. package/test/unit/spec/media/MediaConnectionAwaiter.ts +1 -41
  141. package/test/unit/spec/media/properties.ts +3 -12
  142. package/test/unit/spec/meeting/in-meeting-actions.ts +2 -8
  143. package/test/unit/spec/meeting/index.js +128 -981
  144. package/test/unit/spec/meeting/request.js +0 -70
  145. package/test/unit/spec/meeting/utils.js +26 -438
  146. package/test/unit/spec/meetings/index.js +33 -845
  147. package/test/unit/spec/meetings/utils.js +1 -51
  148. package/test/unit/spec/member/index.js +4 -28
  149. package/test/unit/spec/member/util.js +27 -65
  150. package/test/unit/spec/multistream/mediaRequestManager.ts +85 -2
  151. package/test/unit/spec/multistream/remoteMediaManager.ts +0 -30
  152. package/test/unit/spec/multistream/sendSlotManager.ts +36 -135
  153. package/test/unit/spec/reachability/clusterReachability.ts +1 -125
  154. package/test/unit/spec/reachability/index.ts +3 -26
  155. package/test/unit/spec/reconnection-manager/index.js +8 -4
  156. package/test/unit/spec/webinar/index.ts +37 -534
  157. package/dist/aiEnableRequest/index.js +0 -184
  158. package/dist/aiEnableRequest/index.js.map +0 -1
  159. package/dist/aiEnableRequest/utils.js +0 -36
  160. package/dist/aiEnableRequest/utils.js.map +0 -1
  161. package/dist/hashTree/constants.js +0 -22
  162. package/dist/hashTree/constants.js.map +0 -1
  163. package/dist/hashTree/hashTree.js +0 -533
  164. package/dist/hashTree/hashTree.js.map +0 -1
  165. package/dist/hashTree/utils.js +0 -69
  166. package/dist/hashTree/utils.js.map +0 -1
  167. package/dist/interceptors/constant.js +0 -12
  168. package/dist/interceptors/constant.js.map +0 -1
  169. package/dist/interceptors/dataChannelAuthToken.js +0 -290
  170. package/dist/interceptors/dataChannelAuthToken.js.map +0 -1
  171. package/dist/interceptors/utils.js +0 -27
  172. package/dist/interceptors/utils.js.map +0 -1
  173. package/dist/types/aiEnableRequest/index.d.ts +0 -5
  174. package/dist/types/aiEnableRequest/utils.d.ts +0 -2
  175. package/dist/types/hashTree/constants.d.ts +0 -9
  176. package/dist/types/hashTree/hashTree.d.ts +0 -136
  177. package/dist/types/hashTree/utils.d.ts +0 -22
  178. package/dist/types/interceptors/constant.d.ts +0 -5
  179. package/dist/types/interceptors/dataChannelAuthToken.d.ts +0 -43
  180. package/dist/types/interceptors/utils.d.ts +0 -1
  181. package/dist/types/webinar/utils.d.ts +0 -6
  182. package/dist/webinar/utils.js +0 -25
  183. package/dist/webinar/utils.js.map +0 -1
  184. package/src/aiEnableRequest/README.md +0 -84
  185. package/src/aiEnableRequest/index.ts +0 -170
  186. package/src/aiEnableRequest/utils.ts +0 -25
  187. package/src/hashTree/constants.ts +0 -10
  188. package/src/hashTree/hashTree.ts +0 -480
  189. package/src/hashTree/utils.ts +0 -62
  190. package/src/interceptors/constant.ts +0 -6
  191. package/src/interceptors/dataChannelAuthToken.ts +0 -170
  192. package/src/interceptors/utils.ts +0 -16
  193. package/src/webinar/utils.ts +0 -16
  194. package/test/unit/spec/aiEnableRequest/index.ts +0 -981
  195. package/test/unit/spec/aiEnableRequest/utils.ts +0 -130
  196. package/test/unit/spec/hashTree/hashTree.ts +0 -721
  197. package/test/unit/spec/hashTree/hashTreeParser.ts +0 -3670
  198. package/test/unit/spec/hashTree/utils.ts +0 -140
  199. package/test/unit/spec/interceptors/dataChannelAuthToken.ts +0 -210
  200. package/test/unit/spec/interceptors/utils.ts +0 -75
  201. package/test/unit/spec/webinar/utils.ts +0 -39
@@ -5,7 +5,7 @@ import {assert} from '@webex/test-helper-chai';
5
5
  import MockWebex from '@webex/test-helper-mock-webex';
6
6
  import testUtils from '../../../utils/testUtils';
7
7
  import Meetings from '@webex/plugin-meetings';
8
- import LocusInfo, {createLocusFromHashTreeMessage, findMeetingForHashTreeMessage} from '@webex/plugin-meetings/src/locus-info';
8
+ import LocusInfo from '@webex/plugin-meetings/src/locus-info';
9
9
  import SelfUtils from '@webex/plugin-meetings/src/locus-info/selfUtils';
10
10
  import InfoUtils from '@webex/plugin-meetings/src/locus-info/infoUtils';
11
11
  import EmbeddedAppsUtils from '@webex/plugin-meetings/src/locus-info/embeddedAppsUtils';
@@ -29,8 +29,7 @@ import {
29
29
  } from '../../../../src/constants';
30
30
 
31
31
  import {self, selfWithInactivity} from './selfConstant';
32
- import {MEETING_REMOVED_REASON} from '@webex/plugin-meetings/src/constants';
33
- import LoggerProxy from '@webex/plugin-meetings/src/common/logs/logger-proxy';
32
+ import { MEETING_REMOVED_REASON } from '@webex/plugin-meetings/src/constants';
34
33
 
35
34
  describe('plugin-meetings', () => {
36
35
  describe('LocusInfo index', () => {
@@ -105,10 +104,9 @@ describe('plugin-meetings', () => {
105
104
  });
106
105
 
107
106
  const createHashTreeMessage = (visibleDataSets) => ({
108
- locusUrl: 'http://locus-url.com',
109
107
  locusStateElements: [
110
108
  {
111
- htMeta: {elementId: {type: 'metadata'}},
109
+ htMeta: {elementId: {type: 'self'}},
112
110
  data: {visibleDataSets},
113
111
  },
114
112
  ],
@@ -116,7 +114,6 @@ describe('plugin-meetings', () => {
116
114
  });
117
115
 
118
116
  const createLocusWithVisibleDataSets = (visibleDataSets) => ({
119
- url: 'http://locus-url.com',
120
117
  self: {visibleDataSets},
121
118
  participants: [],
122
119
  links: {
@@ -139,12 +136,8 @@ describe('plugin-meetings', () => {
139
136
  HashTreeParserStub,
140
137
  sinon.match({
141
138
  initialLocus: {
142
- locus: null,
143
- dataSets: hashTreeMessage.dataSets,
144
- },
145
- metadata: {
146
- htMeta: hashTreeMessage.locusStateElements[0].htMeta,
147
- visibleDataSets,
139
+ locus: {self: {visibleDataSets}},
140
+ dataSets: [],
148
141
  },
149
142
  webexRequest: sinon.match.func,
150
143
  locusInfoUpdateCallback: sinon.match.func,
@@ -176,16 +169,11 @@ describe('plugin-meetings', () => {
176
169
  const visibleDataSets = ['dataset1', 'dataset2'];
177
170
  const locus = createLocusWithVisibleDataSets(visibleDataSets);
178
171
  const dataSets = [{name: 'dataset1', url: 'http://dataset-url.com'}];
179
- const metadata = {
180
- htMeta: {elementId: {type: 'metadata'}},
181
- visibleDataSets,
182
- };
183
172
 
184
173
  await locusInfo.initialSetup({
185
174
  trigger: 'join-response',
186
175
  locus,
187
176
  dataSets,
188
- metadata,
189
177
  });
190
178
 
191
179
  assert.calledOnceWithExactly(
@@ -195,7 +183,6 @@ describe('plugin-meetings', () => {
195
183
  locus,
196
184
  dataSets,
197
185
  },
198
- metadata,
199
186
  webexRequest: sinon.match.func,
200
187
  locusInfoUpdateCallback: sinon.match.func,
201
188
  debugId: sinon.match.string,
@@ -233,13 +220,12 @@ describe('plugin-meetings', () => {
233
220
  HashTreeParserStub,
234
221
  sinon.match({
235
222
  initialLocus: {
236
- locus: null,
223
+ locus: {self: {visibleDataSets}},
237
224
  dataSets: [],
238
225
  },
239
226
  webexRequest: sinon.match.func,
240
227
  locusInfoUpdateCallback: sinon.match.func,
241
228
  debugId: sinon.match.string,
242
- metadata: null,
243
229
  })
244
230
  );
245
231
  assert.calledOnceWithExactly(mockHashTreeParser.initializeFromGetLociResponse, locus);
@@ -263,30 +249,6 @@ describe('plugin-meetings', () => {
263
249
  assert.isTrue(locusInfo.emitChange);
264
250
  });
265
251
 
266
- it('throws if called with "locus-message" and Metadata object without visibleDataSets', async () => {
267
- const hashTreeMessage = {
268
- locusStateElements: [
269
- {
270
- htMeta: {elementId: {type: 'Metadata'}},
271
- data: {},
272
- },
273
- ],
274
- dataSets: [{name: 'dataset1', url: 'test-url'}],
275
- };
276
- try {
277
- await locusInfo.initialSetup({
278
- trigger: 'locus-message',
279
- hashTreeMessage,
280
- });
281
- assert.fail('should have thrown an error');
282
- } catch (error) {
283
- assert.equal(
284
- error.message,
285
- 'Metadata object with visibleDataSets is missing in the message'
286
- );
287
- }
288
- });
289
-
290
252
  describe('should setup correct locusInfoUpdateCallback when creating HashTreeParser', () => {
291
253
  const OBJECTS_UPDATED = HashTreeParserModule.LocusInfoUpdateType.OBJECTS_UPDATED;
292
254
  const MEETING_ENDED = HashTreeParserModule.LocusInfoUpdateType.MEETING_ENDED;
@@ -301,11 +263,10 @@ describe('plugin-meetings', () => {
301
263
  await locusInfo.initialSetup({
302
264
  trigger: 'locus-message',
303
265
  hashTreeMessage: {
304
- locusUrl: 'fake-locus-url',
305
266
  locusStateElements: [
306
267
  {
307
- htMeta: {elementId: {type: 'Metadata'}},
308
- data: {visibleDataSets: [{name: 'dataset1', url: 'test-url'}]},
268
+ htMeta: {elementId: {type: 'self'}},
269
+ data: {visibleDataSets: ['dataset1']},
309
270
  },
310
271
  ],
311
272
  dataSets: [{name: 'dataset1', url: 'test-url'}],
@@ -332,16 +293,6 @@ describe('plugin-meetings', () => {
332
293
  htMeta: {elementId: {type: 'mediashare', id: 'fake-ht-mediaShare-2', version: 1}},
333
294
  },
334
295
  ];
335
- locusInfo.embeddedApps = [
336
- {
337
- id: 'fake-embedded-app-1',
338
- htMeta: {elementId: {type: 'embeddedapp', id: 'fake-ht-embeddedApp-1', version: 1}},
339
- },
340
- {
341
- id: 'fake-embedded-app-2',
342
- htMeta: {elementId: {type: 'embeddedapp', id: 'fake-ht-embeddedApp-2', version: 1}},
343
- },
344
- ];
345
296
  locusInfo.meetings = {id: 'fake-meetings'};
346
297
  locusInfo.participants = [
347
298
  {id: 'fake-participant-1', name: 'Participant One'},
@@ -360,13 +311,6 @@ describe('plugin-meetings', () => {
360
311
  locusInfo.url = 'fake-locus-url';
361
312
  locusInfo.htMeta = {elementId: {type: 'locus', id: 'fake-ht-locus-id', version: 1}};
362
313
 
363
- const createdHashTreeParser = locusInfo.hashTreeParsers.get('fake-locus-url');
364
-
365
- assert.isDefined(createdHashTreeParser);
366
- // this flag would have been set to true on the first callback triggered by initialSetup() wa called earlier
367
- // it's not because we're mocking HashTreeParser, so we have to set it manually here
368
- createdHashTreeParser.initializedFromHashTree = true;
369
-
370
314
  // setup the default expected locus info state that each test builds upon
371
315
  expectedLocusInfo = {
372
316
  controls: {id: 'fake-controls'},
@@ -384,18 +328,8 @@ describe('plugin-meetings', () => {
384
328
  htMeta: {elementId: {type: 'mediashare', id: 'fake-ht-mediaShare-2', version: 1}},
385
329
  },
386
330
  ],
387
- embeddedApps: [
388
- {
389
- id: 'fake-embedded-app-1',
390
- htMeta: {elementId: {type: 'embeddedapp', id: 'fake-ht-embeddedApp-1', version: 1}},
391
- },
392
- {
393
- id: 'fake-embedded-app-2',
394
- htMeta: {elementId: {type: 'embeddedapp', id: 'fake-ht-embeddedApp-2', version: 1}},
395
- },
396
- ],
397
331
  meetings: {id: 'fake-meetings'},
398
- jsSdkMeta: {removedParticipantIds: [], forceReplaceMembers: false},
332
+ jsSdkMeta: {removedParticipantIds: []},
399
333
  participants: [], // empty means there were no participant updates
400
334
  replaces: {id: 'fake-replaces'},
401
335
  self: {id: 'fake-self'},
@@ -424,134 +358,11 @@ describe('plugin-meetings', () => {
424
358
  });
425
359
  });
426
360
 
427
- it('should process locus update correctly when called with updated SELF (webinar non-attendee)', () => {
428
- const newSelf = {
429
- id: 'new-self',
430
- visibleDataSets: ['dataset1', 'dataset2'],
431
- controls: {
432
- role: {
433
- roles: [
434
- {type: 'PANELIST', hasRole: true},
435
- {type: 'ATTENDEE', hasRole: false},
436
- ],
437
- },
438
- },
439
- };
440
- locusInfo.info.isWebinar = true;
441
-
442
- // simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
443
- locusInfoUpdateCallback(OBJECTS_UPDATED, {
444
- updatedObjects: [{htMeta: {elementId: {type: 'self'}}, data: newSelf}],
445
- });
446
-
447
- // check onDeltaLocus() was called with correctly updated locus info
448
- // without any participant generated
449
- assert.calledOnceWithExactly(onDeltaLocusStub, {
450
- ...expectedLocusInfo,
451
- info: {
452
- ...expectedLocusInfo.info,
453
- isWebinar: true,
454
- },
455
- self: newSelf,
456
- });
457
- });
458
-
459
- it('should generate a participant when called with updated SELF for webinar attendee', () => {
460
- const newSelf = {
461
- id: 'new-self',
462
- visibleDataSets: ['dataset1', 'dataset2'],
463
- controls: {
464
- role: {
465
- roles: [
466
- {type: 'something else - should be ignored', hasRole: true},
467
- {type: 'ATTENDEE', hasRole: true},
468
- ],
469
- },
470
- },
471
- };
472
-
473
- locusInfo.info.isWebinar = true;
474
-
475
- // simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
476
- locusInfoUpdateCallback(OBJECTS_UPDATED, {
477
- updatedObjects: [{htMeta: {elementId: {type: 'self'}}, data: newSelf}],
478
- });
479
-
480
- // check onDeltaLocus() was called with correctly updated locus info
481
- // that contains a participant created from self
482
- assert.calledOnceWithExactly(onDeltaLocusStub, {
483
- ...expectedLocusInfo,
484
- info: {
485
- ...expectedLocusInfo.info,
486
- isWebinar: true,
487
- },
488
- self: newSelf,
489
- participants: [
490
- {
491
- ...newSelf,
492
- },
493
- ],
494
- });
495
- });
496
-
497
- it('should process locus update correctly when called with updated fullState', () => {
498
- const newFullState = {
499
- id: 'new-fullState',
500
- visibleDataSets: ['dataset1', 'dataset2'],
501
- };
502
-
503
- // simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
504
- locusInfoUpdateCallback(OBJECTS_UPDATED, {
505
- updatedObjects: [{htMeta: {elementId: {type: 'fullState'}}, data: newFullState}],
506
- });
507
-
508
- // check onDeltaLocus() was called with correctly updated locus info
509
- assert.calledOnceWithExactly(onDeltaLocusStub, {
510
- ...expectedLocusInfo,
511
- fullState: newFullState,
512
- });
513
- });
514
-
515
- it('should process locus update correctly when called with updated info', () => {
516
- const newInfo = {
517
- id: 'new-info',
518
- visibleDataSets: ['dataset1', 'dataset2'],
519
- };
520
-
521
- // simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
522
- locusInfoUpdateCallback(OBJECTS_UPDATED, {
523
- updatedObjects: [{htMeta: {elementId: {type: 'info'}}, data: newInfo}],
524
- });
525
-
526
- // check onDeltaLocus() was called with correctly updated locus info
527
- assert.calledOnceWithExactly(onDeltaLocusStub, {
528
- ...expectedLocusInfo,
529
- info: newInfo,
530
- });
531
- });
532
-
533
- it('should process locus update correctly when called with updated links', () => {
534
- const newLinks = {
535
- id: 'new-links',
536
- visibleDataSets: ['dataset1', 'dataset2'],
537
- };
538
-
539
- // simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
540
- locusInfoUpdateCallback(OBJECTS_UPDATED, {
541
- updatedObjects: [{htMeta: {elementId: {type: 'links'}}, data: newLinks}],
542
- });
543
-
544
- // check onDeltaLocus() was called with correctly updated locus info
545
- assert.calledOnceWithExactly(onDeltaLocusStub, {
546
- ...expectedLocusInfo,
547
- links: newLinks,
548
- });
549
- });
550
-
551
361
  it('should process locus update correctly when called with updated LOCUS object', () => {
552
362
  // setup new updated locus that has many things missing
553
363
  const newLocusHtMeta = {elementId: {type: 'locus', version: 42}};
554
364
  const newLocus = {
365
+ conntrols: 'new-controls',
555
366
  host: 'new-host',
556
367
  htMeta: newLocusHtMeta,
557
368
  };
@@ -563,67 +374,10 @@ describe('plugin-meetings', () => {
563
374
 
564
375
  // check onDeltaLocus() was called with correctly updated locus info
565
376
  assert.calledOnceWithExactly(onDeltaLocusStub, {
566
- // these fields are not part of Locus object, so should keep their old values:
567
- controls: {id: 'fake-controls'},
568
- info: {id: 'fake-info'},
569
- fullState: {id: 'fake-full-state'},
570
- self: {id: 'fake-self'},
571
- links: {id: 'fake-links'},
572
- mediaShares: expectedLocusInfo.mediaShares,
573
- embeddedApps: expectedLocusInfo.embeddedApps,
574
- // and now the new fields
575
377
  ...newLocus,
576
378
  htMeta: newLocusHtMeta,
577
379
  participants: [], // empty means there were no participant updates
578
- jsSdkMeta: {removedParticipantIds: [], forceReplaceMembers: false}, // no participants were removed
579
- });
580
- });
581
-
582
- // this test is checking that we cope with an edge case if Locus
583
- // sends us something that they shouldn't
584
- it('should process locus update correctly when called with updated LOCUS object that contains info/fullState/self/participants etc', () => {
585
- // setup new updated locus that has many things missing
586
- const newLocusHtMeta = {elementId: {type: 'locus', version: 42}};
587
- const newLocus = {
588
- host: 'new-host',
589
- htMeta: newLocusHtMeta,
590
- };
591
-
592
- // simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
593
- locusInfoUpdateCallback(OBJECTS_UPDATED, {
594
- updatedObjects: [
595
- {
596
- htMeta: newLocusHtMeta,
597
- data: {
598
- ...newLocus,
599
- // all these fields below should be ignored and not override the existing ones in our "old" Locus
600
- controls: {id: 'new-controls'},
601
- info: 'new-info',
602
- fullState: 'new-fullState',
603
- self: 'new-self',
604
- participants: 'new-participants',
605
- mediaShares: 'new-mediaShares',
606
- embeddedApps: 'new-embeddedApps',
607
- },
608
- },
609
- ],
610
- });
611
-
612
- // check onDeltaLocus() was called with correctly updated locus info
613
- // with old values for the fields that should be ignored (like "info" or "fullState")
614
- assert.calledOnceWithExactly(onDeltaLocusStub, {
615
- // these fields have the "old" values:
616
- controls: {id: 'fake-controls'},
617
- info: {id: 'fake-info'},
618
- fullState: {id: 'fake-full-state'},
619
- self: {id: 'fake-self'},
620
- links: {id: 'fake-links'},
621
- mediaShares: expectedLocusInfo.mediaShares,
622
- embeddedApps: expectedLocusInfo.embeddedApps,
623
- participants: [], // empty means there were no participant updates
624
- jsSdkMeta: {removedParticipantIds: [], forceReplaceMembers: false}, // no participants were removed
625
- ...newLocus,
626
- htMeta: newLocusHtMeta,
380
+ jsSdkMeta: {removedParticipantIds: []}, // no participants were removed
627
381
  });
628
382
  });
629
383
 
@@ -648,19 +402,10 @@ describe('plugin-meetings', () => {
648
402
 
649
403
  // check onDeltaLocus() was called with correctly updated locus info
650
404
  assert.calledOnceWithExactly(onDeltaLocusStub, {
651
- // these fields are not part of Locus object, so should keep their old values:
652
- controls: {id: 'fake-controls'},
653
- info: {id: 'fake-info'},
654
- fullState: {id: 'fake-full-state'},
655
- self: {id: 'fake-self'},
656
- links: {id: 'fake-links'},
657
- mediaShares: expectedLocusInfo.mediaShares,
658
- embeddedApps: expectedLocusInfo.embeddedApps,
659
- // and now the new fields
660
405
  ...newLocus,
661
406
  htMeta: newLocusHtMeta,
662
407
  participants: [], // empty means there were no participant updates
663
- jsSdkMeta: {removedParticipantIds: [], forceReplaceMembers: false}, // no participants were removed
408
+ jsSdkMeta: {removedParticipantIds: []}, // no participants were removed
664
409
  });
665
410
  });
666
411
 
@@ -748,7 +493,7 @@ describe('plugin-meetings', () => {
748
493
  assert.calledOnceWithExactly(onDeltaLocusStub, {
749
494
  ...expectedLocusInfo,
750
495
  participants: [newParticipant, updatedParticipant2],
751
- jsSdkMeta: {removedParticipantIds: ['fake-participant-1'], forceReplaceMembers: false},
496
+ jsSdkMeta: {removedParticipantIds: ['fake-participant-1']},
752
497
  });
753
498
  // and that the hashTreeObjectId2ParticipantId map was updated correctly
754
499
  assert.isUndefined(locusInfo.hashTreeObjectId2ParticipantId.get('fake-ht-participant-1'));
@@ -795,39 +540,6 @@ describe('plugin-meetings', () => {
795
540
  });
796
541
  });
797
542
 
798
- it('should process locus update correctly when called with updated EMBEDDEDAPP objects', () => {
799
- const newEmbeddedApp = {
800
- id: 'new-embedded-app-3',
801
- htMeta: {elementId: {type: 'embeddedapp', id: 'fake-ht-embeddedApp-3', version: 100}},
802
- };
803
- const updatedEmbeddedApp2 = {
804
- id: 'fake-embedded-app-2',
805
- someNewProp: 'newValue',
806
- htMeta: {elementId: {type: 'embeddedapp', id: 'fake-ht-embeddedApp-2', version: 100}},
807
- };
808
- // simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
809
- // with 1 embedded app added, 1 updated, and 1 removed
810
- locusInfoUpdateCallback(OBJECTS_UPDATED, {
811
- updatedObjects: [
812
- {htMeta: {elementId: {type: 'embeddedapp', id: 'fake-ht-embeddedApp-1'}}, data: null},
813
- {
814
- htMeta: {elementId: {type: 'embeddedapp', id: 'fake-ht-embeddedApp-2'}},
815
- data: updatedEmbeddedApp2,
816
- },
817
- {
818
- htMeta: {elementId: {type: 'embeddedapp', id: 'fake-ht-embeddedApp-3'}},
819
- data: newEmbeddedApp,
820
- },
821
- ],
822
- });
823
-
824
- // check onDeltaLocus() was called with correctly updated locus info
825
- assert.calledOnceWithExactly(onDeltaLocusStub, {
826
- ...expectedLocusInfo,
827
- embeddedApps: [updatedEmbeddedApp2, newEmbeddedApp],
828
- });
829
- });
830
-
831
543
  it('should process locus update correctly when called with a combination of various updated objects', () => {
832
544
  const newSelf = {
833
545
  id: 'new-self',
@@ -873,60 +585,6 @@ describe('plugin-meetings', () => {
873
585
  });
874
586
  });
875
587
 
876
- it('should process locus update correctly when called with multiple CONTROL object updates', () => {
877
- const firstControl = {
878
- muteOnEntry: {enabled: true},
879
- lock: {locked: true, meta: {lastModified: 'YESTERDAY', modifiedBy: 'John Doe'}},
880
- };
881
- const secondControl = {
882
- reactions: {enabled: true},
883
- };
884
-
885
- // simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
886
- locusInfoUpdateCallback(OBJECTS_UPDATED, {
887
- updatedObjects: [
888
- {
889
- htMeta: {elementId: {type: 'controlentry', id: 'control-1'}},
890
- data: firstControl,
891
- },
892
- {
893
- htMeta: {elementId: {type: 'controlentry', id: 'control-2'}},
894
- data: secondControl,
895
- },
896
- ],
897
- });
898
-
899
- // check onDeltaLocus() was called with correctly updated locus info
900
- // all keys from both controls should be merged into the controls object
901
- assert.calledOnceWithExactly(onDeltaLocusStub, {
902
- ...expectedLocusInfo,
903
- controls: {
904
- id: 'fake-controls',
905
- muteOnEntry: {enabled: true},
906
- lock: {locked: true, meta: {lastModified: 'YESTERDAY', modifiedBy: 'John Doe'}},
907
- reactions: {enabled: true},
908
- },
909
- });
910
- });
911
-
912
- it('should process locus update correctly when CONTROL object is received with no data', () => {
913
- // simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
914
- locusInfoUpdateCallback(OBJECTS_UPDATED, {
915
- updatedObjects: [
916
- {
917
- htMeta: {elementId: {type: 'controlentry', id: 'some-control-id'}},
918
- data: null,
919
- },
920
- ],
921
- });
922
-
923
- // check onDeltaLocus() was called with correctly updated locus info
924
- // when data is null, it should be ignored and not change the controls
925
- assert.calledOnceWithExactly(onDeltaLocusStub, {
926
- ...expectedLocusInfo,
927
- });
928
- });
929
-
930
588
  it('should handle MEETING_ENDED correctly', () => {
931
589
  const fakeMeeting = {id: 'fake-meeting-from-collection'};
932
590
  const collectionGetStub = sinon
@@ -944,78 +602,6 @@ describe('plugin-meetings', () => {
944
602
  MEETING_REMOVED_REASON.SELF_REMOVED
945
603
  );
946
604
  });
947
-
948
- // this could happen if meeting gets destroyed while we're doing some async hash tree operation like a sync
949
- it('should handle MEETING_ENDED correctly when meeting is not found in the collection', () => {
950
- const collectionGetStub = sinon
951
- .stub(locusInfo.webex.meetings.meetingCollection, 'get')
952
- .returns(null);
953
- const destroyStub = sinon.stub(locusInfo.webex.meetings, 'destroy');
954
-
955
- // simulate an update from the HashTreeParser (normally this would be triggered by incoming locus messages)
956
- locusInfoUpdateCallback(MEETING_ENDED);
957
-
958
- assert.calledOnceWithExactly(collectionGetStub, locusInfo.meetingId);
959
- assert.notCalled(destroyStub);
960
- });
961
-
962
- it('should set forceReplaceMembers to true on the first update for a locusUrl (initializedFromHashTree is false)', () => {
963
- const createdHashTreeParser = locusInfo.hashTreeParsers.get('fake-locus-url');
964
- createdHashTreeParser.initializedFromHashTree = false;
965
-
966
- locusInfoUpdateCallback(OBJECTS_UPDATED, {
967
- updatedObjects: [
968
- {
969
- htMeta: {elementId: {type: 'self'}},
970
- data: {id: 'new-self'},
971
- },
972
- ],
973
- });
974
-
975
- assert.calledOnce(onDeltaLocusStub);
976
- assert.equal(onDeltaLocusStub.firstCall.args[0].jsSdkMeta.forceReplaceMembers, true);
977
- assert.isTrue(createdHashTreeParser.initializedFromHashTree);
978
- });
979
-
980
- it('should set forceReplaceMembers to false on subsequent updates (initializedFromHashTree is true)', () => {
981
- locusInfoUpdateCallback(OBJECTS_UPDATED, {
982
- updatedObjects: [
983
- {
984
- htMeta: {elementId: {type: 'self'}},
985
- data: {id: 'new-self'},
986
- },
987
- ],
988
- });
989
-
990
- assert.calledOnce(onDeltaLocusStub);
991
- assert.equal(onDeltaLocusStub.firstCall.args[0].jsSdkMeta.forceReplaceMembers, false);
992
- });
993
-
994
- it('should copy participant data to self when participant matches self identity and state is LEFT with reason MOVED', () => {
995
- locusInfo.self = {id: 'fake-self', identity: 'user-123'};
996
-
997
- locusInfoUpdateCallback(OBJECTS_UPDATED, {
998
- updatedObjects: [
999
- {
1000
- htMeta: {elementId: {type: 'participant', id: 99}},
1001
- data: {
1002
- id: 'participant-matching-self',
1003
- identity: 'user-123',
1004
- state: 'LEFT',
1005
- reason: 'MOVED',
1006
- roles: ['MODERATOR'],
1007
- },
1008
- },
1009
- ],
1010
- });
1011
-
1012
- assert.calledOnce(onDeltaLocusStub);
1013
- const passedLocus = onDeltaLocusStub.firstCall.args[0];
1014
-
1015
- assert.equal(passedLocus.self.identity, 'user-123');
1016
- assert.equal(passedLocus.self.state, 'LEFT');
1017
- assert.equal(passedLocus.self.reason, 'MOVED');
1018
- });
1019
605
  });
1020
606
  });
1021
607
 
@@ -1251,7 +837,7 @@ describe('plugin-meetings', () => {
1251
837
  it('should trigger the CONTROLS_POLLING_QA_CHANGED event when necessary', () => {
1252
838
  locusInfo.controls = {};
1253
839
  locusInfo.emitScoped = sinon.stub();
1254
- newControls.pollingQAControl = {enabled: true};
840
+ newControls.pollingQAControl = { enabled: true };
1255
841
  locusInfo.updateControls(newControls);
1256
842
 
1257
843
  assert.calledWith(
@@ -1513,62 +1099,6 @@ describe('plugin-meetings', () => {
1513
1099
  );
1514
1100
  });
1515
1101
 
1516
- it('should update the hesiod llm id', () => {
1517
- locusInfo.emitScoped = sinon.stub();
1518
- locusInfo.controls = {
1519
- transcribe: {
1520
- transcribing: false,
1521
- caption: true,
1522
- hesiodLlmId: '123a-456b-789c',
1523
- },
1524
- };
1525
- newControls.transcribe.transcribing = false;
1526
- newControls.transcribe.caption = true;
1527
- newControls.transcribe.hesiodLlmId = '789d-456e-123f';
1528
-
1529
- locusInfo.updateControls(newControls);
1530
-
1531
- assert.calledWith(
1532
- locusInfo.emitScoped,
1533
- {
1534
- file: 'locus-info',
1535
- function: 'updateControls',
1536
- },
1537
- LOCUSINFO.EVENTS.CONTROLS_MEETING_HESIOD_LLM_ID_UPDATED,
1538
- {
1539
- hesiodLlmId: '789d-456e-123f',
1540
- }
1541
- );
1542
- });
1543
-
1544
- it('should emit CONTROLS_AI_SUMMARY_NOTIFICATION_UPDATED when aiSummaryNotification changes', () => {
1545
- locusInfo.emitScoped = sinon.stub();
1546
- locusInfo.controls = {
1547
- transcribe: {
1548
- transcribing: false,
1549
- caption: false,
1550
- aiSummaryNotification: false,
1551
- },
1552
- };
1553
- newControls.transcribe.transcribing = false;
1554
- newControls.transcribe.caption = false;
1555
- newControls.transcribe.aiSummaryNotification = true;
1556
-
1557
- locusInfo.updateControls(newControls);
1558
-
1559
- assert.calledWith(
1560
- locusInfo.emitScoped,
1561
- {
1562
- file: 'locus-info',
1563
- function: 'updateControls',
1564
- },
1565
- LOCUSINFO.EVENTS.CONTROLS_AI_SUMMARY_NOTIFICATION_UPDATED,
1566
- {
1567
- aiSummaryNotification: true,
1568
- }
1569
- );
1570
- });
1571
-
1572
1102
  it('should update the transcribe spoken language', () => {
1573
1103
  locusInfo.emitScoped = sinon.stub();
1574
1104
  locusInfo.controls = {
@@ -1834,6 +1364,7 @@ describe('plugin-meetings', () => {
1834
1364
  );
1835
1365
  });
1836
1366
 
1367
+
1837
1368
  it('should call with participant display name', () => {
1838
1369
  const failureParticipant = [
1839
1370
  {
@@ -1858,7 +1389,7 @@ describe('plugin-meetings', () => {
1858
1389
  displayName: 'Test User',
1859
1390
  }
1860
1391
  );
1861
- });
1392
+ })
1862
1393
  });
1863
1394
 
1864
1395
  describe('#updateSelf', () => {
@@ -2659,8 +2190,8 @@ describe('plugin-meetings', () => {
2659
2190
  {
2660
2191
  isInitializing: !self,
2661
2192
  }
2662
- );
2663
- });
2193
+ );
2194
+ });
2664
2195
 
2665
2196
  const checkMeetingInfoUpdatedCalled = (expected, payload) => {
2666
2197
  const expectedArgs = [
@@ -3008,359 +2539,32 @@ describe('plugin-meetings', () => {
3008
2539
 
3009
2540
  const clonedApps = cloneDeep(newEmbeddedApps);
3010
2541
 
3011
- locusInfo.updateEmbeddedApps(clonedApps);
3012
-
3013
- assert.notCalled(locusInfo.emitScoped);
3014
- });
3015
-
3016
- it('emits EMBEDDED_APPS_UPDATED when apps changed', () => {
3017
- locusInfo.updateEmbeddedApps(newEmbeddedApps);
3018
-
3019
- locusInfo.emitScoped = sinon.stub();
3020
-
3021
- const clonedApps = cloneDeep(newEmbeddedApps);
3022
-
3023
- clonedApps[0].state = 'STOPPED';
3024
- const expectedApps = EmbeddedAppsUtils.parse(clonedApps);
3025
-
3026
- locusInfo.updateEmbeddedApps(clonedApps);
3027
-
3028
- assert.calledWith(
3029
- locusInfo.emitScoped,
3030
- {
3031
- file: 'locus-info',
3032
- function: 'updateEmbeddedApps',
3033
- },
3034
- LOCUSINFO.EVENTS.EMBEDDED_APPS_UPDATED,
3035
- expectedApps
3036
- );
3037
- });
3038
- });
3039
-
3040
- describe('#createHashTreeParser', () => {
3041
- let HashTreeParserStub;
3042
-
3043
- beforeEach(() => {
3044
- HashTreeParserStub = sinon
3045
- .stub(HashTreeParserModule, 'default')
3046
- .returns({
3047
- initializeFromMessage: sinon.stub().resolves(),
3048
- initializeFromGetLociResponse: sinon.stub().resolves(),
3049
- state: 'active',
3050
- stop: sinon.stub(),
3051
- handleMessage: sinon.stub(),
3052
- });
3053
- });
3054
-
3055
- const setupParserViaInitialSetup = async (locusUrl = 'http://locus-url-A.com') => {
3056
- await locusInfo.initialSetup({
3057
- trigger: 'locus-message',
3058
- hashTreeMessage: {
3059
- locusUrl,
3060
- locusStateElements: [
3061
- {
3062
- htMeta: {elementId: {type: 'Metadata'}},
3063
- data: {visibleDataSets: [{name: 'dataset1', url: 'test-url'}]},
3064
- },
3065
- ],
3066
- dataSets: [{name: 'dataset1', url: 'test-url'}],
3067
- },
3068
- });
3069
- };
3070
-
3071
- it('should stop existing active parsers when creating a new one', async () => {
3072
- await setupParserViaInitialSetup('http://locus-url-A.com');
3073
-
3074
- const firstParser = locusInfo.hashTreeParsers.get('http://locus-url-A.com').parser;
3075
-
3076
- await setupParserViaInitialSetup('http://locus-url-B.com');
3077
-
3078
- assert.calledOnce(firstParser.stop);
3079
- });
3080
-
3081
- it('should set replacedAt on existing entries when replacedAt is provided', async () => {
3082
- await setupParserViaInitialSetup('http://locus-url-A.com');
3083
-
3084
- // Call createHashTreeParser with replacedAt via parse -> handleHashTreeParserSwitch
3085
- // which calls createHashTreeParser with replacedAt from the self element
3086
- locusInfo.webex.internal.device.url = 'http://device-url.com';
3087
- const message = {
3088
- locusUrl: 'http://locus-url-B.com',
3089
- locusStateElements: [
3090
- {
3091
- htMeta: {elementId: {type: 'Metadata'}},
3092
- data: {visibleDataSets: [{name: 'dataset1', url: 'test-url'}]},
3093
- },
3094
- {
3095
- htMeta: {elementId: {type: 'Self'}},
3096
- data: {
3097
- devices: [{url: 'http://device-url.com', replaces: [{locusUrl: 'http://locus-url-A.com', replacedAt: '2026-01-01T00:00:00Z'}]}],
3098
- },
3099
- },
3100
- ],
3101
- dataSets: [{name: 'dataset1', url: 'test-url'}],
3102
- };
3103
-
3104
- locusInfo.parse(mockMeeting, {
3105
- eventType: LOCUSEVENT.HASH_TREE_DATA_UPDATED,
3106
- stateElementsMessage: message,
3107
- });
3108
-
3109
- assert.equal(locusInfo.hashTreeParsers.get('http://locus-url-A.com').replacedAt, '2026-01-01T00:00:00Z');
3110
- });
3111
-
3112
- it('should not set replacedAt on existing entries when replacedAt is not provided', async () => {
3113
- await setupParserViaInitialSetup('http://locus-url-A.com');
3114
-
3115
- await setupParserViaInitialSetup('http://locus-url-B.com');
3116
-
3117
- assert.isUndefined(locusInfo.hashTreeParsers.get('http://locus-url-A.com').replacedAt);
3118
- });
3119
-
3120
- it('should store the new parser in hashTreeParsers map with the correct locusUrl key', async () => {
3121
- await setupParserViaInitialSetup('http://locus-url-A.com');
3122
-
3123
- assert.isTrue(locusInfo.hashTreeParsers.has('http://locus-url-A.com'));
3124
- assert.isDefined(locusInfo.hashTreeParsers.get('http://locus-url-A.com').parser);
3125
- });
3126
-
3127
- it('should clear hashTreeObjectId2ParticipantId when creating a new parser', async () => {
3128
- await setupParserViaInitialSetup('http://locus-url-A.com');
3129
- locusInfo.hashTreeObjectId2ParticipantId.set(1, 'participant-1');
3130
-
3131
- await setupParserViaInitialSetup('http://locus-url-B.com');
3132
-
3133
- assert.equal(locusInfo.hashTreeObjectId2ParticipantId.size, 0);
3134
- });
3135
-
3136
- it('should not stop already stopped parsers', async () => {
3137
- await setupParserViaInitialSetup('http://locus-url-A.com');
3138
- const firstParser = locusInfo.hashTreeParsers.get('http://locus-url-A.com').parser;
3139
- firstParser.state = 'stopped';
3140
-
3141
- await setupParserViaInitialSetup('http://locus-url-B.com');
3142
-
3143
- assert.notCalled(firstParser.stop);
3144
- });
3145
- });
3146
-
3147
- describe('#handleHashTreeParserSwitch', () => {
3148
- const deviceUrl = 'http://device-url.com';
3149
- const locusUrlA = 'http://locus-url-A.com';
3150
- const locusUrlB = 'http://locus-url-B.com';
3151
-
3152
- let HashTreeParserStub;
3153
-
3154
- const createMockParser = (state = 'active') => ({
3155
- state,
3156
- stop: sinon.stub(),
3157
- resume: sinon.stub(),
3158
- handleMessage: sinon.stub(),
3159
- });
3160
-
3161
- const createSelfElementWithReplaces = (replacedLocusUrl, replacedAt) => ({
3162
- htMeta: {elementId: {type: 'Self'}},
3163
- data: {
3164
- devices: [{url: deviceUrl, replaces: [{locusUrl: replacedLocusUrl, replacedAt}]}],
3165
- },
3166
- });
3167
-
3168
- const createMetadataElement = () => ({
3169
- htMeta: {elementId: {type: 'Metadata'}},
3170
- data: {visibleDataSets: [{name: 'dataset1', url: 'test-url'}]},
3171
- });
3172
-
3173
- beforeEach(() => {
3174
- locusInfo.webex.internal.device.url = deviceUrl;
3175
- HashTreeParserStub = sinon
3176
- .stub(HashTreeParserModule, 'default')
3177
- .returns(createMockParser());
3178
- });
3179
-
3180
- it('should create a new parser when no entry exists for locusUrl and metadata has visibleDataSets', () => {
3181
- // set up an existing parser for a different url
3182
- locusInfo.hashTreeParsers.set(locusUrlA, {parser: createMockParser(), initializedFromHashTree: true});
3183
-
3184
- const message = {
3185
- locusUrl: locusUrlB,
3186
- locusStateElements: [createMetadataElement()],
3187
- dataSets: [{name: 'dataset1', url: 'test-url'}],
3188
- };
3189
-
3190
- locusInfo.parse(mockMeeting, {
3191
- eventType: LOCUSEVENT.HASH_TREE_DATA_UPDATED,
3192
- stateElementsMessage: message,
3193
- });
3194
-
3195
- assert.isTrue(locusInfo.hashTreeParsers.has(locusUrlB));
3196
- });
3197
-
3198
- it('should return true when no entry exists even if no metadata is available', () => {
3199
- locusInfo.hashTreeParsers.set(locusUrlA, {parser: createMockParser(), initializedFromHashTree: true});
3200
- const parserA = locusInfo.hashTreeParsers.get(locusUrlA).parser;
3201
-
3202
- const message = {
3203
- locusUrl: locusUrlB,
3204
- locusStateElements: [],
3205
- dataSets: [],
3206
- };
3207
-
3208
- locusInfo.parse(mockMeeting, {
3209
- eventType: LOCUSEVENT.HASH_TREE_DATA_UPDATED,
3210
- stateElementsMessage: message,
3211
- });
3212
-
3213
- // no new parser created since no metadata
3214
- assert.isFalse(locusInfo.hashTreeParsers.has(locusUrlB));
3215
- // the existing parser's handleMessage should NOT have been called
3216
- assert.notCalled(parserA.handleMessage);
3217
- });
3218
-
3219
- it('should resume a stopped parser when replaces info is newer', () => {
3220
- const parserA = createMockParser('stopped');
3221
- const parserB = createMockParser('active');
3222
- locusInfo.hashTreeParsers.set(locusUrlA, {parser: parserA, replacedAt: '2026-01-01T00:00:00Z', initializedFromHashTree: true});
3223
- locusInfo.hashTreeParsers.set(locusUrlB, {parser: parserB, initializedFromHashTree: true});
3224
-
3225
- const message = {
3226
- locusUrl: locusUrlA,
3227
- locusStateElements: [
3228
- createSelfElementWithReplaces(locusUrlB, '2026-02-01T00:00:00Z'),
3229
- createMetadataElement(),
3230
- ],
3231
- dataSets: [{name: 'dataset1', url: 'test-url'}],
3232
- };
3233
-
3234
- locusInfo.parse(mockMeeting, {
3235
- eventType: LOCUSEVENT.HASH_TREE_DATA_UPDATED,
3236
- stateElementsMessage: message,
3237
- });
3238
-
3239
- assert.calledOnce(parserA.resume);
3240
- assert.calledOnce(parserB.stop);
3241
- });
3242
-
3243
- it('should not resume a stopped parser when replaces info is not newer', () => {
3244
- const parserA = createMockParser('stopped');
3245
- const parserB = createMockParser('active');
3246
- locusInfo.hashTreeParsers.set(locusUrlA, {parser: parserA, replacedAt: '2026-03-01T00:00:00Z', initializedFromHashTree: true});
3247
- locusInfo.hashTreeParsers.set(locusUrlB, {parser: parserB, initializedFromHashTree: true});
3248
-
3249
- const message = {
3250
- locusUrl: locusUrlA,
3251
- locusStateElements: [
3252
- createSelfElementWithReplaces(locusUrlB, '2026-01-01T00:00:00Z'),
3253
- ],
3254
- dataSets: [{name: 'dataset1', url: 'test-url'}],
3255
- };
3256
-
3257
- locusInfo.parse(mockMeeting, {
3258
- eventType: LOCUSEVENT.HASH_TREE_DATA_UPDATED,
3259
- stateElementsMessage: message,
3260
- });
3261
-
3262
- assert.notCalled(parserA.resume);
3263
- assert.notCalled(parserB.stop);
3264
- });
3265
-
3266
- it('should return true for a stopped parser with no replaces info', () => {
3267
- const parserA = createMockParser('stopped');
3268
- locusInfo.hashTreeParsers.set(locusUrlA, {parser: parserA, initializedFromHashTree: true});
3269
-
3270
- const message = {
3271
- locusUrl: locusUrlA,
3272
- locusStateElements: [],
3273
- dataSets: [],
3274
- };
3275
-
3276
- locusInfo.parse(mockMeeting, {
3277
- eventType: LOCUSEVENT.HASH_TREE_DATA_UPDATED,
3278
- stateElementsMessage: message,
3279
- });
3280
-
3281
- assert.notCalled(parserA.resume);
3282
- assert.notCalled(parserA.handleMessage);
3283
- });
3284
-
3285
- it('should return false when the entry exists and parser is active', () => {
3286
- const parserA = createMockParser('active');
3287
- locusInfo.hashTreeParsers.set(locusUrlA, {parser: parserA, initializedFromHashTree: true});
3288
-
3289
- const message = {
3290
- locusUrl: locusUrlA,
3291
- locusStateElements: [],
3292
- dataSets: [],
3293
- };
3294
-
3295
- locusInfo.parse(mockMeeting, {
3296
- eventType: LOCUSEVENT.HASH_TREE_DATA_UPDATED,
3297
- stateElementsMessage: message,
3298
- });
3299
-
3300
- assert.calledOnceWithExactly(parserA.handleMessage, message);
3301
- });
3302
-
3303
- it('should pass replacedAt from replaces to createHashTreeParser when creating a new parser', () => {
3304
- locusInfo.hashTreeParsers.set(locusUrlA, {parser: createMockParser(), initializedFromHashTree: true});
3305
-
3306
- const message = {
3307
- locusUrl: locusUrlB,
3308
- locusStateElements: [
3309
- createMetadataElement(),
3310
- createSelfElementWithReplaces(locusUrlA, '2026-05-01T00:00:00Z'),
3311
- ],
3312
- dataSets: [{name: 'dataset1', url: 'test-url'}],
3313
- };
3314
-
3315
- locusInfo.parse(mockMeeting, {
3316
- eventType: LOCUSEVENT.HASH_TREE_DATA_UPDATED,
3317
- stateElementsMessage: message,
3318
- });
2542
+ locusInfo.updateEmbeddedApps(clonedApps);
3319
2543
 
3320
- assert.isTrue(locusInfo.hashTreeParsers.has(locusUrlB));
3321
- assert.equal(locusInfo.hashTreeParsers.get(locusUrlA).replacedAt, '2026-05-01T00:00:00Z');
2544
+ assert.notCalled(locusInfo.emitScoped);
3322
2545
  });
3323
- });
3324
-
3325
- describe('#handleHashTreeMessage', () => {
3326
- it('should call handleHashTreeParserSwitch and not call handleMessage if parser was switched', () => {
3327
- const locusUrlA = 'http://locus-url-A.com';
3328
- const locusUrlB = 'http://locus-url-B.com';
3329
- const parserA = {state: 'stopped', handleMessage: sinon.stub(), resume: sinon.stub(), stop: sinon.stub()};
3330
- locusInfo.hashTreeParsers.set(locusUrlA, {parser: parserA, initializedFromHashTree: true});
3331
-
3332
- // message for a stopped parser without replaces -> handleHashTreeParserSwitch returns true
3333
- const message = {
3334
- locusUrl: locusUrlA,
3335
- locusStateElements: [],
3336
- dataSets: [],
3337
- };
3338
2546
 
3339
- locusInfo.parse(mockMeeting, {
3340
- eventType: LOCUSEVENT.HASH_TREE_DATA_UPDATED,
3341
- stateElementsMessage: message,
3342
- });
2547
+ it('emits EMBEDDED_APPS_UPDATED when apps changed', () => {
2548
+ locusInfo.updateEmbeddedApps(newEmbeddedApps);
3343
2549
 
3344
- assert.notCalled(parserA.handleMessage);
3345
- });
2550
+ locusInfo.emitScoped = sinon.stub();
3346
2551
 
3347
- it('should call handleMessage on the correct parser when no switch occurs', () => {
3348
- const locusUrlA = 'http://locus-url-A.com';
3349
- const parserA = {state: 'active', handleMessage: sinon.stub()};
3350
- locusInfo.hashTreeParsers.set(locusUrlA, {parser: parserA, initializedFromHashTree: true});
2552
+ const clonedApps = cloneDeep(newEmbeddedApps);
3351
2553
 
3352
- const message = {
3353
- locusUrl: locusUrlA,
3354
- locusStateElements: [],
3355
- dataSets: [],
3356
- };
2554
+ clonedApps[0].state = 'STOPPED';
2555
+ const expectedApps = EmbeddedAppsUtils.parse(clonedApps);
3357
2556
 
3358
- locusInfo.parse(mockMeeting, {
3359
- eventType: LOCUSEVENT.HASH_TREE_DATA_UPDATED,
3360
- stateElementsMessage: message,
3361
- });
2557
+ locusInfo.updateEmbeddedApps(clonedApps);
3362
2558
 
3363
- assert.calledOnceWithExactly(parserA.handleMessage, message);
2559
+ assert.calledWith(
2560
+ locusInfo.emitScoped,
2561
+ {
2562
+ file: 'locus-info',
2563
+ function: 'updateEmbeddedApps',
2564
+ },
2565
+ LOCUSINFO.EVENTS.EMBEDDED_APPS_UPDATED,
2566
+ expectedApps
2567
+ );
3364
2568
  });
3365
2569
  });
3366
2570
 
@@ -3374,8 +2578,8 @@ describe('plugin-meetings', () => {
3374
2578
 
3375
2579
  assert.calledWith(locusInfo.handleLocusDelta, fakeLocus, mockMeeting);
3376
2580
  });
3377
- it('calls hash tree parser when we are using hash trees', () => {
3378
- const fakeLocus = {eventType: LOCUSEVENT.DIFFERENCE, url: 'http://locus-url.com'};
2581
+ it('does nothing when we are using hash trees', () => {
2582
+ const fakeLocus = {eventType: LOCUSEVENT.DIFFERENCE};
3379
2583
  const fakeDataSets = [{name: 'dataset1', url: 'http://test.com'}];
3380
2584
  const responseBody = {locus: fakeLocus, dataSets: fakeDataSets};
3381
2585
 
@@ -3383,53 +2587,14 @@ describe('plugin-meetings', () => {
3383
2587
  const mockHashTreeParser = {
3384
2588
  handleLocusUpdate: sinon.stub(),
3385
2589
  };
3386
- locusInfo.hashTreeParsers.set(fakeLocus.url, {
3387
- parser: mockHashTreeParser,
3388
- initializedFromHashTree: true,
3389
- });
2590
+ locusInfo.hashTreeParser = mockHashTreeParser;
3390
2591
 
3391
2592
  sinon.stub(locusInfo, 'onDeltaLocus');
3392
2593
 
3393
2594
  locusInfo.handleLocusAPIResponse(mockMeeting, responseBody);
3394
2595
 
3395
- assert.calledOnceWithExactly(mockHashTreeParser.handleLocusUpdate, responseBody);
3396
- });
3397
-
3398
- it('should handle unwrapped LocusDTO (without locus wrapper) when hash tree parser exists', () => {
3399
- const fakeLocus = {url: 'http://locus-url.com', fullState: {state: 'ACTIVE'}};
3400
- const mockHashTreeParser = {handleLocusUpdate: sinon.stub()};
3401
- locusInfo.hashTreeParsers.set(fakeLocus.url, {
3402
- parser: mockHashTreeParser,
3403
- initializedFromHashTree: true,
3404
- });
3405
-
3406
- locusInfo.handleLocusAPIResponse(mockMeeting, fakeLocus);
3407
-
3408
- assert.calledOnceWithExactly(mockHashTreeParser.handleLocusUpdate, {locus: fakeLocus});
3409
- });
3410
-
3411
- it('should handle unwrapped LocusDTO in classic mode (no hash tree parser)', () => {
3412
- const fakeLocus = {url: 'http://locus-url.com', fullState: {state: 'ACTIVE'}};
3413
- sinon.stub(locusInfo, 'handleLocusDelta');
3414
-
3415
- locusInfo.handleLocusAPIResponse(mockMeeting, fakeLocus);
3416
-
3417
- assert.calledOnceWithExactly(locusInfo.handleLocusDelta, fakeLocus, mockMeeting);
3418
- });
3419
-
3420
- it('should send mismatch metric when hash tree parser exists but dataSets are missing in wrapped response', () => {
3421
- const fakeLocus = {url: 'http://locus-url.com'};
3422
- const mockHashTreeParser = {handleLocusUpdate: sinon.stub()};
3423
- locusInfo.hashTreeParsers.set(fakeLocus.url, {
3424
- parser: mockHashTreeParser,
3425
- initializedFromHashTree: true,
3426
- });
3427
- sinon.stub(locusInfo, 'sendClassicVsHashTreeMismatchMetric');
3428
-
3429
- locusInfo.handleLocusAPIResponse(mockMeeting, {locus: fakeLocus});
3430
-
3431
- assert.calledOnce(locusInfo.sendClassicVsHashTreeMismatchMetric);
3432
- assert.calledOnce(mockHashTreeParser.handleLocusUpdate);
2596
+ assert.notCalled(mockHashTreeParser.handleLocusUpdate);
2597
+ assert.notCalled(locusInfo.onDeltaLocus);
3433
2598
  });
3434
2599
  });
3435
2600
 
@@ -3492,28 +2657,31 @@ describe('plugin-meetings', () => {
3492
2657
  assert.isFunction(locusParser.onDeltaAction);
3493
2658
  });
3494
2659
 
3495
- it('#updateLocusInfo invokes updateLocusUrl before updateMeetingInfo', () => {
2660
+ it("#updateLocusInfo invokes updateLocusUrl before updateMeetingInfo", () => {
3496
2661
  const callOrder = [];
3497
- sinon.stub(locusInfo, 'updateControls');
3498
- sinon.stub(locusInfo, 'updateConversationUrl');
3499
- sinon.stub(locusInfo, 'updateCreated');
3500
- sinon.stub(locusInfo, 'updateFullState');
3501
- sinon.stub(locusInfo, 'updateHostInfo');
3502
- sinon.stub(locusInfo, 'updateMeetingInfo').callsFake(() => {
3503
- callOrder.push('updateMeetingInfo');
3504
- });
3505
- sinon.stub(locusInfo, 'updateMediaShares');
3506
- sinon.stub(locusInfo, 'updateReplaces');
3507
- sinon.stub(locusInfo, 'updateSelf');
3508
- sinon.stub(locusInfo, 'updateLocusUrl').callsFake(() => {
3509
- callOrder.push('updateLocusUrl');
3510
- });
3511
- sinon.stub(locusInfo, 'updateAclUrl');
3512
- sinon.stub(locusInfo, 'updateBasequence');
3513
- sinon.stub(locusInfo, 'updateSequence');
3514
- sinon.stub(locusInfo, 'updateEmbeddedApps');
3515
- sinon.stub(locusInfo, 'updateLinks');
3516
- sinon.stub(locusInfo, 'compareAndUpdate');
2662
+ sinon.stub(locusInfo, "updateControls");
2663
+ sinon.stub(locusInfo, "updateConversationUrl");
2664
+ sinon.stub(locusInfo, "updateCreated");
2665
+ sinon.stub(locusInfo, "updateFullState");
2666
+ sinon.stub(locusInfo, "updateHostInfo");
2667
+ sinon.stub(locusInfo, "updateMeetingInfo").callsFake(() => {
2668
+ callOrder.push("updateMeetingInfo");
2669
+ });
2670
+ sinon.stub(locusInfo, "updateMediaShares");
2671
+ sinon.stub(locusInfo, "updateParticipantsUrl");
2672
+ sinon.stub(locusInfo, "updateReplace");
2673
+ sinon.stub(locusInfo, "updateSelf");
2674
+ sinon.stub(locusInfo, "updateLocusUrl").callsFake(() => {
2675
+ callOrder.push("updateLocusUrl");
2676
+ });
2677
+ sinon.stub(locusInfo, "updateAclUrl");
2678
+ sinon.stub(locusInfo, "updateBasequence");
2679
+ sinon.stub(locusInfo, "updateSequence");
2680
+ sinon.stub(locusInfo, "updateMemberShip");
2681
+ sinon.stub(locusInfo, "updateIdentifiers");
2682
+ sinon.stub(locusInfo, "updateEmbeddedApps");
2683
+ sinon.stub(locusInfo, "updateResources");
2684
+ sinon.stub(locusInfo, "compareAndUpdate");
3517
2685
 
3518
2686
  locusInfo.updateLocusInfo(locus);
3519
2687
 
@@ -3536,14 +2704,17 @@ describe('plugin-meetings', () => {
3536
2704
  locusInfo.updateHostInfo = sinon.stub();
3537
2705
  locusInfo.updateMeetingInfo = sinon.stub();
3538
2706
  locusInfo.updateMediaShares = sinon.stub();
3539
- locusInfo.updateReplaces = sinon.stub();
2707
+ locusInfo.updateParticipantsUrl = sinon.stub();
2708
+ locusInfo.updateReplace = sinon.stub();
3540
2709
  locusInfo.updateSelf = sinon.stub();
3541
2710
  locusInfo.updateLocusUrl = sinon.stub();
3542
2711
  locusInfo.updateAclUrl = sinon.stub();
3543
2712
  locusInfo.updateBasequence = sinon.stub();
3544
2713
  locusInfo.updateSequence = sinon.stub();
2714
+ locusInfo.updateMemberShip = sinon.stub();
2715
+ locusInfo.updateIdentifiers = sinon.stub();
3545
2716
  locusInfo.updateEmbeddedApps = sinon.stub();
3546
- locusInfo.updateLinks = sinon.stub();
2717
+ locusInfo.updateResources = sinon.stub();
3547
2718
  locusInfo.compareAndUpdate = sinon.stub();
3548
2719
 
3549
2720
  locusInfo.updateLocusInfo(newLocus);
@@ -3555,49 +2726,21 @@ describe('plugin-meetings', () => {
3555
2726
  assert.notCalled(locusInfo.updateHostInfo);
3556
2727
  assert.notCalled(locusInfo.updateMeetingInfo);
3557
2728
  assert.notCalled(locusInfo.updateMediaShares);
3558
- assert.notCalled(locusInfo.updateReplaces);
2729
+ assert.notCalled(locusInfo.updateParticipantsUrl);
2730
+ assert.notCalled(locusInfo.updateReplace);
3559
2731
  assert.notCalled(locusInfo.updateSelf);
3560
2732
  assert.notCalled(locusInfo.updateLocusUrl);
3561
2733
  assert.notCalled(locusInfo.updateAclUrl);
3562
2734
  assert.notCalled(locusInfo.updateBasequence);
3563
2735
  assert.notCalled(locusInfo.updateSequence);
2736
+ assert.notCalled(locusInfo.updateMemberShip);
2737
+ assert.notCalled(locusInfo.updateIdentifiers);
3564
2738
  assert.notCalled(locusInfo.updateEmbeddedApps);
3565
- assert.notCalled(locusInfo.updateLinks);
2739
+ assert.notCalled(locusInfo.updateResources);
3566
2740
  assert.notCalled(locusInfo.compareAndUpdate);
3567
2741
  });
3568
2742
 
3569
- it('#updateLocusInfo puts the Locus DTO top level properties at the right place in LocusInfo class', () => {
3570
- // this test verifies that the top-level properties of Locus DTO are copied
3571
- // into LocusInfo class and set as top level properties too
3572
- // this is important, because the code handling Locus hash trees relies on it, see updateFromHashTree()
3573
- const info = {id: 'info id'};
3574
- const fullState = {id: 'fullState id'};
3575
- const links = {services: {id: 'service links'}, resources: {id: 'resource links'}};
3576
- const self = {id: 'self id'};
3577
- const mediaShares = [{id: 'fake media share'}];
3578
-
3579
- sinon.stub(SelfUtils, 'getSelves').returns({
3580
- current: {},
3581
- previous: {},
3582
- updates: {},
3583
- });
3584
-
3585
- const newLocus = {
3586
- info,
3587
- fullState,
3588
- links,
3589
- self,
3590
- mediaShares,
3591
- };
3592
-
3593
- locusInfo.updateLocusInfo(newLocus);
3594
2743
 
3595
- assert.deepEqual(locusInfo.info, newLocus.info);
3596
- assert.deepEqual(locusInfo.fullState, newLocus.fullState);
3597
- assert.deepEqual(locusInfo.links, newLocus.links);
3598
- assert.deepEqual(locusInfo.self, newLocus.self);
3599
- assert.deepEqual(locusInfo.mediaShares, newLocus.mediaShares);
3600
- });
3601
2744
 
3602
2745
  it('onFullLocus() updates the working-copy of locus parser', () => {
3603
2746
  const eventType = 'fakeEvent';
@@ -3608,7 +2751,7 @@ describe('plugin-meetings', () => {
3608
2751
  sandbox.stub(locusInfo, 'handleOneOnOneEvent');
3609
2752
  sandbox.stub(locusParser, 'isNewFullLocus').returns(true);
3610
2753
 
3611
- locusInfo.onFullLocus('test', fakeLocus, eventType);
2754
+ locusInfo.onFullLocus(fakeLocus, eventType);
3612
2755
 
3613
2756
  assert.equal(fakeLocus, locusParser.workingCopy);
3614
2757
  });
@@ -3629,7 +2772,7 @@ describe('plugin-meetings', () => {
3629
2772
 
3630
2773
  sandbox.stub(locusParser, 'isNewFullLocus').returns(false);
3631
2774
 
3632
- locusInfo.onFullLocus('test', fakeLocus, eventType);
2775
+ locusInfo.onFullLocus(fakeLocus, eventType);
3633
2776
 
3634
2777
  spies.forEach((spy) => {
3635
2778
  assert.notCalled(spy);
@@ -3779,11 +2922,7 @@ describe('plugin-meetings', () => {
3779
2922
  }).then(() => {
3780
2923
  assert.calledOnceWithExactly(meeting.meetingRequest.getLocusDTO, {url: 'oldLocusUrl'});
3781
2924
 
3782
- assert.calledOnceWithExactly(
3783
- meeting.locusInfo.onFullLocus,
3784
- 'classic Locus sync',
3785
- fakeFullLocusDto
3786
- );
2925
+ assert.calledOnceWithExactly(meeting.locusInfo.onFullLocus, fakeFullLocusDto);
3787
2926
  assert.calledOnce(locusInfo.locusParser.resume);
3788
2927
  });
3789
2928
  });
@@ -3881,11 +3020,7 @@ describe('plugin-meetings', () => {
3881
3020
  });
3882
3021
 
3883
3022
  assert.notCalled(meeting.locusInfo.handleLocusDelta);
3884
- assert.calledOnceWithExactly(
3885
- meeting.locusInfo.onFullLocus,
3886
- 'classic Locus sync',
3887
- fakeFullLocusDto
3888
- );
3023
+ assert.calledOnceWithExactly(meeting.locusInfo.onFullLocus, fakeFullLocusDto);
3889
3024
  assert.calledOnce(locusInfo.locusParser.resume);
3890
3025
  });
3891
3026
  });
@@ -4061,11 +3196,7 @@ describe('plugin-meetings', () => {
4061
3196
  url: 'fake locus DELTA url',
4062
3197
  });
4063
3198
  assert.notCalled(meeting.locusInfo.handleLocusDelta);
4064
- assert.calledOnceWithExactly(
4065
- meeting.locusInfo.onFullLocus,
4066
- 'classic Locus sync',
4067
- fakeFullLocusDto
4068
- );
3199
+ assert.calledOnceWithExactly(meeting.locusInfo.onFullLocus, fakeFullLocusDto);
4069
3200
  assert.calledOnce(locusInfo.locusParser.resume);
4070
3201
  });
4071
3202
  });
@@ -4086,137 +3217,6 @@ describe('plugin-meetings', () => {
4086
3217
 
4087
3218
  assert.isNull(locusInfo.mainSessionLocusCache);
4088
3219
  });
4089
-
4090
- it('should map participant with htMeta.elementId.id of 0 (falsy number) to hashTreeObjectId2ParticipantId', () => {
4091
- const locus = {
4092
- url: 'url',
4093
- participants: [
4094
- {
4095
- id: 'participant-zero',
4096
- htMeta: {elementId: {id: 0}},
4097
- },
4098
- ],
4099
- };
4100
-
4101
- sinon.stub(locusInfo.locusParser, 'isNewFullLocus').returns(true);
4102
- sinon.stub(locusInfo, 'updateLocusInfo');
4103
- sinon.stub(locusInfo, 'updateParticipants');
4104
- sinon.stub(locusInfo, 'isMeetingActive');
4105
- sinon.stub(locusInfo, 'handleOneOnOneEvent');
4106
- sinon.stub(locusInfo, 'updateEmbeddedApps');
4107
- locusInfo.locusParser.workingCopy = null;
4108
-
4109
- locusInfo.onFullLocus('test', locus);
4110
-
4111
- assert.equal(locusInfo.hashTreeObjectId2ParticipantId.get(0), 'participant-zero');
4112
- });
4113
- });
4114
-
4115
- describe('#onDeltaLocus', () => {
4116
- it('should use forceReplaceMembers from jsSdkMeta when it is defined', () => {
4117
- sinon.stub(locusInfo, 'mergeParticipants');
4118
- sinon.stub(locusInfo, 'updateLocusInfo').returns(true);
4119
- sinon.stub(locusInfo, 'updateParticipants');
4120
- sinon.stub(locusInfo, 'isMeetingActive');
4121
-
4122
- locusInfo.onDeltaLocus({
4123
- participants: [],
4124
- jsSdkMeta: {forceReplaceMembers: true, removedParticipantIds: []},
4125
- });
4126
-
4127
- assert.calledOnceWithExactly(locusInfo.updateParticipants, [], [], true);
4128
- });
4129
-
4130
- it('should fall back to isNeedReplaceMembers when forceReplaceMembers is not in jsSdkMeta', () => {
4131
- sinon.stub(locusInfo, 'mergeParticipants');
4132
- sinon.stub(locusInfo, 'updateLocusInfo').returns(true);
4133
- sinon.stub(locusInfo, 'updateParticipants');
4134
- sinon.stub(locusInfo, 'isMeetingActive');
4135
-
4136
- locusInfo.onDeltaLocus({participants: []});
4137
-
4138
- // without jsSdkMeta.forceReplaceMembers, uses ControlsUtils.isNeedReplaceMembers result (false by default)
4139
- assert.calledOnceWithExactly(locusInfo.updateParticipants, [], undefined, false);
4140
- });
4141
-
4142
- it('should not call updateParticipants when updateLocusInfo returns false', () => {
4143
- sinon.stub(locusInfo, 'mergeParticipants');
4144
- sinon.stub(locusInfo, 'updateLocusInfo').returns(false);
4145
- sinon.stub(locusInfo, 'updateParticipants');
4146
- sinon.stub(locusInfo, 'isMeetingActive');
4147
-
4148
- locusInfo.onDeltaLocus({participants: [], self: {state: 'LEFT', reason: 'MOVED'}});
4149
-
4150
- assert.notCalled(locusInfo.updateParticipants);
4151
- });
4152
-
4153
- it('should call updateParticipants when updateLocusInfo returns true', () => {
4154
- sinon.stub(locusInfo, 'mergeParticipants');
4155
- sinon.stub(locusInfo, 'updateLocusInfo').returns(true);
4156
- sinon.stub(locusInfo, 'updateParticipants');
4157
- sinon.stub(locusInfo, 'isMeetingActive');
4158
-
4159
- locusInfo.onDeltaLocus({participants: [{id: 'p1'}]});
4160
-
4161
- assert.calledOnce(locusInfo.updateParticipants);
4162
- });
4163
-
4164
- [
4165
- {forceReplaceMembers: true, selfInParticipants: false, expectedSelfCopied: true},
4166
- {forceReplaceMembers: true, selfInParticipants: true, expectedSelfCopied: false},
4167
- {forceReplaceMembers: false, selfInParticipants: false, expectedSelfCopied: false},
4168
- {forceReplaceMembers: false, selfInParticipants: true, expectedSelfCopied: false},
4169
- ].forEach(({forceReplaceMembers, selfInParticipants, expectedSelfCopied}) => {
4170
- it(`should ${expectedSelfCopied ? '' : 'not '}copy self into participants when forceReplaceMembers=${forceReplaceMembers} and self ${selfInParticipants ? 'is' : 'is not'} in participants`, () => {
4171
- const self = {identity: 'selfId', state: 'JOINED', devices: [], status: {}};
4172
- const participant = {identity: selfInParticipants ? 'selfId' : 'other'};
4173
- const locus = {
4174
- participants: [participant],
4175
- self,
4176
- jsSdkMeta: {forceReplaceMembers, removedParticipantIds: []},
4177
- };
4178
-
4179
- locusInfo.onDeltaLocus(locus);
4180
-
4181
- const expectedParticipants = expectedSelfCopied ? [participant, self] : [participant];
4182
- assert.deepEqual(locus.participants, expectedParticipants);
4183
- });
4184
- });
4185
- });
4186
-
4187
- describe('#updateLocusInfo', () => {
4188
- it('should return false when self.reason is MOVED and self.state is LEFT', () => {
4189
- sinon.stub(locusInfo, 'updateControls');
4190
-
4191
- const result = locusInfo.updateLocusInfo({self: {reason: 'MOVED', state: 'LEFT'}});
4192
-
4193
- assert.isFalse(result);
4194
- assert.notCalled(locusInfo.updateControls);
4195
- });
4196
-
4197
- it('should return true when self is not in MOVED/LEFT state', () => {
4198
- sinon.stub(locusInfo, 'updateControls');
4199
- sinon.stub(locusInfo, 'updateConversationUrl');
4200
- sinon.stub(locusInfo, 'updateCreated');
4201
- sinon.stub(locusInfo, 'updateFullState');
4202
- sinon.stub(locusInfo, 'updateHostInfo');
4203
- sinon.stub(locusInfo, 'updateLocusUrl');
4204
- sinon.stub(locusInfo, 'updateMeetingInfo');
4205
- sinon.stub(locusInfo, 'updateMediaShares');
4206
- sinon.stub(locusInfo, 'updateReplaces');
4207
- sinon.stub(locusInfo, 'updateSelf');
4208
- sinon.stub(locusInfo, 'updateAclUrl');
4209
- sinon.stub(locusInfo, 'updateBasequence');
4210
- sinon.stub(locusInfo, 'updateSequence');
4211
- sinon.stub(locusInfo, 'updateEmbeddedApps');
4212
- sinon.stub(locusInfo, 'updateLinks');
4213
- sinon.stub(locusInfo, 'compareAndUpdate');
4214
-
4215
- const result = locusInfo.updateLocusInfo({self: {state: 'JOINED'}});
4216
-
4217
- assert.isTrue(result);
4218
- assert.calledOnce(locusInfo.updateControls);
4219
- });
4220
3220
  });
4221
3221
 
4222
3222
  describe('#getTheLocusToUpdate', () => {
@@ -4568,7 +3568,7 @@ describe('plugin-meetings', () => {
4568
3568
 
4569
3569
  describe('#updateLocusUrl', () => {
4570
3570
  it('trigger LOCUS_INFO_UPDATE_URL event with isMainLocus is true as default', () => {
4571
- const fakeUrl = 'https://fake.com/locus';
3571
+ const fakeUrl = "https://fake.com/locus";
4572
3572
  locusInfo.emitScoped = sinon.stub();
4573
3573
  locusInfo.updateLocusUrl(fakeUrl);
4574
3574
 
@@ -4581,12 +3581,12 @@ describe('plugin-meetings', () => {
4581
3581
  EVENTS.LOCUS_INFO_UPDATE_URL,
4582
3582
  {
4583
3583
  url: fakeUrl,
4584
- isMainLocus: true,
4585
- }
3584
+ isMainLocus: true
3585
+ },
4586
3586
  );
4587
3587
  });
4588
3588
  it('trigger LOCUS_INFO_UPDATE_URL event with isMainLocus is false', () => {
4589
- const fakeUrl = 'https://fake.com/locus';
3589
+ const fakeUrl = "https://fake.com/locus";
4590
3590
  locusInfo.emitScoped = sinon.stub();
4591
3591
  locusInfo.updateLocusUrl(fakeUrl, false);
4592
3592
 
@@ -4599,8 +3599,8 @@ describe('plugin-meetings', () => {
4599
3599
  EVENTS.LOCUS_INFO_UPDATE_URL,
4600
3600
  {
4601
3601
  url: fakeUrl,
4602
- isMainLocus: false,
4603
- }
3602
+ isMainLocus: false
3603
+ },
4604
3604
  );
4605
3605
  });
4606
3606
  });
@@ -4652,8 +3652,8 @@ describe('plugin-meetings', () => {
4652
3652
 
4653
3653
  sinon.stub(locusInfo, 'updateParticipants');
4654
3654
  sinon.stub(locusInfo, 'isMeetingActive');
4655
- sinon.stub(locusInfo, 'handleOneOnOneEvent');
4656
- updateLocusInfoStub = sinon.stub(locusInfo, 'updateLocusInfo');
3655
+ sinon.stub(locusInfo, 'handleOneOnOneEvent');
3656
+ (updateLocusInfoStub = sinon.stub(locusInfo, 'updateLocusInfo'));
4657
3657
  syncRequestStub = sinon.stub().resolves({body: {}});
4658
3658
 
4659
3659
  mockMeeting.locusInfo = locusInfo;
@@ -4662,7 +3662,7 @@ describe('plugin-meetings', () => {
4662
3662
  getLocusDTO: syncRequestStub,
4663
3663
  };
4664
3664
 
4665
- locusInfo.onFullLocus('test', {
3665
+ locusInfo.onFullLocus({
4666
3666
  sequence: {
4667
3667
  rangeStart: 0,
4668
3668
  rangeEnd: 0,
@@ -4900,9 +3900,7 @@ describe('plugin-meetings', () => {
4900
3900
 
4901
3901
  describe('#parse', () => {
4902
3902
  it('handles hash tree messages correctly', () => {
4903
- const fakeLocusUrl = 'http://locus-url.com';
4904
3903
  const fakeHashTreeMessage = {
4905
- locusUrl: fakeLocusUrl,
4906
3904
  locusStateElements: [
4907
3905
  {
4908
3906
  htMeta: {elementId: {type: 'self'}},
@@ -4921,377 +3919,12 @@ describe('plugin-meetings', () => {
4921
3919
  const mockHashTreeParser = {
4922
3920
  handleMessage: sinon.stub(),
4923
3921
  };
4924
- locusInfo.hashTreeParsers.set(fakeLocusUrl, {
4925
- parser: mockHashTreeParser,
4926
- initializedFromHashTree: true,
4927
- });
3922
+ locusInfo.hashTreeParser = mockHashTreeParser;
4928
3923
 
4929
3924
  locusInfo.parse(mockMeeting, data);
4930
3925
 
4931
3926
  assert.calledOnceWithExactly(mockHashTreeParser.handleMessage, fakeHashTreeMessage);
4932
3927
  });
4933
-
4934
- it('ignores hash tree event when hashTreeParser is not created yet', () => {
4935
- const data = {
4936
- eventType: LOCUSEVENT.HASH_TREE_DATA_UPDATED,
4937
- stateElementsMessage: {
4938
- locusStateElements: [],
4939
- dataSets: [],
4940
- },
4941
- };
4942
-
4943
- const loggerSpy = sinon.spy(LoggerProxy.logger, 'info');
4944
- const getTheLocusToUpdateStub = sinon.stub(locusInfo, 'getTheLocusToUpdate');
4945
-
4946
- // Ensure we're not using hash trees
4947
- assert.equal(locusInfo.hashTreeParsers.size, 0);
4948
-
4949
- locusInfo.parse(mockMeeting, data);
4950
-
4951
- assert.calledWith(
4952
- loggerSpy,
4953
- 'Locus-info:index#parse --> received locus hash tree event before hashTreeParser is created'
4954
- );
4955
- assert.notCalled(getTheLocusToUpdateStub);
4956
- });
4957
- });
4958
- });
4959
-
4960
- describe('#createLocusFromHashTreeMessage', () => {
4961
- const LOCUS_URL = 'https://locus.example.com/loci/abc-123';
4962
-
4963
- const createElement = (type, data) => ({
4964
- htMeta: {elementId: {type, id: 1, version: 1}},
4965
- data,
4966
- });
4967
-
4968
- it('returns locus with url and empty participants when no locusStateElements', () => {
4969
- const result = createLocusFromHashTreeMessage({locusUrl: LOCUS_URL});
4970
-
4971
- assert.deepEqual(result.locus, {participants: [], url: LOCUS_URL});
4972
- assert.isUndefined(result.metadata);
4973
- });
4974
-
4975
- it('skips elements without data', () => {
4976
- const result = createLocusFromHashTreeMessage({
4977
- locusUrl: LOCUS_URL,
4978
- locusStateElements: [{htMeta: {elementId: {type: 'Self', id: 1, version: 1}}, data: null}],
4979
- });
4980
-
4981
- assert.deepEqual(result.locus, {participants: [], url: LOCUS_URL});
4982
- });
4983
-
4984
- [
4985
- {type: 'Self', locusKey: 'self', data: {id: 'self-1', state: 'JOINED'}},
4986
- {type: 'Info', locusKey: 'info', data: {webExMeetingId: '123'}},
4987
- {type: 'FullState', locusKey: 'fullState', data: {state: 'ACTIVE'}},
4988
- {type: 'Links', locusKey: 'links', data: {resources: {}}},
4989
- ].forEach(({type, locusKey, data}) => {
4990
- it(`maps ${type} element to locus.${locusKey}`, () => {
4991
- const result = createLocusFromHashTreeMessage({
4992
- locusUrl: LOCUS_URL,
4993
- locusStateElements: [createElement(type, data)],
4994
- });
4995
-
4996
- assert.deepEqual(result.locus[locusKey], data);
4997
- });
4998
- });
4999
-
5000
- it('pushes Participant elements to locus.participants', () => {
5001
- const p1 = {id: 'p1', state: 'JOINED'};
5002
- const p2 = {id: 'p2', state: 'LEFT'};
5003
-
5004
- const result = createLocusFromHashTreeMessage({
5005
- locusUrl: LOCUS_URL,
5006
- locusStateElements: [createElement('Participant', p1), createElement('Participant', p2)],
5007
- });
5008
-
5009
- assert.deepEqual(result.locus.participants, [p1, p2]);
5010
- });
5011
-
5012
- it('pushes MediaShare elements to locus.mediaShares array', () => {
5013
- const share1 = {name: 'whiteboard'};
5014
- const share2 = {name: 'content'};
5015
-
5016
- const result = createLocusFromHashTreeMessage({
5017
- locusUrl: LOCUS_URL,
5018
- locusStateElements: [
5019
- createElement('MediaShare', share1),
5020
- createElement('MediaShare', share2),
5021
- ],
5022
- });
5023
-
5024
- assert.deepEqual(result.locus.mediaShares, [share1, share2]);
5025
- });
5026
-
5027
- it('pushes EmbeddedApp elements to locus.embeddedApps array', () => {
5028
- const app = {appId: 'app-1', state: 'STARTED'};
5029
-
5030
- const result = createLocusFromHashTreeMessage({
5031
- locusUrl: LOCUS_URL,
5032
- locusStateElements: [createElement('EmbeddedApp', app)],
5033
- });
5034
-
5035
- assert.deepEqual(result.locus.embeddedApps, [app]);
5036
- });
5037
-
5038
- it('merges ControlEntry elements into locus.controls', () => {
5039
- const control1 = {record: {recording: true}};
5040
- const control2 = {lock: {locked: false}};
5041
-
5042
- const result = createLocusFromHashTreeMessage({
5043
- locusUrl: LOCUS_URL,
5044
- locusStateElements: [
5045
- createElement('ControlEntry', control1),
5046
- createElement('ControlEntry', control2),
5047
- ],
5048
- });
5049
-
5050
- assert.deepEqual(result.locus.controls, {record: {recording: true}, lock: {locked: false}});
5051
- });
5052
-
5053
- it('spreads Locus element data onto top level but removes managed keys', () => {
5054
- const locusData = {
5055
- url: 'should-be-overridden',
5056
- someTopLevelField: 'value',
5057
- // these are managed by other ObjectTypes and should be removed
5058
- links: {should: 'be removed'},
5059
- info: {should: 'be removed'},
5060
- fullState: {should: 'be removed'},
5061
- self: {should: 'be removed'},
5062
- participants: [{should: 'be removed'}],
5063
- mediaShares: [{should: 'be removed'}],
5064
- controls: {should: 'be removed'},
5065
- embeddedApps: [{should: 'be removed'}],
5066
- };
5067
-
5068
- const result = createLocusFromHashTreeMessage({
5069
- locusUrl: LOCUS_URL,
5070
- locusStateElements: [createElement('Locus', locusData)],
5071
- });
5072
-
5073
- assert.equal(result.locus.someTopLevelField, 'value');
5074
- assert.deepEqual(result.locus.participants, []);
5075
- assert.isUndefined(result.locus.links);
5076
- assert.isUndefined(result.locus.info);
5077
- assert.isUndefined(result.locus.fullState);
5078
- assert.isUndefined(result.locus.self);
5079
- assert.isUndefined(result.locus.mediaShares);
5080
- assert.isUndefined(result.locus.controls);
5081
- assert.isUndefined(result.locus.embeddedApps);
5082
- });
5083
-
5084
- it('extracts Metadata element as metadata in the result', () => {
5085
- const metadataData = {visibleDataSets: [{name: 'ds1', url: 'http://ds1.url'}]};
5086
- const htMeta = {elementId: {type: 'Metadata', id: 99, version: 3}};
5087
-
5088
- const result = createLocusFromHashTreeMessage({
5089
- locusUrl: LOCUS_URL,
5090
- locusStateElements: [{htMeta, data: metadataData}],
5091
- });
5092
-
5093
- assert.deepEqual(result.metadata, {...metadataData, htMeta});
5094
- assert.isUndefined(result.locus.metadata);
5095
- });
5096
-
5097
- it('handles a message with multiple element types', () => {
5098
- const selfData = {id: 'self-1'};
5099
- const participantData = {id: 'p1'};
5100
- const infoData = {webExMeetingId: '456'};
5101
-
5102
- const result = createLocusFromHashTreeMessage({
5103
- locusUrl: LOCUS_URL,
5104
- locusStateElements: [
5105
- createElement('Self', selfData),
5106
- createElement('Participant', participantData),
5107
- createElement('Info', infoData),
5108
- ],
5109
- });
5110
-
5111
- assert.deepEqual(result.locus.self, selfData);
5112
- assert.deepEqual(result.locus.participants, [participantData]);
5113
- assert.deepEqual(result.locus.info, infoData);
5114
- assert.equal(result.locus.url, LOCUS_URL);
5115
- });
5116
-
5117
- it('ignores unknown element types', () => {
5118
- const result = createLocusFromHashTreeMessage({
5119
- locusUrl: LOCUS_URL,
5120
- locusStateElements: [createElement('UnknownType', {foo: 'bar'})],
5121
- });
5122
-
5123
- assert.deepEqual(result.locus, {participants: [], url: LOCUS_URL});
5124
- });
5125
- });
5126
-
5127
- describe('findMeetingForHashTreeMessage', () => {
5128
- const deviceUrl = 'https://devices.example.com/device1';
5129
-
5130
- function createMockMeetingCollection(meetings) {
5131
- return {
5132
- getAll: () => meetings,
5133
- };
5134
- }
5135
-
5136
- function createMockMeeting(id, hashTreeParsersMap) {
5137
- return {
5138
- id,
5139
- locusInfo: {
5140
- hashTreeParsers: hashTreeParsersMap,
5141
- },
5142
- };
5143
- }
5144
-
5145
- function createSelfElement(devices) {
5146
- return {
5147
- htMeta: {elementId: {type: 'Self'}},
5148
- data: {
5149
- devices,
5150
- },
5151
- };
5152
- }
5153
-
5154
- it('returns the meeting when locusUrl matches a hashTreeParser directly', () => {
5155
- const locusUrl = 'https://locus.example.com/loci/abc123';
5156
- const parsersMap = new Map([[locusUrl, {state: 'active'}]]);
5157
- const meeting = createMockMeeting('meeting1', parsersMap);
5158
- const collection = createMockMeetingCollection({meeting1: meeting});
5159
-
5160
- const message = {locusUrl, locusStateElements: []};
5161
-
5162
- const result = findMeetingForHashTreeMessage(message, collection, deviceUrl);
5163
-
5164
- assert.equal(result, meeting);
5165
- });
5166
-
5167
- it('returns undefined when no meeting matches and message has no locusStateElements', () => {
5168
- const locusUrl = 'https://locus.example.com/loci/unknown';
5169
- const parsersMap = new Map([['https://locus.example.com/loci/other', {state: 'active'}]]);
5170
- const meeting = createMockMeeting('meeting1', parsersMap);
5171
- const collection = createMockMeetingCollection({meeting1: meeting});
5172
-
5173
- const message = {locusUrl};
5174
-
5175
- const result = findMeetingForHashTreeMessage(message, collection, deviceUrl);
5176
-
5177
- assert.isUndefined(result);
5178
- });
5179
-
5180
- it('returns undefined when no meeting matches and self element has no replaces', () => {
5181
- const locusUrl = 'https://locus.example.com/loci/unknown';
5182
- const parsersMap = new Map([['https://locus.example.com/loci/other', {state: 'active'}]]);
5183
- const meeting = createMockMeeting('meeting1', parsersMap);
5184
- const collection = createMockMeetingCollection({meeting1: meeting});
5185
-
5186
- const selfElement = createSelfElement([{url: deviceUrl}]);
5187
- const message = {locusUrl, locusStateElements: [selfElement]};
5188
-
5189
- const result = findMeetingForHashTreeMessage(message, collection, deviceUrl);
5190
-
5191
- assert.isUndefined(result);
5192
- });
5193
-
5194
- it('returns the meeting when locusUrl from replaces matches a hashTreeParser', () => {
5195
- const oldLocusUrl = 'https://locus.example.com/loci/old';
5196
- const newLocusUrl = 'https://locus.example.com/loci/new';
5197
- const parsersMap = new Map([[oldLocusUrl, {state: 'active'}]]);
5198
- const meeting = createMockMeeting('meeting1', parsersMap);
5199
- const collection = createMockMeetingCollection({meeting1: meeting});
5200
-
5201
- const selfElement = createSelfElement([
5202
- {url: deviceUrl, replaces: [{locusUrl: oldLocusUrl}]},
5203
- ]);
5204
- const message = {locusUrl: newLocusUrl, locusStateElements: [selfElement]};
5205
-
5206
- const result = findMeetingForHashTreeMessage(message, collection, deviceUrl);
5207
-
5208
- assert.equal(result, meeting);
5209
- });
5210
-
5211
- it('returns undefined when replaces locusUrl does not match any hashTreeParser', () => {
5212
- const oldLocusUrl = 'https://locus.example.com/loci/old';
5213
- const newLocusUrl = 'https://locus.example.com/loci/new';
5214
- const parsersMap = new Map([
5215
- ['https://locus.example.com/loci/something-else', {state: 'active'}],
5216
- ]);
5217
- const meeting = createMockMeeting('meeting1', parsersMap);
5218
- const collection = createMockMeetingCollection({meeting1: meeting});
5219
-
5220
- const selfElement = createSelfElement([
5221
- {url: deviceUrl, replaces: [{locusUrl: oldLocusUrl}]},
5222
- ]);
5223
- const message = {locusUrl: newLocusUrl, locusStateElements: [selfElement]};
5224
-
5225
- const result = findMeetingForHashTreeMessage(message, collection, deviceUrl);
5226
-
5227
- assert.isUndefined(result);
5228
- });
5229
-
5230
- it('returns undefined when meetingCollection is empty', () => {
5231
- const collection = createMockMeetingCollection({});
5232
- const message = {locusUrl: 'https://locus.example.com/loci/abc', locusStateElements: []};
5233
-
5234
- const result = findMeetingForHashTreeMessage(message, collection, deviceUrl);
5235
-
5236
- assert.isUndefined(result);
5237
- });
5238
-
5239
- it('checks multiple meetings and returns the correct one', () => {
5240
- const targetLocusUrl = 'https://locus.example.com/loci/target';
5241
- const meeting1 = createMockMeeting(
5242
- 'meeting1',
5243
- new Map([['https://locus.example.com/loci/other', {state: 'active'}]])
5244
- );
5245
- const meeting2 = createMockMeeting(
5246
- 'meeting2',
5247
- new Map([[targetLocusUrl, {state: 'active'}]])
5248
- );
5249
- const collection = createMockMeetingCollection({meeting1, meeting2});
5250
-
5251
- const message = {locusUrl: targetLocusUrl, locusStateElements: []};
5252
-
5253
- const result = findMeetingForHashTreeMessage(message, collection, deviceUrl);
5254
-
5255
- assert.equal(result, meeting2);
5256
- });
5257
-
5258
- it('ignores devices that do not match deviceUrl when looking for replaces', () => {
5259
- const oldLocusUrl = 'https://locus.example.com/loci/old';
5260
- const newLocusUrl = 'https://locus.example.com/loci/new';
5261
- const parsersMap = new Map([[oldLocusUrl, {state: 'active'}]]);
5262
- const meeting = createMockMeeting('meeting1', parsersMap);
5263
- const collection = createMockMeetingCollection({meeting1: meeting});
5264
-
5265
- // self element has replaces, but on a different device
5266
- const selfElement = createSelfElement([
5267
- {url: 'https://devices.example.com/other-device', replaces: [{locusUrl: oldLocusUrl}]},
5268
- ]);
5269
- const message = {locusUrl: newLocusUrl, locusStateElements: [selfElement]};
5270
-
5271
- const result = findMeetingForHashTreeMessage(message, collection, deviceUrl);
5272
-
5273
- assert.isUndefined(result);
5274
- });
5275
-
5276
- it('does not use self element if it is not of type Self', () => {
5277
- const oldLocusUrl = 'https://locus.example.com/loci/old';
5278
- const newLocusUrl = 'https://locus.example.com/loci/new';
5279
- const parsersMap = new Map([[oldLocusUrl, {state: 'active'}]]);
5280
- const meeting = createMockMeeting('meeting1', parsersMap);
5281
- const collection = createMockMeetingCollection({meeting1: meeting});
5282
-
5283
- // element has replaces data but is not of type Self
5284
- const nonSelfElement = {
5285
- htMeta: {elementId: {type: 'Participant'}},
5286
- data: {
5287
- devices: [{url: deviceUrl, replaces: [{locusUrl: oldLocusUrl}]}],
5288
- },
5289
- };
5290
- const message = {locusUrl: newLocusUrl, locusStateElements: [nonSelfElement]};
5291
-
5292
- const result = findMeetingForHashTreeMessage(message, collection, deviceUrl);
5293
-
5294
- assert.isUndefined(result);
5295
3928
  });
5296
3929
  });
5297
3930
  });