@webex/plugin-meetings 3.0.0-beta.2 → 3.0.0-beta.200

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 (576) hide show
  1. package/README.md +45 -7
  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/parameter.js +5 -33
  39. package/dist/common/errors/parameter.js.map +1 -1
  40. package/dist/common/errors/password-error.js +10 -24
  41. package/dist/common/errors/password-error.js.map +1 -1
  42. package/dist/common/errors/permission.js +9 -23
  43. package/dist/common/errors/permission.js.map +1 -1
  44. package/dist/common/errors/reconnection-in-progress.js +0 -17
  45. package/dist/common/errors/reconnection-in-progress.js.map +1 -1
  46. package/dist/common/errors/reconnection.js +10 -24
  47. package/dist/common/errors/reconnection.js.map +1 -1
  48. package/dist/common/errors/stats.js +10 -24
  49. package/dist/common/errors/stats.js.map +1 -1
  50. package/dist/common/errors/webex-errors.js +9 -43
  51. package/dist/common/errors/webex-errors.js.map +1 -1
  52. package/dist/common/errors/webex-meetings-error.js +5 -25
  53. package/dist/common/errors/webex-meetings-error.js.map +1 -1
  54. package/dist/common/events/events-scope.js +0 -22
  55. package/dist/common/events/events-scope.js.map +1 -1
  56. package/dist/common/events/events.js +0 -23
  57. package/dist/common/events/events.js.map +1 -1
  58. package/dist/common/events/trigger-proxy.js +0 -12
  59. package/dist/common/events/trigger-proxy.js.map +1 -1
  60. package/dist/common/events/util.js +0 -15
  61. package/dist/common/events/util.js.map +1 -1
  62. package/dist/common/logs/logger-config.js +0 -4
  63. package/dist/common/logs/logger-config.js.map +1 -1
  64. package/dist/common/logs/logger-proxy.js +1 -8
  65. package/dist/common/logs/logger-proxy.js.map +1 -1
  66. package/dist/common/logs/request.js +37 -60
  67. package/dist/common/logs/request.js.map +1 -1
  68. package/dist/common/queue.js +28 -23
  69. package/dist/common/queue.js.map +1 -1
  70. package/dist/config.js +8 -13
  71. package/dist/config.js.map +1 -1
  72. package/dist/constants.js +250 -66
  73. package/dist/constants.js.map +1 -1
  74. package/dist/controls-options-manager/constants.js +14 -0
  75. package/dist/controls-options-manager/constants.js.map +1 -0
  76. package/dist/controls-options-manager/enums.js +27 -0
  77. package/dist/controls-options-manager/enums.js.map +1 -0
  78. package/dist/controls-options-manager/index.js +297 -0
  79. package/dist/controls-options-manager/index.js.map +1 -0
  80. package/dist/controls-options-manager/types.js +7 -0
  81. package/dist/controls-options-manager/types.js.map +1 -0
  82. package/dist/controls-options-manager/util.js +319 -0
  83. package/dist/controls-options-manager/util.js.map +1 -0
  84. package/dist/index.js +108 -17
  85. package/dist/index.js.map +1 -1
  86. package/dist/interpretation/collection.js +23 -0
  87. package/dist/interpretation/collection.js.map +1 -0
  88. package/dist/interpretation/index.js +366 -0
  89. package/dist/interpretation/index.js.map +1 -0
  90. package/dist/interpretation/siLanguage.js +25 -0
  91. package/dist/interpretation/siLanguage.js.map +1 -0
  92. package/dist/locus-info/controlsUtils.js +101 -29
  93. package/dist/locus-info/controlsUtils.js.map +1 -1
  94. package/dist/locus-info/embeddedAppsUtils.js +3 -26
  95. package/dist/locus-info/embeddedAppsUtils.js.map +1 -1
  96. package/dist/locus-info/fullState.js +0 -15
  97. package/dist/locus-info/fullState.js.map +1 -1
  98. package/dist/locus-info/hostUtils.js +4 -12
  99. package/dist/locus-info/hostUtils.js.map +1 -1
  100. package/dist/locus-info/index.js +532 -240
  101. package/dist/locus-info/index.js.map +1 -1
  102. package/dist/locus-info/infoUtils.js +3 -37
  103. package/dist/locus-info/infoUtils.js.map +1 -1
  104. package/dist/locus-info/mediaSharesUtils.js +54 -38
  105. package/dist/locus-info/mediaSharesUtils.js.map +1 -1
  106. package/dist/locus-info/parser.js +284 -154
  107. package/dist/locus-info/parser.js.map +1 -1
  108. package/dist/locus-info/selfUtils.js +110 -92
  109. package/dist/locus-info/selfUtils.js.map +1 -1
  110. package/dist/media/index.js +95 -226
  111. package/dist/media/index.js.map +1 -1
  112. package/dist/media/properties.js +99 -194
  113. package/dist/media/properties.js.map +1 -1
  114. package/dist/media/util.js +2 -9
  115. package/dist/media/util.js.map +1 -1
  116. package/dist/mediaQualityMetrics/config.js +505 -495
  117. package/dist/mediaQualityMetrics/config.js.map +1 -1
  118. package/dist/meeting/in-meeting-actions.js +83 -14
  119. package/dist/meeting/in-meeting-actions.js.map +1 -1
  120. package/dist/meeting/index.js +3478 -3563
  121. package/dist/meeting/index.js.map +1 -1
  122. package/dist/meeting/locusMediaRequest.js +291 -0
  123. package/dist/meeting/locusMediaRequest.js.map +1 -0
  124. package/dist/meeting/muteState.js +247 -183
  125. package/dist/meeting/muteState.js.map +1 -1
  126. package/dist/meeting/request.js +344 -344
  127. package/dist/meeting/request.js.map +1 -1
  128. package/dist/meeting/request.type.js +7 -0
  129. package/dist/meeting/request.type.js.map +1 -0
  130. package/dist/meeting/state.js +21 -31
  131. package/dist/meeting/state.js.map +1 -1
  132. package/dist/meeting/util.js +529 -588
  133. package/dist/meeting/util.js.map +1 -1
  134. package/dist/meeting-info/collection.js +6 -25
  135. package/dist/meeting-info/collection.js.map +1 -1
  136. package/dist/meeting-info/index.js +62 -39
  137. package/dist/meeting-info/index.js.map +1 -1
  138. package/dist/meeting-info/meeting-info-v2.js +328 -283
  139. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  140. package/dist/meeting-info/request.js +3 -15
  141. package/dist/meeting-info/request.js.map +1 -1
  142. package/dist/meeting-info/util.js +98 -183
  143. package/dist/meeting-info/util.js.map +1 -1
  144. package/dist/meeting-info/utilv2.js +156 -232
  145. package/dist/meeting-info/utilv2.js.map +1 -1
  146. package/dist/meetings/collection.js +26 -19
  147. package/dist/meetings/collection.js.map +1 -1
  148. package/dist/meetings/index.js +795 -574
  149. package/dist/meetings/index.js.map +1 -1
  150. package/dist/meetings/meetings.types.js +7 -0
  151. package/dist/meetings/meetings.types.js.map +1 -0
  152. package/dist/meetings/request.js +26 -41
  153. package/dist/meetings/request.js.map +1 -1
  154. package/dist/meetings/util.js +186 -155
  155. package/dist/meetings/util.js.map +1 -1
  156. package/dist/member/index.js +126 -85
  157. package/dist/member/index.js.map +1 -1
  158. package/dist/member/types.js +25 -0
  159. package/dist/member/types.js.map +1 -0
  160. package/dist/member/util.js +147 -88
  161. package/dist/member/util.js.map +1 -1
  162. package/dist/members/collection.js +13 -12
  163. package/dist/members/collection.js.map +1 -1
  164. package/dist/members/index.js +178 -204
  165. package/dist/members/index.js.map +1 -1
  166. package/dist/members/request.js +113 -68
  167. package/dist/members/request.js.map +1 -1
  168. package/dist/members/types.js +15 -0
  169. package/dist/members/types.js.map +1 -0
  170. package/dist/members/util.js +314 -260
  171. package/dist/members/util.js.map +1 -1
  172. package/dist/metrics/constants.js +4 -7
  173. package/dist/metrics/constants.js.map +1 -1
  174. package/dist/metrics/index.js +11 -558
  175. package/dist/metrics/index.js.map +1 -1
  176. package/dist/multistream/mediaRequestManager.js +264 -50
  177. package/dist/multistream/mediaRequestManager.js.map +1 -1
  178. package/dist/multistream/receiveSlot.js +58 -65
  179. package/dist/multistream/receiveSlot.js.map +1 -1
  180. package/dist/multistream/receiveSlotManager.js +76 -95
  181. package/dist/multistream/receiveSlotManager.js.map +1 -1
  182. package/dist/multistream/remoteMedia.js +62 -76
  183. package/dist/multistream/remoteMedia.js.map +1 -1
  184. package/dist/multistream/remoteMediaGroup.js +66 -43
  185. package/dist/multistream/remoteMediaGroup.js.map +1 -1
  186. package/dist/multistream/remoteMediaManager.js +502 -442
  187. package/dist/multistream/remoteMediaManager.js.map +1 -1
  188. package/dist/networkQualityMonitor/index.js +40 -59
  189. package/dist/networkQualityMonitor/index.js.map +1 -1
  190. package/dist/personal-meeting-room/index.js +21 -45
  191. package/dist/personal-meeting-room/index.js.map +1 -1
  192. package/dist/personal-meeting-room/request.js +1 -31
  193. package/dist/personal-meeting-room/request.js.map +1 -1
  194. package/dist/personal-meeting-room/util.js +0 -13
  195. package/dist/personal-meeting-room/util.js.map +1 -1
  196. package/dist/reachability/index.js +192 -191
  197. package/dist/reachability/index.js.map +1 -1
  198. package/dist/reachability/request.js +15 -23
  199. package/dist/reachability/request.js.map +1 -1
  200. package/dist/reactions/constants.js +13 -0
  201. package/dist/reactions/constants.js.map +1 -0
  202. package/dist/reactions/reactions.js +109 -0
  203. package/dist/reactions/reactions.js.map +1 -0
  204. package/dist/reactions/reactions.type.js +36 -0
  205. package/dist/reactions/reactions.type.js.map +1 -0
  206. package/dist/reconnection-manager/index.js +384 -476
  207. package/dist/reconnection-manager/index.js.map +1 -1
  208. package/dist/recording-controller/enums.js +17 -0
  209. package/dist/recording-controller/enums.js.map +1 -0
  210. package/dist/recording-controller/index.js +363 -0
  211. package/dist/recording-controller/index.js.map +1 -0
  212. package/dist/recording-controller/util.js +64 -0
  213. package/dist/recording-controller/util.js.map +1 -0
  214. package/dist/roap/index.js +58 -91
  215. package/dist/roap/index.js.map +1 -1
  216. package/dist/roap/request.js +137 -135
  217. package/dist/roap/request.js.map +1 -1
  218. package/dist/roap/turnDiscovery.js +148 -100
  219. package/dist/roap/turnDiscovery.js.map +1 -1
  220. package/dist/rtcMetrics/constants.js +12 -0
  221. package/dist/rtcMetrics/constants.js.map +1 -0
  222. package/dist/rtcMetrics/index.js +115 -0
  223. package/dist/rtcMetrics/index.js.map +1 -0
  224. package/dist/statsAnalyzer/global.js +1 -95
  225. package/dist/statsAnalyzer/global.js.map +1 -1
  226. package/dist/statsAnalyzer/index.js +385 -460
  227. package/dist/statsAnalyzer/index.js.map +1 -1
  228. package/dist/statsAnalyzer/mqaUtil.js +143 -87
  229. package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
  230. package/dist/transcription/index.js +22 -47
  231. package/dist/transcription/index.js.map +1 -1
  232. package/dist/types/annotation/annotation.types.d.ts +42 -0
  233. package/dist/types/annotation/constants.d.ts +31 -0
  234. package/dist/types/annotation/index.d.ts +117 -0
  235. package/dist/types/breakouts/breakout.d.ts +8 -0
  236. package/dist/types/breakouts/collection.d.ts +5 -0
  237. package/dist/types/breakouts/edit-lock-error.d.ts +15 -0
  238. package/dist/types/breakouts/events.d.ts +8 -0
  239. package/dist/types/breakouts/index.d.ts +5 -0
  240. package/dist/types/breakouts/request.d.ts +22 -0
  241. package/dist/types/breakouts/utils.d.ts +15 -0
  242. package/dist/types/common/browser-detection.d.ts +9 -0
  243. package/dist/types/common/collection.d.ts +48 -0
  244. package/dist/types/common/config.d.ts +2 -0
  245. package/dist/types/common/errors/captcha-error.d.ts +15 -0
  246. package/dist/types/common/errors/intent-to-join.d.ts +16 -0
  247. package/dist/types/common/errors/join-meeting.d.ts +17 -0
  248. package/dist/types/common/errors/media.d.ts +15 -0
  249. package/dist/types/common/errors/parameter.d.ts +15 -0
  250. package/dist/types/common/errors/password-error.d.ts +15 -0
  251. package/dist/types/common/errors/permission.d.ts +14 -0
  252. package/dist/types/common/errors/reconnection-in-progress.d.ts +9 -0
  253. package/dist/types/common/errors/reconnection.d.ts +15 -0
  254. package/dist/types/common/errors/stats.d.ts +15 -0
  255. package/dist/types/common/errors/webex-errors.d.ts +69 -0
  256. package/dist/types/common/errors/webex-meetings-error.d.ts +20 -0
  257. package/dist/types/common/events/events-scope.d.ts +17 -0
  258. package/dist/types/common/events/events.d.ts +12 -0
  259. package/dist/types/common/events/trigger-proxy.d.ts +2 -0
  260. package/dist/types/common/events/util.d.ts +2 -0
  261. package/dist/types/common/logs/logger-config.d.ts +2 -0
  262. package/dist/types/common/logs/logger-proxy.d.ts +2 -0
  263. package/dist/types/common/logs/request.d.ts +34 -0
  264. package/dist/types/common/queue.d.ts +34 -0
  265. package/dist/types/config.d.ts +72 -0
  266. package/dist/types/constants.d.ts +1016 -0
  267. package/dist/types/controls-options-manager/constants.d.ts +4 -0
  268. package/dist/types/controls-options-manager/enums.d.ts +15 -0
  269. package/dist/types/controls-options-manager/index.d.ts +136 -0
  270. package/dist/types/controls-options-manager/types.d.ts +43 -0
  271. package/dist/types/controls-options-manager/util.d.ts +1 -0
  272. package/dist/types/index.d.ts +7 -0
  273. package/dist/types/interpretation/collection.d.ts +5 -0
  274. package/dist/types/interpretation/index.d.ts +5 -0
  275. package/dist/types/interpretation/siLanguage.d.ts +5 -0
  276. package/dist/types/locus-info/controlsUtils.d.ts +2 -0
  277. package/dist/types/locus-info/embeddedAppsUtils.d.ts +2 -0
  278. package/dist/types/locus-info/fullState.d.ts +2 -0
  279. package/dist/types/locus-info/hostUtils.d.ts +2 -0
  280. package/dist/types/locus-info/index.d.ts +322 -0
  281. package/dist/types/locus-info/infoUtils.d.ts +2 -0
  282. package/dist/types/locus-info/mediaSharesUtils.d.ts +2 -0
  283. package/dist/types/locus-info/parser.d.ts +271 -0
  284. package/dist/types/locus-info/selfUtils.d.ts +2 -0
  285. package/dist/types/media/index.d.ts +34 -0
  286. package/dist/types/media/properties.d.ts +93 -0
  287. package/dist/types/media/util.d.ts +2 -0
  288. package/dist/types/mediaQualityMetrics/config.d.ts +365 -0
  289. package/dist/types/meeting/in-meeting-actions.d.ts +153 -0
  290. package/dist/types/meeting/index.d.ts +1471 -0
  291. package/dist/types/meeting/locusMediaRequest.d.ts +70 -0
  292. package/dist/types/meeting/muteState.d.ts +184 -0
  293. package/dist/types/meeting/request.d.ts +257 -0
  294. package/dist/types/meeting/request.type.d.ts +11 -0
  295. package/dist/types/meeting/state.d.ts +9 -0
  296. package/dist/types/meeting/util.d.ts +78 -0
  297. package/dist/types/meeting-info/collection.d.ts +20 -0
  298. package/dist/types/meeting-info/index.d.ts +62 -0
  299. package/dist/types/meeting-info/meeting-info-v2.d.ts +122 -0
  300. package/dist/types/meeting-info/request.d.ts +22 -0
  301. package/dist/types/meeting-info/util.d.ts +2 -0
  302. package/dist/types/meeting-info/utilv2.d.ts +2 -0
  303. package/dist/types/meetings/collection.d.ts +31 -0
  304. package/dist/types/meetings/index.d.ts +367 -0
  305. package/dist/types/meetings/meetings.types.d.ts +4 -0
  306. package/dist/types/meetings/request.d.ts +27 -0
  307. package/dist/types/meetings/util.d.ts +18 -0
  308. package/dist/types/member/index.d.ts +159 -0
  309. package/dist/types/member/types.d.ts +32 -0
  310. package/dist/types/member/util.d.ts +2 -0
  311. package/dist/types/members/collection.d.ts +29 -0
  312. package/dist/types/members/index.d.ts +353 -0
  313. package/dist/types/members/request.d.ts +114 -0
  314. package/dist/types/members/types.d.ts +24 -0
  315. package/dist/types/members/util.d.ts +210 -0
  316. package/dist/types/metrics/constants.d.ts +55 -0
  317. package/dist/types/metrics/index.d.ts +45 -0
  318. package/dist/types/multistream/mediaRequestManager.d.ts +118 -0
  319. package/dist/types/multistream/receiveSlot.d.ts +68 -0
  320. package/dist/types/multistream/receiveSlotManager.d.ts +56 -0
  321. package/dist/types/multistream/remoteMedia.d.ts +72 -0
  322. package/dist/types/multistream/remoteMediaGroup.d.ts +47 -0
  323. package/dist/types/multistream/remoteMediaManager.d.ts +277 -0
  324. package/dist/types/networkQualityMonitor/index.d.ts +70 -0
  325. package/dist/types/personal-meeting-room/index.d.ts +47 -0
  326. package/dist/types/personal-meeting-room/request.d.ts +14 -0
  327. package/dist/types/personal-meeting-room/util.d.ts +2 -0
  328. package/dist/types/reachability/index.d.ts +152 -0
  329. package/dist/types/reachability/request.d.ts +37 -0
  330. package/dist/types/reactions/constants.d.ts +3 -0
  331. package/dist/types/reactions/reactions.d.ts +4 -0
  332. package/dist/types/reactions/reactions.type.d.ts +52 -0
  333. package/dist/types/reconnection-manager/index.d.ts +126 -0
  334. package/dist/types/recording-controller/enums.d.ts +7 -0
  335. package/dist/types/recording-controller/index.d.ts +208 -0
  336. package/dist/types/recording-controller/util.d.ts +14 -0
  337. package/dist/types/roap/index.d.ts +77 -0
  338. package/dist/types/roap/request.d.ts +36 -0
  339. package/dist/types/roap/turnDiscovery.d.ts +91 -0
  340. package/dist/types/rtcMetrics/constants.d.ts +4 -0
  341. package/dist/types/rtcMetrics/index.d.ts +46 -0
  342. package/dist/types/statsAnalyzer/global.d.ts +36 -0
  343. package/dist/types/statsAnalyzer/index.d.ts +200 -0
  344. package/dist/types/statsAnalyzer/mqaUtil.d.ts +24 -0
  345. package/dist/types/transcription/index.d.ts +64 -0
  346. package/internal-README.md +7 -6
  347. package/package.json +29 -21
  348. package/src/annotation/annotation.types.ts +50 -0
  349. package/src/annotation/constants.ts +36 -0
  350. package/src/annotation/index.ts +328 -0
  351. package/src/breakouts/README.md +220 -0
  352. package/src/breakouts/breakout.ts +188 -0
  353. package/src/breakouts/collection.ts +19 -0
  354. package/src/breakouts/edit-lock-error.ts +25 -0
  355. package/src/breakouts/events.ts +56 -0
  356. package/src/breakouts/index.ts +925 -0
  357. package/src/breakouts/request.ts +55 -0
  358. package/src/breakouts/utils.ts +57 -0
  359. package/src/common/{browser-detection.js → browser-detection.ts} +9 -6
  360. package/src/common/collection.ts +9 -7
  361. package/src/common/{config.js → config.ts} +1 -1
  362. package/src/common/errors/{captcha-error.js → captcha-error.ts} +11 -7
  363. package/src/common/errors/{intent-to-join.js → intent-to-join.ts} +12 -7
  364. package/src/common/errors/{join-meeting.js → join-meeting.ts} +17 -8
  365. package/src/common/errors/{media.js → media.ts} +11 -7
  366. package/src/common/errors/parameter.ts +11 -7
  367. package/src/common/errors/{password-error.js → password-error.ts} +11 -7
  368. package/src/common/errors/{permission.js → permission.ts} +10 -6
  369. package/src/common/errors/{reconnection.js → reconnection.ts} +11 -7
  370. package/src/common/errors/{stats.js → stats.ts} +11 -7
  371. package/src/common/errors/{webex-errors.js → webex-errors.ts} +14 -9
  372. package/src/common/errors/{webex-meetings-error.js → webex-meetings-error.ts} +4 -2
  373. package/src/common/events/{events-scope.js → events-scope.ts} +6 -2
  374. package/src/common/events/{events.js → events.ts} +5 -1
  375. package/src/common/events/{trigger-proxy.js → trigger-proxy.ts} +9 -5
  376. package/src/common/events/{util.js → util.ts} +2 -3
  377. package/src/common/logs/{logger-config.js → logger-config.ts} +1 -2
  378. package/src/common/logs/logger-proxy.ts +44 -0
  379. package/src/common/logs/{request.js → request.ts} +22 -9
  380. package/src/common/queue.ts +22 -9
  381. package/src/{config.js → config.ts} +17 -17
  382. package/src/constants.ts +197 -22
  383. package/src/controls-options-manager/constants.ts +5 -0
  384. package/src/controls-options-manager/enums.ts +18 -0
  385. package/src/controls-options-manager/index.ts +278 -0
  386. package/src/controls-options-manager/types.ts +59 -0
  387. package/src/controls-options-manager/util.ts +300 -0
  388. package/src/index.ts +39 -0
  389. package/src/interpretation/README.md +60 -0
  390. package/src/interpretation/collection.ts +19 -0
  391. package/src/interpretation/index.ts +332 -0
  392. package/src/interpretation/siLanguage.ts +18 -0
  393. package/src/locus-info/controlsUtils.ts +222 -0
  394. package/src/locus-info/{embeddedAppsUtils.js → embeddedAppsUtils.ts} +5 -6
  395. package/src/locus-info/{fullState.js → fullState.ts} +16 -12
  396. package/src/locus-info/{hostUtils.js → hostUtils.ts} +9 -8
  397. package/src/locus-info/{index.js → index.ts} +518 -111
  398. package/src/locus-info/{infoUtils.js → infoUtils.ts} +19 -8
  399. package/src/locus-info/{mediaSharesUtils.js → mediaSharesUtils.ts} +65 -17
  400. package/src/locus-info/{parser.js → parser.ts} +271 -98
  401. package/src/locus-info/{selfUtils.js → selfUtils.ts} +199 -68
  402. package/src/media/index.ts +456 -0
  403. package/src/media/{properties.js → properties.ts} +80 -102
  404. package/src/media/{util.js → util.ts} +2 -2
  405. package/src/mediaQualityMetrics/config.ts +384 -0
  406. package/src/meeting/in-meeting-actions.ts +171 -3
  407. package/src/meeting/index.ts +7070 -0
  408. package/src/meeting/locusMediaRequest.ts +309 -0
  409. package/src/meeting/muteState.ts +450 -0
  410. package/src/meeting/{request.js → request.ts} +354 -214
  411. package/src/meeting/request.type.ts +13 -0
  412. package/src/meeting/{state.js → state.ts} +50 -35
  413. package/src/meeting/util.ts +615 -0
  414. package/src/meeting-info/{collection.js → collection.ts} +6 -2
  415. package/src/meeting-info/index.ts +183 -0
  416. package/src/meeting-info/meeting-info-v2.ts +407 -0
  417. package/src/meeting-info/{request.js → request.ts} +14 -4
  418. package/src/meeting-info/{util.js → util.ts} +60 -51
  419. package/src/meeting-info/{utilv2.js → utilv2.ts} +77 -60
  420. package/src/meetings/{collection.js → collection.ts} +26 -3
  421. package/src/meetings/index.ts +1467 -0
  422. package/src/meetings/meetings.types.ts +12 -0
  423. package/src/meetings/{request.js → request.ts} +34 -25
  424. package/src/meetings/{util.js → util.ts} +137 -36
  425. package/src/member/{index.js → index.ts} +151 -56
  426. package/src/member/types.ts +38 -0
  427. package/src/member/util.ts +383 -0
  428. package/src/members/{collection.js → collection.ts} +10 -2
  429. package/src/members/{index.js → index.ts} +323 -145
  430. package/src/members/request.ts +255 -0
  431. package/src/members/types.ts +28 -0
  432. package/src/members/util.ts +339 -0
  433. package/src/metrics/{constants.js → constants.ts} +2 -6
  434. package/src/metrics/index.ts +73 -0
  435. package/src/multistream/mediaRequestManager.ts +337 -61
  436. package/src/multistream/receiveSlot.ts +69 -26
  437. package/src/multistream/receiveSlotManager.ts +66 -42
  438. package/src/multistream/remoteMedia.ts +40 -5
  439. package/src/multistream/remoteMediaGroup.ts +63 -3
  440. package/src/multistream/remoteMediaManager.ts +263 -66
  441. package/src/networkQualityMonitor/{index.js → index.ts} +41 -29
  442. package/src/personal-meeting-room/{index.js → index.ts} +28 -19
  443. package/src/personal-meeting-room/{request.js → request.ts} +13 -4
  444. package/src/personal-meeting-room/{util.js → util.ts} +4 -4
  445. package/src/reachability/{index.js → index.ts} +157 -94
  446. package/src/reachability/request.ts +46 -35
  447. package/src/reactions/constants.ts +4 -0
  448. package/src/reactions/reactions.ts +104 -0
  449. package/src/reactions/reactions.type.ts +62 -0
  450. package/src/reconnection-manager/{index.js → index.ts} +254 -136
  451. package/src/recording-controller/enums.ts +8 -0
  452. package/src/recording-controller/index.ts +333 -0
  453. package/src/recording-controller/util.ts +75 -0
  454. package/src/roap/{index.js → index.ts} +86 -78
  455. package/src/roap/request.ts +163 -0
  456. package/src/roap/turnDiscovery.ts +111 -49
  457. package/src/rtcMetrics/constants.ts +3 -0
  458. package/src/rtcMetrics/index.ts +96 -0
  459. package/src/statsAnalyzer/global.ts +37 -0
  460. package/src/statsAnalyzer/index.ts +1272 -0
  461. package/src/statsAnalyzer/mqaUtil.ts +291 -0
  462. package/src/transcription/{index.js → index.ts} +46 -39
  463. package/test/integration/spec/converged-space-meetings.js +233 -0
  464. package/test/integration/spec/journey.js +804 -526
  465. package/test/integration/spec/space-meeting.js +391 -204
  466. package/test/integration/spec/transcription.js +7 -8
  467. package/test/unit/spec/annotation/index.ts +418 -0
  468. package/test/unit/spec/breakouts/breakout.ts +237 -0
  469. package/test/unit/spec/breakouts/collection.ts +15 -0
  470. package/test/unit/spec/breakouts/edit-lock-error.ts +30 -0
  471. package/test/unit/spec/breakouts/events.ts +89 -0
  472. package/test/unit/spec/breakouts/index.ts +1790 -0
  473. package/test/unit/spec/breakouts/request.ts +104 -0
  474. package/test/unit/spec/breakouts/utils.js +72 -0
  475. package/test/unit/spec/common/browser-detection.js +9 -28
  476. package/test/unit/spec/common/queue.js +31 -2
  477. package/test/unit/spec/controls-options-manager/index.js +287 -0
  478. package/test/unit/spec/controls-options-manager/util.js +582 -0
  479. package/test/unit/spec/fixture/locus.js +93 -90
  480. package/test/unit/spec/interpretation/collection.ts +15 -0
  481. package/test/unit/spec/interpretation/index.ts +589 -0
  482. package/test/unit/spec/interpretation/siLanguage.ts +28 -0
  483. package/test/unit/spec/locus-info/controlsUtils.js +325 -32
  484. package/test/unit/spec/locus-info/embeddedAppsUtils.js +8 -6
  485. package/test/unit/spec/locus-info/index.js +1176 -18
  486. package/test/unit/spec/locus-info/infoUtils.js +41 -32
  487. package/test/unit/spec/locus-info/lib/BasicSeqCmp.json +88 -430
  488. package/test/unit/spec/locus-info/lib/SeqCmp.json +513 -685
  489. package/test/unit/spec/locus-info/mediaSharesUtils.ts +22 -0
  490. package/test/unit/spec/locus-info/parser.js +65 -31
  491. package/test/unit/spec/locus-info/selfConstant.js +120 -103
  492. package/test/unit/spec/locus-info/selfUtils.js +296 -12
  493. package/test/unit/spec/media/index.ts +162 -68
  494. package/test/unit/spec/media/properties.ts +9 -9
  495. package/test/unit/spec/meeting/in-meeting-actions.ts +82 -3
  496. package/test/unit/spec/meeting/index.js +4569 -1773
  497. package/test/unit/spec/meeting/locusMediaRequest.ts +436 -0
  498. package/test/unit/spec/meeting/muteState.js +382 -211
  499. package/test/unit/spec/meeting/request.js +444 -78
  500. package/test/unit/spec/meeting/utils.js +517 -192
  501. package/test/unit/spec/meeting-info/index.js +181 -0
  502. package/test/unit/spec/meeting-info/meetinginfov2.js +481 -76
  503. package/test/unit/spec/meeting-info/request.js +7 -9
  504. package/test/unit/spec/meeting-info/util.js +11 -12
  505. package/test/unit/spec/meeting-info/utilv2.js +131 -74
  506. package/test/unit/spec/meetings/collection.js +15 -1
  507. package/test/unit/spec/meetings/index.js +1254 -330
  508. package/test/unit/spec/meetings/utils.js +220 -14
  509. package/test/unit/spec/member/index.js +58 -5
  510. package/test/unit/spec/member/util.js +494 -26
  511. package/test/unit/spec/members/index.js +423 -55
  512. package/test/unit/spec/members/request.js +228 -40
  513. package/test/unit/spec/members/utils.js +191 -4
  514. package/test/unit/spec/metrics/index.js +12 -66
  515. package/test/unit/spec/multistream/mediaRequestManager.ts +1013 -106
  516. package/test/unit/spec/multistream/receiveSlot.ts +77 -18
  517. package/test/unit/spec/multistream/receiveSlotManager.ts +69 -39
  518. package/test/unit/spec/multistream/remoteMedia.ts +40 -2
  519. package/test/unit/spec/multistream/remoteMediaGroup.ts +271 -5
  520. package/test/unit/spec/multistream/remoteMediaManager.ts +730 -65
  521. package/test/unit/spec/networkQualityMonitor/index.js +24 -18
  522. package/test/unit/spec/personal-meeting-room/personal-meeting-room.js +2 -7
  523. package/test/unit/spec/reachability/index.ts +176 -27
  524. package/test/unit/spec/reachability/request.js +66 -0
  525. package/test/unit/spec/reconnection-manager/index.js +155 -9
  526. package/test/unit/spec/recording-controller/index.js +307 -0
  527. package/test/unit/spec/recording-controller/util.js +229 -0
  528. package/test/unit/spec/roap/index.ts +28 -52
  529. package/test/unit/spec/roap/request.ts +225 -0
  530. package/test/unit/spec/roap/turnDiscovery.ts +92 -50
  531. package/test/unit/spec/rtcMetrics/index.ts +60 -0
  532. package/test/unit/spec/stats-analyzer/index.js +116 -60
  533. package/test/utils/cmr.js +44 -42
  534. package/test/utils/constants.js +9 -0
  535. package/test/utils/integrationTestUtils.js +46 -0
  536. package/test/utils/testUtils.js +63 -99
  537. package/test/utils/webex-config.js +22 -18
  538. package/test/utils/webex-test-users.js +57 -50
  539. package/tsconfig.json +6 -0
  540. package/dist/media/internal-media-core-wrapper.js +0 -22
  541. package/dist/media/internal-media-core-wrapper.js.map +0 -1
  542. package/dist/meeting/effectsState.js +0 -327
  543. package/dist/meeting/effectsState.js.map +0 -1
  544. package/dist/metrics/config.js +0 -301
  545. package/dist/metrics/config.js.map +0 -1
  546. package/dist/multistream/multistreamMedia.js +0 -116
  547. package/dist/multistream/multistreamMedia.js.map +0 -1
  548. package/dist/peer-connection-manager/util.js +0 -124
  549. package/dist/peer-connection-manager/util.js.map +0 -1
  550. package/src/common/logs/logger-proxy.js +0 -33
  551. package/src/index.js +0 -15
  552. package/src/locus-info/controlsUtils.js +0 -102
  553. package/src/media/index.js +0 -459
  554. package/src/media/internal-media-core-wrapper.ts +0 -9
  555. package/src/mediaQualityMetrics/config.js +0 -382
  556. package/src/meeting/effectsState.js +0 -205
  557. package/src/meeting/index.js +0 -6284
  558. package/src/meeting/muteState.js +0 -318
  559. package/src/meeting/util.js +0 -506
  560. package/src/meeting-info/index.js +0 -131
  561. package/src/meeting-info/meeting-info-v2.js +0 -255
  562. package/src/meetings/index.js +0 -1015
  563. package/src/member/util.js +0 -254
  564. package/src/members/request.js +0 -131
  565. package/src/members/util.js +0 -258
  566. package/src/metrics/config.js +0 -324
  567. package/src/metrics/index.js +0 -530
  568. package/src/multistream/multistreamMedia.ts +0 -92
  569. package/src/peer-connection-manager/util.ts +0 -117
  570. package/src/roap/request.js +0 -127
  571. package/src/statsAnalyzer/global.js +0 -133
  572. package/src/statsAnalyzer/index.js +0 -1006
  573. package/src/statsAnalyzer/mqaUtil.js +0 -173
  574. package/test/unit/spec/meeting/effectsState.js +0 -291
  575. package/test/unit/spec/peerconnection-manager/utils.test-fixtures.ts +0 -389
  576. /package/src/common/errors/{reconnection-in-progress.js → reconnection-in-progress.ts} +0 -0
