@webex/plugin-meetings 3.0.0-beta.34 → 3.0.0-beta.340

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