@webex/plugin-meetings 3.0.0-beta.4 → 3.0.0-beta.400

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 (629) hide show
  1. package/README.md +58 -8
  2. package/UPGRADING.md +9 -9
  3. package/browsers.js +19 -24
  4. package/dist/annotation/annotation.types.js +7 -0
  5. package/dist/annotation/annotation.types.js.map +1 -0
  6. package/dist/annotation/constants.js +49 -0
  7. package/dist/annotation/constants.js.map +1 -0
  8. package/dist/annotation/index.js +342 -0
  9. package/dist/annotation/index.js.map +1 -0
  10. package/dist/breakouts/breakout.js +216 -0
  11. package/dist/breakouts/breakout.js.map +1 -0
  12. package/dist/breakouts/collection.js +23 -0
  13. package/dist/breakouts/collection.js.map +1 -0
  14. package/dist/breakouts/edit-lock-error.js +52 -0
  15. package/dist/breakouts/edit-lock-error.js.map +1 -0
  16. package/dist/breakouts/events.js +45 -0
  17. package/dist/breakouts/events.js.map +1 -0
  18. package/dist/breakouts/index.js +1048 -0
  19. package/dist/breakouts/index.js.map +1 -0
  20. package/dist/breakouts/request.js +78 -0
  21. package/dist/breakouts/request.js.map +1 -0
  22. package/dist/breakouts/utils.js +67 -0
  23. package/dist/breakouts/utils.js.map +1 -0
  24. package/dist/common/browser-detection.js +1 -20
  25. package/dist/common/browser-detection.js.map +1 -1
  26. package/dist/common/collection.js +5 -20
  27. package/dist/common/collection.js.map +1 -1
  28. package/dist/common/config.js +0 -7
  29. package/dist/common/config.js.map +1 -1
  30. package/dist/common/errors/captcha-error.js +10 -24
  31. package/dist/common/errors/captcha-error.js.map +1 -1
  32. package/dist/common/errors/intent-to-join.js +11 -24
  33. package/dist/common/errors/intent-to-join.js.map +1 -1
  34. package/dist/common/errors/join-meeting.js +12 -25
  35. package/dist/common/errors/join-meeting.js.map +1 -1
  36. package/dist/common/errors/media.js +10 -24
  37. package/dist/common/errors/media.js.map +1 -1
  38. package/dist/common/errors/no-meeting-info.js +51 -0
  39. package/dist/common/errors/no-meeting-info.js.map +1 -0
  40. package/dist/common/errors/parameter.js +5 -33
  41. package/dist/common/errors/parameter.js.map +1 -1
  42. package/dist/common/errors/password-error.js +10 -24
  43. package/dist/common/errors/password-error.js.map +1 -1
  44. package/dist/common/errors/permission.js +9 -23
  45. package/dist/common/errors/permission.js.map +1 -1
  46. package/dist/common/errors/reclaim-host-role-errors.js +158 -0
  47. package/dist/common/errors/reclaim-host-role-errors.js.map +1 -0
  48. package/dist/common/errors/reconnection-in-progress.js +0 -17
  49. package/dist/common/errors/reconnection-in-progress.js.map +1 -1
  50. package/dist/common/errors/reconnection.js +10 -24
  51. package/dist/common/errors/reconnection.js.map +1 -1
  52. package/dist/common/errors/stats.js +10 -24
  53. package/dist/common/errors/stats.js.map +1 -1
  54. package/dist/common/errors/webex-errors.js +54 -48
  55. package/dist/common/errors/webex-errors.js.map +1 -1
  56. package/dist/common/errors/webex-meetings-error.js +5 -25
  57. package/dist/common/errors/webex-meetings-error.js.map +1 -1
  58. package/dist/common/events/events-scope.js +0 -22
  59. package/dist/common/events/events-scope.js.map +1 -1
  60. package/dist/common/events/events.js +0 -23
  61. package/dist/common/events/events.js.map +1 -1
  62. package/dist/common/events/trigger-proxy.js +0 -12
  63. package/dist/common/events/trigger-proxy.js.map +1 -1
  64. package/dist/common/events/util.js +0 -15
  65. package/dist/common/events/util.js.map +1 -1
  66. package/dist/common/logs/logger-config.js +0 -4
  67. package/dist/common/logs/logger-config.js.map +1 -1
  68. package/dist/common/logs/logger-proxy.js +1 -8
  69. package/dist/common/logs/logger-proxy.js.map +1 -1
  70. package/dist/common/logs/request.js +41 -60
  71. package/dist/common/logs/request.js.map +1 -1
  72. package/dist/common/queue.js +28 -23
  73. package/dist/common/queue.js.map +1 -1
  74. package/dist/config.js +11 -15
  75. package/dist/config.js.map +1 -1
  76. package/dist/constants.js +347 -74
  77. package/dist/constants.js.map +1 -1
  78. package/dist/controls-options-manager/constants.js +14 -0
  79. package/dist/controls-options-manager/constants.js.map +1 -0
  80. package/dist/controls-options-manager/enums.js +27 -0
  81. package/dist/controls-options-manager/enums.js.map +1 -0
  82. package/dist/controls-options-manager/index.js +297 -0
  83. package/dist/controls-options-manager/index.js.map +1 -0
  84. package/dist/controls-options-manager/types.js +7 -0
  85. package/dist/controls-options-manager/types.js.map +1 -0
  86. package/dist/controls-options-manager/util.js +319 -0
  87. package/dist/controls-options-manager/util.js.map +1 -0
  88. package/dist/index.js +125 -18
  89. package/dist/index.js.map +1 -1
  90. package/dist/interceptors/index.js +15 -0
  91. package/dist/interceptors/index.js.map +1 -0
  92. package/dist/interceptors/locusRetry.js +93 -0
  93. package/dist/interceptors/locusRetry.js.map +1 -0
  94. package/dist/interpretation/collection.js +23 -0
  95. package/dist/interpretation/collection.js.map +1 -0
  96. package/dist/interpretation/index.js +380 -0
  97. package/dist/interpretation/index.js.map +1 -0
  98. package/dist/interpretation/siLanguage.js +25 -0
  99. package/dist/interpretation/siLanguage.js.map +1 -0
  100. package/dist/locus-info/controlsUtils.js +101 -29
  101. package/dist/locus-info/controlsUtils.js.map +1 -1
  102. package/dist/locus-info/embeddedAppsUtils.js +3 -26
  103. package/dist/locus-info/embeddedAppsUtils.js.map +1 -1
  104. package/dist/locus-info/fullState.js +0 -15
  105. package/dist/locus-info/fullState.js.map +1 -1
  106. package/dist/locus-info/hostUtils.js +4 -12
  107. package/dist/locus-info/hostUtils.js.map +1 -1
  108. package/dist/locus-info/index.js +564 -246
  109. package/dist/locus-info/index.js.map +1 -1
  110. package/dist/locus-info/infoUtils.js +10 -38
  111. package/dist/locus-info/infoUtils.js.map +1 -1
  112. package/dist/locus-info/mediaSharesUtils.js +82 -38
  113. package/dist/locus-info/mediaSharesUtils.js.map +1 -1
  114. package/dist/locus-info/parser.js +314 -163
  115. package/dist/locus-info/parser.js.map +1 -1
  116. package/dist/locus-info/selfUtils.js +110 -92
  117. package/dist/locus-info/selfUtils.js.map +1 -1
  118. package/dist/media/index.js +107 -231
  119. package/dist/media/index.js.map +1 -1
  120. package/dist/media/properties.js +137 -222
  121. package/dist/media/properties.js.map +1 -1
  122. package/dist/media/util.js +2 -9
  123. package/dist/media/util.js.map +1 -1
  124. package/dist/mediaQualityMetrics/config.js +316 -501
  125. package/dist/mediaQualityMetrics/config.js.map +1 -1
  126. package/dist/meeting/in-meeting-actions.js +97 -14
  127. package/dist/meeting/in-meeting-actions.js.map +1 -1
  128. package/dist/meeting/index.js +5311 -3871
  129. package/dist/meeting/index.js.map +1 -1
  130. package/dist/meeting/locusMediaRequest.js +292 -0
  131. package/dist/meeting/locusMediaRequest.js.map +1 -0
  132. package/dist/meeting/muteState.js +260 -183
  133. package/dist/meeting/muteState.js.map +1 -1
  134. package/dist/meeting/request.js +421 -347
  135. package/dist/meeting/request.js.map +1 -1
  136. package/dist/meeting/request.type.js +7 -0
  137. package/dist/meeting/request.type.js.map +1 -0
  138. package/dist/meeting/state.js +21 -31
  139. package/dist/meeting/state.js.map +1 -1
  140. package/dist/meeting/util.js +672 -585
  141. package/dist/meeting/util.js.map +1 -1
  142. package/dist/meeting/voicea-meeting.js +172 -0
  143. package/dist/meeting/voicea-meeting.js.map +1 -0
  144. package/dist/meeting-info/collection.js +6 -25
  145. package/dist/meeting-info/collection.js.map +1 -1
  146. package/dist/meeting-info/index.js +87 -39
  147. package/dist/meeting-info/index.js.map +1 -1
  148. package/dist/meeting-info/meeting-info-v2.js +352 -283
  149. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  150. package/dist/meeting-info/request.js +3 -15
  151. package/dist/meeting-info/request.js.map +1 -1
  152. package/dist/meeting-info/util.js +99 -183
  153. package/dist/meeting-info/util.js.map +1 -1
  154. package/dist/meeting-info/utilv2.js +147 -234
  155. package/dist/meeting-info/utilv2.js.map +1 -1
  156. package/dist/meetings/collection.js +43 -19
  157. package/dist/meetings/collection.js.map +1 -1
  158. package/dist/meetings/index.js +895 -600
  159. package/dist/meetings/index.js.map +1 -1
  160. package/dist/meetings/meetings.types.js +7 -0
  161. package/dist/meetings/meetings.types.js.map +1 -0
  162. package/dist/meetings/request.js +26 -41
  163. package/dist/meetings/request.js.map +1 -1
  164. package/dist/meetings/util.js +184 -157
  165. package/dist/meetings/util.js.map +1 -1
  166. package/dist/member/index.js +134 -85
  167. package/dist/member/index.js.map +1 -1
  168. package/dist/member/types.js +25 -0
  169. package/dist/member/types.js.map +1 -0
  170. package/dist/member/util.js +158 -88
  171. package/dist/member/util.js.map +1 -1
  172. package/dist/members/collection.js +13 -12
  173. package/dist/members/collection.js.map +1 -1
  174. package/dist/members/index.js +194 -204
  175. package/dist/members/index.js.map +1 -1
  176. package/dist/members/request.js +113 -68
  177. package/dist/members/request.js.map +1 -1
  178. package/dist/members/types.js +15 -0
  179. package/dist/members/types.js.map +1 -0
  180. package/dist/members/util.js +324 -259
  181. package/dist/members/util.js.map +1 -1
  182. package/dist/metrics/constants.js +19 -7
  183. package/dist/metrics/constants.js.map +1 -1
  184. package/dist/metrics/index.js +11 -558
  185. package/dist/metrics/index.js.map +1 -1
  186. package/dist/multistream/mediaRequestManager.js +263 -50
  187. package/dist/multistream/mediaRequestManager.js.map +1 -1
  188. package/dist/multistream/receiveSlot.js +58 -65
  189. package/dist/multistream/receiveSlot.js.map +1 -1
  190. package/dist/multistream/receiveSlotManager.js +76 -95
  191. package/dist/multistream/receiveSlotManager.js.map +1 -1
  192. package/dist/multistream/remoteMedia.js +62 -76
  193. package/dist/multistream/remoteMedia.js.map +1 -1
  194. package/dist/multistream/remoteMediaGroup.js +82 -45
  195. package/dist/multistream/remoteMediaGroup.js.map +1 -1
  196. package/dist/multistream/remoteMediaManager.js +657 -448
  197. package/dist/multistream/remoteMediaManager.js.map +1 -1
  198. package/dist/multistream/sendSlotManager.js +255 -0
  199. package/dist/multistream/sendSlotManager.js.map +1 -0
  200. package/dist/networkQualityMonitor/index.js +40 -59
  201. package/dist/networkQualityMonitor/index.js.map +1 -1
  202. package/dist/personal-meeting-room/index.js +21 -45
  203. package/dist/personal-meeting-room/index.js.map +1 -1
  204. package/dist/personal-meeting-room/request.js +1 -31
  205. package/dist/personal-meeting-room/request.js.map +1 -1
  206. package/dist/personal-meeting-room/util.js +0 -13
  207. package/dist/personal-meeting-room/util.js.map +1 -1
  208. package/dist/reachability/clusterReachability.js +356 -0
  209. package/dist/reachability/clusterReachability.js.map +1 -0
  210. package/dist/reachability/index.js +297 -460
  211. package/dist/reachability/index.js.map +1 -1
  212. package/dist/reachability/request.js +20 -26
  213. package/dist/reachability/request.js.map +1 -1
  214. package/dist/reachability/util.js +29 -0
  215. package/dist/reachability/util.js.map +1 -0
  216. package/dist/reactions/constants.js +13 -0
  217. package/dist/reactions/constants.js.map +1 -0
  218. package/dist/reactions/reactions.js +109 -0
  219. package/dist/reactions/reactions.js.map +1 -0
  220. package/dist/reactions/reactions.type.js +36 -0
  221. package/dist/reactions/reactions.type.js.map +1 -0
  222. package/dist/reconnection-manager/index.js +413 -483
  223. package/dist/reconnection-manager/index.js.map +1 -1
  224. package/dist/recording-controller/enums.js +17 -0
  225. package/dist/recording-controller/enums.js.map +1 -0
  226. package/dist/recording-controller/index.js +362 -0
  227. package/dist/recording-controller/index.js.map +1 -0
  228. package/dist/recording-controller/util.js +64 -0
  229. package/dist/recording-controller/util.js.map +1 -0
  230. package/dist/roap/index.js +102 -86
  231. package/dist/roap/index.js.map +1 -1
  232. package/dist/roap/request.js +131 -135
  233. package/dist/roap/request.js.map +1 -1
  234. package/dist/roap/turnDiscovery.js +437 -116
  235. package/dist/roap/turnDiscovery.js.map +1 -1
  236. package/dist/rtcMetrics/constants.js +12 -0
  237. package/dist/rtcMetrics/constants.js.map +1 -0
  238. package/dist/rtcMetrics/index.js +179 -0
  239. package/dist/rtcMetrics/index.js.map +1 -0
  240. package/dist/statsAnalyzer/global.js +1 -95
  241. package/dist/statsAnalyzer/global.js.map +1 -1
  242. package/dist/statsAnalyzer/index.js +557 -583
  243. package/dist/statsAnalyzer/index.js.map +1 -1
  244. package/dist/statsAnalyzer/mqaUtil.js +326 -130
  245. package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
  246. package/dist/transcription/index.js +22 -47
  247. package/dist/transcription/index.js.map +1 -1
  248. package/dist/types/annotation/annotation.types.d.ts +42 -0
  249. package/dist/types/annotation/constants.d.ts +31 -0
  250. package/dist/types/annotation/index.d.ts +117 -0
  251. package/dist/types/breakouts/breakout.d.ts +8 -0
  252. package/dist/types/breakouts/collection.d.ts +5 -0
  253. package/dist/types/breakouts/edit-lock-error.d.ts +15 -0
  254. package/dist/types/breakouts/events.d.ts +8 -0
  255. package/dist/types/breakouts/index.d.ts +5 -0
  256. package/dist/types/breakouts/request.d.ts +22 -0
  257. package/dist/types/breakouts/utils.d.ts +15 -0
  258. package/dist/types/common/browser-detection.d.ts +9 -0
  259. package/dist/types/common/collection.d.ts +48 -0
  260. package/dist/types/common/config.d.ts +2 -0
  261. package/dist/types/common/errors/captcha-error.d.ts +15 -0
  262. package/dist/types/common/errors/intent-to-join.d.ts +16 -0
  263. package/dist/types/common/errors/join-meeting.d.ts +17 -0
  264. package/dist/types/common/errors/media.d.ts +15 -0
  265. package/dist/types/common/errors/no-meeting-info.d.ts +14 -0
  266. package/dist/types/common/errors/parameter.d.ts +15 -0
  267. package/dist/types/common/errors/password-error.d.ts +15 -0
  268. package/dist/types/common/errors/permission.d.ts +14 -0
  269. package/dist/types/common/errors/reclaim-host-role-errors.d.ts +60 -0
  270. package/dist/types/common/errors/reconnection-in-progress.d.ts +9 -0
  271. package/dist/types/common/errors/reconnection.d.ts +15 -0
  272. package/dist/types/common/errors/stats.d.ts +15 -0
  273. package/dist/types/common/errors/webex-errors.d.ts +93 -0
  274. package/dist/types/common/errors/webex-meetings-error.d.ts +20 -0
  275. package/dist/types/common/events/events-scope.d.ts +17 -0
  276. package/dist/types/common/events/events.d.ts +12 -0
  277. package/dist/types/common/events/trigger-proxy.d.ts +2 -0
  278. package/dist/types/common/events/util.d.ts +2 -0
  279. package/dist/types/common/logs/logger-config.d.ts +2 -0
  280. package/dist/types/common/logs/logger-proxy.d.ts +2 -0
  281. package/dist/types/common/logs/request.d.ts +36 -0
  282. package/dist/types/common/queue.d.ts +34 -0
  283. package/dist/types/config.d.ts +72 -0
  284. package/dist/types/constants.d.ts +1088 -0
  285. package/dist/types/controls-options-manager/constants.d.ts +4 -0
  286. package/dist/types/controls-options-manager/enums.d.ts +15 -0
  287. package/dist/types/controls-options-manager/index.d.ts +136 -0
  288. package/dist/types/controls-options-manager/types.d.ts +43 -0
  289. package/dist/types/controls-options-manager/util.d.ts +1 -0
  290. package/dist/types/index.d.ts +7 -0
  291. package/dist/types/interceptors/index.d.ts +2 -0
  292. package/dist/types/interceptors/locusRetry.d.ts +27 -0
  293. package/dist/types/interpretation/collection.d.ts +5 -0
  294. package/dist/types/interpretation/index.d.ts +5 -0
  295. package/dist/types/interpretation/siLanguage.d.ts +5 -0
  296. package/dist/types/locus-info/controlsUtils.d.ts +2 -0
  297. package/dist/types/locus-info/embeddedAppsUtils.d.ts +2 -0
  298. package/dist/types/locus-info/fullState.d.ts +2 -0
  299. package/dist/types/locus-info/hostUtils.d.ts +2 -0
  300. package/dist/types/locus-info/index.d.ts +322 -0
  301. package/dist/types/locus-info/infoUtils.d.ts +2 -0
  302. package/dist/types/locus-info/mediaSharesUtils.d.ts +2 -0
  303. package/dist/types/locus-info/parser.d.ts +272 -0
  304. package/dist/types/locus-info/selfUtils.d.ts +2 -0
  305. package/dist/types/media/index.d.ts +34 -0
  306. package/dist/types/media/properties.d.ts +93 -0
  307. package/dist/types/media/util.d.ts +2 -0
  308. package/dist/types/mediaQualityMetrics/config.d.ts +241 -0
  309. package/dist/types/meeting/in-meeting-actions.d.ts +167 -0
  310. package/dist/types/meeting/index.d.ts +1824 -0
  311. package/dist/types/meeting/locusMediaRequest.d.ts +74 -0
  312. package/dist/types/meeting/muteState.d.ts +178 -0
  313. package/dist/types/meeting/request.d.ts +293 -0
  314. package/dist/types/meeting/request.type.d.ts +11 -0
  315. package/dist/types/meeting/state.d.ts +9 -0
  316. package/dist/types/meeting/util.d.ts +118 -0
  317. package/dist/types/meeting/voicea-meeting.d.ts +16 -0
  318. package/dist/types/meeting-info/collection.d.ts +20 -0
  319. package/dist/types/meeting-info/index.d.ts +69 -0
  320. package/dist/types/meeting-info/meeting-info-v2.d.ts +123 -0
  321. package/dist/types/meeting-info/request.d.ts +22 -0
  322. package/dist/types/meeting-info/util.d.ts +2 -0
  323. package/dist/types/meeting-info/utilv2.d.ts +2 -0
  324. package/dist/types/meetings/collection.d.ts +40 -0
  325. package/dist/types/meetings/index.d.ts +389 -0
  326. package/dist/types/meetings/meetings.types.d.ts +4 -0
  327. package/dist/types/meetings/request.d.ts +27 -0
  328. package/dist/types/meetings/util.d.ts +18 -0
  329. package/dist/types/member/index.d.ts +160 -0
  330. package/dist/types/member/types.d.ts +32 -0
  331. package/dist/types/member/util.d.ts +2 -0
  332. package/dist/types/members/collection.d.ts +29 -0
  333. package/dist/types/members/index.d.ts +353 -0
  334. package/dist/types/members/request.d.ts +114 -0
  335. package/dist/types/members/types.d.ts +25 -0
  336. package/dist/types/members/util.d.ts +215 -0
  337. package/dist/types/metrics/constants.d.ts +70 -0
  338. package/dist/types/metrics/index.d.ts +45 -0
  339. package/dist/types/multistream/mediaRequestManager.d.ts +120 -0
  340. package/dist/types/multistream/receiveSlot.d.ts +68 -0
  341. package/dist/types/multistream/receiveSlotManager.d.ts +56 -0
  342. package/dist/types/multistream/remoteMedia.d.ts +72 -0
  343. package/dist/types/multistream/remoteMediaGroup.d.ts +49 -0
  344. package/dist/types/multistream/remoteMediaManager.d.ts +301 -0
  345. package/dist/types/multistream/sendSlotManager.d.ts +70 -0
  346. package/dist/types/networkQualityMonitor/index.d.ts +70 -0
  347. package/dist/types/personal-meeting-room/index.d.ts +47 -0
  348. package/dist/types/personal-meeting-room/request.d.ts +14 -0
  349. package/dist/types/personal-meeting-room/util.d.ts +2 -0
  350. package/dist/types/reachability/clusterReachability.d.ts +109 -0
  351. package/dist/types/reachability/index.d.ts +105 -0
  352. package/dist/types/reachability/request.d.ts +39 -0
  353. package/dist/types/reachability/util.d.ts +8 -0
  354. package/dist/types/reactions/constants.d.ts +3 -0
  355. package/dist/types/reactions/reactions.d.ts +4 -0
  356. package/dist/types/reactions/reactions.type.d.ts +52 -0
  357. package/dist/types/reconnection-manager/index.d.ts +136 -0
  358. package/dist/types/recording-controller/enums.d.ts +7 -0
  359. package/dist/types/recording-controller/index.d.ts +207 -0
  360. package/dist/types/recording-controller/util.d.ts +14 -0
  361. package/dist/types/roap/index.d.ts +86 -0
  362. package/dist/types/roap/request.d.ts +39 -0
  363. package/dist/types/roap/turnDiscovery.d.ts +155 -0
  364. package/dist/types/rtcMetrics/constants.d.ts +4 -0
  365. package/dist/types/rtcMetrics/index.d.ts +61 -0
  366. package/dist/types/statsAnalyzer/global.d.ts +36 -0
  367. package/dist/types/statsAnalyzer/index.d.ts +217 -0
  368. package/dist/types/statsAnalyzer/mqaUtil.d.ts +48 -0
  369. package/dist/types/transcription/index.d.ts +64 -0
  370. package/dist/types/webinar/collection.d.ts +16 -0
  371. package/dist/types/webinar/index.d.ts +5 -0
  372. package/dist/webinar/collection.js +44 -0
  373. package/dist/webinar/collection.js.map +1 -0
  374. package/dist/webinar/index.js +69 -0
  375. package/dist/webinar/index.js.map +1 -0
  376. package/internal-README.md +7 -6
  377. package/package.json +30 -21
  378. package/src/annotation/annotation.types.ts +50 -0
  379. package/src/annotation/constants.ts +36 -0
  380. package/src/annotation/index.ts +328 -0
  381. package/src/breakouts/README.md +220 -0
  382. package/src/breakouts/breakout.ts +188 -0
  383. package/src/breakouts/collection.ts +19 -0
  384. package/src/breakouts/edit-lock-error.ts +25 -0
  385. package/src/breakouts/events.ts +56 -0
  386. package/src/breakouts/index.ts +925 -0
  387. package/src/breakouts/request.ts +55 -0
  388. package/src/breakouts/utils.ts +57 -0
  389. package/src/common/{browser-detection.js → browser-detection.ts} +9 -6
  390. package/src/common/collection.ts +9 -7
  391. package/src/common/{config.js → config.ts} +1 -1
  392. package/src/common/errors/{captcha-error.js → captcha-error.ts} +11 -7
  393. package/src/common/errors/{intent-to-join.js → intent-to-join.ts} +12 -7
  394. package/src/common/errors/{join-meeting.js → join-meeting.ts} +17 -8
  395. package/src/common/errors/{media.js → media.ts} +11 -7
  396. package/src/common/errors/no-meeting-info.ts +24 -0
  397. package/src/common/errors/parameter.ts +11 -7
  398. package/src/common/errors/{password-error.js → password-error.ts} +11 -7
  399. package/src/common/errors/{permission.js → permission.ts} +10 -6
  400. package/src/common/errors/reclaim-host-role-errors.ts +134 -0
  401. package/src/common/errors/{reconnection.js → reconnection.ts} +11 -7
  402. package/src/common/errors/{stats.js → stats.ts} +11 -7
  403. package/src/common/errors/{webex-errors.js → webex-errors.ts} +51 -8
  404. package/src/common/errors/{webex-meetings-error.js → webex-meetings-error.ts} +4 -2
  405. package/src/common/events/{events-scope.js → events-scope.ts} +6 -2
  406. package/src/common/events/{events.js → events.ts} +5 -1
  407. package/src/common/events/{trigger-proxy.js → trigger-proxy.ts} +9 -5
  408. package/src/common/events/{util.js → util.ts} +2 -3
  409. package/src/common/logs/{logger-config.js → logger-config.ts} +1 -2
  410. package/src/common/logs/logger-proxy.ts +44 -0
  411. package/src/common/logs/{request.js → request.ts} +26 -9
  412. package/src/common/queue.ts +22 -9
  413. package/src/{config.js → config.ts} +19 -21
  414. package/src/constants.ts +296 -27
  415. package/src/controls-options-manager/constants.ts +5 -0
  416. package/src/controls-options-manager/enums.ts +18 -0
  417. package/src/controls-options-manager/index.ts +278 -0
  418. package/src/controls-options-manager/types.ts +59 -0
  419. package/src/controls-options-manager/util.ts +300 -0
  420. package/src/index.ts +45 -0
  421. package/src/interceptors/index.ts +3 -0
  422. package/src/interceptors/locusRetry.ts +67 -0
  423. package/src/interpretation/README.md +60 -0
  424. package/src/interpretation/collection.ts +19 -0
  425. package/src/interpretation/index.ts +349 -0
  426. package/src/interpretation/siLanguage.ts +18 -0
  427. package/src/locus-info/controlsUtils.ts +222 -0
  428. package/src/locus-info/{embeddedAppsUtils.js → embeddedAppsUtils.ts} +5 -6
  429. package/src/locus-info/{fullState.js → fullState.ts} +16 -12
  430. package/src/locus-info/{hostUtils.js → hostUtils.ts} +9 -8
  431. package/src/locus-info/{index.js → index.ts} +561 -119
  432. package/src/locus-info/{infoUtils.js → infoUtils.ts} +29 -10
  433. package/src/locus-info/{mediaSharesUtils.js → mediaSharesUtils.ts} +97 -17
  434. package/src/locus-info/{parser.js → parser.ts} +303 -104
  435. package/src/locus-info/{selfUtils.js → selfUtils.ts} +199 -68
  436. package/src/media/index.ts +460 -0
  437. package/src/media/properties.ts +283 -0
  438. package/src/media/{util.js → util.ts} +2 -2
  439. package/src/mediaQualityMetrics/config.ts +249 -0
  440. package/src/meeting/in-meeting-actions.ts +199 -3
  441. package/src/meeting/index.ts +8494 -0
  442. package/src/meeting/locusMediaRequest.ts +313 -0
  443. package/src/meeting/muteState.ts +465 -0
  444. package/src/meeting/request.ts +912 -0
  445. package/src/meeting/request.type.ts +13 -0
  446. package/src/meeting/{state.js → state.ts} +50 -35
  447. package/src/meeting/util.ts +799 -0
  448. package/src/meeting/voicea-meeting.ts +122 -0
  449. package/src/meeting-info/{collection.js → collection.ts} +6 -2
  450. package/src/meeting-info/index.ts +210 -0
  451. package/src/meeting-info/meeting-info-v2.ts +423 -0
  452. package/src/meeting-info/{request.js → request.ts} +14 -4
  453. package/src/meeting-info/{util.js → util.ts} +70 -58
  454. package/src/meeting-info/{utilv2.js → utilv2.ts} +99 -82
  455. package/src/meetings/collection.ts +76 -0
  456. package/src/meetings/index.ts +1539 -0
  457. package/src/meetings/meetings.types.ts +12 -0
  458. package/src/meetings/{request.js → request.ts} +34 -25
  459. package/src/meetings/{util.js → util.ts} +133 -38
  460. package/src/member/{index.js → index.ts} +159 -56
  461. package/src/member/types.ts +38 -0
  462. package/src/member/util.ts +397 -0
  463. package/src/members/{collection.js → collection.ts} +10 -2
  464. package/src/members/{index.js → index.ts} +351 -146
  465. package/src/members/request.ts +255 -0
  466. package/src/members/types.ts +29 -0
  467. package/src/members/util.ts +353 -0
  468. package/src/metrics/{constants.js → constants.ts} +17 -6
  469. package/src/metrics/index.ts +73 -0
  470. package/src/multistream/mediaRequestManager.ts +341 -64
  471. package/src/multistream/receiveSlot.ts +69 -26
  472. package/src/multistream/receiveSlotManager.ts +66 -42
  473. package/src/multistream/remoteMedia.ts +40 -5
  474. package/src/multistream/remoteMediaGroup.ts +82 -3
  475. package/src/multistream/remoteMediaManager.ts +401 -81
  476. package/src/multistream/sendSlotManager.ts +199 -0
  477. package/src/networkQualityMonitor/{index.js → index.ts} +41 -29
  478. package/src/personal-meeting-room/{index.js → index.ts} +28 -19
  479. package/src/personal-meeting-room/{request.js → request.ts} +13 -4
  480. package/src/personal-meeting-room/{util.js → util.ts} +4 -4
  481. package/src/reachability/clusterReachability.ts +320 -0
  482. package/src/reachability/index.ts +371 -0
  483. package/src/reachability/request.ts +50 -35
  484. package/src/reachability/util.ts +24 -0
  485. package/src/reactions/constants.ts +4 -0
  486. package/src/reactions/reactions.ts +104 -0
  487. package/src/reactions/reactions.type.ts +62 -0
  488. package/src/reconnection-manager/index.ts +643 -0
  489. package/src/recording-controller/enums.ts +8 -0
  490. package/src/recording-controller/index.ts +332 -0
  491. package/src/recording-controller/util.ts +75 -0
  492. package/src/roap/index.ts +288 -0
  493. package/src/roap/request.ts +153 -0
  494. package/src/roap/turnDiscovery.ts +374 -70
  495. package/src/rtcMetrics/constants.ts +3 -0
  496. package/src/rtcMetrics/index.ts +166 -0
  497. package/src/statsAnalyzer/global.ts +37 -0
  498. package/src/statsAnalyzer/index.ts +1275 -0
  499. package/src/statsAnalyzer/mqaUtil.ts +440 -0
  500. package/src/transcription/{index.js → index.ts} +46 -39
  501. package/src/webinar/collection.ts +31 -0
  502. package/src/webinar/index.ts +62 -0
  503. package/test/integration/spec/converged-space-meetings.js +233 -0
  504. package/test/integration/spec/journey.js +791 -531
  505. package/test/integration/spec/space-meeting.js +391 -204
  506. package/test/integration/spec/transcription.js +7 -8
  507. package/test/unit/spec/annotation/index.ts +418 -0
  508. package/test/unit/spec/breakouts/breakout.ts +238 -0
  509. package/test/unit/spec/breakouts/collection.ts +15 -0
  510. package/test/unit/spec/breakouts/edit-lock-error.ts +30 -0
  511. package/test/unit/spec/breakouts/events.ts +89 -0
  512. package/test/unit/spec/breakouts/index.ts +1793 -0
  513. package/test/unit/spec/breakouts/request.ts +104 -0
  514. package/test/unit/spec/breakouts/utils.js +72 -0
  515. package/test/unit/spec/common/browser-detection.js +9 -28
  516. package/test/unit/spec/common/queue.js +31 -2
  517. package/test/unit/spec/controls-options-manager/index.js +287 -0
  518. package/test/unit/spec/controls-options-manager/util.js +582 -0
  519. package/test/unit/spec/fixture/locus.js +93 -90
  520. package/test/unit/spec/interceptors/locusRetry.ts +131 -0
  521. package/test/unit/spec/interpretation/collection.ts +15 -0
  522. package/test/unit/spec/interpretation/index.ts +625 -0
  523. package/test/unit/spec/interpretation/siLanguage.ts +28 -0
  524. package/test/unit/spec/locus-info/controlsUtils.js +325 -32
  525. package/test/unit/spec/locus-info/embeddedAppsUtils.js +8 -6
  526. package/test/unit/spec/locus-info/index.js +1458 -21
  527. package/test/unit/spec/locus-info/infoUtils.js +71 -40
  528. package/test/unit/spec/locus-info/lib/BasicSeqCmp.json +88 -430
  529. package/test/unit/spec/locus-info/lib/SeqCmp.json +529 -685
  530. package/test/unit/spec/locus-info/mediaSharesUtils.ts +41 -0
  531. package/test/unit/spec/locus-info/parser.js +119 -44
  532. package/test/unit/spec/locus-info/selfConstant.js +120 -103
  533. package/test/unit/spec/locus-info/selfUtils.js +291 -12
  534. package/test/unit/spec/media/index.ts +194 -111
  535. package/test/unit/spec/media/properties.ts +11 -11
  536. package/test/unit/spec/meeting/in-meeting-actions.ts +96 -3
  537. package/test/unit/spec/meeting/index.js +8616 -1921
  538. package/test/unit/spec/meeting/locusMediaRequest.ts +442 -0
  539. package/test/unit/spec/meeting/muteState.js +568 -207
  540. package/test/unit/spec/meeting/request.js +602 -82
  541. package/test/unit/spec/meeting/utils.js +867 -179
  542. package/test/unit/spec/meeting/voicea-meeting.ts +266 -0
  543. package/test/unit/spec/meeting-info/index.js +300 -0
  544. package/test/unit/spec/meeting-info/meetinginfov2.js +631 -78
  545. package/test/unit/spec/meeting-info/request.js +7 -9
  546. package/test/unit/spec/meeting-info/util.js +11 -12
  547. package/test/unit/spec/meeting-info/utilv2.js +131 -74
  548. package/test/unit/spec/meetings/collection.js +27 -1
  549. package/test/unit/spec/meetings/index.js +1826 -374
  550. package/test/unit/spec/meetings/utils.js +243 -14
  551. package/test/unit/spec/member/index.js +61 -7
  552. package/test/unit/spec/member/util.js +526 -26
  553. package/test/unit/spec/members/index.js +536 -55
  554. package/test/unit/spec/members/request.js +228 -40
  555. package/test/unit/spec/members/utils.js +217 -4
  556. package/test/unit/spec/metrics/index.js +13 -68
  557. package/test/unit/spec/multistream/mediaRequestManager.ts +1032 -110
  558. package/test/unit/spec/multistream/receiveSlot.ts +77 -18
  559. package/test/unit/spec/multistream/receiveSlotManager.ts +69 -39
  560. package/test/unit/spec/multistream/remoteMedia.ts +40 -2
  561. package/test/unit/spec/multistream/remoteMediaGroup.ts +350 -5
  562. package/test/unit/spec/multistream/remoteMediaManager.ts +937 -65
  563. package/test/unit/spec/multistream/sendSlotManager.ts +274 -0
  564. package/test/unit/spec/networkQualityMonitor/index.js +24 -18
  565. package/test/unit/spec/personal-meeting-room/personal-meeting-room.js +2 -7
  566. package/test/unit/spec/reachability/clusterReachability.ts +279 -0
  567. package/test/unit/spec/reachability/index.ts +606 -26
  568. package/test/unit/spec/reachability/request.js +68 -0
  569. package/test/unit/spec/reachability/util.ts +40 -0
  570. package/test/unit/spec/reconnection-manager/index.js +222 -34
  571. package/test/unit/spec/recording-controller/index.js +306 -0
  572. package/test/unit/spec/recording-controller/util.js +229 -0
  573. package/test/unit/spec/roap/index.ts +238 -82
  574. package/test/unit/spec/roap/request.ts +255 -0
  575. package/test/unit/spec/roap/turnDiscovery.ts +707 -110
  576. package/test/unit/spec/rtcMetrics/index.ts +122 -0
  577. package/test/unit/spec/stats-analyzer/index.js +1331 -62
  578. package/test/unit/spec/webinar/collection.ts +13 -0
  579. package/test/unit/spec/webinar/index.ts +60 -0
  580. package/test/utils/cmr.js +44 -42
  581. package/test/utils/constants.js +9 -0
  582. package/test/utils/integrationTestUtils.js +46 -0
  583. package/test/utils/testUtils.js +63 -99
  584. package/test/utils/webex-config.js +22 -18
  585. package/test/utils/webex-test-users.js +65 -50
  586. package/tsconfig.json +6 -0
  587. package/dist/media/internal-media-core-wrapper.js +0 -22
  588. package/dist/media/internal-media-core-wrapper.js.map +0 -1
  589. package/dist/meeting/effectsState.js +0 -327
  590. package/dist/meeting/effectsState.js.map +0 -1
  591. package/dist/metrics/config.js +0 -301
  592. package/dist/metrics/config.js.map +0 -1
  593. package/dist/multistream/multistreamMedia.js +0 -116
  594. package/dist/multistream/multistreamMedia.js.map +0 -1
  595. package/dist/peer-connection-manager/util.js +0 -124
  596. package/dist/peer-connection-manager/util.js.map +0 -1
  597. package/src/common/logs/logger-proxy.js +0 -33
  598. package/src/index.js +0 -15
  599. package/src/locus-info/controlsUtils.js +0 -102
  600. package/src/media/index.js +0 -459
  601. package/src/media/internal-media-core-wrapper.ts +0 -9
  602. package/src/media/properties.js +0 -289
  603. package/src/mediaQualityMetrics/config.js +0 -382
  604. package/src/meeting/effectsState.js +0 -205
  605. package/src/meeting/index.js +0 -6284
  606. package/src/meeting/muteState.js +0 -318
  607. package/src/meeting/request.js +0 -684
  608. package/src/meeting/util.js +0 -506
  609. package/src/meeting-info/index.js +0 -131
  610. package/src/meeting-info/meeting-info-v2.js +0 -255
  611. package/src/meetings/collection.js +0 -40
  612. package/src/meetings/index.js +0 -1015
  613. package/src/member/util.js +0 -254
  614. package/src/members/request.js +0 -131
  615. package/src/members/util.js +0 -258
  616. package/src/metrics/config.js +0 -324
  617. package/src/metrics/index.js +0 -530
  618. package/src/multistream/multistreamMedia.ts +0 -92
  619. package/src/peer-connection-manager/util.ts +0 -117
  620. package/src/reachability/index.js +0 -464
  621. package/src/reconnection-manager/index.js +0 -519
  622. package/src/roap/index.js +0 -220
  623. package/src/roap/request.js +0 -127
  624. package/src/statsAnalyzer/global.js +0 -133
  625. package/src/statsAnalyzer/index.js +0 -1006
  626. package/src/statsAnalyzer/mqaUtil.js +0 -173
  627. package/test/unit/spec/meeting/effectsState.js +0 -291
  628. package/test/unit/spec/peerconnection-manager/utils.test-fixtures.ts +0 -389
  629. /package/src/common/errors/{reconnection-in-progress.js → reconnection-in-progress.ts} +0 -0
