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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (539) hide show
  1. package/README.md +58 -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 +234 -100
  90. package/dist/constants.js +433 -444
  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 +705 -520
  159. package/dist/meeting/index.js +5047 -3089
  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 +304 -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 +118 -1
  176. package/dist/meeting/util.js +676 -435
  177. package/dist/meeting/util.js.map +1 -1
  178. package/dist/meeting/voicea-meeting.d.ts +20 -0
  179. package/dist/meeting/voicea-meeting.js +201 -0
  180. package/dist/meeting/voicea-meeting.js.map +1 -0
  181. package/dist/meeting-info/collection.js +3 -4
  182. package/dist/meeting-info/collection.js.map +1 -1
  183. package/dist/meeting-info/index.d.ts +13 -1
  184. package/dist/meeting-info/index.js +74 -7
  185. package/dist/meeting-info/index.js.map +1 -1
  186. package/dist/meeting-info/meeting-info-v2.d.ts +31 -1
  187. package/dist/meeting-info/meeting-info-v2.js +200 -63
  188. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  189. package/dist/meeting-info/request.js +1 -2
  190. package/dist/meeting-info/request.js.map +1 -1
  191. package/dist/meeting-info/util.js +2 -3
  192. package/dist/meeting-info/util.js.map +1 -1
  193. package/dist/meeting-info/utilv2.js +39 -41
  194. package/dist/meeting-info/utilv2.js.map +1 -1
  195. package/dist/meetings/collection.d.ts +17 -0
  196. package/dist/meetings/collection.js +42 -4
  197. package/dist/meetings/collection.js.map +1 -1
  198. package/dist/meetings/index.d.ts +114 -20
  199. package/dist/meetings/index.js +540 -126
  200. package/dist/meetings/index.js.map +1 -1
  201. package/dist/meetings/meetings.types.d.ts +4 -0
  202. package/dist/meetings/meetings.types.js +7 -0
  203. package/dist/meetings/meetings.types.js.map +1 -0
  204. package/dist/meetings/request.js +4 -3
  205. package/dist/meetings/request.js.map +1 -1
  206. package/dist/meetings/util.js +107 -6
  207. package/dist/meetings/util.js.map +1 -1
  208. package/dist/member/index.d.ts +13 -1
  209. package/dist/member/index.js +45 -2
  210. package/dist/member/index.js.map +1 -1
  211. package/dist/member/member.types.js +3 -4
  212. package/dist/member/member.types.js.map +1 -1
  213. package/dist/member/types.d.ts +32 -0
  214. package/dist/member/types.js +23 -0
  215. package/dist/member/types.js.map +1 -0
  216. package/dist/member/util.js +120 -29
  217. package/dist/member/util.js.map +1 -1
  218. package/dist/members/collection.d.ts +5 -0
  219. package/dist/members/collection.js +11 -2
  220. package/dist/members/collection.js.map +1 -1
  221. package/dist/members/index.d.ts +56 -11
  222. package/dist/members/index.js +174 -47
  223. package/dist/members/index.js.map +1 -1
  224. package/dist/members/request.d.ts +67 -11
  225. package/dist/members/request.js +102 -54
  226. package/dist/members/request.js.map +1 -1
  227. package/dist/members/types.js +3 -4
  228. package/dist/members/types.js.map +1 -1
  229. package/dist/members/util.d.ts +214 -1
  230. package/dist/members/util.js +327 -284
  231. package/dist/members/util.js.map +1 -1
  232. package/dist/metrics/constants.d.ts +15 -6
  233. package/dist/metrics/constants.js +17 -9
  234. package/dist/metrics/constants.js.map +1 -1
  235. package/dist/metrics/index.d.ts +4 -111
  236. package/dist/metrics/index.js +4 -452
  237. package/dist/metrics/index.js.map +1 -1
  238. package/dist/multistream/mediaRequestManager.d.ts +118 -0
  239. package/dist/multistream/mediaRequestManager.js +344 -0
  240. package/dist/multistream/mediaRequestManager.js.map +1 -0
  241. package/dist/multistream/receiveSlot.d.ts +68 -0
  242. package/dist/multistream/receiveSlot.js +200 -0
  243. package/dist/multistream/receiveSlot.js.map +1 -0
  244. package/dist/multistream/receiveSlotManager.d.ts +56 -0
  245. package/dist/multistream/receiveSlotManager.js +174 -0
  246. package/dist/multistream/receiveSlotManager.js.map +1 -0
  247. package/dist/multistream/remoteMedia.d.ts +72 -0
  248. package/dist/multistream/remoteMedia.js +268 -0
  249. package/dist/multistream/remoteMedia.js.map +1 -0
  250. package/dist/multistream/remoteMediaGroup.d.ts +47 -0
  251. package/dist/multistream/remoteMediaGroup.js +267 -0
  252. package/dist/multistream/remoteMediaGroup.js.map +1 -0
  253. package/dist/multistream/remoteMediaManager.d.ts +285 -0
  254. package/dist/multistream/remoteMediaManager.js +1211 -0
  255. package/dist/multistream/remoteMediaManager.js.map +1 -0
  256. package/dist/multistream/sendSlotManager.d.ts +61 -0
  257. package/dist/multistream/sendSlotManager.js +236 -0
  258. package/dist/multistream/sendSlotManager.js.map +1 -0
  259. package/dist/networkQualityMonitor/index.js +5 -4
  260. package/dist/networkQualityMonitor/index.js.map +1 -1
  261. package/dist/personal-meeting-room/index.js +2 -3
  262. package/dist/personal-meeting-room/index.js.map +1 -1
  263. package/dist/personal-meeting-room/request.js +2 -3
  264. package/dist/personal-meeting-room/request.js.map +1 -1
  265. package/dist/personal-meeting-room/util.js +1 -2
  266. package/dist/personal-meeting-room/util.js.map +1 -1
  267. package/dist/reachability/clusterReachability.d.ts +109 -0
  268. package/dist/reachability/clusterReachability.js +357 -0
  269. package/dist/reachability/clusterReachability.js.map +1 -0
  270. package/dist/reachability/index.d.ts +61 -95
  271. package/dist/reachability/index.js +304 -392
  272. package/dist/reachability/index.js.map +1 -1
  273. package/dist/reachability/request.d.ts +7 -3
  274. package/dist/reachability/request.js +18 -10
  275. package/dist/reachability/request.js.map +1 -1
  276. package/dist/reachability/util.d.ts +8 -0
  277. package/dist/reachability/util.js +29 -0
  278. package/dist/reachability/util.js.map +1 -0
  279. package/dist/reactions/constants.d.ts +3 -0
  280. package/dist/reactions/constants.js +12 -0
  281. package/dist/reactions/constants.js.map +1 -0
  282. package/dist/reactions/reactions.d.ts +2 -2
  283. package/dist/reactions/reactions.js +4 -6
  284. package/dist/reactions/reactions.js.map +1 -1
  285. package/dist/reactions/reactions.type.d.ts +23 -3
  286. package/dist/reactions/reactions.type.js +21 -23
  287. package/dist/reactions/reactions.type.js.map +1 -1
  288. package/dist/reconnection-manager/index.d.ts +32 -8
  289. package/dist/reconnection-manager/index.js +285 -232
  290. package/dist/reconnection-manager/index.js.map +1 -1
  291. package/dist/recording-controller/enums.js +4 -5
  292. package/dist/recording-controller/enums.js.map +1 -1
  293. package/dist/recording-controller/index.d.ts +15 -1
  294. package/dist/recording-controller/index.js +57 -46
  295. package/dist/recording-controller/index.js.map +1 -1
  296. package/dist/recording-controller/util.d.ts +5 -4
  297. package/dist/recording-controller/util.js +10 -10
  298. package/dist/recording-controller/util.js.map +1 -1
  299. package/dist/roap/index.d.ts +9 -47
  300. package/dist/roap/index.js +100 -238
  301. package/dist/roap/index.js.map +1 -1
  302. package/dist/roap/request.d.ts +18 -12
  303. package/dist/roap/request.js +126 -180
  304. package/dist/roap/request.js.map +1 -1
  305. package/dist/roap/turnDiscovery.d.ts +27 -16
  306. package/dist/roap/turnDiscovery.js +115 -105
  307. package/dist/roap/turnDiscovery.js.map +1 -1
  308. package/dist/rtcMetrics/constants.d.ts +4 -0
  309. package/dist/rtcMetrics/constants.js +11 -0
  310. package/dist/rtcMetrics/constants.js.map +1 -0
  311. package/dist/rtcMetrics/index.d.ts +54 -0
  312. package/dist/rtcMetrics/index.js +140 -0
  313. package/dist/rtcMetrics/index.js.map +1 -0
  314. package/dist/statsAnalyzer/global.d.ts +1 -83
  315. package/dist/statsAnalyzer/global.js +2 -85
  316. package/dist/statsAnalyzer/global.js.map +1 -1
  317. package/dist/statsAnalyzer/index.d.ts +50 -30
  318. package/dist/statsAnalyzer/index.js +436 -511
  319. package/dist/statsAnalyzer/index.js.map +1 -1
  320. package/dist/statsAnalyzer/mqaUtil.d.ts +8 -6
  321. package/dist/statsAnalyzer/mqaUtil.js +130 -90
  322. package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
  323. package/dist/transcription/index.js +1 -2
  324. package/dist/transcription/index.js.map +1 -1
  325. package/dist/webinar/collection.d.ts +16 -0
  326. package/dist/webinar/collection.js +43 -0
  327. package/dist/webinar/collection.js.map +1 -0
  328. package/dist/webinar/index.d.ts +5 -0
  329. package/dist/webinar/index.js +68 -0
  330. package/dist/webinar/index.js.map +1 -0
  331. package/package.json +39 -26
  332. package/src/annotation/annotation.types.ts +50 -0
  333. package/src/annotation/constants.ts +36 -0
  334. package/src/annotation/index.ts +328 -0
  335. package/src/breakouts/README.md +220 -0
  336. package/src/breakouts/breakout.ts +188 -0
  337. package/src/breakouts/collection.ts +19 -0
  338. package/src/breakouts/edit-lock-error.ts +25 -0
  339. package/src/breakouts/events.ts +56 -0
  340. package/src/breakouts/index.ts +925 -0
  341. package/src/breakouts/request.ts +55 -0
  342. package/src/breakouts/utils.ts +57 -0
  343. package/src/common/errors/no-meeting-info.ts +24 -0
  344. package/src/common/errors/webex-errors.ts +36 -12
  345. package/src/common/logs/logger-proxy.ts +1 -1
  346. package/src/common/logs/request.ts +5 -1
  347. package/src/common/queue.ts +22 -8
  348. package/src/config.ts +6 -7
  349. package/src/constants.ts +265 -100
  350. package/src/controls-options-manager/enums.ts +12 -0
  351. package/src/controls-options-manager/index.ts +116 -21
  352. package/src/controls-options-manager/types.ts +59 -0
  353. package/src/controls-options-manager/util.ts +294 -14
  354. package/src/index.ts +44 -0
  355. package/src/interceptors/index.ts +3 -0
  356. package/src/interceptors/locusRetry.ts +67 -0
  357. package/src/interpretation/README.md +60 -0
  358. package/src/interpretation/collection.ts +19 -0
  359. package/src/interpretation/index.ts +332 -0
  360. package/src/interpretation/siLanguage.ts +18 -0
  361. package/src/locus-info/controlsUtils.ts +110 -0
  362. package/src/locus-info/index.ts +450 -61
  363. package/src/locus-info/infoUtils.ts +14 -2
  364. package/src/locus-info/mediaSharesUtils.ts +64 -0
  365. package/src/locus-info/parser.ts +258 -47
  366. package/src/locus-info/selfUtils.ts +85 -2
  367. package/src/media/index.ts +153 -370
  368. package/src/media/properties.ts +106 -136
  369. package/src/media/util.ts +0 -21
  370. package/src/mediaQualityMetrics/config.ts +244 -377
  371. package/src/meeting/in-meeting-actions.ts +176 -0
  372. package/src/meeting/index.ts +4306 -2581
  373. package/src/meeting/locusMediaRequest.ts +313 -0
  374. package/src/meeting/muteState.ts +224 -138
  375. package/src/meeting/request.ts +214 -127
  376. package/src/meeting/request.type.ts +13 -0
  377. package/src/meeting/util.ts +687 -423
  378. package/src/meeting/voicea-meeting.ts +161 -0
  379. package/src/meeting-info/index.ts +81 -8
  380. package/src/meeting-info/meeting-info-v2.ts +163 -13
  381. package/src/meeting-info/util.ts +1 -1
  382. package/src/meeting-info/utilv2.ts +28 -28
  383. package/src/meetings/collection.ts +33 -0
  384. package/src/meetings/index.ts +529 -127
  385. package/src/meetings/meetings.types.ts +12 -0
  386. package/src/meetings/request.ts +2 -0
  387. package/src/meetings/util.ts +116 -5
  388. package/src/member/index.ts +43 -1
  389. package/src/member/types.ts +38 -0
  390. package/src/member/util.ts +125 -28
  391. package/src/members/collection.ts +8 -0
  392. package/src/members/index.ts +187 -52
  393. package/src/members/request.ts +87 -27
  394. package/src/members/util.ts +332 -291
  395. package/src/metrics/constants.ts +15 -6
  396. package/src/metrics/index.ts +1 -471
  397. package/src/multistream/mediaRequestManager.ts +440 -0
  398. package/src/multistream/receiveSlot.ts +184 -0
  399. package/src/multistream/receiveSlotManager.ts +166 -0
  400. package/src/multistream/remoteMedia.ts +254 -0
  401. package/src/multistream/remoteMediaGroup.ts +284 -0
  402. package/src/multistream/remoteMediaManager.ts +1145 -0
  403. package/src/multistream/sendSlotManager.ts +170 -0
  404. package/src/networkQualityMonitor/index.ts +6 -6
  405. package/src/reachability/clusterReachability.ts +320 -0
  406. package/src/reachability/index.ts +246 -347
  407. package/src/reachability/request.ts +17 -8
  408. package/src/reachability/util.ts +24 -0
  409. package/src/reactions/constants.ts +4 -0
  410. package/src/reactions/reactions.ts +4 -4
  411. package/src/reactions/reactions.type.ts +30 -4
  412. package/src/reconnection-manager/index.ts +168 -156
  413. package/src/recording-controller/index.ts +20 -3
  414. package/src/recording-controller/util.ts +26 -9
  415. package/src/roap/index.ts +96 -241
  416. package/src/roap/request.ts +74 -148
  417. package/src/roap/turnDiscovery.ts +62 -56
  418. package/src/rtcMetrics/constants.ts +3 -0
  419. package/src/rtcMetrics/index.ts +124 -0
  420. package/src/statsAnalyzer/global.ts +1 -84
  421. package/src/statsAnalyzer/index.ts +479 -645
  422. package/src/statsAnalyzer/mqaUtil.ts +128 -126
  423. package/src/webinar/collection.ts +31 -0
  424. package/src/webinar/index.ts +62 -0
  425. package/test/integration/spec/converged-space-meetings.js +233 -0
  426. package/test/integration/spec/journey.js +320 -264
  427. package/test/integration/spec/space-meeting.js +77 -4
  428. package/test/unit/spec/annotation/index.ts +418 -0
  429. package/test/unit/spec/breakouts/breakout.ts +237 -0
  430. package/test/unit/spec/breakouts/collection.ts +15 -0
  431. package/test/unit/spec/breakouts/edit-lock-error.ts +30 -0
  432. package/test/unit/spec/breakouts/events.ts +89 -0
  433. package/test/unit/spec/breakouts/index.ts +1790 -0
  434. package/test/unit/spec/breakouts/request.ts +104 -0
  435. package/test/unit/spec/breakouts/utils.js +72 -0
  436. package/test/unit/spec/common/queue.js +31 -2
  437. package/test/unit/spec/controls-options-manager/index.js +163 -0
  438. package/test/unit/spec/controls-options-manager/util.js +576 -60
  439. package/test/unit/spec/fixture/locus.js +1 -0
  440. package/test/unit/spec/interceptors/locusRetry.ts +131 -0
  441. package/test/unit/spec/interpretation/collection.ts +15 -0
  442. package/test/unit/spec/interpretation/index.ts +589 -0
  443. package/test/unit/spec/interpretation/siLanguage.ts +28 -0
  444. package/test/unit/spec/locus-info/controlsUtils.js +323 -30
  445. package/test/unit/spec/locus-info/index.js +1438 -16
  446. package/test/unit/spec/locus-info/infoUtils.js +54 -16
  447. package/test/unit/spec/locus-info/lib/SeqCmp.json +16 -0
  448. package/test/unit/spec/locus-info/lib/selfConstant.js +48 -0
  449. package/test/unit/spec/locus-info/mediaSharesUtils.ts +32 -0
  450. package/test/unit/spec/locus-info/parser.js +116 -35
  451. package/test/unit/spec/locus-info/selfUtils.js +275 -0
  452. package/test/unit/spec/media/index.ts +290 -0
  453. package/test/unit/spec/media/properties.ts +75 -84
  454. package/test/unit/spec/meeting/in-meeting-actions.ts +86 -0
  455. package/test/unit/spec/meeting/index.js +8886 -2815
  456. package/test/unit/spec/meeting/locusMediaRequest.ts +442 -0
  457. package/test/unit/spec/meeting/muteState.js +409 -213
  458. package/test/unit/spec/meeting/request.js +523 -43
  459. package/test/unit/spec/meeting/utils.js +834 -24
  460. package/test/unit/spec/meeting-info/index.js +300 -0
  461. package/test/unit/spec/meeting-info/meetinginfov2.js +527 -5
  462. package/test/unit/spec/meeting-info/utilv2.js +21 -0
  463. package/test/unit/spec/meetings/collection.js +26 -0
  464. package/test/unit/spec/meetings/index.js +1446 -217
  465. package/test/unit/spec/meetings/utils.js +202 -2
  466. package/test/unit/spec/member/index.js +32 -9
  467. package/test/unit/spec/member/util.js +499 -61
  468. package/test/unit/spec/members/index.js +394 -5
  469. package/test/unit/spec/members/request.js +206 -27
  470. package/test/unit/spec/members/utils.js +173 -38
  471. package/test/unit/spec/metrics/index.js +1 -50
  472. package/test/unit/spec/multistream/mediaRequestManager.ts +1418 -0
  473. package/test/unit/spec/multistream/receiveSlot.ts +163 -0
  474. package/test/unit/spec/multistream/receiveSlotManager.ts +203 -0
  475. package/test/unit/spec/multistream/remoteMedia.ts +255 -0
  476. package/test/unit/spec/multistream/remoteMediaGroup.ts +662 -0
  477. package/test/unit/spec/multistream/remoteMediaManager.ts +1924 -0
  478. package/test/unit/spec/multistream/sendSlotManager.ts +242 -0
  479. package/test/unit/spec/networkQualityMonitor/index.js +4 -4
  480. package/test/unit/spec/reachability/clusterReachability.ts +279 -0
  481. package/test/unit/spec/reachability/index.ts +532 -24
  482. package/test/unit/spec/reachability/request.js +68 -0
  483. package/test/unit/spec/reachability/util.ts +40 -0
  484. package/test/unit/spec/reconnection-manager/index.js +163 -24
  485. package/test/unit/spec/recording-controller/index.js +293 -218
  486. package/test/unit/spec/recording-controller/util.js +223 -96
  487. package/test/unit/spec/roap/index.ts +187 -77
  488. package/test/unit/spec/roap/request.ts +255 -0
  489. package/test/unit/spec/roap/turnDiscovery.ts +86 -48
  490. package/test/unit/spec/rtcMetrics/index.ts +93 -0
  491. package/test/unit/spec/stats-analyzer/index.js +644 -165
  492. package/test/unit/spec/webinar/collection.ts +13 -0
  493. package/test/unit/spec/webinar/index.ts +60 -0
  494. package/test/utils/constants.js +9 -0
  495. package/test/utils/integrationTestUtils.js +46 -0
  496. package/test/utils/testUtils.js +0 -45
  497. package/test/utils/webex-config.js +4 -0
  498. package/test/utils/webex-test-users.js +7 -3
  499. package/dist/common/errors/reclaim-host-role-error.js.map +0 -1
  500. package/dist/meeting/effectsState.d.ts +0 -42
  501. package/dist/meeting/effectsState.js +0 -260
  502. package/dist/meeting/effectsState.js.map +0 -1
  503. package/dist/metrics/config.d.ts +0 -169
  504. package/dist/metrics/config.js +0 -289
  505. package/dist/metrics/config.js.map +0 -1
  506. package/dist/peer-connection-manager/index.d.ts +0 -6
  507. package/dist/peer-connection-manager/index.js +0 -671
  508. package/dist/peer-connection-manager/index.js.map +0 -1
  509. package/dist/peer-connection-manager/util.d.ts +0 -6
  510. package/dist/peer-connection-manager/util.js +0 -110
  511. package/dist/peer-connection-manager/util.js.map +0 -1
  512. package/dist/roap/collection.d.ts +0 -10
  513. package/dist/roap/collection.js +0 -63
  514. package/dist/roap/collection.js.map +0 -1
  515. package/dist/roap/handler.d.ts +0 -47
  516. package/dist/roap/handler.js +0 -279
  517. package/dist/roap/handler.js.map +0 -1
  518. package/dist/roap/state.d.ts +0 -9
  519. package/dist/roap/state.js +0 -127
  520. package/dist/roap/state.js.map +0 -1
  521. package/dist/roap/util.d.ts +0 -2
  522. package/dist/roap/util.js +0 -76
  523. package/dist/roap/util.js.map +0 -1
  524. package/src/index.js +0 -15
  525. package/src/meeting/effectsState.ts +0 -209
  526. package/src/metrics/config.ts +0 -485
  527. package/src/peer-connection-manager/index.ts +0 -847
  528. package/src/peer-connection-manager/util.ts +0 -119
  529. package/src/roap/collection.ts +0 -62
  530. package/src/roap/handler.ts +0 -294
  531. package/src/roap/state.ts +0 -156
  532. package/src/roap/util.ts +0 -100
  533. package/test/unit/spec/meeting/effectsState.js +0 -281
  534. package/test/unit/spec/peerconnection-manager/index.js +0 -218
  535. package/test/unit/spec/peerconnection-manager/utils.js +0 -49
  536. package/test/unit/spec/peerconnection-manager/utils.test-fixtures.ts +0 -388
  537. package/test/unit/spec/roap/util.js +0 -30
  538. /package/dist/common/errors/{reclaim-host-role-error.d.ts → reclaim-host-role-errors.d.ts} +0 -0
  539. /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,15 +15,16 @@ 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
