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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (629) hide show
  1. package/README.md +58 -8
  2. package/UPGRADING.md +9 -9
  3. package/browsers.js +19 -24
  4. package/dist/annotation/annotation.types.js +7 -0
  5. package/dist/annotation/annotation.types.js.map +1 -0
  6. package/dist/annotation/constants.js +49 -0
  7. package/dist/annotation/constants.js.map +1 -0
  8. package/dist/annotation/index.js +342 -0
  9. package/dist/annotation/index.js.map +1 -0
  10. package/dist/breakouts/breakout.js +216 -0
  11. package/dist/breakouts/breakout.js.map +1 -0
  12. package/dist/breakouts/collection.js +23 -0
  13. package/dist/breakouts/collection.js.map +1 -0
  14. package/dist/breakouts/edit-lock-error.js +52 -0
  15. package/dist/breakouts/edit-lock-error.js.map +1 -0
  16. package/dist/breakouts/events.js +45 -0
  17. package/dist/breakouts/events.js.map +1 -0
  18. package/dist/breakouts/index.js +1048 -0
  19. package/dist/breakouts/index.js.map +1 -0
  20. package/dist/breakouts/request.js +78 -0
  21. package/dist/breakouts/request.js.map +1 -0
  22. package/dist/breakouts/utils.js +67 -0
  23. package/dist/breakouts/utils.js.map +1 -0
  24. package/dist/common/browser-detection.js +1 -20
  25. package/dist/common/browser-detection.js.map +1 -1
  26. package/dist/common/collection.js +5 -20
  27. package/dist/common/collection.js.map +1 -1
  28. package/dist/common/config.js +0 -7
  29. package/dist/common/config.js.map +1 -1
  30. package/dist/common/errors/captcha-error.js +10 -24
  31. package/dist/common/errors/captcha-error.js.map +1 -1
  32. package/dist/common/errors/intent-to-join.js +11 -24
  33. package/dist/common/errors/intent-to-join.js.map +1 -1
  34. package/dist/common/errors/join-meeting.js +12 -25
  35. package/dist/common/errors/join-meeting.js.map +1 -1
  36. package/dist/common/errors/media.js +10 -24
  37. package/dist/common/errors/media.js.map +1 -1
  38. package/dist/common/errors/no-meeting-info.js +51 -0
  39. package/dist/common/errors/no-meeting-info.js.map +1 -0
  40. package/dist/common/errors/parameter.js +5 -33
  41. package/dist/common/errors/parameter.js.map +1 -1
  42. package/dist/common/errors/password-error.js +10 -24
  43. package/dist/common/errors/password-error.js.map +1 -1
  44. package/dist/common/errors/permission.js +9 -23
  45. package/dist/common/errors/permission.js.map +1 -1
  46. package/dist/common/errors/reclaim-host-role-errors.js +158 -0
  47. package/dist/common/errors/reclaim-host-role-errors.js.map +1 -0
  48. package/dist/common/errors/reconnection-in-progress.js +0 -17
  49. package/dist/common/errors/reconnection-in-progress.js.map +1 -1
  50. package/dist/common/errors/reconnection.js +10 -24
  51. package/dist/common/errors/reconnection.js.map +1 -1
  52. package/dist/common/errors/stats.js +10 -24
  53. package/dist/common/errors/stats.js.map +1 -1
  54. package/dist/common/errors/webex-errors.js +54 -48
  55. package/dist/common/errors/webex-errors.js.map +1 -1
  56. package/dist/common/errors/webex-meetings-error.js +5 -25
  57. package/dist/common/errors/webex-meetings-error.js.map +1 -1
  58. package/dist/common/events/events-scope.js +0 -22
  59. package/dist/common/events/events-scope.js.map +1 -1
  60. package/dist/common/events/events.js +0 -23
  61. package/dist/common/events/events.js.map +1 -1
  62. package/dist/common/events/trigger-proxy.js +0 -12
  63. package/dist/common/events/trigger-proxy.js.map +1 -1
  64. package/dist/common/events/util.js +0 -15
  65. package/dist/common/events/util.js.map +1 -1
  66. package/dist/common/logs/logger-config.js +0 -4
  67. package/dist/common/logs/logger-config.js.map +1 -1
  68. package/dist/common/logs/logger-proxy.js +1 -8
  69. package/dist/common/logs/logger-proxy.js.map +1 -1
  70. package/dist/common/logs/request.js +41 -60
  71. package/dist/common/logs/request.js.map +1 -1
  72. package/dist/common/queue.js +28 -23
  73. package/dist/common/queue.js.map +1 -1
  74. package/dist/config.js +11 -15
  75. package/dist/config.js.map +1 -1
  76. package/dist/constants.js +347 -74
  77. package/dist/constants.js.map +1 -1
  78. package/dist/controls-options-manager/constants.js +14 -0
  79. package/dist/controls-options-manager/constants.js.map +1 -0
  80. package/dist/controls-options-manager/enums.js +27 -0
  81. package/dist/controls-options-manager/enums.js.map +1 -0
  82. package/dist/controls-options-manager/index.js +297 -0
  83. package/dist/controls-options-manager/index.js.map +1 -0
  84. package/dist/controls-options-manager/types.js +7 -0
  85. package/dist/controls-options-manager/types.js.map +1 -0
  86. package/dist/controls-options-manager/util.js +319 -0
  87. package/dist/controls-options-manager/util.js.map +1 -0
  88. package/dist/index.js +125 -18
  89. package/dist/index.js.map +1 -1
  90. package/dist/interceptors/index.js +15 -0
  91. package/dist/interceptors/index.js.map +1 -0
  92. package/dist/interceptors/locusRetry.js +93 -0
  93. package/dist/interceptors/locusRetry.js.map +1 -0
  94. package/dist/interpretation/collection.js +23 -0
  95. package/dist/interpretation/collection.js.map +1 -0
  96. package/dist/interpretation/index.js +380 -0
  97. package/dist/interpretation/index.js.map +1 -0
  98. package/dist/interpretation/siLanguage.js +25 -0
  99. package/dist/interpretation/siLanguage.js.map +1 -0
  100. package/dist/locus-info/controlsUtils.js +101 -29
  101. package/dist/locus-info/controlsUtils.js.map +1 -1
  102. package/dist/locus-info/embeddedAppsUtils.js +3 -26
  103. package/dist/locus-info/embeddedAppsUtils.js.map +1 -1
  104. package/dist/locus-info/fullState.js +0 -15
  105. package/dist/locus-info/fullState.js.map +1 -1
  106. package/dist/locus-info/hostUtils.js +4 -12
  107. package/dist/locus-info/hostUtils.js.map +1 -1
  108. package/dist/locus-info/index.js +564 -246
  109. package/dist/locus-info/index.js.map +1 -1
  110. package/dist/locus-info/infoUtils.js +10 -38
  111. package/dist/locus-info/infoUtils.js.map +1 -1
  112. package/dist/locus-info/mediaSharesUtils.js +82 -38
  113. package/dist/locus-info/mediaSharesUtils.js.map +1 -1
  114. package/dist/locus-info/parser.js +314 -163
  115. package/dist/locus-info/parser.js.map +1 -1
  116. package/dist/locus-info/selfUtils.js +110 -92
  117. package/dist/locus-info/selfUtils.js.map +1 -1
  118. package/dist/media/index.js +107 -231
  119. package/dist/media/index.js.map +1 -1
  120. package/dist/media/properties.js +137 -222
  121. package/dist/media/properties.js.map +1 -1
  122. package/dist/media/util.js +2 -9
  123. package/dist/media/util.js.map +1 -1
  124. package/dist/mediaQualityMetrics/config.js +316 -501
  125. package/dist/mediaQualityMetrics/config.js.map +1 -1
  126. package/dist/meeting/in-meeting-actions.js +97 -14
  127. package/dist/meeting/in-meeting-actions.js.map +1 -1
  128. package/dist/meeting/index.js +5311 -3871
  129. package/dist/meeting/index.js.map +1 -1
  130. package/dist/meeting/locusMediaRequest.js +292 -0
  131. package/dist/meeting/locusMediaRequest.js.map +1 -0
  132. package/dist/meeting/muteState.js +260 -183
  133. package/dist/meeting/muteState.js.map +1 -1
  134. package/dist/meeting/request.js +421 -347
  135. package/dist/meeting/request.js.map +1 -1
  136. package/dist/meeting/request.type.js +7 -0
  137. package/dist/meeting/request.type.js.map +1 -0
  138. package/dist/meeting/state.js +21 -31
  139. package/dist/meeting/state.js.map +1 -1
  140. package/dist/meeting/util.js +672 -585
  141. package/dist/meeting/util.js.map +1 -1
  142. package/dist/meeting/voicea-meeting.js +172 -0
  143. package/dist/meeting/voicea-meeting.js.map +1 -0
  144. package/dist/meeting-info/collection.js +6 -25
  145. package/dist/meeting-info/collection.js.map +1 -1
  146. package/dist/meeting-info/index.js +87 -39
  147. package/dist/meeting-info/index.js.map +1 -1
  148. package/dist/meeting-info/meeting-info-v2.js +352 -283
  149. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  150. package/dist/meeting-info/request.js +3 -15
  151. package/dist/meeting-info/request.js.map +1 -1
  152. package/dist/meeting-info/util.js +99 -183
  153. package/dist/meeting-info/util.js.map +1 -1
  154. package/dist/meeting-info/utilv2.js +147 -234
  155. package/dist/meeting-info/utilv2.js.map +1 -1
  156. package/dist/meetings/collection.js +43 -19
  157. package/dist/meetings/collection.js.map +1 -1
  158. package/dist/meetings/index.js +895 -600
  159. package/dist/meetings/index.js.map +1 -1
  160. package/dist/meetings/meetings.types.js +7 -0
  161. package/dist/meetings/meetings.types.js.map +1 -0
  162. package/dist/meetings/request.js +26 -41
  163. package/dist/meetings/request.js.map +1 -1
  164. package/dist/meetings/util.js +184 -157
  165. package/dist/meetings/util.js.map +1 -1
  166. package/dist/member/index.js +134 -85
  167. package/dist/member/index.js.map +1 -1
  168. package/dist/member/types.js +25 -0
  169. package/dist/member/types.js.map +1 -0
  170. package/dist/member/util.js +158 -88
  171. package/dist/member/util.js.map +1 -1
  172. package/dist/members/collection.js +13 -12
  173. package/dist/members/collection.js.map +1 -1
  174. package/dist/members/index.js +194 -204
  175. package/dist/members/index.js.map +1 -1
  176. package/dist/members/request.js +113 -68
  177. package/dist/members/request.js.map +1 -1
  178. package/dist/members/types.js +15 -0
  179. package/dist/members/types.js.map +1 -0
  180. package/dist/members/util.js +324 -259
  181. package/dist/members/util.js.map +1 -1
  182. package/dist/metrics/constants.js +19 -7
  183. package/dist/metrics/constants.js.map +1 -1
  184. package/dist/metrics/index.js +11 -558
  185. package/dist/metrics/index.js.map +1 -1
  186. package/dist/multistream/mediaRequestManager.js +263 -50
  187. package/dist/multistream/mediaRequestManager.js.map +1 -1
  188. package/dist/multistream/receiveSlot.js +58 -65
  189. package/dist/multistream/receiveSlot.js.map +1 -1
  190. package/dist/multistream/receiveSlotManager.js +76 -95
  191. package/dist/multistream/receiveSlotManager.js.map +1 -1
  192. package/dist/multistream/remoteMedia.js +62 -76
  193. package/dist/multistream/remoteMedia.js.map +1 -1
  194. package/dist/multistream/remoteMediaGroup.js +82 -45
  195. package/dist/multistream/remoteMediaGroup.js.map +1 -1
  196. package/dist/multistream/remoteMediaManager.js +657 -448
  197. package/dist/multistream/remoteMediaManager.js.map +1 -1
  198. package/dist/multistream/sendSlotManager.js +255 -0
  199. package/dist/multistream/sendSlotManager.js.map +1 -0
  200. package/dist/networkQualityMonitor/index.js +40 -59
  201. package/dist/networkQualityMonitor/index.js.map +1 -1
  202. package/dist/personal-meeting-room/index.js +21 -45
  203. package/dist/personal-meeting-room/index.js.map +1 -1
  204. package/dist/personal-meeting-room/request.js +1 -31
  205. package/dist/personal-meeting-room/request.js.map +1 -1
  206. package/dist/personal-meeting-room/util.js +0 -13
  207. package/dist/personal-meeting-room/util.js.map +1 -1
  208. package/dist/reachability/clusterReachability.js +356 -0
  209. package/dist/reachability/clusterReachability.js.map +1 -0
  210. package/dist/reachability/index.js +297 -460
  211. package/dist/reachability/index.js.map +1 -1
  212. package/dist/reachability/request.js +20 -26
  213. package/dist/reachability/request.js.map +1 -1
  214. package/dist/reachability/util.js +29 -0
  215. package/dist/reachability/util.js.map +1 -0
  216. package/dist/reactions/constants.js +13 -0
  217. package/dist/reactions/constants.js.map +1 -0
  218. package/dist/reactions/reactions.js +109 -0
  219. package/dist/reactions/reactions.js.map +1 -0
  220. package/dist/reactions/reactions.type.js +36 -0
  221. package/dist/reactions/reactions.type.js.map +1 -0
  222. package/dist/reconnection-manager/index.js +413 -483
  223. package/dist/reconnection-manager/index.js.map +1 -1
  224. package/dist/recording-controller/enums.js +17 -0
  225. package/dist/recording-controller/enums.js.map +1 -0
  226. package/dist/recording-controller/index.js +362 -0
  227. package/dist/recording-controller/index.js.map +1 -0
  228. package/dist/recording-controller/util.js +64 -0
  229. package/dist/recording-controller/util.js.map +1 -0
  230. package/dist/roap/index.js +102 -86
  231. package/dist/roap/index.js.map +1 -1
  232. package/dist/roap/request.js +131 -135
  233. package/dist/roap/request.js.map +1 -1
  234. package/dist/roap/turnDiscovery.js +437 -116
  235. package/dist/roap/turnDiscovery.js.map +1 -1
  236. package/dist/rtcMetrics/constants.js +12 -0
  237. package/dist/rtcMetrics/constants.js.map +1 -0
  238. package/dist/rtcMetrics/index.js +179 -0
  239. package/dist/rtcMetrics/index.js.map +1 -0
  240. package/dist/statsAnalyzer/global.js +1 -95
  241. package/dist/statsAnalyzer/global.js.map +1 -1
  242. package/dist/statsAnalyzer/index.js +557 -583
  243. package/dist/statsAnalyzer/index.js.map +1 -1
  244. package/dist/statsAnalyzer/mqaUtil.js +326 -130
  245. package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
  246. package/dist/transcription/index.js +22 -47
  247. package/dist/transcription/index.js.map +1 -1
  248. package/dist/types/annotation/annotation.types.d.ts +42 -0
  249. package/dist/types/annotation/constants.d.ts +31 -0
  250. package/dist/types/annotation/index.d.ts +117 -0
  251. package/dist/types/breakouts/breakout.d.ts +8 -0
  252. package/dist/types/breakouts/collection.d.ts +5 -0
  253. package/dist/types/breakouts/edit-lock-error.d.ts +15 -0
  254. package/dist/types/breakouts/events.d.ts +8 -0
  255. package/dist/types/breakouts/index.d.ts +5 -0
  256. package/dist/types/breakouts/request.d.ts +22 -0
  257. package/dist/types/breakouts/utils.d.ts +15 -0
  258. package/dist/types/common/browser-detection.d.ts +9 -0
  259. package/dist/types/common/collection.d.ts +48 -0
  260. package/dist/types/common/config.d.ts +2 -0
  261. package/dist/types/common/errors/captcha-error.d.ts +15 -0
  262. package/dist/types/common/errors/intent-to-join.d.ts +16 -0
  263. package/dist/types/common/errors/join-meeting.d.ts +17 -0
  264. package/dist/types/common/errors/media.d.ts +15 -0
  265. package/dist/types/common/errors/no-meeting-info.d.ts +14 -0
  266. package/dist/types/common/errors/parameter.d.ts +15 -0
  267. package/dist/types/common/errors/password-error.d.ts +15 -0
  268. package/dist/types/common/errors/permission.d.ts +14 -0
  269. package/dist/types/common/errors/reclaim-host-role-errors.d.ts +60 -0
  270. package/dist/types/common/errors/reconnection-in-progress.d.ts +9 -0
  271. package/dist/types/common/errors/reconnection.d.ts +15 -0
  272. package/dist/types/common/errors/stats.d.ts +15 -0
  273. package/dist/types/common/errors/webex-errors.d.ts +93 -0
  274. package/dist/types/common/errors/webex-meetings-error.d.ts +20 -0
  275. package/dist/types/common/events/events-scope.d.ts +17 -0
  276. package/dist/types/common/events/events.d.ts +12 -0
  277. package/dist/types/common/events/trigger-proxy.d.ts +2 -0
  278. package/dist/types/common/events/util.d.ts +2 -0
  279. package/dist/types/common/logs/logger-config.d.ts +2 -0
  280. package/dist/types/common/logs/logger-proxy.d.ts +2 -0
  281. package/dist/types/common/logs/request.d.ts +36 -0
  282. package/dist/types/common/queue.d.ts +34 -0
  283. package/dist/types/config.d.ts +72 -0
  284. package/dist/types/constants.d.ts +1088 -0
  285. package/dist/types/controls-options-manager/constants.d.ts +4 -0
  286. package/dist/types/controls-options-manager/enums.d.ts +15 -0
  287. package/dist/types/controls-options-manager/index.d.ts +136 -0
  288. package/dist/types/controls-options-manager/types.d.ts +43 -0
  289. package/dist/types/controls-options-manager/util.d.ts +1 -0
  290. package/dist/types/index.d.ts +7 -0
  291. package/dist/types/interceptors/index.d.ts +2 -0
  292. package/dist/types/interceptors/locusRetry.d.ts +27 -0
  293. package/dist/types/interpretation/collection.d.ts +5 -0
  294. package/dist/types/interpretation/index.d.ts +5 -0
  295. package/dist/types/interpretation/siLanguage.d.ts +5 -0
  296. package/dist/types/locus-info/controlsUtils.d.ts +2 -0
  297. package/dist/types/locus-info/embeddedAppsUtils.d.ts +2 -0
  298. package/dist/types/locus-info/fullState.d.ts +2 -0
  299. package/dist/types/locus-info/hostUtils.d.ts +2 -0
  300. package/dist/types/locus-info/index.d.ts +322 -0
  301. package/dist/types/locus-info/infoUtils.d.ts +2 -0
  302. package/dist/types/locus-info/mediaSharesUtils.d.ts +2 -0
  303. package/dist/types/locus-info/parser.d.ts +272 -0
  304. package/dist/types/locus-info/selfUtils.d.ts +2 -0
  305. package/dist/types/media/index.d.ts +34 -0
  306. package/dist/types/media/properties.d.ts +93 -0
  307. package/dist/types/media/util.d.ts +2 -0
  308. package/dist/types/mediaQualityMetrics/config.d.ts +241 -0
  309. package/dist/types/meeting/in-meeting-actions.d.ts +167 -0
  310. package/dist/types/meeting/index.d.ts +1824 -0
  311. package/dist/types/meeting/locusMediaRequest.d.ts +74 -0
  312. package/dist/types/meeting/muteState.d.ts +178 -0
  313. package/dist/types/meeting/request.d.ts +293 -0
  314. package/dist/types/meeting/request.type.d.ts +11 -0
  315. package/dist/types/meeting/state.d.ts +9 -0
  316. package/dist/types/meeting/util.d.ts +118 -0
  317. package/dist/types/meeting/voicea-meeting.d.ts +16 -0
  318. package/dist/types/meeting-info/collection.d.ts +20 -0
  319. package/dist/types/meeting-info/index.d.ts +69 -0
  320. package/dist/types/meeting-info/meeting-info-v2.d.ts +123 -0
  321. package/dist/types/meeting-info/request.d.ts +22 -0
  322. package/dist/types/meeting-info/util.d.ts +2 -0
  323. package/dist/types/meeting-info/utilv2.d.ts +2 -0
  324. package/dist/types/meetings/collection.d.ts +40 -0
  325. package/dist/types/meetings/index.d.ts +389 -0
  326. package/dist/types/meetings/meetings.types.d.ts +4 -0
  327. package/dist/types/meetings/request.d.ts +27 -0
  328. package/dist/types/meetings/util.d.ts +18 -0
  329. package/dist/types/member/index.d.ts +160 -0
  330. package/dist/types/member/types.d.ts +32 -0
  331. package/dist/types/member/util.d.ts +2 -0
  332. package/dist/types/members/collection.d.ts +29 -0
  333. package/dist/types/members/index.d.ts +353 -0
  334. package/dist/types/members/request.d.ts +114 -0
  335. package/dist/types/members/types.d.ts +25 -0
  336. package/dist/types/members/util.d.ts +215 -0
  337. package/dist/types/metrics/constants.d.ts +70 -0
  338. package/dist/types/metrics/index.d.ts +45 -0
  339. package/dist/types/multistream/mediaRequestManager.d.ts +120 -0
  340. package/dist/types/multistream/receiveSlot.d.ts +68 -0
  341. package/dist/types/multistream/receiveSlotManager.d.ts +56 -0
  342. package/dist/types/multistream/remoteMedia.d.ts +72 -0
  343. package/dist/types/multistream/remoteMediaGroup.d.ts +49 -0
  344. package/dist/types/multistream/remoteMediaManager.d.ts +301 -0
  345. package/dist/types/multistream/sendSlotManager.d.ts +70 -0
  346. package/dist/types/networkQualityMonitor/index.d.ts +70 -0
  347. package/dist/types/personal-meeting-room/index.d.ts +47 -0
  348. package/dist/types/personal-meeting-room/request.d.ts +14 -0
  349. package/dist/types/personal-meeting-room/util.d.ts +2 -0
  350. package/dist/types/reachability/clusterReachability.d.ts +109 -0
  351. package/dist/types/reachability/index.d.ts +105 -0
  352. package/dist/types/reachability/request.d.ts +39 -0
  353. package/dist/types/reachability/util.d.ts +8 -0
  354. package/dist/types/reactions/constants.d.ts +3 -0
  355. package/dist/types/reactions/reactions.d.ts +4 -0
  356. package/dist/types/reactions/reactions.type.d.ts +52 -0
  357. package/dist/types/reconnection-manager/index.d.ts +136 -0
  358. package/dist/types/recording-controller/enums.d.ts +7 -0
  359. package/dist/types/recording-controller/index.d.ts +207 -0
  360. package/dist/types/recording-controller/util.d.ts +14 -0
  361. package/dist/types/roap/index.d.ts +86 -0
  362. package/dist/types/roap/request.d.ts +39 -0
  363. package/dist/types/roap/turnDiscovery.d.ts +155 -0
  364. package/dist/types/rtcMetrics/constants.d.ts +4 -0
  365. package/dist/types/rtcMetrics/index.d.ts +61 -0
  366. package/dist/types/statsAnalyzer/global.d.ts +36 -0
  367. package/dist/types/statsAnalyzer/index.d.ts +217 -0
  368. package/dist/types/statsAnalyzer/mqaUtil.d.ts +48 -0
  369. package/dist/types/transcription/index.d.ts +64 -0
  370. package/dist/types/webinar/collection.d.ts +16 -0
  371. package/dist/types/webinar/index.d.ts +5 -0
  372. package/dist/webinar/collection.js +44 -0
  373. package/dist/webinar/collection.js.map +1 -0
  374. package/dist/webinar/index.js +69 -0
  375. package/dist/webinar/index.js.map +1 -0
  376. package/internal-README.md +7 -6
  377. package/package.json +30 -21
  378. package/src/annotation/annotation.types.ts +50 -0
  379. package/src/annotation/constants.ts +36 -0
  380. package/src/annotation/index.ts +328 -0
  381. package/src/breakouts/README.md +220 -0
  382. package/src/breakouts/breakout.ts +188 -0
  383. package/src/breakouts/collection.ts +19 -0
  384. package/src/breakouts/edit-lock-error.ts +25 -0
  385. package/src/breakouts/events.ts +56 -0
  386. package/src/breakouts/index.ts +925 -0
  387. package/src/breakouts/request.ts +55 -0
  388. package/src/breakouts/utils.ts +57 -0
  389. package/src/common/{browser-detection.js → browser-detection.ts} +9 -6
  390. package/src/common/collection.ts +9 -7
  391. package/src/common/{config.js → config.ts} +1 -1
  392. package/src/common/errors/{captcha-error.js → captcha-error.ts} +11 -7
  393. package/src/common/errors/{intent-to-join.js → intent-to-join.ts} +12 -7
  394. package/src/common/errors/{join-meeting.js → join-meeting.ts} +17 -8
  395. package/src/common/errors/{media.js → media.ts} +11 -7
  396. package/src/common/errors/no-meeting-info.ts +24 -0
  397. package/src/common/errors/parameter.ts +11 -7
  398. package/src/common/errors/{password-error.js → password-error.ts} +11 -7
  399. package/src/common/errors/{permission.js → permission.ts} +10 -6
  400. package/src/common/errors/reclaim-host-role-errors.ts +134 -0
  401. package/src/common/errors/{reconnection.js → reconnection.ts} +11 -7
  402. package/src/common/errors/{stats.js → stats.ts} +11 -7
  403. package/src/common/errors/{webex-errors.js → webex-errors.ts} +51 -8
  404. package/src/common/errors/{webex-meetings-error.js → webex-meetings-error.ts} +4 -2
  405. package/src/common/events/{events-scope.js → events-scope.ts} +6 -2
  406. package/src/common/events/{events.js → events.ts} +5 -1
  407. package/src/common/events/{trigger-proxy.js → trigger-proxy.ts} +9 -5
  408. package/src/common/events/{util.js → util.ts} +2 -3
  409. package/src/common/logs/{logger-config.js → logger-config.ts} +1 -2
  410. package/src/common/logs/logger-proxy.ts +44 -0
  411. package/src/common/logs/{request.js → request.ts} +26 -9
  412. package/src/common/queue.ts +22 -9
  413. package/src/{config.js → config.ts} +19 -21
  414. package/src/constants.ts +296 -27
  415. package/src/controls-options-manager/constants.ts +5 -0
  416. package/src/controls-options-manager/enums.ts +18 -0
  417. package/src/controls-options-manager/index.ts +278 -0
  418. package/src/controls-options-manager/types.ts +59 -0
  419. package/src/controls-options-manager/util.ts +300 -0
  420. package/src/index.ts +45 -0
  421. package/src/interceptors/index.ts +3 -0
  422. package/src/interceptors/locusRetry.ts +67 -0
  423. package/src/interpretation/README.md +60 -0
  424. package/src/interpretation/collection.ts +19 -0
  425. package/src/interpretation/index.ts +349 -0
  426. package/src/interpretation/siLanguage.ts +18 -0
  427. package/src/locus-info/controlsUtils.ts +222 -0
  428. package/src/locus-info/{embeddedAppsUtils.js → embeddedAppsUtils.ts} +5 -6
  429. package/src/locus-info/{fullState.js → fullState.ts} +16 -12
  430. package/src/locus-info/{hostUtils.js → hostUtils.ts} +9 -8
  431. package/src/locus-info/{index.js → index.ts} +561 -119
  432. package/src/locus-info/{infoUtils.js → infoUtils.ts} +29 -10
  433. package/src/locus-info/{mediaSharesUtils.js → mediaSharesUtils.ts} +97 -17
  434. package/src/locus-info/{parser.js → parser.ts} +303 -104
  435. package/src/locus-info/{selfUtils.js → selfUtils.ts} +199 -68
  436. package/src/media/index.ts +460 -0
  437. package/src/media/properties.ts +283 -0
  438. package/src/media/{util.js → util.ts} +2 -2
  439. package/src/mediaQualityMetrics/config.ts +249 -0
  440. package/src/meeting/in-meeting-actions.ts +199 -3
  441. package/src/meeting/index.ts +8494 -0
  442. package/src/meeting/locusMediaRequest.ts +313 -0
  443. package/src/meeting/muteState.ts +465 -0
  444. package/src/meeting/request.ts +912 -0
  445. package/src/meeting/request.type.ts +13 -0
  446. package/src/meeting/{state.js → state.ts} +50 -35
  447. package/src/meeting/util.ts +799 -0
  448. package/src/meeting/voicea-meeting.ts +122 -0
  449. package/src/meeting-info/{collection.js → collection.ts} +6 -2
  450. package/src/meeting-info/index.ts +210 -0
  451. package/src/meeting-info/meeting-info-v2.ts +423 -0
  452. package/src/meeting-info/{request.js → request.ts} +14 -4
  453. package/src/meeting-info/{util.js → util.ts} +70 -58
  454. package/src/meeting-info/{utilv2.js → utilv2.ts} +99 -82
  455. package/src/meetings/collection.ts +76 -0
  456. package/src/meetings/index.ts +1539 -0
  457. package/src/meetings/meetings.types.ts +12 -0
  458. package/src/meetings/{request.js → request.ts} +34 -25
  459. package/src/meetings/{util.js → util.ts} +133 -38
  460. package/src/member/{index.js → index.ts} +159 -56
  461. package/src/member/types.ts +38 -0
  462. package/src/member/util.ts +397 -0
  463. package/src/members/{collection.js → collection.ts} +10 -2
  464. package/src/members/{index.js → index.ts} +351 -146
  465. package/src/members/request.ts +255 -0
  466. package/src/members/types.ts +29 -0
  467. package/src/members/util.ts +353 -0
  468. package/src/metrics/{constants.js → constants.ts} +17 -6
  469. package/src/metrics/index.ts +73 -0
  470. package/src/multistream/mediaRequestManager.ts +341 -64
  471. package/src/multistream/receiveSlot.ts +69 -26
  472. package/src/multistream/receiveSlotManager.ts +66 -42
  473. package/src/multistream/remoteMedia.ts +40 -5
  474. package/src/multistream/remoteMediaGroup.ts +82 -3
  475. package/src/multistream/remoteMediaManager.ts +401 -81
  476. package/src/multistream/sendSlotManager.ts +199 -0
  477. package/src/networkQualityMonitor/{index.js → index.ts} +41 -29
  478. package/src/personal-meeting-room/{index.js → index.ts} +28 -19
  479. package/src/personal-meeting-room/{request.js → request.ts} +13 -4
  480. package/src/personal-meeting-room/{util.js → util.ts} +4 -4
  481. package/src/reachability/clusterReachability.ts +320 -0
  482. package/src/reachability/index.ts +371 -0
  483. package/src/reachability/request.ts +50 -35
  484. package/src/reachability/util.ts +24 -0
  485. package/src/reactions/constants.ts +4 -0
  486. package/src/reactions/reactions.ts +104 -0
  487. package/src/reactions/reactions.type.ts +62 -0
  488. package/src/reconnection-manager/index.ts +643 -0
  489. package/src/recording-controller/enums.ts +8 -0
  490. package/src/recording-controller/index.ts +332 -0
  491. package/src/recording-controller/util.ts +75 -0
  492. package/src/roap/index.ts +288 -0
  493. package/src/roap/request.ts +153 -0
  494. package/src/roap/turnDiscovery.ts +374 -70
  495. package/src/rtcMetrics/constants.ts +3 -0
  496. package/src/rtcMetrics/index.ts +166 -0
  497. package/src/statsAnalyzer/global.ts +37 -0
  498. package/src/statsAnalyzer/index.ts +1275 -0
  499. package/src/statsAnalyzer/mqaUtil.ts +440 -0
  500. package/src/transcription/{index.js → index.ts} +46 -39
  501. package/src/webinar/collection.ts +31 -0
  502. package/src/webinar/index.ts +62 -0
  503. package/test/integration/spec/converged-space-meetings.js +233 -0
  504. package/test/integration/spec/journey.js +791 -531
  505. package/test/integration/spec/space-meeting.js +391 -204
  506. package/test/integration/spec/transcription.js +7 -8
  507. package/test/unit/spec/annotation/index.ts +418 -0
  508. package/test/unit/spec/breakouts/breakout.ts +238 -0
  509. package/test/unit/spec/breakouts/collection.ts +15 -0
  510. package/test/unit/spec/breakouts/edit-lock-error.ts +30 -0
  511. package/test/unit/spec/breakouts/events.ts +89 -0
  512. package/test/unit/spec/breakouts/index.ts +1793 -0
  513. package/test/unit/spec/breakouts/request.ts +104 -0
  514. package/test/unit/spec/breakouts/utils.js +72 -0
  515. package/test/unit/spec/common/browser-detection.js +9 -28
  516. package/test/unit/spec/common/queue.js +31 -2
  517. package/test/unit/spec/controls-options-manager/index.js +287 -0
  518. package/test/unit/spec/controls-options-manager/util.js +582 -0
  519. package/test/unit/spec/fixture/locus.js +93 -90
  520. package/test/unit/spec/interceptors/locusRetry.ts +131 -0
  521. package/test/unit/spec/interpretation/collection.ts +15 -0
  522. package/test/unit/spec/interpretation/index.ts +625 -0
  523. package/test/unit/spec/interpretation/siLanguage.ts +28 -0
  524. package/test/unit/spec/locus-info/controlsUtils.js +325 -32
  525. package/test/unit/spec/locus-info/embeddedAppsUtils.js +8 -6
  526. package/test/unit/spec/locus-info/index.js +1458 -21
  527. package/test/unit/spec/locus-info/infoUtils.js +71 -40
  528. package/test/unit/spec/locus-info/lib/BasicSeqCmp.json +88 -430
  529. package/test/unit/spec/locus-info/lib/SeqCmp.json +529 -685
  530. package/test/unit/spec/locus-info/mediaSharesUtils.ts +41 -0
  531. package/test/unit/spec/locus-info/parser.js +119 -44
  532. package/test/unit/spec/locus-info/selfConstant.js +120 -103
  533. package/test/unit/spec/locus-info/selfUtils.js +291 -12
  534. package/test/unit/spec/media/index.ts +194 -111
  535. package/test/unit/spec/media/properties.ts +11 -11
  536. package/test/unit/spec/meeting/in-meeting-actions.ts +96 -3
  537. package/test/unit/spec/meeting/index.js +8616 -1921
  538. package/test/unit/spec/meeting/locusMediaRequest.ts +442 -0
  539. package/test/unit/spec/meeting/muteState.js +568 -207
  540. package/test/unit/spec/meeting/request.js +602 -82
  541. package/test/unit/spec/meeting/utils.js +867 -179
  542. package/test/unit/spec/meeting/voicea-meeting.ts +266 -0
  543. package/test/unit/spec/meeting-info/index.js +300 -0
  544. package/test/unit/spec/meeting-info/meetinginfov2.js +631 -78
  545. package/test/unit/spec/meeting-info/request.js +7 -9
  546. package/test/unit/spec/meeting-info/util.js +11 -12
  547. package/test/unit/spec/meeting-info/utilv2.js +131 -74
  548. package/test/unit/spec/meetings/collection.js +27 -1
  549. package/test/unit/spec/meetings/index.js +1826 -374
  550. package/test/unit/spec/meetings/utils.js +243 -14
  551. package/test/unit/spec/member/index.js +61 -7
  552. package/test/unit/spec/member/util.js +526 -26
  553. package/test/unit/spec/members/index.js +536 -55
  554. package/test/unit/spec/members/request.js +228 -40
  555. package/test/unit/spec/members/utils.js +217 -4
  556. package/test/unit/spec/metrics/index.js +13 -68
  557. package/test/unit/spec/multistream/mediaRequestManager.ts +1032 -110
  558. package/test/unit/spec/multistream/receiveSlot.ts +77 -18
  559. package/test/unit/spec/multistream/receiveSlotManager.ts +69 -39
  560. package/test/unit/spec/multistream/remoteMedia.ts +40 -2
  561. package/test/unit/spec/multistream/remoteMediaGroup.ts +350 -5
  562. package/test/unit/spec/multistream/remoteMediaManager.ts +937 -65
  563. package/test/unit/spec/multistream/sendSlotManager.ts +274 -0
  564. package/test/unit/spec/networkQualityMonitor/index.js +24 -18
  565. package/test/unit/spec/personal-meeting-room/personal-meeting-room.js +2 -7
  566. package/test/unit/spec/reachability/clusterReachability.ts +279 -0
  567. package/test/unit/spec/reachability/index.ts +606 -26
  568. package/test/unit/spec/reachability/request.js +68 -0
  569. package/test/unit/spec/reachability/util.ts +40 -0
  570. package/test/unit/spec/reconnection-manager/index.js +222 -34
  571. package/test/unit/spec/recording-controller/index.js +306 -0
  572. package/test/unit/spec/recording-controller/util.js +229 -0
  573. package/test/unit/spec/roap/index.ts +238 -82
  574. package/test/unit/spec/roap/request.ts +255 -0
  575. package/test/unit/spec/roap/turnDiscovery.ts +707 -110
  576. package/test/unit/spec/rtcMetrics/index.ts +122 -0
  577. package/test/unit/spec/stats-analyzer/index.js +1331 -62
  578. package/test/unit/spec/webinar/collection.ts +13 -0
  579. package/test/unit/spec/webinar/index.ts +60 -0
  580. package/test/utils/cmr.js +44 -42
  581. package/test/utils/constants.js +9 -0
  582. package/test/utils/integrationTestUtils.js +46 -0
  583. package/test/utils/testUtils.js +63 -99
  584. package/test/utils/webex-config.js +22 -18
  585. package/test/utils/webex-test-users.js +65 -50
  586. package/tsconfig.json +6 -0
  587. package/dist/media/internal-media-core-wrapper.js +0 -22
  588. package/dist/media/internal-media-core-wrapper.js.map +0 -1
  589. package/dist/meeting/effectsState.js +0 -327
  590. package/dist/meeting/effectsState.js.map +0 -1
  591. package/dist/metrics/config.js +0 -301
  592. package/dist/metrics/config.js.map +0 -1
  593. package/dist/multistream/multistreamMedia.js +0 -116
  594. package/dist/multistream/multistreamMedia.js.map +0 -1
  595. package/dist/peer-connection-manager/util.js +0 -124
  596. package/dist/peer-connection-manager/util.js.map +0 -1
  597. package/src/common/logs/logger-proxy.js +0 -33
  598. package/src/index.js +0 -15
  599. package/src/locus-info/controlsUtils.js +0 -102
  600. package/src/media/index.js +0 -459
  601. package/src/media/internal-media-core-wrapper.ts +0 -9
  602. package/src/media/properties.js +0 -289
  603. package/src/mediaQualityMetrics/config.js +0 -382
  604. package/src/meeting/effectsState.js +0 -205
  605. package/src/meeting/index.js +0 -6284
  606. package/src/meeting/muteState.js +0 -318
  607. package/src/meeting/request.js +0 -684
  608. package/src/meeting/util.js +0 -506
  609. package/src/meeting-info/index.js +0 -131
  610. package/src/meeting-info/meeting-info-v2.js +0 -255
  611. package/src/meetings/collection.js +0 -40
  612. package/src/meetings/index.js +0 -1015
  613. package/src/member/util.js +0 -254
  614. package/src/members/request.js +0 -131
  615. package/src/members/util.js +0 -258
  616. package/src/metrics/config.js +0 -324
  617. package/src/metrics/index.js +0 -530
  618. package/src/multistream/multistreamMedia.ts +0 -92
  619. package/src/peer-connection-manager/util.ts +0 -117
  620. package/src/reachability/index.js +0 -464
  621. package/src/reconnection-manager/index.js +0 -519
  622. package/src/roap/index.js +0 -220
  623. package/src/roap/request.js +0 -127
  624. package/src/statsAnalyzer/global.js +0 -133
  625. package/src/statsAnalyzer/index.js +0 -1006
  626. package/src/statsAnalyzer/mqaUtil.js +0 -173
  627. package/test/unit/spec/meeting/effectsState.js +0 -291
  628. package/test/unit/spec/peerconnection-manager/utils.test-fixtures.ts +0 -389
  629. /package/src/common/errors/{reconnection-in-progress.js → reconnection-in-progress.ts} +0 -0
