@webex/plugin-meetings 2.60.0 → 2.60.1-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (535) hide show
  1. package/README.md +46 -8
  2. package/dist/annotation/annotation.types.d.ts +42 -0
  3. package/dist/annotation/annotation.types.js +7 -0
  4. package/dist/annotation/annotation.types.js.map +1 -0
  5. package/dist/annotation/constants.d.ts +31 -0
  6. package/dist/annotation/constants.js +41 -0
  7. package/dist/annotation/constants.js.map +1 -0
  8. package/dist/annotation/index.d.ts +117 -0
  9. package/dist/annotation/index.js +357 -0
  10. package/dist/annotation/index.js.map +1 -0
  11. package/dist/breakouts/breakout.d.ts +8 -0
  12. package/dist/breakouts/breakout.js +215 -0
  13. package/dist/breakouts/breakout.js.map +1 -0
  14. package/dist/breakouts/collection.d.ts +5 -0
  15. package/dist/breakouts/collection.js +22 -0
  16. package/dist/breakouts/collection.js.map +1 -0
  17. package/dist/breakouts/edit-lock-error.d.ts +15 -0
  18. package/dist/breakouts/edit-lock-error.js +51 -0
  19. package/dist/breakouts/edit-lock-error.js.map +1 -0
  20. package/dist/breakouts/events.d.ts +8 -0
  21. package/dist/breakouts/events.js +44 -0
  22. package/dist/breakouts/events.js.map +1 -0
  23. package/dist/breakouts/index.d.ts +5 -0
  24. package/dist/breakouts/index.js +1047 -0
  25. package/dist/breakouts/index.js.map +1 -0
  26. package/dist/breakouts/request.d.ts +22 -0
  27. package/dist/breakouts/request.js +77 -0
  28. package/dist/breakouts/request.js.map +1 -0
  29. package/dist/breakouts/utils.d.ts +15 -0
  30. package/dist/breakouts/utils.js +64 -0
  31. package/dist/breakouts/utils.js.map +1 -0
  32. package/dist/common/browser-detection.js +2 -3
  33. package/dist/common/browser-detection.js.map +1 -1
  34. package/dist/common/collection.js +3 -4
  35. package/dist/common/collection.js.map +1 -1
  36. package/dist/common/config.js +1 -2
  37. package/dist/common/config.js.map +1 -1
  38. package/dist/common/errors/captcha-error.js +1 -2
  39. package/dist/common/errors/captcha-error.js.map +1 -1
  40. package/dist/common/errors/intent-to-join.js +1 -2
  41. package/dist/common/errors/intent-to-join.js.map +1 -1
  42. package/dist/common/errors/join-meeting.js +1 -2
  43. package/dist/common/errors/join-meeting.js.map +1 -1
  44. package/dist/common/errors/media.js +1 -2
  45. package/dist/common/errors/media.js.map +1 -1
  46. package/dist/common/errors/no-meeting-info.d.ts +14 -0
  47. package/dist/common/errors/no-meeting-info.js +50 -0
  48. package/dist/common/errors/no-meeting-info.js.map +1 -0
  49. package/dist/common/errors/parameter.js +3 -4
  50. package/dist/common/errors/parameter.js.map +1 -1
  51. package/dist/common/errors/password-error.js +1 -2
  52. package/dist/common/errors/password-error.js.map +1 -1
  53. package/dist/common/errors/permission.js +1 -2
  54. package/dist/common/errors/permission.js.map +1 -1
  55. package/dist/common/errors/{reclaim-host-role-error.js → reclaim-host-role-errors.js} +7 -11
  56. package/dist/common/errors/reclaim-host-role-errors.js.map +1 -0
  57. package/dist/common/errors/reconnection-in-progress.js +1 -2
  58. package/dist/common/errors/reconnection-in-progress.js.map +1 -1
  59. package/dist/common/errors/reconnection.js +1 -2
  60. package/dist/common/errors/reconnection.js.map +1 -1
  61. package/dist/common/errors/stats.js +1 -2
  62. package/dist/common/errors/stats.js.map +1 -1
  63. package/dist/common/errors/webex-errors.d.ts +20 -8
  64. package/dist/common/errors/webex-errors.js +48 -28
  65. package/dist/common/errors/webex-errors.js.map +1 -1
  66. package/dist/common/errors/webex-meetings-error.js +1 -2
  67. package/dist/common/errors/webex-meetings-error.js.map +1 -1
  68. package/dist/common/events/events-scope.js +1 -2
  69. package/dist/common/events/events-scope.js.map +1 -1
  70. package/dist/common/events/events.js +1 -2
  71. package/dist/common/events/events.js.map +1 -1
  72. package/dist/common/events/trigger-proxy.js +1 -2
  73. package/dist/common/events/trigger-proxy.js.map +1 -1
  74. package/dist/common/events/util.js +1 -2
  75. package/dist/common/events/util.js.map +1 -1
  76. package/dist/common/logs/logger-config.js +1 -2
  77. package/dist/common/logs/logger-config.js.map +1 -1
  78. package/dist/common/logs/logger-proxy.js +2 -3
  79. package/dist/common/logs/logger-proxy.js.map +1 -1
  80. package/dist/common/logs/request.d.ts +3 -1
  81. package/dist/common/logs/request.js +8 -5
  82. package/dist/common/logs/request.js.map +1 -1
  83. package/dist/common/queue.d.ts +9 -7
  84. package/dist/common/queue.js +22 -9
  85. package/dist/common/queue.js.map +1 -1
  86. package/dist/config.d.ts +6 -7
  87. package/dist/config.js +8 -10
  88. package/dist/config.js.map +1 -1
  89. package/dist/constants.d.ts +217 -97
  90. package/dist/constants.js +416 -441
  91. package/dist/constants.js.map +1 -1
  92. package/dist/controls-options-manager/constants.js +3 -6
  93. package/dist/controls-options-manager/constants.js.map +1 -1
  94. package/dist/controls-options-manager/enums.d.ts +11 -1
  95. package/dist/controls-options-manager/enums.js +15 -6
  96. package/dist/controls-options-manager/enums.js.map +1 -1
  97. package/dist/controls-options-manager/index.d.ts +17 -1
  98. package/dist/controls-options-manager/index.js +127 -38
  99. package/dist/controls-options-manager/index.js.map +1 -1
  100. package/dist/controls-options-manager/types.d.ts +43 -0
  101. package/dist/controls-options-manager/types.js +7 -0
  102. package/dist/controls-options-manager/types.js.map +1 -0
  103. package/dist/controls-options-manager/util.d.ts +1 -7
  104. package/dist/controls-options-manager/util.js +309 -19
  105. package/dist/controls-options-manager/util.js.map +1 -1
  106. package/dist/index.d.ts +6 -3
  107. package/dist/index.js +121 -5
  108. package/dist/index.js.map +1 -1
  109. package/dist/interceptors/index.d.ts +2 -0
  110. package/dist/interceptors/index.js +15 -0
  111. package/dist/interceptors/index.js.map +1 -0
  112. package/dist/interceptors/locusRetry.d.ts +27 -0
  113. package/dist/interceptors/locusRetry.js +94 -0
  114. package/dist/interceptors/locusRetry.js.map +1 -0
  115. package/dist/interpretation/collection.d.ts +5 -0
  116. package/dist/interpretation/collection.js +22 -0
  117. package/dist/interpretation/collection.js.map +1 -0
  118. package/dist/interpretation/index.d.ts +5 -0
  119. package/dist/interpretation/index.js +365 -0
  120. package/dist/interpretation/index.js.map +1 -0
  121. package/dist/interpretation/siLanguage.d.ts +5 -0
  122. package/dist/interpretation/siLanguage.js +24 -0
  123. package/dist/interpretation/siLanguage.js.map +1 -0
  124. package/dist/locus-info/controlsUtils.js +100 -11
  125. package/dist/locus-info/controlsUtils.js.map +1 -1
  126. package/dist/locus-info/embeddedAppsUtils.js +3 -4
  127. package/dist/locus-info/embeddedAppsUtils.js.map +1 -1
  128. package/dist/locus-info/fullState.js +1 -2
  129. package/dist/locus-info/fullState.js.map +1 -1
  130. package/dist/locus-info/hostUtils.js +1 -2
  131. package/dist/locus-info/hostUtils.js.map +1 -1
  132. package/dist/locus-info/index.d.ts +57 -4
  133. package/dist/locus-info/index.js +425 -84
  134. package/dist/locus-info/index.js.map +1 -1
  135. package/dist/locus-info/infoUtils.js +13 -5
  136. package/dist/locus-info/infoUtils.js.map +1 -1
  137. package/dist/locus-info/mediaSharesUtils.js +58 -3
  138. package/dist/locus-info/mediaSharesUtils.js.map +1 -1
  139. package/dist/locus-info/parser.d.ts +66 -6
  140. package/dist/locus-info/parser.js +253 -80
  141. package/dist/locus-info/parser.js.map +1 -1
  142. package/dist/locus-info/selfUtils.js +97 -13
  143. package/dist/locus-info/selfUtils.js.map +1 -1
  144. package/dist/media/index.d.ts +2 -0
  145. package/dist/media/index.js +107 -319
  146. package/dist/media/index.js.map +1 -1
  147. package/dist/media/properties.d.ts +38 -53
  148. package/dist/media/properties.js +96 -153
  149. package/dist/media/properties.js.map +1 -1
  150. package/dist/media/util.js +1 -22
  151. package/dist/media/util.js.map +1 -1
  152. package/dist/mediaQualityMetrics/config.d.ts +234 -230
  153. package/dist/mediaQualityMetrics/config.js +302 -498
  154. package/dist/mediaQualityMetrics/config.js.map +1 -1
  155. package/dist/meeting/in-meeting-actions.d.ts +88 -0
  156. package/dist/meeting/in-meeting-actions.js +94 -3
  157. package/dist/meeting/in-meeting-actions.js.map +1 -1
  158. package/dist/meeting/index.d.ts +591 -494
  159. package/dist/meeting/index.js +4732 -2990
  160. package/dist/meeting/index.js.map +1 -1
  161. package/dist/meeting/locusMediaRequest.d.ts +74 -0
  162. package/dist/meeting/locusMediaRequest.js +291 -0
  163. package/dist/meeting/locusMediaRequest.js.map +1 -0
  164. package/dist/meeting/muteState.d.ts +93 -25
  165. package/dist/meeting/muteState.js +224 -133
  166. package/dist/meeting/muteState.js.map +1 -1
  167. package/dist/meeting/request.d.ts +82 -47
  168. package/dist/meeting/request.js +297 -199
  169. package/dist/meeting/request.js.map +1 -1
  170. package/dist/meeting/request.type.d.ts +11 -0
  171. package/dist/meeting/request.type.js +7 -0
  172. package/dist/meeting/request.type.js.map +1 -0
  173. package/dist/meeting/state.js +1 -2
  174. package/dist/meeting/state.js.map +1 -1
  175. package/dist/meeting/util.d.ts +102 -1
  176. package/dist/meeting/util.js +605 -435
  177. package/dist/meeting/util.js.map +1 -1
  178. package/dist/meeting-info/collection.js +3 -4
  179. package/dist/meeting-info/collection.js.map +1 -1
  180. package/dist/meeting-info/index.d.ts +13 -1
  181. package/dist/meeting-info/index.js +74 -7
  182. package/dist/meeting-info/index.js.map +1 -1
  183. package/dist/meeting-info/meeting-info-v2.d.ts +31 -1
  184. package/dist/meeting-info/meeting-info-v2.js +200 -63
  185. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  186. package/dist/meeting-info/request.js +1 -2
  187. package/dist/meeting-info/request.js.map +1 -1
  188. package/dist/meeting-info/util.js +2 -3
  189. package/dist/meeting-info/util.js.map +1 -1
  190. package/dist/meeting-info/utilv2.js +39 -41
  191. package/dist/meeting-info/utilv2.js.map +1 -1
  192. package/dist/meetings/collection.d.ts +17 -0
  193. package/dist/meetings/collection.js +42 -4
  194. package/dist/meetings/collection.js.map +1 -1
  195. package/dist/meetings/index.d.ts +93 -21
  196. package/dist/meetings/index.js +490 -127
  197. package/dist/meetings/index.js.map +1 -1
  198. package/dist/meetings/meetings.types.d.ts +4 -0
  199. package/dist/meetings/meetings.types.js +7 -0
  200. package/dist/meetings/meetings.types.js.map +1 -0
  201. package/dist/meetings/request.js +4 -3
  202. package/dist/meetings/request.js.map +1 -1
  203. package/dist/meetings/util.js +107 -6
  204. package/dist/meetings/util.js.map +1 -1
  205. package/dist/member/index.d.ts +13 -1
  206. package/dist/member/index.js +45 -2
  207. package/dist/member/index.js.map +1 -1
  208. package/dist/member/member.types.js +3 -4
  209. package/dist/member/member.types.js.map +1 -1
  210. package/dist/member/types.d.ts +32 -0
  211. package/dist/member/types.js +23 -0
  212. package/dist/member/types.js.map +1 -0
  213. package/dist/member/util.js +120 -29
  214. package/dist/member/util.js.map +1 -1
  215. package/dist/members/collection.d.ts +5 -0
  216. package/dist/members/collection.js +11 -2
  217. package/dist/members/collection.js.map +1 -1
  218. package/dist/members/index.d.ts +56 -11
  219. package/dist/members/index.js +174 -47
  220. package/dist/members/index.js.map +1 -1
  221. package/dist/members/request.d.ts +67 -11
  222. package/dist/members/request.js +102 -54
  223. package/dist/members/request.js.map +1 -1
  224. package/dist/members/types.js +3 -4
  225. package/dist/members/types.js.map +1 -1
  226. package/dist/members/util.d.ts +214 -1
  227. package/dist/members/util.js +327 -284
  228. package/dist/members/util.js.map +1 -1
  229. package/dist/metrics/constants.d.ts +15 -6
  230. package/dist/metrics/constants.js +17 -9
  231. package/dist/metrics/constants.js.map +1 -1
  232. package/dist/metrics/index.d.ts +4 -111
  233. package/dist/metrics/index.js +4 -452
  234. package/dist/metrics/index.js.map +1 -1
  235. package/dist/multistream/mediaRequestManager.d.ts +118 -0
  236. package/dist/multistream/mediaRequestManager.js +344 -0
  237. package/dist/multistream/mediaRequestManager.js.map +1 -0
  238. package/dist/multistream/receiveSlot.d.ts +68 -0
  239. package/dist/multistream/receiveSlot.js +200 -0
  240. package/dist/multistream/receiveSlot.js.map +1 -0
  241. package/dist/multistream/receiveSlotManager.d.ts +56 -0
  242. package/dist/multistream/receiveSlotManager.js +174 -0
  243. package/dist/multistream/receiveSlotManager.js.map +1 -0
  244. package/dist/multistream/remoteMedia.d.ts +72 -0
  245. package/dist/multistream/remoteMedia.js +268 -0
  246. package/dist/multistream/remoteMedia.js.map +1 -0
  247. package/dist/multistream/remoteMediaGroup.d.ts +47 -0
  248. package/dist/multistream/remoteMediaGroup.js +267 -0
  249. package/dist/multistream/remoteMediaGroup.js.map +1 -0
  250. package/dist/multistream/remoteMediaManager.d.ts +285 -0
  251. package/dist/multistream/remoteMediaManager.js +1211 -0
  252. package/dist/multistream/remoteMediaManager.js.map +1 -0
  253. package/dist/multistream/sendSlotManager.d.ts +61 -0
  254. package/dist/multistream/sendSlotManager.js +236 -0
  255. package/dist/multistream/sendSlotManager.js.map +1 -0
  256. package/dist/networkQualityMonitor/index.js +5 -4
  257. package/dist/networkQualityMonitor/index.js.map +1 -1
  258. package/dist/personal-meeting-room/index.js +2 -3
  259. package/dist/personal-meeting-room/index.js.map +1 -1
  260. package/dist/personal-meeting-room/request.js +2 -3
  261. package/dist/personal-meeting-room/request.js.map +1 -1
  262. package/dist/personal-meeting-room/util.js +1 -2
  263. package/dist/personal-meeting-room/util.js.map +1 -1
  264. package/dist/reachability/clusterReachability.d.ts +109 -0
  265. package/dist/reachability/clusterReachability.js +357 -0
  266. package/dist/reachability/clusterReachability.js.map +1 -0
  267. package/dist/reachability/index.d.ts +61 -95
  268. package/dist/reachability/index.js +300 -393
  269. package/dist/reachability/index.js.map +1 -1
  270. package/dist/reachability/request.d.ts +7 -3
  271. package/dist/reachability/request.js +18 -10
  272. package/dist/reachability/request.js.map +1 -1
  273. package/dist/reachability/util.d.ts +8 -0
  274. package/dist/reachability/util.js +29 -0
  275. package/dist/reachability/util.js.map +1 -0
  276. package/dist/reactions/constants.d.ts +3 -0
  277. package/dist/reactions/constants.js +12 -0
  278. package/dist/reactions/constants.js.map +1 -0
  279. package/dist/reactions/reactions.d.ts +2 -2
  280. package/dist/reactions/reactions.js +4 -6
  281. package/dist/reactions/reactions.js.map +1 -1
  282. package/dist/reactions/reactions.type.d.ts +23 -3
  283. package/dist/reactions/reactions.type.js +21 -23
  284. package/dist/reactions/reactions.type.js.map +1 -1
  285. package/dist/reconnection-manager/index.d.ts +32 -8
  286. package/dist/reconnection-manager/index.js +282 -231
  287. package/dist/reconnection-manager/index.js.map +1 -1
  288. package/dist/recording-controller/enums.js +4 -5
  289. package/dist/recording-controller/enums.js.map +1 -1
  290. package/dist/recording-controller/index.d.ts +15 -1
  291. package/dist/recording-controller/index.js +57 -46
  292. package/dist/recording-controller/index.js.map +1 -1
  293. package/dist/recording-controller/util.d.ts +5 -4
  294. package/dist/recording-controller/util.js +10 -10
  295. package/dist/recording-controller/util.js.map +1 -1
  296. package/dist/roap/index.d.ts +9 -47
  297. package/dist/roap/index.js +101 -235
  298. package/dist/roap/index.js.map +1 -1
  299. package/dist/roap/request.d.ts +18 -12
  300. package/dist/roap/request.js +126 -180
  301. package/dist/roap/request.js.map +1 -1
  302. package/dist/roap/turnDiscovery.d.ts +27 -16
  303. package/dist/roap/turnDiscovery.js +115 -105
  304. package/dist/roap/turnDiscovery.js.map +1 -1
  305. package/dist/rtcMetrics/constants.d.ts +4 -0
  306. package/dist/rtcMetrics/constants.js +11 -0
  307. package/dist/rtcMetrics/constants.js.map +1 -0
  308. package/dist/rtcMetrics/index.d.ts +54 -0
  309. package/dist/rtcMetrics/index.js +140 -0
  310. package/dist/rtcMetrics/index.js.map +1 -0
  311. package/dist/statsAnalyzer/global.d.ts +1 -83
  312. package/dist/statsAnalyzer/global.js +2 -85
  313. package/dist/statsAnalyzer/global.js.map +1 -1
  314. package/dist/statsAnalyzer/index.d.ts +50 -30
  315. package/dist/statsAnalyzer/index.js +435 -510
  316. package/dist/statsAnalyzer/index.js.map +1 -1
  317. package/dist/statsAnalyzer/mqaUtil.d.ts +8 -6
  318. package/dist/statsAnalyzer/mqaUtil.js +120 -83
  319. package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
  320. package/dist/transcription/index.js +1 -2
  321. package/dist/transcription/index.js.map +1 -1
  322. package/dist/webinar/collection.d.ts +16 -0
  323. package/dist/webinar/collection.js +43 -0
  324. package/dist/webinar/collection.js.map +1 -0
  325. package/dist/webinar/index.d.ts +5 -0
  326. package/dist/webinar/index.js +68 -0
  327. package/dist/webinar/index.js.map +1 -0
  328. package/package.json +38 -26
  329. package/src/annotation/annotation.types.ts +50 -0
  330. package/src/annotation/constants.ts +36 -0
  331. package/src/annotation/index.ts +328 -0
  332. package/src/breakouts/README.md +220 -0
  333. package/src/breakouts/breakout.ts +188 -0
  334. package/src/breakouts/collection.ts +19 -0
  335. package/src/breakouts/edit-lock-error.ts +25 -0
  336. package/src/breakouts/events.ts +56 -0
  337. package/src/breakouts/index.ts +925 -0
  338. package/src/breakouts/request.ts +55 -0
  339. package/src/breakouts/utils.ts +57 -0
  340. package/src/common/errors/no-meeting-info.ts +24 -0
  341. package/src/common/errors/webex-errors.ts +36 -12
  342. package/src/common/logs/logger-proxy.ts +1 -1
  343. package/src/common/logs/request.ts +5 -1
  344. package/src/common/queue.ts +22 -8
  345. package/src/config.ts +6 -7
  346. package/src/constants.ts +244 -97
  347. package/src/controls-options-manager/enums.ts +12 -0
  348. package/src/controls-options-manager/index.ts +116 -21
  349. package/src/controls-options-manager/types.ts +59 -0
  350. package/src/controls-options-manager/util.ts +294 -14
  351. package/src/index.ts +44 -0
  352. package/src/interceptors/index.ts +3 -0
  353. package/src/interceptors/locusRetry.ts +67 -0
  354. package/src/interpretation/README.md +60 -0
  355. package/src/interpretation/collection.ts +19 -0
  356. package/src/interpretation/index.ts +332 -0
  357. package/src/interpretation/siLanguage.ts +18 -0
  358. package/src/locus-info/controlsUtils.ts +110 -0
  359. package/src/locus-info/index.ts +449 -61
  360. package/src/locus-info/infoUtils.ts +14 -2
  361. package/src/locus-info/mediaSharesUtils.ts +64 -0
  362. package/src/locus-info/parser.ts +258 -47
  363. package/src/locus-info/selfUtils.ts +85 -2
  364. package/src/media/index.ts +153 -370
  365. package/src/media/properties.ts +106 -136
  366. package/src/media/util.ts +0 -21
  367. package/src/mediaQualityMetrics/config.ts +244 -377
  368. package/src/meeting/in-meeting-actions.ts +176 -0
  369. package/src/meeting/index.ts +3944 -2489
  370. package/src/meeting/locusMediaRequest.ts +313 -0
  371. package/src/meeting/muteState.ts +224 -138
  372. package/src/meeting/request.ts +207 -127
  373. package/src/meeting/request.type.ts +13 -0
  374. package/src/meeting/util.ts +590 -423
  375. package/src/meeting-info/index.ts +81 -8
  376. package/src/meeting-info/meeting-info-v2.ts +163 -13
  377. package/src/meeting-info/util.ts +1 -1
  378. package/src/meeting-info/utilv2.ts +28 -28
  379. package/src/meetings/collection.ts +33 -0
  380. package/src/meetings/index.ts +487 -126
  381. package/src/meetings/meetings.types.ts +12 -0
  382. package/src/meetings/request.ts +2 -0
  383. package/src/meetings/util.ts +116 -5
  384. package/src/member/index.ts +43 -1
  385. package/src/member/types.ts +38 -0
  386. package/src/member/util.ts +125 -28
  387. package/src/members/collection.ts +8 -0
  388. package/src/members/index.ts +187 -52
  389. package/src/members/request.ts +87 -27
  390. package/src/members/util.ts +332 -291
  391. package/src/metrics/constants.ts +15 -6
  392. package/src/metrics/index.ts +1 -471
  393. package/src/multistream/mediaRequestManager.ts +440 -0
  394. package/src/multistream/receiveSlot.ts +184 -0
  395. package/src/multistream/receiveSlotManager.ts +166 -0
  396. package/src/multistream/remoteMedia.ts +254 -0
  397. package/src/multistream/remoteMediaGroup.ts +284 -0
  398. package/src/multistream/remoteMediaManager.ts +1145 -0
  399. package/src/multistream/sendSlotManager.ts +170 -0
  400. package/src/networkQualityMonitor/index.ts +6 -6
  401. package/src/reachability/clusterReachability.ts +320 -0
  402. package/src/reachability/index.ts +243 -347
  403. package/src/reachability/request.ts +17 -8
  404. package/src/reachability/util.ts +24 -0
  405. package/src/reactions/constants.ts +4 -0
  406. package/src/reactions/reactions.ts +4 -4
  407. package/src/reactions/reactions.type.ts +30 -4
  408. package/src/reconnection-manager/index.ts +168 -156
  409. package/src/recording-controller/index.ts +20 -3
  410. package/src/recording-controller/util.ts +26 -9
  411. package/src/roap/index.ts +98 -241
  412. package/src/roap/request.ts +74 -148
  413. package/src/roap/turnDiscovery.ts +62 -56
  414. package/src/rtcMetrics/constants.ts +3 -0
  415. package/src/rtcMetrics/index.ts +124 -0
  416. package/src/statsAnalyzer/global.ts +1 -84
  417. package/src/statsAnalyzer/index.ts +477 -643
  418. package/src/statsAnalyzer/mqaUtil.ts +115 -114
  419. package/src/webinar/collection.ts +31 -0
  420. package/src/webinar/index.ts +62 -0
  421. package/test/integration/spec/converged-space-meetings.js +233 -0
  422. package/test/integration/spec/journey.js +320 -264
  423. package/test/integration/spec/space-meeting.js +77 -4
  424. package/test/unit/spec/annotation/index.ts +418 -0
  425. package/test/unit/spec/breakouts/breakout.ts +237 -0
  426. package/test/unit/spec/breakouts/collection.ts +15 -0
  427. package/test/unit/spec/breakouts/edit-lock-error.ts +30 -0
  428. package/test/unit/spec/breakouts/events.ts +89 -0
  429. package/test/unit/spec/breakouts/index.ts +1790 -0
  430. package/test/unit/spec/breakouts/request.ts +104 -0
  431. package/test/unit/spec/breakouts/utils.js +72 -0
  432. package/test/unit/spec/common/queue.js +31 -2
  433. package/test/unit/spec/controls-options-manager/index.js +163 -0
  434. package/test/unit/spec/controls-options-manager/util.js +576 -60
  435. package/test/unit/spec/fixture/locus.js +1 -0
  436. package/test/unit/spec/interceptors/locusRetry.ts +131 -0
  437. package/test/unit/spec/interpretation/collection.ts +15 -0
  438. package/test/unit/spec/interpretation/index.ts +589 -0
  439. package/test/unit/spec/interpretation/siLanguage.ts +28 -0
  440. package/test/unit/spec/locus-info/controlsUtils.js +323 -30
  441. package/test/unit/spec/locus-info/index.js +1390 -16
  442. package/test/unit/spec/locus-info/infoUtils.js +54 -16
  443. package/test/unit/spec/locus-info/lib/SeqCmp.json +16 -0
  444. package/test/unit/spec/locus-info/lib/selfConstant.js +48 -0
  445. package/test/unit/spec/locus-info/mediaSharesUtils.ts +32 -0
  446. package/test/unit/spec/locus-info/parser.js +116 -35
  447. package/test/unit/spec/locus-info/selfUtils.js +275 -0
  448. package/test/unit/spec/media/index.ts +290 -0
  449. package/test/unit/spec/media/properties.ts +75 -84
  450. package/test/unit/spec/meeting/in-meeting-actions.ts +86 -0
  451. package/test/unit/spec/meeting/index.js +8187 -2769
  452. package/test/unit/spec/meeting/locusMediaRequest.ts +442 -0
  453. package/test/unit/spec/meeting/muteState.js +409 -213
  454. package/test/unit/spec/meeting/request.js +512 -42
  455. package/test/unit/spec/meeting/utils.js +741 -24
  456. package/test/unit/spec/meeting-info/index.js +300 -0
  457. package/test/unit/spec/meeting-info/meetinginfov2.js +527 -5
  458. package/test/unit/spec/meeting-info/utilv2.js +21 -0
  459. package/test/unit/spec/meetings/collection.js +26 -0
  460. package/test/unit/spec/meetings/index.js +1313 -243
  461. package/test/unit/spec/meetings/utils.js +202 -2
  462. package/test/unit/spec/member/index.js +32 -9
  463. package/test/unit/spec/member/util.js +499 -61
  464. package/test/unit/spec/members/index.js +394 -5
  465. package/test/unit/spec/members/request.js +206 -27
  466. package/test/unit/spec/members/utils.js +173 -38
  467. package/test/unit/spec/metrics/index.js +1 -50
  468. package/test/unit/spec/multistream/mediaRequestManager.ts +1418 -0
  469. package/test/unit/spec/multistream/receiveSlot.ts +163 -0
  470. package/test/unit/spec/multistream/receiveSlotManager.ts +203 -0
  471. package/test/unit/spec/multistream/remoteMedia.ts +255 -0
  472. package/test/unit/spec/multistream/remoteMediaGroup.ts +662 -0
  473. package/test/unit/spec/multistream/remoteMediaManager.ts +1924 -0
  474. package/test/unit/spec/multistream/sendSlotManager.ts +242 -0
  475. package/test/unit/spec/networkQualityMonitor/index.js +4 -4
  476. package/test/unit/spec/reachability/clusterReachability.ts +279 -0
  477. package/test/unit/spec/reachability/index.ts +531 -24
  478. package/test/unit/spec/reachability/request.js +68 -0
  479. package/test/unit/spec/reachability/util.ts +40 -0
  480. package/test/unit/spec/reconnection-manager/index.js +162 -24
  481. package/test/unit/spec/recording-controller/index.js +293 -218
  482. package/test/unit/spec/recording-controller/util.js +223 -96
  483. package/test/unit/spec/roap/index.ts +200 -76
  484. package/test/unit/spec/roap/request.ts +255 -0
  485. package/test/unit/spec/roap/turnDiscovery.ts +86 -48
  486. package/test/unit/spec/rtcMetrics/index.ts +93 -0
  487. package/test/unit/spec/stats-analyzer/index.js +261 -167
  488. package/test/unit/spec/webinar/collection.ts +13 -0
  489. package/test/unit/spec/webinar/index.ts +60 -0
  490. package/test/utils/constants.js +9 -0
  491. package/test/utils/integrationTestUtils.js +46 -0
  492. package/test/utils/testUtils.js +0 -45
  493. package/test/utils/webex-config.js +4 -0
  494. package/test/utils/webex-test-users.js +7 -3
  495. package/dist/common/errors/reclaim-host-role-error.js.map +0 -1
  496. package/dist/meeting/effectsState.d.ts +0 -42
  497. package/dist/meeting/effectsState.js +0 -260
  498. package/dist/meeting/effectsState.js.map +0 -1
  499. package/dist/metrics/config.d.ts +0 -169
  500. package/dist/metrics/config.js +0 -289
  501. package/dist/metrics/config.js.map +0 -1
  502. package/dist/peer-connection-manager/index.d.ts +0 -6
  503. package/dist/peer-connection-manager/index.js +0 -671
  504. package/dist/peer-connection-manager/index.js.map +0 -1
  505. package/dist/peer-connection-manager/util.d.ts +0 -6
  506. package/dist/peer-connection-manager/util.js +0 -110
  507. package/dist/peer-connection-manager/util.js.map +0 -1
  508. package/dist/roap/collection.d.ts +0 -10
  509. package/dist/roap/collection.js +0 -63
  510. package/dist/roap/collection.js.map +0 -1
  511. package/dist/roap/handler.d.ts +0 -47
  512. package/dist/roap/handler.js +0 -279
  513. package/dist/roap/handler.js.map +0 -1
  514. package/dist/roap/state.d.ts +0 -9
  515. package/dist/roap/state.js +0 -127
  516. package/dist/roap/state.js.map +0 -1
  517. package/dist/roap/util.d.ts +0 -2
  518. package/dist/roap/util.js +0 -76
  519. package/dist/roap/util.js.map +0 -1
  520. package/src/index.js +0 -15
  521. package/src/meeting/effectsState.ts +0 -209
  522. package/src/metrics/config.ts +0 -485
  523. package/src/peer-connection-manager/index.ts +0 -847
  524. package/src/peer-connection-manager/util.ts +0 -119
  525. package/src/roap/collection.ts +0 -62
  526. package/src/roap/handler.ts +0 -294
  527. package/src/roap/state.ts +0 -156
  528. package/src/roap/util.ts +0 -100
  529. package/test/unit/spec/meeting/effectsState.js +0 -281
  530. package/test/unit/spec/peerconnection-manager/index.js +0 -218
  531. package/test/unit/spec/peerconnection-manager/utils.js +0 -49
  532. package/test/unit/spec/peerconnection-manager/utils.test-fixtures.ts +0 -388
  533. package/test/unit/spec/roap/util.js +0 -30
  534. /package/dist/common/errors/{reclaim-host-role-error.d.ts → reclaim-host-role-errors.d.ts} +0 -0
  535. /package/src/common/errors/{reclaim-host-role-error.ts → reclaim-host-role-errors.ts} +0 -0