- import Meeting from '@webex/plugin-meetings/src/meeting';
20
+ import Meeting, {CallStateForMetrics} 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';
19
23
  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,30 +240,15 @@ 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');
243
+ describe('#_toggleTcpReachability', () => {
244
+ it('should have _toggleTcpReachability', () => {
245
+ assert.equal(typeof webex.meetings._toggleTcpReachability, 'function');
223
246
  });
224
247
 
225
248
  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
- );
249
+ it('should update meetings to do TCP reachability', () => {
250
+ webex.meetings._toggleTcpReachability(true);
251
+ assert.equal(webex.meetings.config.experimental.enableTcpReachability, true);
244
252
  });
245
253
  });
246
254
  });
@@ -338,37 +346,110 @@ describe('plugin-meetings', () => {
338
346
  });
339
347
  });
340
348
 
349
+ describe('virtual background effect', () => {
350
+ beforeEach(() => {
351
+ webex.credentials = {
352
+ supertoken: {
353
+ access_token: 'fake_token',
354
+ },
355
+ };
356
+ });
357
+
358
+ it('creates background effect', async () => {
359
+ const result = await webex.meetings.createVirtualBackgroundEffect();
360
+
361
+ assert.exists(result);
362
+ assert.instanceOf(result, VirtualBackgroundEffect);
363
+ assert.containsAllKeys(result, ['loadModel', 'isEnabled', 'options']);
364
+ assert.deepEqual(result.options, {
365
+ mode: 'BLUR',
366
+ blurStrength: 'STRONG',
367
+ generator: 'worker',
368
+ quality: 'LOW',
369
+ authToken: 'fake_token',
370
+ mirror: false,
371
+ });
372
+ assert.exists(result.enable);
373
+ assert.exists(result.disable);
374
+ assert.exists(result.dispose);
375
+ });
376
+
377
+ it('creates background effect with custom options passed', async () => {
378
+ const effectOptions = {
379
+ generator: 'local',
380
+ frameRate: 45,
381
+ mode: 'IMAGE',
382
+ mirror: false,
383
+ quality: 'HIGH',
384
+ blurStrength: 'STRONG',
385
+ bgImageUrl: 'https://test.webex.com/landscape.5a535788.jpg',
386
+ };
387
+
388
+ const result = await webex.meetings.createVirtualBackgroundEffect(effectOptions);
389
+
390
+ assert.exists(result);
391
+ assert.instanceOf(result, VirtualBackgroundEffect);
392
+ assert.containsAllKeys(result, ['loadModel', 'isEnabled', 'options']);
393
+ assert.deepEqual(result.options, {...effectOptions, authToken: 'fake_token'});
394
+ assert.exists(result.enable);
395
+ assert.exists(result.disable);
396
+ assert.exists(result.dispose);
397
+ });
398
+ });
399
+
400
+ describe('noise reduction effect', () => {
401
+ beforeEach(() => {
402
+ webex.credentials = {
403
+ supertoken: {
404
+ access_token: 'fake_token',
405
+ },
406
+ };
407
+ });
408
+
409
+ it('creates noise reduction effect', async () => {
410
+ const result = await webex.meetings.createNoiseReductionEffect({audioContext: {}});
411
+
412
+ assert.exists(result);
413
+ assert.instanceOf(result, NoiseReductionEffect);
414
+ assert.containsAllKeys(result, ['audioContext', 'isEnabled', 'isReady', 'options']);
415
+ assert.deepEqual(result.options, {
416
+ authToken: 'fake_token',
417
+ audioContext: {},
418
+ });
419
+ assert.exists(result.enable);
420
+ assert.exists(result.disable);
421
+ assert.exists(result.dispose);
422
+ });
423
+
424
+ it('creates noise reduction effect with custom options passed', async () => {
425
+ const effectOptions = {
426
+ audioContext: {},
427
+ mode: 'WORKLET',
428
+ env: 'prod',
429
+ };
430
+
431
+ const result = await webex.meetings.createNoiseReductionEffect(effectOptions);
432
+
433
+ assert.exists(result);
434
+ assert.instanceOf(result, NoiseReductionEffect);
435
+ assert.containsAllKeys(result, ['audioContext', 'isEnabled', 'isReady', 'options']);
436
+ assert.deepEqual(result.options, {...effectOptions, authToken: 'fake_token'});
437
+ assert.exists(result.enable);
438
+ assert.exists(result.disable);
439
+ assert.exists(result.dispose);
440
+ });
441
+ });
442
+
341
443
  describe('gets', () => {
342
444
  describe('#getReachability', () => {
343
445
  it('should have #getReachability', () => {
344
446
  assert.exists(webex.meetings.getReachability);
345
447
  });
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();
448
+ it('gets the reachability data instance from webex.meetings', () => {
449
+ const reachability = webex.meetings.getReachability();
365
450
 
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
- });
451
+ assert.exists(reachability, 'reachability is defined');
452
+ assert.instanceOf(reachability, Reachability, 'should be a reachability instance');
372
453
  });
373
454
  });