@@ -3,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,14 +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 Meeting from '@webex/plugin-meetings/src/meeting';
20
+ import Meeting, {CallStateForMetrics} from '@webex/plugin-meetings/src/meeting';
16
21
  import MeetingUtil from '@webex/plugin-meetings/src/meeting/util';
17
22
  import Meetings from '@webex/plugin-meetings/src/meetings';
18
23
  import MeetingCollection from '@webex/plugin-meetings/src/meetings/collection';
19
24
  import MeetingsUtil from '@webex/plugin-meetings/src/meetings/util';
20
25
  import PersonalMeetingRoom from '@webex/plugin-meetings/src/personal-meeting-room';
21
26
  import Reachability from '@webex/plugin-meetings/src/reachability';
27
+ import Metrics from '@webex/plugin-meetings/src/metrics';
22
28
 
23
29
  import testUtils from '../../../utils/testUtils';
24
30
  import {
@@ -27,8 +33,14 @@ import {
27
33
  ONLINE,
28
34
  ROAP,
29
35
  LOCUSINFO,
30
- EVENT_TRIGGERS
36
+ EVENT_TRIGGERS,
31
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';
32
44
 
33
45
  describe('plugin-meetings', () => {
34
46
  const logger = {
@@ -37,19 +49,23 @@ describe('plugin-meetings', () => {
37
49
  error: () => {},
38
50
  warn: () => {},
39
51
  trace: () => {},
40
- debug: () => {}
52
+ debug: () => {},
41
53
  };
42
54
 
55
+ let triggerProxyStub;
56
+
43
57
  beforeEach(() => {
44
58
  StaticConfig.set({
45
59
  bandwidth: {
46
- audio: 50, video: 500
47
- }
60
+ audio: 50,
61
+ video: 500,
62
+ },
48
63
  });
49
64
  LoggerConfig.set({
50
- verboseEvents: true, enable: false
65
+ verboseEvents: true,
66
+ enable: false,
51
67
  });
52
- TriggerProxy.trigger = sinon.stub().returns(true);
68
+ triggerProxyStub = sinon.stub(TriggerProxy, 'trigger').returns(true);
53
69
  });
54
70
 
55
71
  let webex;
@@ -58,6 +74,7 @@ describe('plugin-meetings', () => {
58
74
  let url1;
59
75
  let test1;
60
76
  let test2;
77
+ let locusInfo;
61
78
 
62
79
  describe('meetings index', () => {
63
80
  beforeEach(() => {
@@ -67,17 +84,20 @@ describe('plugin-meetings', () => {
67
84
  uri1 = `test-${uuid.v4()}@example.com`;
68
85
  test1 = `test-${uuid.v4()}`;
69
86
  test2 = `test2-${uuid.v4()}`;
87
+ locusInfo = {
88
+ parse: sinon.stub().returns(true),
89
+ updateMainSessionLocusCache: sinon.stub(),
90
+ };
70
91
  webex = new MockWebex({
71
92
  children: {
72
93
  device: Device,
73
94
  mercury: Mercury,
74
- meetings: Meetings
75
- }
95
+ meetings: Meetings,
96
+ },
76
97
  });
77
98
 
78
-
79
99
  Object.assign(webex, {
80
- logging: logger
100
+ logging: logger,
81
101
  });
82
102
 
83
103
  Object.assign(webex.meetings.config, {
@@ -86,68 +106,74 @@ describe('plugin-meetings', () => {
86
106
  // the server supports, minimums have to be tested
87
107
  audio: 64000,
88
108
  video: 4000000,
89
- startBitrate: 2000
109
+ startBitrate: 2000,
90
110
  },
91
111
  experimental: {
92
- enableUnifiedMeetings: true
112
+ enableUnifiedMeetings: true,
93
113
  },
94
114
  logging: {
95
115
  enable: true,
96
- verboseEvents: true
97
- }
116
+ verboseEvents: true,
117
+ },
98
118
  });
99
119
 
100
120
  Object.assign(webex, {
101
- logger
121
+ logger,
102
122
  });
103
123
 
104
124
  Object.assign(webex.meetings, {
105
- startReachability: sinon.stub().returns(Promise.resolve())
125
+ startReachability: sinon.stub().returns(Promise.resolve()),
106
126
  });
107
127
 
108
128
  Object.assign(webex.internal, {
129
+ llm: {on: sinon.stub()},
109
130
  device: {
110
131
  deviceType: 'FAKE_DEVICE',
111
132
  register: sinon.stub().returns(Promise.resolve()),
112
- unregister: sinon.stub().returns(Promise.resolve())
133
+ unregister: sinon.stub().returns(Promise.resolve()),
113
134
  },
114
135
  mercury: {
115
136
  connect: sinon.stub().returns(Promise.resolve()),
116
137
  disconnect: sinon.stub().returns(Promise.resolve()),
117
138
  on: () => {},
118
- off: () => {}
139
+ off: () => {},
119
140
  },
120
141
  services: {
121
- getMeetingPreferences: sinon.stub().returns(Promise.resolve({
122
- sites: [
123
- {
124
- siteUrl: 'site1-example.webex.com',
125
- default: false
126
- },
127
- {
128
- siteUrl: 'site2-example.webex.com',
129
- default: false
130
- },
131
- {
132
- siteUrl: 'site3-example.webex.com',
133
- default: false
134
- },
135
- {
136
- siteUrl: 'go.webex.com',
137
- default: true
138
- }
139
- ]
140
- })),
141
- fetchClientRegionInfo: sinon.stub().returns(Promise.resolve())
142
+ getMeetingPreferences: sinon.stub().returns(
143
+ Promise.resolve({
144
+ sites: [
145
+ {
146
+ siteUrl: 'site1-example.webex.com',
147
+ default: false,
148
+ },
149
+ {
150
+ siteUrl: 'site2-example.webex.com',
151
+ default: false,
152
+ },
153
+ {
154
+ siteUrl: 'site3-example.webex.com',
155
+ default: false,
156
+ },
157
+ {
158
+ siteUrl: 'go.webex.com',
159
+ default: true,
160
+ },
161
+ ],
162
+ })
163
+ ),
164
+ fetchClientRegionInfo: sinon.stub().returns(Promise.resolve()),
142
165
  },
143
166
  metrics: {
144
- submitClientMetrics: sinon.stub().returns(Promise.resolve())
145
- }
146
-
167
+ submitClientMetrics: sinon.stub().returns(Promise.resolve()),
168
+ },
147
169
  });
148
170
  webex.emit('ready');
149
171
  });
150
172
 
173
+ afterEach(() => {
174
+ sinon.restore();
175
+ });
176
+
151
177
  it('has a webex instance with a meetings property', () => {
152
178
  assert.exists(webex, 'webex was initialized with children');
153
179
  assert.exists(webex.meetings, 'meetings child was set up on the webex instance');
@@ -177,10 +203,14 @@ describe('plugin-meetings', () => {
177
203
 
178
204
  describe('failure', () => {
179
205
  it('should not accept non boolean input', () => {
180
- const currentEnableUnifiedMeetings = webex.meetings.config.experimental.enableUnifiedMeetings;
206
+ const currentEnableUnifiedMeetings =
207
+ webex.meetings.config.experimental.enableUnifiedMeetings;
181
208
 
182
209
  webex.meetings._toggleUnifiedMeetings('test');
183
- assert.equal(webex.meetings.config.experimental.enableUnifiedMeetings, currentEnableUnifiedMeetings);
210
+ assert.equal(
211
+ webex.meetings.config.experimental.enableUnifiedMeetings,
212
+ currentEnableUnifiedMeetings
213
+ );
184
214
  });
185
215
  });
186
216
  });
@@ -202,43 +232,38 @@ describe('plugin-meetings', () => {
202
232
  const currentEnableAdhocMeetings = webex.meetings.config.experimental.enableAdhocMeetings;
203
233
 
204
234
  webex.meetings._toggleAdhocMeetings('test');
205
- assert.equal(webex.meetings.config.experimental.enableAdhocMeetings, currentEnableAdhocMeetings);
235
+ assert.equal(
236
+ webex.meetings.config.experimental.enableAdhocMeetings,
237
+ currentEnableAdhocMeetings
238
+ );
206
239
  });
207
240
  });
208
241
  });
209
242
 
210
- describe('#_toggleTurnDiscovery', () => {
211
- it('should have toggleAdhocMeetings', () => {
212
- assert.equal(typeof webex.meetings._toggleTurnDiscovery, 'function');
243
+ describe('#_toggleTcpReachability', () => {
244
+ it('should have _toggleTcpReachability', () => {
245
+ assert.equal(typeof webex.meetings._toggleTcpReachability, 'function');
213
246
  });
214
247
 
215
248
  describe('success', () => {
216
- it('should update meetings to do TURN discovery', () => {
217
- webex.meetings._toggleTurnDiscovery(true);
218
- assert.equal(webex.meetings.config.experimental.enableTurnDiscovery, true);
219
-
220
- webex.meetings._toggleTurnDiscovery(false);
221
- assert.equal(webex.meetings.config.experimental.enableTurnDiscovery, false);
222
- });
223
- });
224
-
225
- describe('failure', () => {
226
- it('should not accept non boolean input', () => {
227
- const currentEnableTurnDiscovery = webex.meetings.config.experimental.enableTurnDiscovery;
228
-
229
- webex.meetings._toggleTurnDiscovery('test');
230
- assert.equal(webex.meetings.config.experimental.enableAdhocMeetings, currentEnableTurnDiscovery);
249
+ it('should update meetings to do TCP reachability', () => {
250
+ webex.meetings._toggleTcpReachability(true);
251
+ assert.equal(webex.meetings.config.experimental.enableTcpReachability, true);
231
252
  });
232
253
  });
233
254
  });
234
255
 
235
-
236
256
  describe('Public API Contracts', () => {
237
257
  describe('#register', () => {
238
258
  it('emits an event and resolves when register succeeds', async () => {
239
259
  webex.canAuthorize = true;
240
260
  await webex.meetings.register();
241
- assert.calledWith(TriggerProxy.trigger, sinon.match.instanceOf(Meetings), {file: 'meetings', function: 'register'}, 'meetings:registered');
261
+ assert.calledWith(
262
+ TriggerProxy.trigger,
263
+ sinon.match.instanceOf(Meetings),
264
+ {file: 'meetings', function: 'register'},
265
+ 'meetings:registered'
266
+ );
242
267
  assert.isTrue(webex.meetings.registered);
243
268
  });
244
269
 
@@ -284,9 +309,15 @@ describe('plugin-meetings', () => {
284
309
  it('emits an event and resolves when unregister succeeds', (done) => {
285
310
  webex.meetings.registered = true;
286
311
  webex.meetings.unregister().then(() => {
287
- assert.calledWith(TriggerProxy.trigger, sinon.match.instanceOf(Meetings), {
288
- file: 'meetings', function: 'unregister'
289
- }, 'meetings:unregistered');
312
+ assert.calledWith(
313
+ TriggerProxy.trigger,
314
+ sinon.match.instanceOf(Meetings),
315
+ {
316
+ file: 'meetings',
317
+ function: 'unregister',
318
+ },
319
+ 'meetings:unregistered'
320
+ );
290
321
  assert.isFalse(webex.meetings.registered);
291
322
  done();
292
323
  });
@@ -315,31 +346,110 @@ describe('plugin-meetings', () => {
315
346
  });
316
347
  });
317
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
+
318
443
  describe('gets', () => {
319
444
  describe('#getReachability', () => {
320
445
  it('should have #getReachability', () => {
321
446
  assert.exists(webex.meetings.getReachability);
322
447
  });
323
- describe('before #setReachability', () => {
324
- it('does not get a reachability instance', () => {
325
- const reachability = webex.meetings.getReachability();
326
-
327
- assert.notExists(reachability, 'reachability is undefined because #setReachability has not been called');
328
- });
329
- });
330
- describe('after #setReachability', () => {
331
- beforeEach(() => {
332
- webex.meetings.setReachability();
333
- const reachabilityMocker = webex.meetings.getReachability();
334
-
335
- sinon.stub(reachabilityMocker, 'gatherReachability').returns(true);
336
- });
337
- it('gets the reachability data instance from webex.meetings', () => {
338
- const reachability = webex.meetings.getReachability();
448
+ it('gets the reachability data instance from webex.meetings', () => {
449
+ const reachability = webex.meetings.getReachability();
339
450
 
340
- assert.exists(reachability, 'reachability is defined because #setReachability has been called');
341
- assert.instanceOf(reachability, Reachability, 'should be a reachability instance');
342
- });
451
+ assert.exists(reachability, 'reachability is defined');
452
+ assert.instanceOf(reachability, Reachability, 'should be a reachability instance');
343
453
  });
344
454
  });
345
455
  describe('#getPersonalMeetingRoom', () => {
@@ -349,8 +459,15 @@ describe('plugin-meetings', () => {
349
459
  it('gets the personal meeting room instance from webex.meetings', () => {
350
460
  const personalMeetingRoom = webex.meetings.getPersonalMeetingRoom();
351
461
 
352
- assert.exists(personalMeetingRoom, 'personal meeting room instance is set up at object creation');
353
- assert.instanceOf(personalMeetingRoom, PersonalMeetingRoom, 'should be a personal meeting room instance');
462
+ assert.exists(
463
+ personalMeetingRoom,
464
+ 'personal meeting room instance is set up at object creation'
465
+ );
466
+ assert.instanceOf(
467
+ personalMeetingRoom,
468
+ PersonalMeetingRoom,
469
+ 'should be a personal meeting room instance'
470
+ );
354
471
  });
355
472
  });
356
473
  describe('Static shortcut proxy methods', () => {
@@ -379,11 +496,11 @@ describe('plugin-meetings', () => {
379
496
  describe('#getAllMeetings', () => {
380
497
  it('calls MeetingCollection to get all meetings with supplied options', () => {
381
498
  webex.meetings.getAllMeetings({
382
- test: test1
499
+ test: test1,
383
500
  });
384
501
  assert.calledOnce(webex.meetings.meetingCollection.getAll);
385
502
  assert.calledWith(webex.meetings.meetingCollection.getAll, {
386
- test: test1
503
+ test: test1,
387
504
  });
388
505
  });
389
506
  });
@@ -394,30 +511,50 @@ describe('plugin-meetings', () => {
394
511
  it('should have #syncMeetings', () => {
395
512
  assert.exists(webex.meetings.syncMeetings);
396
513
  });
514
+ it('should do nothing and return a resolved promise if unverified guest', async () => {
515
+ webex.meetings.request.getActiveMeetings = sinon.stub().returns(
516
+ Promise.resolve({
517
+ loci: [
518
+ {
519
+ url: url1,
520
+ },
521
+ ],
522
+ })
523
+ );
524
+ webex.credentials.isUnverifiedGuest = true;
525
+ LoggerProxy.logger.info = sinon.stub();
526
+
527
+ await webex.meetings.syncMeetings();
528
+
529
+ assert.notCalled(webex.meetings.request.getActiveMeetings);
530
+ assert.calledWith(
531
+ LoggerProxy.logger.info,
532
+ 'Meetings:index#syncMeetings --> skipping meeting sync as unverified guest'
533
+ );
534
+ });
397
535
  describe('succesful requests', () => {
398
536
  beforeEach(() => {
399
- webex.meetings.request.getActiveMeetings = sinon.stub().returns(Promise.resolve({
400
- loci: [{
401
- url: url1
402
- }]
403
- }));
537
+ webex.meetings.request.getActiveMeetings = sinon.stub().returns(
538
+ Promise.resolve({
539
+ loci: [
540
+ {
541
+ url: url1,
542
+ },
543
+ ],
544
+ })
545
+ );
404
546
  });
405
547
  describe('when meeting is returned', () => {
406
- let parse;
407
-
408
548
  beforeEach(() => {
409
- parse = sinon.stub().returns(true);
410
549
  webex.meetings.meetingCollection.getByKey = sinon.stub().returns({
411
- locusInfo: {
412
- parse
413
- }
550
+ locusInfo,
414
551
  });
415
552
  });
416
553
  it('tests the sync meeting calls for existing meeting', async () => {
417
554
  await webex.meetings.syncMeetings();
418
555
  assert.calledOnce(webex.meetings.request.getActiveMeetings);
419
556
  assert.calledOnce(webex.meetings.meetingCollection.getByKey);
420
- assert.calledOnce(parse);
557
+ assert.calledOnce(locusInfo.parse);
421
558
  assert.calledWith(webex.meetings.meetingCollection.getByKey, 'locusUrl', url1);
422
559
  });
423
560
  });
@@ -427,63 +564,105 @@ describe('plugin-meetings', () => {
427
564
  beforeEach(() => {
428
565
  initialSetup = sinon.stub().returns(true);
429
566
  webex.meetings.meetingCollection.getByKey = sinon.stub().returns(null);
430
- webex.meetings.create = sinon.stub().returns(Promise.resolve({
431
- locusInfo: {
432
- initialSetup
433
- }
434
- }));
567
+ webex.meetings.create = sinon.stub().returns(
568
+ Promise.resolve({
569
+ locusInfo: {
570
+ ...locusInfo,
571
+ initialSetup,
572
+ },
573
+ })
574
+ );
435
575
  });
436
576
  it('tests the sync meeting calls for not existing meeting', async () => {
437
577
  await webex.meetings.syncMeetings();
438
578
  assert.calledOnce(webex.meetings.request.getActiveMeetings);
439
- assert.callCount(webex.meetings.meetingCollection.getByKey, 4);
579
+ assert.callCount(webex.meetings.meetingCollection.getByKey, 5);
440
580
  assert.calledOnce(initialSetup);
441
581
  assert.calledOnce(webex.meetings.create);
442
582
  assert.calledWith(webex.meetings.request.getActiveMeetings);
443
583
  assert.calledWith(webex.meetings.meetingCollection.getByKey, 'locusUrl', url1);
444
- assert.calledWith(webex.meetings.create, {
445
- url: url1
446
- }, 'LOCUS_ID');
584
+ assert.calledWith(
585
+ webex.meetings.create,
586
+ {
587
+ url: url1,
588
+ },
589
+ 'LOCUS_ID'
590
+ );
447
591
  assert.calledWith(initialSetup, {
448
- url: url1
592
+ url: url1,
449
593
  });
450
594
  });
451
595
  });
452
- describe('destory non active meeting', () => {
453
- let initialSetup;
454
- let parse;
596
+ describe('when destroying meeting is needed', () => {
455
597
  let destroySpy;
598
+ let cleanUpSpy;
599
+
600
+ const meetingCollectionMeetings = {
601
+ stillValidLocusMeeting: {
602
+ locusUrl: 'still-valid-locus-url',
603
+ sendCallAnalyzerMetrics: sinon.stub(),
604
+ },
605
+ noLongerValidLocusMeeting: {
606
+ locusUrl: 'no-longer-valid-locus-url',
607
+ sendCallAnalyzerMetrics: sinon.stub(),
608
+ },
609
+ otherNonLocusMeeting1: {
610
+ locusUrl: null,
611
+ sendCallAnalyzerMetrics: sinon.stub(),
612
+ },
613
+ otherNonLocusMeeting2: {
614
+ locusUrl: undefined,
615
+ sendCallAnalyzerMetrics: sinon.stub(),
616
+ },
617
+ };
456
618
 
457
619
  beforeEach(() => {
458
620
  destroySpy = sinon.spy(webex.meetings, 'destroy');
459
- parse = sinon.stub().returns(true);
460
- initialSetup = sinon.stub().returns(true);
461
- webex.meetings.meetingCollection.getByKey = sinon.stub().returns({
462
- locusInfo: {
463
- parse
464
- },
465
- sendCallAnalyzerMetrics: sinon.stub()
466
- });
467
- webex.meetings.meetingCollection.getAll = sinon.stub().returns({
468
- meetingutk: {
469
- locusUrl: 'fdfdjfdhj', sendCallAnalyzerMetrics: sinon.stub()
470
- }
471
- });
472
- webex.meetings.create = sinon.stub().returns(Promise.resolve({
473
- locusInfo: {
474
- initialSetup
475
- },
476
- sendCallAnalyzerMetrics: sinon.stub()
477
- }));
478
- webex.meetings.request.getActiveMeetings = sinon.stub().returns(Promise.resolve({
479
- loci: []
480
- }));
481
- MeetingUtil.cleanUp = sinon.stub().returns(Promise.resolve());
621
+ webex.meetings.meetingCollection.getAll = sinon
622
+ .stub()
623
+ .returns(meetingCollectionMeetings);
624
+ webex.meetings.request.getActiveMeetings = sinon.stub().returns(
625
+ Promise.resolve({
626
+ loci: [{url: 'still-valid-locus-url'}],
627
+ })
628
+ );
629
+ cleanUpSpy = sinon.stub(MeetingUtil, 'cleanUp').returns(Promise.resolve());
630
+ });
631
+
632
+ afterEach(() => {
633
+ cleanUpSpy.restore();
482
634
  });
483
- it('destroy non active meetings', async () => {
635
+
636
+ it('destroy any meeting that has no active locus url if keepOnlyLocusMeetings is not defined', async () => {
484
637
  await webex.meetings.syncMeetings();
485
638
  assert.calledOnce(webex.meetings.request.getActiveMeetings);
486
- assert.calledOnce(destroySpy);
639
+ assert.calledOnce(webex.meetings.meetingCollection.getAll);
640
+ assert.calledWith(destroySpy, meetingCollectionMeetings.noLongerValidLocusMeeting);
641
+ assert.calledWith(destroySpy, meetingCollectionMeetings.otherNonLocusMeeting1);
642
+ assert.calledWith(destroySpy, meetingCollectionMeetings.otherNonLocusMeeting2);
643
+ assert.callCount(destroySpy, 3);
644
+
645
+ assert.callCount(MeetingUtil.cleanUp, 3);
646
+ });
647
+
648
+ it('destroy any meeting that has no active locus url if keepOnlyLocusMeetings === true', async () => {
649
+ await webex.meetings.syncMeetings({keepOnlyLocusMeetings: true});
650
+ assert.calledOnce(webex.meetings.request.getActiveMeetings);
651
+ assert.calledOnce(webex.meetings.meetingCollection.getAll);
652
+ assert.calledWith(destroySpy, meetingCollectionMeetings.noLongerValidLocusMeeting);
653
+ assert.calledWith(destroySpy, meetingCollectionMeetings.otherNonLocusMeeting1);
654
+ assert.calledWith(destroySpy, meetingCollectionMeetings.otherNonLocusMeeting2);
655
+ assert.callCount(destroySpy, 3);
656
+
657
+ assert.callCount(MeetingUtil.cleanUp, 3);
658
+ });
659
+
660
+ it('destroy any LOCUS meetings that have no active locus url if keepOnlyLocusMeetings === false', async () => {
661
+ await webex.meetings.syncMeetings({keepOnlyLocusMeetings: false});
662
+ assert.calledOnce(webex.meetings.request.getActiveMeetings);
663
+ assert.calledOnce(webex.meetings.meetingCollection.getAll);
664
+ assert.calledWith(destroySpy, meetingCollectionMeetings.noLongerValidLocusMeeting);
665
+ assert.callCount(destroySpy, 1);
487
666
 
488
667
  assert.calledOnce(MeetingUtil.cleanUp);
489
668
  });
@@ -499,43 +678,163 @@ describe('plugin-meetings', () => {
499
678
  beforeEach(() => {
500
679
  infoOptions = {
501
680
  destination: 'dest-example',
502
- type: 'CONVERSATION_URL'
681
+ type: 'CONVERSATION_URL',
503
682
  };
504
683
  webex.meetings.meetingCollection.getByKey = sinon.stub().returns();
505
- webex.meetings.createMeeting = sinon.stub().returns(Promise.resolve({
506
- on: () => true
507
- }));
684
+ webex.meetings.createMeeting = sinon.stub().returns(
685
+ Promise.resolve({
686
+ on: () => true,
687
+ })
688
+ );
508
689
  });
509
690
 
510
- it('should call MeetingInfo#fetchInfoOptions() with proper params',
511
- () => {
512
- webex.meetings.meetingInfo.fetchInfoOptions = sinon.stub().resolves(
513
- infoOptions
514
- );
691
+ it('should call MeetingInfo#fetchInfoOptions() with proper params', () => {
692
+ webex.meetings.meetingInfo.fetchInfoOptions = sinon.stub().resolves(infoOptions);
515
693
 
516
- return webex.meetings.create(
694
+ return webex.meetings.create(infoOptions.destination, infoOptions.type).then(() => {
695
+ assert.calledWith(
696
+ webex.meetings.meetingInfo.fetchInfoOptions,
517
697
  infoOptions.destination,
518
698
  infoOptions.type
519
- )
520
- .then(() => {
521
- assert.calledWith(
522
- webex.meetings.meetingInfo.fetchInfoOptions,
523
- infoOptions.destination,
524
- infoOptions.type
525
- );
699
+ );
526
700
 
527
- assert.calledTwice(webex.meetings.meetingCollection.getByKey);
528
- });
701
+ assert.calledTwice(webex.meetings.meetingCollection.getByKey);
702
+ });
703
+ });
704
+
705
+ const FAKE_USE_RANDOM_DELAY = true;
706
+ const correlationId = 'my-correlationId';
707
+ const callStateForMetrics = {
708
+ correlationId: 'my-correlationId2',
709
+ joinTrigger: 'my-join-trigger',
710
+ loginType: 'my-login-type',
711
+ };
712
+
713
+ it('should call setCallStateForMetrics on any pre-existing meeting', async () => {
714
+ const fakeMeeting = {setCallStateForMetrics: sinon.mock()};
715
+ webex.meetings.meetingCollection.getByKey = sinon.stub().returns(fakeMeeting);
716
+ await webex.meetings.create(
717
+ test1,
718
+ test2,
719
+ FAKE_USE_RANDOM_DELAY,
720
+ {},
721
+ correlationId,
722
+ true,
723
+ callStateForMetrics
724
+ );
725
+ assert.calledOnceWithExactly(fakeMeeting.setCallStateForMetrics, {
726
+ ...callStateForMetrics,
727
+ correlationId,
529
728
  });
729
+ });
730
+
731
+ const checkCallCreateMeeting = async (createParameters, createMeetingParameters) => {
732
+ const create = webex.meetings.create(...createParameters);
733
+
734
+ assert.exists(create.then);
735
+ await create;
736
+ assert.calledOnce(webex.meetings.createMeeting);
737
+ assert.calledWith(webex.meetings.createMeeting, ...createMeetingParameters);
738
+ };
530
739
 
531
740
  it('calls createMeeting and returns its promise', async () => {
532
- const FAKE_USE_RANDOM_DELAY = true;
533
- const create = webex.meetings.create(test1, test2, FAKE_USE_RANDOM_DELAY);
741
+ await checkCallCreateMeeting(
742
+ [test1, test2, FAKE_USE_RANDOM_DELAY, {}, correlationId, true],
743
+ [test1, test2, FAKE_USE_RANDOM_DELAY, {}, {correlationId}, true]
744
+ );
745
+ });
746
+
747
+ it('calls createMeeting, pass the meeting info param and returns its promise', async () => {
748
+ const meetingInfo = {};
749
+ await checkCallCreateMeeting(
750
+ [test1, test2, FAKE_USE_RANDOM_DELAY, {}, correlationId, true, undefined, meetingInfo],
751
+ [test1, test2, FAKE_USE_RANDOM_DELAY, {}, {correlationId}, true, meetingInfo]
752
+ );
753
+ });
754
+
755
+ it('calls createMeeting, pass the meeting info and meetingLookupURL param and returns its promise', async () => {
756
+ const meetingInfo = {};
757
+ await checkCallCreateMeeting(
758
+ [
759
+ test1,
760
+ test2,
761
+ FAKE_USE_RANDOM_DELAY,
762
+ {},
763
+ correlationId,
764
+ true,
765
+ undefined,
766
+ meetingInfo,
767
+ 'meetingLookupURL',
768
+ ],
769
+ [
770
+ test1,
771
+ test2,
772
+ FAKE_USE_RANDOM_DELAY,
773
+ {},
774
+ {correlationId},
775
+ true,
776
+ meetingInfo,
777
+ 'meetingLookupURL',
778
+ ]
779
+ );
780
+ });
781
+
782
+ it('calls createMeeting when failOnMissingMeetinginfo is undefined and returns its promise', async () => {
783
+ await checkCallCreateMeeting(
784
+ [test1, test2, FAKE_USE_RANDOM_DELAY, {}, correlationId, undefined],
785
+ [test1, test2, FAKE_USE_RANDOM_DELAY, {}, {correlationId}, false]
786
+ );
787
+ });
788
+
789
+ it('calls createMeeting when failOnMissingMeetinginfo is false and returns its promise', async () => {
790
+ await checkCallCreateMeeting(
791
+ [test1, test2, FAKE_USE_RANDOM_DELAY, {}, correlationId, false],
792
+ [test1, test2, FAKE_USE_RANDOM_DELAY, {}, {correlationId}, false]
793
+ );
794
+ });
795
+
796
+ it('calls createMeeting with callStateForMetrics and returns its promise', async () => {
797
+ await checkCallCreateMeeting(
798
+ [test1, test2, FAKE_USE_RANDOM_DELAY, {}, undefined, true, callStateForMetrics],
799
+ [test1, test2, FAKE_USE_RANDOM_DELAY, {}, callStateForMetrics, true]
800
+ );
801
+ });
802
+
803
+ it('calls createMeeting with callStateForMetrics overwritten with correlationId and returns its promise', async () => {
804
+ await checkCallCreateMeeting(
805
+ [test1, test2, FAKE_USE_RANDOM_DELAY, {}, correlationId, true, callStateForMetrics],
806
+ [test1, test2, FAKE_USE_RANDOM_DELAY, {}, {...callStateForMetrics, correlationId}, true]
807
+ );
808
+ });
809
+
810
+ it('calls createMeeting with extra info params and returns its promise', async () => {
811
+ const FAKE_USE_RANDOM_DELAY = false;
812
+ const correlationId = 'my-correlationId';
813
+
814
+ const FAKE_INFO_EXTRA_PARAMS = {
815
+ mtid: 'm9fe0afd8c435e892afcce9ea25b97046',
816
+ joinTXId: 'TSmrX61wNF',
817
+ };
818
+ const create = webex.meetings.create(
819
+ test1,
820
+ test2,
821
+ FAKE_USE_RANDOM_DELAY,
822
+ FAKE_INFO_EXTRA_PARAMS,
823
+ correlationId
824
+ );
534
825
 
535
826
  assert.exists(create.then);
536
827
  await create;
537
828
  assert.calledOnce(webex.meetings.createMeeting);
538
- assert.calledWith(webex.meetings.createMeeting, test1, test2, FAKE_USE_RANDOM_DELAY);
829
+ assert.calledWith(
830
+ webex.meetings.createMeeting,
831
+ test1,
832
+ test2,
833
+ FAKE_USE_RANDOM_DELAY,
834
+ FAKE_INFO_EXTRA_PARAMS,
835
+ {correlationId},
836
+ false
837
+ );
539
838
  });
540
839
 
541
840
  it('creates a new meeting when a scheduled meeting exists in the conversation', async () => {
@@ -565,9 +864,9 @@ describe('plugin-meetings', () => {
565
864
  return undefined;
566
865
  });
567
866
 
568
- webex.meetings.meetingInfo.fetchInfoOptions = sinon.stub().resolves(
569
- scheduledMeetingFixture
570
- );
867
+ webex.meetings.meetingInfo.fetchInfoOptions = sinon
868
+ .stub()
869
+ .resolves(scheduledMeetingFixture);
571
870
 
572
871
  webex.meetings.meetingCollection.set(scheduledMeetingFixture);
573
872
 
@@ -602,99 +901,129 @@ describe('plugin-meetings', () => {
602
901
  it('doesnt call handle locus mercury for a locus roap event', () => {
603
902
  webex.meetings.handleLocusMercury({
604
903
  data: {
605
- eventType: 'locus.message.roap'
606
- }
904
+ eventType: 'locus.message.roap',
905
+ },
607
906
  });
608
907
  assert.notCalled(webex.meetings.handleLocusEvent);
609
908
  });
610
909
  it('doesnt call handle locus mercury for an undefined eventType', () => {
611
910
  webex.meetings.handleLocusMercury({
612
- data: {
613
- }
911
+ data: {},
614
912
  });
615
913
  assert.notCalled(webex.meetings.handleLocusEvent);
616
914
  });
617
915
  it('calls handle locus mercury for all locus events', () => {
618
916
  webex.meetings.handleLocusMercury({
619
917
  data: {
620
- eventType: test1
621
- }
918
+ eventType: test1,
919
+ },
622
920
  });
623
921
  assert.calledOnce(webex.meetings.handleLocusEvent);
624
- assert.calledWith(webex.meetings.handleLocusEvent, {
625
- eventType: test1
626
- }, true);
922
+ assert.calledWith(
923
+ webex.meetings.handleLocusEvent,
924
+ {
925
+ eventType: test1,
926
+ },
927
+ true
928
+ );
627
929
  });
628
930
  });
629
931
  describe('#handleLocusEvent', () => {
630
932
  describe('there was a meeting', () => {
631
- let parse;
632
-
633
933
  beforeEach(() => {
634
- parse = sinon.stub().returns(true);
635
934
  webex.meetings.meetingCollection.getByKey = sinon.stub().returns({
636
- locusInfo: {
637
- parse
638
- }
935
+ locusInfo,
639
936
  });
640
937
  });
641
- it('should parse the meeting info', () => {
938
+ it('should parse the meeting info and update main session locus cache', () => {
939
+ sinon.stub(MeetingsUtil, 'isBreakoutLocusDTO').returns(false);
642
940
  webex.meetings.handleLocusEvent({
643
- locusUrl: url1
941
+ locusUrl: url1,
644
942
  });
645
943
  assert.calledOnce(webex.meetings.meetingCollection.getByKey);
646
944
  assert.calledWith(webex.meetings.meetingCollection.getByKey, 'locusUrl', url1);
647
- assert.calledOnce(parse);
648
- assert.calledWith(parse, {
649
- locusInfo: {
650
- parse
945
+ assert.calledOnce(locusInfo.parse);
946
+ assert.calledOnce(locusInfo.updateMainSessionLocusCache);
947
+ assert.calledWith(
948
+ locusInfo.parse,
949
+ {
950
+ locusInfo,
951
+ },
952
+ {
953
+ locusUrl: url1,
651
954
  }
652
- }, {
653
- locusUrl: url1
955
+ );
956
+ });
957
+
958
+ it('should not update main session locus cache', () => {
959
+ sinon.stub(MeetingsUtil, 'isBreakoutLocusDTO').returns(true);
960
+ webex.meetings.handleLocusEvent({
961
+ locusUrl: url1,
654
962
  });
963
+ assert.notCalled(locusInfo.updateMainSessionLocusCache);
655
964
  });
656
965
  });
657
966
  describe('there was not a meeting', () => {
658
967
  let initialSetup;
968
+ const webExMeetingId = '123456';
659
969
 
660
970
  beforeEach(() => {
661
971
  initialSetup = sinon.stub().returns(true);
662
972
  webex.meetings.meetingCollection.getByKey = sinon.stub().returns(undefined);
663
- webex.meetings.create = sinon.stub().returns(Promise.resolve({
664
- locusInfo: {
665
- initialSetup
666
- }
667
- }));
973
+ webex.meetings.create = sinon.stub().returns(
974
+ Promise.resolve({
975
+ id: 'meeting-id',
976
+ locusInfo: {
977
+ ...locusInfo,
978
+ initialSetup,
979
+ },
980
+ })
981
+ );
668
982
  });
669
983
  it('should setup the meeting by difference event', async () => {
670
984
  await webex.meetings.handleLocusEvent({
671
985
  locus: {
672
986
  id: uuid1,
673
- replaces: [{
674
- locusUrl: 'http:locusUrl'
675
- }],
987
+ replaces: [
988
+ {
989
+ locusUrl: 'http:locusUrl',
990
+ },
991
+ ],
676
992
  self: {
677
993
  callBackInfo: {
678
- callbackAddress: uri1
679
- }
680
- }
994
+ callbackAddress: uri1,
995
+ },
996
+ },
997
+ info: {
998
+ webExMeetingId,
999
+ },
681
1000
  },
682
1001
  eventType: 'locus.difference',
683
- locusUrl: url1
1002
+ locusUrl: url1,
684
1003
  });
685
- assert.callCount(webex.meetings.meetingCollection.getByKey, 5);
1004
+ assert.callCount(webex.meetings.meetingCollection.getByKey, 6);
686
1005
  assert.calledWith(webex.meetings.meetingCollection.getByKey, 'locusUrl', url1);
1006
+ assert.calledWith(
1007
+ webex.meetings.meetingCollection.getByKey,
1008
+ 'meetingNumber',
1009
+ webExMeetingId
1010
+ );
687
1011
  assert.calledOnce(initialSetup);
688
1012
  assert.calledWith(initialSetup, {
689
1013
  id: uuid1,
690
- replaces: [{
691
- locusUrl: 'http:locusUrl'
692
- }],
1014
+ replaces: [
1015
+ {
1016
+ locusUrl: 'http:locusUrl',
1017
+ },
1018
+ ],
693
1019
  self: {
694
1020
  callBackInfo: {
695
- callbackAddress: uri1
696
- }
697
- }
1021
+ callbackAddress: uri1,
1022
+ },
1023
+ },
1024
+ info: {
1025
+ webExMeetingId,
1026
+ },
698
1027
  });
699
1028
  });
700
1029
  it('should setup the meeting by difference event without replaces', async () => {
@@ -703,48 +1032,103 @@ describe('plugin-meetings', () => {
703
1032
  id: uuid1,
704
1033
  self: {
705
1034
  callBackInfo: {
706
- callbackAddress: uri1
707
- }
708
- }
1035
+ callbackAddress: uri1,
1036
+ },
1037
+ },
1038
+ info: {
1039
+ webExMeetingId,
1040
+ },
709
1041
  },
710
1042
  eventType: 'locus.difference',
711
- locusUrl: url1
1043
+ locusUrl: url1,
712
1044
  });
713
- assert.callCount(webex.meetings.meetingCollection.getByKey, 4);
1045
+ assert.callCount(webex.meetings.meetingCollection.getByKey, 5);
714
1046
  assert.calledWith(webex.meetings.meetingCollection.getByKey, 'locusUrl', url1);
1047
+ assert.calledWith(
1048
+ webex.meetings.meetingCollection.getByKey,
1049
+ 'meetingNumber',
1050
+ webExMeetingId
1051
+ );
715
1052
  assert.calledOnce(initialSetup);
716
1053
  assert.calledWith(initialSetup, {
717
1054
  id: uuid1,
718
1055
  self: {
719
1056
  callBackInfo: {
720
- callbackAddress: uri1
721
- }
722
- }
1057
+ callbackAddress: uri1,
1058
+ },
1059
+ },
1060
+ info: {
1061
+ webExMeetingId,
1062
+ },
1063
+ });
1064
+ });
1065
+
1066
+ it('sends client event correctly on finally', async () => {
1067
+ webex.meetings.getMeetingByType = sinon.stub().returns(true);
1068
+
1069
+ await webex.meetings.handleLocusEvent({
1070
+ locus: {
1071
+ id: uuid1,
1072
+ self: {
1073
+ callBackInfo: {
1074
+ callbackAddress: uri1,
1075
+ },
1076
+ },
1077
+ info: {
1078
+ webExMeetingId,
1079
+ },
1080
+ },
1081
+ eventType: 'locus.difference',
1082
+ locusUrl: url1,
1083
+ });
1084
+
1085
+ await testUtils.flushPromises();
1086
+
1087
+ assert.calledWith(webex.internal.newMetrics.submitClientEvent, {
1088
+ name: 'client.call.remote-started',
1089
+ payload: {
1090
+ trigger: 'mercury-event',
1091
+ },
1092
+ options: {
1093
+ meetingId: 'meeting-id',
1094
+ },
723
1095
  });
724
1096
  });
1097
+
725
1098
  it('should setup the meeting by a not difference event', async () => {
726
1099
  await webex.meetings.handleLocusEvent({
727
1100
  locus: {
728
1101
  id: uuid1,
729
1102
  self: {
730
1103
  callBackInfo: {
731
- callbackAddress: uri1
732
- }
733
- }
1104
+ callbackAddress: uri1,
1105
+ },
1106
+ },
1107
+ info: {
1108
+ webExMeetingId,
1109
+ },
734
1110
  },
735
1111
  eventType: test1,
736
- locusUrl: url1
1112
+ locusUrl: url1,
737
1113
  });
738
- assert.callCount(webex.meetings.meetingCollection.getByKey, 4);
1114
+ assert.callCount(webex.meetings.meetingCollection.getByKey, 5);
739
1115
  assert.calledWith(webex.meetings.meetingCollection.getByKey, 'locusUrl', url1);
1116
+ assert.calledWith(
1117
+ webex.meetings.meetingCollection.getByKey,
1118
+ 'meetingNumber',
1119
+ webExMeetingId
1120
+ );
740
1121
  assert.calledOnce(initialSetup);
741
1122
  assert.calledWith(initialSetup, {
742
1123
  id: uuid1,
743
1124
  self: {
744
1125
  callBackInfo: {
745
- callbackAddress: uri1
746
- }
747
- }
1126
+ callbackAddress: uri1,
1127
+ },
1128
+ },
1129
+ info: {
1130
+ webExMeetingId,
1131
+ },
748
1132
  });
749
1133
  });
750
1134
 
@@ -753,13 +1137,13 @@ describe('plugin-meetings', () => {
753
1137
  id: uuid1,
754
1138
  self: {
755
1139
  callbackInfo: {
756
- callbackAddress: uri1
757
- }
1140
+ callbackAddress: uri1,
1141
+ },
758
1142
  },
759
1143
  info: {
760
- isUnifiedSpaceMeeting
1144
+ isUnifiedSpaceMeeting,
761
1145
  },
762
- conversationUrl: 'fakeConvoUrl'
1146
+ conversationUrl: 'fakeConvoUrl',
763
1147
  },
764
1148
  eventType: test1,
765
1149
  locusUrl: url1,
@@ -767,19 +1151,40 @@ describe('plugin-meetings', () => {
767
1151
 
768
1152
  it('should not try to match USM meetings by conversation url', async () => {
769
1153
  await webex.meetings.handleLocusEvent(generateFakeLocusData(true));
770
- assert.callCount(webex.meetings.meetingCollection.getByKey, 3);
771
- assert.deepEqual(webex.meetings.meetingCollection.getByKey.getCall(0).args, ['locusUrl', url1]);
772
- assert.deepEqual(webex.meetings.meetingCollection.getByKey.getCall(1).args, ['correlationId', false]);
773
- assert.deepEqual(webex.meetings.meetingCollection.getByKey.getCall(2).args, ['sipUri', uri1]);
1154
+ assert.callCount(webex.meetings.meetingCollection.getByKey, 4);
1155
+ assert.deepEqual(webex.meetings.meetingCollection.getByKey.getCall(0).args, [
1156
+ 'locusUrl',
1157
+ url1,
1158
+ ]);
1159
+ assert.deepEqual(webex.meetings.meetingCollection.getByKey.getCall(1).args, [
1160
+ 'correlationId',
1161
+ false,
1162
+ ]);
1163
+ assert.deepEqual(webex.meetings.meetingCollection.getByKey.getCall(2).args, [
1164
+ 'sipUri',
1165
+ uri1,
1166
+ ]);
774
1167
  assert.calledOnce(initialSetup);
775
1168
  });
776
1169
  it('should try to match non-USM meetings by conversation url', async () => {
777
1170
  await webex.meetings.handleLocusEvent(generateFakeLocusData(false));
778
- assert.callCount(webex.meetings.meetingCollection.getByKey, 4);
779
- assert.deepEqual(webex.meetings.meetingCollection.getByKey.getCall(0).args, ['locusUrl', url1]);
780
- assert.deepEqual(webex.meetings.meetingCollection.getByKey.getCall(1).args, ['correlationId', false]);
781
- assert.deepEqual(webex.meetings.meetingCollection.getByKey.getCall(2).args, ['sipUri', uri1]);
782
- assert.deepEqual(webex.meetings.meetingCollection.getByKey.getCall(3).args, ['conversationUrl', 'fakeConvoUrl']);
1171
+ assert.callCount(webex.meetings.meetingCollection.getByKey, 5);
1172
+ assert.deepEqual(webex.meetings.meetingCollection.getByKey.getCall(0).args, [
1173
+ 'locusUrl',
1174
+ url1,
1175
+ ]);
1176
+ assert.deepEqual(webex.meetings.meetingCollection.getByKey.getCall(1).args, [
1177
+ 'correlationId',
1178
+ false,
1179
+ ]);
1180
+ assert.deepEqual(webex.meetings.meetingCollection.getByKey.getCall(2).args, [
1181
+ 'sipUri',
1182
+ uri1,
1183
+ ]);
1184
+ assert.deepEqual(webex.meetings.meetingCollection.getByKey.getCall(3).args, [
1185
+ 'conversationUrl',
1186
+ 'fakeConvoUrl',
1187
+ ]);
783
1188
  assert.calledOnce(initialSetup);
784
1189
  });
785
1190
  });
@@ -794,15 +1199,23 @@ describe('plugin-meetings', () => {
794
1199
  });
795
1200
  describe('successful MeetingInfo.#fetchMeetingInfo', () => {
796
1201
  let clock, setTimeoutSpy, fakeMeetingStartTimeString, FAKE_TIME_TO_START;
1202
+ const FAKE_INFO_EXTRA_PARAMS = {
1203
+ mtid: 'm9fe0afd8c435e892afcce9ea25b97046',
1204
+ joinTXId: 'TSmrX61wNF',
1205
+ };
797
1206
 
798
1207
  beforeEach(() => {
799
1208
  clock = sinon.useFakeTimers();
800
1209
  setTimeoutSpy = sinon.spy(clock, 'setTimeout');
801
- webex.meetings.meetingInfo.fetchMeetingInfo = sinon.stub().returns(Promise.resolve({
802
- body: {
803
- permissionToken: 'PT', meetingJoinUrl: 'meetingJoinUrl'
804
- }
805
- }));
1210
+ webex.meetings.meetingInfo.fetchMeetingInfo = sinon.stub().returns(
1211
+ Promise.resolve({
1212
+ body: {
1213
+ permissionToken:
1214
+ 'eyJhbGciOiJIUzI1NiJ9.eyJleHAiOiIxMjM0NTYiLCJwZXJtaXNzaW9uIjp7InVzZXJQb2xpY2llcyI6eyJhIjp0cnVlfX19.wkTk0Hp8sUlq2wi2nP4-Ym4Xb7aEUHzyXA1kzk6f0V0',
1215
+ meetingJoinUrl: 'meetingJoinUrl',
1216
+ },
1217
+ })
1218
+ );
806
1219
  const nowTimeStamp = Date.now();
807
1220
 
808
1221
  FAKE_TIME_TO_START = 0.1 * 60 * 1000;
@@ -816,12 +1229,41 @@ describe('plugin-meetings', () => {
816
1229
  clock.restore();
817
1230
  });
818
1231
 
819
- const checkCreateWithoutDelay = (meeting, destination, type, expectedMeetingData = {}) => {
820
- assert.calledOnce(webex.meetings.meetingInfo.fetchMeetingInfo);
1232
+ const checkCreateWithoutDelay = (
1233
+ meeting,
1234
+ destination,
1235
+ type,
1236
+ extraParams = {},
1237
+ expectedMeetingData = {},
1238
+ sendCAevents = false,
1239
+ injectMeetingInfo = false
1240
+ ) => {
1241
+ if (injectMeetingInfo) {
1242
+ assert.notCalled(webex.meetings.meetingInfo.fetchMeetingInfo);
1243
+ } else {
1244
+ assert.calledOnce(webex.meetings.meetingInfo.fetchMeetingInfo);
1245
+ }
1246
+
821
1247
  assert.calledOnce(MeetingsUtil.getMeetingAddedType);
822
1248
  assert.notCalled(setTimeoutSpy);
823
- assert.calledThrice(TriggerProxy.trigger);
824
- assert.calledWith(webex.meetings.meetingInfo.fetchMeetingInfo, destination, type);
1249
+ assert.callCount(TriggerProxy.trigger, 5);
1250
+
1251
+ if (injectMeetingInfo) {
1252
+ assert.notCalled(webex.meetings.meetingInfo.fetchMeetingInfo);
1253
+ } else {
1254
+ assert.calledWith(
1255
+ webex.meetings.meetingInfo.fetchMeetingInfo,
1256
+ destination,
1257
+ type,
1258
+ null,
1259
+ null,
1260
+ undefined,
1261
+ undefined,
1262
+ extraParams,
1263
+ {meetingId: meeting.id, sendCAevents}
1264
+ );
1265
+ }
1266
+
825
1267
  assert.calledWith(MeetingsUtil.getMeetingAddedType, 'test type');
826
1268
 
827
1269
  if (expectedMeetingData.permissionToken) {
@@ -830,94 +1272,257 @@ describe('plugin-meetings', () => {
830
1272
  if (expectedMeetingData.meetingJoinUrl) {
831
1273
  assert.equal(meeting.meetingJoinUrl, expectedMeetingData.meetingJoinUrl);
832
1274
  }
1275
+ if (expectedMeetingData.correlationId) {
1276
+ assert.equal(meeting.correlationId, expectedMeetingData.correlationId);
1277
+ }
1278
+ if (expectedMeetingData.callStateForMetrics) {
1279
+ assert.deepEqual(
1280
+ meeting.callStateForMetrics,
1281
+ expectedMeetingData.callStateForMetrics
1282
+ );
1283
+ }
1284
+ if (expectedMeetingData.meetingLookupUrl) {
1285
+ assert.equal(
1286
+ meeting.meetingInfo.meetingLookupUrl,
1287
+ expectedMeetingData.meetingLookupUrl
1288
+ );
1289
+ }
833
1290
  assert.equal(meeting.destination, destination);
834
1291
  assert.equal(meeting.destinationType, type);
835
- assert.calledWith(TriggerProxy.trigger, sinon.match.instanceOf(Meetings), {
836
- file: 'meetings', function: 'createMeeting'
837
- }, 'meeting:added', {
838
- meeting: sinon.match.instanceOf(Meeting), type: 'test meeting added type'
839
- });
840
- assert.calledWith(TriggerProxy.trigger, meeting, {file: 'meetings', function: 'fetchMeetingInfo'}, 'meeting:meetingInfoAvailable');
1292
+ assert.calledWith(
1293
+ TriggerProxy.trigger,
1294
+ sinon.match.instanceOf(Meetings),
1295
+ {
1296
+ file: 'meetings',
1297
+ function: 'createMeeting',
1298
+ },
1299
+ 'meeting:added',
1300
+ {
1301
+ meeting: sinon.match.instanceOf(Meeting),
1302
+ type: 'test meeting added type',
1303
+ }
1304
+ );
1305
+ assert.calledWith(
1306
+ TriggerProxy.trigger,
1307
+ meeting,
1308
+ {file: 'meetings', function: 'fetchMeetingInfo'},
1309
+ 'meeting:meetingInfoAvailable'
1310
+ );
841
1311
  };
842
1312
 
843
1313
  it('creates the meeting from a successful meeting info fetch promise testing', async () => {
844
1314
  const meeting = await webex.meetings.createMeeting('test destination', 'test type');
845
1315
 
846
1316
  const expectedMeetingData = {
847
- permissionToken: 'PT',
848
- meetingJoinUrl: 'meetingJoinUrl'
1317
+ permissionToken:
1318
+ 'eyJhbGciOiJIUzI1NiJ9.eyJleHAiOiIxMjM0NTYiLCJwZXJtaXNzaW9uIjp7InVzZXJQb2xpY2llcyI6eyJhIjp0cnVlfX19.wkTk0Hp8sUlq2wi2nP4-Ym4Xb7aEUHzyXA1kzk6f0V0',
1319
+ meetingJoinUrl: 'meetingJoinUrl',
1320
+ correlationId: meeting.id,
849
1321
  };
850
1322
 
851
- checkCreateWithoutDelay(meeting, 'test destination', 'test type', expectedMeetingData);
1323
+ checkCreateWithoutDelay(
1324
+ meeting,
1325
+ 'test destination',
1326
+ 'test type',
1327
+ {},
1328
+ expectedMeetingData
1329
+ );
852
1330
  });
853
1331
 
854
- it('creates the meeting from a successful meeting info fetch meeting resolve testing', async () => {
855
- const meeting = await webex.meetings.createMeeting('test destination', 'test type');
1332
+ it('accepts injected meeting info', async () => {
1333
+ const meetingInfo = {
1334
+ permissionToken:
1335
+ 'eyJhbGciOiJIUzI1NiJ9.eyJleHAiOiIxMjM0NTYiLCJwZXJtaXNzaW9uIjp7InVzZXJQb2xpY2llcyI6eyJhIjp0cnVlfX19.wkTk0Hp8sUlq2wi2nP4-Ym4Xb7aEUHzyXA1kzk6f0V0',
1336
+ meetingJoinUrl: 'meetingJoinUrl',
1337
+ };
1338
+
1339
+ const meeting = await webex.meetings.createMeeting(
1340
+ 'test destination',
1341
+ 'test type',
1342
+ false,
1343
+ {},
1344
+ undefined,
1345
+ false,
1346
+ meetingInfo
1347
+ );
1348
+
856
1349
  const expectedMeetingData = {
857
- permissionToken: 'PT',
858
- meetingJoinUrl: 'meetingJoinUrl'
1350
+ ...meetingInfo,
1351
+ correlationId: meeting.id,
859
1352
  };
860
1353
 
861
- assert.instanceOf(meeting, Meeting, 'createMeeting should eventually resolve to a Meeting Object');
862
- checkCreateWithoutDelay(meeting, 'test destination', 'test type', expectedMeetingData);
1354
+ checkCreateWithoutDelay(
1355
+ meeting,
1356
+ 'test destination',
1357
+ 'test type',
1358
+ {},
1359
+ expectedMeetingData,
1360
+ false,
1361
+ true
1362
+ );
863
1363
  });
864
1364
 
865
- it('creates the meeting from a successful meeting info fetch with random delay', async () => {
866
- const FAKE_LOCUS_MEETING = {
867
- conversationUrl: 'locusConvURL',
868
- url: 'locusUrl',
869
- info: {
870
- webExMeetingId: 'locusMeetingId',
871
- sipUri: 'locusSipUri',
872
- owner: 'locusOwner'
873
- },
874
- meeting: {
875
- startTime: fakeMeetingStartTimeString
876
- },
877
- fullState: {
878
- active: false
879
- }
1365
+ it('accepts injected meeting info with meeting lookup url', async () => {
1366
+ const meetingInfo = {
1367
+ permissionToken:
1368
+ 'eyJhbGciOiJIUzI1NiJ9.eyJleHAiOiIxMjM0NTYiLCJwZXJtaXNzaW9uIjp7InVzZXJQb2xpY2llcyI6eyJhIjp0cnVlfX19.wkTk0Hp8sUlq2wi2nP4-Ym4Xb7aEUHzyXA1kzk6f0V0',
1369
+ meetingJoinUrl: 'meetingJoinUrl',
880
1370
  };
881
1371
 
882
- const meeting = await webex.meetings.createMeeting(FAKE_LOCUS_MEETING, 'test type', true);
1372
+ const meeting = await webex.meetings.createMeeting(
1373
+ 'test destination',
1374
+ 'test type',
1375
+ false,
1376
+ {},
1377
+ undefined,
1378
+ false,
1379
+ meetingInfo,
1380
+ 'meetingLookupUrl'
1381
+ );
883
1382
 
884
- assert.instanceOf(meeting, Meeting, 'createMeeting should eventually resolve to a Meeting Object');
885
- assert.notCalled(webex.meetings.meetingInfo.fetchMeetingInfo);
886
- assert.calledOnce(setTimeoutSpy);
1383
+ const expectedMeetingData = {
1384
+ ...meetingInfo,
1385
+ meetingLookupUrl: 'meetingLookupUrl',
1386
+ correlationId: meeting.id,
1387
+ };
887
1388
 
888
- // Parse meeting info with locus object
889
- assert.equal(meeting.conversationUrl, 'locusConvURL');
890
- assert.equal(meeting.locusUrl, 'locusUrl');
891
- assert.equal(meeting.sipUri, 'locusSipUri');
892
- assert.equal(meeting.meetingNumber, 'locusMeetingId');
893
- assert.isUndefined(meeting.meetingJoinUrl);
894
- assert.equal(meeting.owner, 'locusOwner');
895
- assert.isUndefined(meeting.permissionToken);
1389
+ checkCreateWithoutDelay(
1390
+ meeting,
1391
+ 'test destination',
1392
+ 'test type',
1393
+ {},
1394
+ expectedMeetingData,
1395
+ false,
1396
+ true
1397
+ );
1398
+ });
896
1399
 
897
- // Add meeting and send trigger
898
- assert.calledWith(MeetingsUtil.getMeetingAddedType, 'test type');
899
- assert.calledTwice(TriggerProxy.trigger);
900
- assert.calledWith(TriggerProxy.trigger, sinon.match.instanceOf(Meetings), {
901
- file: 'meetings', function: 'createMeeting'
902
- }, 'meeting:added', {
903
- meeting: sinon.match.instanceOf(Meeting), type: 'test meeting added type'
1400
+ [undefined, FAKE_INFO_EXTRA_PARAMS].forEach((infoExtraParams) => {
1401
+ const infoExtraParamsProvided = infoExtraParams !== undefined;
1402
+
1403
+ it(`creates the meeting from a successful meeting info fetch meeting resolve testing${
1404
+ infoExtraParamsProvided ? ' with infoExtraParams' : ''
1405
+ }`, async () => {
1406
+ const meeting = await webex.meetings.createMeeting(
1407
+ 'test destination',
1408
+ 'test type',
1409
+ false,
1410
+ infoExtraParams
1411
+ );
1412
+ const expectedMeetingData = {
1413
+ permissionToken:
1414
+ 'eyJhbGciOiJIUzI1NiJ9.eyJleHAiOiIxMjM0NTYiLCJwZXJtaXNzaW9uIjp7InVzZXJQb2xpY2llcyI6eyJhIjp0cnVlfX19.wkTk0Hp8sUlq2wi2nP4-Ym4Xb7aEUHzyXA1kzk6f0V0',
1415
+ meetingJoinUrl: 'meetingJoinUrl',
1416
+ };
1417
+
1418
+ assert.instanceOf(
1419
+ meeting,
1420
+ Meeting,
1421
+ 'createMeeting should eventually resolve to a Meeting Object'
1422
+ );
1423
+ checkCreateWithoutDelay(
1424
+ meeting,
1425
+ 'test destination',
1426
+ 'test type',
1427
+ infoExtraParamsProvided ? infoExtraParams : {},
1428
+ expectedMeetingData
1429
+ );
904
1430
  });
905
1431
 
906
- // When timer expires
907
- clock.tick(FAKE_TIME_TO_START);
908
- assert.calledWith(webex.meetings.meetingInfo.fetchMeetingInfo, FAKE_LOCUS_MEETING, 'test type');
909
-
910
- // Parse meeting info is called again with new meeting info
911
- await testUtils.flushPromises();
912
- assert.equal(meeting.conversationUrl, 'locusConvURL');
913
- assert.equal(meeting.locusUrl, 'locusUrl');
914
- assert.equal(meeting.sipUri, 'locusSipUri');
915
- assert.equal(meeting.meetingNumber, 'locusMeetingId');
916
- assert.equal(meeting.meetingJoinUrl, 'meetingJoinUrl');
917
- assert.equal(meeting.owner, 'locusOwner');
918
- assert.equal(meeting.permissionToken, 'PT');
919
-
920
- assert.calledWith(TriggerProxy.trigger, meeting, {file: 'meetings', function: 'fetchMeetingInfo'}, 'meeting:meetingInfoAvailable');
1432
+ it(`creates the meeting from a successful meeting info fetch with random delay${
1433
+ infoExtraParamsProvided ? ' with infoExtraParams' : ''
1434
+ }`, async () => {
1435
+ const FAKE_LOCUS_MEETING = {
1436
+ conversationUrl: 'locusConvURL',
1437
+ url: 'locusUrl',
1438
+ info: {
1439
+ webExMeetingId: 'locusMeetingId',
1440
+ sipUri: 'locusSipUri',
1441
+ owner: 'locusOwner',
1442
+ },
1443
+ meeting: {
1444
+ startTime: fakeMeetingStartTimeString,
1445
+ },
1446
+ fullState: {
1447
+ active: false,
1448
+ },
1449
+ };
1450
+
1451
+ const meeting = await webex.meetings.createMeeting(
1452
+ FAKE_LOCUS_MEETING,
1453
+ 'test type',
1454
+ true,
1455
+ infoExtraParams
1456
+ );
1457
+
1458
+ assert.instanceOf(
1459
+ meeting,
1460
+ Meeting,
1461
+ 'createMeeting should eventually resolve to a Meeting Object'
1462
+ );
1463
+ assert.notCalled(webex.meetings.meetingInfo.fetchMeetingInfo);
1464
+ assert.calledOnce(setTimeoutSpy);
1465
+
1466
+ // Parse meeting info with locus object
1467
+ assert.equal(meeting.conversationUrl, 'locusConvURL');
1468
+ assert.equal(meeting.locusUrl, 'locusUrl');
1469
+ assert.equal(meeting.sipUri, 'locusSipUri');
1470
+ assert.equal(meeting.meetingNumber, 'locusMeetingId');
1471
+ assert.isUndefined(meeting.meetingJoinUrl);
1472
+ assert.equal(meeting.owner, 'locusOwner');
1473
+ assert.isUndefined(meeting.permissionToken);
1474
+
1475
+ // Add meeting and send trigger
1476
+ assert.calledWith(MeetingsUtil.getMeetingAddedType, 'test type');
1477
+ assert.calledTwice(TriggerProxy.trigger);
1478
+ assert.calledWith(
1479
+ TriggerProxy.trigger,
1480
+ sinon.match.instanceOf(Meetings),
1481
+ {
1482
+ file: 'meetings',
1483
+ function: 'createMeeting',
1484
+ },
1485
+ 'meeting:added',
1486
+ {
1487
+ meeting: sinon.match.instanceOf(Meeting),
1488
+ type: 'test meeting added type',
1489
+ }
1490
+ );
1491
+
1492
+ // When timer expires
1493
+ clock.tick(FAKE_TIME_TO_START);
1494
+ await testUtils.flushPromises();
1495
+
1496
+ assert.calledWith(
1497
+ webex.meetings.meetingInfo.fetchMeetingInfo,
1498
+ FAKE_LOCUS_MEETING,
1499
+ 'test type',
1500
+ null,
1501
+ null,
1502
+ undefined,
1503
+ undefined,
1504
+ infoExtraParamsProvided ? infoExtraParams : {}
1505
+ );
1506
+
1507
+ // Parse meeting info is called again with new meeting info
1508
+ await testUtils.flushPromises();
1509
+ assert.equal(meeting.conversationUrl, 'locusConvURL');
1510
+ assert.equal(meeting.locusUrl, 'locusUrl');
1511
+ assert.equal(meeting.sipUri, 'locusSipUri');
1512
+ assert.equal(meeting.meetingNumber, 'locusMeetingId');
1513
+ assert.equal(meeting.meetingJoinUrl, 'meetingJoinUrl');
1514
+ assert.equal(meeting.owner, 'locusOwner');
1515
+ assert.equal(
1516
+ meeting.permissionToken,
1517
+ 'eyJhbGciOiJIUzI1NiJ9.eyJleHAiOiIxMjM0NTYiLCJwZXJtaXNzaW9uIjp7InVzZXJQb2xpY2llcyI6eyJhIjp0cnVlfX19.wkTk0Hp8sUlq2wi2nP4-Ym4Xb7aEUHzyXA1kzk6f0V0'
1518
+ );
1519
+ assert.calledWith(
1520
+ TriggerProxy.trigger,
1521
+ meeting,
1522
+ {file: 'meetings', function: 'fetchMeetingInfo'},
1523
+ 'meeting:meetingInfoAvailable'
1524
+ );
1525
+ });
921
1526
  });
922
1527
 
923
1528
  it('creates the meeting from a successful meeting info fetch that has no random delay because it is active', async () => {
@@ -927,19 +1532,27 @@ describe('plugin-meetings', () => {
927
1532
  info: {
928
1533
  webExMeetingId: 'locusMeetingId',
929
1534
  sipUri: 'locusSipUri',
930
- owner: 'locusOwner'
1535
+ owner: 'locusOwner',
931
1536
  },
932
1537
  meeting: {
933
- startTime: fakeMeetingStartTimeString
1538
+ startTime: fakeMeetingStartTimeString,
934
1539
  },
935
1540
  fullState: {
936
- active: true
937
- }
1541
+ active: true,
1542
+ },
938
1543
  };
939
1544
 
940
- const meeting = await webex.meetings.createMeeting(FAKE_LOCUS_MEETING, 'test type', true);
1545
+ const meeting = await webex.meetings.createMeeting(
1546
+ FAKE_LOCUS_MEETING,
1547
+ 'test type',
1548
+ true
1549
+ );
941
1550
 
942
- assert.instanceOf(meeting, Meeting, 'createMeeting should eventually resolve to a Meeting Object');
1551
+ assert.instanceOf(
1552
+ meeting,
1553
+ Meeting,
1554
+ 'createMeeting should eventually resolve to a Meeting Object'
1555
+ );
943
1556
  checkCreateWithoutDelay(meeting, FAKE_LOCUS_MEETING, 'test type');
944
1557
  });
945
1558
 
@@ -950,27 +1563,35 @@ describe('plugin-meetings', () => {
950
1563
  info: {
951
1564
  webExMeetingId: 'locusMeetingId',
952
1565
  sipUri: 'locusSipUri',
953
- owner: 'locusOwner'
1566
+ owner: 'locusOwner',
954
1567
  },
955
1568
  meeting: {
956
- startTime: fakeMeetingStartTimeString - (1 * 60 * 60 * 1000)
1569
+ startTime: fakeMeetingStartTimeString - 1 * 60 * 60 * 1000,
957
1570
  },
958
1571
  fullState: {
959
- active: false
960
- }
1572
+ active: false,
1573
+ },
961
1574
  };
962
1575
 
963
- const meeting = await webex.meetings.createMeeting(FAKE_LOCUS_MEETING, 'test type', true);
1576
+ const meeting = await webex.meetings.createMeeting(
1577
+ FAKE_LOCUS_MEETING,
1578
+ 'test type',
1579
+ true
1580
+ );
964
1581
 
965
- assert.instanceOf(meeting, Meeting, 'createMeeting should eventually resolve to a Meeting Object');
1582
+ assert.instanceOf(
1583
+ meeting,
1584
+ Meeting,
1585
+ 'createMeeting should eventually resolve to a Meeting Object'
1586
+ );
966
1587
  checkCreateWithoutDelay(meeting, FAKE_LOCUS_MEETING, 'test type');
967
1588
  });
968
1589
 
969
1590
  it('creates the meeting from a successful meeting info fetch that has no random delay because enableUnifiedMeetings is disabled', async () => {
970
1591
  Object.assign(webex.meetings.config, {
971
1592
  experimental: {
972
- enableUnifiedMeetings: false
973
- }
1593
+ enableUnifiedMeetings: false,
1594
+ },
974
1595
  });
975
1596
  const FAKE_LOCUS_MEETING = {
976
1597
  conversationUrl: 'locusConvURL',
@@ -978,51 +1599,228 @@ describe('plugin-meetings', () => {
978
1599
  info: {
979
1600
  webExMeetingId: 'locusMeetingId',
980
1601
  sipUri: 'locusSipUri',
981
- owner: 'locusOwner'
1602
+ owner: 'locusOwner',
982
1603
  },
983
1604
  meeting: {
984
- startTime: fakeMeetingStartTimeString
1605
+ startTime: fakeMeetingStartTimeString,
985
1606
  },
986
1607
  fullState: {
987
- active: false
988
- }
1608
+ active: false,
1609
+ },
989
1610
  };
990
1611
 
991
- const meeting = await webex.meetings.createMeeting(FAKE_LOCUS_MEETING, 'test type', true);
1612
+ const meeting = await webex.meetings.createMeeting(
1613
+ FAKE_LOCUS_MEETING,
1614
+ 'test type',
1615
+ true
1616
+ );
992
1617
 
993
- assert.instanceOf(meeting, Meeting, 'createMeeting should eventually resolve to a Meeting Object');
1618
+ assert.instanceOf(
1619
+ meeting,
1620
+ Meeting,
1621
+ 'createMeeting should eventually resolve to a Meeting Object'
1622
+ );
994
1623
  checkCreateWithoutDelay(meeting, FAKE_LOCUS_MEETING, 'test type');
995
1624
  });
1625
+
1626
+ it('creates meeting with the correlationId provided', async () => {
1627
+ const meeting = await webex.meetings.createMeeting(
1628
+ 'test destination',
1629
+ 'test type',
1630
+ false,
1631
+ {},
1632
+ {correlationId: 'my-correlationId'}
1633
+ );
1634
+
1635
+ const expectedMeetingData = {
1636
+ correlationId: 'my-correlationId',
1637
+ };
1638
+
1639
+ checkCreateWithoutDelay(
1640
+ meeting,
1641
+ 'test destination',
1642
+ 'test type',
1643
+ {},
1644
+ expectedMeetingData,
1645
+ true
1646
+ );
1647
+ });
1648
+
1649
+ it('creates meeting with the callStateForMetrics provided', async () => {
1650
+ const meeting = await webex.meetings.createMeeting(
1651
+ 'test destination',
1652
+ 'test type',
1653
+ false,
1654
+ {},
1655
+ {
1656
+ correlationId: 'my-correlationId',
1657
+ joinTrigger: 'my-join-trigger',
1658
+ loginType: 'my-login-type',
1659
+ }
1660
+ );
1661
+
1662
+ const expectedMeetingData = {
1663
+ correlationId: 'my-correlationId',
1664
+ callStateForMetrics: {
1665
+ correlationId: 'my-correlationId',
1666
+ joinTrigger: 'my-join-trigger',
1667
+ loginType: 'my-login-type',
1668
+ },
1669
+ };
1670
+
1671
+ checkCreateWithoutDelay(
1672
+ meeting,
1673
+ 'test destination',
1674
+ 'test type',
1675
+ {},
1676
+ expectedMeetingData,
1677
+ true
1678
+ );
1679
+ });
996
1680
  });
997
1681
 
998
1682
  describe('rejected MeetingInfo.#fetchMeetingInfo', () => {
999
1683
  beforeEach(() => {
1000
1684
  console.error = sinon.stub().returns(false);
1001
1685
  TriggerProxy.trigger.reset();
1002
- webex.meetings.meetingInfo.fetchMeetingInfo = sinon.stub().returns(Promise.reject(new Error('test')));
1686
+ webex.meetings.meetingInfo.fetchMeetingInfo = sinon
1687
+ .stub()
1688
+ .returns(Promise.reject(new Error('test')));
1689
+ webex.meetings.destroy = sinon.stub().returns(Promise.resolve());
1690
+ webex.meetings.createMeeting = sinon.spy(webex.meetings.createMeeting);
1003
1691
  });
1692
+
1693
+ const checkCreateMeetingWithNoMeetingInfo = async (failOnMissingMeetingInfo, destroy) => {
1694
+ try {
1695
+ const meeting = await webex.meetings.createMeeting(
1696
+ 'test destination',
1697
+ 'test type',
1698
+ undefined,
1699
+ undefined,
1700
+ undefined,
1701
+ failOnMissingMeetingInfo
1702
+ );
1703
+
1704
+ assert.instanceOf(
1705
+ meeting,
1706
+ Meeting,
1707
+ 'createMeeting should eventually resolve to a Meeting Object'
1708
+ );
1709
+ assert.calledOnce(webex.meetings.meetingInfo.fetchMeetingInfo);
1710
+ assert.calledOnce(MeetingsUtil.getMeetingAddedType);
1711
+ assert.calledThrice(TriggerProxy.trigger);
1712
+ assert.calledWith(
1713
+ webex.meetings.meetingInfo.fetchMeetingInfo,
1714
+ 'test destination',
1715
+ 'test type'
1716
+ );
1717
+
1718
+ if (destroy) {
1719
+ assert.calledWith(
1720
+ webex.meetings.destroy,
1721
+ sinon.match.instanceOf(Meeting),
1722
+ 'MISSING_MEETING_INFO'
1723
+ );
1724
+ assert.notCalled(MeetingsUtil.getMeetingAddedType);
1725
+ assert.notCalled(TriggerProxy.trigger);
1726
+ assert.throw(webex.meetings.createMeeting, 'meeting information not found');
1727
+ } else {
1728
+ assert.notCalled(webex.meetings.destroy);
1729
+ assert.calledWith(MeetingsUtil.getMeetingAddedType, 'test type');
1730
+ assert.calledWith(
1731
+ TriggerProxy.trigger,
1732
+ sinon.match.instanceOf(Meetings),
1733
+ {
1734
+ file: 'meetings',
1735
+ function: 'createMeeting',
1736
+ },
1737
+ 'meeting:added',
1738
+ {
1739
+ meeting: sinon.match.instanceOf(Meeting),
1740
+ type: 'test meeting added type',
1741
+ }
1742
+ );
1743
+ }
1744
+ } catch (err) {
1745
+ assert.instanceOf(err, NoMeetingInfoError);
1746
+ }
1747
+ };
1748
+
1004
1749
  it('creates the meeting from a rejected meeting info fetch', async () => {
1005
- const meeting = await webex.meetings.createMeeting('test destination', 'test type');
1750
+ checkCreateMeetingWithNoMeetingInfo(false, false);
1751
+ });
1006
1752
 
1007
- assert.instanceOf(meeting, Meeting, 'createMeeting should eventually resolve to a Meeting Object');
1008
- assert.calledOnce(webex.meetings.meetingInfo.fetchMeetingInfo);
1009
- assert.calledOnce(MeetingsUtil.getMeetingAddedType);
1010
- assert.calledTwice(TriggerProxy.trigger);
1011
- assert.calledWith(webex.meetings.meetingInfo.fetchMeetingInfo, 'test destination', 'test type');
1012
- assert.calledWith(MeetingsUtil.getMeetingAddedType, 'test type');
1013
- assert.calledWith(TriggerProxy.trigger, sinon.match.instanceOf(Meetings), {
1014
- file: 'meetings', function: 'createMeeting'
1015
- }, 'meeting:added', {
1016
- meeting: sinon.match.instanceOf(Meeting), type: 'test meeting added type'
1017
- });
1753
+ it('creates the meeting from a rejected meeting info fetch and destroys it if failOnMissingMeetingInfo', async () => {
1754
+ checkCreateMeetingWithNoMeetingInfo(true, true);
1018
1755
  });
1019
1756
  });
1757
+
1758
+ describe('rejected MeetingInfo.#fetchMeetingInfo - does not log for known Error types', () => {
1759
+ forEach(
1760
+ [
1761
+ {
1762
+ error: new CaptchaError(),
1763
+ debugLogMessage:
1764
+ 'Meetings:index#createMeeting --> Debug CaptchaError: Captcha is required. fetching /meetingInfo for creation.',
1765
+ },
1766
+ {
1767
+ error: new PasswordError(),
1768
+ debugLogMessage:
1769
+ 'Meetings:index#createMeeting --> Debug PasswordError: Password is required, please use verifyPassword() fetching /meetingInfo for creation.',
1770
+ },
1771
+ {
1772
+ error: new PermissionError(),
1773
+ debugLogMessage:
1774
+ '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.',
1775
+ },
1776
+ {
1777
+ error: new Error(),
1778
+ infoLogMessage: true,
1779
+ debugLogMessage:
1780
+ 'Meetings:index#createMeeting --> Debug Error fetching /meetingInfo for creation.',
1781
+ },
1782
+ ],
1783
+ ({error, debugLogMessage, infoLogMessage}) => {
1784
+ it('creates the meeting from a rejected meeting info fetch', async () => {
1785
+ webex.meetings.meetingInfo.fetchMeetingInfo = sinon
1786
+ .stub()
1787
+ .returns(Promise.reject(error));
1788
+
1789
+ LoggerProxy.logger.debug = sinon.stub();
1790
+ LoggerProxy.logger.info = sinon.stub();
1791
+
1792
+ const meeting = await webex.meetings.createMeeting('test destination', 'test type');
1793
+
1794
+ assert.instanceOf(
1795
+ meeting,
1796
+ Meeting,
1797
+ 'createMeeting should eventually resolve to a Meeting Object'
1798
+ );
1799
+
1800
+ assert.calledWith(LoggerProxy.logger.debug, debugLogMessage);
1801
+
1802
+ if (infoLogMessage) {
1803
+ assert.calledWith(
1804
+ LoggerProxy.logger.info,
1805
+ 'Meetings:index#createMeeting --> Info Unable to fetch meeting info for test destination.'
1806
+ );
1807
+ } else {
1808
+ assert.notCalled(LoggerProxy.logger.info);
1809
+ }
1810
+ });
1811
+ }
1812
+ );
1813
+ });
1020
1814
  });
1021
1815
  });
1022
1816
  describe('Public Event Triggers', () => {
1817
+ let cleanUpSpy;
1023
1818
  describe('#destroy', () => {
1024
1819
  beforeEach(() => {
1025
- MeetingUtil.cleanUp = sinon.stub();
1820
+ cleanUpSpy = sinon.stub(MeetingUtil, 'cleanUp');
1821
+ });
1822
+ afterEach(() => {
1823
+ cleanUpSpy.restore();
1026
1824
  });
1027
1825
  it('should have #destroy', () => {
1028
1826
  assert.exists(webex.meetings.destroy);
@@ -1039,11 +1837,19 @@ describe('plugin-meetings', () => {
1039
1837
 
1040
1838
  assert.calledOnce(webex.meetings.meetingCollection.delete);
1041
1839
  assert.calledWith(webex.meetings.meetingCollection.delete, meeting.id);
1042
- assert.calledWith(TriggerProxy.trigger, sinon.match.instanceOf(Meetings), {
1043
- file: 'meetings', function: 'destroy'
1044
- }, 'meeting:removed', {
1045
- meetingId: meeting.id, reason: test1
1046
- });
1840
+ assert.calledWith(
1841
+ TriggerProxy.trigger,
1842
+ sinon.match.instanceOf(Meetings),
1843
+ {
1844
+ file: 'meetings',
1845
+ function: 'destroy',
1846
+ },
1847
+ 'meeting:removed',
1848
+ {
1849
+ meetingId: meeting.id,
1850
+ reason: test1,
1851
+ }
1852
+ );
1047
1853
  });
1048
1854
  });
1049
1855
 
@@ -1068,7 +1874,8 @@ describe('plugin-meetings', () => {
1068
1874
  it('should trigger event upon mercury disconnect', () => {
1069
1875
  const {meetings} = webex;
1070
1876
  const SCOPE = {
1071
- file: 'meetings/index', function: 'handleMercuryOffline'
1877
+ file: 'meetings/index',
1878
+ function: 'handleMercuryOffline',
1072
1879
  };
1073
1880
  const EVENT = 'network:disconnected';
1074
1881
 
@@ -1086,6 +1893,8 @@ describe('plugin-meetings', () => {
1086
1893
  });
1087
1894
 
1088
1895
  describe('#fetchUserPreferredWebexSite', () => {
1896
+ let loggerProxySpy;
1897
+
1089
1898
  it('should call request.getMeetingPreferences to get the preferred webex site ', async () => {
1090
1899
  assert.isDefined(webex.meetings.preferredWebexSite);
1091
1900
  await webex.meetings.fetchUserPreferredWebexSite();
@@ -1093,19 +1902,140 @@ describe('plugin-meetings', () => {
1093
1902
  assert.equal(webex.meetings.preferredWebexSite, 'go.webex.com');
1094
1903
  });
1095
1904
 
1905
+ const setup = ({user} = {}) => {
1906
+ loggerProxySpy = sinon.spy(LoggerProxy.logger, 'error');
1907
+
1908
+ Object.assign(webex.internal, {
1909
+ services: {
1910
+ getMeetingPreferences: sinon.stub().returns(Promise.resolve({})),
1911
+ },
1912
+ user: {
1913
+ get: sinon.stub().returns(Promise.resolve(user)),
1914
+ },
1915
+ });
1916
+ };
1917
+
1096
1918
  it('should not fail if UserPreferred info is not fetched ', async () => {
1919
+ setup();
1920
+
1097
1921
  Object.assign(webex.internal, {
1098
1922
  services: {
1099
1923
  getMeetingPreferences: sinon.stub().returns(Promise.resolve({})),
1100
1924
  },
1925
+ });
1926
+
1927
+ await webex.meetings.fetchUserPreferredWebexSite().then(() => {
1928
+ assert.equal(webex.meetings.preferredWebexSite, '');
1929
+ });
1930
+ assert.calledOnceWithExactly(
1931
+ loggerProxySpy,
1932
+ 'Failed to fetch preferred site from user - no site will be set'
1933
+ );
1934
+ });
1101
1935
 
1936
+ it('should fall back to fetching the site from the user', async () => {
1937
+ setup({
1938
+ user: {
1939
+ userPreferences: {
1940
+ userPreferencesItems: {
1941
+ preferredWebExSite: 'site.webex.com',
1942
+ },
1943
+ },
1944
+ },
1102
1945
  });
1103
1946
 
1104
- await webex.meetings.fetchUserPreferredWebexSite()
1105
- .then(() => {
1947
+ await webex.meetings.fetchUserPreferredWebexSite();
1948
+
1949
+ assert.equal(webex.meetings.preferredWebexSite, 'site.webex.com');
1950
+ assert.notCalled(loggerProxySpy);
1951
+ });
1952
+
1953
+ forEach(
1954
+ [
1955
+ {user: undefined},
1956
+ {user: {userPreferences: {}}},
1957
+ {user: {userPreferences: {userPreferencesItems: {}}}},
1958
+ {user: {userPreferences: {userPreferencesItems: {preferredWebExSite: undefined}}}},
1959
+ ],
1960
+ ({user}) => {
1961
+ it(`should handle invalid user data ${user}`, async () => {
1962
+ setup({user});
1963
+
1964
+ await webex.meetings.fetchUserPreferredWebexSite();
1965
+
1106
1966
  assert.equal(webex.meetings.preferredWebexSite, '');
1967
+ assert.calledOnceWithExactly(
1968
+ loggerProxySpy,
1969
+ 'Failed to fetch preferred site from user - no site will be set'
1970
+ );
1107
1971
  });
1972
+ }
1973
+ );
1974
+
1975
+ it('should handle a get user failure', async () => {
1976
+ setup();
1977
+
1978
+ webex.internal.user.get.rejects(new Error());
1979
+
1980
+ await webex.meetings.fetchUserPreferredWebexSite();
1981
+
1982
+ assert.equal(webex.meetings.preferredWebexSite, '');
1983
+ assert.calledOnceWithExactly(
1984
+ loggerProxySpy,
1985
+ 'Failed to fetch preferred site from user - no site will be set'
1986
+ );
1987
+ });
1988
+
1989
+ it('should fall back to fetching the site from the user', async () => {
1990
+ setup({
1991
+ user: {
1992
+ userPreferences: {
1993
+ userPreferencesItems: {
1994
+ preferredWebExSite: 'site.webex.com',
1995
+ },
1996
+ },
1997
+ },
1998
+ });
1999
+
2000
+ await webex.meetings.fetchUserPreferredWebexSite();
2001
+
2002
+ assert.equal(webex.meetings.preferredWebexSite, 'site.webex.com');
2003
+ assert.notCalled(loggerProxySpy);
2004
+ });
2005
+
2006
+ forEach([
2007
+ {user: undefined},
2008
+ {user: {userPreferences: {}}},
2009
+ {user: {userPreferences: {userPreferencesItems: {}}}},
2010
+ {user: {userPreferences: {userPreferencesItems: {preferredWebExSite: undefined}}}},
2011
+ ], ({user}) => {
2012
+ it(`should handle invalid user data ${user}`, async () => {
2013
+ setup({user});
2014
+
2015
+ await webex.meetings.fetchUserPreferredWebexSite();
2016
+
2017
+ assert.equal(webex.meetings.preferredWebexSite, '');
2018
+ assert.calledOnceWithExactly(
2019
+ loggerProxySpy,
2020
+ 'Failed to fetch preferred site from user - no site will be set'
2021
+ );
2022
+ });
2023
+ });
2024
+
2025
+ it('should handle a get user failure', async () => {
2026
+ setup();
2027
+
2028
+ webex.internal.user.get.rejects(new Error());
2029
+
2030
+ await webex.meetings.fetchUserPreferredWebexSite();
2031
+
2032
+ assert.equal(webex.meetings.preferredWebexSite, '');
2033
+ assert.calledOnceWithExactly(
2034
+ loggerProxySpy,
2035
+ 'Failed to fetch preferred site from user - no site will be set'
2036
+ );
1108
2037
  });
2038
+
1109
2039
  });
1110
2040
  });
1111
2041
 
@@ -1120,11 +2050,15 @@ describe('plugin-meetings', () => {
1120
2050
  TriggerProxy.trigger.reset();
1121
2051
  // clock = sinon.useFakeTimers();
1122
2052
  // setTimeoutSpy = sinon.spy(clock, 'setTimeout');
1123
- webex.meetings.meetingInfo.fetchMeetingInfo = sinon.stub().returns(Promise.resolve({
1124
- body: {
1125
- permissionToken: 'PT', meetingJoinUrl: 'meetingJoinUrl'
1126
- }
1127
- }));
2053
+ webex.meetings.meetingInfo.fetchMeetingInfo = sinon.stub().returns(
2054
+ Promise.resolve({
2055
+ body: {
2056
+ permissionToken:
2057
+ 'eyJhbGciOiJIUzI1NiJ9.eyJleHAiOiIxMjM0NTYiLCJwZXJtaXNzaW9uIjp7InVzZXJQb2xpY2llcyI6eyJhIjp0cnVlfX19.wkTk0Hp8sUlq2wi2nP4-Ym4Xb7aEUHzyXA1kzk6f0V0',
2058
+ meetingJoinUrl: 'meetingJoinUrl',
2059
+ },
2060
+ })
2061
+ );
1128
2062
 
1129
2063
  meeting = await webex.meetings.createMeeting('test destination', 'test type');
1130
2064
 
@@ -1132,37 +2066,37 @@ describe('plugin-meetings', () => {
1132
2066
  });
1133
2067
 
1134
2068
  it('triggers correct event when CONTROLS_ENTRY_EXIT_TONE_UPDATED emitted', async () => {
1135
- await meeting.locusInfo.emitScoped(
1136
- {},
1137
- LOCUSINFO.EVENTS.CONTROLS_ENTRY_EXIT_TONE_UPDATED,
1138
- {entryExitTone: 'foo'}
1139
- );
2069
+ await meeting.locusInfo.emitScoped({}, LOCUSINFO.EVENTS.CONTROLS_ENTRY_EXIT_TONE_UPDATED, {
2070
+ entryExitTone: 'foo',
2071
+ });
1140
2072
 
1141
2073
  assert.calledOnce(TriggerProxy.trigger);
1142
- assert.calledWith(TriggerProxy.trigger, sinon.match.instanceOf(Meeting),
2074
+ assert.calledWith(
2075
+ TriggerProxy.trigger,
2076
+ sinon.match.instanceOf(Meeting),
1143
2077
  {
1144
2078
  file: 'meeting/index',
1145
- function: 'setupLocusControlsListener'
2079
+ function: 'setupLocusControlsListener',
1146
2080
  },
1147
2081
  EVENT_TRIGGERS.MEETING_ENTRY_EXIT_TONE_UPDATE,
1148
- {entryExitTone: 'foo'});
2082
+ {entryExitTone: 'foo'}
2083
+ );
1149
2084
  });
1150
2085
 
1151
2086
  const checkSelfTrigger = async (inEvent, outEvent) => {
1152
- await meeting.locusInfo.emitScoped(
1153
- {},
1154
- inEvent,
1155
- {foo: 'bar'}
1156
- );
2087
+ await meeting.locusInfo.emitScoped({}, inEvent, {foo: 'bar'});
1157
2088
 
1158
2089
  assert.calledOnce(TriggerProxy.trigger);
1159
- assert.calledWith(TriggerProxy.trigger, sinon.match.instanceOf(Meeting),
2090
+ assert.calledWith(
2091
+ TriggerProxy.trigger,
2092
+ sinon.match.instanceOf(Meeting),
1160
2093
  {
1161
2094
  file: 'meeting/index',
1162
- function: 'setUpLocusInfoSelfListener'
2095
+ function: 'setUpLocusInfoSelfListener',
1163
2096
  },
1164
2097
  outEvent,
1165
- {payload: {foo: 'bar'}});
2098
+ {payload: {foo: 'bar'}}
2099
+ );
1166
2100
  };
1167
2101
 
1168
2102
  it('triggers correct event when SELF_CANNOT_VIEW_PARTICIPANT_LIST_CHANGE emitted', async () => {
@@ -1186,5 +2120,523 @@ describe('plugin-meetings', () => {
1186
2120
  );
1187
2121
  });
1188
2122
  });
2123
+
2124
+ describe('#isNeedHandleMainLocus', () => {
2125
+ let meeting;
2126
+ let newLocus;
2127
+ beforeEach(() => {
2128
+ meeting = {
2129
+ controls: {},
2130
+ self: {},
2131
+ };
2132
+ newLocus = {
2133
+ controls: {},
2134
+ self: {},
2135
+ };
2136
+ });
2137
+ afterEach(() => {
2138
+ sinon.restore();
2139
+ });
2140
+ it('check normal case will return true', () => {
2141
+ sinon.stub(webex.meetings.meetingCollection, 'getActiveBreakoutLocus').returns(null);
2142
+ LoggerProxy.logger.log = sinon.stub();
2143
+ const result = webex.meetings.isNeedHandleMainLocus(meeting, newLocus);
2144
+ assert.equal(result, true);
2145
+ assert.calledWith(
2146
+ LoggerProxy.logger.log,
2147
+ 'Meetings:index#isNeedHandleMainLocus --> this is a normal main session locusDTO update case'
2148
+ );
2149
+ });
2150
+
2151
+ it('check self joined and joined on this device, return true', () => {
2152
+ sinon.stub(webex.meetings.meetingCollection, 'getActiveBreakoutLocus').returns(null);
2153
+ newLocus.self.state = 'JOINED';
2154
+ sinon.stub(MeetingsUtil, 'joinedOnThisDevice').returns(true);
2155
+
2156
+ LoggerProxy.logger.log = sinon.stub();
2157
+ const result = webex.meetings.isNeedHandleMainLocus(meeting, newLocus);
2158
+ assert.equal(result, true);
2159
+ assert.calledWith(
2160
+ LoggerProxy.logger.log,
2161
+ 'Meetings:index#isNeedHandleMainLocus --> self this device shown as JOINED in the main session'
2162
+ );
2163
+ });
2164
+
2165
+ it('if newLocus replaceAt time is expired, then return false', () => {
2166
+ sinon.stub(webex.meetings.meetingCollection, 'getActiveBreakoutLocus').returns({
2167
+ joinedWith: {
2168
+ replaces: [
2169
+ {
2170
+ replaceAt: '2023-03-27T02:17:02.506Z',
2171
+ },
2172
+ ],
2173
+ },
2174
+ });
2175
+ newLocus.self.state = 'JOINED';
2176
+ sinon.stub(MeetingsUtil, 'joinedOnThisDevice').returns(true);
2177
+ sinon.stub(MeetingsUtil, 'getThisDevice').returns({
2178
+ replaces: [
2179
+ {
2180
+ replaceAt: '2023-03-27T02:17:01.506Z',
2181
+ },
2182
+ ],
2183
+ });
2184
+
2185
+ LoggerProxy.logger.log = sinon.stub();
2186
+ const result = webex.meetings.isNeedHandleMainLocus(meeting, newLocus);
2187
+ assert.equal(result, false);
2188
+ assert.calledWith(
2189
+ LoggerProxy.logger.log,
2190
+ `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`
2191
+ );
2192
+ });
2193
+
2194
+ it('check current is in breakout join with this device, return false', () => {
2195
+ sinon.stub(webex.meetings.meetingCollection, 'getActiveBreakoutLocus').returns({
2196
+ joinedWith: {
2197
+ correlationId: '111',
2198
+ },
2199
+ });
2200
+ newLocus.controls.breakout = {url: 'url'};
2201
+ meeting.correlationId = '111';
2202
+
2203
+ LoggerProxy.logger.log = sinon.stub();
2204
+ const result = webex.meetings.isNeedHandleMainLocus(meeting, newLocus);
2205
+ assert.equal(result, false);
2206
+ assert.calledWith(
2207
+ LoggerProxy.logger.log,
2208
+ `Meetings:index#isNeedHandleMainLocus --> there is active breakout session and joined on this device, and don't need to handle main session: url`
2209
+ );
2210
+ });
2211
+
2212
+ it('check self is moved and removed, return false', () => {
2213
+ webex.meetings.meetingCollection.getActiveBreakoutLocus = sinon.stub().returns(null);
2214
+ newLocus.self.state = 'LEFT';
2215
+ newLocus.self.reason = 'MOVED';
2216
+ newLocus.self.removed = true;
2217
+ LoggerProxy.logger.log = sinon.stub();
2218
+ const result = webex.meetings.isNeedHandleMainLocus(meeting, newLocus);
2219
+ assert.equal(result, false);
2220
+ assert.calledWith(
2221
+ LoggerProxy.logger.log,
2222
+ 'Meetings:index#isNeedHandleMainLocus --> self moved main locus with self removed status or with device resource moved, not need to handle'
2223
+ );
2224
+ });
2225
+
2226
+ it('check self is moved and device resource removed, return false', () => {
2227
+ webex.meetings.meetingCollection.getActiveBreakoutLocus = sinon.stub().returns(null);
2228
+ newLocus.self.state = 'LEFT';
2229
+ newLocus.self.reason = 'MOVED';
2230
+ sinon.stub(MeetingsUtil, 'getThisDevice').returns({
2231
+ state: 'LEFT',
2232
+ reason: 'MOVED',
2233
+ });
2234
+ LoggerProxy.logger.log = sinon.stub();
2235
+ const result = webex.meetings.isNeedHandleMainLocus(meeting, newLocus);
2236
+ assert.equal(result, false);
2237
+ assert.calledWith(
2238
+ LoggerProxy.logger.log,
2239
+ 'Meetings:index#isNeedHandleMainLocus --> self moved main locus with self removed status or with device resource moved, not need to handle'
2240
+ );
2241
+ });
2242
+
2243
+ it('check self is joined but device resource removed, return false', () => {
2244
+ webex.meetings.meetingCollection.getActiveBreakoutLocus = sinon.stub().returns(null);
2245
+ sinon.stub(MeetingsUtil, 'joinedOnThisDevice').returns(false);
2246
+ newLocus.self.state = 'JOINED';
2247
+ sinon.stub(MeetingsUtil, 'getThisDevice').returns({
2248
+ state: 'LEFT',
2249
+ reason: 'MOVED',
2250
+ });
2251
+ LoggerProxy.logger.log = sinon.stub();
2252
+ const result = webex.meetings.isNeedHandleMainLocus(meeting, newLocus);
2253
+ assert.equal(result, false);
2254
+ assert.calledWith(
2255
+ LoggerProxy.logger.log,
2256
+ 'Meetings:index#isNeedHandleMainLocus --> self device left&moved in main locus with self joined status, not need to handle'
2257
+ );
2258
+ });
2259
+ });
2260
+
2261
+ describe('#isNeedHandleLocusDTO', () => {
2262
+ let meeting;
2263
+ let newLocus;
2264
+ beforeEach(() => {
2265
+ meeting = {
2266
+ controls: {},
2267
+ self: {},
2268
+ };
2269
+ newLocus = {
2270
+ controls: {},
2271
+ self: {},
2272
+ };
2273
+ });
2274
+ afterEach(() => {
2275
+ sinon.restore();
2276
+ });
2277
+ it('initial DTO , joined breakout session, return true', () => {
2278
+ newLocus.controls.breakout = {
2279
+ sessionType: 'BREAKOUT',
2280
+ };
2281
+ newLocus.self.state = 'JOINED';
2282
+ newLocus.fullState = {
2283
+ active: true,
2284
+ };
2285
+ LoggerProxy.logger.log = sinon.stub();
2286
+ const result = webex.meetings.isNeedHandleLocusDTO(null, newLocus);
2287
+ assert.equal(result, true);
2288
+ assert.calledWith(
2289
+ LoggerProxy.logger.log,
2290
+ `Meetings:index#isNeedHandleLocusDTO --> the first breakout session locusDTO active status: true`
2291
+ );
2292
+ });
2293
+ it('others go to check isNeedHandleMainLocus', () => {
2294
+ newLocus.controls.breakout = {
2295
+ sessionType: 'MAIN',
2296
+ };
2297
+ newLocus.self.state = 'JOINED';
2298
+
2299
+ LoggerProxy.logger.log = sinon.stub();
2300
+ const result = webex.meetings.isNeedHandleLocusDTO(meeting, newLocus);
2301
+ assert.equal(result, true);
2302
+ assert.calledWith(
2303
+ LoggerProxy.logger.log,
2304
+ 'Meetings:index#isNeedHandleMainLocus --> this is a normal main session locusDTO update case'
2305
+ );
2306
+ });
2307
+ it('joined breakout session, self status is moved, return false', () => {
2308
+ newLocus.controls.breakout = {
2309
+ sessionType: 'BREAKOUT',
2310
+ };
2311
+ newLocus.self.state = 'LEFT';
2312
+ newLocus.self.reason = 'MOVED';
2313
+
2314
+ LoggerProxy.logger.log = sinon.stub();
2315
+ const result = webex.meetings.isNeedHandleLocusDTO(meeting, newLocus);
2316
+ assert.equal(result, false);
2317
+ });
2318
+ });
2319
+
2320
+ describe('#getCorrespondingMeetingByLocus', () => {
2321
+ let locus;
2322
+ let mockReturnMeeting = {meeting: 'meeting1'};
2323
+ const mockGetByKey = (keyWillReturnMeeting) => {
2324
+ webex.meetings.meetingCollection.getByKey = sinon.stub().callsFake((key) => {
2325
+ if (key === keyWillReturnMeeting) {
2326
+ return mockReturnMeeting;
2327
+ }
2328
+ return null;
2329
+ });
2330
+ };
2331
+
2332
+ beforeEach(() => {
2333
+ locus = {
2334
+ controls: {},
2335
+ self: {
2336
+ callbackInfo: {
2337
+ callbackAddress: 'address1',
2338
+ },
2339
+ },
2340
+ info: {
2341
+ webExMeetingId: '123456',
2342
+ isUnifiedSpaceMeeting: false,
2343
+ },
2344
+ conversationUrl: 'conversationUrl1',
2345
+ };
2346
+
2347
+ sinon.stub(MeetingsUtil, 'checkForCorrelationId').returns('correlationId1');
2348
+ });
2349
+ afterEach(() => {
2350
+ sinon.restore();
2351
+ });
2352
+ it('check the calls when no meeting found in meetingCollection', () => {
2353
+ mockGetByKey();
2354
+ const result = webex.meetings.getCorrespondingMeetingByLocus({locus, locusUrl: url1});
2355
+ assert.isNull(result);
2356
+ assert.callCount(webex.meetings.meetingCollection.getByKey, 5);
2357
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'locusUrl', url1);
2358
+ assert.calledWith(
2359
+ webex.meetings.meetingCollection.getByKey,
2360
+ 'correlationId',
2361
+ 'correlationId1'
2362
+ );
2363
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'sipUri', 'address1');
2364
+ assert.calledWith(
2365
+ webex.meetings.meetingCollection.getByKey,
2366
+ 'conversationUrl',
2367
+ 'conversationUrl1'
2368
+ );
2369
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'meetingNumber', '123456');
2370
+ });
2371
+
2372
+ it('not try getByKey "conversationUrl" when isUnifiedSpaceMeeting is true', () => {
2373
+ mockGetByKey();
2374
+ locus.info.isUnifiedSpaceMeeting = true;
2375
+ const result = webex.meetings.getCorrespondingMeetingByLocus({locus, locusUrl: url1});
2376
+ assert.isNull(result);
2377
+ assert.callCount(webex.meetings.meetingCollection.getByKey, 4);
2378
+ });
2379
+
2380
+ it('check the calls when meeting found by key: locusUrl', () => {
2381
+ mockGetByKey('locusUrl');
2382
+ const result = webex.meetings.getCorrespondingMeetingByLocus({locus, locusUrl: url1});
2383
+ assert.deepEqual(result, mockReturnMeeting);
2384
+ assert.callCount(webex.meetings.meetingCollection.getByKey, 1);
2385
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'locusUrl', url1);
2386
+ });
2387
+
2388
+ it('check the calls when meeting found by key: correlationId', () => {
2389
+ mockGetByKey('correlationId');
2390
+ const result = webex.meetings.getCorrespondingMeetingByLocus({locus, locusUrl: url1});
2391
+ assert.deepEqual(result, mockReturnMeeting);
2392
+ assert.callCount(webex.meetings.meetingCollection.getByKey, 2);
2393
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'locusUrl', url1);
2394
+ assert.calledWith(
2395
+ webex.meetings.meetingCollection.getByKey,
2396
+ 'correlationId',
2397
+ 'correlationId1'
2398
+ );
2399
+ });
2400
+
2401
+ it('check the calls when meeting found by key: sipUri', () => {
2402
+ mockGetByKey('sipUri');
2403
+ const result = webex.meetings.getCorrespondingMeetingByLocus({locus, locusUrl: url1});
2404
+ assert.deepEqual(result, mockReturnMeeting);
2405
+ assert.callCount(webex.meetings.meetingCollection.getByKey, 3);
2406
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'locusUrl', url1);
2407
+ assert.calledWith(
2408
+ webex.meetings.meetingCollection.getByKey,
2409
+ 'correlationId',
2410
+ 'correlationId1'
2411
+ );
2412
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'sipUri', 'address1');
2413
+ });
2414
+
2415
+ it('check the calls when meeting found by key: conversationUrl', () => {
2416
+ mockGetByKey('conversationUrl');
2417
+ const result = webex.meetings.getCorrespondingMeetingByLocus({locus, locusUrl: url1});
2418
+ assert.deepEqual(result, mockReturnMeeting);
2419
+ assert.callCount(webex.meetings.meetingCollection.getByKey, 4);
2420
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'locusUrl', url1);
2421
+ assert.calledWith(
2422
+ webex.meetings.meetingCollection.getByKey,
2423
+ 'correlationId',
2424
+ 'correlationId1'
2425
+ );
2426
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'sipUri', 'address1');
2427
+ assert.calledWith(
2428
+ webex.meetings.meetingCollection.getByKey,
2429
+ 'conversationUrl',
2430
+ 'conversationUrl1'
2431
+ );
2432
+ });
2433
+
2434
+ it('check the calls when meeting found by key: meetingNumber', () => {
2435
+ mockGetByKey('meetingNumber');
2436
+ const result = webex.meetings.getCorrespondingMeetingByLocus({locus, locusUrl: url1});
2437
+ assert.deepEqual(result, mockReturnMeeting);
2438
+ assert.callCount(webex.meetings.meetingCollection.getByKey, 5);
2439
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'locusUrl', url1);
2440
+ assert.calledWith(
2441
+ webex.meetings.meetingCollection.getByKey,
2442
+ 'correlationId',
2443
+ 'correlationId1'
2444
+ );
2445
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'sipUri', 'address1');
2446
+ assert.calledWith(
2447
+ webex.meetings.meetingCollection.getByKey,
2448
+ 'conversationUrl',
2449
+ 'conversationUrl1'
2450
+ );
2451
+ assert.calledWith(webex.meetings.meetingCollection.getByKey, 'meetingNumber', '123456');
2452
+ });
2453
+ });
2454
+
2455
+ describe('#sortLocusArrayToUpdate', () => {
2456
+ let lociArray;
2457
+ let mainLocus;
2458
+ let breakoutLocus;
2459
+ beforeEach(() => {
2460
+ mainLocus = {
2461
+ url: 'mainUrl1',
2462
+ controls: {
2463
+ breakout: {
2464
+ sessionType: 'MAIN',
2465
+ url: 'breakoutUnifiedUrl1',
2466
+ },
2467
+ },
2468
+ };
2469
+ breakoutLocus = {
2470
+ url: 'breakoutUrl1',
2471
+ controls: {
2472
+ breakout: {
2473
+ sessionType: 'BREAKOUT',
2474
+ url: 'breakoutUnifiedUrl1',
2475
+ },
2476
+ },
2477
+ };
2478
+ lociArray = [mainLocus, breakoutLocus];
2479
+
2480
+ sinon.stub(MeetingsUtil, 'isValidBreakoutLocus').callsFake((locus) => {
2481
+ return locus.url === 'breakoutUrl1';
2482
+ });
2483
+ });
2484
+ afterEach(() => {
2485
+ sinon.restore();
2486
+ });
2487
+
2488
+ it('if both main and breakout locus is in array for non-exist meeting, return main locus to create first', () => {
2489
+ webex.meetings.meetingCollection.getByKey = sinon.stub().returns(undefined);
2490
+ const result = webex.meetings.sortLocusArrayToUpdate(lociArray);
2491
+ assert.deepEqual(result, [mainLocus]);
2492
+ assert.deepEqual(webex.meetings.breakoutLocusForHandleLater, [breakoutLocus]);
2493
+ });
2494
+
2495
+ it('if both main and breakout locus is in array for an exist meeting, return all locus', () => {
2496
+ webex.meetings.meetingCollection.getByKey = sinon.stub().returns({});
2497
+ const result = webex.meetings.sortLocusArrayToUpdate(lociArray);
2498
+ assert.deepEqual(result, [mainLocus, breakoutLocus]);
2499
+ assert.deepEqual(webex.meetings.breakoutLocusForHandleLater, []);
2500
+ });
2501
+
2502
+ it('if the breakout locus has no associated main locus, return all', () => {
2503
+ webex.meetings.meetingCollection.getByKey = sinon.stub().returns({});
2504
+ breakoutLocus.controls.breakout.url = 'testUrl';
2505
+ const result = webex.meetings.sortLocusArrayToUpdate(lociArray);
2506
+ assert.deepEqual(result, [mainLocus, breakoutLocus]);
2507
+ });
2508
+ });
2509
+
2510
+ describe('#checkHandleBreakoutLocus', () => {
2511
+ let breakoutLocus;
2512
+ beforeEach(() => {
2513
+ breakoutLocus = {
2514
+ url: 'breakoutUrl1',
2515
+ controls: {
2516
+ breakout: {
2517
+ sessionType: 'BREAKOUT',
2518
+ url: 'breakoutUnifiedUrl1',
2519
+ },
2520
+ },
2521
+ };
2522
+
2523
+ webex.meetings.handleLocusEvent = sinon.stub();
2524
+ });
2525
+ afterEach(() => {
2526
+ sinon.restore();
2527
+ });
2528
+ it('do nothing if new created locus is null/no cached breakouts for updating', () => {
2529
+ webex.meetings.checkHandleBreakoutLocus(null);
2530
+ webex.meetings.breakoutLocusForHandleLater = null;
2531
+ webex.meetings.checkHandleBreakoutLocus({});
2532
+ webex.meetings.breakoutLocusForHandleLater = [];
2533
+ webex.meetings.checkHandleBreakoutLocus({});
2534
+ assert.notCalled(webex.meetings.handleLocusEvent);
2535
+ });
2536
+
2537
+ it('do nothing if new created locus is breakout locus', () => {
2538
+ webex.meetings.breakoutLocusForHandleLater = [breakoutLocus];
2539
+ webex.meetings.checkHandleBreakoutLocus(breakoutLocus);
2540
+ assert.notCalled(webex.meetings.handleLocusEvent);
2541
+ });
2542
+
2543
+ it('do nothing if no cached locus is associated with the new created locus', () => {
2544
+ webex.meetings.breakoutLocusForHandleLater = [breakoutLocus];
2545
+ webex.meetings.checkHandleBreakoutLocus({
2546
+ controls: {
2547
+ breakout: {
2548
+ sessionType: 'MAIN',
2549
+ url: 'breakoutUnifiedUrl2',
2550
+ },
2551
+ },
2552
+ });
2553
+ assert.notCalled(webex.meetings.handleLocusEvent);
2554
+ });
2555
+
2556
+ it('update the cached breakout locus which associate the new created locus', () => {
2557
+ webex.meetings.breakoutLocusForHandleLater = [breakoutLocus];
2558
+ webex.meetings.checkHandleBreakoutLocus({
2559
+ controls: {
2560
+ breakout: {
2561
+ sessionType: 'MAIN',
2562
+ url: 'breakoutUnifiedUrl1',
2563
+ },
2564
+ },
2565
+ });
2566
+ assert.calledWith(webex.meetings.handleLocusEvent, {
2567
+ locus: breakoutLocus,
2568
+ locusUrl: breakoutLocus.url,
2569
+ });
2570
+ });
2571
+ });
2572
+
2573
+ describe('uploading of logs', () => {
2574
+ let metricsSpy;
2575
+ let meeting;
2576
+
2577
+ beforeEach(async () => {
2578
+ webex.meetings.config.autoUploadLogs = true;
2579
+ webex.meetings.loggerRequest.uploadLogs = sinon.stub().resolves();
2580
+
2581
+ sinon.stub(webex.meetings.meetingInfo, 'fetchInfoOptions').resolves({});
2582
+ sinon.stub(webex.meetings.meetingInfo, 'fetchMeetingInfo').resolves({});
2583
+
2584
+ triggerProxyStub.restore();
2585
+
2586
+ metricsSpy = sinon.stub(Metrics, 'sendBehavioralMetric');
2587
+
2588
+ meeting = await webex.meetings.create('test');
2589
+
2590
+ meeting.locusId = 'locus id';
2591
+ meeting.correlationId = 'correlation id';
2592
+ meeting.locusInfo = {
2593
+ fullState: {lastActive: 'last active', sessionId: 'locus session id'},
2594
+ info: {webExMeetingId: 'meeting id'},
2595
+ };
2596
+ });
2597
+
2598
+ afterEach(() => {
2599
+ sinon.restore();
2600
+ });
2601
+
2602
+ it('sends metrics on success', async () => {
2603
+ await meeting.uploadLogs();
2604
+
2605
+ await testUtils.flushPromises();
2606
+
2607
+ assert.calledOnceWithExactly(metricsSpy, 'js_sdk_upload_logs_success', {
2608
+ callStart: 'last active',
2609
+ correlationId: 'correlation id',
2610
+ feedbackId: 'correlation id',
2611
+ locusId: 'locus id',
2612
+ meetingId: 'meeting id',
2613
+ autoupload: true,
2614
+ locussessionid: 'locus session id',
2615
+ });
2616
+ });
2617
+
2618
+ it('sends metrics on failure', async () => {
2619
+ webex.meetings.loggerRequest.uploadLogs.rejects(new Error('fake error'));
2620
+
2621
+ await meeting.uploadLogs();
2622
+
2623
+ await testUtils.flushPromises();
2624
+
2625
+ assert.calledOnceWithExactly(
2626
+ metricsSpy,
2627
+ 'js_sdk_upload_logs_failure',
2628
+ sinon.match({
2629
+ callStart: 'last active',
2630
+ correlationId: 'correlation id',
2631
+ feedbackId: 'correlation id',
2632
+ locusId: 'locus id',
2633
+ meetingId: 'meeting id',
2634
+ reason: 'fake error',
2635
+ autoupload: true,
2636
+ locussessionid: 'locus session id',
2637
+ })
2638
+ );
2639
+ });
2640
+ });
1189
2641
  });
1190
2642
  });