@@ -3,6 +3,7 @@ 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';
@@ -16,9 +17,14 @@ import {
16
17
  LOCUSEVENT,
17
18
  EVENTS,
18
19
  DISPLAY_HINTS,
20
+ _CALL_,
21
+ LOCUS,
22
+ MEETING_STATE,
23
+ _MEETING_,
19
24
  } from '../../../../src/constants';
20
25
 
21
26
  import {self, selfWithInactivity} from './selfConstant';
27
+ import uuid from 'uuid';
22
28
 
23
29
  describe('plugin-meetings', () => {
24
30
  describe('LocusInfo index', () => {
@@ -66,8 +72,12 @@ describe('plugin-meetings', () => {
66
72
 
67
73
  beforeEach('setup new controls', () => {
68
74
  newControls = {
75
+ disallowUnmute: {enabled: true},
69
76
  lock: {},
70
77
  meetingFull: {},
78
+ muteOnEntry: {enabled: true},
79
+ raiseHand: {enabled: true},
80
+ reactions: {enabled: true, showDisplayNameWithReactions: true},
71
81
  record: {
72
82
  recording: false,
73
83
  paused: false,
@@ -76,12 +86,14 @@ describe('plugin-meetings', () => {
76
86
  modifiedBy: 'George Kittle',
77
87
  },
78
88
  },
79
- shareControl: {},
89
+ shareControl: {control: 'example-value'},
80
90
  transcribe: {},
91
+ viewTheParticipantList: {enabled: true},
81
92
  meetingContainer: {
82
93
  meetingContainerUrl: 'http://new-url.com',
83
94
  },
84
95
  entryExitTone: {enabled: true, mode: 'foo'},
96
+ video: {enabled: true},
85
97
  };
86
98
  });
87
99
 
@@ -95,6 +107,97 @@ describe('plugin-meetings', () => {
95
107
  assert.equal(locusInfo.controls, newControls);
96
108
  });
97
109
 
110
+ it('should trigger the CONTROLS_MUTE_ON_ENTRY_CHANGED event when necessary', () => {
111
+ locusInfo.controls = {};
112
+ locusInfo.emitScoped = sinon.stub();
113
+ locusInfo.updateControls(newControls);
114
+
115
+ assert.calledWith(
116
+ locusInfo.emitScoped,
117
+ {file: 'locus-info', function: 'updateControls'},
118
+ LOCUSINFO.EVENTS.CONTROLS_MUTE_ON_ENTRY_CHANGED,
119
+ {state: newControls.muteOnEntry}
120
+ );
121
+ });
122
+
123
+ it('should trigger the CONTROLS_SHARE_CONTROL_CHANGED event when necessary', () => {
124
+ locusInfo.controls = {};
125
+ locusInfo.emitScoped = sinon.stub();
126
+ locusInfo.updateControls(newControls);
127
+
128
+ assert.calledWith(
129
+ locusInfo.emitScoped,
130
+ {file: 'locus-info', function: 'updateControls'},
131
+ LOCUSINFO.EVENTS.CONTROLS_SHARE_CONTROL_CHANGED,
132
+ {state: newControls.shareControl}
133
+ );
134
+ });
135
+
136
+ it('should trigger the CONTROLS_DISALLOW_UNMUTE_CHANGED event when necessary', () => {
137
+ locusInfo.controls = {};
138
+ locusInfo.emitScoped = sinon.stub();
139
+ locusInfo.updateControls(newControls);
140
+
141
+ assert.calledWith(
142
+ locusInfo.emitScoped,
143
+ {file: 'locus-info', function: 'updateControls'},
144
+ LOCUSINFO.EVENTS.CONTROLS_DISALLOW_UNMUTE_CHANGED,
145
+ {state: newControls.disallowUnmute}
146
+ );
147
+ });
148
+
149
+ it('should trigger the CONTROLS_REACTIONS_CHANGED event when necessary', () => {
150
+ locusInfo.controls = {};
151
+ locusInfo.emitScoped = sinon.stub();
152
+ locusInfo.updateControls(newControls);
153
+
154
+ assert.calledWith(
155
+ locusInfo.emitScoped,
156
+ {file: 'locus-info', function: 'updateControls'},
157
+ LOCUSINFO.EVENTS.CONTROLS_REACTIONS_CHANGED,
158
+ {state: newControls.reactions}
159
+ );
160
+ });
161
+
162
+ it('should trigger the CONTROLS_VIEW_THE_PARTICIPANTS_LIST_CHANGED event when necessary', () => {
163
+ locusInfo.controls = {};
164
+ locusInfo.emitScoped = sinon.stub();
165
+ locusInfo.updateControls(newControls);
166
+
167
+ assert.calledWith(
168
+ locusInfo.emitScoped,
169
+ {file: 'locus-info', function: 'updateControls'},
170
+ LOCUSINFO.EVENTS.CONTROLS_VIEW_THE_PARTICIPANTS_LIST_CHANGED,
171
+ {state: newControls.viewTheParticipantList}
172
+ );
173
+ });
174
+
175
+ it('should trigger the CONTROLS_RAISE_HAND_CHANGED event when necessary', () => {
176
+ locusInfo.controls = {};
177
+ locusInfo.emitScoped = sinon.stub();
178
+ locusInfo.updateControls(newControls);
179
+
180
+ assert.calledWith(
181
+ locusInfo.emitScoped,
182
+ {file: 'locus-info', function: 'updateControls'},
183
+ LOCUSINFO.EVENTS.CONTROLS_RAISE_HAND_CHANGED,
184
+ {state: newControls.raiseHand}
185
+ );
186
+ });
187
+
188
+ it('should trigger the CONTROLS_VIDEO_CHANGED event when necessary', () => {
189
+ locusInfo.controls = {};
190
+ locusInfo.emitScoped = sinon.stub();
191
+ locusInfo.updateControls(newControls);
192
+
193
+ assert.calledWith(
194
+ locusInfo.emitScoped,
195
+ {file: 'locus-info', function: 'updateControls'},
196
+ LOCUSINFO.EVENTS.CONTROLS_VIDEO_CHANGED,
197
+ {state: newControls.video}
198
+ );
199
+ });
200
+
98
201
  it('should not trigger the CONTROLS_RECORDING_UPDATED event', () => {
99
202
  locusInfo.controls = {};
100
203
  locusInfo.emitScoped = sinon.stub();
@@ -277,6 +380,48 @@ describe('plugin-meetings', () => {
277
380
  );
278
381
  });
279
382
 
383
+ it('should update the breakout state', () => {
384
+ locusInfo.emitScoped = sinon.stub();
385
+ let tmpStub = sinon.stub(SelfUtils, 'getReplacedBreakoutMoveId').returns('breakoutMoveId');
386
+ newControls.breakout = {breakout: {}};
387
+ let selfInfo = {};
388
+
389
+ locusInfo.updateControls(newControls, selfInfo);
390
+
391
+ assert.calledWith(
392
+ locusInfo.emitScoped,
393
+ {
394
+ file: 'locus-info',
395
+ function: 'updateControls',
396
+ },
397
+ LOCUSINFO.EVENTS.CONTROLS_MEETING_BREAKOUT_UPDATED,
398
+ {
399
+ breakout: newControls.breakout,
400
+ }
401
+ );
402
+ tmpStub.restore();
403
+ });
404
+
405
+ it('should update the interpretation state', () => {
406
+ locusInfo.emitScoped = sinon.stub();
407
+ newControls.interpretation = {siLanguages: [{languageCode: 20, languageName: 'en'}]};
408
+ let selfInfo = {};
409
+
410
+ locusInfo.updateControls(newControls, selfInfo);
411
+
412
+ assert.calledWith(
413
+ locusInfo.emitScoped,
414
+ {
415
+ file: 'locus-info',
416
+ function: 'updateControls',
417
+ },
418
+ LOCUSINFO.EVENTS.CONTROLS_MEETING_INTERPRETATION_UPDATED,
419
+ {
420
+ interpretation: newControls.interpretation,
421
+ }
422
+ );
423
+ });
424
+
280
425
  it('should update the transcript state', () => {
281
426
  locusInfo.emitScoped = sinon.stub();
282
427
  locusInfo.controls = {
@@ -398,6 +543,39 @@ describe('plugin-meetings', () => {
398
543
  assert.notEqual(x.args[1], LOCUSINFO.EVENTS.CONTROLS_ENTRY_EXIT_TONE_UPDATED);
399
544
  });
400
545
  });
546
+
547
+ it('should update videoEnabled when changed', () => {
548
+ locusInfo.controls = {};
549
+
550
+ locusInfo.emitScoped = sinon.stub();
551
+ locusInfo.updateControls(newControls);
552
+
553
+ assert.calledWith(
554
+ locusInfo.emitScoped,
555
+ {
556
+ file: 'locus-info',
557
+ function: 'updateControls',
558
+ },
559
+ LOCUSINFO.EVENTS.SELF_REMOTE_VIDEO_MUTE_STATUS_UPDATED,
560
+ {unmuteAllowed: true}
561
+ );
562
+
563
+ assert.equal(mockMeeting.unmuteVideoAllowed, true);
564
+ });
565
+
566
+ it('should not update videoEnabled when unchanged', () => {
567
+ locusInfo.controls = {videoEnabled: true};
568
+
569
+ locusInfo.emitScoped = sinon.stub();
570
+ locusInfo.updateControls(newControls);
571
+
572
+ locusInfo.emitScoped.getCalls().forEach((x) => {
573
+ // check that no calls in emitScoped are for SELF_REMOTE_VIDEO_MUTE_STATUS_UPDATED
574
+ assert.notEqual(x.args[1], LOCUSINFO.EVENTS.SELF_REMOTE_VIDEO_MUTE_STATUS_UPDATED);
575
+ });
576
+
577
+ assert.equal(mockMeeting.unmuteVideoAllowed, undefined);
578
+ });
401
579
  });
402
580
 
403
581
  describe('#updateParticipants()', () => {
@@ -451,6 +629,7 @@ describe('plugin-meetings', () => {
451
629
  selfIdentity: '123',
452
630
  selfId: '2',
453
631
  hostId: '3',
632
+ isReplace: undefined,
454
633
  }
455
634
  );
456
635
  // note: in a real use case, recordingId, selfId, and hostId would all be the same
@@ -458,6 +637,43 @@ describe('plugin-meetings', () => {
458
637
  // are being correctly grabbed from locusInfo.parsedLocus within updateParticipants
459
638
  });
460
639
 
640
+ it('should call with breakout control info', () => {
641
+ locusInfo.parsedLocus = {
642
+ controls: {
643
+ record: {
644
+ modifiedBy: '1',
645
+ },
646
+ },
647
+ self: {
648
+ selfIdentity: '123',
649
+ selfId: '2',
650
+ },
651
+ host: {
652
+ hostId: '3',
653
+ },
654
+ };
655
+
656
+ locusInfo.emitScoped = sinon.stub();
657
+ locusInfo.updateParticipants({}, true);
658
+
659
+ assert.calledWith(
660
+ locusInfo.emitScoped,
661
+ {
662
+ file: 'locus-info',
663
+ function: 'updateParticipants',
664
+ },
665
+ EVENTS.LOCUS_INFO_UPDATE_PARTICIPANTS,
666
+ {
667
+ participants: {},
668
+ recordingId: '1',
669
+ selfIdentity: '123',
670
+ selfId: '2',
671
+ hostId: '3',
672
+ isReplace: true,
673
+ }
674
+ );
675
+ });
676
+
461
677
  it('should update the deltaParticipants object', () => {
462
678
  const prev = locusInfo.deltaParticipants;
463
679
 
@@ -664,6 +880,122 @@ describe('plugin-meetings', () => {
664
880
  );
665
881
  });
666
882
 
883
+ describe('SELF_REMOTE_VIDEO_MUTE_STATUS_UPDATED', () => {
884
+ it('should emit event when video muted on entry', () => {
885
+ // usually "previous self" is just undefined when we get first self from locus
886
+ locusInfo.self = undefined;
887
+ const selfWithMutedByOthers = cloneDeep(self);
888
+
889
+ // remoteVideoMuted
890
+ selfWithMutedByOthers.controls.video.muted = true;
891
+
892
+ locusInfo.webex.internal.device.url = self.deviceUrl;
893
+ locusInfo.emitScoped = sinon.stub();
894
+ locusInfo.updateSelf(selfWithMutedByOthers, []);
895
+
896
+ assert.calledWith(
897
+ locusInfo.emitScoped,
898
+ {
899
+ file: 'locus-info',
900
+ function: 'updateSelf',
901
+ },
902
+ LOCUSINFO.EVENTS.SELF_REMOTE_VIDEO_MUTE_STATUS_UPDATED,
903
+ {muted: true}
904
+ );
905
+
906
+ // but sometimes "previous self" is defined, but without controls.audio.muted, so we test this here:
907
+ locusInfo.self = cloneDeep(self);
908
+ locusInfo.self.controls.video = {};
909
+
910
+ locusInfo.updateSelf(selfWithMutedByOthers, []);
911
+ assert.calledWith(
912
+ locusInfo.emitScoped,
913
+ {
914
+ file: 'locus-info',
915
+ function: 'updateSelf',
916
+ },
917
+ LOCUSINFO.EVENTS.SELF_REMOTE_VIDEO_MUTE_STATUS_UPDATED,
918
+ {muted: true}
919
+ );
920
+ });
921
+
922
+ it('should not emit event when not muted on entry', () => {
923
+ locusInfo.self = undefined;
924
+ const selfWithMutedByOthersFalse = cloneDeep(self);
925
+
926
+ selfWithMutedByOthersFalse.controls.video.muted = false;
927
+
928
+ locusInfo.webex.internal.device.url = self.deviceUrl;
929
+ locusInfo.emitScoped = sinon.stub();
930
+ locusInfo.updateSelf(selfWithMutedByOthersFalse, []);
931
+
932
+ // we might get some calls to emitScoped, but we need to check that none of them are for SELF_REMOTE_VIDEO_MUTE_STATUS_UPDATED
933
+ locusInfo.emitScoped.getCalls().forEach((x) => {
934
+ assert.notEqual(x.args[1], LOCUSINFO.EVENTS.SELF_REMOTE_VIDEO_MUTE_STATUS_UPDATED);
935
+ });
936
+ });
937
+
938
+ it('should emit event when remoteVideoMuted changed', () => {
939
+ locusInfo.self = self;
940
+ const selfWithMutedByOthers = cloneDeep(self);
941
+
942
+ selfWithMutedByOthers.controls.video.muted = true;
943
+
944
+ locusInfo.webex.internal.device.url = self.deviceUrl;
945
+ locusInfo.emitScoped = sinon.stub();
946
+ locusInfo.updateSelf(selfWithMutedByOthers, []);
947
+
948
+ assert.calledWith(
949
+ locusInfo.emitScoped,
950
+ {
951
+ file: 'locus-info',
952
+ function: 'updateSelf',
953
+ },
954
+ LOCUSINFO.EVENTS.SELF_REMOTE_VIDEO_MUTE_STATUS_UPDATED,
955
+ {muted: true}
956
+ );
957
+ });
958
+ });
959
+
960
+ it('should trigger SELF_MEETING_BREAKOUTS_CHANGED when breakouts changed', () => {
961
+ locusInfo.self = self;
962
+ const selfWithBreakoutsChanged = cloneDeep(self);
963
+
964
+ selfWithBreakoutsChanged.controls.breakout.sessions.active[0].name = 'new name';
965
+
966
+ locusInfo.emitScoped = sinon.stub();
967
+ locusInfo.updateSelf(selfWithBreakoutsChanged, []);
968
+
969
+ assert.calledWith(
970
+ locusInfo.emitScoped,
971
+ {
972
+ file: 'locus-info',
973
+ function: 'updateSelf',
974
+ },
975
+ LOCUSINFO.EVENTS.SELF_MEETING_BREAKOUTS_CHANGED,
976
+ {
977
+ breakoutSessions: {
978
+ active: [
979
+ {
980
+ name: 'new name',
981
+ groupId: '0e73abb8-5584-49d8-be8d-806d2a8247ca',
982
+ sessionId: '1cf41ab1-2e57-4d95-b7e9-5613acddfb0f',
983
+ sessionType: 'BREAKOUT',
984
+ },
985
+ ],
986
+ allowed: [
987
+ {
988
+ name: 'Breakout session 2',
989
+ groupId: '0e73abb8-5584-49d8-be8d-806d2a8247ca',
990
+ sessionId: '1cf41ab1-2e57-4d95-b7e9-5613acddfb0f',
991
+ sessionType: 'BREAKOUT',
992
+ },
993
+ ],
994
+ },
995
+ }
996
+ );
997
+ });
998
+
667
999
  it('should trigger SELF_REMOTE_MUTE_STATUS_UPDATED if muted and disallowUnmute changed', () => {
668
1000
  locusInfo.self = self;
669
1001
  const selfWithMutedByOthersAndDissalowUnmute = cloneDeep(self);
@@ -735,6 +1067,8 @@ describe('plugin-meetings', () => {
735
1067
  const selfWithRequestedToUnmute = cloneDeep(self);
736
1068
 
737
1069
  selfWithRequestedToUnmute.controls.audio.requestedToUnmute = true;
1070
+ selfWithRequestedToUnmute.controls.audio.lastModifiedRequestedToUnmute =
1071
+ '2023-06-16T19:25:04.369Z';
738
1072
 
739
1073
  locusInfo.webex.internal.device.url = self.deviceUrl;
740
1074
  locusInfo.emitScoped = sinon.stub();
@@ -889,6 +1223,88 @@ describe('plugin-meetings', () => {
889
1223
  {isSharingBlocked: true}
890
1224
  );
891
1225
  });
1226
+
1227
+ it('should trigger SELF_ROLES_CHANGED if self roles changed', () => {
1228
+ locusInfo.self = self;
1229
+ locusInfo.emitScoped = sinon.stub();
1230
+ const sampleNewSelf = cloneDeep(self);
1231
+ sampleNewSelf.controls.role.roles = [{type: 'COHOST', hasRole: true}];
1232
+
1233
+ locusInfo.updateSelf(sampleNewSelf, []);
1234
+
1235
+ assert.calledWith(
1236
+ locusInfo.emitScoped,
1237
+ {
1238
+ file: 'locus-info',
1239
+ function: 'updateSelf',
1240
+ },
1241
+ LOCUSINFO.EVENTS.SELF_ROLES_CHANGED,
1242
+ {oldRoles: ['PRESENTER'], newRoles: ['COHOST']}
1243
+ );
1244
+ });
1245
+
1246
+ it('should not trigger SELF_ROLES_CHANGED if self roles not changed', () => {
1247
+ locusInfo.self = self;
1248
+ locusInfo.emitScoped = sinon.stub();
1249
+ const sampleNewSelf = cloneDeep(self);
1250
+ sampleNewSelf.controls.role.roles = [{type: 'PRESENTER', hasRole: true}];
1251
+
1252
+ locusInfo.updateSelf(sampleNewSelf, []);
1253
+
1254
+ assert.neverCalledWith(
1255
+ locusInfo.emitScoped,
1256
+ {
1257
+ file: 'locus-info',
1258
+ function: 'updateSelf',
1259
+ },
1260
+ LOCUSINFO.EVENTS.SELF_ROLES_CHANGED,
1261
+ {oldRoles: ['PRESENTER'], newRoles: ['PRESENTER']}
1262
+ );
1263
+ });
1264
+
1265
+ it('should trigger SELF_MEETING_INTERPRETATION_CHANGED if self interpretation info changed', () => {
1266
+ locusInfo.self = self;
1267
+ locusInfo.emitScoped = sinon.stub();
1268
+ const sampleNewSelf = cloneDeep(self);
1269
+ sampleNewSelf.controls.interpretation.targetLanguage = 'it';
1270
+
1271
+ locusInfo.updateSelf(sampleNewSelf, []);
1272
+
1273
+ assert.calledWith(
1274
+ locusInfo.emitScoped,
1275
+ {
1276
+ file: 'locus-info',
1277
+ function: 'updateSelf',
1278
+ },
1279
+ LOCUSINFO.EVENTS.SELF_MEETING_INTERPRETATION_CHANGED,
1280
+ {
1281
+ interpretation: sampleNewSelf.controls.interpretation,
1282
+ selfParticipantId: self.id,
1283
+ }
1284
+ );
1285
+ });
1286
+
1287
+ it('should not trigger SELF_MEETING_INTERPRETATION_CHANGED if self interpretation info not changed', () => {
1288
+ locusInfo.self = self;
1289
+ locusInfo.emitScoped = sinon.stub();
1290
+ const sampleNewSelf = cloneDeep(self);
1291
+ sampleNewSelf.controls.interpretation.targetLanguage = 'cn'; // same with previous one
1292
+
1293
+ locusInfo.updateSelf(sampleNewSelf, []);
1294
+
1295
+ assert.neverCalledWith(
1296
+ locusInfo.emitScoped,
1297
+ {
1298
+ file: 'locus-info',
1299
+ function: 'updateSelf',
1300
+ },
1301
+ LOCUSINFO.EVENTS.SELF_MEETING_INTERPRETATION_CHANGED,
1302
+ {
1303
+ interpretation: sampleNewSelf.controls.interpretation,
1304
+ selfParticipantId: self.id,
1305
+ }
1306
+ );
1307
+ });
892
1308
  });
893
1309
 
894
1310
  describe('#updateMeetingInfo', () => {
@@ -967,19 +1383,36 @@ describe('plugin-meetings', () => {
967
1383
 
968
1384
  if (expected) {
969
1385
  assert.calledWith(...expectedArgs);
970
- }
971
- else {
1386
+ } else {
972
1387
  assert.neverCalledWith(...expectedArgs);
973
1388
  }
974
1389
  locusInfo.emitScoped.resetHistory();
975
1390
  };
976
1391
 
977
- it('emits MEETING_INFO_UPDATED if the info changes', () => {
978
- const initialInfo = cloneDeep(meetingInfo);
979
-
980
- locusInfo.emitScoped = sinon.stub();
1392
+ const checkMeetingInfoUpdatedCalledForRoles = (expected) => {
1393
+ const expectedArgs = [
1394
+ locusInfo.emitScoped,
1395
+ {
1396
+ file: 'locus-info',
1397
+ function: 'updateMeetingInfo',
1398
+ },
1399
+ LOCUSINFO.EVENTS.MEETING_INFO_UPDATED,
1400
+ ];
981
1401
 
982
- // set the info initially as locusInfo.info starts as undefined
1402
+ if (expected) {
1403
+ assert.calledWith(...expectedArgs);
1404
+ } else {
1405
+ assert.neverCalledWith(...expectedArgs);
1406
+ }
1407
+ locusInfo.emitScoped.resetHistory();
1408
+ };
1409
+
1410
+ it('emits MEETING_INFO_UPDATED if the info changes', () => {
1411
+ const initialInfo = cloneDeep(meetingInfo);
1412
+
1413
+ locusInfo.emitScoped = sinon.stub();
1414
+
1415
+ // set the info initially as locusInfo.info starts as undefined
983
1416
  locusInfo.updateMeetingInfo(initialInfo, self);
984
1417
 
985
1418
  // since it was initially undefined, this should trigger the event
@@ -999,6 +1432,16 @@ describe('plugin-meetings', () => {
999
1432
 
1000
1433
  // since the info is the same it should not call trigger the event
1001
1434
  checkMeetingInfoUpdatedCalled(false);
1435
+
1436
+ // update it with the same info, but roles changed
1437
+ const updateSelf = cloneDeep(self);
1438
+ updateSelf?.controls?.role?.roles.push({
1439
+ type: 'COHOST',
1440
+ hasRole: true,
1441
+ });
1442
+ locusInfo.updateMeetingInfo(newInfo, updateSelf);
1443
+ // since the info is the same but roles changed, it should call trigger the event
1444
+ checkMeetingInfoUpdatedCalledForRoles(true);
1002
1445
  });
1003
1446
 
1004
1447
  it('gets roles from self if available', () => {
@@ -1104,6 +1547,8 @@ describe('plugin-meetings', () => {
1104
1547
  fakeLocus = {
1105
1548
  meeting: true,
1106
1549
  participants: true,
1550
+ url: 'newLocusUrl',
1551
+ syncUrl: 'newSyncUrl',
1107
1552
  };
1108
1553
  });
1109
1554
 
@@ -1148,6 +1593,55 @@ describe('plugin-meetings', () => {
1148
1593
  assert.isFunction(locusParser.onDeltaAction);
1149
1594
  });
1150
1595
 
1596
+ it('#updateLocusInfo ignores breakout LEFT message', () => {
1597
+ const newLocus = {
1598
+ self: {
1599
+ reason: 'MOVED',
1600
+ state: 'LEFT',
1601
+ },
1602
+ };
1603
+
1604
+ locusInfo.updateControls = sinon.stub();
1605
+ locusInfo.updateConversationUrl = sinon.stub();
1606
+ locusInfo.updateCreated = sinon.stub();
1607
+ locusInfo.updateFullState = sinon.stub();
1608
+ locusInfo.updateHostInfo = sinon.stub();
1609
+ locusInfo.updateMeetingInfo = sinon.stub();
1610
+ locusInfo.updateMediaShares = sinon.stub();
1611
+ locusInfo.updateParticipantsUrl = sinon.stub();
1612
+ locusInfo.updateReplace = sinon.stub();
1613
+ locusInfo.updateSelf = sinon.stub();
1614
+ locusInfo.updateLocusUrl = sinon.stub();
1615
+ locusInfo.updateAclUrl = sinon.stub();
1616
+ locusInfo.updateBasequence = sinon.stub();
1617
+ locusInfo.updateSequence = sinon.stub();
1618
+ locusInfo.updateMemberShip = sinon.stub();
1619
+ locusInfo.updateIdentifiers = sinon.stub();
1620
+ locusInfo.updateEmbeddedApps = sinon.stub();
1621
+ locusInfo.compareAndUpdate = sinon.stub();
1622
+
1623
+ locusInfo.updateLocusInfo(newLocus);
1624
+
1625
+ assert.notCalled(locusInfo.updateControls);
1626
+ assert.notCalled(locusInfo.updateConversationUrl);
1627
+ assert.notCalled(locusInfo.updateCreated);
1628
+ assert.notCalled(locusInfo.updateFullState);
1629
+ assert.notCalled(locusInfo.updateHostInfo);
1630
+ assert.notCalled(locusInfo.updateMeetingInfo);
1631
+ assert.notCalled(locusInfo.updateMediaShares);
1632
+ assert.notCalled(locusInfo.updateParticipantsUrl);
1633
+ assert.notCalled(locusInfo.updateReplace);
1634
+ assert.notCalled(locusInfo.updateSelf);
1635
+ assert.notCalled(locusInfo.updateLocusUrl);
1636
+ assert.notCalled(locusInfo.updateAclUrl);
1637
+ assert.notCalled(locusInfo.updateBasequence);
1638
+ assert.notCalled(locusInfo.updateSequence);
1639
+ assert.notCalled(locusInfo.updateMemberShip);
1640
+ assert.notCalled(locusInfo.updateIdentifiers);
1641
+ assert.notCalled(locusInfo.updateEmbeddedApps);
1642
+ assert.notCalled(locusInfo.compareAndUpdate);
1643
+ });
1644
+
1151
1645
  it('onFullLocus() updates the working-copy of locus parser', () => {
1152
1646
  const eventType = 'fakeEvent';
1153
1647
 
@@ -1156,12 +1650,39 @@ describe('plugin-meetings', () => {
1156
1650
  sandbox.stub(locusInfo, 'updateParticipants');
1157
1651
  sandbox.stub(locusInfo, 'isMeetingActive');
1158
1652
  sandbox.stub(locusInfo, 'handleOneOnOneEvent');
1653
+ sandbox.stub(locusParser, 'isNewFullLocus').returns(true);
1159
1654
 
1160
1655
  locusInfo.onFullLocus(fakeLocus, eventType);
1161
1656
 
1162
1657
  assert.equal(fakeLocus, locusParser.workingCopy);
1163
1658
  });
1164
1659
 
1660
+ it('onFullLocus() does not do anything if the incoming full locus DTO is old', () => {
1661
+ const eventType = 'fakeEvent';
1662
+
1663
+ locusParser.workingCopy = {};
1664
+
1665
+ const oldWorkingCopy = locusParser.workingCopy;
1666
+
1667
+ const spies = [
1668
+ sandbox.stub(locusInfo, 'updateParticipantDeltas'),
1669
+ sandbox.stub(locusInfo, 'updateLocusInfo'),
1670
+ sandbox.stub(locusInfo, 'updateParticipants'),
1671
+ sandbox.stub(locusInfo, 'isMeetingActive'),
1672
+ sandbox.stub(locusInfo, 'handleOneOnOneEvent'),
1673
+ ];
1674
+
1675
+ sandbox.stub(locusParser, 'isNewFullLocus').returns(false);
1676
+
1677
+ locusInfo.onFullLocus(fakeLocus, eventType);
1678
+
1679
+ spies.forEach((spy) => {
1680
+ assert.notCalled(spy);
1681
+ });
1682
+
1683
+ assert.equal(oldWorkingCopy, locusParser.workingCopy);
1684
+ });
1685
+
1165
1686
  it('onDeltaAction applies locus delta data to meeting', () => {
1166
1687
  const action = 'fake action';
1167
1688
  const parsedLoci = 'fake loci';
@@ -1188,33 +1709,82 @@ describe('plugin-meetings', () => {
1188
1709
  assert.calledWith(meeting.locusInfo.onDeltaLocus, fakeLocus);
1189
1710
  });
1190
1711
 
1191
- it('applyLocusDeltaData gets full locus on DESYNC action', () => {
1712
+ it('applyLocusDeltaData gets delta locus on DESYNC action if we have a syncUrl', () => {
1192
1713
  const {DESYNC} = LocusDeltaParser.loci;
1714
+ const fakeDeltaLocus = {id: 'fake delta locus'};
1193
1715
  const meeting = {
1194
1716
  meetingRequest: {
1195
- getFullLocus: sandbox.stub().resolves(true),
1717
+ getLocusDTO: sandbox.stub().resolves({body: fakeDeltaLocus}),
1196
1718
  },
1197
1719
  locusInfo: {
1720
+ handleLocusDelta: sandbox.stub(),
1721
+ },
1722
+ locusUrl: 'oldLocusUrl',
1723
+ };
1724
+
1725
+ locusInfo.locusParser.workingCopy = {
1726
+ syncUrl: 'oldSyncUrl',
1727
+ };
1728
+
1729
+ // Since we have a promise inside a function we want to test that's not returned,
1730
+ // we will wait and stub it's last function to resolve this waiting promise.
1731
+ // Also ensures .handleLocusDelta() is called before .resume()
1732
+ return new Promise((resolve) => {
1733
+ locusInfo.locusParser.resume = sandbox.stub().callsFake(() => resolve());
1734
+ locusInfo.applyLocusDeltaData(DESYNC, fakeLocus, meeting);
1735
+ }).then(() => {
1736
+ assert.calledOnceWithExactly(meeting.meetingRequest.getLocusDTO, {url: 'oldSyncUrl'});
1737
+
1738
+ assert.calledOnceWithExactly(meeting.locusInfo.handleLocusDelta, fakeDeltaLocus, meeting);
1739
+ assert.calledOnce(locusInfo.locusParser.resume);
1740
+ });
1741
+ });
1742
+
1743
+ it('applyLocusDeltaData gets delta locus on DESYNC action if we have a syncUrl (empty response body)', () => {
1744
+ const {DESYNC} = LocusDeltaParser.loci;
1745
+ const meeting = {
1746
+ meetingRequest: {
1747
+ getLocusDTO: sandbox.stub().resolves({body: {}}),
1748
+ },
1749
+ locusInfo: {
1750
+ handleLocusDelta: sandbox.stub(),
1198
1751
  onFullLocus: sandbox.stub(),
1199
1752
  },
1753
+ locusUrl: 'oldLocusUrl',
1754
+ };
1755
+
1756
+ locusInfo.locusParser.workingCopy = {
1757
+ syncUrl: 'oldSyncUrl',
1200
1758
  };
1201
1759
 
1202
- locusInfo.locusParser.resume = sandbox.stub();
1203
- locusInfo.applyLocusDeltaData(DESYNC, fakeLocus, meeting);
1760
+ // Since we have a promise inside a function we want to test that's not returned,
1761
+ // we will wait and stub it's last function to resolve this waiting promise.
1762
+ return new Promise((resolve) => {
1763
+ locusInfo.locusParser.resume = sandbox.stub().callsFake(() => resolve());
1764
+ locusInfo.applyLocusDeltaData(DESYNC, fakeLocus, meeting);
1765
+ }).then(() => {
1766
+ assert.calledOnceWithExactly(meeting.meetingRequest.getLocusDTO, {url: 'oldSyncUrl'});
1204
1767
 
1205
- assert.calledOnce(meeting.meetingRequest.getFullLocus);
1768
+ assert.notCalled(meeting.locusInfo.handleLocusDelta);
1769
+ assert.notCalled(meeting.locusInfo.onFullLocus);
1770
+ assert.calledOnce(locusInfo.locusParser.resume);
1771
+ });
1206
1772
  });
1207
1773
 
1208
- it('getFullLocus handles DESYNC action correctly', () => {
1774
+ it('applyLocusDeltaData gets full locus on DESYNC action if we do not have a syncUrl', () => {
1209
1775
  const {DESYNC} = LocusDeltaParser.loci;
1776
+ const fakeFullLocusDto = {id: 'fake full locus dto'};
1210
1777
  const meeting = {
1211
1778
  meetingRequest: {
1212
- getFullLocus: sandbox.stub().resolves({body: true}),
1779
+ getLocusDTO: sandbox.stub().resolves({body: fakeFullLocusDto}),
1780
+ },
1781
+ locusInfo: {
1782
+ onFullLocus: sandbox.stub(),
1213
1783
  },
1214
- locusInfo,
1784
+ locusUrl: 'oldLocusUrl',
1215
1785
  };
1216
1786
 
1217
- locusInfo.onFullLocus = sandbox.stub();
1787
+ locusInfo.locusParser.workingCopy = {}; // no syncUrl
1218
1788
 
1219
1789
  // Since we have a promise inside a function we want to test that's not returned,
1220
1790
  // we will wait and stub it's last function to resolve this waiting promise.
@@ -1223,10 +1793,201 @@ describe('plugin-meetings', () => {
1223
1793
  locusInfo.locusParser.resume = sandbox.stub().callsFake(() => resolve());
1224
1794
  locusInfo.applyLocusDeltaData(DESYNC, fakeLocus, meeting);
1225
1795
  }).then(() => {
1226
- assert.calledOnce(meeting.locusInfo.onFullLocus);
1796
+ assert.calledOnceWithExactly(meeting.meetingRequest.getLocusDTO, {url: 'oldLocusUrl'});
1797
+
1798
+ assert.calledOnceWithExactly(meeting.locusInfo.onFullLocus, fakeFullLocusDto);
1227
1799
  assert.calledOnce(locusInfo.locusParser.resume);
1228
1800
  });
1229
1801
  });
1802
+
1803
+ it('onDeltaLocus handle delta data', () => {
1804
+ fakeLocus.participants = {};
1805
+ const fakeBreakout = {
1806
+ sessionId: 'sessionId',
1807
+ groupId: 'groupId',
1808
+ };
1809
+
1810
+ fakeLocus.controls = {
1811
+ breakout: fakeBreakout,
1812
+ };
1813
+ locusInfo.controls = {
1814
+ breakout: {
1815
+ sessionId: 'sessionId',
1816
+ groupId: 'groupId',
1817
+ },
1818
+ };
1819
+ locusInfo.updateParticipants = sinon.stub();
1820
+ locusInfo.onDeltaLocus(fakeLocus);
1821
+ assert.calledWith(locusInfo.updateParticipants, {}, false);
1822
+
1823
+ fakeLocus.controls.breakout.sessionId = 'sessionId2';
1824
+ locusInfo.onDeltaLocus(fakeLocus);
1825
+ assert.calledWith(locusInfo.updateParticipants, {}, true);
1826
+ });
1827
+ });
1828
+
1829
+ describe('#updateLocusCache', () => {
1830
+ it('cache it if income locus is main session locus', () => {
1831
+ const locus = {url: 'url'};
1832
+ locusInfo.mainSessionLocusCache = null;
1833
+ locusInfo.updateLocusCache(locus);
1834
+
1835
+ assert.deepEqual(locusInfo.mainSessionLocusCache, locus);
1836
+ });
1837
+
1838
+ it('not cache it if income locus is breakout session locus', () => {
1839
+ const locus = {url: 'url', controls: {breakout: {sessionType: 'BREAKOUT'}}};
1840
+ locusInfo.mainSessionLocusCache = null;
1841
+ locusInfo.updateLocusCache(locus);
1842
+
1843
+ assert.isNull(locusInfo.mainSessionLocusCache);
1844
+ });
1845
+ });
1846
+
1847
+ describe('#getTheLocusToUpdate', () => {
1848
+ it('return the cache locus if return to main session', () => {
1849
+ locusInfo.mainSessionLocusCache = {url: 'url'};
1850
+ locusInfo.controls = {
1851
+ breakout: {
1852
+ sessionType: 'BREAKOUT',
1853
+ },
1854
+ };
1855
+ const newLocus = {
1856
+ controls: {
1857
+ breakout: {
1858
+ sessionType: 'MAIN',
1859
+ },
1860
+ },
1861
+ };
1862
+
1863
+ assert.deepEqual(locusInfo.getTheLocusToUpdate(newLocus), {url: 'url'});
1864
+ });
1865
+
1866
+ it('return the new locus if return to main session but no cache', () => {
1867
+ locusInfo.mainSessionLocusCache = null;
1868
+ locusInfo.controls = {
1869
+ breakout: {
1870
+ sessionType: 'BREAKOUT',
1871
+ },
1872
+ };
1873
+ const newLocus = {
1874
+ controls: {
1875
+ breakout: {
1876
+ sessionType: 'MAIN',
1877
+ },
1878
+ },
1879
+ };
1880
+
1881
+ assert.deepEqual(locusInfo.getTheLocusToUpdate(newLocus), newLocus);
1882
+ });
1883
+
1884
+ it('return the new locus if not return to main session', () => {
1885
+ locusInfo.mainSessionLocusCache = {url: 'url'};
1886
+ locusInfo.controls = {
1887
+ breakout: {
1888
+ sessionType: 'MAIN',
1889
+ },
1890
+ };
1891
+ const newLocus = {
1892
+ controls: {
1893
+ breakout: {
1894
+ sessionType: 'BREAKOUT',
1895
+ },
1896
+ },
1897
+ };
1898
+
1899
+ assert.deepEqual(locusInfo.getTheLocusToUpdate(newLocus), newLocus);
1900
+ });
1901
+ });
1902
+
1903
+ describe('#mergeParticipants', () => {
1904
+ let participants;
1905
+ let sourceParticipants;
1906
+ beforeEach(() => {
1907
+ participants = [{id: '111', status: 'JOINED'}, {id: '222'}];
1908
+ sourceParticipants = [{id: '111', status: 'LEFT'}, {id: '333'}];
1909
+ });
1910
+
1911
+ it('merge the participants, replace it by id if exist in old array', () => {
1912
+ const result = locusInfo.mergeParticipants(participants, sourceParticipants);
1913
+ assert.deepEqual(result, [{id: '111', status: 'LEFT'}, {id: '222'}, {id: '333'}]);
1914
+ });
1915
+
1916
+ it('return new participants if previous participants is empty', () => {
1917
+ const result = locusInfo.mergeParticipants([], sourceParticipants);
1918
+ assert.deepEqual(result, sourceParticipants);
1919
+ });
1920
+
1921
+ it('return new participants if previous participants is null/undefined', () => {
1922
+ let result = locusInfo.mergeParticipants(null, sourceParticipants);
1923
+ assert.deepEqual(result, sourceParticipants);
1924
+
1925
+ result = locusInfo.mergeParticipants(undefined, sourceParticipants);
1926
+ assert.deepEqual(result, sourceParticipants);
1927
+ });
1928
+
1929
+ it('return previous participants if new participants is empty', () => {
1930
+ const result = locusInfo.mergeParticipants(participants, []);
1931
+ assert.deepEqual(result, participants);
1932
+ });
1933
+
1934
+ it('return previous participants if new participants is null/undefined', () => {
1935
+ let result = locusInfo.mergeParticipants(participants, null);
1936
+ assert.deepEqual(result, participants);
1937
+
1938
+ result = locusInfo.mergeParticipants(participants, undefined);
1939
+ assert.deepEqual(result, participants);
1940
+ });
1941
+ });
1942
+
1943
+ describe('#updateMainSessionLocusCache', () => {
1944
+ let cachedLocus;
1945
+ let newLocus;
1946
+ beforeEach(() => {
1947
+ cachedLocus = {
1948
+ controls: {},
1949
+ participants: [],
1950
+ info: {webExMeetingId: 'testId1', topic: 'test'},
1951
+ };
1952
+ newLocus = {
1953
+ self: {},
1954
+ participants: [{id: '111'}],
1955
+ info: {testId: 'testId2', webExMeetingName: 'hello'},
1956
+ };
1957
+ });
1958
+ it('shallow merge new locus into cache', () => {
1959
+ locusInfo.mainSessionLocusCache = cachedLocus;
1960
+ locusInfo.updateMainSessionLocusCache(newLocus);
1961
+
1962
+ assert.deepEqual(locusInfo.mainSessionLocusCache, {
1963
+ controls: {},
1964
+ participants: [{id: '111'}],
1965
+ info: {testId: 'testId2', webExMeetingName: 'hello'},
1966
+ self: {},
1967
+ });
1968
+ });
1969
+
1970
+ it('cache new locus if no cache before', () => {
1971
+ locusInfo.mainSessionLocusCache = null;
1972
+ locusInfo.updateMainSessionLocusCache(newLocus);
1973
+
1974
+ assert.deepEqual(locusInfo.mainSessionLocusCache, newLocus);
1975
+ });
1976
+
1977
+ it('do nothing if new locus is null', () => {
1978
+ locusInfo.mainSessionLocusCache = cachedLocus;
1979
+ locusInfo.updateMainSessionLocusCache(null);
1980
+
1981
+ assert.deepEqual(locusInfo.mainSessionLocusCache, cachedLocus);
1982
+ });
1983
+ });
1984
+
1985
+ describe('#clearMainSessionLocusCache', () => {
1986
+ it('clear main session locus cache', () => {
1987
+ locusInfo.mainSessionLocusCache = {controls: {}};
1988
+ locusInfo.clearMainSessionLocusCache();
1989
+ assert.isNull(locusInfo.mainSessionLocusCache);
1990
+ });
1230
1991
  });
1231
1992
 
1232
1993
  describe('#handleOneonOneEvent', () => {
@@ -1269,5 +2030,402 @@ describe('plugin-meetings', () => {
1269
2030
  );
1270
2031
  });
1271
2032
  });
2033
+
2034
+ describe('#isMeetingActive', () => {
2035
+ it('sends client event correctly for state = inactive', () => {
2036
+ locusInfo.parsedLocus = {
2037
+ fullState: {
2038
+ type: _CALL_,
2039
+ },
2040
+ };
2041
+
2042
+ locusInfo.fullState = {
2043
+ state: LOCUS.STATE.INACTIVE,
2044
+ };
2045
+
2046
+ locusInfo.isMeetingActive();
2047
+
2048
+ assert.calledWith(webex.internal.newMetrics.submitClientEvent, {
2049
+ name: 'client.call.remote-ended',
2050
+ options: {
2051
+ meetingId: locusInfo.meetingId,
2052
+ },
2053
+ });
2054
+ });
2055
+
2056
+ it('sends client event correctly for state = PARTNER_LEFT', () => {
2057
+ locusInfo.getLocusPartner = sinon.stub().returns({state: MEETING_STATE.STATES.LEFT});
2058
+ locusInfo.parsedLocus = {
2059
+ fullState: {
2060
+ type: _CALL_,
2061
+ },
2062
+ self: {
2063
+ state: MEETING_STATE.STATES.DECLINED,
2064
+ },
2065
+ };
2066
+ locusInfo.isMeetingActive();
2067
+
2068
+ assert.calledWith(webex.internal.newMetrics.submitClientEvent, {
2069
+ name: 'client.call.remote-ended',
2070
+ options: {
2071
+ meetingId: locusInfo.meetingId,
2072
+ },
2073
+ });
2074
+ });
2075
+
2076
+ it('sends client event correctly for state = SELF_LEFT', () => {
2077
+ locusInfo.getLocusPartner = sinon.stub().returns({state: MEETING_STATE.STATES.LEFT});
2078
+ locusInfo.parsedLocus = {
2079
+ fullState: {
2080
+ type: _CALL_,
2081
+ },
2082
+ self: {
2083
+ state: MEETING_STATE.STATES.LEFT,
2084
+ },
2085
+ };
2086
+
2087
+ locusInfo.isMeetingActive();
2088
+
2089
+ assert.calledWith(webex.internal.newMetrics.submitClientEvent, {
2090
+ name: 'client.call.remote-ended',
2091
+ options: {
2092
+ meetingId: locusInfo.meetingId,
2093
+ },
2094
+ });
2095
+ });
2096
+
2097
+ it('sends client event correctly for state = MEETING_INACTIVE_TERMINATING', () => {
2098
+ locusInfo.getLocusPartner = sinon.stub().returns({state: MEETING_STATE.STATES.LEFT});
2099
+ locusInfo.parsedLocus = {
2100
+ fullState: {
2101
+ type: _MEETING_,
2102
+ },
2103
+ };
2104
+
2105
+ locusInfo.fullState = {
2106
+ state: LOCUS.STATE.INACTIVE,
2107
+ };
2108
+
2109
+ locusInfo.isMeetingActive();
2110
+
2111
+ assert.calledWith(webex.internal.newMetrics.submitClientEvent, {
2112
+ name: 'client.call.remote-ended',
2113
+ options: {
2114
+ meetingId: locusInfo.meetingId,
2115
+ },
2116
+ });
2117
+ });
2118
+
2119
+ it('sends client event correctly for state = FULLSTATE_REMOVED', () => {
2120
+ locusInfo.getLocusPartner = sinon.stub().returns({state: MEETING_STATE.STATES.LEFT});
2121
+ locusInfo.parsedLocus = {
2122
+ fullState: {
2123
+ type: _MEETING_,
2124
+ },
2125
+ };
2126
+
2127
+ locusInfo.fullState = {
2128
+ removed: true,
2129
+ };
2130
+
2131
+ locusInfo.isMeetingActive();
2132
+
2133
+ assert.calledWith(webex.internal.newMetrics.submitClientEvent, {
2134
+ name: 'client.call.remote-ended',
2135
+ options: {
2136
+ meetingId: locusInfo.meetingId,
2137
+ },
2138
+ });
2139
+ });
2140
+ });
2141
+
2142
+ // semi-integration tests that use real LocusInfo with real Parser
2143
+ // and test various scenarios related to handling out-of-order Locus delta events
2144
+ describe('handling of out-of-order Locus delta events', () => {
2145
+ let clock;
2146
+
2147
+ const generateDeltaEvent = (base, sequence) => {
2148
+ return {
2149
+ baseSequence: {
2150
+ rangeStart: 0,
2151
+ rangeEnd: 0,
2152
+ entries: [base],
2153
+ },
2154
+ sequence: {
2155
+ rangeStart: 0,
2156
+ rangeEnd: 0,
2157
+ entries: [sequence],
2158
+ },
2159
+ syncUrl: `fake sync url for sequence ${sequence}`,
2160
+ self: {
2161
+ person: {
2162
+ id: 'test person id',
2163
+ },
2164
+ },
2165
+ };
2166
+ };
2167
+
2168
+ // a list of example delta events, sorted by time and each event is based on the previous one
2169
+ const deltaEvents = [
2170
+ generateDeltaEvent(10, 20), // 0
2171
+ generateDeltaEvent(20, 30), // 1
2172
+ generateDeltaEvent(30, 40), // 2
2173
+ generateDeltaEvent(40, 50), // 3
2174
+ generateDeltaEvent(50, 60), // 4
2175
+ generateDeltaEvent(60, 70), // 5
2176
+ generateDeltaEvent(70, 80), // 6
2177
+ generateDeltaEvent(80, 90), // 7
2178
+ generateDeltaEvent(90, 100), // 8
2179
+ ];
2180
+
2181
+ let updateLocusInfoStub; // we use this stub to verify that an event has been fully processed
2182
+ let syncRequestStub;
2183
+
2184
+ beforeEach(() => {
2185
+ clock = sinon.useFakeTimers();
2186
+
2187
+ sinon.stub(locusInfo, 'updateParticipantDeltas');
2188
+ sinon.stub(locusInfo, 'updateParticipants');
2189
+ sinon.stub(locusInfo, 'isMeetingActive'),
2190
+ sinon.stub(locusInfo, 'handleOneOnOneEvent'),
2191
+ (updateLocusInfoStub = sinon.stub(locusInfo, 'updateLocusInfo'));
2192
+ syncRequestStub = sinon.stub().resolves({body: {}});
2193
+
2194
+ mockMeeting.locusInfo = locusInfo;
2195
+ mockMeeting.locusUrl = 'fake locus url';
2196
+ mockMeeting.meetingRequest = {
2197
+ getLocusDTO: syncRequestStub,
2198
+ };
2199
+
2200
+ locusInfo.onFullLocus({
2201
+ sequence: {
2202
+ rangeStart: 0,
2203
+ rangeEnd: 0,
2204
+ entries: [10],
2205
+ },
2206
+ self: {
2207
+ person: {
2208
+ id: 'test person id',
2209
+ },
2210
+ },
2211
+ });
2212
+
2213
+ updateLocusInfoStub.resetHistory();
2214
+ });
2215
+
2216
+ afterEach(() => {
2217
+ clock.restore();
2218
+ });
2219
+
2220
+ it('queues out-of-order deltas until it receives a correct delta', () => {
2221
+ // send some out-of-order deltas
2222
+ locusInfo.handleLocusDelta(deltaEvents[1], mockMeeting);
2223
+ locusInfo.handleLocusDelta(deltaEvents[4], mockMeeting);
2224
+
2225
+ // they should be queued and not processed
2226
+ assert.notCalled(updateLocusInfoStub);
2227
+
2228
+ // now one of the missing ones, but not the one SDK is really waiting for
2229
+ locusInfo.handleLocusDelta(deltaEvents[2], mockMeeting);
2230
+
2231
+ // still nothing should be processed
2232
+ assert.notCalled(updateLocusInfoStub);
2233
+
2234
+ // now send the one SDK is waiting for
2235
+ locusInfo.handleLocusDelta(deltaEvents[0], mockMeeting);
2236
+
2237
+ // so deltaEvents with indexes 1,2,3 can be processed, but 5 still not, because 4 is missing
2238
+ assert.callCount(updateLocusInfoStub, 3);
2239
+ assert.calledWith(updateLocusInfoStub.getCall(0), deltaEvents[0]);
2240
+ assert.calledWith(updateLocusInfoStub.getCall(1), deltaEvents[1]);
2241
+ assert.calledWith(updateLocusInfoStub.getCall(2), deltaEvents[2]);
2242
+
2243
+ updateLocusInfoStub.resetHistory();
2244
+
2245
+ // now send deltaEvents[4]
2246
+ locusInfo.handleLocusDelta(deltaEvents[3], mockMeeting);
2247
+
2248
+ // and verify deltaEvents[4] and deltaEvents[5] have been processed
2249
+ assert.callCount(updateLocusInfoStub, 2);
2250
+ assert.calledWith(updateLocusInfoStub.getCall(0), deltaEvents[3]);
2251
+ assert.calledWith(updateLocusInfoStub.getCall(1), deltaEvents[4]);
2252
+ });
2253
+
2254
+ it('handles out-of-order deltas correctly even if all arrive in reverse order', () => {
2255
+ // send a bunch deltas in reverse order
2256
+ for (let i = 4; i >= 0; i--) {
2257
+ locusInfo.handleLocusDelta(deltaEvents[i], mockMeeting);
2258
+ }
2259
+
2260
+ // they should be queued and then processed in correct order
2261
+ assert.callCount(updateLocusInfoStub, 5);
2262
+ assert.calledWith(updateLocusInfoStub.getCall(0), deltaEvents[0]);
2263
+ assert.calledWith(updateLocusInfoStub.getCall(1), deltaEvents[1]);
2264
+ assert.calledWith(updateLocusInfoStub.getCall(2), deltaEvents[2]);
2265
+ assert.calledWith(updateLocusInfoStub.getCall(3), deltaEvents[3]);
2266
+ assert.calledWith(updateLocusInfoStub.getCall(4), deltaEvents[4]);
2267
+ });
2268
+
2269
+ it('sends a sync request using syncUrl if it receives at least 1 delta event and processes later deltas after sync correctly', async () => {
2270
+ // the test first sends an initial "good" delta
2271
+ const initialDeltaIdx = 0;
2272
+ const initialDelta = deltaEvents[initialDeltaIdx];
2273
+
2274
+ // then it sends a bunch of out-of-order deltas (at least 6 to trigger a sync), last one being lastOooDelta
2275
+ const firstOooDeltaIdx = 2;
2276
+ const lastOooDeltaIdx = 7;
2277
+ const lastOooDelta = deltaEvents[lastOooDeltaIdx];
2278
+
2279
+ // and finally, after the sync it sends another "good" delta
2280
+ const goodDeltaAfterSync = deltaEvents[8];
2281
+
2282
+ const deltaLocusFromSyncResponse = {
2283
+ baseSequence: {
2284
+ rangeStart: 0,
2285
+ rangeEnd: 0,
2286
+ entries: [initialDelta.sequence.entries[0]],
2287
+ },
2288
+ sequence: {
2289
+ rangeStart: 0,
2290
+ rangeEnd: 0,
2291
+ entries: [lastOooDelta.sequence.entries[0]],
2292
+ },
2293
+ syncUrl: `fake sync url for sequence ${lastOooDelta.sequence.entries[0]}`,
2294
+ self: {
2295
+ person: {
2296
+ id: 'test person id',
2297
+ },
2298
+ },
2299
+ };
2300
+
2301
+ syncRequestStub.resolves({
2302
+ body: deltaLocusFromSyncResponse,
2303
+ });
2304
+
2305
+ // send one correct delta so that SDK has the syncUrl
2306
+ locusInfo.handleLocusDelta(initialDelta, mockMeeting);
2307
+
2308
+ updateLocusInfoStub.resetHistory();
2309
+
2310
+ // send 6 out-of-order deltas to trigger a sync (we're skipping deltaEvents[1])
2311
+ for (let i = firstOooDeltaIdx; i <= lastOooDeltaIdx; i++) {
2312
+ locusInfo.handleLocusDelta(deltaEvents[i], mockMeeting);
2313
+ }
2314
+
2315
+ await testUtils.flushPromises();
2316
+
2317
+ // check that sync was done using the correct syncUrl
2318
+ assert.calledOnceWithExactly(syncRequestStub, {url: initialDelta.syncUrl});
2319
+ assert.calledOnceWithExactly(updateLocusInfoStub, deltaLocusFromSyncResponse);
2320
+
2321
+ updateLocusInfoStub.resetHistory();
2322
+
2323
+ // now send another delta - a good one, it should be processed as normal
2324
+ locusInfo.handleLocusDelta(goodDeltaAfterSync, mockMeeting);
2325
+
2326
+ assert.calledOnceWithExactly(updateLocusInfoStub, goodDeltaAfterSync);
2327
+ });
2328
+
2329
+ it('does a sync if blocked on out-of-order deltas for too long', async () => {
2330
+ // stub random so that the timer fires after 12500 ms
2331
+ sinon.stub(Math, 'random').returns(0.5);
2332
+
2333
+ const oooDelta = deltaEvents[3];
2334
+
2335
+ // setup the stubs so that the sync request receives a full DTO with the sequence equal to the out-of-order delta we simulate
2336
+ const fullLocus = {
2337
+ sequence: oooDelta.sequence,
2338
+ };
2339
+ syncRequestStub.resolves({
2340
+ body: fullLocus,
2341
+ });
2342
+
2343
+ // send an out-of-order delta
2344
+ locusInfo.handleLocusDelta(oooDelta, mockMeeting);
2345
+
2346
+ await clock.tickAsync(12499);
2347
+ await testUtils.flushPromises();
2348
+ assert.notCalled(syncRequestStub);
2349
+ assert.notCalled(updateLocusInfoStub);
2350
+
2351
+ await clock.tickAsync(1);
2352
+ await testUtils.flushPromises();
2353
+
2354
+ assert.calledOnceWithExactly(syncRequestStub, {url: mockMeeting.locusUrl});
2355
+ assert.calledOnceWithExactly(updateLocusInfoStub, fullLocus);
2356
+ });
2357
+
2358
+ it('does a sync if out-of-order deltas queue becomes too big', async () => {
2359
+ // setup the stubs so that the sync request receives a full DTO with the sequence equal to the out-of-order delta we simulate
2360
+ const fullLocus = {
2361
+ sequence: deltaEvents[6].sequence,
2362
+ };
2363
+ syncRequestStub.resolves({
2364
+ body: fullLocus,
2365
+ });
2366
+
2367
+ // send 5 deltas, starting from deltaEvents[1] so that SDK is blocked waiting for deltaEvents[0]
2368
+ for (let i = 0; i < 5; i++) {
2369
+ locusInfo.handleLocusDelta(deltaEvents[i + 1], mockMeeting);
2370
+ }
2371
+
2372
+ // nothing should happen, SDK should still be waiting for deltaEvents[0]
2373
+ assert.notCalled(syncRequestStub);
2374
+ assert.notCalled(updateLocusInfoStub);
2375
+
2376
+ // now send one more out-of-order delta to trigger a sync request
2377
+ locusInfo.handleLocusDelta(deltaEvents[6], mockMeeting);
2378
+
2379
+ await testUtils.flushPromises();
2380
+
2381
+ // check sync was done
2382
+ assert.calledOnceWithExactly(syncRequestStub, {url: mockMeeting.locusUrl});
2383
+ assert.calledOnceWithExactly(updateLocusInfoStub, fullLocus);
2384
+ });
2385
+
2386
+ it('processes delta events that are not included in sync response', async () => {
2387
+ // this test sends a bunch of out-of-order deltas, this triggers a sync
2388
+ // but the full locus response doesn't include the last 2 deltas received, so
2389
+ // we check that these 2 deltas are also processed after sync response
2390
+ const fullLocusFromSyncResponse = {
2391
+ baseSequence: {
2392
+ rangeStart: 0,
2393
+ rangeEnd: 0,
2394
+ entries: [deltaEvents[0].sequence.entries[0]],
2395
+ },
2396
+ sequence: {
2397
+ rangeStart: 0,
2398
+ rangeEnd: 0,
2399
+ entries: [deltaEvents[5].sequence.entries[0]],
2400
+ },
2401
+ syncUrl: `fake sync url for sequence ${deltaEvents[5].sequence.entries[0]}`,
2402
+ self: {
2403
+ person: {
2404
+ id: 'test person id',
2405
+ },
2406
+ },
2407
+ };
2408
+
2409
+ syncRequestStub.resolves({
2410
+ body: fullLocusFromSyncResponse,
2411
+ });
2412
+
2413
+ // send at least 6 out-of-order deltas to trigger a sync (we're skipping deltaEvents[0])
2414
+ for (let i = 1; i <= 7; i++) {
2415
+ locusInfo.handleLocusDelta(deltaEvents[i], mockMeeting);
2416
+ }
2417
+
2418
+ await testUtils.flushPromises();
2419
+
2420
+ // check that sync was done
2421
+ assert.calledOnceWithExactly(syncRequestStub, {url: mockMeeting.locusUrl});
2422
+
2423
+ // and that remaining deltas from the queue that were not included in full Locus were also processed
2424
+ assert.callCount(updateLocusInfoStub, 3);
2425
+ assert.calledWith(updateLocusInfoStub.getCall(0), fullLocusFromSyncResponse);
2426
+ assert.calledWith(updateLocusInfoStub.getCall(1), deltaEvents[6]);
2427
+ assert.calledWith(updateLocusInfoStub.getCall(2), deltaEvents[7]);
2428
+ });
2429
+ });
1272
2430
  });
1273
2431
  });