@@ -3,12 +3,14 @@ import sinon from 'sinon';
3
3
  import {cloneDeep} from 'lodash';
4
4
  import {assert} from '@webex/test-helper-chai';
5
5
  import MockWebex from '@webex/test-helper-mock-webex';
6
+ import testUtils from '../../../utils/testUtils';
6
7
  import Meetings from '@webex/plugin-meetings';
7
8
  import LocusInfo from '@webex/plugin-meetings/src/locus-info';
8
9
  import SelfUtils from '@webex/plugin-meetings/src/locus-info/selfUtils';
9
10
  import InfoUtils from '@webex/plugin-meetings/src/locus-info/infoUtils';
10
11
  import EmbeddedAppsUtils from '@webex/plugin-meetings/src/locus-info/embeddedAppsUtils';
11
12
  import LocusDeltaParser from '@webex/plugin-meetings/src/locus-info/parser';
13
+ import Metrics from '@webex/plugin-meetings/src/metrics';
12
14
 
13
15
  import {
14
16
  LOCUSINFO,
@@ -16,6 +18,10 @@ import {
16
18
  LOCUSEVENT,
17
19
  EVENTS,
18
20
  DISPLAY_HINTS,
21
+ _CALL_,
22
+ LOCUS,
23
+ MEETING_STATE,
24
+ _MEETING_,
19
25
  } from '../../../../src/constants';
20
26
 
21
27
  import {self, selfWithInactivity} from './selfConstant';
@@ -33,6 +39,7 @@ describe('plugin-meetings', () => {
33
39
  const locus = {};
34
40
  const meetingId = 'meetingId';
35
41
  let locusInfo;
42
+ let sendBehavioralMetricStub;
36
43
 
37
44
  const webex = new MockWebex({
38
45
  children: {
@@ -59,6 +66,12 @@ describe('plugin-meetings', () => {
59
66
  },
60
67
  },
61
68
  };
69
+
70
+ sendBehavioralMetricStub = sinon.stub(Metrics, 'sendBehavioralMetric');
71
+ });
72
+
73
+ afterEach(() => {
74
+ sinon.restore();
62
75
  });
63
76
 
64
77
  describe('#updateControls', () => {
@@ -66,8 +79,12 @@ describe('plugin-meetings', () => {
66
79
 
67
80
  beforeEach('setup new controls', () => {
68
81
  newControls = {
82
+ disallowUnmute: {enabled: true},
69
83
  lock: {},
70
84
  meetingFull: {},
85
+ muteOnEntry: {enabled: true},
86
+ raiseHand: {enabled: true},
87
+ reactions: {enabled: true, showDisplayNameWithReactions: true},
71
88
  record: {
72
89
  recording: false,
73
90
  paused: false,
@@ -76,12 +93,14 @@ describe('plugin-meetings', () => {
76
93
  modifiedBy: 'George Kittle',
77
94
  },
78
95
  },
79
- shareControl: {},
96
+ shareControl: {control: 'example-value'},
80
97
  transcribe: {},
98
+ viewTheParticipantList: {enabled: true},
81
99
  meetingContainer: {
82
100
  meetingContainerUrl: 'http://new-url.com',
83
101
  },
84
102
  entryExitTone: {enabled: true, mode: 'foo'},
103
+ video: {enabled: true},
85
104
  };
86
105
  });
87
106
 
@@ -95,6 +114,97 @@ describe('plugin-meetings', () => {
95
114
  assert.equal(locusInfo.controls, newControls);
96
115
  });
97
116
 
117
+ it('should trigger the CONTROLS_MUTE_ON_ENTRY_CHANGED event when necessary', () => {
118
+ locusInfo.controls = {};
119
+ locusInfo.emitScoped = sinon.stub();
120
+ locusInfo.updateControls(newControls);
121
+
122
+ assert.calledWith(
123
+ locusInfo.emitScoped,
124
+ {file: 'locus-info', function: 'updateControls'},
125
+ LOCUSINFO.EVENTS.CONTROLS_MUTE_ON_ENTRY_CHANGED,
126
+ {state: newControls.muteOnEntry}
127
+ );
128
+ });
129
+
130
+ it('should trigger the CONTROLS_SHARE_CONTROL_CHANGED event when necessary', () => {
131
+ locusInfo.controls = {};
132
+ locusInfo.emitScoped = sinon.stub();
133
+ locusInfo.updateControls(newControls);
134
+
135
+ assert.calledWith(
136
+ locusInfo.emitScoped,
137
+ {file: 'locus-info', function: 'updateControls'},
138
+ LOCUSINFO.EVENTS.CONTROLS_SHARE_CONTROL_CHANGED,
139
+ {state: newControls.shareControl}
140
+ );
141
+ });
142
+
143
+ it('should trigger the CONTROLS_DISALLOW_UNMUTE_CHANGED event when necessary', () => {
144
+ locusInfo.controls = {};
145
+ locusInfo.emitScoped = sinon.stub();
146
+ locusInfo.updateControls(newControls);
147
+
148
+ assert.calledWith(
149
+ locusInfo.emitScoped,
150
+ {file: 'locus-info', function: 'updateControls'},
151
+ LOCUSINFO.EVENTS.CONTROLS_DISALLOW_UNMUTE_CHANGED,
152
+ {state: newControls.disallowUnmute}
153
+ );
154
+ });
155
+
156
+ it('should trigger the CONTROLS_REACTIONS_CHANGED event when necessary', () => {
157
+ locusInfo.controls = {};
158
+ locusInfo.emitScoped = sinon.stub();
159
+ locusInfo.updateControls(newControls);
160
+
161
+ assert.calledWith(
162
+ locusInfo.emitScoped,
163
+ {file: 'locus-info', function: 'updateControls'},
164
+ LOCUSINFO.EVENTS.CONTROLS_REACTIONS_CHANGED,
165
+ {state: newControls.reactions}
166
+ );
167
+ });
168
+
169
+ it('should trigger the CONTROLS_VIEW_THE_PARTICIPANTS_LIST_CHANGED event when necessary', () => {
170
+ locusInfo.controls = {};
171
+ locusInfo.emitScoped = sinon.stub();
172
+ locusInfo.updateControls(newControls);
173
+
174
+ assert.calledWith(
175
+ locusInfo.emitScoped,
176
+ {file: 'locus-info', function: 'updateControls'},
177
+ LOCUSINFO.EVENTS.CONTROLS_VIEW_THE_PARTICIPANTS_LIST_CHANGED,
178
+ {state: newControls.viewTheParticipantList}
179
+ );
180
+ });
181
+
182
+ it('should trigger the CONTROLS_RAISE_HAND_CHANGED event when necessary', () => {
183
+ locusInfo.controls = {};
184
+ locusInfo.emitScoped = sinon.stub();
185
+ locusInfo.updateControls(newControls);
186
+
187
+ assert.calledWith(
188
+ locusInfo.emitScoped,
189
+ {file: 'locus-info', function: 'updateControls'},
190
+ LOCUSINFO.EVENTS.CONTROLS_RAISE_HAND_CHANGED,
191
+ {state: newControls.raiseHand}
192
+ );
193
+ });
194
+
195
+ it('should trigger the CONTROLS_VIDEO_CHANGED event when necessary', () => {
196
+ locusInfo.controls = {};
197
+ locusInfo.emitScoped = sinon.stub();
198
+ locusInfo.updateControls(newControls);
199
+
200
+ assert.calledWith(
201
+ locusInfo.emitScoped,
202
+ {file: 'locus-info', function: 'updateControls'},
203
+ LOCUSINFO.EVENTS.CONTROLS_VIDEO_CHANGED,
204
+ {state: newControls.video}
205
+ );
206
+ });
207
+
98
208
  it('should not trigger the CONTROLS_RECORDING_UPDATED event', () => {
99
209
  locusInfo.controls = {};
100
210
  locusInfo.emitScoped = sinon.stub();
@@ -277,6 +387,48 @@ describe('plugin-meetings', () => {
277
387
  );
278
388
  });
279
389
 
390
+ it('should update the breakout state', () => {
391
+ locusInfo.emitScoped = sinon.stub();
392
+ let tmpStub = sinon.stub(SelfUtils, 'getReplacedBreakoutMoveId').returns('breakoutMoveId');
393
+ newControls.breakout = {breakout: {}};
394
+ let selfInfo = {};
395
+
396
+ locusInfo.updateControls(newControls, selfInfo);
397
+
398
+ assert.calledWith(
399
+ locusInfo.emitScoped,
400
+ {
401
+ file: 'locus-info',
402
+ function: 'updateControls',
403
+ },
404
+ LOCUSINFO.EVENTS.CONTROLS_MEETING_BREAKOUT_UPDATED,
405
+ {
406
+ breakout: newControls.breakout,
407
+ }
408
+ );
409
+ tmpStub.restore();
410
+ });
411
+
412
+ it('should update the interpretation state', () => {
413
+ locusInfo.emitScoped = sinon.stub();
414
+ newControls.interpretation = {siLanguages: [{languageCode: 20, languageName: 'en'}]};
415
+ let selfInfo = {};
416
+
417
+ locusInfo.updateControls(newControls, selfInfo);
418
+
419
+ assert.calledWith(
420
+ locusInfo.emitScoped,
421
+ {
422
+ file: 'locus-info',
423
+ function: 'updateControls',
424
+ },
425
+ LOCUSINFO.EVENTS.CONTROLS_MEETING_INTERPRETATION_UPDATED,
426
+ {
427
+ interpretation: newControls.interpretation,
428
+ }
429
+ );
430
+ });
431
+
280
432
  it('should update the transcript state', () => {
281
433
  locusInfo.emitScoped = sinon.stub();
282
434
  locusInfo.controls = {
@@ -398,6 +550,39 @@ describe('plugin-meetings', () => {
398
550
  assert.notEqual(x.args[1], LOCUSINFO.EVENTS.CONTROLS_ENTRY_EXIT_TONE_UPDATED);
399
551
  });
400
552
  });
553
+
554
+ it('should update videoEnabled when changed', () => {
555
+ locusInfo.controls = {};
556
+
557
+ locusInfo.emitScoped = sinon.stub();
558
+ locusInfo.updateControls(newControls);
559
+
560
+ assert.calledWith(
561
+ locusInfo.emitScoped,
562
+ {
563
+ file: 'locus-info',
564
+ function: 'updateControls',
565
+ },
566
+ LOCUSINFO.EVENTS.SELF_REMOTE_VIDEO_MUTE_STATUS_UPDATED,
567
+ {unmuteAllowed: true}
568
+ );
569
+
570
+ assert.equal(mockMeeting.unmuteVideoAllowed, true);
571
+ });
572
+
573
+ it('should not update videoEnabled when unchanged', () => {
574
+ locusInfo.controls = {videoEnabled: true};
575
+
576
+ locusInfo.emitScoped = sinon.stub();
577
+ locusInfo.updateControls(newControls);
578
+
579
+ locusInfo.emitScoped.getCalls().forEach((x) => {
580
+ // check that no calls in emitScoped are for SELF_REMOTE_VIDEO_MUTE_STATUS_UPDATED
581
+ assert.notEqual(x.args[1], LOCUSINFO.EVENTS.SELF_REMOTE_VIDEO_MUTE_STATUS_UPDATED);
582
+ });
583
+
584
+ assert.equal(mockMeeting.unmuteVideoAllowed, undefined);
585
+ });
401
586
  });
402
587
 
403
588
  describe('#updateParticipants()', () => {
@@ -451,6 +636,7 @@ describe('plugin-meetings', () => {
451
636
  selfIdentity: '123',
452
637
  selfId: '2',
453
638
  hostId: '3',
639
+ isReplace: undefined,
454
640
  }
455
641
  );
456
642
  // note: in a real use case, recordingId, selfId, and hostId would all be the same
@@ -458,6 +644,43 @@ describe('plugin-meetings', () => {
458
644
  // are being correctly grabbed from locusInfo.parsedLocus within updateParticipants
459
645
  });
460
646
 
647
+ it('should call with breakout control info', () => {
648
+ locusInfo.parsedLocus = {
649
+ controls: {
650
+ record: {
651
+ modifiedBy: '1',
652
+ },
653
+ },
654
+ self: {
655
+ selfIdentity: '123',
656
+ selfId: '2',
657
+ },
658
+ host: {
659
+ hostId: '3',
660
+ },
661
+ };
662
+
663
+ locusInfo.emitScoped = sinon.stub();
664
+ locusInfo.updateParticipants({}, true);
665
+
666
+ assert.calledWith(
667
+ locusInfo.emitScoped,
668
+ {
669
+ file: 'locus-info',
670
+ function: 'updateParticipants',
671
+ },
672
+ EVENTS.LOCUS_INFO_UPDATE_PARTICIPANTS,
673
+ {
674
+ participants: {},
675
+ recordingId: '1',
676
+ selfIdentity: '123',
677
+ selfId: '2',
678
+ hostId: '3',
679
+ isReplace: true,
680
+ }
681
+ );
682
+ });
683
+
461
684
  it('should update the deltaParticipants object', () => {
462
685
  const prev = locusInfo.deltaParticipants;
463
686
 
@@ -664,6 +887,122 @@ describe('plugin-meetings', () => {
664
887
  );
665
888
  });
666
889
 
890
+ describe('SELF_REMOTE_VIDEO_MUTE_STATUS_UPDATED', () => {
891
+ it('should emit event when video muted on entry', () => {
892
+ // usually "previous self" is just undefined when we get first self from locus
893
+ locusInfo.self = undefined;
894
+ const selfWithMutedByOthers = cloneDeep(self);
895
+
896
+ // remoteVideoMuted
897
+ selfWithMutedByOthers.controls.video.muted = true;
898
+
899
+ locusInfo.webex.internal.device.url = self.deviceUrl;
900
+ locusInfo.emitScoped = sinon.stub();
901
+ locusInfo.updateSelf(selfWithMutedByOthers, []);
902
+
903
+ assert.calledWith(
904
+ locusInfo.emitScoped,
905
+ {
906
+ file: 'locus-info',
907
+ function: 'updateSelf',
908
+ },
909
+ LOCUSINFO.EVENTS.SELF_REMOTE_VIDEO_MUTE_STATUS_UPDATED,
910
+ {muted: true}
911
+ );
912
+
913
+ // but sometimes "previous self" is defined, but without controls.audio.muted, so we test this here:
914
+ locusInfo.self = cloneDeep(self);
915
+ locusInfo.self.controls.video = {};
916
+
917
+ locusInfo.updateSelf(selfWithMutedByOthers, []);
918
+ assert.calledWith(
919
+ locusInfo.emitScoped,
920
+ {
921
+ file: 'locus-info',
922
+ function: 'updateSelf',
923
+ },
924
+ LOCUSINFO.EVENTS.SELF_REMOTE_VIDEO_MUTE_STATUS_UPDATED,
925
+ {muted: true}
926
+ );
927
+ });
928
+
929
+ it('should not emit event when not muted on entry', () => {
930
+ locusInfo.self = undefined;
931
+ const selfWithMutedByOthersFalse = cloneDeep(self);
932
+
933
+ selfWithMutedByOthersFalse.controls.video.muted = false;
934
+
935
+ locusInfo.webex.internal.device.url = self.deviceUrl;
936
+ locusInfo.emitScoped = sinon.stub();
937
+ locusInfo.updateSelf(selfWithMutedByOthersFalse, []);
938
+
939
+ // we might get some calls to emitScoped, but we need to check that none of them are for SELF_REMOTE_VIDEO_MUTE_STATUS_UPDATED
940
+ locusInfo.emitScoped.getCalls().forEach((x) => {
941
+ assert.notEqual(x.args[1], LOCUSINFO.EVENTS.SELF_REMOTE_VIDEO_MUTE_STATUS_UPDATED);
942
+ });
943
+ });
944
+
945
+ it('should emit event when remoteVideoMuted changed', () => {
946
+ locusInfo.self = self;
947
+ const selfWithMutedByOthers = cloneDeep(self);
948
+
949
+ selfWithMutedByOthers.controls.video.muted = true;
950
+
951
+ locusInfo.webex.internal.device.url = self.deviceUrl;
952
+ locusInfo.emitScoped = sinon.stub();
953
+ locusInfo.updateSelf(selfWithMutedByOthers, []);
954
+
955
+ assert.calledWith(
956
+ locusInfo.emitScoped,
957
+ {
958
+ file: 'locus-info',
959
+ function: 'updateSelf',
960
+ },
961
+ LOCUSINFO.EVENTS.SELF_REMOTE_VIDEO_MUTE_STATUS_UPDATED,
962
+ {muted: true}
963
+ );
964
+ });
965
+ });
966
+
967
+ it('should trigger SELF_MEETING_BREAKOUTS_CHANGED when breakouts changed', () => {
968
+ locusInfo.self = self;
969
+ const selfWithBreakoutsChanged = cloneDeep(self);
970
+
971
+ selfWithBreakoutsChanged.controls.breakout.sessions.active[0].name = 'new name';
972
+
973
+ locusInfo.emitScoped = sinon.stub();
974
+ locusInfo.updateSelf(selfWithBreakoutsChanged, []);
975
+
976
+ assert.calledWith(
977
+ locusInfo.emitScoped,
978
+ {
979
+ file: 'locus-info',
980
+ function: 'updateSelf',
981
+ },
982
+ LOCUSINFO.EVENTS.SELF_MEETING_BREAKOUTS_CHANGED,
983
+ {
984
+ breakoutSessions: {
985
+ active: [
986
+ {
987
+ name: 'new name',
988
+ groupId: '0e73abb8-5584-49d8-be8d-806d2a8247ca',
989
+ sessionId: '1cf41ab1-2e57-4d95-b7e9-5613acddfb0f',
990
+ sessionType: 'BREAKOUT',
991
+ },
992
+ ],
993
+ allowed: [
994
+ {
995
+ name: 'Breakout session 2',
996
+ groupId: '0e73abb8-5584-49d8-be8d-806d2a8247ca',
997
+ sessionId: '1cf41ab1-2e57-4d95-b7e9-5613acddfb0f',
998
+ sessionType: 'BREAKOUT',
999
+ },
1000
+ ],
1001
+ },
1002
+ }
1003
+ );
1004
+ });
1005
+
667
1006
  it('should trigger SELF_REMOTE_MUTE_STATUS_UPDATED if muted and disallowUnmute changed', () => {
668
1007
  locusInfo.self = self;
669
1008
  const selfWithMutedByOthersAndDissalowUnmute = cloneDeep(self);
@@ -735,6 +1074,8 @@ describe('plugin-meetings', () => {
735
1074
  const selfWithRequestedToUnmute = cloneDeep(self);
736
1075
 
737
1076
  selfWithRequestedToUnmute.controls.audio.requestedToUnmute = true;
1077
+ selfWithRequestedToUnmute.controls.audio.lastModifiedRequestedToUnmute =
1078
+ '2023-06-16T19:25:04.369Z';
738
1079
 
739
1080
  locusInfo.webex.internal.device.url = self.deviceUrl;
740
1081
  locusInfo.emitScoped = sinon.stub();
@@ -889,6 +1230,88 @@ describe('plugin-meetings', () => {
889
1230
  {isSharingBlocked: true}
890
1231
  );
891
1232
  });
1233
+
1234
+ it('should trigger SELF_ROLES_CHANGED if self roles changed', () => {
1235
+ locusInfo.self = self;
1236
+ locusInfo.emitScoped = sinon.stub();
1237
+ const sampleNewSelf = cloneDeep(self);
1238
+ sampleNewSelf.controls.role.roles = [{type: 'COHOST', hasRole: true}];
1239
+
1240
+ locusInfo.updateSelf(sampleNewSelf, []);
1241
+
1242
+ assert.calledWith(
1243
+ locusInfo.emitScoped,
1244
+ {
1245
+ file: 'locus-info',
1246
+ function: 'updateSelf',
1247
+ },
1248
+ LOCUSINFO.EVENTS.SELF_ROLES_CHANGED,
1249
+ {oldRoles: ['PRESENTER'], newRoles: ['COHOST']}
1250
+ );
1251
+ });
1252
+
1253
+ it('should not trigger SELF_ROLES_CHANGED if self roles not changed', () => {
1254
+ locusInfo.self = self;
1255
+ locusInfo.emitScoped = sinon.stub();
1256
+ const sampleNewSelf = cloneDeep(self);
1257
+ sampleNewSelf.controls.role.roles = [{type: 'PRESENTER', hasRole: true}];
1258
+
1259
+ locusInfo.updateSelf(sampleNewSelf, []);
1260
+
1261
+ assert.neverCalledWith(
1262
+ locusInfo.emitScoped,
1263
+ {
1264
+ file: 'locus-info',
1265
+ function: 'updateSelf',
1266
+ },
1267
+ LOCUSINFO.EVENTS.SELF_ROLES_CHANGED,
1268
+ {oldRoles: ['PRESENTER'], newRoles: ['PRESENTER']}
1269
+ );
1270
+ });
1271
+
1272
+ it('should trigger SELF_MEETING_INTERPRETATION_CHANGED if self interpretation info changed', () => {
1273
+ locusInfo.self = self;
1274
+ locusInfo.emitScoped = sinon.stub();
1275
+ const sampleNewSelf = cloneDeep(self);
1276
+ sampleNewSelf.controls.interpretation.targetLanguage = 'it';
1277
+
1278
+ locusInfo.updateSelf(sampleNewSelf, []);
1279
+
1280
+ assert.calledWith(
1281
+ locusInfo.emitScoped,
1282
+ {
1283
+ file: 'locus-info',
1284
+ function: 'updateSelf',
1285
+ },
1286
+ LOCUSINFO.EVENTS.SELF_MEETING_INTERPRETATION_CHANGED,
1287
+ {
1288
+ interpretation: sampleNewSelf.controls.interpretation,
1289
+ selfParticipantId: self.id,
1290
+ }
1291
+ );
1292
+ });
1293
+
1294
+ it('should not trigger SELF_MEETING_INTERPRETATION_CHANGED if self interpretation info not changed', () => {
1295
+ locusInfo.self = self;
1296
+ locusInfo.emitScoped = sinon.stub();
1297
+ const sampleNewSelf = cloneDeep(self);
1298
+ sampleNewSelf.controls.interpretation.targetLanguage = 'cn'; // same with previous one
1299
+
1300
+ locusInfo.updateSelf(sampleNewSelf, []);
1301
+
1302
+ assert.neverCalledWith(
1303
+ locusInfo.emitScoped,
1304
+ {
1305
+ file: 'locus-info',
1306
+ function: 'updateSelf',
1307
+ },
1308
+ LOCUSINFO.EVENTS.SELF_MEETING_INTERPRETATION_CHANGED,
1309
+ {
1310
+ interpretation: sampleNewSelf.controls.interpretation,
1311
+ selfParticipantId: self.id,
1312
+ }
1313
+ );
1314
+ });
892
1315
  });
893
1316
 
894
1317
  describe('#updateMeetingInfo', () => {
@@ -954,7 +1377,7 @@ describe('plugin-meetings', () => {
954
1377
  );
955
1378
  });
956
1379
 
957
- const checkMeetingInfoUpdatedCalled = (expected) => {
1380
+ const checkMeetingInfoUpdatedCalled = (expected, payload) => {
958
1381
  const expectedArgs = [
959
1382
  locusInfo.emitScoped,
960
1383
  {
@@ -962,43 +1385,148 @@ describe('plugin-meetings', () => {
962
1385
  function: 'updateMeetingInfo',
963
1386
  },
964
1387
  LOCUSINFO.EVENTS.MEETING_INFO_UPDATED,
965
- {info: locusInfo.parsedLocus.info, self},
1388
+ payload,
966
1389
  ];
967
1390
 
968
1391
  if (expected) {
969
1392
  assert.calledWith(...expectedArgs);
970
- }
971
- else {
1393
+ } else {
972
1394
  assert.neverCalledWith(...expectedArgs);
973
1395
  }
974
1396
  locusInfo.emitScoped.resetHistory();
975
1397
  };
976
1398
 
977
- it('emits MEETING_INFO_UPDATED if the info changes', () => {
978
- const initialInfo = cloneDeep(meetingInfo);
1399
+ const checkMeetingInfoUpdatedCalledForRoles = (expected, payload) => {
1400
+ const expectedArgs = [
1401
+ locusInfo.emitScoped,
1402
+ {
1403
+ file: 'locus-info',
1404
+ function: 'updateMeetingInfo',
1405
+ },
1406
+ LOCUSINFO.EVENTS.MEETING_INFO_UPDATED,
1407
+ payload,
1408
+ ];
979
1409
 
980
- locusInfo.emitScoped = sinon.stub();
1410
+ if (expected) {
1411
+ assert.calledWith(...expectedArgs);
1412
+ } else {
1413
+ assert.neverCalledWith(...expectedArgs);
1414
+ }
1415
+ locusInfo.emitScoped.resetHistory();
1416
+ };
1417
+
1418
+ it('emits MEETING_INFO_UPDATED and updates the meeting if the info changes', () => {
1419
+ const initialInfo = cloneDeep(meetingInfo);
1420
+
1421
+ let expectedMeeting;
1422
+
1423
+ /*
1424
+ When the event is triggered, it is required that the meeting has already
1425
+ been updated. This is why the meeting is being checked within the stubbed event emitter
1426
+ */
1427
+ sinon.stub(locusInfo, 'emitScoped').callsFake(() => {
1428
+ assert.deepEqual(mockMeeting, expectedMeeting);
1429
+ });
981
1430
 
982
1431
  // set the info initially as locusInfo.info starts as undefined
1432
+ expectedMeeting = {
1433
+ coHost: {
1434
+ LOWER_SOMEONE_ELSES_HAND: true,
1435
+ },
1436
+ isLocked: false,
1437
+ isUnlocked: true,
1438
+ moderator: {
1439
+ LOWER_SOMEONE_ELSES_HAND: true,
1440
+ },
1441
+ policy: {
1442
+ LOCK_STATUS_UNLOCKED: true,
1443
+ ROSTER_IN_MEETING: true,
1444
+ },
1445
+ userDisplayHints: ['ROSTER_IN_MEETING', 'LOCK_STATUS_UNLOCKED'],
1446
+ };
983
1447
  locusInfo.updateMeetingInfo(initialInfo, self);
984
1448
 
985
1449
  // since it was initially undefined, this should trigger the event
986
- checkMeetingInfoUpdatedCalled(true);
1450
+
1451
+ checkMeetingInfoUpdatedCalled(true, {isInitializing: false});
987
1452
 
988
1453
  const newInfo = cloneDeep(meetingInfo);
989
1454
 
990
1455
  newInfo.displayHints.coHost = [DISPLAY_HINTS.LOCK_CONTROL_LOCK];
991
1456
 
992
1457
  // Updating with different info should trigger the event
1458
+ expectedMeeting = {
1459
+ coHost: {
1460
+ LOWER_SOMEONE_ELSES_HAND: true,
1461
+ LOCK_CONTROL_LOCK: true,
1462
+ },
1463
+ isLocked: false,
1464
+ isUnlocked: true,
1465
+ moderator: {
1466
+ LOWER_SOMEONE_ELSES_HAND: true,
1467
+ },
1468
+ policy: {
1469
+ LOCK_STATUS_UNLOCKED: true,
1470
+ ROSTER_IN_MEETING: true,
1471
+ },
1472
+ userDisplayHints: ['ROSTER_IN_MEETING', 'LOCK_STATUS_UNLOCKED'],
1473
+ };
993
1474
  locusInfo.updateMeetingInfo(newInfo, self);
994
1475
 
995
- checkMeetingInfoUpdatedCalled(true);
1476
+ checkMeetingInfoUpdatedCalled(true, {isInitializing: false});
996
1477
 
997
1478
  // update it with the same info
1479
+ expectedMeeting = {
1480
+ coHost: {
1481
+ LOWER_SOMEONE_ELSES_HAND: true,
1482
+ LOCK_CONTROL_LOCK: true,
1483
+ },
1484
+ isLocked: false,
1485
+ isUnlocked: true,
1486
+ moderator: {
1487
+ LOWER_SOMEONE_ELSES_HAND: true,
1488
+ },
1489
+ policy: {
1490
+ LOCK_STATUS_UNLOCKED: true,
1491
+ ROSTER_IN_MEETING: true,
1492
+ },
1493
+ userDisplayHints: ['ROSTER_IN_MEETING', 'LOCK_STATUS_UNLOCKED'],
1494
+ };
998
1495
  locusInfo.updateMeetingInfo(newInfo, self);
999
1496
 
1000
1497
  // since the info is the same it should not call trigger the event
1001
- checkMeetingInfoUpdatedCalled(false);
1498
+ checkMeetingInfoUpdatedCalled(false, {isInitializing: false});
1499
+
1500
+ // update it with the same info, but roles changed
1501
+ const updateSelf = cloneDeep(self);
1502
+ updateSelf?.controls?.role?.roles.push({
1503
+ type: 'COHOST',
1504
+ hasRole: true,
1505
+ });
1506
+ expectedMeeting = {
1507
+ coHost: {
1508
+ LOWER_SOMEONE_ELSES_HAND: true,
1509
+ LOCK_CONTROL_LOCK: true,
1510
+ },
1511
+ isLocked: false,
1512
+ isUnlocked: true,
1513
+ moderator: {
1514
+ LOWER_SOMEONE_ELSES_HAND: true,
1515
+ },
1516
+ policy: {
1517
+ LOCK_STATUS_UNLOCKED: true,
1518
+ ROSTER_IN_MEETING: true,
1519
+ },
1520
+ userDisplayHints: [
1521
+ 'ROSTER_IN_MEETING',
1522
+ 'LOCK_STATUS_UNLOCKED',
1523
+ 'LOCK_CONTROL_LOCK',
1524
+ 'LOWER_SOMEONE_ELSES_HAND',
1525
+ ],
1526
+ };
1527
+ locusInfo.updateMeetingInfo(newInfo, updateSelf);
1528
+ // since the info is the same but roles changed, it should call trigger the event
1529
+ checkMeetingInfoUpdatedCalledForRoles(true, {isInitializing: false});
1002
1530
  });
1003
1531
 
1004
1532
  it('gets roles from self if available', () => {
@@ -1019,12 +1547,17 @@ describe('plugin-meetings', () => {
1019
1547
  roles: ['MODERATOR', 'COHOST'],
1020
1548
  };
1021
1549
 
1550
+ sinon.stub(locusInfo, 'emitScoped');
1551
+
1022
1552
  const parsedLocusInfo = cloneDeep(locusInfo.parsedLocus.info);
1023
1553
 
1024
1554
  locusInfo.updateMeetingInfo(initialInfo);
1025
1555
  assert.calledWith(isJoinedSpy, locusInfo.parsedLocus.self);
1026
1556
  assert.neverCalledWith(getRolesSpy, self);
1027
1557
  assert.calledWith(getInfosSpy, parsedLocusInfo, initialInfo, ['MODERATOR', 'COHOST']);
1558
+
1559
+ // since self is not passed to updateMeetingInfo, MEETING_INFO_UPDATED should be triggered with isIntializing: true
1560
+ checkMeetingInfoUpdatedCalledForRoles(true, {isInitializing: true});
1028
1561
  });
1029
1562
  });
1030
1563
 
@@ -1104,6 +1637,8 @@ describe('plugin-meetings', () => {
1104
1637
  fakeLocus = {
1105
1638
  meeting: true,
1106
1639
  participants: true,
1640
+ url: 'newLocusUrl',
1641
+ syncUrl: 'newSyncUrl',
1107
1642
  };
1108
1643
  });
1109
1644
 
@@ -1148,6 +1683,55 @@ describe('plugin-meetings', () => {
1148
1683
  assert.isFunction(locusParser.onDeltaAction);
1149
1684
  });
1150
1685
 
1686
+ it('#updateLocusInfo ignores breakout LEFT message', () => {
1687
+ const newLocus = {
1688
+ self: {
1689
+ reason: 'MOVED',
1690
+ state: 'LEFT',
1691
+ },
1692
+ };
1693
+
1694
+ locusInfo.updateControls = sinon.stub();
1695
+ locusInfo.updateConversationUrl = sinon.stub();
1696
+ locusInfo.updateCreated = sinon.stub();
1697
+ locusInfo.updateFullState = sinon.stub();
1698
+ locusInfo.updateHostInfo = sinon.stub();
1699
+ locusInfo.updateMeetingInfo = sinon.stub();
1700
+ locusInfo.updateMediaShares = sinon.stub();
1701
+ locusInfo.updateParticipantsUrl = sinon.stub();
1702
+ locusInfo.updateReplace = sinon.stub();
1703
+ locusInfo.updateSelf = sinon.stub();
1704
+ locusInfo.updateLocusUrl = sinon.stub();
1705
+ locusInfo.updateAclUrl = sinon.stub();
1706
+ locusInfo.updateBasequence = sinon.stub();
1707
+ locusInfo.updateSequence = sinon.stub();
1708
+ locusInfo.updateMemberShip = sinon.stub();
1709
+ locusInfo.updateIdentifiers = sinon.stub();
1710
+ locusInfo.updateEmbeddedApps = sinon.stub();
1711
+ locusInfo.compareAndUpdate = sinon.stub();
1712
+
1713
+ locusInfo.updateLocusInfo(newLocus);
1714
+
1715
+ assert.notCalled(locusInfo.updateControls);
1716
+ assert.notCalled(locusInfo.updateConversationUrl);
1717
+ assert.notCalled(locusInfo.updateCreated);
1718
+ assert.notCalled(locusInfo.updateFullState);
1719
+ assert.notCalled(locusInfo.updateHostInfo);
1720
+ assert.notCalled(locusInfo.updateMeetingInfo);
1721
+ assert.notCalled(locusInfo.updateMediaShares);
1722
+ assert.notCalled(locusInfo.updateParticipantsUrl);
1723
+ assert.notCalled(locusInfo.updateReplace);
1724
+ assert.notCalled(locusInfo.updateSelf);
1725
+ assert.notCalled(locusInfo.updateLocusUrl);
1726
+ assert.notCalled(locusInfo.updateAclUrl);
1727
+ assert.notCalled(locusInfo.updateBasequence);
1728
+ assert.notCalled(locusInfo.updateSequence);
1729
+ assert.notCalled(locusInfo.updateMemberShip);
1730
+ assert.notCalled(locusInfo.updateIdentifiers);
1731
+ assert.notCalled(locusInfo.updateEmbeddedApps);
1732
+ assert.notCalled(locusInfo.compareAndUpdate);
1733
+ });
1734
+
1151
1735
  it('onFullLocus() updates the working-copy of locus parser', () => {
1152
1736
  const eventType = 'fakeEvent';
1153
1737
 
@@ -1156,12 +1740,39 @@ describe('plugin-meetings', () => {
1156
1740
  sandbox.stub(locusInfo, 'updateParticipants');
1157
1741
  sandbox.stub(locusInfo, 'isMeetingActive');
1158
1742
  sandbox.stub(locusInfo, 'handleOneOnOneEvent');
1743
+ sandbox.stub(locusParser, 'isNewFullLocus').returns(true);
1159
1744
 
1160
1745
  locusInfo.onFullLocus(fakeLocus, eventType);
1161
1746
 
1162
1747
  assert.equal(fakeLocus, locusParser.workingCopy);
1163
1748
  });
1164
1749
 
1750
+ it('onFullLocus() does not do anything if the incoming full locus DTO is old', () => {
1751
+ const eventType = 'fakeEvent';
1752
+
1753
+ locusParser.workingCopy = {};
1754
+
1755
+ const oldWorkingCopy = locusParser.workingCopy;
1756
+
1757
+ const spies = [
1758
+ sandbox.stub(locusInfo, 'updateParticipantDeltas'),
1759
+ sandbox.stub(locusInfo, 'updateLocusInfo'),
1760
+ sandbox.stub(locusInfo, 'updateParticipants'),
1761
+ sandbox.stub(locusInfo, 'isMeetingActive'),
1762
+ sandbox.stub(locusInfo, 'handleOneOnOneEvent'),
1763
+ ];
1764
+
1765
+ sandbox.stub(locusParser, 'isNewFullLocus').returns(false);
1766
+
1767
+ locusInfo.onFullLocus(fakeLocus, eventType);
1768
+
1769
+ spies.forEach((spy) => {
1770
+ assert.notCalled(spy);
1771
+ });
1772
+
1773
+ assert.equal(oldWorkingCopy, locusParser.workingCopy);
1774
+ });
1775
+
1165
1776
  it('onDeltaAction applies locus delta data to meeting', () => {
1166
1777
  const action = 'fake action';
1167
1778
  const parsedLoci = 'fake loci';
@@ -1188,33 +1799,82 @@ describe('plugin-meetings', () => {
1188
1799
  assert.calledWith(meeting.locusInfo.onDeltaLocus, fakeLocus);
1189
1800
  });
1190
1801
 
1191
- it('applyLocusDeltaData gets full locus on DESYNC action', () => {
1802
+ it('applyLocusDeltaData gets delta locus on DESYNC action if we have a syncUrl', () => {
1803
+ const {DESYNC} = LocusDeltaParser.loci;
1804
+ const fakeDeltaLocus = {id: 'fake delta locus'};
1805
+ const meeting = {
1806
+ meetingRequest: {
1807
+ getLocusDTO: sandbox.stub().resolves({body: fakeDeltaLocus}),
1808
+ },
1809
+ locusInfo: {
1810
+ handleLocusDelta: sandbox.stub(),
1811
+ },
1812
+ locusUrl: 'oldLocusUrl',
1813
+ };
1814
+
1815
+ locusInfo.locusParser.workingCopy = {
1816
+ syncUrl: 'oldSyncUrl',
1817
+ };
1818
+
1819
+ // Since we have a promise inside a function we want to test that's not returned,
1820
+ // we will wait and stub it's last function to resolve this waiting promise.
1821
+ // Also ensures .handleLocusDelta() is called before .resume()
1822
+ return new Promise((resolve) => {
1823
+ locusInfo.locusParser.resume = sandbox.stub().callsFake(() => resolve());
1824
+ locusInfo.applyLocusDeltaData(DESYNC, fakeLocus, meeting);
1825
+ }).then(() => {
1826
+ assert.calledOnceWithExactly(meeting.meetingRequest.getLocusDTO, {url: 'oldSyncUrl'});
1827
+
1828
+ assert.calledOnceWithExactly(meeting.locusInfo.handleLocusDelta, fakeDeltaLocus, meeting);
1829
+ assert.calledOnce(locusInfo.locusParser.resume);
1830
+ });
1831
+ });
1832
+
1833
+ it('applyLocusDeltaData gets delta locus on DESYNC action if we have a syncUrl (empty response body)', () => {
1192
1834
  const {DESYNC} = LocusDeltaParser.loci;
1193
1835
  const meeting = {
1194
1836
  meetingRequest: {
1195
- getFullLocus: sandbox.stub().resolves(true),
1837
+ getLocusDTO: sandbox.stub().resolves({body: {}}),
1196
1838
  },
1197
1839
  locusInfo: {
1840
+ handleLocusDelta: sandbox.stub(),
1198
1841
  onFullLocus: sandbox.stub(),
1199
1842
  },
1843
+ locusUrl: 'oldLocusUrl',
1844
+ };
1845
+
1846
+ locusInfo.locusParser.workingCopy = {
1847
+ syncUrl: 'oldSyncUrl',
1200
1848
  };
1201
1849
 
1202
- locusInfo.locusParser.resume = sandbox.stub();
1203
- locusInfo.applyLocusDeltaData(DESYNC, fakeLocus, meeting);
1850
+ // Since we have a promise inside a function we want to test that's not returned,
1851
+ // we will wait and stub it's last function to resolve this waiting promise.
1852
+ return new Promise((resolve) => {
1853
+ locusInfo.locusParser.resume = sandbox.stub().callsFake(() => resolve());
1854
+ locusInfo.applyLocusDeltaData(DESYNC, fakeLocus, meeting);
1855
+ }).then(() => {
1856
+ assert.calledOnceWithExactly(meeting.meetingRequest.getLocusDTO, {url: 'oldSyncUrl'});
1204
1857
 
1205
- assert.calledOnce(meeting.meetingRequest.getFullLocus);
1858
+ assert.notCalled(meeting.locusInfo.handleLocusDelta);
1859
+ assert.notCalled(meeting.locusInfo.onFullLocus);
1860
+ assert.calledOnce(locusInfo.locusParser.resume);
1861
+ });
1206
1862
  });
1207
1863
 
1208
- it('getFullLocus handles DESYNC action correctly', () => {
1864
+ it('applyLocusDeltaData gets full locus on DESYNC action if we do not have a syncUrl', () => {
1209
1865
  const {DESYNC} = LocusDeltaParser.loci;
1866
+ const fakeFullLocusDto = {id: 'fake full locus dto'};
1210
1867
  const meeting = {
1211
1868
  meetingRequest: {
1212
- getFullLocus: sandbox.stub().resolves({body: true}),
1869
+ getLocusDTO: sandbox.stub().resolves({body: fakeFullLocusDto}),
1213
1870
  },
1214
- locusInfo,
1871
+ locusInfo: {
1872
+ onFullLocus: sandbox.stub(),
1873
+ },
1874
+ locusUrl: 'oldLocusUrl',
1215
1875
  };
1216
1876
 
1217
- locusInfo.onFullLocus = sandbox.stub();
1877
+ locusInfo.locusParser.workingCopy = {}; // no syncUrl
1218
1878
 
1219
1879
  // Since we have a promise inside a function we want to test that's not returned,
1220
1880
  // we will wait and stub it's last function to resolve this waiting promise.
@@ -1223,10 +1883,386 @@ describe('plugin-meetings', () => {
1223
1883
  locusInfo.locusParser.resume = sandbox.stub().callsFake(() => resolve());
1224
1884
  locusInfo.applyLocusDeltaData(DESYNC, fakeLocus, meeting);
1225
1885
  }).then(() => {
1226
- assert.calledOnce(meeting.locusInfo.onFullLocus);
1886
+ assert.calledOnceWithExactly(meeting.meetingRequest.getLocusDTO, {url: 'oldLocusUrl'});
1887
+
1888
+ assert.calledOnceWithExactly(meeting.locusInfo.onFullLocus, fakeFullLocusDto);
1227
1889
  assert.calledOnce(locusInfo.locusParser.resume);
1228
1890
  });
1229
1891
  });
1892
+
1893
+ it('applyLocusDeltaData handles LOCUS_URL_CHANGED action correctly', () => {
1894
+ const {LOCUS_URL_CHANGED} = LocusDeltaParser.loci;
1895
+ const fakeDeltaLocus = {id: 'fake delta locus'};
1896
+ const meeting = {
1897
+ meetingRequest: {
1898
+ getLocusDTO: sandbox.stub().resolves({body: fakeDeltaLocus}),
1899
+ },
1900
+ locusInfo: {
1901
+ handleLocusDelta: sandbox.stub(),
1902
+ },
1903
+ locusUrl: 'current locus url',
1904
+ };
1905
+
1906
+ locusInfo.locusParser.workingCopy = {
1907
+ syncUrl: 'current sync url',
1908
+ };
1909
+
1910
+ locusInfo.applyLocusDeltaData(LOCUS_URL_CHANGED, fakeLocus, meeting);
1911
+ assert.calledOnceWithExactly(meeting.meetingRequest.getLocusDTO, {url: 'current sync url'});
1912
+ });
1913
+
1914
+ describe('edge cases for sync failing', () => {
1915
+ const {DESYNC} = LocusDeltaParser.loci;
1916
+ const fakeFullLocusDto = {id: 'fake full locus dto'};
1917
+ let meeting;
1918
+
1919
+ beforeEach(() => {
1920
+ sinon.stub(locusInfo.locusParser, 'resume');
1921
+ sinon.stub(webex.meetings, 'destroy');
1922
+
1923
+ meeting = {
1924
+ meetingRequest: {
1925
+ getLocusDTO: sandbox.stub(),
1926
+ },
1927
+ locusInfo: {
1928
+ handleLocusDelta: sandbox.stub(),
1929
+ onFullLocus: sandbox.stub(),
1930
+ },
1931
+ locusUrl: 'fullSyncUrl',
1932
+ };
1933
+
1934
+ locusInfo.locusParser.workingCopy = {
1935
+ syncUrl: 'deltaSyncUrl',
1936
+ };
1937
+ });
1938
+
1939
+ it('applyLocusDeltaData gets full locus on DESYNC action if we do not have a syncUrl and destroys the meeting if that fails', () => {
1940
+ meeting.meetingRequest.getLocusDTO.rejects(new Error('fake error'));
1941
+
1942
+ locusInfo.locusParser.workingCopy = {}; // no syncUrl
1943
+
1944
+ // Since we have a promise inside a function we want to test that's not returned,
1945
+ // we will wait and stub it's last function to resolve this waiting promise.
1946
+ return new Promise((resolve) => {
1947
+ webex.meetings.destroy.callsFake(() => resolve());
1948
+ locusInfo.applyLocusDeltaData(DESYNC, fakeLocus, meeting);
1949
+ }).then(() => {
1950
+ assert.calledOnceWithExactly(meeting.meetingRequest.getLocusDTO, {url: 'fullSyncUrl'});
1951
+
1952
+ assert.notCalled(meeting.locusInfo.handleLocusDelta);
1953
+ assert.notCalled(meeting.locusInfo.onFullLocus);
1954
+ assert.notCalled(locusInfo.locusParser.resume);
1955
+
1956
+ assert.calledOnceWithExactly(webex.meetings.destroy, meeting, 'LOCUS_DTO_SYNC_FAILED');
1957
+ });
1958
+ });
1959
+
1960
+ it('applyLocusDeltaData first tries a delta sync on DESYNC action and if that fails, does a full locus sync', () => {
1961
+ meeting.meetingRequest.getLocusDTO.onCall(0).rejects(new Error('fake error'));
1962
+ meeting.meetingRequest.getLocusDTO.onCall(1).resolves({body: fakeFullLocusDto});
1963
+
1964
+ // Since we have a promise inside a function we want to test that's not returned,
1965
+ // we will wait and stub it's last function to resolve this waiting promise.
1966
+ return new Promise((resolve) => {
1967
+ locusInfo.locusParser.resume.callsFake(() => resolve());
1968
+ locusInfo.applyLocusDeltaData(DESYNC, fakeLocus, meeting);
1969
+ }).then(() => {
1970
+ assert.calledTwice(meeting.meetingRequest.getLocusDTO);
1971
+
1972
+ assert.deepEqual(meeting.meetingRequest.getLocusDTO.getCalls()[0].args, [
1973
+ {url: 'deltaSyncUrl'},
1974
+ ]);
1975
+ assert.deepEqual(meeting.meetingRequest.getLocusDTO.getCalls()[1].args, [
1976
+ {url: 'fullSyncUrl'},
1977
+ ]);
1978
+
1979
+ assert.calledWith(sendBehavioralMetricStub, 'js_sdk_locus_delta_sync_failed', {
1980
+ correlationId: meeting.correlationId,
1981
+ url: 'deltaSyncUrl',
1982
+ reason: 'fake error',
1983
+ errorName: 'Error',
1984
+ stack: sinon.match.any,
1985
+ code: sinon.match.any,
1986
+ });
1987
+
1988
+ assert.notCalled(meeting.locusInfo.handleLocusDelta);
1989
+ assert.calledOnceWithExactly(meeting.locusInfo.onFullLocus, fakeFullLocusDto);
1990
+ assert.calledOnce(locusInfo.locusParser.resume);
1991
+ });
1992
+ });
1993
+
1994
+ it('applyLocusDeltaData destroys the meeting if both delta sync and full sync fail', () => {
1995
+ meeting.meetingRequest.getLocusDTO.rejects(new Error('fake error'));
1996
+
1997
+ // Since we have a promise inside a function we want to test that's not returned,
1998
+ // we will wait and stub it's last function to resolve this waiting promise.
1999
+ return new Promise((resolve) => {
2000
+ webex.meetings.destroy.callsFake(() => resolve());
2001
+ locusInfo.applyLocusDeltaData(DESYNC, fakeLocus, meeting);
2002
+ }).then(() => {
2003
+ assert.calledTwice(meeting.meetingRequest.getLocusDTO);
2004
+
2005
+ assert.deepEqual(meeting.meetingRequest.getLocusDTO.getCalls()[0].args, [
2006
+ {url: 'deltaSyncUrl'},
2007
+ ]);
2008
+ assert.deepEqual(meeting.meetingRequest.getLocusDTO.getCalls()[1].args, [
2009
+ {url: 'fullSyncUrl'},
2010
+ ]);
2011
+
2012
+ assert.calledWith(sendBehavioralMetricStub, 'js_sdk_locus_delta_sync_failed', {
2013
+ correlationId: meeting.correlationId,
2014
+ url: 'deltaSyncUrl',
2015
+ reason: 'fake error',
2016
+ errorName: 'Error',
2017
+ stack: sinon.match.any,
2018
+ code: sinon.match.any,
2019
+ });
2020
+
2021
+ assert.notCalled(meeting.locusInfo.handleLocusDelta);
2022
+ assert.notCalled(meeting.locusInfo.onFullLocus);
2023
+ assert.notCalled(locusInfo.locusParser.resume);
2024
+
2025
+ assert.calledOnceWithExactly(webex.meetings.destroy, meeting, 'LOCUS_DTO_SYNC_FAILED');
2026
+ });
2027
+ });
2028
+ });
2029
+
2030
+ it('onDeltaLocus handle delta data', () => {
2031
+ fakeLocus.participants = {};
2032
+ const fakeBreakout = {
2033
+ sessionId: 'sessionId',
2034
+ groupId: 'groupId',
2035
+ };
2036
+
2037
+ fakeLocus.controls = {
2038
+ breakout: fakeBreakout,
2039
+ };
2040
+ locusInfo.controls = {
2041
+ breakout: {
2042
+ sessionId: 'sessionId',
2043
+ groupId: 'groupId',
2044
+ },
2045
+ };
2046
+ locusInfo.updateParticipants = sinon.stub();
2047
+ locusInfo.onDeltaLocus(fakeLocus);
2048
+ assert.calledWith(locusInfo.updateParticipants, {}, false);
2049
+
2050
+ fakeLocus.controls.breakout.sessionId = 'sessionId2';
2051
+ locusInfo.onDeltaLocus(fakeLocus);
2052
+ assert.calledWith(locusInfo.updateParticipants, {}, true);
2053
+ });
2054
+ });
2055
+
2056
+ describe('#updateLocusCache', () => {
2057
+ it('cache it if income locus is main session locus', () => {
2058
+ const locus = {url: 'url'};
2059
+ locusInfo.mainSessionLocusCache = null;
2060
+ locusInfo.updateLocusCache(locus);
2061
+
2062
+ assert.deepEqual(locusInfo.mainSessionLocusCache, locus);
2063
+ });
2064
+
2065
+ it('not cache it if income locus is breakout session locus', () => {
2066
+ const locus = {url: 'url', controls: {breakout: {sessionType: 'BREAKOUT'}}};
2067
+ locusInfo.mainSessionLocusCache = null;
2068
+ locusInfo.updateLocusCache(locus);
2069
+
2070
+ assert.isNull(locusInfo.mainSessionLocusCache);
2071
+ });
2072
+ });
2073
+
2074
+ describe('#getTheLocusToUpdate', () => {
2075
+ it('return the cache locus if return to main session and do not clear main session cache', () => {
2076
+ locusInfo.mainSessionLocusCache = {url: 'url'};
2077
+ locusInfo.controls = {
2078
+ breakout: {
2079
+ sessionType: 'BREAKOUT',
2080
+ },
2081
+ };
2082
+ const newLocus = {
2083
+ controls: {
2084
+ breakout: {
2085
+ sessionType: 'MAIN',
2086
+ },
2087
+ },
2088
+ };
2089
+
2090
+ assert.deepEqual(locusInfo.getTheLocusToUpdate(newLocus), {url: 'url'});
2091
+
2092
+ locusInfo.clearMainSessionLocusCache = sinon.stub();
2093
+ locusInfo.getTheLocusToUpdate(newLocus);
2094
+ assert.notCalled(locusInfo.clearMainSessionLocusCache);
2095
+ });
2096
+
2097
+ it('return the new locus if return to main session but no cache and do not clear main session cache', () => {
2098
+ locusInfo.mainSessionLocusCache = null;
2099
+ locusInfo.controls = {
2100
+ breakout: {
2101
+ sessionType: 'BREAKOUT',
2102
+ },
2103
+ };
2104
+ const newLocus = {
2105
+ controls: {
2106
+ breakout: {
2107
+ sessionType: 'MAIN',
2108
+ },
2109
+ },
2110
+ };
2111
+
2112
+ assert.deepEqual(locusInfo.getTheLocusToUpdate(newLocus), newLocus);
2113
+
2114
+ locusInfo.clearMainSessionLocusCache = sinon.stub();
2115
+ locusInfo.getTheLocusToUpdate(newLocus);
2116
+ assert.notCalled(locusInfo.clearMainSessionLocusCache);
2117
+ });
2118
+
2119
+ it('return the new locus if not return to main session and clear main session cache', () => {
2120
+ locusInfo.mainSessionLocusCache = {
2121
+ controls: {
2122
+ breakout: {
2123
+ sessionType: 'MAIN',
2124
+ },
2125
+ },
2126
+ self: {removed: true},
2127
+ };
2128
+ locusInfo.fullState = {state: 'ACTIVE'};
2129
+ locusInfo.controls = {
2130
+ breakout: {
2131
+ sessionType: 'MAIN',
2132
+ },
2133
+ };
2134
+ const newLocus = {
2135
+ controls: {
2136
+ breakout: {
2137
+ sessionType: 'BREAKOUT',
2138
+ },
2139
+ },
2140
+ };
2141
+
2142
+ locusInfo.clearMainSessionLocusCache = sinon.stub();
2143
+ const result = locusInfo.getTheLocusToUpdate(newLocus);
2144
+ assert.calledOnce(locusInfo.clearMainSessionLocusCache);
2145
+
2146
+ assert.deepEqual(result, newLocus);
2147
+ });
2148
+
2149
+ it('do not clear main session cache when "mainSessionLocusCache?.self?.removed" is not true', () => {
2150
+ locusInfo.mainSessionLocusCache = {
2151
+ controls: {
2152
+ breakout: {
2153
+ sessionType: 'MAIN',
2154
+ },
2155
+ },
2156
+ self: {removed: undefined},
2157
+ };
2158
+ locusInfo.fullState = {state: 'ACTIVE'};
2159
+ locusInfo.controls = {
2160
+ breakout: {
2161
+ sessionType: 'MAIN',
2162
+ },
2163
+ };
2164
+ const newLocus = {
2165
+ controls: {
2166
+ breakout: {
2167
+ sessionType: 'BREAKOUT',
2168
+ },
2169
+ },
2170
+ };
2171
+
2172
+ locusInfo.clearMainSessionLocusCache = sinon.stub();
2173
+ locusInfo.getTheLocusToUpdate(newLocus);
2174
+ assert.notCalled(locusInfo.clearMainSessionLocusCache);
2175
+ });
2176
+ });
2177
+
2178
+ describe('#mergeParticipants', () => {
2179
+ let participants;
2180
+ let sourceParticipants;
2181
+ beforeEach(() => {
2182
+ participants = [{id: '111', status: 'JOINED'}, {id: '222'}];
2183
+ sourceParticipants = [{id: '111', status: 'LEFT'}, {id: '333'}];
2184
+ });
2185
+
2186
+ it('merge the participants, replace it by id if exist in old array', () => {
2187
+ const result = locusInfo.mergeParticipants(participants, sourceParticipants);
2188
+ assert.deepEqual(result, [{id: '111', status: 'LEFT'}, {id: '222'}, {id: '333'}]);
2189
+ });
2190
+
2191
+ it('return new participants if previous participants is empty', () => {
2192
+ const result = locusInfo.mergeParticipants([], sourceParticipants);
2193
+ assert.deepEqual(result, sourceParticipants);
2194
+ });
2195
+
2196
+ it('return new participants if previous participants is null/undefined', () => {
2197
+ let result = locusInfo.mergeParticipants(null, sourceParticipants);
2198
+ assert.deepEqual(result, sourceParticipants);
2199
+
2200
+ result = locusInfo.mergeParticipants(undefined, sourceParticipants);
2201
+ assert.deepEqual(result, sourceParticipants);
2202
+ });
2203
+
2204
+ it('return previous participants if new participants is empty', () => {
2205
+ const result = locusInfo.mergeParticipants(participants, []);
2206
+ assert.deepEqual(result, participants);
2207
+ });
2208
+
2209
+ it('return previous participants if new participants is null/undefined', () => {
2210
+ let result = locusInfo.mergeParticipants(participants, null);
2211
+ assert.deepEqual(result, participants);
2212
+
2213
+ result = locusInfo.mergeParticipants(participants, undefined);
2214
+ assert.deepEqual(result, participants);
2215
+ });
2216
+ });
2217
+
2218
+ describe('#updateMainSessionLocusCache', () => {
2219
+ let cachedLocus;
2220
+ let newLocus;
2221
+ beforeEach(() => {
2222
+ cachedLocus = {
2223
+ controls: {},
2224
+ participants: [],
2225
+ info: {webExMeetingId: 'testId1', topic: 'test'},
2226
+ };
2227
+ newLocus = {
2228
+ self: {},
2229
+ participants: [{id: '111'}],
2230
+ info: {testId: 'testId2', webExMeetingName: 'hello'},
2231
+ };
2232
+ });
2233
+ it('shallow merge new locus into cache', () => {
2234
+ locusInfo.mainSessionLocusCache = cachedLocus;
2235
+ locusInfo.updateMainSessionLocusCache(newLocus);
2236
+
2237
+ assert.deepEqual(locusInfo.mainSessionLocusCache, {
2238
+ controls: {},
2239
+ participants: [{id: '111'}],
2240
+ info: {testId: 'testId2', webExMeetingName: 'hello'},
2241
+ self: {},
2242
+ });
2243
+ });
2244
+
2245
+ it('cache new locus if no cache before', () => {
2246
+ locusInfo.mainSessionLocusCache = null;
2247
+ locusInfo.updateMainSessionLocusCache(newLocus);
2248
+
2249
+ assert.deepEqual(locusInfo.mainSessionLocusCache, newLocus);
2250
+ });
2251
+
2252
+ it('do nothing if new locus is null', () => {
2253
+ locusInfo.mainSessionLocusCache = cachedLocus;
2254
+ locusInfo.updateMainSessionLocusCache(null);
2255
+
2256
+ assert.deepEqual(locusInfo.mainSessionLocusCache, cachedLocus);
2257
+ });
2258
+ });
2259
+
2260
+ describe('#clearMainSessionLocusCache', () => {
2261
+ it('clear main session locus cache', () => {
2262
+ locusInfo.mainSessionLocusCache = {controls: {}};
2263
+ locusInfo.clearMainSessionLocusCache();
2264
+ assert.isNull(locusInfo.mainSessionLocusCache);
2265
+ });
1230
2266
  });
1231
2267
 
1232
2268
  describe('#handleOneonOneEvent', () => {
@@ -1269,5 +2305,406 @@ describe('plugin-meetings', () => {
1269
2305
  );
1270
2306
  });
1271
2307
  });
2308
+
2309
+ describe('#isMeetingActive', () => {
2310
+ it('sends client event correctly for state = inactive', () => {
2311
+ locusInfo.parsedLocus = {
2312
+ fullState: {
2313
+ type: _CALL_,
2314
+ },
2315
+ };
2316
+
2317
+ locusInfo.fullState = {
2318
+ state: LOCUS.STATE.INACTIVE,
2319
+ };
2320
+
2321
+ locusInfo.isMeetingActive();
2322
+
2323
+ assert.calledWith(webex.internal.newMetrics.submitClientEvent, {
2324
+ name: 'client.call.remote-ended',
2325
+ options: {
2326
+ meetingId: locusInfo.meetingId,
2327
+ },
2328
+ });
2329
+ });
2330
+
2331
+ it('sends client event correctly for state = PARTNER_LEFT', () => {
2332
+ locusInfo.getLocusPartner = sinon.stub().returns({state: MEETING_STATE.STATES.LEFT});
2333
+ locusInfo.parsedLocus = {
2334
+ fullState: {
2335
+ type: _CALL_,
2336
+ },
2337
+ self: {
2338
+ state: MEETING_STATE.STATES.DECLINED,
2339
+ },
2340
+ };
2341
+ locusInfo.isMeetingActive();
2342
+
2343
+ assert.calledWith(webex.internal.newMetrics.submitClientEvent, {
2344
+ name: 'client.call.remote-ended',
2345
+ options: {
2346
+ meetingId: locusInfo.meetingId,
2347
+ },
2348
+ });
2349
+ });
2350
+
2351
+ it('sends client event correctly for state = SELF_LEFT', () => {
2352
+ locusInfo.getLocusPartner = sinon.stub().returns({state: MEETING_STATE.STATES.LEFT});
2353
+ locusInfo.parsedLocus = {
2354
+ fullState: {
2355
+ type: _CALL_,
2356
+ },
2357
+ self: {
2358
+ state: MEETING_STATE.STATES.LEFT,
2359
+ },
2360
+ };
2361
+
2362
+ locusInfo.isMeetingActive();
2363
+
2364
+ assert.calledWith(webex.internal.newMetrics.submitClientEvent, {
2365
+ name: 'client.call.remote-ended',
2366
+ options: {
2367
+ meetingId: locusInfo.meetingId,
2368
+ },
2369
+ });
2370
+ });
2371
+
2372
+ it('sends client event correctly for state = MEETING_INACTIVE_TERMINATING', () => {
2373
+ locusInfo.getLocusPartner = sinon.stub().returns({state: MEETING_STATE.STATES.LEFT});
2374
+ locusInfo.parsedLocus = {
2375
+ fullState: {
2376
+ type: _MEETING_,
2377
+ },
2378
+ };
2379
+
2380
+ locusInfo.fullState = {
2381
+ state: LOCUS.STATE.INACTIVE,
2382
+ };
2383
+
2384
+ locusInfo.isMeetingActive();
2385
+
2386
+ assert.calledWith(webex.internal.newMetrics.submitClientEvent, {
2387
+ name: 'client.call.remote-ended',
2388
+ options: {
2389
+ meetingId: locusInfo.meetingId,
2390
+ },
2391
+ });
2392
+ });
2393
+
2394
+ it('sends client event correctly for state = FULLSTATE_REMOVED', () => {
2395
+ locusInfo.getLocusPartner = sinon.stub().returns({state: MEETING_STATE.STATES.LEFT});
2396
+ locusInfo.parsedLocus = {
2397
+ fullState: {
2398
+ type: _MEETING_,
2399
+ },
2400
+ };
2401
+
2402
+ locusInfo.fullState = {
2403
+ removed: true,
2404
+ };
2405
+
2406
+ locusInfo.isMeetingActive();
2407
+
2408
+ assert.calledWith(webex.internal.newMetrics.submitClientEvent, {
2409
+ name: 'client.call.remote-ended',
2410
+ options: {
2411
+ meetingId: locusInfo.meetingId,
2412
+ },
2413
+ });
2414
+ });
2415
+ });
2416
+
2417
+ // semi-integration tests that use real LocusInfo with real Parser
2418
+ // and test various scenarios related to handling out-of-order Locus delta events
2419
+ describe('handling of out-of-order Locus delta events', () => {
2420
+ let clock;
2421
+
2422
+ const generateDeltaEvent = (base, sequence) => {
2423
+ return {
2424
+ baseSequence: {
2425
+ rangeStart: 0,
2426
+ rangeEnd: 0,
2427
+ entries: [base],
2428
+ },
2429
+ sequence: {
2430
+ rangeStart: 0,
2431
+ rangeEnd: 0,
2432
+ entries: [sequence],
2433
+ },
2434
+ syncUrl: `fake sync url for sequence ${sequence}`,
2435
+ self: {
2436
+ person: {
2437
+ id: 'test person id',
2438
+ },
2439
+ },
2440
+ };
2441
+ };
2442
+
2443
+ // a list of example delta events, sorted by time and each event is based on the previous one
2444
+ const deltaEvents = [
2445
+ generateDeltaEvent(10, 20), // 0
2446
+ generateDeltaEvent(20, 30), // 1
2447
+ generateDeltaEvent(30, 40), // 2
2448
+ generateDeltaEvent(40, 50), // 3
2449
+ generateDeltaEvent(50, 60), // 4
2450
+ generateDeltaEvent(60, 70), // 5
2451
+ generateDeltaEvent(70, 80), // 6
2452
+ generateDeltaEvent(80, 90), // 7
2453
+ generateDeltaEvent(90, 100), // 8
2454
+ ];
2455
+
2456
+ let updateLocusInfoStub; // we use this stub to verify that an event has been fully processed
2457
+ let syncRequestStub;
2458
+
2459
+ beforeEach(() => {
2460
+ clock = sinon.useFakeTimers();
2461
+
2462
+ sinon.stub(locusInfo, 'updateParticipantDeltas');
2463
+ sinon.stub(locusInfo, 'updateParticipants');
2464
+ sinon.stub(locusInfo, 'isMeetingActive'),
2465
+ sinon.stub(locusInfo, 'handleOneOnOneEvent'),
2466
+ (updateLocusInfoStub = sinon.stub(locusInfo, 'updateLocusInfo'));
2467
+ syncRequestStub = sinon.stub().resolves({body: {}});
2468
+
2469
+ mockMeeting.locusInfo = locusInfo;
2470
+ mockMeeting.locusUrl = 'fake locus url';
2471
+ mockMeeting.meetingRequest = {
2472
+ getLocusDTO: syncRequestStub,
2473
+ };
2474
+
2475
+ locusInfo.onFullLocus({
2476
+ sequence: {
2477
+ rangeStart: 0,
2478
+ rangeEnd: 0,
2479
+ entries: [10],
2480
+ },
2481
+ self: {
2482
+ person: {
2483
+ id: 'test person id',
2484
+ },
2485
+ },
2486
+ });
2487
+
2488
+ updateLocusInfoStub.resetHistory();
2489
+ });
2490
+
2491
+ afterEach(() => {
2492
+ clock.restore();
2493
+ });
2494
+
2495
+ it('queues out-of-order deltas until it receives a correct delta', () => {
2496
+ // send some out-of-order deltas
2497
+ locusInfo.handleLocusDelta(deltaEvents[1], mockMeeting);
2498
+ locusInfo.handleLocusDelta(deltaEvents[4], mockMeeting);
2499
+
2500
+ // they should be queued and not processed
2501
+ assert.notCalled(updateLocusInfoStub);
2502
+
2503
+ // now one of the missing ones, but not the one SDK is really waiting for
2504
+ locusInfo.handleLocusDelta(deltaEvents[2], mockMeeting);
2505
+
2506
+ // still nothing should be processed
2507
+ assert.notCalled(updateLocusInfoStub);
2508
+
2509
+ // now send the one SDK is waiting for
2510
+ locusInfo.handleLocusDelta(deltaEvents[0], mockMeeting);
2511
+
2512
+ // so deltaEvents with indexes 1,2,3 can be processed, but 5 still not, because 4 is missing
2513
+ assert.callCount(updateLocusInfoStub, 3);
2514
+ assert.calledWith(updateLocusInfoStub.getCall(0), deltaEvents[0]);
2515
+ assert.calledWith(updateLocusInfoStub.getCall(1), deltaEvents[1]);
2516
+ assert.calledWith(updateLocusInfoStub.getCall(2), deltaEvents[2]);
2517
+
2518
+ updateLocusInfoStub.resetHistory();
2519
+
2520
+ // now send deltaEvents[4]
2521
+ locusInfo.handleLocusDelta(deltaEvents[3], mockMeeting);
2522
+
2523
+ // and verify deltaEvents[4] and deltaEvents[5] have been processed
2524
+ assert.callCount(updateLocusInfoStub, 2);
2525
+ assert.calledWith(updateLocusInfoStub.getCall(0), deltaEvents[3]);
2526
+ assert.calledWith(updateLocusInfoStub.getCall(1), deltaEvents[4]);
2527
+ });
2528
+
2529
+ it('handles out-of-order deltas correctly even if all arrive in reverse order', () => {
2530
+ // send a bunch deltas in reverse order
2531
+ for (let i = 4; i >= 0; i--) {
2532
+ locusInfo.handleLocusDelta(deltaEvents[i], mockMeeting);
2533
+ }
2534
+
2535
+ // they should be queued and then processed in correct order
2536
+ assert.callCount(updateLocusInfoStub, 5);
2537
+ assert.calledWith(updateLocusInfoStub.getCall(0), deltaEvents[0]);
2538
+ assert.calledWith(updateLocusInfoStub.getCall(1), deltaEvents[1]);
2539
+ assert.calledWith(updateLocusInfoStub.getCall(2), deltaEvents[2]);
2540
+ assert.calledWith(updateLocusInfoStub.getCall(3), deltaEvents[3]);
2541
+ assert.calledWith(updateLocusInfoStub.getCall(4), deltaEvents[4]);
2542
+ });
2543
+
2544
+ it('sends a sync request using syncUrl if it receives at least 1 delta event and processes later deltas after sync correctly', async () => {
2545
+ // the test first sends an initial "good" delta
2546
+ const initialDeltaIdx = 0;
2547
+ const initialDelta = deltaEvents[initialDeltaIdx];
2548
+
2549
+ // then it sends a bunch of out-of-order deltas (at least 6 to trigger a sync), last one being lastOooDelta
2550
+ const firstOooDeltaIdx = 2;
2551
+ const lastOooDeltaIdx = 7;
2552
+ const lastOooDelta = deltaEvents[lastOooDeltaIdx];
2553
+
2554
+ // and finally, after the sync it sends another "good" delta
2555
+ const goodDeltaAfterSync = deltaEvents[8];
2556
+
2557
+ const deltaLocusFromSyncResponse = {
2558
+ baseSequence: {
2559
+ rangeStart: 0,
2560
+ rangeEnd: 0,
2561
+ entries: [initialDelta.sequence.entries[0]],
2562
+ },
2563
+ sequence: {
2564
+ rangeStart: 0,
2565
+ rangeEnd: 0,
2566
+ entries: [lastOooDelta.sequence.entries[0]],
2567
+ },
2568
+ syncUrl: `fake sync url for sequence ${lastOooDelta.sequence.entries[0]}`,
2569
+ self: {
2570
+ person: {
2571
+ id: 'test person id',
2572
+ },
2573
+ },
2574
+ };
2575
+
2576
+ syncRequestStub.resolves({
2577
+ body: deltaLocusFromSyncResponse,
2578
+ });
2579
+
2580
+ // send one correct delta so that SDK has the syncUrl
2581
+ locusInfo.handleLocusDelta(initialDelta, mockMeeting);
2582
+
2583
+ updateLocusInfoStub.resetHistory();
2584
+
2585
+ // send 6 out-of-order deltas to trigger a sync (we're skipping deltaEvents[1])
2586
+ for (let i = firstOooDeltaIdx; i <= lastOooDeltaIdx; i++) {
2587
+ locusInfo.handleLocusDelta(deltaEvents[i], mockMeeting);
2588
+ }
2589
+
2590
+ await testUtils.flushPromises();
2591
+
2592
+ // check that sync was done using the correct syncUrl
2593
+ assert.calledOnceWithExactly(syncRequestStub, {url: initialDelta.syncUrl});
2594
+ assert.calledOnceWithExactly(updateLocusInfoStub, deltaLocusFromSyncResponse);
2595
+
2596
+ updateLocusInfoStub.resetHistory();
2597
+
2598
+ // now send another delta - a good one, it should be processed as normal
2599
+ locusInfo.handleLocusDelta(goodDeltaAfterSync, mockMeeting);
2600
+
2601
+ assert.calledOnceWithExactly(updateLocusInfoStub, goodDeltaAfterSync);
2602
+ });
2603
+
2604
+ it('does a sync if blocked on out-of-order deltas for too long', async () => {
2605
+ // stub random so that the timer fires after 12500 ms
2606
+ sinon.stub(Math, 'random').returns(0.5);
2607
+
2608
+ const oooDelta = deltaEvents[3];
2609
+
2610
+ // setup the stubs so that the sync request receives a full DTO with the sequence equal to the out-of-order delta we simulate
2611
+ const fullLocus = {
2612
+ sequence: oooDelta.sequence,
2613
+ };
2614
+ syncRequestStub.resolves({
2615
+ body: fullLocus,
2616
+ });
2617
+
2618
+ // send an out-of-order delta
2619
+ locusInfo.handleLocusDelta(oooDelta, mockMeeting);
2620
+
2621
+ assert.calledOnceWithExactly(sendBehavioralMetricStub, 'js_sdk_locus_delta_ooo', {
2622
+ stack: sinon.match.any,
2623
+ });
2624
+
2625
+ await clock.tickAsync(12499);
2626
+ await testUtils.flushPromises();
2627
+ assert.notCalled(syncRequestStub);
2628
+ assert.notCalled(updateLocusInfoStub);
2629
+
2630
+ await clock.tickAsync(1);
2631
+ await testUtils.flushPromises();
2632
+
2633
+ assert.calledOnceWithExactly(syncRequestStub, {url: mockMeeting.locusUrl});
2634
+ assert.calledOnceWithExactly(updateLocusInfoStub, fullLocus);
2635
+ });
2636
+
2637
+ it('does a sync if out-of-order deltas queue becomes too big', async () => {
2638
+ // setup the stubs so that the sync request receives a full DTO with the sequence equal to the out-of-order delta we simulate
2639
+ const fullLocus = {
2640
+ sequence: deltaEvents[6].sequence,
2641
+ };
2642
+ syncRequestStub.resolves({
2643
+ body: fullLocus,
2644
+ });
2645
+
2646
+ // send 5 deltas, starting from deltaEvents[1] so that SDK is blocked waiting for deltaEvents[0]
2647
+ for (let i = 0; i < 5; i++) {
2648
+ locusInfo.handleLocusDelta(deltaEvents[i + 1], mockMeeting);
2649
+ }
2650
+
2651
+ // nothing should happen, SDK should still be waiting for deltaEvents[0]
2652
+ assert.notCalled(syncRequestStub);
2653
+ assert.notCalled(updateLocusInfoStub);
2654
+
2655
+ // now send one more out-of-order delta to trigger a sync request
2656
+ locusInfo.handleLocusDelta(deltaEvents[6], mockMeeting);
2657
+
2658
+ await testUtils.flushPromises();
2659
+
2660
+ // check sync was done
2661
+ assert.calledOnceWithExactly(syncRequestStub, {url: mockMeeting.locusUrl});
2662
+ assert.calledOnceWithExactly(updateLocusInfoStub, fullLocus);
2663
+ });
2664
+
2665
+ it('processes delta events that are not included in sync response', async () => {
2666
+ // this test sends a bunch of out-of-order deltas, this triggers a sync
2667
+ // but the full locus response doesn't include the last 2 deltas received, so
2668
+ // we check that these 2 deltas are also processed after sync response
2669
+ const fullLocusFromSyncResponse = {
2670
+ baseSequence: {
2671
+ rangeStart: 0,
2672
+ rangeEnd: 0,
2673
+ entries: [deltaEvents[0].sequence.entries[0]],
2674
+ },
2675
+ sequence: {
2676
+ rangeStart: 0,
2677
+ rangeEnd: 0,
2678
+ entries: [deltaEvents[5].sequence.entries[0]],
2679
+ },
2680
+ syncUrl: `fake sync url for sequence ${deltaEvents[5].sequence.entries[0]}`,
2681
+ self: {
2682
+ person: {
2683
+ id: 'test person id',
2684
+ },
2685
+ },
2686
+ };
2687
+
2688
+ syncRequestStub.resolves({
2689
+ body: fullLocusFromSyncResponse,
2690
+ });
2691
+
2692
+ // send at least 6 out-of-order deltas to trigger a sync (we're skipping deltaEvents[0])
2693
+ for (let i = 1; i <= 7; i++) {
2694
+ locusInfo.handleLocusDelta(deltaEvents[i], mockMeeting);
2695
+ }
2696
+
2697
+ await testUtils.flushPromises();
2698
+
2699
+ // check that sync was done
2700
+ assert.calledOnceWithExactly(syncRequestStub, {url: mockMeeting.locusUrl});
2701
+
2702
+ // and that remaining deltas from the queue that were not included in full Locus were also processed
2703
+ assert.callCount(updateLocusInfoStub, 3);
2704
+ assert.calledWith(updateLocusInfoStub.getCall(0), fullLocusFromSyncResponse);
2705
+ assert.calledWith(updateLocusInfoStub.getCall(1), deltaEvents[6]);
2706
+ assert.calledWith(updateLocusInfoStub.getCall(2), deltaEvents[7]);
2707
+ });
2708
+ });
1272
2709
  });
1273
2710
  });