@@ -3,6 +3,10 @@
3
3
  */
4
4
  import 'jsdom-global/register';
5
5
 
6
+ // Polyfill for crypto: https://github.com/jsdom/jsdom/issues/1612#issuecomment-663210638
7
+ import {Crypto} from '@peculiar/webcrypto';
8
+ global.crypto = new Crypto();
9
+
6
10
  import Device from '@webex/internal-plugin-device';
7
11
  import Mercury from '@webex/internal-plugin-mercury';
8
12
  import {assert} from '@webex/test-helper-chai';
@@ -11,8 +15,8 @@ import sinon from 'sinon';
11
15
  import uuid from 'uuid';
12
16
  import StaticConfig from '@webex/plugin-meetings/src/common/config';
13
17
  import TriggerProxy from '@webex/plugin-meetings/src/common/events/trigger-proxy';
18
+ import LoggerProxy from '@webex/plugin-meetings/src/common/logs/logger-proxy';
14
19
  import LoggerConfig from '@webex/plugin-meetings/src/common/logs/logger-config';
15
- import MediaUtil from '@webex/plugin-meetings/src/media/util';
16
20
  import Meeting from '@webex/plugin-meetings/src/meeting';
17
21
  import MeetingUtil from '@webex/plugin-meetings/src/meeting/util';