374
455
  describe('#getPersonalMeetingRoom', () => {
@@ -443,21 +524,16 @@ describe('plugin-meetings', () => {
443
524
  );
444
525
  });
445
526
  describe('when meeting is returned', () => {
446
- let parse;
447
-
448
527
  beforeEach(() => {
449
- parse = sinon.stub().returns(true);
450
528
  webex.meetings.meetingCollection.getByKey = sinon.stub().returns({
451
- locusInfo: {
452
- parse,
453
- },
529
+ locusInfo,
454
530
  });
455
531
  });
456
532
  it('tests the sync meeting calls for existing meeting', async () => {
457
533
  await webex.meetings.syncMeetings();
458
534
  assert.calledOnce(webex.meetings.request.getActiveMeetings);
459
535
  assert.calledOnce(webex.meetings.meetingCollection.getByKey);
460
- assert.calledOnce(parse);
536
+ assert.calledOnce(locusInfo.parse);
461
537
  assert.calledWith(webex.meetings.meetingCollection.getByKey, 'locusUrl', url1);
462
538
  });
463
539
  });
@@ -470,6 +546,7 @@ describe('plugin-meetings', () => {
470
546
  webex.meetings.create = sinon.stub().returns(
471
547
  Promise.resolve({
472
548
  locusInfo: {
549
+ ...locusInfo,
473
550
  initialSetup,
474
551
  },
475
552
  })
@@ -478,7 +555,7 @@ describe('plugin-meetings', () => {
478
555
  it('tests the sync meeting calls for not existing meeting', async () => {
479
556
  await webex.meetings.syncMeetings();
480
557
  assert.calledOnce(webex.meetings.request.getActiveMeetings);
481
- assert.callCount(webex.meetings.meetingCollection.getByKey, 4);
558
+ assert.callCount(webex.meetings.meetingCollection.getByKey, 5);
482
559
  assert.calledOnce(initialSetup);
483
560
  assert.calledOnce(webex.meetings.create);
484
561
  assert.calledWith(webex.meetings.request.getActiveMeetings);
@@ -495,46 +572,71 @@ describe('plugin-meetings', () => {
495
572
  });
496
573
  });
497
574
  });
498
- describe('destory non active meeting', () => {
499
- let initialSetup;
500
- let parse;
575
+ describe('when destroying meeting is needed', () => {
501
576
  let destroySpy;
502
577
 
578
+ const meetingCollectionMeetings = {
579
+ stillValidLocusMeeting: {
580
+ locusUrl: 'still-valid-locus-url',
581
+ sendCallAnalyzerMetrics: sinon.stub(),
582
+ },
583
+ noLongerValidLocusMeeting: {
584
+ locusUrl: 'no-longer-valid-locus-url',
585
+ sendCallAnalyzerMetrics: sinon.stub(),
586
+ },
587
+ otherNonLocusMeeting1: {
588
+ locusUrl: null,
589
+ sendCallAnalyzerMetrics: sinon.stub(),
590
+ },
591
+ otherNonLocusMeeting2: {
592
+ locusUrl: undefined,
593
+ sendCallAnalyzerMetrics: sinon.stub(),
594
+ },
595
+ };
596
+
503
597
  beforeEach(() => {
504
598
  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
- );
599
+ webex.meetings.meetingCollection.getAll = sinon
600
+ .stub()
601
+ .returns(meetingCollectionMeetings);
527
602
  webex.meetings.request.getActiveMeetings = sinon.stub().returns(
528
603
  Promise.resolve({
529
- loci: [],
604
+ loci: [{url: 'still-valid-locus-url'}],
530
605
  })
531
606
  );
532
607
  MeetingUtil.cleanUp = sinon.stub().returns(Promise.resolve());
533
608
  });
534
- it('destroy non active meetings', async () => {
609
+
610
+ it('destroy any meeting that has no active locus url if keepOnlyLocusMeetings is not defined', async () => {
535
611
  await webex.meetings.syncMeetings();
536
612
  assert.calledOnce(webex.meetings.request.getActiveMeetings);
537
- assert.calledOnce(destroySpy);
613
+ assert.calledOnce(webex.meetings.meetingCollection.getAll);
614
+ assert.calledWith(destroySpy, meetingCollectionMeetings.noLongerValidLocusMeeting);
615
+ assert.calledWith(destroySpy, meetingCollectionMeetings.otherNonLocusMeeting1);
616
+ assert.calledWith(destroySpy, meetingCollectionMeetings.otherNonLocusMeeting2);
617
+ assert.callCount(destroySpy, 3);
618
+
619
+ assert.callCount(MeetingUtil.cleanUp, 3);
620
+ });
621
+
622
+ it('destroy any meeting that has no active locus url if keepOnlyLocusMeetings === true', async () => {
623
+ await webex.meetings.syncMeetings({keepOnlyLocusMeetings: true});
624
+ assert.calledOnce(webex.meetings.request.getActiveMeetings);
625
+ assert.calledOnce(webex.meetings.meetingCollection.getAll);
626
+ assert.calledWith(destroySpy, meetingCollectionMeetings.noLongerValidLocusMeeting);
627
+ assert.calledWith(destroySpy, meetingCollectionMeetings.otherNonLocusMeeting1);
628
+ assert.calledWith(destroySpy, meetingCollectionMeetings.otherNonLocusMeeting2);
629
+ assert.callCount(destroySpy, 3);
630
+
631
+ assert.callCount(MeetingUtil.cleanUp, 3);
632
+ });
633
+
634
+ it('destroy any LOCUS meetings that have no active locus url if keepOnlyLocusMeetings === false', async () => {
635
+ await webex.meetings.syncMeetings({keepOnlyLocusMeetings: false});
636
+ assert.calledOnce(webex.meetings.request.getActiveMeetings);
637
+ assert.calledOnce(webex.meetings.meetingCollection.getAll);
638
+ assert.calledWith(destroySpy, meetingCollectionMeetings.noLongerValidLocusMeeting);
639
+ assert.callCount(destroySpy, 1);
538
640
 
539
641
  assert.calledOnce(MeetingUtil.cleanUp);
540
642
  });
@@ -574,14 +676,139 @@ describe('plugin-meetings', () => {
574
676
  });
575
677
  });
576
678
 
679
+ const FAKE_USE_RANDOM_DELAY = true;
680
+ const correlationId = 'my-correlationId';
681
+ const callStateForMetrics = {
682
+ correlationId: 'my-correlationId2',
683
+ joinTrigger: 'my-join-trigger',
684
+ loginType: 'my-login-type',
685
+ };
686
+
687
+ it('should call setCallStateForMetrics on any pre-existing meeting', async () => {
688
+ const fakeMeeting = {setCallStateForMetrics: sinon.mock()};
689
+ webex.meetings.meetingCollection.getByKey = sinon.stub().returns(fakeMeeting);
690
+ await webex.meetings.create(
691
+ test1,
692
+ test2,
693
+ FAKE_USE_RANDOM_DELAY,
694
+ {},
695
+ correlationId,
696
+ true,
697
+ callStateForMetrics
698
+ );
699
+ assert.calledOnceWithExactly(fakeMeeting.setCallStateForMetrics, {
700
+ ...callStateForMetrics,
701
+ correlationId,
702
+ });
703
+ });
704
+
705
+ const checkCallCreateMeeting = async (createParameters, createMeetingParameters) => {
706
+ const create = webex.meetings.create(...createParameters);
707
+
708
+ assert.exists(create.then);
709
+ await create;
710
+ assert.calledOnce(webex.meetings.createMeeting);
711
+ assert.calledWith(webex.meetings.createMeeting, ...createMeetingParameters);
712
+ };
713
+
577
714
  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);
715
+ await checkCallCreateMeeting(
716
+ [test1, test2, FAKE_USE_RANDOM_DELAY, {}, correlationId, true],
717
+ [test1, test2, FAKE_USE_RANDOM_DELAY, {}, {correlationId}, true]
718
+ );
719
+ });
720
+
721
+ it('calls createMeeting, pass the meeting info param and returns its promise', async () => {
722
+ const meetingInfo = {};
723
+ await checkCallCreateMeeting(
724
+ [test1, test2, FAKE_USE_RANDOM_DELAY, {}, correlationId, true, undefined, meetingInfo],
725
+ [test1, test2, FAKE_USE_RANDOM_DELAY, {}, {correlationId}, true, meetingInfo]
726
+ );
727
+ });
728
+
729
+ it('calls createMeeting, pass the meeting info and meetingLookupURL param and returns its promise', async () => {
730
+ const meetingInfo = {};
731
+ await checkCallCreateMeeting(
732
+ [
733
+ test1,
734
+ test2,
735
+ FAKE_USE_RANDOM_DELAY,
736
+ {},
737
+ correlationId,
738
+ true,
739
+ undefined,
740
+ meetingInfo,
741
+ 'meetingLookupURL',
742
+ ],
743
+ [
744
+ test1,
745
+ test2,
746
+ FAKE_USE_RANDOM_DELAY,
747
+ {},
748
+ {correlationId},
749
+ true,
750
+ meetingInfo,
751
+ 'meetingLookupURL',
752
+ ]
753
+ );
754
+ });
755
+
756
+ it('calls createMeeting when failOnMissingMeetinginfo is undefined and returns its promise', async () => {
757
+ await checkCallCreateMeeting(
758
+ [test1, test2, FAKE_USE_RANDOM_DELAY, {}, correlationId, undefined],
759
+ [test1, test2, FAKE_USE_RANDOM_DELAY, {}, {correlationId}, false]
760
+ );
761
+ });
762
+
763
+ it('calls createMeeting when failOnMissingMeetinginfo is false and returns its promise', async () => {
764
+ await checkCallCreateMeeting(
765
+ [test1, test2, FAKE_USE_RANDOM_DELAY, {}, correlationId, false],
766
+ [test1, test2, FAKE_USE_RANDOM_DELAY, {}, {correlationId}, false]
767
+ );
768
+ });
769
+
770
+ it('calls createMeeting with callStateForMetrics and returns its promise', async () => {
771
+ await checkCallCreateMeeting(
772
+ [test1, test2, FAKE_USE_RANDOM_DELAY, {}, undefined, true, callStateForMetrics],
773
+ [test1, test2, FAKE_USE_RANDOM_DELAY, {}, callStateForMetrics, true]
774
+ );
775
+ });
776
+
777
+ it('calls createMeeting with callStateForMetrics overwritten with correlationId and returns its promise', async () => {
778
+ await checkCallCreateMeeting(
779
+ [test1, test2, FAKE_USE_RANDOM_DELAY, {}, correlationId, true, callStateForMetrics],
780
+ [test1, test2, FAKE_USE_RANDOM_DELAY, {}, {...callStateForMetrics, correlationId}, true]
781
+ );
782
+ });
783
+
784
+ it('calls createMeeting with extra info params and returns its promise', async () => {
785
+ const FAKE_USE_RANDOM_DELAY = false;
786
+ const correlationId = 'my-correlationId';
787
+
788
+ const FAKE_INFO_EXTRA_PARAMS = {
789
+ mtid: 'm9fe0afd8c435e892afcce9ea25b97046',
790
+ joinTXId: 'TSmrX61wNF',
791
+ };
792
+ const create = webex.meetings.create(
793
+ test1,
794
+ test2,
795
+ FAKE_USE_RANDOM_DELAY,
796
+ FAKE_INFO_EXTRA_PARAMS,
797
+ correlationId
798
+ );
580
799
 
581
800
  assert.exists(create.then);
582
801
  await create;
583
802
  assert.calledOnce(webex.meetings.createMeeting);
584
- assert.calledWith(webex.meetings.createMeeting, test1, test2, FAKE_USE_RANDOM_DELAY);
803
+ assert.calledWith(
804
+ webex.meetings.createMeeting,
805
+ test1,
806
+ test2,
807
+ FAKE_USE_RANDOM_DELAY,
808
+ FAKE_INFO_EXTRA_PARAMS,
809
+ {correlationId},
810
+ false
811
+ );
585
812
  });
586
813
 
587
814
  it('creates a new meeting when a scheduled meeting exists in the conversation', async () => {
@@ -677,45 +904,51 @@ describe('plugin-meetings', () => {
677
904
  });
678
905
  describe('#handleLocusEvent', () => {
679
906
  describe('there was a meeting', () => {
680
- let parse;
681
-
682
907
  beforeEach(() => {
683
- parse = sinon.stub().returns(true);
684
908
  webex.meetings.meetingCollection.getByKey = sinon.stub().returns({
685
- locusInfo: {
686
- parse,
687
- },
909
+ locusInfo,
688
910
  });
689
911
  });
690
- it('should parse the meeting info', () => {
912
+ it('should parse the meeting info and update main session locus cache', () => {
913
+ sinon.stub(MeetingsUtil, 'isBreakoutLocusDTO').returns(false);
691
914
  webex.meetings.handleLocusEvent({
692
915
  locusUrl: url1,
693
916
  });
694
917
  assert.calledOnce(webex.meetings.meetingCollection.getByKey);
695
918
  assert.calledWith(webex.meetings.meetingCollection.getByKey, 'locusUrl', url1);
696
- assert.calledOnce(parse);
919
+ assert.calledOnce(locusInfo.parse);
920
+ assert.calledOnce(locusInfo.updateMainSessionLocusCache);
697
921
  assert.calledWith(
698
- parse,
922
+ locusInfo.parse,
699
923
  {
700
- locusInfo: {
701
- parse,
702
- },
924
+ locusInfo,
703
925
  },
704
926
  {
705
927
  locusUrl: url1,
706
928
  }
707
929
  );
708
930
  });
931
+
932
+ it('should not update main session locus cache', () => {
933
+ sinon.stub(MeetingsUtil, 'isBreakoutLocusDTO').returns(true);
934
+ webex.meetings.handleLocusEvent({
935
+ locusUrl: url1,
936
+ });
937
+ assert.notCalled(locusInfo.updateMainSessionLocusCache);
938
+ });
709
939
  });
710
940
  describe('there was not a meeting', () => {
711
941
  let initialSetup;
942
+ const webExMeetingId = '123456';
712
943
 
713
944
  beforeEach(() => {
714
945
  initialSetup = sinon.stub().returns(true);
715
946
  webex.meetings.meetingCollection.getByKey = sinon.stub().returns(undefined);
716
947
  webex.meetings.create = sinon.stub().returns(
717
948
  Promise.resolve({
949
+ id: 'meeting-id',
718
950
  locusInfo: {
951
+ ...locusInfo,
719
952
  initialSetup,
720
953
  },
721
954
  })
@@ -735,12 +968,20 @@ describe('plugin-meetings', () => {
735
968
  callbackAddress: uri1,
736
969
  },
737
970
  },
971
+ info: {
972
+ webExMeetingId,
973
+ },
738
974
  },
739
975
  eventType: 'locus.difference',
740
976
  locusUrl: url1,
741
977
  });
742
- assert.callCount(webex.meetings.meetingCollection.getByKey, 5);
978
+ assert.callCount(webex.meetings.meetingCollection.getByKey, 6);
743
979
  assert.calledWith(webex.meetings.meetingCollection.getByKey, 'locusUrl', url1);
980
+ assert.calledWith(
981
+ webex.meetings.meetingCollection.getByKey,
982
+ 'meetingNumber',
983
+ webExMeetingId
984
+ );
744
985
  assert.calledOnce(initialSetup);
745
986
  assert.calledWith(initialSetup, {
746
987
  id: uuid1,
@@ -754,6 +995,9 @@ describe('plugin-meetings', () => {
754
995
  callbackAddress: uri1,
755
996
  },
756
997
  },
998
+ info: {
999
+ webExMeetingId,
1000
+ },
757
1001
  });
758
1002
  });
759
1003
  it('should setup the meeting by difference event without replaces', async () => {
@@ -765,12 +1009,20 @@ describe('plugin-meetings', () => {
765
1009
  callbackAddress: uri1,
766
1010
  },
767
1011
  },
1012
+ info: {
1013
+ webExMeetingId,
1014
+ },
768
1015
  },
769
1016
  eventType: 'locus.difference',
770
1017
  locusUrl: url1,
771
1018
  });
772
- assert.callCount(webex.meetings.meetingCollection.getByKey, 4);
1019
+ assert.callCount(webex.meetings.meetingCollection.getByKey, 5);
773
1020
  assert.calledWith(webex.meetings.meetingCollection.getByKey, 'locusUrl', url1);
1021
+ assert.calledWith(
1022
+ webex.meetings.meetingCollection.getByKey,
1023
+ 'meetingNumber',
1024
+ webExMeetingId
1025
+ );
774
1026
  assert.calledOnce(initialSetup);
775
1027
  assert.calledWith(initialSetup, {
776
1028
  id: uuid1,
@@ -779,8 +1031,44 @@ describe('plugin-meetings', () => {
779
1031
  callbackAddress: uri1,
780
1032
  },
781
1033
  },
1034
+ info: {
1035
+ webExMeetingId,
1036
+ },
1037
+ });
1038
+ });
1039
+
1040
+ it('sends client event correctly on finally', async () => {
1041
+ webex.meetings.getMeetingByType = sinon.stub().returns(true);
1042
+
1043
+ await webex.meetings.handleLocusEvent({
1044
+ locus: {
1045
+ id: uuid1,
1046
+ self: {
1047
+ callBackInfo: {
1048
+ callbackAddress: uri1,
1049
+ },
1050
+ },
1051
+ info: {
1052
+ webExMeetingId,
1053
+ },
1054
+ },
1055
+ eventType: 'locus.difference',
1056
+ locusUrl: url1,
1057
+ });
1058
+
1059
+ await testUtils.flushPromises();
1060
+
1061
+ assert.calledWith(webex.internal.newMetrics.submitClientEvent, {
1062
+ name: 'client.call.remote-started',
1063
+ payload: {
1064
+ trigger: 'mercury-event',
1065
+ },
1066
+ options: {
1067
+ meetingId: 'meeting-id',
1068
+ },
782
1069
  });
783
1070
  });
1071
+
784
1072
  it('should setup the meeting by a not difference event', async () => {
785
1073
  await webex.meetings.handleLocusEvent({
786
1074
  locus: {
@@ -790,12 +1078,20 @@ describe('plugin-meetings', () => {
790
1078
  callbackAddress: uri1,
791
1079
  },
792
1080
  },
1081
+ info: {
1082
+ webExMeetingId,
1083
+ },
793
1084
  },
794
1085
  eventType: test1,
795
1086
  locusUrl: url1,
796
1087
  });
797
- assert.callCount(webex.meetings.meetingCollection.getByKey, 4);
1088
+ assert.callCount(webex.meetings.meetingCollection.getByKey, 5);
798
1089
  assert.calledWith(webex.meetings.meetingCollection.getByKey, 'locusUrl', url1);
1090
+ assert.calledWith(
1091
+ webex.meetings.meetingCollection.getByKey,
1092
+ 'meetingNumber',
1093
+ webExMeetingId
1094
+ );
799
1095
  assert.calledOnce(initialSetup);
800
1096
  assert.calledWith(initialSetup, {
801
1097
  id: uuid1,
@@ -804,6 +1100,9 @@ describe('plugin-meetings', () => {
804
1100
  callbackAddress: uri1,
805
1101
  },
806
1102
  },
1103
+ info: {
1104
+ webExMeetingId,
1105
+ },
807
1106
  });
808
1107
  });
809
1108
 
@@ -826,7 +1125,7 @@ describe('plugin-meetings', () => {
826
1125
 
827
1126
  it('should not try to match USM meetings by conversation url', async () => {
828
1127
  await webex.meetings.handleLocusEvent(generateFakeLocusData(true));
829
- assert.callCount(webex.meetings.meetingCollection.getByKey, 3);
1128
+ assert.callCount(webex.meetings.meetingCollection.getByKey, 4);
830
1129
  assert.deepEqual(webex.meetings.meetingCollection.getByKey.getCall(0).args, [
831
1130
  'locusUrl',
832
1131
  url1,
@@ -843,7 +1142,7 @@ describe('plugin-meetings', () => {
843
1142
  });
844
1143
  it('should try to match non-USM meetings by conversation url', async () => {
845
1144
  await webex.meetings.handleLocusEvent(generateFakeLocusData(false));
846
- assert.callCount(webex.meetings.meetingCollection.getByKey, 4);
1145
+ assert.callCount(webex.meetings.meetingCollection.getByKey, 5);
847
1146
  assert.deepEqual(webex.meetings.meetingCollection.getByKey.getCall(0).args, [
848
1147
  'locusUrl',
849
1148
  url1,
@@ -866,7 +1165,6 @@ describe('plugin-meetings', () => {
866
1165
  });
867
1166
  describe('#createMeeting', () => {
868
1167
  beforeEach(() => {
869
- MediaUtil.createPeerConnection = sinon.stub().returns(true);
870
1168
  webex.internal.device.userId = uuid1;
871
1169
  webex.internal.device.url = url1;
872
1170
  MeetingCollection.set = sinon.stub().returns(true);
@@ -875,6 +1173,10 @@ describe('plugin-meetings', () => {
875
1173
  });
876
1174
  describe('successful MeetingInfo.#fetchMeetingInfo', () => {
877
1175
  let clock, setTimeoutSpy, fakeMeetingStartTimeString, FAKE_TIME_TO_START;
1176
+ const FAKE_INFO_EXTRA_PARAMS = {
1177
+ mtid: 'm9fe0afd8c435e892afcce9ea25b97046',
1178
+ joinTXId: 'TSmrX61wNF',
1179
+ };
878
1180
 
879
1181
  beforeEach(() => {
880
1182
  clock = sinon.useFakeTimers();
@@ -904,13 +1206,37 @@ describe('plugin-meetings', () => {
904
1206
  meeting,
905
1207
  destination,
906
1208
  type,
907
- expectedMeetingData = {}
1209
+ extraParams = {},
1210
+ expectedMeetingData = {},
1211
+ sendCAevents = false,
1212
+ injectMeetingInfo = false
908
1213
  ) => {
909
- assert.calledOnce(webex.meetings.meetingInfo.fetchMeetingInfo);
1214
+ if (injectMeetingInfo) {
1215
+ assert.notCalled(webex.meetings.meetingInfo.fetchMeetingInfo);
1216
+ } else {
1217
+ assert.calledOnce(webex.meetings.meetingInfo.fetchMeetingInfo);
1218
+ }
1219
+
910
1220
  assert.calledOnce(MeetingsUtil.getMeetingAddedType);
911
1221
  assert.notCalled(setTimeoutSpy);
912
- assert.calledThrice(TriggerProxy.trigger);
913
- assert.calledWith(webex.meetings.meetingInfo.fetchMeetingInfo, destination, type);
1222
+ assert.callCount(TriggerProxy.trigger, 5);
1223
+
1224
+ if (injectMeetingInfo) {
1225
+ assert.notCalled(webex.meetings.meetingInfo.fetchMeetingInfo);
1226
+ } else {
1227
+ assert.calledWith(
1228
+ webex.meetings.meetingInfo.fetchMeetingInfo,
1229
+ destination,
1230
+ type,
1231
+ null,
1232
+ null,
1233
+ undefined,
1234
+ undefined,
1235
+ extraParams,
1236
+ {meetingId: meeting.id, sendCAevents}
1237
+ );
1238
+ }
1239
+
914
1240
  assert.calledWith(MeetingsUtil.getMeetingAddedType, 'test type');
915
1241
 
916
1242
  if (expectedMeetingData.permissionToken) {
@@ -919,6 +1245,21 @@ describe('plugin-meetings', () => {
919
1245
  if (expectedMeetingData.meetingJoinUrl) {
920
1246
  assert.equal(meeting.meetingJoinUrl, expectedMeetingData.meetingJoinUrl);
921
1247
  }
1248
+ if (expectedMeetingData.correlationId) {
1249
+ assert.equal(meeting.correlationId, expectedMeetingData.correlationId);
1250
+ }
1251
+ if (expectedMeetingData.callStateForMetrics) {
1252
+ assert.deepEqual(
1253
+ meeting.callStateForMetrics,
1254
+ expectedMeetingData.callStateForMetrics
1255
+ );
1256
+ }
1257
+ if (expectedMeetingData.meetingLookupUrl) {
1258
+ assert.equal(
1259
+ meeting.meetingInfo.meetingLookupUrl,
1260
+ expectedMeetingData.meetingLookupUrl
1261
+ );
1262
+ }
922
1263
  assert.equal(meeting.destination, destination);
923
1264
  assert.equal(meeting.destinationType, type);
924
1265
  assert.calledWith(
@@ -948,107 +1289,207 @@ describe('plugin-meetings', () => {
948
1289
  const expectedMeetingData = {
949
1290
  permissionToken: 'PT',
950
1291
  meetingJoinUrl: 'meetingJoinUrl',
1292
+ correlationId: meeting.id,
951
1293
  };
952
1294
 
953
- checkCreateWithoutDelay(meeting, 'test destination', 'test type', expectedMeetingData);
1295
+ checkCreateWithoutDelay(
1296
+ meeting,
1297
+ 'test destination',
1298
+ 'test type',
1299
+ {},
1300
+ expectedMeetingData
1301
+ );
954
1302
  });
955
1303
 
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 = {
1304
+ it('accepts injected meeting info', async () => {
1305
+ const meetingInfo = {
959
1306
  permissionToken: 'PT',
960
1307
  meetingJoinUrl: 'meetingJoinUrl',
961
1308
  };
962
1309
 
963
- assert.instanceOf(
1310
+ const meeting = await webex.meetings.createMeeting(
1311
+ 'test destination',
1312
+ 'test type',
1313
+ false,
1314
+ {},
1315
+ undefined,
1316
+ false,
1317
+ meetingInfo
1318
+ );
1319
+
1320
+ const expectedMeetingData = {
1321
+ ...meetingInfo,
1322
+ correlationId: meeting.id,
1323
+ };
1324
+
1325
+ checkCreateWithoutDelay(
964
1326
  meeting,
965
- Meeting,
966
- 'createMeeting should eventually resolve to a Meeting Object'
1327
+ 'test destination',
1328
+ 'test type',
1329
+ {},
1330
+ expectedMeetingData,
1331
+ false,
1332
+ true
967
1333
  );
968
- checkCreateWithoutDelay(meeting, 'test destination', 'test type', expectedMeetingData);
969
1334
  });
970
1335
 
971
- it('creates the meeting from a successful meeting info fetch with random delay', async () => {
972
- const FAKE_LOCUS_MEETING = {
973
- conversationUrl: 'locusConvURL',
974
- url: 'locusUrl',
975
- info: {
976
- webExMeetingId: 'locusMeetingId',
977
- sipUri: 'locusSipUri',
978
- owner: 'locusOwner',
979
- },
980
- meeting: {
981
- startTime: fakeMeetingStartTimeString,
982
- },
983
- fullState: {
984
- active: false,
985
- },
1336
+ it('accepts injected meeting info with meeting lookup url', async () => {
1337
+ const meetingInfo = {
1338
+ permissionToken: 'PT',
1339
+ meetingJoinUrl: 'meetingJoinUrl',
986
1340
  };
987
1341
 
988
1342
  const meeting = await webex.meetings.createMeeting(
989
- FAKE_LOCUS_MEETING,
1343
+ 'test destination',
990
1344
  'test type',
991
- true
1345
+ false,
1346
+ {},
1347
+ undefined,
1348
+ false,
1349
+ meetingInfo,
1350
+ 'meetingLookupUrl'
992
1351
  );
993
1352
 
994
- assert.instanceOf(
1353
+ const expectedMeetingData = {
1354
+ ...meetingInfo,
1355
+ meetingLookupUrl: 'meetingLookupUrl',
1356
+ correlationId: meeting.id,
1357
+ };
1358
+
1359
+ checkCreateWithoutDelay(
995
1360
  meeting,
996
- Meeting,
997
- 'createMeeting should eventually resolve to a Meeting Object'
998
- );
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
- }
1361
+ 'test destination',
1362
+ 'test type',
1363
+ {},
1364
+ expectedMeetingData,
1365
+ false,
1366
+ true
1026
1367
  );
1368
+ });
1027
1369
 
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
- );
1370
+ [undefined, FAKE_INFO_EXTRA_PARAMS].forEach((infoExtraParams) => {
1371
+ const infoExtraParamsProvided = infoExtraParams !== undefined;
1372
+
1373
+ it(`creates the meeting from a successful meeting info fetch meeting resolve testing${
1374
+ infoExtraParamsProvided ? ' with infoExtraParams' : ''
1375
+ }`, async () => {
1376
+ const meeting = await webex.meetings.createMeeting(
1377
+ 'test destination',
1378
+ 'test type',
1379
+ false,
1380
+ infoExtraParams
1381
+ );
1382
+ const expectedMeetingData = {
1383
+ permissionToken: 'PT',
1384
+ meetingJoinUrl: 'meetingJoinUrl',
1385
+ };
1386
+
1387
+ assert.instanceOf(
1388
+ meeting,
1389
+ Meeting,
1390
+ 'createMeeting should eventually resolve to a Meeting Object'
1391
+ );
1392
+ checkCreateWithoutDelay(
1393
+ meeting,
1394
+ 'test destination',
1395
+ 'test type',
1396
+ infoExtraParamsProvided ? infoExtraParams : {},
1397
+ expectedMeetingData
1398
+ );
1399
+ });
1035
1400
 
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');
1401
+ it(`creates the meeting from a successful meeting info fetch with random delay${
1402
+ infoExtraParamsProvided ? ' with infoExtraParams' : ''
1403
+ }`, async () => {
1404
+ const FAKE_LOCUS_MEETING = {
1405
+ conversationUrl: 'locusConvURL',
1406
+ url: 'locusUrl',
1407
+ info: {
1408
+ webExMeetingId: 'locusMeetingId',
1409
+ sipUri: 'locusSipUri',
1410
+ owner: 'locusOwner',
1411
+ },
1412
+ meeting: {
1413
+ startTime: fakeMeetingStartTimeString,
1414
+ },
1415
+ fullState: {
1416
+ active: false,
1417
+ },
1418
+ };
1045
1419
 
1046
- assert.calledWith(
1047
- TriggerProxy.trigger,
1048
- meeting,
1049
- {file: 'meetings', function: 'fetchMeetingInfo'},
1050
- 'meeting:meetingInfoAvailable'
1051
- );
1420
+ const meeting = await webex.meetings.createMeeting(
1421
+ FAKE_LOCUS_MEETING,
1422
+ 'test type',
1423
+ true,
1424
+ infoExtraParams
1425
+ );
1426
+
1427
+ assert.instanceOf(
1428
+ meeting,
1429
+ Meeting,
1430
+ 'createMeeting should eventually resolve to a Meeting Object'
1431
+ );
1432
+ assert.notCalled(webex.meetings.meetingInfo.fetchMeetingInfo);
1433
+ assert.calledOnce(setTimeoutSpy);
1434
+
1435
+ // Parse meeting info with locus object
1436
+ assert.equal(meeting.conversationUrl, 'locusConvURL');
1437
+ assert.equal(meeting.locusUrl, 'locusUrl');
1438
+ assert.equal(meeting.sipUri, 'locusSipUri');
1439
+ assert.equal(meeting.meetingNumber, 'locusMeetingId');
1440
+ assert.isUndefined(meeting.meetingJoinUrl);
1441
+ assert.equal(meeting.owner, 'locusOwner');
1442
+ assert.isUndefined(meeting.permissionToken);
1443
+
1444
+ // Add meeting and send trigger
1445
+ assert.calledWith(MeetingsUtil.getMeetingAddedType, 'test type');
1446
+ assert.calledTwice(TriggerProxy.trigger);
1447
+ assert.calledWith(
1448
+ TriggerProxy.trigger,
1449
+ sinon.match.instanceOf(Meetings),
1450
+ {
1451
+ file: 'meetings',
1452
+ function: 'createMeeting',
1453
+ },
1454
+ 'meeting:added',
1455
+ {
1456
+ meeting: sinon.match.instanceOf(Meeting),
1457
+ type: 'test meeting added type',
1458
+ }
1459
+ );
1460
+
1461
+ // When timer expires
1462
+ clock.tick(FAKE_TIME_TO_START);
1463
+ await testUtils.flushPromises();
1464
+
1465
+ assert.calledWith(
1466
+ webex.meetings.meetingInfo.fetchMeetingInfo,
1467
+ FAKE_LOCUS_MEETING,
1468
+ 'test type',
1469
+ null,
1470
+ null,
1471
+ undefined,
1472
+ undefined,
1473
+ infoExtraParamsProvided ? infoExtraParams : {}
1474
+ );
1475
+
1476
+ // Parse meeting info is called again with new meeting info
1477
+ await testUtils.flushPromises();
1478
+ assert.equal(meeting.conversationUrl, 'locusConvURL');
1479
+ assert.equal(meeting.locusUrl, 'locusUrl');
1480
+ assert.equal(meeting.sipUri, 'locusSipUri');
1481
+ assert.equal(meeting.meetingNumber, 'locusMeetingId');
1482
+ assert.equal(meeting.meetingJoinUrl, 'meetingJoinUrl');
1483
+ assert.equal(meeting.owner, 'locusOwner');
1484
+ assert.equal(meeting.permissionToken, 'PT');
1485
+
1486
+ assert.calledWith(
1487
+ TriggerProxy.trigger,
1488
+ meeting,
1489
+ {file: 'meetings', function: 'fetchMeetingInfo'},
1490
+ 'meeting:meetingInfoAvailable'
1491
+ );
1492
+ });
1052
1493
  });
1053
1494
 
1054
1495
  it('creates the meeting from a successful meeting info fetch that has no random delay because it is active', async () => {
@@ -1148,6 +1589,61 @@ describe('plugin-meetings', () => {
1148
1589
  );
1149
1590
  checkCreateWithoutDelay(meeting, FAKE_LOCUS_MEETING, 'test type');
1150
1591
  });
1592
+
1593
+ it('creates meeting with the correlationId provided', async () => {
1594
+ const meeting = await webex.meetings.createMeeting(
1595
+ 'test destination',
1596
+ 'test type',
1597
+ false,
1598
+ {},
1599
+ {correlationId: 'my-correlationId'}
1600
+ );
1601
+
1602
+ const expectedMeetingData = {
1603
+ correlationId: 'my-correlationId',
1604
+ };
1605
+
1606
+ checkCreateWithoutDelay(
1607
+ meeting,
1608
+ 'test destination',
1609
+ 'test type',
1610
+ {},
1611
+ expectedMeetingData,
1612
+ true
1613
+ );
1614
+ });
1615
+
1616
+ it('creates meeting with the callStateForMetrics provided', async () => {
1617
+ const meeting = await webex.meetings.createMeeting(
1618
+ 'test destination',
1619
+ 'test type',
1620
+ false,
1621
+ {},
1622
+ {
1623
+ correlationId: 'my-correlationId',
1624
+ joinTrigger: 'my-join-trigger',
1625
+ loginType: 'my-login-type',
1626
+ }
1627
+ );
1628
+
1629
+ const expectedMeetingData = {
1630
+ correlationId: 'my-correlationId',
1631
+ callStateForMetrics: {
1632
+ correlationId: 'my-correlationId',
1633
+ joinTrigger: 'my-join-trigger',
1634
+ loginType: 'my-login-type',
1635
+ },
1636
+ };
1637
+
1638
+ checkCreateWithoutDelay(
1639
+ meeting,
1640
+ 'test destination',
1641
+ 'test type',
1642
+ {},
1643
+ expectedMeetingData,
1644
+ true
1645
+ );
1646
+ });
1151
1647
  });
1152
1648
 
1153
1649
  describe('rejected MeetingInfo.#fetchMeetingInfo', () => {
@@ -1157,45 +1653,136 @@ describe('plugin-meetings', () => {
1157
1653
  webex.meetings.meetingInfo.fetchMeetingInfo = sinon
1158
1654
  .stub()
1159
1655
  .returns(Promise.reject(new Error('test')));
1656
+ webex.meetings.destroy = sinon.stub().returns(Promise.resolve());
1657
+ webex.meetings.createMeeting = sinon.spy(webex.meetings.createMeeting);
1160
1658
  });
1659
+
1660
+ const checkCreateMeetingWithNoMeetingInfo = async (failOnMissingMeetingInfo, destroy) => {
1661
+ try {
1662
+ const meeting = await webex.meetings.createMeeting(
1663
+ 'test destination',
1664
+ 'test type',
1665
+ undefined,
1666
+ undefined,
1667
+ undefined,
1668
+ failOnMissingMeetingInfo
1669
+ );
1670
+
1671
+ assert.instanceOf(
1672
+ meeting,
1673
+ Meeting,
1674
+ 'createMeeting should eventually resolve to a Meeting Object'
1675
+ );
1676
+ assert.calledOnce(webex.meetings.meetingInfo.fetchMeetingInfo);
1677
+ assert.calledOnce(MeetingsUtil.getMeetingAddedType);
1678
+ assert.calledThrice(TriggerProxy.trigger);
1679
+ assert.calledWith(
1680
+ webex.meetings.meetingInfo.fetchMeetingInfo,
1681
+ 'test destination',
1682
+ 'test type'
1683
+ );
1684
+
1685
+ if (destroy) {
1686
+ assert.calledWith(
1687
+ webex.meetings.destroy,
1688
+ sinon.match.instanceOf(Meeting),
1689
+ 'MISSING_MEETING_INFO'
1690
+ );
1691
+ assert.notCalled(MeetingsUtil.getMeetingAddedType);
1692
+ assert.notCalled(TriggerProxy.trigger);
1693
+ assert.throw(webex.meetings.createMeeting, 'meeting information not found');
1694
+ } else {
1695
+ assert.notCalled(webex.meetings.destroy);
1696
+ assert.calledWith(MeetingsUtil.getMeetingAddedType, 'test type');
1697
+ assert.calledWith(
1698
+ TriggerProxy.trigger,
1699
+ sinon.match.instanceOf(Meetings),
1700
+ {
1701
+ file: 'meetings',
1702
+ function: 'createMeeting',
1703
+ },
1704
+ 'meeting:added',
1705
+ {
1706
+ meeting: sinon.match.instanceOf(Meeting),
1707
+ type: 'test meeting added type',
1708
+ }
1709
+ );
1710
+ }
1711
+ } catch (err) {
1712
+ assert.instanceOf(err, NoMeetingInfoError);
1713
+ }
1714
+ };
1715
+
1161
1716
  it('creates the meeting from a rejected meeting info fetch', async () => {
1162
- const meeting = await webex.meetings.createMeeting('test destination', 'test type');
1717
+ checkCreateMeetingWithNoMeetingInfo(false, false);
1718
+ });
1163
1719
 
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),
1720
+ it('creates the meeting from a rejected meeting info fetch and destroys it if failOnMissingMeetingInfo', async () => {
1721
+ checkCreateMeetingWithNoMeetingInfo(true, true);
1722
+ });
1723
+ });
1724
+
1725
+ describe('rejected MeetingInfo.#fetchMeetingInfo - does not log for known Error types', () => {
1726
+ forEach(
1727
+ [
1181
1728
  {
1182
- file: 'meetings',
1183
- function: 'createMeeting',
1729
+ error: new CaptchaError(),
1730
+ debugLogMessage:
1731
+ 'Meetings:index#createMeeting --> Debug CaptchaError: Captcha is required. fetching /meetingInfo for creation.',
1184
1732
  },
1185
- 'meeting:added',
1186
1733
  {
1187
- meeting: sinon.match.instanceOf(Meeting),
1188
- type: 'test meeting added type',
1189
- }
1190
- );
1191
- });
1734
+ error: new PasswordError(),
1735
+ debugLogMessage:
1736
+ 'Meetings:index#createMeeting --> Debug PasswordError: Password is required, please use verifyPassword() fetching /meetingInfo for creation.',
1737
+ },
1738
+ {
1739
+ error: new PermissionError(),
1740
+ debugLogMessage:
1741
+ '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.',
1742
+ },
1743
+ {
1744
+ error: new Error(),
1745
+ infoLogMessage: true,
1746
+ debugLogMessage:
1747
+ 'Meetings:index#createMeeting --> Debug Error fetching /meetingInfo for creation.',
1748
+ },
1749
+ ],
1750
+ ({error, debugLogMessage, infoLogMessage}) => {
1751
+ it('creates the meeting from a rejected meeting info fetch', async () => {
1752
+ webex.meetings.meetingInfo.fetchMeetingInfo = sinon
1753
+ .stub()
1754
+ .returns(Promise.reject(error));
1755
+
1756
+ LoggerProxy.logger.debug = sinon.stub();
1757
+ LoggerProxy.logger.info = sinon.stub();
1758
+
1759
+ const meeting = await webex.meetings.createMeeting('test destination', 'test type');
1760
+
1761
+ assert.instanceOf(
1762
+ meeting,
1763
+ Meeting,
1764
+ 'createMeeting should eventually resolve to a Meeting Object'
1765
+ );
1766
+
1767
+ assert.calledWith(LoggerProxy.logger.debug, debugLogMessage);
1768
+
1769
+ if (infoLogMessage) {
1770
+ assert.calledWith(
1771
+ LoggerProxy.logger.info,
1772
+ 'Meetings:index#createMeeting --> Info Unable to fetch meeting info for test destination.'
1773
+ );
1774
+ } else {
1775
+ assert.notCalled(LoggerProxy.logger.info);
1776
+ }
1777
+ });
1778
+ }
1779
+ );
1192
1780
  });
1193
1781
  });
1194
1782
  });
1195
1783
  describe('Public Event Triggers', () => {
1196
1784
  describe('#destroy', () => {
1197
1785
  beforeEach(() => {
1198
- MediaUtil.createPeerConnection = sinon.stub().returns(true);
1199
1786
  MeetingUtil.cleanUp = sinon.stub();
1200
1787
  });
1201
1788
  it('should have #destroy', () => {
@@ -1269,6 +1856,8 @@ describe('plugin-meetings', () => {
1269
1856
  });
1270
1857
 
1271
1858
  describe('#fetchUserPreferredWebexSite', () => {
1859
+ let loggerProxySpy;
1860
+
1272
1861
  it('should call request.getMeetingPreferences to get the preferred webex site ', async () => {
1273
1862
  assert.isDefined(webex.meetings.preferredWebexSite);
1274
1863
  await webex.meetings.fetchUserPreferredWebexSite();
@@ -1276,7 +1865,22 @@ describe('plugin-meetings', () => {
1276
1865
  assert.equal(webex.meetings.preferredWebexSite, 'go.webex.com');
1277
1866
  });
1278
1867
 
1868
+ const setup = ({user} = {}) => {
1869
+ loggerProxySpy = sinon.spy(LoggerProxy.logger, 'error');
1870
+
1871
+ Object.assign(webex.internal, {
1872
+ services: {
1873
+ getMeetingPreferences: sinon.stub().returns(Promise.resolve({})),
1874
+ },
1875
+ user: {
1876
+ get: sinon.stub().returns(Promise.resolve(user)),
1877
+ },
1878
+ });
1879
+ };
1880
+
1279
1881
  it('should not fail if UserPreferred info is not fetched ', async () => {
1882
+ setup();
1883
+
1280
1884
  Object.assign(webex.internal, {
1281
1885
  services: {
1282
1886
  getMeetingPreferences: sinon.stub().returns(Promise.resolve({})),
@@ -1286,7 +1890,115 @@ describe('plugin-meetings', () => {
1286
1890
  await webex.meetings.fetchUserPreferredWebexSite().then(() => {
1287
1891
  assert.equal(webex.meetings.preferredWebexSite, '');
1288
1892
  });
1893
+ assert.calledOnceWithExactly(
1894
+ loggerProxySpy,
1895
+ 'Failed to fetch preferred site from user - no site will be set'
1896
+ );
1897
+ });
1898
+
1899
+ it('should fall back to fetching the site from the user', async () => {
1900
+ setup({
1901
+ user: {
1902
+ userPreferences: {
1903
+ userPreferencesItems: {
1904
+ preferredWebExSite: 'site.webex.com',
1905
+ },
1906
+ },
1907
+ },
1908
+ });
1909
+
1910
+ await webex.meetings.fetchUserPreferredWebexSite();
1911
+
1912
+ assert.equal(webex.meetings.preferredWebexSite, 'site.webex.com');
1913
+ assert.notCalled(loggerProxySpy);
1914
+ });
1915
+
1916
+ forEach(
1917
+ [
1918
+ {user: undefined},
1919
+ {user: {userPreferences: {}}},
1920
+ {user: {userPreferences: {userPreferencesItems: {}}}},
1921
+ {user: {userPreferences: {userPreferencesItems: {preferredWebExSite: undefined}}}},
1922
+ ],
1923
+ ({user}) => {
1924
+ it(`should handle invalid user data ${user}`, async () => {
1925
+ setup({user});
1926
+
1927
+ await webex.meetings.fetchUserPreferredWebexSite();
1928
+
1929
+ assert.equal(webex.meetings.preferredWebexSite, '');
1930
+ assert.calledOnceWithExactly(
1931
+ loggerProxySpy,
1932
+ 'Failed to fetch preferred site from user - no site will be set'
1933
+ );
1934
+ });
1935
+ }
1936
+ );
1937
+
1938
+ it('should handle a get user failure', async () => {
1939
+ setup();
1940
+
1941
+ webex.internal.user.get.rejects(new Error());
1942
+
1943
+ await webex.meetings.fetchUserPreferredWebexSite();
1944
+
1945
+ assert.equal(webex.meetings.preferredWebexSite, '');
1946
+ assert.calledOnceWithExactly(
1947
+ loggerProxySpy,
1948
+ 'Failed to fetch preferred site from user - no site will be set'
1949
+ );
1950
+ });
1951
+
1952
+ it('should fall back to fetching the site from the user', async () => {
1953
+ setup({
1954
+ user: {
1955
+ userPreferences: {
1956
+ userPreferencesItems: {
1957
+ preferredWebExSite: 'site.webex.com',
1958
+ },
1959
+ },
1960
+ },
1961
+ });
1962
+
1963
+ await webex.meetings.fetchUserPreferredWebexSite();
1964
+
1965
+ assert.equal(webex.meetings.preferredWebexSite, 'site.webex.com');
1966
+ assert.notCalled(loggerProxySpy);
1289
1967
  });
1968
+
1969
+ forEach([
1970
+ {user: undefined},
1971
+ {user: {userPreferences: {}}},
1972
+ {user: {userPreferences: {userPreferencesItems: {}}}},
1973
+ {user: {userPreferences: {userPreferencesItems: {preferredWebExSite: undefined}}}},
1974
+ ], ({user}) => {
1975
+ it(`should handle invalid user data ${user}`, async () => {
1976
+ setup({user});
1977
+
1978
+ await webex.meetings.fetchUserPreferredWebexSite();
1979
+
1980
+ assert.equal(webex.meetings.preferredWebexSite, '');
1981
+ assert.calledOnceWithExactly(
1982
+ loggerProxySpy,
1983
+ 'Failed to fetch preferred site from user - no site will be set'
1984
+ );
1985
+ });
1986
+ });
1987
+
1988
+ it('should handle a get user failure', async () => {
1989
+ setup();
1990
+
1991
+ webex.internal.user.get.rejects(new Error());
1992
+
1993
+ await webex.meetings.fetchUserPreferredWebexSite();
1994
+
1995
+ assert.equal(webex.meetings.preferredWebexSite, '');
1996
+ assert.calledOnceWithExactly(
1997
+ loggerProxySpy,
1998
+ 'Failed to fetch preferred site from user - no site will be set'
1999
+ );
2000
+ });
2001
+
1290
2002
  });
1291
2003
  });
1292
2004
 
@@ -1294,7 +2006,6 @@ describe('plugin-meetings', () => {
1294
2006
  let meeting;
1295
2007
 
1296
2008
  beforeEach(async () => {
1297
- MediaUtil.createPeerConnection = sinon.stub().returns(true);
1298
2009
  webex.internal.device.userId = uuid1;
1299
2010
  webex.internal.device.url = url1;
1300
2011
  MeetingCollection.set = sinon.stub().returns(true);
@@ -1371,5 +2082,523 @@ describe('plugin-meetings', () => {
1371
2082
  );
1372
2083
  });
1373
2084
  });
2085
+
2086
+ describe('#isNeedHandleMainLocus', () => {
2087
+ let meeting;
2088
+ let newLocus;
2089
+ beforeEach(() => {
2090
+ meeting = {
2091
+ controls: {},
2092
+ self: {},
2093
+ };
2094
+ newLocus = {
2095
+ controls: {},
2096
+ self: {},
2097
+ };
2098
+ });
2099
+ afterEach(() => {
2100
+ sinon.restore();
2101
+ });
2102
+ it('check normal case will return true', () => {
2103
+ sinon.stub(webex.meetings.meetingCollection, 'getActiveBreakoutLocus').returns(null);
2104
+ LoggerProxy.logger.log = sinon.stub();
2105
+ const result = webex.meetings.isNeedHandleMainLocus(meeting, newLocus);
2106
+ assert.equal(result, true);
2107
+ assert.calledWith(
2108
+ LoggerProxy.logger.log,
2109
+ 'Meetings:index#isNeedHandleMainLocus --> this is a normal main session locusDTO update case'
2110
+ );
2111
+ });
2112
+
2113
+ it('check self joined and joined on this device, return true', () => {
2114
+ sinon.stub(webex.meetings.meetingCollection, 'getActiveBreakoutLocus').returns(null);
2115
+ newLocus.self.state = 'JOINED';
2116
+ sinon.stub(MeetingsUtil, 'joinedOnThisDevice').returns(true);
2117
+
2118
+ LoggerProxy.logger.log = sinon.stub();
2119
+ const result = webex.meetings.isNeedHandleMainLocus(meeting, newLocus);
2120
+ assert.equal(result, true);
2121
+ assert.calledWith(
2122
+ LoggerProxy.logger.log,
2123
+ 'Meetings:index#isNeedHandleMainLocus --> self this device shown as JOINED in the main session'
2124
+ );
2125
+ });
2126
+
2127
+ it('if newLocus replaceAt time is expired, then return false', () => {
2128
+ sinon.stub(webex.meetings.meetingCollection, 'getActiveBreakoutLocus').returns({
2129
+ joinedWith: {
2130
+ replaces: [
2131
+ {
2132
+ replaceAt: '2023-03-27T02:17:02.506Z',
2133
+ },
2134
+ ],
2135
+ },
2136
+ });
2137
+ newLocus.self.state = 'JOINED';
2138
+ sinon.stub(MeetingsUtil, 'joinedOnThisDevice').returns(true);
2139
+ sinon.stub(MeetingsUtil, 'getThisDevice').returns({
2140
+ replaces: [
2141
+ {
2142
+ replaceAt: '2023-03-27T02:17:01.506Z',
2143
+ },
2144
+ ],
2145
+ });
2146
+
2147
+ LoggerProxy.logger.log = sinon.stub();
2148
+ const result = webex.meetings.isNeedHandleMainLocus(meeting, newLocus);
2149
+ assert.equal(result, false);
2150
+ assert.calledWith(
2151
+ LoggerProxy.logger.log,
2152
+ `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`
2153
+ );
2154
+ });
2155
+
2156
+ it('check current is in breakout join with this device, return false', () => {
2157
+ sinon.stub(webex.meetings.meetingCollection, 'getActiveBreakoutLocus').returns({
2158
+ joinedWith: {
2159
+ correlationId: '111',
2160
+ },
2161
+ });
2162
+ newLocus.controls.breakout = {url: 'url'};
2163
+ meeting.correlationId = '111';
2164
+
2165
+ LoggerProxy.logger.log = sinon.stub();
2166
+ const result = webex.meetings.isNeedHandleMainLocus(meeting, newLocus);
2167
+ assert.equal(result, false);
2168
+ assert.calledWith(
2169
+ LoggerProxy.logger.log,
2170
+ `Meetings:index#isNeedHandleMainLocus --> there is active breakout session and joined on this device, and don't need to handle main session: url`
2171
+ );
2172
+ });
2173
+
2174
+ it('check self is moved and removed, return false', () => {
2175
+ webex.meetings.meetingCollection.getActiveBreakoutLocus = sinon.stub().returns(null);
2176
+ newLocus.self.state = 'LEFT';
2177
+ newLocus.self.reason = 'MOVED';
2178
+ newLocus.self.removed = true;
2179
+ LoggerProxy.logger.log = sinon.stub();
2180
+ const result = webex.meetings.isNeedHandleMainLocus(meeting, newLocus);
2181
+ assert.equal(result, false);
2182
+ assert.calledWith(
2183
+ LoggerProxy.logger.log,
2184
+ 'Meetings:index#isNeedHandleMainLocus --> self moved main locus with self removed status or with device resource moved, not need to handle'
2185
+ );
2186
+ });
2187
+
2188
+ it('check self is moved and device resource removed, return false', () => {
2189
+ webex.meetings.meetingCollection.getActiveBreakoutLocus = sinon.stub().returns(null);
2190
+ newLocus.self.state = 'LEFT';
2191
+ newLocus.self.reason = 'MOVED';
2192
+ sinon.stub(MeetingsUtil, 'getThisDevice').returns({
2193
+ state: 'LEFT',
2194
+ reason: 'MOVED',
2195
+ });
2196
+ LoggerProxy.logger.log = sinon.stub();
2197
+ const result = webex.meetings.isNeedHandleMainLocus(meeting, newLocus);
2198
+ assert.equal(result, false);
2199
+ assert.calledWith(
2200
+ LoggerProxy.logger.log,
2201
+ 'Meetings:index#isNeedHandleMainLocus --> self moved main locus with self removed status or with device resource moved, not need to handle'
2202
+ );
2203
+ });
2204
+
2205
+ it('check self is joined but device resource removed, return false', () => {
2206
+ webex.meetings.meetingCollection.getActiveBreakoutLocus = sinon.stub().returns(null);
2207
+ sinon.stub(MeetingsUtil, 'joinedOnThisDevice').returns(false);
2208
+ newLocus.self.state = 'JOINED';
2209
+ sinon.stub(MeetingsUtil, 'getThisDevice').returns({
2210
+ state: 'LEFT',
2211
+ reason: 'MOVED',
2212
+ });
2213
+ LoggerProxy.logger.log = sinon.stub();
2214
+ const result = webex.meetings.isNeedHandleMainLocus(meeting, newLocus);
2215
+ assert.equal(result, false);
2216
+ assert.calledWith(
2217
+ LoggerProxy.logger.log,
2218
+ 'Meetings:index#isNeedHandleMainLocus --> self device left&moved in main locus with self joined status, not need to handle'
2219
+ );
2220
+ });
2221
+ });
2222
+
2223
+ describe('#isNeedHandleLocusDTO', () => {
2224
+ let meeting;
2225
+ let newLocus;
2226
+ beforeEach(() => {
2227
+ meeting = {
2228
+ controls: {},
2229
+ self: {},
2230
+ };
2231
+ newLocus = {
2232
+ controls: {},
2233
+ self: {},
2234
+ };
2235
+ });
2236
+ afterEach(() => {
2237
+ sinon.restore();
2238
+ });
2239
+ it('initial DTO , joined breakout session, return true', () => {
2240
+ newLocus.controls.breakout = {
2241
+ sessionType: 'BREAKOUT',
2242
+ };
2243
+ newLocus.self.state = 'JOINED';
2244
+ newLocus.fullState = {
2245
+ active: true,
2246
+ };
2247
+ LoggerProxy.logger.log = sinon.stub();
2248
+ const result = webex.meetings.isNeedHandleLocusDTO(null, newLocus);
2249
+ assert.equal(result, true);
2250
+ assert.calledWith(
2251
+ LoggerProxy.logger.log,
2252
+ `Meetings:index#isNeedHandleLocusDTO --> the first breakout session locusDTO active status: true`
2253
+ );
2254
+ });
2255
+ it('others go to check isNeedHandleMainLocus', () => {
2256
+ newLocus.controls.breakout = {
2257
+ sessionType: 'MAIN',
2258
+ };
2259
+ newLocus.self.state = 'JOINED';
2260
+
2261
+ LoggerProxy.logger.log = sinon.stub();
2262
+ const result = webex.meetings.isNeedHandleLocusDTO(meeting, newLocus);
2263
+ assert.equal(result, true);
2264
+ assert.calledWith(
2265
+ LoggerProxy.logger.log,
2266
+ 'Meetings:index#isNeedHandleMainLocus --> this is a normal main session locusDTO update case'
2267
+ );
2268
+ });
2269
+ it('joined breakout session, self status is moved, return false', () => {
2270
+ newLocus.controls.breakout = {
2271
+ sessionType: 'BREAKOUT',
2272
+ };
2273
+ newLocus.self.state = 'LEFT';
2274
+ newLocus.self.reason = 'MOVED';
2275
+
2276
+ LoggerProxy.logger.log = sinon.stub();
2277
+ const result = webex.meetings.isNeedHandleLocusDTO(meeting, newLocus);
2278
+ assert.equal(result, false);
2279
+ });
2280
+ });
2281
+
2282
+ describe('#getCorrespondingMeetingByLocus', () => {
2283
+ let locus;
2284
+ let mockReturnMeeting = {meeting: 'meeting1'};
2285
+ const mockGetByKey = (keyWillReturnMeeting) => {
2286
+ webex.meetings.meetingCollection.getByKey = sinon.stub().callsFake((key) => {
2287
+ if (key === keyWillReturnMeeting) {
2288
+ return mockReturnMeeting;
2289
+ }
2290
+ return null;
2291
+ });
2292
+ };
2293
+
2294
+ beforeEach(() => {
2295
+ locus = {
2296
+ controls: {},
2297
+ self: {
2298
+ callbackInfo: {
2299
+ callbackAddress: 'address1',
2300
+ },
2301
+ },
2302
+ info: {
2303
+ webExMeetingId: '123456',
2304
+ isUnifiedSpaceMeeting: false,
2305
+ },
2306
+ conversationUrl: 'conversationUrl1',
2307
+ };
2308
+
2309
+ sinon.stub(MeetingsUtil, 'checkForCorrelationId').returns('correlationId1');
2310
+ });
2311
+ afterEach(() => {
2312
+ sinon.restore();
2313
+ });
2314
+ it('check the calls when no meeting found in meetingCollection', () => {
2315
+ mockGetByKey();
2316
+ const result = webex.meetings.getCorrespondingMeetingByLocus({locus, locusUrl: url1});
2317
+ assert.isNull(result);
2318
+ assert.callCount(webex.meetings.meetingCollection.getByKey, 5);
2319
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'locusUrl', url1);
2320
+ assert.calledWith(
2321
+ webex.meetings.meetingCollection.getByKey,
2322
+ 'correlationId',
2323
+ 'correlationId1'
2324
+ );
2325
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'sipUri', 'address1');
2326
+ assert.calledWith(
2327
+ webex.meetings.meetingCollection.getByKey,
2328
+ 'conversationUrl',
2329
+ 'conversationUrl1'
2330
+ );
2331
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'meetingNumber', '123456');
2332
+ });
2333
+
2334
+ it('not try getByKey "conversationUrl" when isUnifiedSpaceMeeting is true', () => {
2335
+ mockGetByKey();
2336
+ locus.info.isUnifiedSpaceMeeting = true;
2337
+ const result = webex.meetings.getCorrespondingMeetingByLocus({locus, locusUrl: url1});
2338
+ assert.isNull(result);
2339
+ assert.callCount(webex.meetings.meetingCollection.getByKey, 4);
2340
+ });
2341
+
2342
+ it('check the calls when meeting found by key: locusUrl', () => {
2343
+ mockGetByKey('locusUrl');
2344
+ const result = webex.meetings.getCorrespondingMeetingByLocus({locus, locusUrl: url1});
2345
+ assert.deepEqual(result, mockReturnMeeting);
2346
+ assert.callCount(webex.meetings.meetingCollection.getByKey, 1);
2347
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'locusUrl', url1);
2348
+ });
2349
+
2350
+ it('check the calls when meeting found by key: correlationId', () => {
2351
+ mockGetByKey('correlationId');
2352
+ const result = webex.meetings.getCorrespondingMeetingByLocus({locus, locusUrl: url1});
2353
+ assert.deepEqual(result, mockReturnMeeting);
2354
+ assert.callCount(webex.meetings.meetingCollection.getByKey, 2);
2355
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'locusUrl', url1);
2356
+ assert.calledWith(
2357
+ webex.meetings.meetingCollection.getByKey,
2358
+ 'correlationId',
2359
+ 'correlationId1'
2360
+ );
2361
+ });
2362
+
2363
+ it('check the calls when meeting found by key: sipUri', () => {
2364
+ mockGetByKey('sipUri');
2365
+ const result = webex.meetings.getCorrespondingMeetingByLocus({locus, locusUrl: url1});
2366
+ assert.deepEqual(result, mockReturnMeeting);
2367
+ assert.callCount(webex.meetings.meetingCollection.getByKey, 3);
2368
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'locusUrl', url1);
2369
+ assert.calledWith(
2370
+ webex.meetings.meetingCollection.getByKey,
2371
+ 'correlationId',
2372
+ 'correlationId1'
2373
+ );
2374
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'sipUri', 'address1');
2375
+ });
2376
+
2377
+ it('check the calls when meeting found by key: conversationUrl', () => {
2378
+ mockGetByKey('conversationUrl');
2379
+ const result = webex.meetings.getCorrespondingMeetingByLocus({locus, locusUrl: url1});
2380
+ assert.deepEqual(result, mockReturnMeeting);
2381
+ assert.callCount(webex.meetings.meetingCollection.getByKey, 4);
2382
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'locusUrl', url1);
2383
+ assert.calledWith(
2384
+ webex.meetings.meetingCollection.getByKey,
2385
+ 'correlationId',
2386
+ 'correlationId1'
2387
+ );
2388
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'sipUri', 'address1');
2389
+ assert.calledWith(
2390
+ webex.meetings.meetingCollection.getByKey,
2391
+ 'conversationUrl',
2392
+ 'conversationUrl1'
2393
+ );
2394
+ });
2395
+
2396
+ it('check the calls when meeting found by key: meetingNumber', () => {
2397
+ mockGetByKey('meetingNumber');
2398
+ const result = webex.meetings.getCorrespondingMeetingByLocus({locus, locusUrl: url1});
2399
+ assert.deepEqual(result, mockReturnMeeting);
2400
+ assert.callCount(webex.meetings.meetingCollection.getByKey, 5);
2401
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'locusUrl', url1);
2402
+ assert.calledWith(
2403
+ webex.meetings.meetingCollection.getByKey,
2404
+ 'correlationId',
2405
+ 'correlationId1'
2406
+ );
2407
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'sipUri', 'address1');
2408
+ assert.calledWith(
2409
+ webex.meetings.meetingCollection.getByKey,
2410
+ 'conversationUrl',
2411
+ 'conversationUrl1'
2412
+ );
2413
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'meetingNumber', '123456');
2414
+ });
2415
+ });
2416
+
2417
+ describe('#sortLocusArrayToUpdate', () => {
2418
+ let lociArray;
2419
+ let mainLocus;
2420
+ let breakoutLocus;
2421
+ beforeEach(() => {
2422
+ mainLocus = {
2423
+ url: 'mainUrl1',
2424
+ controls: {
2425
+ breakout: {
2426
+ sessionType: 'MAIN',
2427
+ url: 'breakoutUnifiedUrl1',
2428
+ },
2429
+ },
2430
+ };
2431
+ breakoutLocus = {
2432
+ url: 'breakoutUrl1',
2433
+ controls: {
2434
+ breakout: {
2435
+ sessionType: 'BREAKOUT',
2436
+ url: 'breakoutUnifiedUrl1',
2437
+ },
2438
+ },
2439
+ };
2440
+ lociArray = [mainLocus, breakoutLocus];
2441
+
2442
+ sinon.stub(MeetingsUtil, 'isValidBreakoutLocus').callsFake((locus) => {
2443
+ return locus.url === 'breakoutUrl1';
2444
+ });
2445
+ });
2446
+ afterEach(() => {
2447
+ sinon.restore();
2448
+ });
2449
+
2450
+ it('if both main and breakout locus is in array for non-exist meeting, return main locus to create first', () => {
2451
+ webex.meetings.meetingCollection.getByKey = sinon.stub().returns(undefined);
2452
+ const result = webex.meetings.sortLocusArrayToUpdate(lociArray);
2453
+ assert.deepEqual(result, [mainLocus]);
2454
+ assert.deepEqual(webex.meetings.breakoutLocusForHandleLater, [breakoutLocus]);
2455
+ });
2456
+
2457
+ it('if both main and breakout locus is in array for an exist meeting, return all locus', () => {
2458
+ webex.meetings.meetingCollection.getByKey = sinon.stub().returns({});
2459
+ const result = webex.meetings.sortLocusArrayToUpdate(lociArray);
2460
+ assert.deepEqual(result, [mainLocus, breakoutLocus]);
2461
+ assert.deepEqual(webex.meetings.breakoutLocusForHandleLater, []);
2462
+ });
2463
+
2464
+ it('if the breakout locus has no associated main locus, return all', () => {
2465
+ webex.meetings.meetingCollection.getByKey = sinon.stub().returns({});
2466
+ breakoutLocus.controls.breakout.url = 'testUrl';
2467
+ const result = webex.meetings.sortLocusArrayToUpdate(lociArray);
2468
+ assert.deepEqual(result, [mainLocus, breakoutLocus]);
2469
+ });
2470
+ });
2471
+
2472
+ describe('#checkHandleBreakoutLocus', () => {
2473
+ let breakoutLocus;
2474
+ beforeEach(() => {
2475
+ breakoutLocus = {
2476
+ url: 'breakoutUrl1',
2477
+ controls: {
2478
+ breakout: {
2479
+ sessionType: 'BREAKOUT',
2480
+ url: 'breakoutUnifiedUrl1',
2481
+ },
2482
+ },
2483
+ };
2484
+
2485
+ webex.meetings.handleLocusEvent = sinon.stub();
2486
+ });
2487
+ afterEach(() => {
2488
+ sinon.restore();
2489
+ });
2490
+ it('do nothing if new created locus is null/no cached breakouts for updating', () => {
2491
+ webex.meetings.checkHandleBreakoutLocus(null);
2492
+ webex.meetings.breakoutLocusForHandleLater = null;
2493
+ webex.meetings.checkHandleBreakoutLocus({});
2494
+ webex.meetings.breakoutLocusForHandleLater = [];
2495
+ webex.meetings.checkHandleBreakoutLocus({});
2496
+ assert.notCalled(webex.meetings.handleLocusEvent);
2497
+ });
2498
+
2499
+ it('do nothing if new created locus is breakout locus', () => {
2500
+ webex.meetings.breakoutLocusForHandleLater = [breakoutLocus];
2501
+ webex.meetings.checkHandleBreakoutLocus(breakoutLocus);
2502
+ assert.notCalled(webex.meetings.handleLocusEvent);
2503
+ });
2504
+
2505
+ it('do nothing if no cached locus is associated with the new created locus', () => {
2506
+ webex.meetings.breakoutLocusForHandleLater = [breakoutLocus];
2507
+ webex.meetings.checkHandleBreakoutLocus({
2508
+ controls: {
2509
+ breakout: {
2510
+ sessionType: 'MAIN',
2511
+ url: 'breakoutUnifiedUrl2',
2512
+ },
2513
+ },
2514
+ });
2515
+ assert.notCalled(webex.meetings.handleLocusEvent);
2516
+ });
2517
+
2518
+ it('update the cached breakout locus which associate the new created locus', () => {
2519
+ webex.meetings.breakoutLocusForHandleLater = [breakoutLocus];
2520
+ webex.meetings.checkHandleBreakoutLocus({
2521
+ controls: {
2522
+ breakout: {
2523
+ sessionType: 'MAIN',
2524
+ url: 'breakoutUnifiedUrl1',
2525
+ },
2526
+ },
2527
+ });
2528
+ assert.calledWith(webex.meetings.handleLocusEvent, {
2529
+ locus: breakoutLocus,
2530
+ locusUrl: breakoutLocus.url,
2531
+ });
2532
+ });
2533
+ });
2534
+
2535
+ describe('uploading of logs', () => {
2536
+ let metricsSpy;
2537
+ let meeting;
2538
+
2539
+ beforeEach(async () => {
2540
+ webex.meetings.config.autoUploadLogs = true;
2541
+ webex.meetings.loggerRequest.uploadLogs = sinon.stub().resolves();
2542
+
2543
+ sinon.stub(webex.meetings.meetingInfo, 'fetchInfoOptions').resolves({});
2544
+ sinon.stub(webex.meetings.meetingInfo, 'fetchMeetingInfo').resolves({});
2545
+
2546
+ triggerProxyStub.restore();
2547
+
2548
+ metricsSpy = sinon.stub(Metrics, 'sendBehavioralMetric');
2549
+
2550
+ meeting = await webex.meetings.create('test');
2551
+
2552
+ meeting.locusId = 'locus id';
2553
+ meeting.correlationId = 'correlation id';
2554
+ meeting.locusInfo = {
2555
+ fullState: {lastActive: 'last active', sessionId: 'locus session id'},
2556
+ info: {webExMeetingId: 'meeting id'},
2557
+ };
2558
+ });
2559
+
2560
+ afterEach(() => {
2561
+ sinon.restore();
2562
+ });
2563
+
2564
+ it('sends metrics on success', async () => {
2565
+ await meeting.uploadLogs();
2566
+
2567
+ await testUtils.flushPromises();
2568
+
2569
+ assert.calledOnceWithExactly(metricsSpy, 'js_sdk_upload_logs_success', {
2570
+ callStart: 'last active',
2571
+ correlationId: 'correlation id',
2572
+ feedbackId: 'correlation id',
2573
+ locusId: 'locus id',
2574
+ meetingId: 'meeting id',
2575
+ autoupload: true,
2576
+ locussessionid: 'locus session id',
2577
+ });
2578
+ });
2579
+
2580
+ it('sends metrics on failure', async () => {
2581
+ webex.meetings.loggerRequest.uploadLogs.rejects(new Error('fake error'));
2582
+
2583
+ await meeting.uploadLogs();
2584
+
2585
+ await testUtils.flushPromises();
2586
+
2587
+ assert.calledOnceWithExactly(
2588
+ metricsSpy,
2589
+ 'js_sdk_upload_logs_failure',
2590
+ sinon.match({
2591
+ callStart: 'last active',
2592
+ correlationId: 'correlation id',
2593
+ feedbackId: 'correlation id',
2594
+ locusId: 'locus id',
2595
+ meetingId: 'meeting id',
2596
+ reason: 'fake error',
2597
+ autoupload: true,
2598
+ locussessionid: 'locus session id',
2599
+ })
2600
+ );
2601
+ });
2602
+ });
1374
2603
  });
1375
2604
  });