18
22
  import Meetings from '@webex/plugin-meetings/src/meetings';
@@ -20,6 +24,7 @@ import MeetingCollection from '@webex/plugin-meetings/src/meetings/collection';
20
24
  import MeetingsUtil from '@webex/plugin-meetings/src/meetings/util';
21
25
  import PersonalMeetingRoom from '@webex/plugin-meetings/src/personal-meeting-room';
22
26
  import Reachability from '@webex/plugin-meetings/src/reachability';
27
+ import Metrics from '@webex/plugin-meetings/src/metrics';
23
28
 
24
29
  import testUtils from '../../../utils/testUtils';
25
30
  import {
@@ -30,6 +35,12 @@ import {
30
35
  LOCUSINFO,
31
36
  EVENT_TRIGGERS,
32
37
  } from '../../../../src/constants';
38
+ import CaptchaError from '@webex/plugin-meetings/src/common/errors/captcha-error';
39
+ import {forEach} from 'lodash';
40
+ import PasswordError from '@webex/plugin-meetings/src/common/errors/password-error';
41
+ import PermissionError from '@webex/plugin-meetings/src/common/errors/permission';
42
+ import {NoiseReductionEffect, VirtualBackgroundEffect} from '@webex/media-helpers';
43
+ import NoMeetingInfoError from '../../../../src/common/errors/no-meeting-info';
33
44
 
34
45
  describe('plugin-meetings', () => {
35
46
  const logger = {
@@ -41,6 +52,8 @@ describe('plugin-meetings', () => {
41
52
  debug: () => {},
42
53
  };
43
54
 
55
+ let triggerProxyStub;
56
+
44
57
  beforeEach(() => {
45
58
  StaticConfig.set({
46
59
  bandwidth: {
@@ -52,7 +65,7 @@ describe('plugin-meetings', () => {
52
65
  verboseEvents: true,
53
66
  enable: false,
54
67
  });
55
- TriggerProxy.trigger = sinon.stub().returns(true);
68
+ triggerProxyStub = sinon.stub(TriggerProxy, 'trigger').returns(true);
56
69
  });
57
70
 
58
71
  let webex;
@@ -61,15 +74,20 @@ describe('plugin-meetings', () => {
61
74
  let url1;
62
75
  let test1;
63
76
  let test2;
77
+ let locusInfo;
64
78
 
65
79
  describe('meetings index', () => {
66
80
  beforeEach(() => {
67
81
  MeetingsUtil.checkH264Support = sinon.stub();
68
- uuid1 = uuid.v4();
82
+ uuid1 = uuid.v4();
69
83
  url1 = `https://example.com/${uuid.v4()}`;
70
84
  uri1 = `test-${uuid.v4()}@example.com`;
71
85
  test1 = `test-${uuid.v4()}`;
72
86
  test2 = `test2-${uuid.v4()}`;
87
+ locusInfo = {
88
+ parse: sinon.stub().returns(true),
89
+ updateMainSessionLocusCache: sinon.stub(),
90
+ };
73
91
  webex = new MockWebex({
74
92
  children: {
75
93
  device: Device,
@@ -108,6 +126,7 @@ describe('plugin-meetings', () => {
108
126
  });
109
127
 
110
128
  Object.assign(webex.internal, {
129
+ llm: {on: sinon.stub()},
111
130
  device: {
112
131
  deviceType: 'FAKE_DEVICE',
113
132
  register: sinon.stub().returns(Promise.resolve()),
@@ -151,6 +170,10 @@ describe('plugin-meetings', () => {
151
170
  webex.emit('ready');
152
171
  });
153
172
 
173
+ afterEach(() => {
174
+ sinon.restore();
175
+ });
176
+
154
177
  it('has a webex instance with a meetings property', () => {
155
178
  assert.exists(webex, 'webex was initialized with children');
156
179
  assert.exists(webex.meetings, 'meetings child was set up on the webex instance');
@@ -217,34 +240,6 @@ describe('plugin-meetings', () => {
217
240
  });
218
241
  });
219
242
 
220
- describe('#_toggleTurnDiscovery', () => {
221
- it('should have toggleAdhocMeetings', () => {
222
- assert.equal(typeof webex.meetings._toggleTurnDiscovery, 'function');
223
- });
224
-
225
- describe('success', () => {
226
- it('should update meetings to do TURN discovery', () => {
227
- webex.meetings._toggleTurnDiscovery(true);
228
- assert.equal(webex.meetings.config.experimental.enableTurnDiscovery, true);
229
-
230
- webex.meetings._toggleTurnDiscovery(false);
231
- assert.equal(webex.meetings.config.experimental.enableTurnDiscovery, false);
232
- });
233
- });
234
-
235
- describe('failure', () => {
236
- it('should not accept non boolean input', () => {
237
- const currentEnableTurnDiscovery = webex.meetings.config.experimental.enableTurnDiscovery;
238
-
239
- webex.meetings._toggleTurnDiscovery('test');
240
- assert.equal(
241
- webex.meetings.config.experimental.enableAdhocMeetings,
242
- currentEnableTurnDiscovery
243
- );
244
- });
245
- });
246
- });
247
-
248
243
  describe('Public API Contracts', () => {
249
244
  describe('#register', () => {
250
245
  it('emits an event and resolves when register succeeds', async () => {
@@ -338,37 +333,110 @@ describe('plugin-meetings', () => {
338
333
  });
339
334
  });
340
335
 
336
+ describe('virtual background effect', () => {
337
+ beforeEach(() => {
338
+ webex.credentials = {
339
+ supertoken: {
340
+ access_token: 'fake_token',
341
+ },
342
+ };
343
+ });
344
+
345
+ it('creates background effect', async () => {
346
+ const result = await webex.meetings.createVirtualBackgroundEffect();
347
+
348
+ assert.exists(result);
349
+ assert.instanceOf(result, VirtualBackgroundEffect);
350
+ assert.containsAllKeys(result, ['loadModel', 'isEnabled', 'options']);
351
+ assert.deepEqual(result.options, {
352
+ mode: 'BLUR',
353
+ blurStrength: 'STRONG',
354
+ generator: 'worker',
355
+ quality: 'LOW',
356
+ authToken: 'fake_token',
357
+ mirror: false,
358
+ });
359
+ assert.exists(result.enable);
360
+ assert.exists(result.disable);
361
+ assert.exists(result.dispose);
362
+ });
363
+
364
+ it('creates background effect with custom options passed', async () => {
365
+ const effectOptions = {
366
+ generator: 'local',
367
+ frameRate: 45,
368
+ mode: 'IMAGE',
369
+ mirror: false,
370
+ quality: 'HIGH',
371
+ blurStrength: 'STRONG',
372
+ bgImageUrl: 'https://test.webex.com/landscape.5a535788.jpg',
373
+ };
374
+
375
+ const result = await webex.meetings.createVirtualBackgroundEffect(effectOptions);
376
+
377
+ assert.exists(result);
378
+ assert.instanceOf(result, VirtualBackgroundEffect);
379
+ assert.containsAllKeys(result, ['loadModel', 'isEnabled', 'options']);
380
+ assert.deepEqual(result.options, {...effectOptions, authToken: 'fake_token'});
381
+ assert.exists(result.enable);
382
+ assert.exists(result.disable);
383
+ assert.exists(result.dispose);
384
+ });
385
+ });
386
+
387
+ describe('noise reduction effect', () => {
388
+ beforeEach(() => {
389
+ webex.credentials = {
390
+ supertoken: {
391
+ access_token: 'fake_token',
392
+ },
393
+ };
394
+ });
395
+
396
+ it('creates noise reduction effect', async () => {
397
+ const result = await webex.meetings.createNoiseReductionEffect({audioContext: {}});
398
+
399
+ assert.exists(result);
400
+ assert.instanceOf(result, NoiseReductionEffect);
401
+ assert.containsAllKeys(result, ['audioContext', 'isEnabled', 'isReady', 'options']);
402
+ assert.deepEqual(result.options, {
403
+ authToken: 'fake_token',
404
+ audioContext: {},
405
+ });
406
+ assert.exists(result.enable);
407
+ assert.exists(result.disable);
408
+ assert.exists(result.dispose);
409
+ });
410
+
411
+ it('creates noise reduction effect with custom options passed', async () => {
412
+ const effectOptions = {
413
+ audioContext: {},
414
+ mode: 'WORKLET',
415
+ env: 'prod',
416
+ };
417
+
418
+ const result = await webex.meetings.createNoiseReductionEffect(effectOptions);
419
+
420
+ assert.exists(result);
421
+ assert.instanceOf(result, NoiseReductionEffect);
422
+ assert.containsAllKeys(result, ['audioContext', 'isEnabled', 'isReady', 'options']);
423
+ assert.deepEqual(result.options, {...effectOptions, authToken: 'fake_token'});
424
+ assert.exists(result.enable);
425
+ assert.exists(result.disable);
426
+ assert.exists(result.dispose);
427
+ });
428
+ });
429
+
341
430
  describe('gets', () => {
342
431
  describe('#getReachability', () => {
343
432
  it('should have #getReachability', () => {
344
433
  assert.exists(webex.meetings.getReachability);
345
434
  });
346
- describe('before #setReachability', () => {
347
- it('does not get a reachability instance', () => {
348
- const reachability = webex.meetings.getReachability();
349
-
350
- assert.notExists(
351
- reachability,
352
- 'reachability is undefined because #setReachability has not been called'
353
- );
354
- });
355
- });
356
- describe('after #setReachability', () => {
357
- beforeEach(() => {
358
- webex.meetings.setReachability();
359
- const reachabilityMocker = webex.meetings.getReachability();
360
-
361
- sinon.stub(reachabilityMocker, 'gatherReachability').returns(true);
362
- });
363
- it('gets the reachability data instance from webex.meetings', () => {
364
- const reachability = webex.meetings.getReachability();
435
+ it('gets the reachability data instance from webex.meetings', () => {
436
+ const reachability = webex.meetings.getReachability();
365
437
 
366
- assert.exists(
367
- reachability,
368
- 'reachability is defined because #setReachability has been called'
369
- );
370
- assert.instanceOf(reachability, Reachability, 'should be a reachability instance');
371
- });
438
+ assert.exists(reachability, 'reachability is defined');
439
+ assert.instanceOf(reachability, Reachability, 'should be a reachability instance');
372
440
  });
373
441
  });
374
442
  describe('#getPersonalMeetingRoom', () => {
@@ -443,21 +511,16 @@ describe('plugin-meetings', () => {
443
511
  );
444
512
  });
445
513
  describe('when meeting is returned', () => {
446
- let parse;
447
-
448
514
  beforeEach(() => {
449
- parse = sinon.stub().returns(true);
450
515
  webex.meetings.meetingCollection.getByKey = sinon.stub().returns({
451
- locusInfo: {
452
- parse,
453
- },
516
+ locusInfo,
454
517
  });
455
518
  });
456
519
  it('tests the sync meeting calls for existing meeting', async () => {
457
520
  await webex.meetings.syncMeetings();
458
521
  assert.calledOnce(webex.meetings.request.getActiveMeetings);
459
522
  assert.calledOnce(webex.meetings.meetingCollection.getByKey);
460
- assert.calledOnce(parse);
523
+ assert.calledOnce(locusInfo.parse);
461
524
  assert.calledWith(webex.meetings.meetingCollection.getByKey, 'locusUrl', url1);
462
525
  });
463
526
  });
@@ -470,6 +533,7 @@ describe('plugin-meetings', () => {
470
533
  webex.meetings.create = sinon.stub().returns(
471
534
  Promise.resolve({
472
535
  locusInfo: {
536
+ ...locusInfo,
473
537
  initialSetup,
474
538
  },
475
539
  })
@@ -478,7 +542,7 @@ describe('plugin-meetings', () => {
478
542
  it('tests the sync meeting calls for not existing meeting', async () => {
479
543
  await webex.meetings.syncMeetings();
480
544
  assert.calledOnce(webex.meetings.request.getActiveMeetings);
481
- assert.callCount(webex.meetings.meetingCollection.getByKey, 4);
545
+ assert.callCount(webex.meetings.meetingCollection.getByKey, 5);
482
546
  assert.calledOnce(initialSetup);
483
547
  assert.calledOnce(webex.meetings.create);
484
548
  assert.calledWith(webex.meetings.request.getActiveMeetings);
@@ -495,46 +559,46 @@ describe('plugin-meetings', () => {
495
559
  });
496
560
  });
497
561
  });
498
- describe('destory non active meeting', () => {
499
- let initialSetup;
500
- let parse;
562
+ describe('destroy non active locus meetings', () => {
501
563
  let destroySpy;
502
564
 
565
+ const meetingCollectionMeetings = {
566
+ stillValidLocusMeeting: {
567
+ locusUrl: 'still-valid-locus-url',
568
+ sendCallAnalyzerMetrics: sinon.stub(),
569
+ },
570
+ noLongerValidLocusMeeting: {
571
+ locusUrl: 'no-longer-valid-locus-url',
572
+ sendCallAnalyzerMetrics: sinon.stub(),
573
+ },
574
+ otherNonLocusMeeting1: {
575
+ locusUrl: null,
576
+ sendCallAnalyzerMetrics: sinon.stub(),
577
+ },
578
+ otherNonLocusMeeting2: {
579
+ locusUrl: undefined,
580
+ sendCallAnalyzerMetrics: sinon.stub(),
581
+ },
582
+ };
583
+
503
584
  beforeEach(() => {
504
585
  destroySpy = sinon.spy(webex.meetings, 'destroy');
505
- parse = sinon.stub().returns(true);
506
- initialSetup = sinon.stub().returns(true);
507
- webex.meetings.meetingCollection.getByKey = sinon.stub().returns({
508
- locusInfo: {
509
- parse,
510
- },
511
- sendCallAnalyzerMetrics: sinon.stub(),
512
- });
513
- webex.meetings.meetingCollection.getAll = sinon.stub().returns({
514
- meetingutk: {
515
- locusUrl: 'fdfdjfdhj',
516
- sendCallAnalyzerMetrics: sinon.stub(),
517
- },
518
- });
519
- webex.meetings.create = sinon.stub().returns(
520
- Promise.resolve({
521
- locusInfo: {
522
- initialSetup,
523
- },
524
- sendCallAnalyzerMetrics: sinon.stub(),
525
- })
526
- );
586
+ webex.meetings.meetingCollection.getAll = sinon.stub().returns(meetingCollectionMeetings);
527
587
  webex.meetings.request.getActiveMeetings = sinon.stub().returns(
528
588
  Promise.resolve({
529
- loci: [],
589
+ loci: [
590
+ {url: 'still-valid-locus-url'}
591
+ ],
530
592
  })
531
593
  );
532
594
  MeetingUtil.cleanUp = sinon.stub().returns(Promise.resolve());
533
595
  });
534
- it('destroy non active meetings', async () => {
596
+ it('destroy only non active locus meetings and keep active locus meetings and any other non-locus meeting', async () => {
535
597
  await webex.meetings.syncMeetings();
536
598
  assert.calledOnce(webex.meetings.request.getActiveMeetings);
537
- assert.calledOnce(destroySpy);
599
+ assert.calledOnce(webex.meetings.meetingCollection.getAll);
600
+ assert.calledWith(destroySpy, meetingCollectionMeetings.noLongerValidLocusMeeting);
601
+ assert.callCount(destroySpy, 1);
538
602
 
539
603
  assert.calledOnce(MeetingUtil.cleanUp);
540
604
  });
@@ -574,14 +638,104 @@ describe('plugin-meetings', () => {
574
638
  });
575
639
  });
576
640
 
641
+ const FAKE_USE_RANDOM_DELAY = true;
642
+ const correlationId = 'my-correlationId';
643
+ const callStateForMetrics = {
644
+ correlationId: 'my-correlationId2',
645
+ joinTrigger: 'my-join-trigger',
646
+ loginType: 'my-login-type',
647
+ };
648
+
649
+ it('should call setCallStateForMetrics on any pre-existing meeting', async () => {
650
+ const fakeMeeting = {setCallStateForMetrics: sinon.mock()};
651
+ webex.meetings.meetingCollection.getByKey = sinon.stub().returns(fakeMeeting);
652
+ await webex.meetings.create(
653
+ test1,
654
+ test2,
655
+ FAKE_USE_RANDOM_DELAY,
656
+ {},
657
+ correlationId,
658
+ true,
659
+ callStateForMetrics
660
+ );
661
+ assert.calledOnceWithExactly(fakeMeeting.setCallStateForMetrics, {
662
+ ...callStateForMetrics,
663
+ correlationId,
664
+ });
665
+ });
666
+
667
+ const checkCallCreateMeeting = async (createParameters, createMeetingParameters) => {
668
+ const create = webex.meetings.create(...createParameters);
669
+
670
+ assert.exists(create.then);
671
+ await create;
672
+ assert.calledOnce(webex.meetings.createMeeting);
673
+ assert.calledWith(webex.meetings.createMeeting, ...createMeetingParameters);
674
+ };
675
+
577
676
  it('calls createMeeting and returns its promise', async () => {
578
- const FAKE_USE_RANDOM_DELAY = true;
579
- const create = webex.meetings.create(test1, test2, FAKE_USE_RANDOM_DELAY);
677
+ await checkCallCreateMeeting(
678
+ [test1, test2, FAKE_USE_RANDOM_DELAY, {}, correlationId, true],
679
+ [test1, test2, FAKE_USE_RANDOM_DELAY, {}, {correlationId}, true]
680
+ );
681
+ });
682
+
683
+ it('calls createMeeting when failOnMissingMeetinginfo is undefined and returns its promise', async () => {
684
+ await checkCallCreateMeeting(
685
+ [test1, test2, FAKE_USE_RANDOM_DELAY, {}, correlationId, undefined],
686
+ [test1, test2, FAKE_USE_RANDOM_DELAY, {}, {correlationId}, false]
687
+ );
688
+ });
689
+
690
+ it('calls createMeeting when failOnMissingMeetinginfo is false and returns its promise', async () => {
691
+ await checkCallCreateMeeting(
692
+ [test1, test2, FAKE_USE_RANDOM_DELAY, {}, correlationId, false],
693
+ [test1, test2, FAKE_USE_RANDOM_DELAY, {}, {correlationId}, false]
694
+ );
695
+ });
696
+
697
+ it('calls createMeeting with callStateForMetrics and returns its promise', async () => {
698
+ await checkCallCreateMeeting(
699
+ [test1, test2, FAKE_USE_RANDOM_DELAY, {}, undefined, true, callStateForMetrics],
700
+ [test1, test2, FAKE_USE_RANDOM_DELAY, {}, callStateForMetrics, true]
701
+ );
702
+ });
703
+
704
+ it('calls createMeeting with callStateForMetrics overwritten with correlationId and returns its promise', async () => {
705
+ await checkCallCreateMeeting(
706
+ [test1, test2, FAKE_USE_RANDOM_DELAY, {}, correlationId, true, callStateForMetrics],
707
+ [test1, test2, FAKE_USE_RANDOM_DELAY, {}, {...callStateForMetrics, correlationId}, true]
708
+ );
709
+ });
710
+
711
+ it('calls createMeeting with extra info params and returns its promise', async () => {
712
+ const FAKE_USE_RANDOM_DELAY = false;
713
+ const correlationId = 'my-correlationId';
714
+
715
+ const FAKE_INFO_EXTRA_PARAMS = {
716
+ mtid: 'm9fe0afd8c435e892afcce9ea25b97046',
717
+ joinTXId: 'TSmrX61wNF',
718
+ };
719
+ const create = webex.meetings.create(
720
+ test1,
721
+ test2,
722
+ FAKE_USE_RANDOM_DELAY,
723
+ FAKE_INFO_EXTRA_PARAMS,
724
+ correlationId
725
+ );
580
726
 
581
727
  assert.exists(create.then);
582
728
  await create;
583
729
  assert.calledOnce(webex.meetings.createMeeting);
584
- assert.calledWith(webex.meetings.createMeeting, test1, test2, FAKE_USE_RANDOM_DELAY);
730
+ assert.calledWith(
731
+ webex.meetings.createMeeting,
732
+ test1,
733
+ test2,
734
+ FAKE_USE_RANDOM_DELAY,
735
+ FAKE_INFO_EXTRA_PARAMS,
736
+ {correlationId},
737
+ false
738
+ );
585
739
  });
586
740
 
587
741
  it('creates a new meeting when a scheduled meeting exists in the conversation', async () => {
@@ -677,45 +831,51 @@ describe('plugin-meetings', () => {
677
831
  });
678
832
  describe('#handleLocusEvent', () => {
679
833
  describe('there was a meeting', () => {
680
- let parse;
681
-
682
834
  beforeEach(() => {
683
- parse = sinon.stub().returns(true);
684
835
  webex.meetings.meetingCollection.getByKey = sinon.stub().returns({
685
- locusInfo: {
686
- parse,
687
- },
836
+ locusInfo,
688
837
  });
689
838
  });
690
- it('should parse the meeting info', () => {
839
+ it('should parse the meeting info and update main session locus cache', () => {
840
+ sinon.stub(MeetingsUtil, 'isBreakoutLocusDTO').returns(false);
691
841
  webex.meetings.handleLocusEvent({
692
842
  locusUrl: url1,
693
843
  });
694
844
  assert.calledOnce(webex.meetings.meetingCollection.getByKey);
695
845
  assert.calledWith(webex.meetings.meetingCollection.getByKey, 'locusUrl', url1);
696
- assert.calledOnce(parse);
846
+ assert.calledOnce(locusInfo.parse);
847
+ assert.calledOnce(locusInfo.updateMainSessionLocusCache);
697
848
  assert.calledWith(
698
- parse,
849
+ locusInfo.parse,
699
850
  {
700
- locusInfo: {
701
- parse,
702
- },
851
+ locusInfo,
703
852
  },
704
853
  {
705
854
  locusUrl: url1,
706
855
  }
707
856
  );
708
857
  });
858
+
859
+ it('should not update main session locus cache', () => {
860
+ sinon.stub(MeetingsUtil, 'isBreakoutLocusDTO').returns(true);
861
+ webex.meetings.handleLocusEvent({
862
+ locusUrl: url1,
863
+ });
864
+ assert.notCalled(locusInfo.updateMainSessionLocusCache);
865
+ });
709
866
  });
710
867
  describe('there was not a meeting', () => {
711
868
  let initialSetup;
869
+ const webExMeetingId = '123456';
712
870
 
713
871
  beforeEach(() => {
714
872
  initialSetup = sinon.stub().returns(true);
715
873
  webex.meetings.meetingCollection.getByKey = sinon.stub().returns(undefined);
716
874
  webex.meetings.create = sinon.stub().returns(
717
875
  Promise.resolve({
876
+ id: 'meeting-id',
718
877
  locusInfo: {
878
+ ...locusInfo,
719
879
  initialSetup,
720
880
  },
721
881
  })
@@ -735,12 +895,20 @@ describe('plugin-meetings', () => {
735
895
  callbackAddress: uri1,
736
896
  },
737
897
  },
898
+ info: {
899
+ webExMeetingId,
900
+ },
738
901
  },
739
902
  eventType: 'locus.difference',
740
903
  locusUrl: url1,
741
904
  });
742
- assert.callCount(webex.meetings.meetingCollection.getByKey, 5);
905
+ assert.callCount(webex.meetings.meetingCollection.getByKey, 6);
743
906
  assert.calledWith(webex.meetings.meetingCollection.getByKey, 'locusUrl', url1);
907
+ assert.calledWith(
908
+ webex.meetings.meetingCollection.getByKey,
909
+ 'meetingNumber',
910
+ webExMeetingId
911
+ );
744
912
  assert.calledOnce(initialSetup);
745
913
  assert.calledWith(initialSetup, {
746
914
  id: uuid1,
@@ -754,6 +922,9 @@ describe('plugin-meetings', () => {
754
922
  callbackAddress: uri1,
755
923
  },
756
924
  },
925
+ info: {
926
+ webExMeetingId,
927
+ },
757
928
  });
758
929
  });
759
930
  it('should setup the meeting by difference event without replaces', async () => {
@@ -765,12 +936,20 @@ describe('plugin-meetings', () => {
765
936
  callbackAddress: uri1,
766
937
  },
767
938
  },
939
+ info: {
940
+ webExMeetingId,
941
+ },
768
942
  },
769
943
  eventType: 'locus.difference',
770
944
  locusUrl: url1,
771
945
  });
772
- assert.callCount(webex.meetings.meetingCollection.getByKey, 4);
946
+ assert.callCount(webex.meetings.meetingCollection.getByKey, 5);
773
947
  assert.calledWith(webex.meetings.meetingCollection.getByKey, 'locusUrl', url1);
948
+ assert.calledWith(
949
+ webex.meetings.meetingCollection.getByKey,
950
+ 'meetingNumber',
951
+ webExMeetingId
952
+ );
774
953
  assert.calledOnce(initialSetup);
775
954
  assert.calledWith(initialSetup, {
776
955
  id: uuid1,
@@ -779,8 +958,44 @@ describe('plugin-meetings', () => {
779
958
  callbackAddress: uri1,
780
959
  },
781
960
  },
961
+ info: {
962
+ webExMeetingId,
963
+ },
964
+ });
965
+ });
966
+
967
+ it('sends client event correctly on finally', async () => {
968
+ webex.meetings.getMeetingByType = sinon.stub().returns(true);
969
+
970
+ await webex.meetings.handleLocusEvent({
971
+ locus: {
972
+ id: uuid1,
973
+ self: {
974
+ callBackInfo: {
975
+ callbackAddress: uri1,
976
+ },
977
+ },
978
+ info: {
979
+ webExMeetingId,
980
+ },
981
+ },
982
+ eventType: 'locus.difference',
983
+ locusUrl: url1,
984
+ });
985
+
986
+ await testUtils.flushPromises();
987
+
988
+ assert.calledWith(webex.internal.newMetrics.submitClientEvent, {
989
+ name: 'client.call.remote-started',
990
+ payload: {
991
+ trigger: 'mercury-event',
992
+ },
993
+ options: {
994
+ meetingId: 'meeting-id',
995
+ },
782
996
  });
783
997
  });
998
+
784
999
  it('should setup the meeting by a not difference event', async () => {
785
1000
  await webex.meetings.handleLocusEvent({
786
1001
  locus: {
@@ -790,12 +1005,20 @@ describe('plugin-meetings', () => {
790
1005
  callbackAddress: uri1,
791
1006
  },
792
1007
  },
1008
+ info: {
1009
+ webExMeetingId,
1010
+ },
793
1011
  },
794
1012
  eventType: test1,
795
1013
  locusUrl: url1,
796
1014
  });
797
- assert.callCount(webex.meetings.meetingCollection.getByKey, 4);
1015
+ assert.callCount(webex.meetings.meetingCollection.getByKey, 5);
798
1016
  assert.calledWith(webex.meetings.meetingCollection.getByKey, 'locusUrl', url1);
1017
+ assert.calledWith(
1018
+ webex.meetings.meetingCollection.getByKey,
1019
+ 'meetingNumber',
1020
+ webExMeetingId
1021
+ );
799
1022
  assert.calledOnce(initialSetup);
800
1023
  assert.calledWith(initialSetup, {
801
1024
  id: uuid1,
@@ -804,6 +1027,9 @@ describe('plugin-meetings', () => {
804
1027
  callbackAddress: uri1,
805
1028
  },
806
1029
  },
1030
+ info: {
1031
+ webExMeetingId,
1032
+ },
807
1033
  });
808
1034
  });
809
1035
 
@@ -826,7 +1052,7 @@ describe('plugin-meetings', () => {
826
1052
 
827
1053
  it('should not try to match USM meetings by conversation url', async () => {
828
1054
  await webex.meetings.handleLocusEvent(generateFakeLocusData(true));
829
- assert.callCount(webex.meetings.meetingCollection.getByKey, 3);
1055
+ assert.callCount(webex.meetings.meetingCollection.getByKey, 4);
830
1056
  assert.deepEqual(webex.meetings.meetingCollection.getByKey.getCall(0).args, [
831
1057
  'locusUrl',
832
1058
  url1,
@@ -843,7 +1069,7 @@ describe('plugin-meetings', () => {
843
1069
  });
844
1070
  it('should try to match non-USM meetings by conversation url', async () => {
845
1071
  await webex.meetings.handleLocusEvent(generateFakeLocusData(false));
846
- assert.callCount(webex.meetings.meetingCollection.getByKey, 4);
1072
+ assert.callCount(webex.meetings.meetingCollection.getByKey, 5);
847
1073
  assert.deepEqual(webex.meetings.meetingCollection.getByKey.getCall(0).args, [
848
1074
  'locusUrl',
849
1075
  url1,
@@ -866,7 +1092,6 @@ describe('plugin-meetings', () => {
866
1092
  });
867
1093
  describe('#createMeeting', () => {
868
1094
  beforeEach(() => {
869
- MediaUtil.createPeerConnection = sinon.stub().returns(true);
870
1095
  webex.internal.device.userId = uuid1;
871
1096
  webex.internal.device.url = url1;
872
1097
  MeetingCollection.set = sinon.stub().returns(true);
@@ -875,6 +1100,10 @@ describe('plugin-meetings', () => {
875
1100
  });
876
1101
  describe('successful MeetingInfo.#fetchMeetingInfo', () => {
877
1102
  let clock, setTimeoutSpy, fakeMeetingStartTimeString, FAKE_TIME_TO_START;
1103
+ const FAKE_INFO_EXTRA_PARAMS = {
1104
+ mtid: 'm9fe0afd8c435e892afcce9ea25b97046',
1105
+ joinTXId: 'TSmrX61wNF',
1106
+ };
878
1107
 
879
1108
  beforeEach(() => {
880
1109
  clock = sinon.useFakeTimers();
@@ -904,13 +1133,25 @@ describe('plugin-meetings', () => {
904
1133
  meeting,
905
1134
  destination,
906
1135
  type,
907
- expectedMeetingData = {}
1136
+ extraParams = {},
1137
+ expectedMeetingData = {},
1138
+ sendCAevents = false
908
1139
  ) => {
909
1140
  assert.calledOnce(webex.meetings.meetingInfo.fetchMeetingInfo);
910
1141
  assert.calledOnce(MeetingsUtil.getMeetingAddedType);
911
1142
  assert.notCalled(setTimeoutSpy);
912
- assert.calledThrice(TriggerProxy.trigger);
913
- assert.calledWith(webex.meetings.meetingInfo.fetchMeetingInfo, destination, type);
1143
+ assert.callCount(TriggerProxy.trigger, 5);
1144
+ assert.calledWith(
1145
+ webex.meetings.meetingInfo.fetchMeetingInfo,
1146
+ destination,
1147
+ type,
1148
+ null,
1149
+ null,
1150
+ undefined,
1151
+ undefined,
1152
+ extraParams,
1153
+ {meetingId: meeting.id, sendCAevents}
1154
+ );
914
1155
  assert.calledWith(MeetingsUtil.getMeetingAddedType, 'test type');
915
1156
 
916
1157
  if (expectedMeetingData.permissionToken) {
@@ -919,6 +1160,15 @@ describe('plugin-meetings', () => {
919
1160
  if (expectedMeetingData.meetingJoinUrl) {
920
1161
  assert.equal(meeting.meetingJoinUrl, expectedMeetingData.meetingJoinUrl);
921
1162
  }
1163
+ if (expectedMeetingData.correlationId) {
1164
+ assert.equal(meeting.correlationId, expectedMeetingData.correlationId);
1165
+ }
1166
+ if (expectedMeetingData.callStateForMetrics) {
1167
+ assert.deepEqual(
1168
+ meeting.callStateForMetrics,
1169
+ expectedMeetingData.callStateForMetrics
1170
+ );
1171
+ }
922
1172
  assert.equal(meeting.destination, destination);
923
1173
  assert.equal(meeting.destinationType, type);
924
1174
  assert.calledWith(
@@ -948,27 +1198,142 @@ describe('plugin-meetings', () => {
948
1198
  const expectedMeetingData = {
949
1199
  permissionToken: 'PT',
950
1200
  meetingJoinUrl: 'meetingJoinUrl',
1201
+ correlationId: meeting.id,
951
1202
  };
952
1203
 
953
- checkCreateWithoutDelay(meeting, 'test destination', 'test type', expectedMeetingData);
1204
+ checkCreateWithoutDelay(
1205
+ meeting,
1206
+ 'test destination',
1207
+ 'test type',
1208
+ {},
1209
+ expectedMeetingData
1210
+ );
954
1211
  });
955
1212
 
956
- it('creates the meeting from a successful meeting info fetch meeting resolve testing', async () => {
957
- const meeting = await webex.meetings.createMeeting('test destination', 'test type');
958
- const expectedMeetingData = {
959
- permissionToken: 'PT',
960
- meetingJoinUrl: 'meetingJoinUrl',
961
- };
1213
+ [undefined, FAKE_INFO_EXTRA_PARAMS].forEach((infoExtraParams) => {
1214
+ const infoExtraParamsProvided = infoExtraParams !== undefined;
1215
+
1216
+ it(`creates the meeting from a successful meeting info fetch meeting resolve testing${
1217
+ infoExtraParamsProvided ? ' with infoExtraParams' : ''
1218
+ }`, async () => {
1219
+ const meeting = await webex.meetings.createMeeting(
1220
+ 'test destination',
1221
+ 'test type',
1222
+ false,
1223
+ infoExtraParams
1224
+ );
1225
+ const expectedMeetingData = {
1226
+ permissionToken: 'PT',
1227
+ meetingJoinUrl: 'meetingJoinUrl',
1228
+ };
1229
+
1230
+ assert.instanceOf(
1231
+ meeting,
1232
+ Meeting,
1233
+ 'createMeeting should eventually resolve to a Meeting Object'
1234
+ );
1235
+ checkCreateWithoutDelay(
1236
+ meeting,
1237
+ 'test destination',
1238
+ 'test type',
1239
+ infoExtraParamsProvided ? infoExtraParams : {},
1240
+ expectedMeetingData
1241
+ );
1242
+ });
962
1243
 
963
- assert.instanceOf(
964
- meeting,
965
- Meeting,
966
- 'createMeeting should eventually resolve to a Meeting Object'
967
- );
968
- checkCreateWithoutDelay(meeting, 'test destination', 'test type', expectedMeetingData);
1244
+ it(`creates the meeting from a successful meeting info fetch with random delay${
1245
+ infoExtraParamsProvided ? ' with infoExtraParams' : ''
1246
+ }`, async () => {
1247
+ const FAKE_LOCUS_MEETING = {
1248
+ conversationUrl: 'locusConvURL',
1249
+ url: 'locusUrl',
1250
+ info: {
1251
+ webExMeetingId: 'locusMeetingId',
1252
+ sipUri: 'locusSipUri',
1253
+ owner: 'locusOwner',
1254
+ },
1255
+ meeting: {
1256
+ startTime: fakeMeetingStartTimeString,
1257
+ },
1258
+ fullState: {
1259
+ active: false,
1260
+ },
1261
+ };
1262
+
1263
+ const meeting = await webex.meetings.createMeeting(
1264
+ FAKE_LOCUS_MEETING,
1265
+ 'test type',
1266
+ true,
1267
+ infoExtraParams
1268
+ );
1269
+
1270
+ assert.instanceOf(
1271
+ meeting,
1272
+ Meeting,
1273
+ 'createMeeting should eventually resolve to a Meeting Object'
1274
+ );
1275
+ assert.notCalled(webex.meetings.meetingInfo.fetchMeetingInfo);
1276
+ assert.calledOnce(setTimeoutSpy);
1277
+
1278
+ // Parse meeting info with locus object
1279
+ assert.equal(meeting.conversationUrl, 'locusConvURL');
1280
+ assert.equal(meeting.locusUrl, 'locusUrl');
1281
+ assert.equal(meeting.sipUri, 'locusSipUri');
1282
+ assert.equal(meeting.meetingNumber, 'locusMeetingId');
1283
+ assert.isUndefined(meeting.meetingJoinUrl);
1284
+ assert.equal(meeting.owner, 'locusOwner');
1285
+ assert.isUndefined(meeting.permissionToken);
1286
+
1287
+ // Add meeting and send trigger
1288
+ assert.calledWith(MeetingsUtil.getMeetingAddedType, 'test type');
1289
+ assert.calledTwice(TriggerProxy.trigger);
1290
+ assert.calledWith(
1291
+ TriggerProxy.trigger,
1292
+ sinon.match.instanceOf(Meetings),
1293
+ {
1294
+ file: 'meetings',
1295
+ function: 'createMeeting',
1296
+ },
1297
+ 'meeting:added',
1298
+ {
1299
+ meeting: sinon.match.instanceOf(Meeting),
1300
+ type: 'test meeting added type',
1301
+ }
1302
+ );
1303
+
1304
+ // When timer expires
1305
+ clock.tick(FAKE_TIME_TO_START);
1306
+ assert.calledWith(
1307
+ webex.meetings.meetingInfo.fetchMeetingInfo,
1308
+ FAKE_LOCUS_MEETING,
1309
+ 'test type',
1310
+ null,
1311
+ null,
1312
+ undefined,
1313
+ undefined,
1314
+ infoExtraParamsProvided ? infoExtraParams : {}
1315
+ );
1316
+
1317
+ // Parse meeting info is called again with new meeting info
1318
+ await testUtils.flushPromises();
1319
+ assert.equal(meeting.conversationUrl, 'locusConvURL');
1320
+ assert.equal(meeting.locusUrl, 'locusUrl');
1321
+ assert.equal(meeting.sipUri, 'locusSipUri');
1322
+ assert.equal(meeting.meetingNumber, 'locusMeetingId');
1323
+ assert.equal(meeting.meetingJoinUrl, 'meetingJoinUrl');
1324
+ assert.equal(meeting.owner, 'locusOwner');
1325
+ assert.equal(meeting.permissionToken, 'PT');
1326
+
1327
+ assert.calledWith(
1328
+ TriggerProxy.trigger,
1329
+ meeting,
1330
+ {file: 'meetings', function: 'fetchMeetingInfo'},
1331
+ 'meeting:meetingInfoAvailable'
1332
+ );
1333
+ });
969
1334
  });
970
1335
 
971
- it('creates the meeting from a successful meeting info fetch with random delay', async () => {
1336
+ it('creates the meeting from a successful meeting info fetch that has no random delay because it is active', async () => {
972
1337
  const FAKE_LOCUS_MEETING = {
973
1338
  conversationUrl: 'locusConvURL',
974
1339
  url: 'locusUrl',
@@ -981,7 +1346,7 @@ describe('plugin-meetings', () => {
981
1346
  startTime: fakeMeetingStartTimeString,
982
1347
  },
983
1348
  fullState: {
984
- active: false,
1349
+ active: true,
985
1350
  },
986
1351
  };
987
1352
 
@@ -996,62 +1361,10 @@ describe('plugin-meetings', () => {
996
1361
  Meeting,
997
1362
  'createMeeting should eventually resolve to a Meeting Object'
998
1363
  );
999
- assert.notCalled(webex.meetings.meetingInfo.fetchMeetingInfo);
1000
- assert.calledOnce(setTimeoutSpy);
1001
-
1002
- // Parse meeting info with locus object
1003
- assert.equal(meeting.conversationUrl, 'locusConvURL');
1004
- assert.equal(meeting.locusUrl, 'locusUrl');
1005
- assert.equal(meeting.sipUri, 'locusSipUri');
1006
- assert.equal(meeting.meetingNumber, 'locusMeetingId');
1007
- assert.isUndefined(meeting.meetingJoinUrl);
1008
- assert.equal(meeting.owner, 'locusOwner');
1009
- assert.isUndefined(meeting.permissionToken);
1010
-
1011
- // Add meeting and send trigger
1012
- assert.calledWith(MeetingsUtil.getMeetingAddedType, 'test type');
1013
- assert.calledTwice(TriggerProxy.trigger);
1014
- assert.calledWith(
1015
- TriggerProxy.trigger,
1016
- sinon.match.instanceOf(Meetings),
1017
- {
1018
- file: 'meetings',
1019
- function: 'createMeeting',
1020
- },
1021
- 'meeting:added',
1022
- {
1023
- meeting: sinon.match.instanceOf(Meeting),
1024
- type: 'test meeting added type',
1025
- }
1026
- );
1027
-
1028
- // When timer expires
1029
- clock.tick(FAKE_TIME_TO_START);
1030
- assert.calledWith(
1031
- webex.meetings.meetingInfo.fetchMeetingInfo,
1032
- FAKE_LOCUS_MEETING,
1033
- 'test type'
1034
- );
1035
-
1036
- // Parse meeting info is called again with new meeting info
1037
- await testUtils.flushPromises();
1038
- assert.equal(meeting.conversationUrl, 'locusConvURL');
1039
- assert.equal(meeting.locusUrl, 'locusUrl');
1040
- assert.equal(meeting.sipUri, 'locusSipUri');
1041
- assert.equal(meeting.meetingNumber, 'locusMeetingId');
1042
- assert.equal(meeting.meetingJoinUrl, 'meetingJoinUrl');
1043
- assert.equal(meeting.owner, 'locusOwner');
1044
- assert.equal(meeting.permissionToken, 'PT');
1045
-
1046
- assert.calledWith(
1047
- TriggerProxy.trigger,
1048
- meeting,
1049
- {file: 'meetings', function: 'fetchMeetingInfo'},
1050
- 'meeting:meetingInfoAvailable'
1051
- );
1364
+ checkCreateWithoutDelay(meeting, FAKE_LOCUS_MEETING, 'test type');
1052
1365
  });
1053
1366
 
1054
- it('creates the meeting from a successful meeting info fetch that has no random delay because it is active', async () => {
1367
+ it('creates the meeting from a successful meeting info fetch that has no random delay because meeting start time is in the past', async () => {
1055
1368
  const FAKE_LOCUS_MEETING = {
1056
1369
  conversationUrl: 'locusConvURL',
1057
1370
  url: 'locusUrl',
@@ -1061,10 +1374,10 @@ describe('plugin-meetings', () => {
1061
1374
  owner: 'locusOwner',
1062
1375
  },
1063
1376
  meeting: {
1064
- startTime: fakeMeetingStartTimeString,
1377
+ startTime: fakeMeetingStartTimeString - 1 * 60 * 60 * 1000,
1065
1378
  },
1066
1379
  fullState: {
1067
- active: true,
1380
+ active: false,
1068
1381
  },
1069
1382
  };
1070
1383
 
@@ -1082,7 +1395,12 @@ describe('plugin-meetings', () => {
1082
1395
  checkCreateWithoutDelay(meeting, FAKE_LOCUS_MEETING, 'test type');
1083
1396
  });
1084
1397
 
1085
- it('creates the meeting from a successful meeting info fetch that has no random delay because meeting start time is in the past', async () => {
1398
+ it('creates the meeting from a successful meeting info fetch that has no random delay because enableUnifiedMeetings is disabled', async () => {
1399
+ Object.assign(webex.meetings.config, {
1400
+ experimental: {
1401
+ enableUnifiedMeetings: false,
1402
+ },
1403
+ });
1086
1404
  const FAKE_LOCUS_MEETING = {
1087
1405
  conversationUrl: 'locusConvURL',
1088
1406
  url: 'locusUrl',
@@ -1092,7 +1410,7 @@ describe('plugin-meetings', () => {
1092
1410
  owner: 'locusOwner',
1093
1411
  },
1094
1412
  meeting: {
1095
- startTime: fakeMeetingStartTimeString - 1 * 60 * 60 * 1000,
1413
+ startTime: fakeMeetingStartTimeString,
1096
1414
  },
1097
1415
  fullState: {
1098
1416
  active: false,
@@ -1113,40 +1431,59 @@ describe('plugin-meetings', () => {
1113
1431
  checkCreateWithoutDelay(meeting, FAKE_LOCUS_MEETING, 'test type');
1114
1432
  });
1115
1433
 
1116
- it('creates the meeting from a successful meeting info fetch that has no random delay because enableUnifiedMeetings is disabled', async () => {
1117
- Object.assign(webex.meetings.config, {
1118
- experimental: {
1119
- enableUnifiedMeetings: false,
1120
- },
1121
- });
1122
- const FAKE_LOCUS_MEETING = {
1123
- conversationUrl: 'locusConvURL',
1124
- url: 'locusUrl',
1125
- info: {
1126
- webExMeetingId: 'locusMeetingId',
1127
- sipUri: 'locusSipUri',
1128
- owner: 'locusOwner',
1129
- },
1130
- meeting: {
1131
- startTime: fakeMeetingStartTimeString,
1132
- },
1133
- fullState: {
1134
- active: false,
1135
- },
1434
+ it('creates meeting with the correlationId provided', async () => {
1435
+ const meeting = await webex.meetings.createMeeting(
1436
+ 'test destination',
1437
+ 'test type',
1438
+ false,
1439
+ {},
1440
+ {correlationId: 'my-correlationId'}
1441
+ );
1442
+
1443
+ const expectedMeetingData = {
1444
+ correlationId: 'my-correlationId',
1136
1445
  };
1137
1446
 
1138
- const meeting = await webex.meetings.createMeeting(
1139
- FAKE_LOCUS_MEETING,
1447
+ checkCreateWithoutDelay(
1448
+ meeting,
1449
+ 'test destination',
1140
1450
  'test type',
1451
+ {},
1452
+ expectedMeetingData,
1141
1453
  true
1142
1454
  );
1455
+ });
1143
1456
 
1144
- assert.instanceOf(
1457
+ it('creates meeting with the callStateForMetrics provided', async () => {
1458
+ const meeting = await webex.meetings.createMeeting(
1459
+ 'test destination',
1460
+ 'test type',
1461
+ false,
1462
+ {},
1463
+ {
1464
+ correlationId: 'my-correlationId',
1465
+ joinTrigger: 'my-join-trigger',
1466
+ loginType: 'my-login-type',
1467
+ }
1468
+ );
1469
+
1470
+ const expectedMeetingData = {
1471
+ correlationId: 'my-correlationId',
1472
+ callStateForMetrics: {
1473
+ correlationId: 'my-correlationId',
1474
+ joinTrigger: 'my-join-trigger',
1475
+ loginType: 'my-login-type',
1476
+ },
1477
+ };
1478
+
1479
+ checkCreateWithoutDelay(
1145
1480
  meeting,
1146
- Meeting,
1147
- 'createMeeting should eventually resolve to a Meeting Object'
1481
+ 'test destination',
1482
+ 'test type',
1483
+ {},
1484
+ expectedMeetingData,
1485
+ true
1148
1486
  );
1149
- checkCreateWithoutDelay(meeting, FAKE_LOCUS_MEETING, 'test type');
1150
1487
  });
1151
1488
  });
1152
1489
 
@@ -1157,45 +1494,136 @@ describe('plugin-meetings', () => {
1157
1494
  webex.meetings.meetingInfo.fetchMeetingInfo = sinon
1158
1495
  .stub()
1159
1496
  .returns(Promise.reject(new Error('test')));
1497
+ webex.meetings.destroy = sinon.stub().returns(Promise.resolve());
1498
+ webex.meetings.createMeeting = sinon.spy(webex.meetings.createMeeting);
1160
1499
  });
1500
+
1501
+ const checkCreateMeetingWithNoMeetingInfo = async (failOnMissingMeetingInfo, destroy) => {
1502
+ try {
1503
+ const meeting = await webex.meetings.createMeeting(
1504
+ 'test destination',
1505
+ 'test type',
1506
+ undefined,
1507
+ undefined,
1508
+ undefined,
1509
+ failOnMissingMeetingInfo
1510
+ );
1511
+
1512
+ assert.instanceOf(
1513
+ meeting,
1514
+ Meeting,
1515
+ 'createMeeting should eventually resolve to a Meeting Object'
1516
+ );
1517
+ assert.calledOnce(webex.meetings.meetingInfo.fetchMeetingInfo);
1518
+ assert.calledOnce(MeetingsUtil.getMeetingAddedType);
1519
+ assert.calledThrice(TriggerProxy.trigger);
1520
+ assert.calledWith(
1521
+ webex.meetings.meetingInfo.fetchMeetingInfo,
1522
+ 'test destination',
1523
+ 'test type'
1524
+ );
1525
+
1526
+ if (destroy) {
1527
+ assert.calledWith(
1528
+ webex.meetings.destroy,
1529
+ sinon.match.instanceOf(Meeting),
1530
+ 'MISSING_MEETING_INFO'
1531
+ );
1532
+ assert.notCalled(MeetingsUtil.getMeetingAddedType);
1533
+ assert.notCalled(TriggerProxy.trigger);
1534
+ assert.throw(webex.meetings.createMeeting, 'meeting information not found');
1535
+ } else {
1536
+ assert.notCalled(webex.meetings.destroy);
1537
+ assert.calledWith(MeetingsUtil.getMeetingAddedType, 'test type');
1538
+ assert.calledWith(
1539
+ TriggerProxy.trigger,
1540
+ sinon.match.instanceOf(Meetings),
1541
+ {
1542
+ file: 'meetings',
1543
+ function: 'createMeeting',
1544
+ },
1545
+ 'meeting:added',
1546
+ {
1547
+ meeting: sinon.match.instanceOf(Meeting),
1548
+ type: 'test meeting added type',
1549
+ }
1550
+ );
1551
+ }
1552
+ } catch (err) {
1553
+ assert.instanceOf(err, NoMeetingInfoError);
1554
+ }
1555
+ };
1556
+
1161
1557
  it('creates the meeting from a rejected meeting info fetch', async () => {
1162
- const meeting = await webex.meetings.createMeeting('test destination', 'test type');
1558
+ checkCreateMeetingWithNoMeetingInfo(false, false);
1559
+ });
1163
1560
 
1164
- assert.instanceOf(
1165
- meeting,
1166
- Meeting,
1167
- 'createMeeting should eventually resolve to a Meeting Object'
1168
- );
1169
- assert.calledOnce(webex.meetings.meetingInfo.fetchMeetingInfo);
1170
- assert.calledOnce(MeetingsUtil.getMeetingAddedType);
1171
- assert.calledTwice(TriggerProxy.trigger);
1172
- assert.calledWith(
1173
- webex.meetings.meetingInfo.fetchMeetingInfo,
1174
- 'test destination',
1175
- 'test type'
1176
- );
1177
- assert.calledWith(MeetingsUtil.getMeetingAddedType, 'test type');
1178
- assert.calledWith(
1179
- TriggerProxy.trigger,
1180
- sinon.match.instanceOf(Meetings),
1561
+ it('creates the meeting from a rejected meeting info fetch and destroys it if failOnMissingMeetingInfo', async () => {
1562
+ checkCreateMeetingWithNoMeetingInfo(true, true);
1563
+ });
1564
+ });
1565
+
1566
+ describe('rejected MeetingInfo.#fetchMeetingInfo - does not log for known Error types', () => {
1567
+ forEach(
1568
+ [
1181
1569
  {
1182
- file: 'meetings',
1183
- function: 'createMeeting',
1570
+ error: new CaptchaError(),
1571
+ debugLogMessage:
1572
+ 'Meetings:index#createMeeting --> Debug CaptchaError: Captcha is required. fetching /meetingInfo for creation.',
1184
1573
  },
1185
- 'meeting:added',
1186
1574
  {
1187
- meeting: sinon.match.instanceOf(Meeting),
1188
- type: 'test meeting added type',
1189
- }
1190
- );
1191
- });
1575
+ error: new PasswordError(),
1576
+ debugLogMessage:
1577
+ 'Meetings:index#createMeeting --> Debug PasswordError: Password is required, please use verifyPassword() fetching /meetingInfo for creation.',
1578
+ },
1579
+ {
1580
+ error: new PermissionError(),
1581
+ debugLogMessage:
1582
+ 'Meetings:index#createMeeting --> Debug PermissionError: Not allowed to execute the function, some properties on server, or local client state do not allow you to complete this action. fetching /meetingInfo for creation.',
1583
+ },
1584
+ {
1585
+ error: new Error(),
1586
+ infoLogMessage: true,
1587
+ debugLogMessage:
1588
+ 'Meetings:index#createMeeting --> Debug Error fetching /meetingInfo for creation.',
1589
+ },
1590
+ ],
1591
+ ({error, debugLogMessage, infoLogMessage}) => {
1592
+ it('creates the meeting from a rejected meeting info fetch', async () => {
1593
+ webex.meetings.meetingInfo.fetchMeetingInfo = sinon
1594
+ .stub()
1595
+ .returns(Promise.reject(error));
1596
+
1597
+ LoggerProxy.logger.debug = sinon.stub();
1598
+ LoggerProxy.logger.info = sinon.stub();
1599
+
1600
+ const meeting = await webex.meetings.createMeeting('test destination', 'test type');
1601
+
1602
+ assert.instanceOf(
1603
+ meeting,
1604
+ Meeting,
1605
+ 'createMeeting should eventually resolve to a Meeting Object'
1606
+ );
1607
+
1608
+ assert.calledWith(LoggerProxy.logger.debug, debugLogMessage);
1609
+
1610
+ if (infoLogMessage) {
1611
+ assert.calledWith(
1612
+ LoggerProxy.logger.info,
1613
+ 'Meetings:index#createMeeting --> Info Unable to fetch meeting info for test destination.'
1614
+ );
1615
+ } else {
1616
+ assert.notCalled(LoggerProxy.logger.info);
1617
+ }
1618
+ });
1619
+ }
1620
+ );
1192
1621
  });
1193
1622
  });
1194
1623
  });
1195
1624
  describe('Public Event Triggers', () => {
1196
1625
  describe('#destroy', () => {
1197
1626
  beforeEach(() => {
1198
- MediaUtil.createPeerConnection = sinon.stub().returns(true);
1199
1627
  MeetingUtil.cleanUp = sinon.stub();
1200
1628
  });
1201
1629
  it('should have #destroy', () => {
@@ -1269,6 +1697,8 @@ describe('plugin-meetings', () => {
1269
1697
  });
1270
1698
 
1271
1699
  describe('#fetchUserPreferredWebexSite', () => {
1700
+ let loggerProxySpy;
1701
+
1272
1702
  it('should call request.getMeetingPreferences to get the preferred webex site ', async () => {
1273
1703
  assert.isDefined(webex.meetings.preferredWebexSite);
1274
1704
  await webex.meetings.fetchUserPreferredWebexSite();
@@ -1276,7 +1706,22 @@ describe('plugin-meetings', () => {
1276
1706
  assert.equal(webex.meetings.preferredWebexSite, 'go.webex.com');
1277
1707
  });
1278
1708
 
1709
+ const setup = ({user} = {}) => {
1710
+ loggerProxySpy = sinon.spy(LoggerProxy.logger, 'error');
1711
+
1712
+ Object.assign(webex.internal, {
1713
+ services: {
1714
+ getMeetingPreferences: sinon.stub().returns(Promise.resolve({})),
1715
+ },
1716
+ user: {
1717
+ get: sinon.stub().returns(Promise.resolve(user)),
1718
+ },
1719
+ });
1720
+ };
1721
+
1279
1722
  it('should not fail if UserPreferred info is not fetched ', async () => {
1723
+ setup();
1724
+
1280
1725
  Object.assign(webex.internal, {
1281
1726
  services: {
1282
1727
  getMeetingPreferences: sinon.stub().returns(Promise.resolve({})),
@@ -1286,7 +1731,115 @@ describe('plugin-meetings', () => {
1286
1731
  await webex.meetings.fetchUserPreferredWebexSite().then(() => {
1287
1732
  assert.equal(webex.meetings.preferredWebexSite, '');
1288
1733
  });
1734
+ assert.calledOnceWithExactly(
1735
+ loggerProxySpy,
1736
+ 'Failed to fetch preferred site from user - no site will be set'
1737
+ );
1738
+ });
1739
+
1740
+ it('should fall back to fetching the site from the user', async () => {
1741
+ setup({
1742
+ user: {
1743
+ userPreferences: {
1744
+ userPreferencesItems: {
1745
+ preferredWebExSite: 'site.webex.com',
1746
+ },
1747
+ },
1748
+ },
1749
+ });
1750
+
1751
+ await webex.meetings.fetchUserPreferredWebexSite();
1752
+
1753
+ assert.equal(webex.meetings.preferredWebexSite, 'site.webex.com');
1754
+ assert.notCalled(loggerProxySpy);
1755
+ });
1756
+
1757
+ forEach(
1758
+ [
1759
+ {user: undefined},
1760
+ {user: {userPreferences: {}}},
1761
+ {user: {userPreferences: {userPreferencesItems: {}}}},
1762
+ {user: {userPreferences: {userPreferencesItems: {preferredWebExSite: undefined}}}},
1763
+ ],
1764
+ ({user}) => {
1765
+ it(`should handle invalid user data ${user}`, async () => {
1766
+ setup({user});
1767
+
1768
+ await webex.meetings.fetchUserPreferredWebexSite();
1769
+
1770
+ assert.equal(webex.meetings.preferredWebexSite, '');
1771
+ assert.calledOnceWithExactly(
1772
+ loggerProxySpy,
1773
+ 'Failed to fetch preferred site from user - no site will be set'
1774
+ );
1775
+ });
1776
+ }
1777
+ );
1778
+
1779
+ it('should handle a get user failure', async () => {
1780
+ setup();
1781
+
1782
+ webex.internal.user.get.rejects(new Error());
1783
+
1784
+ await webex.meetings.fetchUserPreferredWebexSite();
1785
+
1786
+ assert.equal(webex.meetings.preferredWebexSite, '');
1787
+ assert.calledOnceWithExactly(
1788
+ loggerProxySpy,
1789
+ 'Failed to fetch preferred site from user - no site will be set'
1790
+ );
1791
+ });
1792
+
1793
+ it('should fall back to fetching the site from the user', async () => {
1794
+ setup({
1795
+ user: {
1796
+ userPreferences: {
1797
+ userPreferencesItems: {
1798
+ preferredWebExSite: 'site.webex.com',
1799
+ },
1800
+ },
1801
+ },
1802
+ });
1803
+
1804
+ await webex.meetings.fetchUserPreferredWebexSite();
1805
+
1806
+ assert.equal(webex.meetings.preferredWebexSite, 'site.webex.com');
1807
+ assert.notCalled(loggerProxySpy);
1289
1808
  });
1809
+
1810
+ forEach([
1811
+ {user: undefined},
1812
+ {user: {userPreferences: {}}},
1813
+ {user: {userPreferences: {userPreferencesItems: {}}}},
1814
+ {user: {userPreferences: {userPreferencesItems: {preferredWebExSite: undefined}}}},
1815
+ ], ({user}) => {
1816
+ it(`should handle invalid user data ${user}`, async () => {
1817
+ setup({user});
1818
+
1819
+ await webex.meetings.fetchUserPreferredWebexSite();
1820
+
1821
+ assert.equal(webex.meetings.preferredWebexSite, '');
1822
+ assert.calledOnceWithExactly(
1823
+ loggerProxySpy,
1824
+ 'Failed to fetch preferred site from user - no site will be set'
1825
+ );
1826
+ });
1827
+ });
1828
+
1829
+ it('should handle a get user failure', async () => {
1830
+ setup();
1831
+
1832
+ webex.internal.user.get.rejects(new Error());
1833
+
1834
+ await webex.meetings.fetchUserPreferredWebexSite();
1835
+
1836
+ assert.equal(webex.meetings.preferredWebexSite, '');
1837
+ assert.calledOnceWithExactly(
1838
+ loggerProxySpy,
1839
+ 'Failed to fetch preferred site from user - no site will be set'
1840
+ );
1841
+ });
1842
+
1290
1843
  });
1291
1844
  });
1292
1845
 
@@ -1294,7 +1847,6 @@ describe('plugin-meetings', () => {
1294
1847
  let meeting;
1295
1848
 
1296
1849
  beforeEach(async () => {
1297
- MediaUtil.createPeerConnection = sinon.stub().returns(true);
1298
1850
  webex.internal.device.userId = uuid1;
1299
1851
  webex.internal.device.url = url1;
1300
1852
  MeetingCollection.set = sinon.stub().returns(true);
@@ -1371,5 +1923,523 @@ describe('plugin-meetings', () => {
1371
1923
  );
1372
1924
  });
1373
1925
  });
1926
+
1927
+ describe('#isNeedHandleMainLocus', () => {
1928
+ let meeting;
1929
+ let newLocus;
1930
+ beforeEach(() => {
1931
+ meeting = {
1932
+ controls: {},
1933
+ self: {},
1934
+ };
1935
+ newLocus = {
1936
+ controls: {},
1937
+ self: {},
1938
+ };
1939
+ });
1940
+ afterEach(() => {
1941
+ sinon.restore();
1942
+ });
1943
+ it('check normal case will return true', () => {
1944
+ sinon.stub(webex.meetings.meetingCollection, 'getActiveBreakoutLocus').returns(null);
1945
+ LoggerProxy.logger.log = sinon.stub();
1946
+ const result = webex.meetings.isNeedHandleMainLocus(meeting, newLocus);
1947
+ assert.equal(result, true);
1948
+ assert.calledWith(
1949
+ LoggerProxy.logger.log,
1950
+ 'Meetings:index#isNeedHandleMainLocus --> this is a normal main session locusDTO update case'
1951
+ );
1952
+ });
1953
+
1954
+ it('check self joined and joined on this device, return true', () => {
1955
+ sinon.stub(webex.meetings.meetingCollection, 'getActiveBreakoutLocus').returns(null);
1956
+ newLocus.self.state = 'JOINED';
1957
+ sinon.stub(MeetingsUtil, 'joinedOnThisDevice').returns(true);
1958
+
1959
+ LoggerProxy.logger.log = sinon.stub();
1960
+ const result = webex.meetings.isNeedHandleMainLocus(meeting, newLocus);
1961
+ assert.equal(result, true);
1962
+ assert.calledWith(
1963
+ LoggerProxy.logger.log,
1964
+ 'Meetings:index#isNeedHandleMainLocus --> self this device shown as JOINED in the main session'
1965
+ );
1966
+ });
1967
+
1968
+ it('if newLocus replaceAt time is expired, then return false', () => {
1969
+ sinon.stub(webex.meetings.meetingCollection, 'getActiveBreakoutLocus').returns({
1970
+ joinedWith: {
1971
+ replaces: [
1972
+ {
1973
+ replaceAt: '2023-03-27T02:17:02.506Z',
1974
+ },
1975
+ ],
1976
+ },
1977
+ });
1978
+ newLocus.self.state = 'JOINED';
1979
+ sinon.stub(MeetingsUtil, 'joinedOnThisDevice').returns(true);
1980
+ sinon.stub(MeetingsUtil, 'getThisDevice').returns({
1981
+ replaces: [
1982
+ {
1983
+ replaceAt: '2023-03-27T02:17:01.506Z',
1984
+ },
1985
+ ],
1986
+ });
1987
+
1988
+ LoggerProxy.logger.log = sinon.stub();
1989
+ const result = webex.meetings.isNeedHandleMainLocus(meeting, newLocus);
1990
+ assert.equal(result, false);
1991
+ assert.calledWith(
1992
+ LoggerProxy.logger.log,
1993
+ `Meetings:index#isNeedHandleMainLocus --> this is expired main joined status locus_dto replacedAt 2023-03-27T02:17:01.506Z bo replacedAt 2023-03-27T02:17:02.506Z`
1994
+ );
1995
+ });
1996
+
1997
+ it('check current is in breakout join with this device, return false', () => {
1998
+ sinon.stub(webex.meetings.meetingCollection, 'getActiveBreakoutLocus').returns({
1999
+ joinedWith: {
2000
+ correlationId: '111',
2001
+ },
2002
+ });
2003
+ newLocus.controls.breakout = {url: 'url'};
2004
+ meeting.correlationId = '111';
2005
+
2006
+ LoggerProxy.logger.log = sinon.stub();
2007
+ const result = webex.meetings.isNeedHandleMainLocus(meeting, newLocus);
2008
+ assert.equal(result, false);
2009
+ assert.calledWith(
2010
+ LoggerProxy.logger.log,
2011
+ `Meetings:index#isNeedHandleMainLocus --> there is active breakout session and joined on this device, and don't need to handle main session: url`
2012
+ );
2013
+ });
2014
+
2015
+ it('check self is moved and removed, return false', () => {
2016
+ webex.meetings.meetingCollection.getActiveBreakoutLocus = sinon.stub().returns(null);
2017
+ newLocus.self.state = 'LEFT';
2018
+ newLocus.self.reason = 'MOVED';
2019
+ newLocus.self.removed = true;
2020
+ LoggerProxy.logger.log = sinon.stub();
2021
+ const result = webex.meetings.isNeedHandleMainLocus(meeting, newLocus);
2022
+ assert.equal(result, false);
2023
+ assert.calledWith(
2024
+ LoggerProxy.logger.log,
2025
+ 'Meetings:index#isNeedHandleMainLocus --> self moved main locus with self removed status or with device resource moved, not need to handle'
2026
+ );
2027
+ });
2028
+
2029
+ it('check self is moved and device resource removed, return false', () => {
2030
+ webex.meetings.meetingCollection.getActiveBreakoutLocus = sinon.stub().returns(null);
2031
+ newLocus.self.state = 'LEFT';
2032
+ newLocus.self.reason = 'MOVED';
2033
+ sinon.stub(MeetingsUtil, 'getThisDevice').returns({
2034
+ state: 'LEFT',
2035
+ reason: 'MOVED',
2036
+ });
2037
+ LoggerProxy.logger.log = sinon.stub();
2038
+ const result = webex.meetings.isNeedHandleMainLocus(meeting, newLocus);
2039
+ assert.equal(result, false);
2040
+ assert.calledWith(
2041
+ LoggerProxy.logger.log,
2042
+ 'Meetings:index#isNeedHandleMainLocus --> self moved main locus with self removed status or with device resource moved, not need to handle'
2043
+ );
2044
+ });
2045
+
2046
+ it('check self is joined but device resource removed, return false', () => {
2047
+ webex.meetings.meetingCollection.getActiveBreakoutLocus = sinon.stub().returns(null);
2048
+ sinon.stub(MeetingsUtil, 'joinedOnThisDevice').returns(false);
2049
+ newLocus.self.state = 'JOINED';
2050
+ sinon.stub(MeetingsUtil, 'getThisDevice').returns({
2051
+ state: 'LEFT',
2052
+ reason: 'MOVED',
2053
+ });
2054
+ LoggerProxy.logger.log = sinon.stub();
2055
+ const result = webex.meetings.isNeedHandleMainLocus(meeting, newLocus);
2056
+ assert.equal(result, false);
2057
+ assert.calledWith(
2058
+ LoggerProxy.logger.log,
2059
+ 'Meetings:index#isNeedHandleMainLocus --> self device left&moved in main locus with self joined status, not need to handle'
2060
+ );
2061
+ });
2062
+ });
2063
+
2064
+ describe('#isNeedHandleLocusDTO', () => {
2065
+ let meeting;
2066
+ let newLocus;
2067
+ beforeEach(() => {
2068
+ meeting = {
2069
+ controls: {},
2070
+ self: {},
2071
+ };
2072
+ newLocus = {
2073
+ controls: {},
2074
+ self: {},
2075
+ };
2076
+ });
2077
+ afterEach(() => {
2078
+ sinon.restore();
2079
+ });
2080
+ it('initial DTO , joined breakout session, return true', () => {
2081
+ newLocus.controls.breakout = {
2082
+ sessionType: 'BREAKOUT',
2083
+ };
2084
+ newLocus.self.state = 'JOINED';
2085
+ newLocus.fullState = {
2086
+ active: true,
2087
+ };
2088
+ LoggerProxy.logger.log = sinon.stub();
2089
+ const result = webex.meetings.isNeedHandleLocusDTO(null, newLocus);
2090
+ assert.equal(result, true);
2091
+ assert.calledWith(
2092
+ LoggerProxy.logger.log,
2093
+ `Meetings:index#isNeedHandleLocusDTO --> the first breakout session locusDTO active status: true`
2094
+ );
2095
+ });
2096
+ it('others go to check isNeedHandleMainLocus', () => {
2097
+ newLocus.controls.breakout = {
2098
+ sessionType: 'MAIN',
2099
+ };
2100
+ newLocus.self.state = 'JOINED';
2101
+
2102
+ LoggerProxy.logger.log = sinon.stub();
2103
+ const result = webex.meetings.isNeedHandleLocusDTO(meeting, newLocus);
2104
+ assert.equal(result, true);
2105
+ assert.calledWith(
2106
+ LoggerProxy.logger.log,
2107
+ 'Meetings:index#isNeedHandleMainLocus --> this is a normal main session locusDTO update case'
2108
+ );
2109
+ });
2110
+ it('joined breakout session, self status is moved, return false', () => {
2111
+ newLocus.controls.breakout = {
2112
+ sessionType: 'BREAKOUT',
2113
+ };
2114
+ newLocus.self.state = 'LEFT';
2115
+ newLocus.self.reason = 'MOVED';
2116
+
2117
+ LoggerProxy.logger.log = sinon.stub();
2118
+ const result = webex.meetings.isNeedHandleLocusDTO(meeting, newLocus);
2119
+ assert.equal(result, false);
2120
+ });
2121
+ });
2122
+
2123
+ describe('#getCorrespondingMeetingByLocus', () => {
2124
+ let locus;
2125
+ let mockReturnMeeting = {meeting: 'meeting1'};
2126
+ const mockGetByKey = (keyWillReturnMeeting) => {
2127
+ webex.meetings.meetingCollection.getByKey = sinon.stub().callsFake((key) => {
2128
+ if (key === keyWillReturnMeeting) {
2129
+ return mockReturnMeeting;
2130
+ }
2131
+ return null;
2132
+ });
2133
+ };
2134
+
2135
+ beforeEach(() => {
2136
+ locus = {
2137
+ controls: {},
2138
+ self: {
2139
+ callbackInfo: {
2140
+ callbackAddress: 'address1',
2141
+ },
2142
+ },
2143
+ info: {
2144
+ webExMeetingId: '123456',
2145
+ isUnifiedSpaceMeeting: false,
2146
+ },
2147
+ conversationUrl: 'conversationUrl1',
2148
+ };
2149
+
2150
+ sinon.stub(MeetingsUtil, 'checkForCorrelationId').returns('correlationId1');
2151
+ });
2152
+ afterEach(() => {
2153
+ sinon.restore();
2154
+ });
2155
+ it('check the calls when no meeting found in meetingCollection', () => {
2156
+ mockGetByKey();
2157
+ const result = webex.meetings.getCorrespondingMeetingByLocus({locus, locusUrl: url1});
2158
+ assert.isNull(result);
2159
+ assert.callCount(webex.meetings.meetingCollection.getByKey, 5);
2160
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'locusUrl', url1);
2161
+ assert.calledWith(
2162
+ webex.meetings.meetingCollection.getByKey,
2163
+ 'correlationId',
2164
+ 'correlationId1'
2165
+ );
2166
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'sipUri', 'address1');
2167
+ assert.calledWith(
2168
+ webex.meetings.meetingCollection.getByKey,
2169
+ 'conversationUrl',
2170
+ 'conversationUrl1'
2171
+ );
2172
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'meetingNumber', '123456');
2173
+ });
2174
+
2175
+ it('not try getByKey "conversationUrl" when isUnifiedSpaceMeeting is true', () => {
2176
+ mockGetByKey();
2177
+ locus.info.isUnifiedSpaceMeeting = true;
2178
+ const result = webex.meetings.getCorrespondingMeetingByLocus({locus, locusUrl: url1});
2179
+ assert.isNull(result);
2180
+ assert.callCount(webex.meetings.meetingCollection.getByKey, 4);
2181
+ });
2182
+
2183
+ it('check the calls when meeting found by key: locusUrl', () => {
2184
+ mockGetByKey('locusUrl');
2185
+ const result = webex.meetings.getCorrespondingMeetingByLocus({locus, locusUrl: url1});
2186
+ assert.deepEqual(result, mockReturnMeeting);
2187
+ assert.callCount(webex.meetings.meetingCollection.getByKey, 1);
2188
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'locusUrl', url1);
2189
+ });
2190
+
2191
+ it('check the calls when meeting found by key: correlationId', () => {
2192
+ mockGetByKey('correlationId');
2193
+ const result = webex.meetings.getCorrespondingMeetingByLocus({locus, locusUrl: url1});
2194
+ assert.deepEqual(result, mockReturnMeeting);
2195
+ assert.callCount(webex.meetings.meetingCollection.getByKey, 2);
2196
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'locusUrl', url1);
2197
+ assert.calledWith(
2198
+ webex.meetings.meetingCollection.getByKey,
2199
+ 'correlationId',
2200
+ 'correlationId1'
2201
+ );
2202
+ });
2203
+
2204
+ it('check the calls when meeting found by key: sipUri', () => {
2205
+ mockGetByKey('sipUri');
2206
+ const result = webex.meetings.getCorrespondingMeetingByLocus({locus, locusUrl: url1});
2207
+ assert.deepEqual(result, mockReturnMeeting);
2208
+ assert.callCount(webex.meetings.meetingCollection.getByKey, 3);
2209
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'locusUrl', url1);
2210
+ assert.calledWith(
2211
+ webex.meetings.meetingCollection.getByKey,
2212
+ 'correlationId',
2213
+ 'correlationId1'
2214
+ );
2215
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'sipUri', 'address1');
2216
+ });
2217
+
2218
+ it('check the calls when meeting found by key: conversationUrl', () => {
2219
+ mockGetByKey('conversationUrl');
2220
+ const result = webex.meetings.getCorrespondingMeetingByLocus({locus, locusUrl: url1});
2221
+ assert.deepEqual(result, mockReturnMeeting);
2222
+ assert.callCount(webex.meetings.meetingCollection.getByKey, 4);
2223
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'locusUrl', url1);
2224
+ assert.calledWith(
2225
+ webex.meetings.meetingCollection.getByKey,
2226
+ 'correlationId',
2227
+ 'correlationId1'
2228
+ );
2229
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'sipUri', 'address1');
2230
+ assert.calledWith(
2231
+ webex.meetings.meetingCollection.getByKey,
2232
+ 'conversationUrl',
2233
+ 'conversationUrl1'
2234
+ );
2235
+ });
2236
+
2237
+ it('check the calls when meeting found by key: meetingNumber', () => {
2238
+ mockGetByKey('meetingNumber');
2239
+ const result = webex.meetings.getCorrespondingMeetingByLocus({locus, locusUrl: url1});
2240
+ assert.deepEqual(result, mockReturnMeeting);
2241
+ assert.callCount(webex.meetings.meetingCollection.getByKey, 5);
2242
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'locusUrl', url1);
2243
+ assert.calledWith(
2244
+ webex.meetings.meetingCollection.getByKey,
2245
+ 'correlationId',
2246
+ 'correlationId1'
2247
+ );
2248
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'sipUri', 'address1');
2249
+ assert.calledWith(
2250
+ webex.meetings.meetingCollection.getByKey,
2251
+ 'conversationUrl',
2252
+ 'conversationUrl1'
2253
+ );
2254
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'meetingNumber', '123456');
2255
+ });
2256
+ });
2257
+
2258
+ describe('#sortLocusArrayToUpdate', () => {
2259
+ let lociArray;
2260
+ let mainLocus;
2261
+ let breakoutLocus;
2262
+ beforeEach(() => {
2263
+ mainLocus = {
2264
+ url: 'mainUrl1',
2265
+ controls: {
2266
+ breakout: {
2267
+ sessionType: 'MAIN',
2268
+ url: 'breakoutUnifiedUrl1',
2269
+ },
2270
+ },
2271
+ };
2272
+ breakoutLocus = {
2273
+ url: 'breakoutUrl1',
2274
+ controls: {
2275
+ breakout: {
2276
+ sessionType: 'BREAKOUT',
2277
+ url: 'breakoutUnifiedUrl1',
2278
+ },
2279
+ },
2280
+ };
2281
+ lociArray = [mainLocus, breakoutLocus];
2282
+
2283
+ sinon.stub(MeetingsUtil, 'isValidBreakoutLocus').callsFake((locus) => {
2284
+ return locus.url === 'breakoutUrl1';
2285
+ });
2286
+ });
2287
+ afterEach(() => {
2288
+ sinon.restore();
2289
+ });
2290
+
2291
+ it('if both main and breakout locus is in array for non-exist meeting, return main locus to create first', () => {
2292
+ webex.meetings.meetingCollection.getByKey = sinon.stub().returns(undefined);
2293
+ const result = webex.meetings.sortLocusArrayToUpdate(lociArray);
2294
+ assert.deepEqual(result, [mainLocus]);
2295
+ assert.deepEqual(webex.meetings.breakoutLocusForHandleLater, [breakoutLocus]);
2296
+ });
2297
+
2298
+ it('if both main and breakout locus is in array for an exist meeting, return all locus', () => {
2299
+ webex.meetings.meetingCollection.getByKey = sinon.stub().returns({});
2300
+ const result = webex.meetings.sortLocusArrayToUpdate(lociArray);
2301
+ assert.deepEqual(result, [mainLocus, breakoutLocus]);
2302
+ assert.deepEqual(webex.meetings.breakoutLocusForHandleLater, []);
2303
+ });
2304
+
2305
+ it('if the breakout locus has no associated main locus, return all', () => {
2306
+ webex.meetings.meetingCollection.getByKey = sinon.stub().returns({});
2307
+ breakoutLocus.controls.breakout.url = 'testUrl';
2308
+ const result = webex.meetings.sortLocusArrayToUpdate(lociArray);
2309
+ assert.deepEqual(result, [mainLocus, breakoutLocus]);
2310
+ });
2311
+ });
2312
+
2313
+ describe('#checkHandleBreakoutLocus', () => {
2314
+ let breakoutLocus;
2315
+ beforeEach(() => {
2316
+ breakoutLocus = {
2317
+ url: 'breakoutUrl1',
2318
+ controls: {
2319
+ breakout: {
2320
+ sessionType: 'BREAKOUT',
2321
+ url: 'breakoutUnifiedUrl1',
2322
+ },
2323
+ },
2324
+ };
2325
+
2326
+ webex.meetings.handleLocusEvent = sinon.stub();
2327
+ });
2328
+ afterEach(() => {
2329
+ sinon.restore();
2330
+ });
2331
+ it('do nothing if new created locus is null/no cached breakouts for updating', () => {
2332
+ webex.meetings.checkHandleBreakoutLocus(null);
2333
+ webex.meetings.breakoutLocusForHandleLater = null;
2334
+ webex.meetings.checkHandleBreakoutLocus({});
2335
+ webex.meetings.breakoutLocusForHandleLater = [];
2336
+ webex.meetings.checkHandleBreakoutLocus({});
2337
+ assert.notCalled(webex.meetings.handleLocusEvent);
2338
+ });
2339
+
2340
+ it('do nothing if new created locus is breakout locus', () => {
2341
+ webex.meetings.breakoutLocusForHandleLater = [breakoutLocus];
2342
+ webex.meetings.checkHandleBreakoutLocus(breakoutLocus);
2343
+ assert.notCalled(webex.meetings.handleLocusEvent);
2344
+ });
2345
+
2346
+ it('do nothing if no cached locus is associated with the new created locus', () => {
2347
+ webex.meetings.breakoutLocusForHandleLater = [breakoutLocus];
2348
+ webex.meetings.checkHandleBreakoutLocus({
2349
+ controls: {
2350
+ breakout: {
2351
+ sessionType: 'MAIN',
2352
+ url: 'breakoutUnifiedUrl2',
2353
+ },
2354
+ },
2355
+ });
2356
+ assert.notCalled(webex.meetings.handleLocusEvent);
2357
+ });
2358
+
2359
+ it('update the cached breakout locus which associate the new created locus', () => {
2360
+ webex.meetings.breakoutLocusForHandleLater = [breakoutLocus];
2361
+ webex.meetings.checkHandleBreakoutLocus({
2362
+ controls: {
2363
+ breakout: {
2364
+ sessionType: 'MAIN',
2365
+ url: 'breakoutUnifiedUrl1',
2366
+ },
2367
+ },
2368
+ });
2369
+ assert.calledWith(webex.meetings.handleLocusEvent, {
2370
+ locus: breakoutLocus,
2371
+ locusUrl: breakoutLocus.url,
2372
+ });
2373
+ });
2374
+ });
2375
+
2376
+ describe('uploading of logs', () => {
2377
+ let metricsSpy;
2378
+ let meeting;
2379
+
2380
+ beforeEach(async () => {
2381
+ webex.meetings.config.autoUploadLogs = true;
2382
+ webex.meetings.loggerRequest.uploadLogs = sinon.stub().resolves();
2383
+
2384
+ sinon.stub(webex.meetings.meetingInfo, 'fetchInfoOptions').resolves({});
2385
+ sinon.stub(webex.meetings.meetingInfo, 'fetchMeetingInfo').resolves({});
2386
+
2387
+ triggerProxyStub.restore();
2388
+
2389
+ metricsSpy = sinon.stub(Metrics, 'sendBehavioralMetric');
2390
+
2391
+ meeting = await webex.meetings.create('test');
2392
+
2393
+ meeting.locusId = 'locus id';
2394
+ meeting.correlationId = 'correlation id';
2395
+ meeting.locusInfo = {
2396
+ fullState: {lastActive: 'last active', sessionId: 'locus session id'},
2397
+ info: {webExMeetingId: 'meeting id'},
2398
+ };
2399
+ });
2400
+
2401
+ afterEach(() => {
2402
+ sinon.restore();
2403
+ });
2404
+
2405
+ it('sends metrics on success', async () => {
2406
+ await meeting.uploadLogs();
2407
+
2408
+ await testUtils.flushPromises();
2409
+
2410
+ assert.calledOnceWithExactly(metricsSpy, 'js_sdk_upload_logs_success', {
2411
+ callStart: 'last active',
2412
+ correlationId: 'correlation id',
2413
+ feedbackId: 'correlation id',
2414
+ locusId: 'locus id',
2415
+ meetingId: 'meeting id',
2416
+ autoupload: true,
2417
+ locussessionid: 'locus session id',
2418
+ });
2419
+ });
2420
+
2421
+ it('sends metrics on failure', async () => {
2422
+ webex.meetings.loggerRequest.uploadLogs.rejects(new Error('fake error'));
2423
+
2424
+ await meeting.uploadLogs();
2425
+
2426
+ await testUtils.flushPromises();
2427
+
2428
+ assert.calledOnceWithExactly(
2429
+ metricsSpy,
2430
+ 'js_sdk_upload_logs_failure',
2431
+ sinon.match({
2432
+ callStart: 'last active',
2433
+ correlationId: 'correlation id',
2434
+ feedbackId: 'correlation id',
2435
+ locusId: 'locus id',
2436
+ meetingId: 'meeting id',
2437
+ reason: 'fake error',
2438
+ autoupload: true,
2439
+ locussessionid: 'locus session id',
2440
+ })
2441
+ );
2442
+ });
2443
+ });
1374
2444
  });
1375
2445
  });